diff --git a/as5013-test/nrf52-as5013-xy/nrf52-as5013-xy.ino b/as5013-test/nrf52-as5013-xy/nrf52-as5013-xy.ino new file mode 100644 index 0000000000000000000000000000000000000000..c6021b83c8f1b616ff7b72bf7ba4cf36e8ae55a2 --- /dev/null +++ b/as5013-test/nrf52-as5013-xy/nrf52-as5013-xy.ino @@ -0,0 +1,251 @@ +//read raw values from as5013 hall effect array ic and pass to uarte +//sec, 2017 +#include "radio.h" + +//as5013 registers +#define ctrl_reg 0x0F +#define x_res_reg 0x10 +#define y_res_reg 0x11 +#define T_ctrl_reg 0x2D +#define agc_reg 0x2A +#define c_raw_values_start 0x16 //starting address for raw sensor values + +#define scl_pin 0 //XL1 +#define sda_pin 1 //XL2 +#define rst_pin 3 //A1 + +//#define n_tx_bytes 1 //only need transmit the start read address +//#define n_rx_bytes 20 //20 values pos_low, pos_high, neg_low, neg_high for each of c1, c2, c3, c4, c5 +#define payload_size 6 //byte for x_res, byte for y_res, 2 bytes for x_act, 2 bytes for y_act +#define framing_size 8 + +static uint8_t txdata[2] = {0}; //the most we'll need is to send an i2c register and possibly a value. +static uint8_t rxdata[payload_size+framing_size] = {0}; //add four bytes for xi, yi over uarte, 8 bytes for framing + +//uarte +const uint8_t pin_rx = 8; +const uint8_t pin_tx = 6; + +//stepper motor +int16_t step_period = 4; + +// +//UARTE +// +void uarte_setup(){ + //uart with dma + NRF_UARTE0->PSEL.TXD = (pin_tx << UARTE_PSEL_TXD_PIN_Pos) & UARTE_PSEL_TXD_PIN_Msk; + NRF_UARTE0->PSEL.RXD = (pin_rx << UARTE_PSEL_RXD_PIN_Pos) & UARTE_PSEL_RXD_PIN_Msk; + NRF_UARTE0->CONFIG = ((UART_CONFIG_PARITY_Excluded << UARTE_CONFIG_PARITY_Pos) & UARTE_CONFIG_PARITY_Msk) + | ((UARTE_CONFIG_HWFC_Disabled << UARTE_CONFIG_HWFC_Pos) & UARTE_CONFIG_HWFC_Msk); + NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1M; + NRF_UARTE0->ENABLE = (UARTE_ENABLE_ENABLE_Enabled << UARTE_ENABLE_ENABLE_Pos) & UARTE_ENABLE_ENABLE_Msk; + + NRF_UARTE0->TXD.MAXCNT = payload_size+framing_size; +} + + +// +//TWI +// +void twi_set_cnts(uint8_t n_tx, uint8_t n_rx){ + NRF_TWIM0->TXD.MAXCNT = (n_tx << TWIM_TXD_MAXCNT_MAXCNT_Pos) & TWIM_TXD_MAXCNT_MAXCNT_Msk; + NRF_TWIM0->RXD.MAXCNT = (n_rx << TWIM_RXD_MAXCNT_MAXCNT_Pos) & TWIM_RXD_MAXCNT_MAXCNT_Msk; + NRF_TWIM0->TXD.PTR = (uint32_t)(&txdata); + NRF_TWIM0->RXD.PTR = (uint32_t)(&rxdata); +} +void twi_setup(){ + //Need to switch to internal LFCLK to disconnect from XL1 and XL2 + NRF_CLOCK->LFCLKSRC = 0; //disconnect XL1 AND XL2 FROM LFCLK? + NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; + NRF_CLOCK->TASKS_LFCLKSTART = 1; + while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0){} + + NRF_TWIM0->ENABLE = (TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos) & TWI_ENABLE_ENABLE_Msk; + NRF_GPIO->PIN_CNF[scl_pin] = (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) & GPIO_PIN_CNF_DRIVE_Msk; + NRF_GPIO->PIN_CNF[sda_pin] = (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) & GPIO_PIN_CNF_DRIVE_Msk; + NRF_GPIO->DIRCLR = (1<<scl_pin)|(1<<sda_pin); //set SDA/SCL as inputs (likely not necessary) + + NRF_TWIM0->PSEL.SCL = ((scl_pin << TWIM_PSEL_SCL_PIN_Pos) & TWIM_PSEL_SCL_PIN_Msk) + | ((TWIM_PSEL_SCL_CONNECT_Connected << TWIM_PSEL_SCL_CONNECT_Pos) & TWIM_PSEL_SCL_CONNECT_Msk); + NRF_TWIM0->PSEL.SDA = ((sda_pin << TWIM_PSEL_SDA_PIN_Pos) & TWIM_PSEL_SDA_PIN_Msk) + | ((TWIM_PSEL_SDA_CONNECT_Connected << TWIM_PSEL_SDA_CONNECT_Pos) & TWIM_PSEL_SDA_CONNECT_Msk); + NRF_TWIM0->FREQUENCY = (TWI_FREQUENCY_FREQUENCY_K100 << TWI_FREQUENCY_FREQUENCY_Pos) & TWI_FREQUENCY_FREQUENCY_Msk; + NRF_TWIM0->ADDRESS = (0x41 << TWI_ADDRESS_ADDRESS_Pos) & TWI_ADDRESS_ADDRESS_Msk; + NRF_TWIM0->TXD.PTR = (uint32_t)(&txdata); + NRF_TWIM0->RXD.PTR = (uint32_t)(&rxdata); + //twi_set_cnts(n_tx_bytes, n_rx_bytes); + //set up short between LASTTX and STOP and between LASTRX and STOP + NRF_TWIM0->SHORTS = ((TWIM_SHORTS_LASTTX_STOP_Enabled << TWIM_SHORTS_LASTTX_STOP_Pos) & TWIM_SHORTS_LASTTX_STOP_Msk) + | ((TWIM_SHORTS_LASTRX_STOP_Enabled << TWIM_SHORTS_LASTRX_STOP_Pos) & TWIM_SHORTS_LASTRX_STOP_Msk); + //NRF_TWIM0->ENABLE = (TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos) & TWI_ENABLE_ENABLE_Msk; + //There's a typo in nrf52_bitfields, so we set this manually. + NRF_TWIM0->ENABLE = (6 << TWI_ENABLE_ENABLE_Pos) & TWI_ENABLE_ENABLE_Msk; +} +void twi_tx(){ + // clear the stopped event + NRF_TWIM0->EVENTS_STOPPED = 0; + // triggering the STARTTX task + NRF_TWIM0->TASKS_STARTTX = 1; + while( !(NRF_TWIM0->EVENTS_STOPPED) ){} +} +void twi_rx(){ + // clear the stopped event + NRF_TWIM0->EVENTS_STOPPED = 0; + // trigger the STARTRX task + NRF_TWIM0->TASKS_STARTRX = 1; + while( !(NRF_TWIM0->EVENTS_STOPPED) ){} +} + + +// +//AS5013 +// +void as5013_setup(){ + NRF_GPIO->DIRSET = (1<<rst_pin); //set AS5013 nRESET pin as output + NRF_GPIO->OUTCLR = (1<<rst_pin); //hardware reset + delayMicroseconds(10); + NRF_GPIO->OUTSET = (1<<rst_pin); //turn on as5013 + delayMicroseconds(1000); + + //Loop check ctrl_reg until the value F0h or F1h is present + delay(1); //in practice even no delay is fine. + + //config sensors for maximum gain + //twi_set_cnts(2, 0); + //txdata[0] = agc_reg; + //txdata[1] = 0x3F; //gain value, 0x3F maximum + //twi_tx(); + + //configure t_ctrl + twi_set_cnts(2,0); + txdata[0] = T_ctrl_reg; + txdata[1] = 4; //200% sensitivity of baseline (max = 500%) + twi_tx(); + delay(1); +} +void as5013_read(){ + //assume idle mode, so we must read from Y_res_int register to trigger new conversion + //twi_set_cnts(1, 1); + //txdata[0] = y_res_reg; + //twi_tx(); + //twi_rx(); + //delay(1); + //read x and y values + twi_set_cnts(1, 2); + txdata[0] = x_res_reg; //set up to read from this address + twi_tx(); + twi_rx(); //read x and y results +} + +void configure_motor(){ + //set step speed + radio_buffer[0] = 2; //set step speed + radio_buffer[1] = step_period*1000; //10ms step period + radio_send_redundant(); + //set full steps + radio_buffer[0] = 3; //set microstepping + radio_buffer[1] = 1; //M1,M0 = 1,0, half step + radio_send_redundant(); + //set current limit + radio_buffer[0] = 4; //set current limit + radio_buffer[1] = 200; // + radio_send_redundant(); +} + +// +//main +// +void setup() { + //Serial.begin(115200); + uarte_setup(); + twi_setup(); + as5013_setup(); + radio_setup(); + + NRF_RADIO->TXADDRESS = 0x00UL; // Set device address 0: X axis + configure_motor(); + NRF_RADIO->TXADDRESS = 0x01UL; // Set device address 1: Y axis + configure_motor(); + + int16_t xi=0; //counter + int16_t yi=0; //counter + int16_t Nx = 100; //max number of x moves, must be even + int16_t Ny = 100; //max number of y moves, must be even + int16_t steps_per_move = 1; //1 quarter step = 312 nm (requires half stepping) + //int16_t steps_per_move = 8; //8 steps = 10 microns + //int16_t steps_per_move = 16; //16 steps = 20 microns + + //insert framing. + for(uint8_t bi=0; bi<framing_size; bi++){ + rxdata[payload_size+bi] = bi; + } + /*rxdata[n_rx_bytes+4] = (uint8_t)0; + rxdata[n_rx_bytes+5] = (uint8_t)1; + rxdata[n_rx_bytes+6] = (uint8_t)2; + rxdata[n_rx_bytes+7] = (uint8_t)3; + rxdata[n_rx_bytes+8] = (uint8_t)4; + rxdata[n_rx_bytes+9] = (uint8_t)5; + rxdata[n_rx_bytes+10] = (uint8_t)6; + rxdata[n_rx_bytes+11] = (uint8_t)7;*/ + + //move negative half total distance in both directions at start + NRF_RADIO->TXADDRESS = 0x00UL; // Set device address 0: X axis + radio_buffer[0] = 1; //send a move command + radio_buffer[1] = -steps_per_move*Nx/2; //move back to start + radio_send_redundant(); //send command + delay(step_period*Nx*steps_per_move+1); + + NRF_RADIO->TXADDRESS = 0x01UL; // Set device address 1: Y axis + radio_buffer[0] = 1; //send a move command + radio_buffer[1] = -steps_per_move*Ny/2; //move back to start + radio_send_redundant(); //send command + delay(step_period*Ny*steps_per_move+1); + + while(1){ + as5013_read(); + rxdata[2] = (uint8_t)(xi); + rxdata[3] = (uint8_t)(xi>>8); + rxdata[4] = (uint8_t)(yi); + rxdata[5] = (uint8_t)(yi>>8); + NRF_UARTE0->TXD.PTR = (uint32_t)(&rxdata); //reset pointer to start of buffer + NRF_UARTE0->TASKS_STARTTX = 1; //trigger start task to send data to host + + delay(1); //for demonstration, not really necessary. + + NRF_RADIO->TXADDRESS = 0x00UL; // Set device address 0: X axis + radio_buffer[0] = 1; //send a move command + radio_buffer[1] = steps_per_move; //move 10 steps forward + radio_send_redundant(); //send command + delay(step_period*steps_per_move+1); + xi++; + + if (xi==Nx){ + xi=0; + NRF_RADIO->TXADDRESS = 0x00UL; // Set device address 0: X axis + radio_buffer[0] = 1; //send a move command + radio_buffer[1] = -steps_per_move*Nx; //move back to start + radio_send_redundant(); //send command + delay(step_period*Nx*steps_per_move+1); + + NRF_RADIO->TXADDRESS = 0x01UL; // Set device address 1: Y axis + radio_buffer[0] = 1; //send a move command + radio_buffer[1] = steps_per_move; //move back to start + radio_send_redundant(); //send command + delay(step_period*steps_per_move+1); + yi++; + } + if (yi==Ny){ + delay(10); + yi=0; + NRF_RADIO->TXADDRESS = 0x01UL; // Set device address 1: Y axis + radio_buffer[0] = 1; //send a move command + radio_buffer[1] = -steps_per_move*Ny; //move back to start + radio_send_redundant(); //send command + delay(step_period*Ny*steps_per_move+1); + } + } +} + + +void loop() {} diff --git a/as5013-test/nrf52-as5013-xy/radio.h b/as5013-test/nrf52-as5013-xy/radio.h new file mode 100644 index 0000000000000000000000000000000000000000..ab8fa106d0a9da62a5a263dbf5f3f47b6531e292 --- /dev/null +++ b/as5013-test/nrf52-as5013-xy/radio.h @@ -0,0 +1,114 @@ +#define PACKET_BASE_ADDRESS_LENGTH (2UL) //Packet base address length field size in bytes +#define PACKET_LENGTH 4 +#define REDUNDANCY_COUNT 10 //number of transmissions to ensure delivery... hack. +static int16_t radio_buffer[PACKET_LENGTH] = {0}; + +//static int16_t reference_buffer[PACKET_LENGTH] = {0}; //for checking against receipt + + +// +//RADIO +// +void radio_setup(){ + NRF_RADIO->POWER = RADIO_POWER_POWER_Disabled; //turn off radio to reset registers + delay(10); + NRF_RADIO->POWER = RADIO_POWER_POWER_Enabled; //turn on radio + delay(10); + + NRF_RADIO->TXPOWER = (RADIO_TXPOWER_TXPOWER_Pos3dBm << RADIO_TXPOWER_TXPOWER_Pos); + NRF_RADIO->FREQUENCY = 11UL; // 2400 + X MHz + NRF_RADIO->MODE = (RADIO_MODE_MODE_Nrf_2Mbit << RADIO_MODE_MODE_Pos); + + NRF_RADIO->PREFIX0 = ((uint32_t)0xC0 << 0); // Prefix byte of address 0 + NRF_RADIO->BASE0 = 0x01234567UL; // Base address for prefix 0 + NRF_RADIO->BASE1 = 0x02345678UL; // Base address for prefix 0 + NRF_RADIO->TXADDRESS = 0x00UL; // Set device address 0 to use when transmitting + NRF_RADIO->RXADDRESSES = 0x01UL; // Enable device address 0 to use to select which addresses to receive + //NRF_RADIO->RXADDRESSES = 0x02UL; // Enable device address 1 to use to select which addresses to receive + + // Packet configuration + NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S1LEN_Pos) | (0 << RADIO_PCNF0_S0LEN_Pos) | (0 << RADIO_PCNF0_LFLEN_Pos); + NRF_RADIO->PCNF1 = (RADIO_PCNF1_WHITEEN_Enabled << RADIO_PCNF1_WHITEEN_Pos) | + ((PACKET_LENGTH) << RADIO_PCNF1_STATLEN_Pos) | + (RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) | + (2 << RADIO_PCNF1_BALEN_Pos); + NRF_RADIO->CRCCNF = (RADIO_CRCCNF_LEN_Three << RADIO_CRCCNF_LEN_Pos); // Number of checksum bits + NRF_RADIO->CRCINIT = 0xFFFFUL; // Initial value + NRF_RADIO->CRCPOLY = 0x18D; //x8 + x7 + x3 + x2 + 1 = 110001101 + NRF_RADIO->MODECNF0 |= RADIO_MODECNF0_RU_Fast << RADIO_MODECNF0_RU_Pos; //turn on fast ramp up + NRF_RADIO->SHORTS = 0; //turn off all shortcuts, for debug + NRF_RADIO->PACKETPTR = (uint32_t)&radio_buffer; //set pointer to packet buffer + //start HFCLK + NRF_CLOCK->TASKS_HFCLKSTART = 1; + while(!(NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_STATE_Msk)); //wait for hfclk to start + delay(10); +} + +void radio_wait_for_end(){ while(!(NRF_RADIO->EVENTS_END)); NRF_RADIO->EVENTS_END = 0;} //clear end event +void radio_wait_for_ready(){ while(!(NRF_RADIO->EVENTS_READY)); NRF_RADIO->EVENTS_READY = 0;} //clear ready event +void radio_disable(){ + NRF_RADIO->EVENTS_DISABLED = 0; //clear disabled event + NRF_RADIO->TASKS_DISABLE = 1; + while(!(NRF_RADIO->EVENTS_DISABLED)); +} +void radio_send(){ + NRF_RADIO->EVENTS_READY = 0; //clear ready event + NRF_RADIO->TASKS_TXEN=1; //trigger tx enable task + delayMicroseconds(20); + //radio_wait_for_ready(); //only generated when actually switching to tx mode + NRF_RADIO->TASKS_START=1; //start + radio_wait_for_end(); +} +void radio_send_redundant(){ + for(int i=0; i<REDUNDANCY_COUNT; i++){ + radio_send(); + } +} + +int radio_recv(){ + //return number of packets before CRC match + NRF_RADIO->EVENTS_CRCOK = 0; + //NRF_RADIO->EVENTS_CRCERROR = 0; + int i=1; + while(true){ + NRF_RADIO->EVENTS_READY = 0; //clear ready event + NRF_RADIO->TASKS_RXEN=1; //trigger rx enable task + delayMicroseconds(20); + //radio_wait_for_ready(); //only generated when actually switching to rx mode + NRF_RADIO->TASKS_START=1; + radio_wait_for_end(); + if (NRF_RADIO->EVENTS_CRCOK == 1){ break;} + i++; + } + return i; +} + + +/* +// start of 3 way handshake implementation +void copy_buffer_to_reference(){ + for(int i=0; i++; i<PACKET_LENGTH){ + reference_buffer[i] = radio_buffer[i]; + } +} +bool buffer_matches_reference(){ + bool match = true; + for(int i=0; i++; i<PACKET_LENGTH){ + if (reference_buffer[i] != radio_buffer[i]){ + match = false; + break; + } + } + return match; +} +void radio_send_with_handshake(){ + copy_buffer_to_reference(); + radio_send(); //send packet + int crc_match = radio_recv(); //receive ack + if crc_match && buffer_matches_reference(){ + radio_send(); //send ack + } +} +*/ + +