diff --git a/.gitignore b/.gitignore index 449fc15f746d0797925a51a746d8f7fa0a2b4227..b56264024e9f393f86ba06d2418e24078af823ac 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ *.lst *.bin *.map +minichlink/minichlink +minichlink/minichlink.so diff --git a/README.md b/README.md index a58718a47da75a34e3d2650c282009a13da84024..59599c9385d8b74709a27d39525e522ffdfe8512 100644 --- a/README.md +++ b/README.md @@ -55,14 +55,15 @@ You can use the pre-compiled minichlink or go to minichlink dir and `make` it. ``` cd examples/blink -make flash +make ``` -Just use `make` if you want to compile but not flash. +In Linux this will "just work"(TM) using `minichlink`. +In Windows, if you want to use minichlink, you will need to use Zadig to install WinUSB to the WCH-Link interface 0. +The generated .hex file is compatible with the official WCH flash tool. -In Linux this will "just work"(TM) using `minichlink`. -In Windows, if you want to use minichlink, you will need to use Zadig to install WinUSB to the WCH-Link interface 0. -The generated .hex file is compatible with the official WCH flash tool. +text = code, data = constants and initialization values, bss = uninitialized values. +dec is the sum of the 3 and reflects the number of bytes in flash that will get taken up by the program. ## ESP32S2 Programming @@ -75,7 +76,9 @@ It enumerates as 2 interfaces. If you want to mess with the programming code in Windows, you will have to install WinUSB to the interface 0. Then you can uninstall it in Device Manager under USB Devices. -On linux you find the serial port with `ls -l /dev/ttyUSB* /dev/ttyACM*` and connect to it with `screen /dev/ttyACM0 115200` +On linux you find the serial port with `ls -l /dev/ttyUSB* /dev/ttyACM*` and connect to it with `screen /dev/ttyACM0 115200` +Disconnect with `CTRL+a` `:quit`. + Adding your user to these groups will remove the need to `sudo` for access to the serial port: debian-based `sudo usermod -a -G dialout $USER` @@ -106,8 +109,19 @@ To use the WCH-Link in WSL, it is required to "attach" the USB hardware on the W 6. In powershell, use the command `usbipd wsl attach --busid=<BUSID>` to attach the device at the busid from previous step 7. You will hear the windows sound for the USB device being removed (and silently attached to WSL instead) 8. In WSL, you will now be able to run `lsusb` and see that the SCH-Link is attached -9. For unknown reasons, you must run make under root access in order to connect to the programmer with minichlink. Recommend running `sudo make flash` when building and programming projects using WSL -Feel free to solve this issue and figure out a way to give the user hardware access to WCH-Link and modify these instructions. +9. For unknown reasons, you must run make under root access in order to connect to the programmer with minichlink. Recommend running `sudo make` when building and programming projects using WSL. This may work too (to be confirmed): + +### non-root access on linux +Unlike serial interfaces, by default, the USB device is owned by root, has group set to root and everyone else may only read. +1. Get the vendor:device ID of the WCH-Link from `lsusb`. +2. Create a udev rule with `sudo nano /etc/udev/rules.d/80-USB_WCH-Link.rules`, paste (CTRL+SHIFT+V) `SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="8010", MODE="0666"` and save, replacing the idVendor and idProduct if what you got previously was different. +3. Reboot or reload the udev rules with `sudo udevadm control --reload-rules && sudo udevadm trigger` +4. ??? +5. profit +Now anyone on your PC has access to the WCH-Link device, so you can stop using sudo for make. +I don't think there are any security risks here. +You may also tie this to the WCH-Link serial number or some other attribute from `udevadm info -a -n /dev/bus/usb/busid/deviceid` with the bus id and device id you got from lsusb earlier. + ## minichlink diff --git a/examples/spi_24L01_rx/Makefile b/examples/spi_24L01_rx/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..5b54c64b56f41e500746d29d60f3dc740ad132dc --- /dev/null +++ b/examples/spi_24L01_rx/Makefile @@ -0,0 +1,11 @@ +all : flash + +TARGET:=spi_24L01_rx +ADDITIONAL_C_FILES+=nrf24l01_low_level.c nrf24l01.c + +CFLAGS+=-DSTDOUT_UART + +include ../../ch32v003fun/ch32v003fun.mk + +flash : cv_flash +clean : cv_clean diff --git a/examples/spi_24L01_rx/NRF24L01_RX_Arduino/NRF24L01_RX_Arduino.ino b/examples/spi_24L01_rx/NRF24L01_RX_Arduino/NRF24L01_RX_Arduino.ino new file mode 100644 index 0000000000000000000000000000000000000000..df2beaa215a0a1211577952329e5f1716d1701d8 --- /dev/null +++ b/examples/spi_24L01_rx/NRF24L01_RX_Arduino/NRF24L01_RX_Arduino.ino @@ -0,0 +1,128 @@ +//receiver code example, prints the received payload to the serial port +//payload length of 1 or 16 byte, 1Mbps datarate, -6 dbm rf transmit power, channel 32 of 125 + +extern "C"{ + #include "nrf24l01.h" +} + + + +#define TIME_GAP 300 +uint8_t ascending_number = 0; +char txt[16]; + + + +//######### debug fn + +void uint8_to_binary_string(uint8_t value, char* output, int len) { + for (int i = 0; i < len; i++) { + output[len - i - 1] = (value & 1) ? '1' : '0'; + value >>= 1; + } + output[len] = '\0'; +} + +void print_reg(char* name, uint8_t addr) { + Serial.print(" "); + Serial.print(name); + Serial.print(" register:"); + char str[9]; + uint8_t REG; + nrf24_read(addr, ®, 1, CLOSE); + uint8_to_binary_string(REG, str, 8); + Serial.println(str); +} + +void print_debug() { + print_reg("FEATURE ", FEATURE_ADDRESS); + print_reg("TX OBSERVE ", OBSERVE_TX_ADDRESS); + print_reg("STATUS ", STATUS_ADDRESS); + print_reg("RX_PW_P0 ADDR", RX_ADDR_P0_ADDRESS); + print_reg("TX ADDR ", TX_ADDR_ADDRESS); + print_reg("EN_AA ", EN_AA_ADDRESS); + print_reg("EN_RXADDR ", EN_RXADDR_ADDRESS); +} + + + +//######### LED fn + +//LED_BUILTIN is pin 13 is SCK of SPI, already using that +void led_on() { + digitalWrite(4, HIGH); +} + +void led_off() { + digitalWrite(4, LOW); +} + + + +//######### RX fn + +uint8_t recvnumber() { + return nrf24_receive(&ascending_number, 1); +} + +uint8_t recvstr() { + return nrf24_receive((uint8_t*)&txt, 16); +} + +void receive() { + // to switch between sending an uint8_t and a 16-byte-char-array, just uncomment one of these two: + //uint8_t result = recvnumber(); + uint8_t result = recvstr(); + // also uncomment the corresponding one for case OPERATION_DONE + + //print_debug(); + switch(result) { + case OPERATION_ERROR: + Serial.println("EEE RX operation error"); + break; + case RECEIVE_FIFO_EMPTY: + Serial.println(" RX empty"); + //Serial.print(" RX empty, last received: "); + //Serial.println(ascending_number); + break; + case OPERATION_DONE: + led_on(); + Serial.print("*** RX success, received: "); + // pick one of these two: + //Serial.println(ascending_number); + Serial.println(txt); + + break; + } + delay(TIME_GAP*1/2); + led_off(); + delay(TIME_GAP*1/2); +} + + +//######### MAIN + +void setup() +{ + Serial.begin(115200); + + Serial.print("\r\r\n\nspi_24L01_RX\n\r"); + + Serial.print("initializing radio as RX..."); + nrf24_device(RECEIVER, RESET); //initializing nrf24l01+ as a receiver device with one simple function call + nrf24_rf_power(18); //default TX power is -6dB, pretty strong, reduce to -18dBm for one room (ACK = TX) + Serial.println("done"); + + pinMode(4, OUTPUT); + + print_debug(); + + delay(1000); + + Serial.println("entering loop()"); +} + +void loop() +{ + receive(); +} diff --git a/examples/spi_24L01_rx/NRF24L01_RX_Arduino/nrf24l01.c b/examples/spi_24L01_rx/NRF24L01_RX_Arduino/nrf24l01.c new file mode 100644 index 0000000000000000000000000000000000000000..9f3b0e34c9b3849e16b42e55a9cb245b32e55c83 --- /dev/null +++ b/examples/spi_24L01_rx/NRF24L01_RX_Arduino/nrf24l01.c @@ -0,0 +1,614 @@ +#include "nrf24l01.h" + +/*nRF24L01+ features, enable / disable as needed*/ +static uint8_t NRF24_en_ack = ENABLE; +static uint8_t NRF24_en_no_ack = ENABLE; +static uint8_t NRF24_en_dynamic_payload = ENABLE; + +/*global variables related to this file*/ +static uint8_t SPI_command; /*1 byte spi command*/ +static uint8_t register_current_value; /*in order to change some bits of internal registers or to check their content*/ +static uint8_t register_new_value; /*used to write new value to nrf24l01+ registers*/ +static uint8_t write_pointer; /*used as an input for read and write functions (as a pointer)*/ +static uint8_t current_address_width; /*current address width for receiver pipe addresses (up to 6 pipes), from 3 to 5 bytes*/ +static uint8_t reset_flag = 0; /*reset flag lets the software know if the nrf24l01+ has ever been reset or not*/ +static uint8_t current_mode = DEVICE_NOT_INITIALIZED; /*current mode of operation: DEVICE_NOT_INITIALIZED, PRX, PTX, STANDBYI, STANDBYII, POWER_DOWN*/ +static uint8_t current_payload_width; /*payload width could be from 1 to 32 bytes, in either dynamic or static forms*/ +static uint8_t current_acknowledgement_state = NO_ACK_MODE; + +/*2 dimensional array of pipe addresses (5 byte address width) by default. you can change addresses using a new array later. + Pipe 1 address could be anything. pipe 3 to 6 addresses share the first 4 bytes with pipe 2 and only differ in byte 5*/ +uint8_t datapipe_address[MAXIMUM_NUMBER_OF_DATAPIPES][ADDRESS_WIDTH_DEFAULT] = { + {0X20, 0XC3, 0XC2, 0XC1, 0XA0}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA1}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA2}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA3}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA4}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA5} +}; + +/*function for PTX device to transmit 1 to 32 bytes of data, used for both dynamic payload length + and static payload length methods. acknowledgemet state could be NO_ACK_MODE or ACK_MODE*/ +uint8_t nrf24_transmit(uint8_t *payload, uint8_t payload_width, uint8_t acknowledgement_state) +{ + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*in order to check TX_FIFO status*/ + if ((!(register_current_value & (1 << TX_FULL))) && (current_mode == PTX)) + { + current_acknowledgement_state = acknowledgement_state; /*setting the acknowledgement state to either NO_ACK or ACK, based on input*/ + if (NRF24_en_dynamic_payload == ENABLE) + current_payload_width = payload_width; + nrf24_send_payload(payload, payload_width); /*the actual function to send data*/ + return (TRANSMIT_BEGIN); /*TX FIFO is not full and nrf24l01+ mode is standby ii or ptx*/ + } + else + { + return (TRANSMIT_FAIL); /*TX FIFO full or wrong mode*/ + } +} + +/*used by nrf24_transmit function to send the actual data*/ +void nrf24_send_payload(uint8_t *payload, uint8_t payload_width) +{ + nrf24_SPI(SPI_ON); + if (current_acknowledgement_state == NO_ACK_MODE) + SPI_command = W_TX_PAYLOAD_NOACK; + else + SPI_command = W_TX_PAYLOAD; + SPI_send_command(SPI_command); + for (; payload_width; payload_width--) + { + SPI_command = *payload; + SPI_send_command(SPI_command); + payload++; + } + nrf24_SPI(SPI_OFF); +} + +/*reports back transmit status: TRANSMIT_DONE, TRANSMIT_FAILED (in case of reaching maximum number of retransmits in auto acknowledgement mode) + and TRANSMIT_IN_PROGRESS, if neither flags are set. automatically resets the '1' flags.*/ +uint8_t nrf24_transmit_status() +{ + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*status register is read to check TX_DS flag*/ + if (register_current_value & (1 << TX_DS)) /*if the TX_DS == 1, */ + { + nrf24_write(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*reseting the TX_DS flag. as mentioned by datasheet, writing '1' to a flag resets that flag*/ + return TRANSMIT_DONE; + } + else if (register_current_value & (1 << MAX_RT)) + { + nrf24_write(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*reseting the MAX_RT flag. as mentioned by datasheet, writing '1' to a flag resets that flag*/ + return TRANSMIT_FAILED; + } + else + return TRANSMIT_IN_PROGRESS; +} + +/*the receive function output is used as a polling method to check the received data inside RX FIFOs. +If there is any data available, it will be loaded inside payload array*/ +uint8_t nrf24_receive(uint8_t *payload, uint8_t payload_width) +{ + if (current_mode == PRX) + { + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); + if (register_current_value & (1 << RX_DR)) /*if received data is ready inside RX FIFO*/ + { + if(NRF24_en_dynamic_payload == DISABLE) /*if dynamif payload width is disabled, use the static payload width and ignore the input*/ + payload_width = current_payload_width; + + nrf24_SPI(SPI_ON); /*sending the read payload command to nrf24l01+*/ + SPI_command = R_RX_PAYLOAD; + SPI_send_command(SPI_command); + + for (; payload_width; payload_width--) + { + SPI_command = NOP_CMD; + *payload = SPI_send_command(SPI_command); + payload++; + } + nrf24_SPI(SPI_OFF); + nrf24_read(FIFO_STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*in order to check the RX_EMPTY flag*/ + if(register_current_value & (1 << RX_EMPTY)) /*if the RX FIFO is empty, reset the RX_DR flag inside STATUS register*/ + { + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << RX_DR); + nrf24_write(STATUS_ADDRESS, ®ister_new_value, 1, CLOSE); + } + return OPERATION_DONE; + } + else + { + return RECEIVE_FIFO_EMPTY; + } + } + else + return OPERATION_ERROR; +} + +/*function which uses TX_FLUSH or RX_FLUSH command to flush the fifo buffers. if successful, output is OPERATION_DONE. + if not successful (wrong input or wrong mode of operation) output will be OPERATION_ERROR*/ +uint8_t nrf24_flush(uint8_t fifo_select) +{ + switch (fifo_select) + { + case TX_BUFFER: + if (current_mode == PTX) + { + nrf24_SPI(SPI_ON); + SPI_command = FLUSH_TX; + SPI_send_command(SPI_command); + nrf24_SPI(SPI_OFF); + return OPERATION_DONE; + } + else + return OPERATION_ERROR; + case RX_BUFFER: + if (current_mode == PRX) + { + nrf24_SPI(SPI_ON); + SPI_command = FLUSH_RX; + SPI_send_command(SPI_command); + nrf24_SPI(SPI_OFF); + return OPERATION_DONE; + } + else + return OPERATION_ERROR; + default: + return OPERATION_ERROR; + } +} + +/*must be called atleast once, which happens with calling nrf24_device function*/ +void nrf24_reset() +{ + reset_flag = RESET; + nrf24_CE(CE_OFF); + register_new_value = CONFIG_REGISTER_DEFAULT; + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = EN_AA_REGISTER_DEFAULT; + nrf24_write(EN_AA_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = EN_RXADDR_REGISTER_DEFAULT; + nrf24_write(EN_RXADDR_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = SETUP_AW_REGISTER_DEFAULT; + nrf24_write(SETUP_AW_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = RF_CH_REGISTER_DEFAULT; + nrf24_write(RF_CH_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = RF_SETUP_REGISTER_DEFAULT; + nrf24_write(RF_SETUP_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = STATUS_REGISTER_DEFAULT; + nrf24_write(STATUS_ADDRESS, ®ister_new_value, 1, CLOSE); + + nrf24_mode(PTX); + nrf24_flush(TX_BUFFER); + nrf24_mode(PRX); + nrf24_flush(RX_BUFFER); + + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << RX_DR) | (1 << TX_DS) | (1 << MAX_RT); + nrf24_write(STATUS_ADDRESS, ®ister_new_value, 1, CLOSE); + + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + nrf24_crc_configuration(ENABLE, 1); + nrf24_address_width(ADDRESS_WIDTH_DEFAULT); + nrf24_rf_datarate(RF_DATARATE_DEFAULT); + nrf24_rf_power(RF_PWR_DEFAULT); + nrf24_rf_channel(RF_CHANNEL_DEFAULT); + nrf24_datapipe_enable(NUMBER_OF_DP_DEFAULT); + /*nrf24_datapipe_address_configuration();*/ + /*nrf24_datapipe_ptx(1);*/ + nrf24_prx_static_payload_width(STATIC_PAYLOAD_WIDTH_DEFAULT, NUMBER_OF_DP_DEFAULT); + nrf24_automatic_retransmit_setup(RETRANSMIT_DELAY_DEFAULT, RETRANSMIT_COUNT_DEFAULT); + nrf24_auto_acknowledgment_setup(NUMBER_OF_DP_DEFAULT); + nrf24_dynamic_payload(NRF24_en_dynamic_payload, NUMBER_OF_DP_DEFAULT); + nrf24_payload_without_ack(NRF24_en_no_ack); + nrf24_payload_with_ack(NRF24_en_ack); +} + +/*used by firmware to set the nrf24 mode in TRANSMITTER, RECEIVER, POWER_SAVING or TURN_OFF states, and reseting the device + if it has not been done yet. This is the initializer, and everything starts by calling nrf24_device first.It has a higher + level of abstraction than nrf24_mode and must be used by user*/ +void nrf24_device(uint8_t device_mode, uint8_t reset_state) +{ + SPI_Initializer(); + pinout_Initializer(); + delay_function(STARTUP_DELAY); + + if ((reset_state == RESET) || (reset_flag == 0)) + { + nrf24_reset(); + } + + switch (device_mode) + { + case TRANSMITTER: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, DISABLE, DISABLE); /*disabling tx interrupt mask*/ + nrf24_mode(PTX); + break; + case RECEIVER: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(DISABLE, ENABLE, ENABLE); /*disabling rx interrupt mask*/ + nrf24_mode(PRX); + delay_function(PRX_MODE_DELAY); /*100ms for PRX mode*/ + break; + case POWER_SAVING: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + nrf24_mode(STANDBYI); + break; + case TURN_OFF: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + break; + default: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + break; + } +} + +/*setting automatic retransmit delay time and maximum number of retransmits*/ +void nrf24_automatic_retransmit_setup(uint16_t delay_time, uint8_t retransmit_count) +{ + register_new_value = 0x00; + for (; (delay_time > 250) && (register_new_value < 0X0F); delay_time -= 250) + register_new_value++; + register_new_value <<= ARD_0; + if ((retransmit_count > 0) && (retransmit_count < 16)) + register_new_value |= retransmit_count; + else + register_new_value |= 0; + nrf24_write(SETUP_RETR_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*setting auto acknoledgement on datapipes*/ +void nrf24_auto_acknowledgment_setup(uint8_t datapipe) +{ + if (datapipe < 7) + register_new_value = (1 << datapipe) - 1; + nrf24_write(EN_AA_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*turns on or off the dynamic payload width capability*/ +void nrf24_dynamic_payload(uint8_t state, uint8_t datapipe) +{ + nrf24_auto_acknowledgment_setup(datapipe); /*setting auto acknowledgment before setting dynamic payload*/ + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + if (state == ENABLE) + { + register_new_value = register_current_value | (1 << EN_DPL); /*EN_DPL bit turns dynamic payload width on or off on all datapipes*/ + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + if (datapipe < 7) + register_new_value = (1 << datapipe) - 1; /*turning on dynamic payload width on chosen datapipes, using DYNPD register*/ + nrf24_write(DYNPD_ADDRESS, ®ister_new_value, 1, CLOSE); + NRF24_en_dynamic_payload = ENABLE; + } + else + { + register_new_value = register_current_value & (~(1 << EN_DPL)); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + NRF24_en_dynamic_payload = DISABLE; + } +} + +/*function to enable or disable sending without acknowledge. + if disabled, TX must send a payload with ACK-request and receiver must be able to answer it. + manipulates EN_DYN_ACK inside FEATURE*/ +void nrf24_payload_without_ack(uint8_t state) +{ + if (state == ENABLE) + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << EN_DYN_ACK); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + } + else + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value & (~(1 << EN_DYN_ACK)); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + } +} + +/*function to enable or disable sending with acknowledge. + if disabled, the payload can be sent only without ACK-request. + manipulates EN_ACK_PAY and EN_DPL inside FEATURE as Dynamic Payload Length is required.*/ +void nrf24_payload_with_ack(uint8_t state) +{ + if (state == ENABLE) + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << EN_ACK_PAY) | (1 << EN_DPL); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + nrf24_read(DYNPD_ADDRESS, ®ister_current_value, 1, CLOSE); + // enable dynamic payload for all pipes + register_new_value = register_current_value | 0b111111; + nrf24_write(DYNPD_ADDRESS, ®ister_new_value, 1, CLOSE); + } + else + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value & (~((1 << EN_ACK_PAY) | (1 << EN_DPL))); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + } +} + +/*on nrf24l01+ there is only one address for PTX device which must be the same as PRX data pipe address 0*/ +void nrf24_datapipe_ptx(uint8_t datapipe_number) +{ + nrf24_write(TX_ADDR_ADDRESS, &datapipe_address[datapipe_number - 1][0], current_address_width, CLOSE); +} + +/*setting the 6 datapipe addresses using the datapipe_address[][]*/ +void nrf24_datapipe_address_configuration() +{ + uint8_t address = RX_ADDR_P0_ADDRESS; + for (uint8_t counter = 0; counter < 6; counter++) + { + nrf24_write(address, &datapipe_address[counter][0], current_address_width, CLOSE); + address++; + } +} + +/*function to change static payload width, from 1 to 32 bytes in each payload*/ +void nrf24_prx_static_payload_width(uint8_t static_payload_width, uint8_t number_of_datapipes) +{ + for (uint8_t address = RX_PW_P0_ADDRESS; number_of_datapipes; number_of_datapipes--) + { + nrf24_write(address, &static_payload_width, 1, CLOSE); + address++; + } + current_payload_width = static_payload_width; +} + +/*datapipes are turned on and off using EN_RXADDR register, PRX datapipe addresses are located in RX_ADDR_Pn, TX address is located inside TX_ADDR*/ +void nrf24_datapipe_enable(uint8_t datapipe) +{ + nrf24_read(EN_RXADDR_ADDRESS, ®ister_current_value, 1, CLOSE); + if (NRF24_en_ack) { + register_current_value |= (1 << ERX_P0); + } + register_new_value = register_current_value | (1 << datapipe); + nrf24_write(EN_RXADDR_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*function to set the nrf24l01+ address width, from 3 to 5 bytes*/ +void nrf24_address_width(uint8_t address_width) +{ + if ((address_width <= 5) && (address_width >= 3)) + { + write_pointer = address_width - 2; + } + else + { + write_pointer = 3; + } + nrf24_write(SETUP_AW_ADDRESS, &write_pointer, 1, CLOSE); /*5 bytes is the maximum address width available*/ + current_address_width = address_width; +} + +/*datarate settings, you can choose between 2mbps, 1mbps, 250kbps*/ +void nrf24_rf_datarate(uint16_t rf_datarate) +{ + nrf24_read(RF_SETUP_ADDRESS, ®ister_current_value, 1, CLOSE); + register_current_value &= ~((1 << RF_DR_LOW) | (1 << RF_DR_HIGH)); + switch (rf_datarate) + { + case 2000: + register_new_value = register_current_value | (1 << RF_DR_HIGH); + break; + case 1000: + register_new_value = register_current_value; + break; + case 250: + register_new_value = register_current_value | (1 << RF_DR_LOW); + break; + default: + register_new_value = register_current_value; + break; + } + nrf24_write(RF_SETUP_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*nrf24l01+ RF power settings. 0dbm, -6dbm, -12dbm, -18dbm*/ +void nrf24_rf_power(uint8_t rf_power) +{ + nrf24_read(RF_SETUP_ADDRESS, ®ister_current_value, 1, CLOSE); + register_current_value &= ~((1 << RF_PWR_1) | (1 << RF_PWR_0)); + switch (rf_power) + { + case 0: + register_new_value = register_current_value | ((1 << RF_PWR_1) | (1 << RF_PWR_0)); + break; + case 6: + register_new_value = register_current_value | (1 << RF_PWR_1); + break; + case 12: + register_new_value = register_current_value | (1 << RF_PWR_0); + break; + case 18: + register_new_value = register_current_value; + break; + default: + register_new_value = register_current_value | (1 << RF_PWR_1); + break; + } + nrf24_write(RF_SETUP_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*read whether the current channel is busy (has traffic), needs to be called from RX mode*/ +uint8_t nrf24_rf_channel_read_busy(uint8_t rf_channel) +{ + uint8_t signals_detected; + nrf24_read(RPD_REG_ADDRESS, &signals_detected, 1, CLOSE); + if (signals_detected) { + return CHANNEL_BUSY; + } + else { + return CHANNEL_CLEAR; + } +} + +/*test whether a channel is busy (has traffic), waiting for ms_to_test*/ +uint8_t nrf24_rf_channel_test_busy(uint8_t rf_channel, uint16_t ms_to_test) +{ + if ((rf_channel <= 125) && (rf_channel >= 1)) + { + // back up old channel + uint8_t previous_channel; + nrf24_read(RF_CH_ADDRESS, &previous_channel, 1, CLOSE); + // back up old mode + uint8_t previous_mode = current_mode; + // switch to new channel + nrf24_rf_channel(rf_channel); + // switch to RX, Received Power Detector is set to 0 and begins sampling + if (previous_mode != PRX) { + nrf24_mode(PRX); + } + // wait at least 1 ms before declaring channel clear + delay_function(1 > ms_to_test ? 1 : ms_to_test); + // Received Power Detector latches to 1 if there was a signal >-64dBm for at least 40 uS consecutively since RX mode was enabled + uint8_t signals_detected = nrf24_rf_channel_read_busy(rf_channel); + // switch back to old channel + nrf24_rf_channel(previous_channel); + // switch back to old mode + if (previous_mode != PRX) { + nrf24_mode(previous_mode); + } + if (signals_detected) { + return CHANNEL_BUSY; + } + else { + return CHANNEL_CLEAR; + } + } + else + { + return CHANNEL_BUSY; + } +} + +/*nrf24l01+ RF channel selection, from 1 to 125*/ +void nrf24_rf_channel(uint8_t rf_channel) +{ + if ((rf_channel <= 125) && (rf_channel >= 1)) + { + uint8_t write_pointer = rf_channel; + nrf24_write(RF_CH_ADDRESS, &write_pointer, 1, CLOSE); + } + else + { + uint8_t write_pointer = 1; + nrf24_write(RF_CH_ADDRESS, &write_pointer, 1, CLOSE); + } +} + +/*interrupt mask settings. 3 seperate masks for RX, TX, and RT (maximum numbers of retransmission reached*/ +void nrf24_interrupt_mask(uint8_t rx_mask, uint8_t tx_mask, uint8_t max_rt_mask) +{ + nrf24_read(CONFIG_ADDRESS, ®ister_current_value, 1, CLOSE); + if (rx_mask) + register_new_value = (register_current_value) | (1 << MASK_RX_DR); + else + register_new_value &= (~(1 << MASK_RX_DR)); + if (tx_mask) + register_new_value |= (1 << MASK_TX_DS); + else + register_new_value &= (~(1 << MASK_TX_DS)); + if (max_rt_mask) + register_new_value |= (1 << MASK_MAX_RT); + else + register_new_value &= (~(1 << MASK_MAX_RT)); + + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*enabling or disabling crc in payload; setting crc encoding scheme between 1 or 2 bytes*/ +void nrf24_crc_configuration(uint8_t crc_enable, uint8_t crc_encoding_scheme) +{ + nrf24_read(CONFIG_ADDRESS, ®ister_current_value, 1, CLOSE); + if (crc_enable) + register_new_value = (register_current_value) | (1 << EN_CRC); + else + register_new_value &= (~(1 << EN_CRC)); + if (crc_encoding_scheme == 2) + register_new_value |= (1 << CRCO); + else + register_new_value &= (~(1 << CRCO)); + + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*mode selector: power down, standby i, standby ii, ptx, prx. used by nrf24_device function*/ +void nrf24_mode(uint8_t mode) +{ + nrf24_read(CONFIG_ADDRESS, ®ister_current_value, 1, CLOSE); + switch (mode) + { + case POWER_DOWN: + nrf24_CE(CE_OFF); + register_new_value = (register_current_value) & (~(1 << PWR_UP)); + delay_function(POWER_DOWN_DELAY); + break; + case STANDBYI: /*standby I is defined by 'PWR_UP = 1' and 'CE pin LOW'*/ + nrf24_CE(CE_OFF); + register_new_value = (register_current_value) | (1 << PWR_UP); + delay_function(STANDBYI_DELAY); + break; + case STANDBYII: /*standby ii is related to a ptx device*/ + nrf24_CE(CE_ON); + register_new_value = ((register_current_value) | (1 << PWR_UP)) & (~(1 << PRIM_RX)); + delay_function(STANDBYI_DELAY); + break; + case PTX: + nrf24_CE(CE_ON); + register_new_value = ((register_current_value) | (1 << PWR_UP)) & (~(1 << PRIM_RX)); + delay_function(STANDBYI_DELAY); + break; + case PRX: + nrf24_CE(CE_ON); + register_new_value = (register_current_value) | (1 << PWR_UP) | (1 << PRIM_RX); + delay_function(STANDBYI_DELAY); + break; + default: + nrf24_CE(CE_OFF); + register_new_value = (register_current_value) & (~(1 << PWR_UP)); + delay_function(POWER_DOWN_DELAY); + break; + } + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); + current_mode = mode; +} + +/*reads the number of bytes (data_length) from the register in nrf24l01+ (address) and stores them inside an array (value), + then closes the spi connection (spi_state = CLOSE) or leaves it open (spi_state = OPEN)*/ +void nrf24_read(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state) +{ + nrf24_SPI(SPI_ON); + SPI_command = R_REGISTER | address; /*in order to read CONFIG, then change one bit*/ + SPI_send_command(SPI_command); + SPI_command = NOP_CMD; + for (; data_length ; data_length--) + { + *value = SPI_send_command(SPI_command); + value++; + } + if (spi_state == CLOSE) + nrf24_SPI(SPI_OFF); +} + +/*writes the number of bytes (data_length) from an array (value) inside registers in nrf24l01+ (address), + then closes the spi connection (spi_state = CLOSE) or leaves it open (spi_state = OPEN)*/ +void nrf24_write(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state) +{ + nrf24_SPI(SPI_ON); + SPI_command = W_REGISTER | address; /*in order to read CONFIG, then change one bit*/ + SPI_send_command(SPI_command); + for (; data_length ; data_length--) + { + SPI_command = *value; + value++; + SPI_send_command(SPI_command); + } + if (spi_state == CLOSE) + nrf24_SPI(SPI_OFF); +} diff --git a/examples/spi_24L01_rx/NRF24L01_RX_Arduino/nrf24l01.h b/examples/spi_24L01_rx/NRF24L01_RX_Arduino/nrf24l01.h new file mode 100644 index 0000000000000000000000000000000000000000..20876f691b44710ef4d034c78458623cbed827dc --- /dev/null +++ b/examples/spi_24L01_rx/NRF24L01_RX_Arduino/nrf24l01.h @@ -0,0 +1,219 @@ +#ifndef NRF24L01_H +#define NRF24L01_H +/*nrf24l01: MSbit to LSbit, LSbyte to MSbyte*/ +#include <stdio.h> +#include <stdint.h> + +#define STARTUP_DELAY 150 /*in milliseconds*/ +#define POWER_DOWN_DELAY 2 +#define STANDBYI_DELAY 2 +#define PRX_MODE_DELAY 100 +#define ADDRESS_WIDTH_DEFAULT 5 /*address width in bytes, for default value*/ +#define RF_CHANNEL_DEFAULT 32 +#define RF_DATARATE_DEFAULT 1000 /*250, 1000, 2000*/ +#define RF_PWR_DEFAULT 6 /*0, -6, -12, -18*/ +#define STATIC_PAYLOAD_WIDTH_DEFAULT 1 /*for static payload mode, configurable between 1 and 32 bytes for PRX device ONLY (RX_PW_Pn, n for data pipe n)(no register for payload length in PTX device)*/ +#define NUMBER_OF_DP_DEFAULT 1 /*number of datapipes, 1 to 6*/ +#define RETRANSMIT_DELAY_DEFAULT 500 /*in uS*/ +#define RETRANSMIT_COUNT_DEFAULT 3 + +#define OPEN 1 +#define CLOSE 0 +#define ENABLE 1 +#define DISABLE 0 +#define SPI_OFF 1 +#define SPI_ON 0 +#define CE_OFF 0 +#define CE_ON 1 + +#define CONFIG_REGISTER_DEFAULT 0X08 +#define EN_AA_REGISTER_DEFAULT 0X3F +#define EN_RXADDR_REGISTER_DEFAULT 0X00 +#define SETUP_AW_REGISTER_DEFAULT 0X03 +#define SETUP_RETR_REGISTER_DEFAULT 0X03 +#define RF_CH_REGISTER_DEFAULT 0X02 +#define RF_SETUP_REGISTER_DEFAULT 0X0E +#define STATUS_REGISTER_DEFAULT 0X0E +#define MAXIMUM_NUMBER_OF_DATAPIPES 6 + +#define POWER_DOWN 0X00 +#define STANDBYI 0X01 +#define STANDBYII 0X02 +#define PTX 0X03 +#define PRX 0X04 +#define DEVICE_NOT_INITIALIZED 0X05 + +#define TRANSMITTER 0X00 +#define RECEIVER 0X01 +#define POWER_SAVING 0X02 +#define TURN_OFF 0X03 + +#define RESET 1 +#define NO_RESET 0 +#define NO_ACK_MODE 1 +#define ACK_MODE 0 +#define TRANSMIT_BEGIN 1 +#define TRANSMIT_FAIL 0 +#define TRANSMIT_IN_PROGRESS 0 +#define TRANSMIT_DONE 1 +#define TRANSMIT_FAILED 0XFF +#define OPERATION_DONE 1 +#define OPERATION_ERROR 0 +#define RECEIVE_FIFO_EMPTY 2 +#define TX_BUFFER 1 +#define RX_BUFFER 0 +// return states for nrf24_rf_channel_test_busy +#define CHANNEL_CLEAR 0 +#define CHANNEL_BUSY 1 + +/*bits definition section*/ +#define MASK_RX_DR 6 /*mask interrupt caused by RX_DR: 1 interrupt not reflected on IRQ pin (IRQ is active low), inside CONFIG register*/ +#define MASK_TX_DS 5 /*mask interrupt caused by TX_DS: 1 interrupt not reflected on IRQ pin (IRQ is active low), inside CONFIG register*/ +#define MASK_MAX_RT 4 /*mask interrupt caused by MAX_RT means maximum number of retransmissions reached: 1 interrupt not reflected on IRQ pin (IRQ is active low), inside CONFIG register*/ +#define EN_CRC 3 /*enale CRC, forced high if one of the bits in EN_AA is high, inside CONFIG register*/ +#define CRCO 2 /*CRC encoding scheme, 0 is 1 byte, 1 is 2 bytes, inside CONFIG register*/ +#define PWR_UP 1 /*1 is power up, inside CONFIG register*/ +#define PRIM_RX 0 /*RX/TX control, 1: PRX, inside CONFIG register*/ +#define ENAA_P5 5 /*enable auto acknowledgement data pipe 5*/ +#define ENAA_P4 4 +#define ENAA_P3 3 +#define ENAA_P2 2 +#define ENAA_P1 1 +#define ENAA_P0 0 +#define ERX_P5 5 /*part of EN_RXADDR, enable data pipe 5*/ +#define ERX_P4 4 +#define ERX_P3 3 +#define ERX_P2 2 +#define ERX_P1 1 +#define ERX_P0 0 +#define AW_1 1 /*RX/TX address field width, 00 illegal, 01 3 bytes, 10 4 bytes, 11 5 bytes*/ +#define AW_0 0 +#define ARD_3 7 /*auto retransmit delay, 0000 250us, 0001 500us ...> 1111 4000us*/ +#define ARD_2 6 +#define ARD_1 5 +#define ARD_0 4 +#define ARC_3 3 /*auto retransmit count, 0000 retransmit deisabled, 1111 up to 15 retransmit on failure of AA. (inside SETUP_RETR register)*/ +#define ARC_2 2 +#define ARC_1 1 +#define ARC_0 0 +#define RF_CH_6 6 /*sets the frequencvy channel nRF24L01+ operates on*/ +#define RF_CH_5 5 +#define RF_CH_4 4 +#define RF_CH_3 3 +#define RF_CH_2 2 +#define RF_CH_1 1 +#define RF_CH_0 0 +#define CONT_WAVE 7 /*enables continuous carrier transmit when high*/ +#define RF_DR_LOW 5 /*sets the RF data rate to 250kbps*/ +#define PLL_LOCK 4 /*force PLL lock signal. used for testing ONLY*/ +#define RF_DR_HIGH 3 /*select between high speed data rates and works ONLY when RF_DR_LOW is 0. 0 for 1Mbps, 1 for 2Mbps*/ +#define RF_PWR_1 2 +#define RF_PWR_0 1 +#define RX_DR 6 /*IRQ for new packet in RX FIFO (newly received)*/ +#define TX_DS 5 /*IRQ for ACK received in TX mode*/ +#define MAX_RT 4 +#define RX_P_NO_2 3 +#define RX_P_NO_1 2 +#define RX_P_NO_0 1 +#define PLOS_CNT_3 7 /*inside OBSERVE_TX register, counts the total number of retransmissions since last channel change. reset by writing to RF_CH*/ +#define PLOS_CNT_2 6 +#define PLOS_CNT_1 5 +#define PLOS_CNT_0 4 +#define ARC_CNT_3 3 /*inside OBSERVE_TX register, counts the number of retransmissions for current transaction. reset by initiating new transaction*/ +#define ARC_CNT_2 2 +#define ARC_CNT_1 1 +#define ARC_CNT_0 0 +#define RPD 0 /*received power detector, if received power is less than -64dbm, RPD = 0*/ +#define TX_REUSE 6 +#define TX_FULL 5 +#define TX_EMPTY 4 +#define RX_FULL 1 +#define RX_EMPTY 0 +#define DPL_P5 5 +#define DPL_P4 4 +#define DPL_P3 3 +#define DPL_P2 2 +#define DPL_P1 1 +#define DPL_P0 0 /*must be set on PTX in dynamic payload length mode*/ +#define EN_DPL 2 /*set to enable dynamic payload length*/ +#define EN_ACK_PAY 1 /*used to enable auto acknowledgement with payload in PRX (inside FEATURE register)*/ +#define EN_DYN_ACK 0 /**/ + +/*registers definition section*/ +#define CONFIG_ADDRESS 0X00 +#define EN_AA_ADDRESS 0X01 /*enable auto acknowledgement feature*/ +#define EN_RXADDR_ADDRESS 0X02 /*register containing bits to enable 6 data pipes individually*/ +#define SETUP_AW_ADDRESS 0X03 /*address field length is configured in here to be 3, 4 or 5 bytes long*/ +#define SETUP_RETR_ADDRESS 0X04 /*setup ARC bits to configure auto retransmission count*/ +#define RF_CH_ADDRESS 0X05 +#define RF_SETUP_ADDRESS 0X06 +#define STATUS_ADDRESS 0X07 /*contains RX_DR, TX_DS, MAX_RT, RX_P_NO, TX_FULL, send R_REGISTER then NOP to read*/ +#define OBSERVE_TX_ADDRESS 0X08 /*contains ARC_CNT and PLOS_CNT, two counters for retransmission. these counters could be used to assess the network quality*/ +#define RPD_REG_ADDRESS 0X09 +#define RX_ADDR_P0_ADDRESS 0X0A /*the address for PRX device. if a packet contains this address, enhanced shockburst starts validating the packet*/ +#define RX_ADDR_P1_ADDRESS 0X0B /*a total of 6 unique addresses could be assigned to a PRX device (Multiceiver feature)*/ +#define RX_ADDR_P2_ADDRESS 0X0C /*these addresses must NOT be the same*/ +#define RX_ADDR_P3_ADDRESS 0X0D +#define RX_ADDR_P4_ADDRESS 0X0E +#define RX_ADDR_P5_ADDRESS 0X0F +#define TX_ADDR_ADDRESS 0X10 /*40 bits long register, transmit address, used for a PTX device only. configure address legth in SETUP_AW register. set RX_ADDR_P0 equal to this address to handle automatic acknowledge*/ +#define RX_PW_P0_ADDRESS 0X11 /*these registers are for setting the static payload length in static payload length mode (receiver side)*/ +#define RX_PW_P1_ADDRESS 0X12 +#define RX_PW_P2_ADDRESS 0X13 +#define RX_PW_P3_ADDRESS 0X14 +#define RX_PW_P4_ADDRESS 0X15 +#define RX_PW_P5_ADDRESS 0X16 +#define FIFO_STATUS_ADDRESS 0X17 +#define DYNPD_ADDRESS 0X1C /*on receiver side (RX mode), this register must be set to enable dynamic payload length. a PTX in dynamic mode, must have the DYNPD_P0 set*/ +#define FEATURE_ADDRESS 0X1D /*contains the EN_DPL bit to enable dynamic payload length*/ + +/*commands definition section*/ +#define R_REGISTER 0X00 /*read commmand and STATUS registers, 5 bit register map address*/ +#define W_REGISTER 0X20 /*write commmand and STATUS registers, 5 bit register map address, executable in POWER DOWN or STANDBY modes only*/ +#define R_RX_PAYLOAD 0X61 /*read RX payload, 1-32 bytes. read operation starts at byte 0. payload is deleted from FIFO after its read*/ +#define W_TX_PAYLOAD 0XA0 /*write TX payload, starts at byte 0, 1-32 bytes*/ +#define FLUSH_TX 0XE1 /*flush TX FIFO, used in TX mode*/ +#define FLUSH_RX 0XE2 /*flush RX FIFO, used in RX mode*/ +#define REUSE_TX_PL 0XE3 /*used for a PTX device, reuse last transmitted payload for an exact number. alternative to auto retransmission*/ +#define R_RX_PL_WID 0X60 /*command for receiver side, in order to read the payload length in dynamic payload length mode*/ +#define W_ACK_PAYLOAD 0XA0 /*used in RX mode, to write payload in TX FIFO and later transmit the payloads along with ACK packet to PTX, if DPL is enabled*/ +#define W_TX_PAYLOAD_NOACK 0XB0 /*used in TX mode, disables AUTOACK on this specific packet. must be first enabled in FEATURE register by setting the EN_DYN_ACK bit. if used, PTX will not wait for ACK and goes directly to standby I*/ +#define NOP_CMD 0XFF /*might be used to read the status register*/ + +void nrf24_reset(); +void nrf24_device(uint8_t device_mode, uint8_t reset_state); +uint8_t SPI_send_command(uint8_t command); +void pinout_Initializer(); +void SPI_Initializer(); +void nrf24_mode(uint8_t mode); +void nrf24_SPI(uint8_t input); +void nrf24_CE(uint8_t input); +void nrf24_address_width(uint8_t address_width); +uint8_t nrf24_rf_channel_read_busy(uint8_t rf_channel); +uint8_t nrf24_rf_channel_test_busy(uint8_t rf_channel, uint16_t ms_to_test); +void nrf24_rf_channel(uint8_t rf_channel); +void nrf24_rf_power(uint8_t rf_power); +void nrf24_rf_datarate(uint16_t rf_datarate); +void nrf24_read(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state); +void nrf24_write(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state); +void delay_function(uint32_t duration_ms); +void nrf24_crc_configuration(uint8_t crc_enable, uint8_t crc_encoding_scheme); +void nrf24_interrupt_mask(uint8_t rx_mask, uint8_t tx_mask, uint8_t max_rt_mask); +void nrf24_datapipe_enable(uint8_t number_of_datapipes); +void nrf24_prx_static_payload_width(uint8_t static_payload_width, uint8_t number_of_datapipes); +void nrf24_datapipe_address_configuration(); +void nrf24_datapipe_ptx(uint8_t datapipe_number); +void nrf24_automatic_retransmit_setup(uint16_t delay_time, uint8_t retransmit_count); +void nrf24_auto_acknowledgment_setup(uint8_t datapipe); +void nrf24_payload_without_ack(uint8_t state); +void nrf24_payload_with_ack(uint8_t state); +void nrf24_dynamic_payload(uint8_t state, uint8_t datapipe); +void nrf24_device(uint8_t device_mode, uint8_t reset_state); +void nrf24_send_payload(uint8_t *payload, uint8_t payload_width); +uint8_t nrf24_receive(uint8_t *payload, uint8_t payload_width); +uint8_t nrf24_transmit(uint8_t *payload, uint8_t payload_width, uint8_t acknowledgement_state); +uint8_t nrf24_transmit_status(); +void nrf24_dynamic_ack(uint8_t state); +uint8_t nrf24_flush(uint8_t fifo_select); + +#endif diff --git a/examples/spi_24L01_rx/NRF24L01_RX_Arduino/nrf24l01_low_level.c b/examples/spi_24L01_rx/NRF24L01_RX_Arduino/nrf24l01_low_level.c new file mode 100644 index 0000000000000000000000000000000000000000..412a98c533e10b11e6ef5b9e8a9259ea85997786 --- /dev/null +++ b/examples/spi_24L01_rx/NRF24L01_RX_Arduino/nrf24l01_low_level.c @@ -0,0 +1,70 @@ +/*low level api example for avr/arduino*/ + +#include <Arduino.h> +#include "nrf24l01.h" + +/*macros for SPI, CE and CSN pin configuration, change pins # according to mcu*/ +#define MOSI_PIN 11 +#define MISO_PIN 12 +#define SCK_PIN 13 +#define SS_PIN 10 +#define NRF24_CSN 9 /*to enable SPI on nrf24, active LOW*/ +#define NRF24_CE 8 /*active HIGH, activate chip in RX or TX mode*/ + +/*start of low level functions, specific to the mcu and compiler*/ + +/*delay in miliseconds*/ +void delay_function(uint32_t duration_ms) +{ + delay(duration_ms); +} + +/*contains all SPI configuations, such as pins and control registers*/ +/*SPI control: master, interrupts disabled, clock polarity low when idle, clock phase falling edge, clock up tp 1 MHz*/ +void SPI_Initializer() +{ + pinMode(MOSI_PIN, OUTPUT); + pinMode(MISO_PIN, INPUT); + pinMode(SCK_PIN, OUTPUT); + pinMode(SS_PIN, OUTPUT); + + SPCR = 0X51; /*master, interrupt disabled, spi enabled, clock polarity low when idle, clock phase falling edge, 1 MHz clock*/ +} + +/*contains all CSN and CE pins gpio configurations, including setting them as gpio outputs and turning SPI off and CE '1'*/ +void pinout_Initializer() +{ + pinMode(NRF24_CSN, OUTPUT); + pinMode(NRF24_CE, OUTPUT); + + digitalWrite(NRF24_CSN, SPI_OFF); /*nrf24l01 is not accepting commands*/ + nrf24_CE(HIGH); /*no need to change this line*/ +} + +/*CSN pin manipulation to high or low (SPI on or off)*/ +void nrf24_SPI(uint8_t input) +{ + if (input > 0) + digitalWrite(NRF24_CSN, HIGH); + else + digitalWrite(NRF24_CSN, LOW); +} + +/*1 byte SPI shift register send and receive routine*/ +uint8_t SPI_send_command(uint8_t command) +{ + SPDR = command; + while ((SPSR & (1 << SPIF)) == 0) {} + return SPDR; +} + +/*CE pin maniplation to high or low*/ +void nrf24_CE(uint8_t input) +{ + if (input > 0) + digitalWrite(NRF24_CE, HIGH); + //digitalWrite(NRF24_CE, LOW); + else + digitalWrite(NRF24_CE, LOW); + //digitalWrite(NRF24_CE, HIGH); +} diff --git a/examples/spi_24L01_rx/README.md b/examples/spi_24L01_rx/README.md new file mode 100644 index 0000000000000000000000000000000000000000..086fd6a3efa7db583ea1232517d3208df77cbbae --- /dev/null +++ b/examples/spi_24L01_rx/README.md @@ -0,0 +1,12 @@ +# SPI 24L01+ RX demo +This is the receiving half of the spi_24L01 "example". +Enjoy SPI and 2.4GHz: + + +https://user-images.githubusercontent.com/104343143/235313119-4facb0b4-9f8d-4b79-b39e-647600fdaa26.mp4 + + +https://user-images.githubusercontent.com/104343143/235313123-14a2114d-388a-4c98-89b9-63e5e40de682.mp4 + +## nRF24L01(+) library +This is the doing of [Reza Ebrahimi](https://github.com/ebrezadev), I've just included a copy of [his library](https://github.com/ebrezadev/nRF24L01-C-Driver) here and made some modifications. diff --git a/examples/spi_24L01_rx/ch32v003_SPI.h b/examples/spi_24L01_rx/ch32v003_SPI.h new file mode 100644 index 0000000000000000000000000000000000000000..7f30a051583a07ed3f417202fbd54ebefd7395b3 --- /dev/null +++ b/examples/spi_24L01_rx/ch32v003_SPI.h @@ -0,0 +1,339 @@ +//######## necessities + +// include guards +#ifndef CH32V003_SPI_H +#define CH32V003_SPI_H + +// includes +#include<stdint.h> //uintN_t support +#include"../../ch32v003fun/ch32v003fun.h" + + + +/*######## library usage and configuration + +in the .c files that use this library, you'll need to #define some configuration options _before_ the #include "ch32v003_SPI.h" + +SYSTEM_CORE_CLOCK and APB_CLOCK should be defined already as APB_CLOCK is used by this library + +to enable using the functions of this library: +#define CH32V003_SPI_IMPLEMENTATION + +to configure the settings of the SPI bus, first, declare the desired bus speed + +#define CH32V003_SPI_SPEED_HZ 1000000 + +then pick the desired setting of each group: + +#define CH32V003_SPI_DIRECTION_2LINE_TXRX +#define CH32V003_SPI_DIRECTION_1LINE_TX + +#define CH32V003_SPI_CLK_MODE_POL0_PHA0 //leading = rising trailing = falling sample on leading default if you're unsure +#define CH32V003_SPI_CLK_MODE_POL0_PHA1 //leading = rising trailing = falling sample on trailing +#define CH32V003_SPI_CLK_MODE_POL1_PHA0 //leading = falling trailing = rising sample on leading +#define CH32V003_SPI_CLK_MODE_POL1_PHA1 //leading = falling trailing = rising sample on trailing + +#define CH32V003_SPI_NSS_HARDWARE_PC0 // _NSS toggled by hardware, automatic +#define CH32V003_SPI_NSS_HADRWARE_PC1 // NSS toggled by hardware, automatic +#define CH32V003_SPI_NSS_SOFTWARE_PC3 // PC3 toggled by software, automatic, manual setters available +#define CH32V003_SPI_NSS_SOFTWARE_PC4 // PC4 toggled by software, automatic, manual setters available +#define CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL // toggle manually! +*/ + + + +//######## function overview (declarations): use these! +// initialize and configure the SPI peripheral +static inline void SPI_init(); + +// establish / end a connection to the SPI device +static inline void SPI_begin_8(); +static inline void SPI_begin_16(); +static inline void SPI_end(); + +// manually set the NSS (chip select) pin high / low +// "SPI_NSS_HIGH_FN" and "SPI_NSS_LOW_FN" only become available functions if the selected NSS is software PC3 or PC4 +#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) +static inline void SPI_NSS_software_low(); +static inline void SPI_NSS_software_high(); +#endif + +// read / write the SPI device +// these commands are raw, you'll have to consider all other steps in SPI_transfer! +static inline uint8_t SPI_read_8(); +static inline uint16_t SPI_read_16(); +static inline void SPI_write_8(uint8_t data); +static inline void SPI_write_16(uint16_t data); + +// send a command and get a response from the SPI device +// you'll use this for most devices +static inline uint8_t SPI_transfer_8(uint8_t data); +static inline uint8_t SPI_transfer_16(uint16_t data); + +// SPI peripheral power enable / disable (default off, init() automatically enables) +// send SPI peripheral to sleep +static inline void SPI_poweroff(); +// wake SPI peripheral from sleep +static inline void SPI_poweron(); + +// helper: kill / restore all interrupts on the CH32V003 +static inline void kill_interrrupts(); +static inline void restore_interrupts(); + + + +//######## internal function declarations +static inline void SPI_wait_TX_complete(); +static inline uint8_t SPI_is_RX_empty(); +static inline void SPI_wait_RX_available(); + + + +//######## internal variables +static uint16_t EXT1_INTENR_backup; + + + +//######## preprocessor macros +// min and max helper macros +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +// stringify for displaying what #defines evaluated to at preprocessor stage +#define VALUE_TO_STRING(x) #x +#define VALUE(x) VALUE_TO_STRING(x) +#define VAR_NAME_VALUE(var) #var "=" VALUE(var) + +//compile-time log2 +#define LOG2(x) ((x) == 0 ? -1 : __builtin_ctz(x)) + +// compile-time clock prescaler calculation: log2(APB_CLOCK/SPEED_BUS) +#define SPI_CLK_RATIO (APB_CLOCK / CH32V003_SPI_SPEED_HZ) +#define SPI_CLK_PRESCALER LOG2(SPI_CLK_RATIO) + +// ensure that CLOCK_PRESCALER_VALUE is within the range of 0..7 +_Static_assert(SPI_CLK_PRESCALER >= 0 && SPI_CLK_PRESCALER <= 7, "SPI_CLK_PRESCALER is out of range (0..7). Please set a different SPI bus speed. prescaler = log2(f_CPU/f_SPI)"); +//#pragma message(VAR_NAME_VALUE(SPI_CLK_PRESCALER)) + + + +//######## preprocessor #define requirements + +#if !defined(CH32V003_SPI_DIRECTION_2LINE_TXRX) && !defined(CH32V003_SPI_DIRECTION_1LINE_TX) + #warning "none of the CH32V003_SPI_DIRECTION_ options were defined!" +#endif +#if defined(CH32V003_SPI_DIRECTION_2LINE_TXRX) && defined(CH32V003_SPI_DIRECTION_1LINE_TX) + #warning "both CH32V003_SPI_DIRECTION_ options were defined!" +#endif + +#if ((defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) ? 1 : 0) + \ + (defined(CH32V003_SPI_CLK_MODE_POL0_PHA1) ? 1 : 0) + \ + (defined(CH32V003_SPI_CLK_MODE_POL1_PHA0) ? 1 : 0) + \ + (defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) > 1 + #warning "more than one of the CH32V003_SPI_CLK_MODE_ options were defined!" +#endif +#if ((defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) ? 1 : 0) + \ + (defined(CH32V003_SPI_CLK_MODE_POL0_PHA1) ? 1 : 0) + \ + (defined(CH32V003_SPI_CLK_MODE_POL1_PHA0) ? 1 : 0) + \ + (defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) == 0 + #warning "none of the CH32V003_SPI_CLK_MODE_ options were defined!" +#endif + +#if ((defined(CH32V003_SPI_NSS_HARDWARE_PC0) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) > 1 + #warning "more than one of the CH32V003_SPI_NSS_ options were defined!" +#endif +#if ((defined(CH32V003_SPI_NSS_HARDWARE_PC0) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) == 0 + #warning "none of the CH32V003_SPI_NSS_ options were defined!" +#endif + + + +//######## small function definitions, static inline +static inline void SPI_init() { + SPI_poweron(); + + // reset control register + SPI1->CTLR1 = 0; + + // set prescaler + SPI1->CTLR1 |= SPI_CTLR1_BR & (SPI_CLK_PRESCALER<<3); + + // set clock polarity and phase + #if defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) + SPI1->CTLR1 |= (SPI_CPOL_Low | SPI_CPHA_1Edge); + #elif defined (CH32V003_SPI_CLK_MODE_POL0_PHA1) + SPI1->CTLR1 |= (SPI_CPOL_Low | SPI_CPHA_2Edge); + #elif defined (CH32V003_SPI_CLK_MODE_POL1_PHA0) + SPI1->CTLR1 |= (SPI_CPOL_High | SPI_CPHA_1Edge); + #elif defined (CH32V003_SPI_CLK_MODE_POL1_PHA1) + SPI1->CTLR1 |= (SPI_CPOL_High | SPI_CPHA_2Edge); + #endif + + // configure NSS pin, master mode + #if defined(CH32V003_SPI_NSS_HARDWARE_PC0) + // _NSS (negative slave select) on PC0, 10MHz Output, alt func, push-pull1 + SPI1->CTLR1 |= SPI_NSS_Hard; // NSS hardware control mode + GPIOC->CFGLR &= ~(0xf<<(4*0)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*0); + AFIO->PCFR1 |= GPIO_Remap_SPI1; // remap NSS (C1) to _NSS (C0) + SPI1->CTLR2 |= SPI_CTLR2_SSOE; // pull _NSS high + #elif defined(CH32V003_SPI_NSS_HADRWARE_PC1) + // NSS (negative slave select) on PC1, 10MHz Output, alt func, push-pull1 + SPI1->CTLR1 |= SPI_NSS_Hard; // NSS hardware control mode + GPIOC->CFGLR &= ~(0xf<<(4*1)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*1); + SPI1->CTLR2 |= SPI_CTLR2_SSOE; // pull _NSS high + #elif defined(CH32V003_SPI_NSS_SOFTWARE_PC3) + SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode + GPIOC->CFGLR &= ~(0xf<<(4*3)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*3); + #elif defined(CH32V003_SPI_NSS_SOFTWARE_PC4) + SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode + GPIOC->CFGLR &= ~(0xf<<(4*4)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*4); + #elif defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) + SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode + #endif + + // SCK on PC5, 10MHz Output, alt func, push-pull + GPIOC->CFGLR &= ~(0xf<<(4*5)); + GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF)<<(4*5); + + // CH32V003 is master + SPI1->CTLR1 |= SPI_Mode_Master; + + // set data direction and configure data pins + #if defined(CH32V003_SPI_DIRECTION_2LINE_TXRX) + SPI1->CTLR1 |= SPI_Direction_2Lines_FullDuplex; + + // MOSI on PC6, 10MHz Output, alt func, push-pull + GPIOC->CFGLR &= ~(0xf<<(4*6)); + GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF)<<(4*6); + + // MISO on PC7, 10MHz input, floating + GPIOC->CFGLR &= ~(0xf<<(4*7)); + GPIOC->CFGLR |= GPIO_CNF_IN_FLOATING<<(4*7); + #elif defined(CH32V003_SPI_DIRECTION_1LINE_TX) + SPI1->CTLR1 |= SPI_Direction_1Line_Tx; + + // MOSI on PC6, 10MHz Output, alt func, push-pull + GPIOC->CFGLR &= ~(0xf<<(4*6)); + GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF)<<(4*6); + #endif +} + +static inline void SPI_begin_8() { + SPI1->CTLR1 |= SPI_DataSize_8b; // DFF 16bit data-length enable, writable only when SPE is 0 + SPI1->CTLR1 |= CTLR1_SPE_Set; +} +static inline void SPI_begin_16() { + SPI1->CTLR1 |= SPI_DataSize_16b; // DFF 16bit data-length enable, writable only when SPE is 0 + SPI1->CTLR1 |= CTLR1_SPE_Set; +} +static inline void SPI_end() { + SPI1->CTLR1 &= CTLR1_SPE_Reset; +} + +#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) +static inline void SPI_NSS_software_high() { + GPIOC->BSHR = (1<<3); +} +static inline void SPI_NSS_software_low() { + GPIOC->BSHR = (1<<(16+3)); +} +#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC4) +static inline void SPI_NSS_software_high() { + GPIOC->BSHR = (1<<4); +} +static inline void SPI_NSS_software_low() { + GPIOC->BSHR = (1<<(16+4)); +} +#endif + +static inline uint8_t SPI_read_8() { + return SPI1->DATAR; +} +static inline uint16_t SPI_read_16() { + return SPI1->DATAR; +} +static inline void SPI_write_8(uint8_t data) { + SPI1->DATAR = data; +} +static inline void SPI_write_16(uint16_t data) { + SPI1->DATAR = data; +} +static inline uint8_t SPI_transfer_8(uint8_t data) { + #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) + SPI_NSS_software_high(); + #endif + SPI_write_8(data); + SPI_wait_TX_complete(); + asm volatile("nop"); + SPI_wait_RX_available(); + #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) + SPI_NSS_software_low(); + #endif + return SPI_read_8(); +} +static inline uint8_t SPI_transfer_16(uint16_t data) { + #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) + SPI_NSS_software_high(); + #endif + SPI_write_16(data); + SPI_wait_TX_complete(); + asm volatile("nop"); + SPI_wait_RX_available(); + #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) + SPI_NSS_software_low(); + #endif + return SPI_read_16(); +} + +static inline void SPI_poweroff() { + SPI_end(); + RCC->APB2PCENR &= ~RCC_APB2Periph_SPI1; +} +static inline void SPI_poweron() { + RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1; +} + +static inline void kill_interrrupts() { + EXT1_INTENR_backup = EXTI->INTENR; + // zero the interrupt enable register to disable all interrupts + EXTI->INTENR = 0; +} +static inline void restore_interrupts() { + EXTI->INTENR = EXT1_INTENR_backup; +} + + + +//######## small internal function definitions, static inline +static inline void SPI_wait_TX_complete() { + while(!(SPI1->STATR & SPI_STATR_TXE)) {} +} +static inline uint8_t SPI_is_RX_empty() { + return SPI1->STATR & SPI_STATR_RXNE; +} +static inline void SPI_wait_RX_available() { + while(!(SPI1->STATR & SPI_STATR_RXNE)) {} +} + + + +//######## implementation block +//#define CH32V003_SPI_IMPLEMENTATION //enable so LSP can give you text colors while working on the implementation block, disable for normal use of the library +#if defined(CH32V003_SPI_IMPLEMENTATION) + +//no functions here because I think all of the functions are small enough to static inline + +#endif // CH32V003_SPI_IMPLEMENTATION +#endif // CH32V003_SPI_H diff --git a/examples/spi_24L01_rx/nrf24l01.c b/examples/spi_24L01_rx/nrf24l01.c new file mode 100644 index 0000000000000000000000000000000000000000..75a2111beb5593bcad897f410cd03315d2fb06cf --- /dev/null +++ b/examples/spi_24L01_rx/nrf24l01.c @@ -0,0 +1,616 @@ +#include "nrf24l01.h" + +/*nRF24L01+ features, enable / disable as needed*/ +static uint8_t NRF24_en_ack = ENABLE; +static uint8_t NRF24_en_no_ack = ENABLE; +static uint8_t NRF24_en_dynamic_payload = ENABLE; + +/*global variables related to this file*/ +static uint8_t SPI_command; /*1 byte spi command*/ +static uint8_t register_current_value; /*in order to change some bits of internal registers or to check their content*/ +static uint8_t register_new_value; /*used to write new value to nrf24l01+ registers*/ +static uint8_t write_pointer; /*used as an input for read and write functions (as a pointer)*/ +static uint8_t current_address_width; /*current address width for receiver pipe addresses (up to 6 pipes), from 3 to 5 bytes*/ +static uint8_t reset_flag = 0; /*reset flag lets the software know if the nrf24l01+ has ever been reset or not*/ +static uint8_t current_mode = DEVICE_NOT_INITIALIZED; /*current mode of operation: DEVICE_NOT_INITIALIZED, PRX, PTX, STANDBYI, STANDBYII, POWER_DOWN*/ +static uint8_t current_payload_width; /*payload width could be from 1 to 32 bytes, in either dynamic or static forms*/ +static uint8_t current_acknowledgement_state = NO_ACK_MODE; + +/*2 dimensional array of pipe addresses (5 byte address width) by default. you can change addresses using a new array later. + Pipe 1 address could be anything. pipe 3 to 6 addresses share the first 4 bytes with pipe 2 and only differ in byte 5*/ +uint8_t datapipe_address[MAXIMUM_NUMBER_OF_DATAPIPES][ADDRESS_WIDTH_DEFAULT] = { + {0X20, 0XC3, 0XC2, 0XC1, 0XA0}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA1}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA2}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA3}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA4}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA5} +}; + +/*function for PTX device to transmit 1 to 32 bytes of data, used for both dynamic payload length + and static payload length methods. acknowledgemet state could be NO_ACK_MODE or ACK_MODE*/ +uint8_t nrf24_transmit(uint8_t *payload, uint8_t payload_width, uint8_t acknowledgement_state) +{ + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*in order to check TX_FIFO status*/ + if ((!(register_current_value & (1 << TX_FULL))) && (current_mode == PTX)) + { + current_acknowledgement_state = acknowledgement_state; /*setting the acknowledgement state to either NO_ACK or ACK, based on input*/ + if (NRF24_en_dynamic_payload == ENABLE) + current_payload_width = payload_width; + nrf24_send_payload(payload, payload_width); /*the actual function to send data*/ + return (TRANSMIT_BEGIN); /*TX FIFO is not full and nrf24l01+ mode is standby ii or ptx*/ + } + else + { + return (TRANSMIT_FAIL); /*TX FIFO full or wrong mode*/ + } +} + +/*used by nrf24_transmit function to send the actual data*/ +void nrf24_send_payload(uint8_t *payload, uint8_t payload_width) +{ + nrf24_SPI(SPI_ON); + if (current_acknowledgement_state == NO_ACK_MODE) + SPI_command = W_TX_PAYLOAD_NOACK; + else + SPI_command = W_TX_PAYLOAD; + SPI_send_command(SPI_command); + for (; payload_width; payload_width--) + { + SPI_command = *payload; + SPI_send_command(SPI_command); + payload++; + } + nrf24_SPI(SPI_OFF); +} + +/*reports back transmit status: TRANSMIT_DONE, TRANSMIT_FAILED (in case of reaching maximum number of retransmits in auto acknowledgement mode) + and TRANSMIT_IN_PROGRESS, if neither flags are set. automatically resets the '1' flags.*/ +uint8_t nrf24_transmit_status() +{ + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*status register is read to check TX_DS flag*/ + if (register_current_value & (1 << TX_DS)) /*if the TX_DS == 1, */ + { + nrf24_write(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*reseting the TX_DS flag. as mentioned by datasheet, writing '1' to a flag resets that flag*/ + return TRANSMIT_DONE; + } + else if (register_current_value & (1 << MAX_RT)) + { + nrf24_write(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*reseting the MAX_RT flag. as mentioned by datasheet, writing '1' to a flag resets that flag*/ + return TRANSMIT_FAILED; + } + else + return TRANSMIT_IN_PROGRESS; +} + +/*the receive function output is used as a polling method to check the received data inside RX FIFOs. +If there is any data available, it will be loaded inside payload array*/ +uint8_t nrf24_receive(uint8_t *payload, uint8_t payload_width) +{ + if (current_mode == PRX) + { + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); + if (register_current_value & (1 << RX_DR)) /*if received data is ready inside RX FIFO*/ + { + if(NRF24_en_dynamic_payload == DISABLE) /*if dynamif payload width is disabled, use the static payload width and ignore the input*/ + payload_width = current_payload_width; + + nrf24_SPI(SPI_ON); /*sending the read payload command to nrf24l01+*/ + SPI_command = R_RX_PAYLOAD; + SPI_send_command(SPI_command); + + for (; payload_width; payload_width--) + { + SPI_command = NOP_CMD; + *payload = SPI_send_command(SPI_command); + payload++; + } + nrf24_SPI(SPI_OFF); + nrf24_read(FIFO_STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*in order to check the RX_EMPTY flag*/ + if(register_current_value & (1 << RX_EMPTY)) /*if the RX FIFO is empty, reset the RX_DR flag inside STATUS register*/ + { + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << RX_DR); + nrf24_write(STATUS_ADDRESS, ®ister_new_value, 1, CLOSE); + } + return OPERATION_DONE; + } + else + { + return RECEIVE_FIFO_EMPTY; + } + } + else + return OPERATION_ERROR; +} + +/*function which uses TX_FLUSH or RX_FLUSH command to flush the fifo buffers. if successful, output is OPERATION_DONE. + if not successful (wrong input or wrong mode of operation) output will be OPERATION_ERROR*/ +uint8_t nrf24_flush(uint8_t fifo_select) +{ + switch (fifo_select) + { + case TX_BUFFER: + if (current_mode == PTX) + { + nrf24_SPI(SPI_ON); + SPI_command = FLUSH_TX; + SPI_send_command(SPI_command); + nrf24_SPI(SPI_OFF); + return OPERATION_DONE; + } + else + return OPERATION_ERROR; + case RX_BUFFER: + if (current_mode == PRX) + { + nrf24_SPI(SPI_ON); + SPI_command = FLUSH_RX; + SPI_send_command(SPI_command); + nrf24_SPI(SPI_OFF); + return OPERATION_DONE; + } + else + return OPERATION_ERROR; + default: + return OPERATION_ERROR; + } +} + +/*must be called atleast once, which happens with calling nrf24_device function*/ +void nrf24_reset() +{ + reset_flag = RESET; + nrf24_CE(CE_OFF); + register_new_value = CONFIG_REGISTER_DEFAULT; + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = EN_AA_REGISTER_DEFAULT; + nrf24_write(EN_AA_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = EN_RXADDR_REGISTER_DEFAULT; + nrf24_write(EN_RXADDR_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = SETUP_AW_REGISTER_DEFAULT; + nrf24_write(SETUP_AW_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = RF_CH_REGISTER_DEFAULT; + nrf24_write(RF_CH_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = RF_SETUP_REGISTER_DEFAULT; + nrf24_write(RF_SETUP_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = STATUS_REGISTER_DEFAULT; + nrf24_write(STATUS_ADDRESS, ®ister_new_value, 1, CLOSE); + + nrf24_mode(PTX); + nrf24_flush(TX_BUFFER); + nrf24_mode(PRX); + nrf24_flush(RX_BUFFER); + + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << RX_DR) | (1 << TX_DS) | (1 << MAX_RT); + nrf24_write(STATUS_ADDRESS, ®ister_new_value, 1, CLOSE); + + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + nrf24_crc_configuration(ENABLE, 1); + nrf24_address_width(ADDRESS_WIDTH_DEFAULT); + nrf24_rf_datarate(RF_DATARATE_DEFAULT); + nrf24_rf_power(RF_PWR_DEFAULT); + nrf24_rf_channel(RF_CHANNEL_DEFAULT); + nrf24_datapipe_enable(NUMBER_OF_DP_DEFAULT); + /*nrf24_datapipe_address_configuration();*/ + /*nrf24_datapipe_ptx(1);*/ + nrf24_prx_static_payload_width(STATIC_PAYLOAD_WIDTH_DEFAULT, NUMBER_OF_DP_DEFAULT); + nrf24_automatic_retransmit_setup(RETRANSMIT_DELAY_DEFAULT, RETRANSMIT_COUNT_DEFAULT); + nrf24_auto_acknowledge_datapipe(NUMBER_OF_DP_DEFAULT); + nrf24_auto_acknowledge_datapipe(0); + nrf24_dynamic_payload(NRF24_en_dynamic_payload, NUMBER_OF_DP_DEFAULT); + nrf24_payload_without_ack(NRF24_en_no_ack); + nrf24_payload_with_ack(NRF24_en_ack); +} + +/*used by firmware to set the nrf24 mode in TRANSMITTER, RECEIVER, POWER_SAVING or TURN_OFF states, and reseting the device + if it has not been done yet. This is the initializer, and everything starts by calling nrf24_device first.It has a higher + level of abstraction than nrf24_mode and must be used by user*/ +void nrf24_device(uint8_t device_mode, uint8_t reset_state) +{ + SPI_Initializer(); + pinout_Initializer(); + delay_function(STARTUP_DELAY); + + if ((reset_state == RESET) || (reset_flag == 0)) + { + nrf24_reset(); + } + + switch (device_mode) + { + case TRANSMITTER: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, DISABLE, DISABLE); /*disabling tx interrupt mask*/ + nrf24_mode(PTX); + break; + case RECEIVER: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(DISABLE, ENABLE, ENABLE); /*disabling rx interrupt mask*/ + nrf24_mode(PRX); + delay_function(PRX_MODE_DELAY); /*100ms for PRX mode*/ + break; + case POWER_SAVING: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + nrf24_mode(STANDBYI); + break; + case TURN_OFF: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + break; + default: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + break; + } +} + +/*setting automatic retransmit delay time and maximum number of retransmits*/ +void nrf24_automatic_retransmit_setup(uint16_t delay_time, uint8_t retransmit_count) +{ + register_new_value = 0x00; + for (; (delay_time > 250) && (register_new_value < 0X0F); delay_time -= 250) + register_new_value++; + register_new_value <<= ARD_0; + if ((retransmit_count > 0) && (retransmit_count < 16)) + register_new_value |= retransmit_count; + else + register_new_value |= 0; + nrf24_write(SETUP_RETR_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*setting auto acknoledgement on datapipes*/ +void nrf24_auto_acknowledge_datapipe(uint8_t datapipe) +{ + if (datapipe < 7) + register_new_value = (1 << datapipe); + nrf24_write(EN_AA_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*turns on or off the dynamic payload width capability*/ +void nrf24_dynamic_payload(uint8_t state, uint8_t datapipe) +{ + nrf24_auto_acknowledge_datapipe(datapipe); /*setting auto acknowledgment before setting dynamic payload*/ + nrf24_auto_acknowledge_datapipe(0); + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + if (state == ENABLE) + { + register_new_value = register_current_value | (1 << EN_DPL); /*EN_DPL bit turns dynamic payload width on or off on all datapipes*/ + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + if (datapipe < 7) + register_new_value = (1 << datapipe) - 1; /*turning on dynamic payload width on chosen datapipes, using DYNPD register*/ + nrf24_write(DYNPD_ADDRESS, ®ister_new_value, 1, CLOSE); + NRF24_en_dynamic_payload = ENABLE; + } + else + { + register_new_value = register_current_value & (~(1 << EN_DPL)); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + NRF24_en_dynamic_payload = DISABLE; + } +} + +/*function to enable or disable sending without acknowledge. + if disabled, TX must send a payload with ACK-request and receiver must be able to answer it. + manipulates EN_DYN_ACK inside FEATURE*/ +void nrf24_payload_without_ack(uint8_t state) +{ + if (state == ENABLE) + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << EN_DYN_ACK); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + } + else + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value & (~(1 << EN_DYN_ACK)); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + } +} + +/*function to enable or disable sending with acknowledge. + if disabled, the payload can be sent only without ACK-request. + manipulates EN_ACK_PAY and EN_DPL inside FEATURE as Dynamic Payload Length is required.*/ +void nrf24_payload_with_ack(uint8_t state) +{ + if (state == ENABLE) + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << EN_ACK_PAY) | (1 << EN_DPL); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + nrf24_read(DYNPD_ADDRESS, ®ister_current_value, 1, CLOSE); + // enable dynamic payload for all pipes + register_new_value = register_current_value | 0b111111; + nrf24_write(DYNPD_ADDRESS, ®ister_new_value, 1, CLOSE); + } + else + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value & (~((1 << EN_ACK_PAY) | (1 << EN_DPL))); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + } +} + +/*on nrf24l01+ there is only one address for PTX device which must be the same as PRX data pipe address 0*/ +void nrf24_datapipe_ptx(uint8_t datapipe_number) +{ + nrf24_write(TX_ADDR_ADDRESS, &datapipe_address[datapipe_number - 1][0], current_address_width, CLOSE); +} + +/*setting the 6 datapipe addresses using the datapipe_address[][]*/ +void nrf24_datapipe_address_configuration() +{ + uint8_t address = RX_ADDR_P0_ADDRESS; + for (uint8_t counter = 0; counter < 6; counter++) + { + nrf24_write(address, &datapipe_address[counter][0], current_address_width, CLOSE); + address++; + } +} + +/*function to change static payload width, from 1 to 32 bytes in each payload*/ +void nrf24_prx_static_payload_width(uint8_t static_payload_width, uint8_t number_of_datapipes) +{ + for (uint8_t address = RX_PW_P0_ADDRESS; number_of_datapipes; number_of_datapipes--) + { + nrf24_write(address, &static_payload_width, 1, CLOSE); + address++; + } + current_payload_width = static_payload_width; +} + +/*datapipes are turned on and off using EN_RXADDR register, PRX datapipe addresses are located in RX_ADDR_Pn, TX address is located inside TX_ADDR*/ +void nrf24_datapipe_enable(uint8_t datapipe) +{ + nrf24_read(EN_RXADDR_ADDRESS, ®ister_current_value, 1, CLOSE); + if (NRF24_en_ack) { + register_current_value |= (1 << ERX_P0); + } + register_new_value = register_current_value | (1 << datapipe); + nrf24_write(EN_RXADDR_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*function to set the nrf24l01+ address width, from 3 to 5 bytes*/ +void nrf24_address_width(uint8_t address_width) +{ + if ((address_width <= 5) && (address_width >= 3)) + { + write_pointer = address_width - 2; + } + else + { + write_pointer = 3; + } + nrf24_write(SETUP_AW_ADDRESS, &write_pointer, 1, CLOSE); /*5 bytes is the maximum address width available*/ + current_address_width = address_width; +} + +/*datarate settings, you can choose between 2mbps, 1mbps, 250kbps*/ +void nrf24_rf_datarate(uint16_t rf_datarate) +{ + nrf24_read(RF_SETUP_ADDRESS, ®ister_current_value, 1, CLOSE); + register_current_value &= ~((1 << RF_DR_LOW) | (1 << RF_DR_HIGH)); + switch (rf_datarate) + { + case 2000: + register_new_value = register_current_value | (1 << RF_DR_HIGH); + break; + case 1000: + register_new_value = register_current_value; + break; + case 250: + register_new_value = register_current_value | (1 << RF_DR_LOW); + break; + default: + register_new_value = register_current_value; + break; + } + nrf24_write(RF_SETUP_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*nrf24l01+ RF power settings. 0dbm, -6dbm, -12dbm, -18dbm*/ +void nrf24_rf_power(uint8_t rf_power) +{ + nrf24_read(RF_SETUP_ADDRESS, ®ister_current_value, 1, CLOSE); + register_current_value &= ~((1 << RF_PWR_1) | (1 << RF_PWR_0)); + switch (rf_power) + { + case 0: + register_new_value = register_current_value | ((1 << RF_PWR_1) | (1 << RF_PWR_0)); + break; + case 6: + register_new_value = register_current_value | (1 << RF_PWR_1); + break; + case 12: + register_new_value = register_current_value | (1 << RF_PWR_0); + break; + case 18: + register_new_value = register_current_value; + break; + default: + register_new_value = register_current_value | (1 << RF_PWR_1); + break; + } + nrf24_write(RF_SETUP_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*read whether the current channel is busy (has traffic), needs to be called from RX mode*/ +uint8_t nrf24_rf_channel_read_busy(uint8_t rf_channel) +{ + uint8_t signals_detected; + nrf24_read(RPD_REG_ADDRESS, &signals_detected, 1, CLOSE); + if (signals_detected) { + return CHANNEL_BUSY; + } + else { + return CHANNEL_CLEAR; + } +} + +/*test whether a channel is busy (has traffic), waiting for ms_to_test*/ +uint8_t nrf24_rf_channel_test_busy(uint8_t rf_channel, uint16_t ms_to_test) +{ + if ((rf_channel <= 125) && (rf_channel >= 1)) + { + // back up old channel + uint8_t previous_channel; + nrf24_read(RF_CH_ADDRESS, &previous_channel, 1, CLOSE); + // back up old mode + uint8_t previous_mode = current_mode; + // switch to new channel + nrf24_rf_channel(rf_channel); + // switch to RX, Received Power Detector is set to 0 and begins sampling + if (previous_mode != PRX) { + nrf24_mode(PRX); + } + // wait at least 1 ms before declaring channel clear + delay_function(1 > ms_to_test ? 1 : ms_to_test); + // Received Power Detector latches to 1 if there was a signal >-64dBm for at least 40 uS consecutively since RX mode was enabled + uint8_t signals_detected = nrf24_rf_channel_read_busy(rf_channel); + // switch back to old channel + nrf24_rf_channel(previous_channel); + // switch back to old mode + if (previous_mode != PRX) { + nrf24_mode(previous_mode); + } + if (signals_detected) { + return CHANNEL_BUSY; + } + else { + return CHANNEL_CLEAR; + } + } + else + { + return CHANNEL_BUSY; + } +} + +/*nrf24l01+ RF channel selection, from 1 to 125*/ +void nrf24_rf_channel(uint8_t rf_channel) +{ + if ((rf_channel <= 125) && (rf_channel >= 1)) + { + uint8_t write_pointer = rf_channel; + nrf24_write(RF_CH_ADDRESS, &write_pointer, 1, CLOSE); + } + else + { + uint8_t write_pointer = 1; + nrf24_write(RF_CH_ADDRESS, &write_pointer, 1, CLOSE); + } +} + +/*interrupt mask settings. 3 seperate masks for RX, TX, and RT (maximum numbers of retransmission reached*/ +void nrf24_interrupt_mask(uint8_t rx_mask, uint8_t tx_mask, uint8_t max_rt_mask) +{ + nrf24_read(CONFIG_ADDRESS, ®ister_current_value, 1, CLOSE); + if (rx_mask) + register_new_value = (register_current_value) | (1 << MASK_RX_DR); + else + register_new_value &= (~(1 << MASK_RX_DR)); + if (tx_mask) + register_new_value |= (1 << MASK_TX_DS); + else + register_new_value &= (~(1 << MASK_TX_DS)); + if (max_rt_mask) + register_new_value |= (1 << MASK_MAX_RT); + else + register_new_value &= (~(1 << MASK_MAX_RT)); + + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*enabling or disabling crc in payload; setting crc encoding scheme between 1 or 2 bytes*/ +void nrf24_crc_configuration(uint8_t crc_enable, uint8_t crc_encoding_scheme) +{ + nrf24_read(CONFIG_ADDRESS, ®ister_current_value, 1, CLOSE); + if (crc_enable) + register_new_value = (register_current_value) | (1 << EN_CRC); + else + register_new_value &= (~(1 << EN_CRC)); + if (crc_encoding_scheme == 2) + register_new_value |= (1 << CRCO); + else + register_new_value &= (~(1 << CRCO)); + + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*mode selector: power down, standby i, standby ii, ptx, prx. used by nrf24_device function*/ +void nrf24_mode(uint8_t mode) +{ + nrf24_read(CONFIG_ADDRESS, ®ister_current_value, 1, CLOSE); + switch (mode) + { + case POWER_DOWN: + nrf24_CE(CE_OFF); + register_new_value = (register_current_value) & (~(1 << PWR_UP)); + delay_function(POWER_DOWN_DELAY); + break; + case STANDBYI: /*standby I is defined by 'PWR_UP = 1' and 'CE pin LOW'*/ + nrf24_CE(CE_OFF); + register_new_value = (register_current_value) | (1 << PWR_UP); + delay_function(STANDBYI_DELAY); + break; + case STANDBYII: /*standby ii is related to a ptx device*/ + nrf24_CE(CE_ON); + register_new_value = ((register_current_value) | (1 << PWR_UP)) & (~(1 << PRIM_RX)); + delay_function(STANDBYI_DELAY); + break; + case PTX: + nrf24_CE(CE_ON); + register_new_value = ((register_current_value) | (1 << PWR_UP)) & (~(1 << PRIM_RX)); + delay_function(STANDBYI_DELAY); + break; + case PRX: + nrf24_CE(CE_ON); + register_new_value = (register_current_value) | (1 << PWR_UP) | (1 << PRIM_RX); + delay_function(STANDBYI_DELAY); + break; + default: + nrf24_CE(CE_OFF); + register_new_value = (register_current_value) & (~(1 << PWR_UP)); + delay_function(POWER_DOWN_DELAY); + break; + } + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); + current_mode = mode; +} + +/*reads the number of bytes (data_length) from the register in nrf24l01+ (address) and stores them inside an array (value), + then closes the spi connection (spi_state = CLOSE) or leaves it open (spi_state = OPEN)*/ +void nrf24_read(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state) +{ + nrf24_SPI(SPI_ON); + SPI_command = R_REGISTER | address; /*in order to read CONFIG, then change one bit*/ + SPI_send_command(SPI_command); + SPI_command = NOP_CMD; + for (; data_length ; data_length--) + { + *value = SPI_send_command(SPI_command); + value++; + } + if (spi_state == CLOSE) + nrf24_SPI(SPI_OFF); +} + +/*writes the number of bytes (data_length) from an array (value) inside registers in nrf24l01+ (address), + then closes the spi connection (spi_state = CLOSE) or leaves it open (spi_state = OPEN)*/ +void nrf24_write(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state) +{ + nrf24_SPI(SPI_ON); + SPI_command = W_REGISTER | address; /*in order to read CONFIG, then change one bit*/ + SPI_send_command(SPI_command); + for (; data_length ; data_length--) + { + SPI_command = *value; + value++; + SPI_send_command(SPI_command); + } + if (spi_state == CLOSE) + nrf24_SPI(SPI_OFF); +} diff --git a/examples/spi_24L01_rx/nrf24l01.h b/examples/spi_24L01_rx/nrf24l01.h new file mode 100644 index 0000000000000000000000000000000000000000..f3552406a2500078ccae4c6495cf957161efc398 --- /dev/null +++ b/examples/spi_24L01_rx/nrf24l01.h @@ -0,0 +1,219 @@ +#ifndef NRF24L01_H +#define NRF24L01_H +/*nrf24l01: MSbit to LSbit, LSbyte to MSbyte*/ +#include <stdio.h> +#include <stdint.h> + +#define STARTUP_DELAY 150 /*in milliseconds*/ +#define POWER_DOWN_DELAY 2 +#define STANDBYI_DELAY 2 +#define PRX_MODE_DELAY 100 +#define ADDRESS_WIDTH_DEFAULT 5 /*address width in bytes, for default value*/ +#define RF_CHANNEL_DEFAULT 32 +#define RF_DATARATE_DEFAULT 1000 /*250, 1000, 2000*/ +#define RF_PWR_DEFAULT 6 /*0, -6, -12, -18*/ +#define STATIC_PAYLOAD_WIDTH_DEFAULT 1 /*for static payload mode, configurable between 1 and 32 bytes for PRX device ONLY (RX_PW_Pn, n for data pipe n)(no register for payload length in PTX device)*/ +#define NUMBER_OF_DP_DEFAULT 1 /*number of datapipes, 1 to 6*/ +#define RETRANSMIT_DELAY_DEFAULT 500 /*in uS*/ +#define RETRANSMIT_COUNT_DEFAULT 3 + +#define OPEN 1 +#define CLOSE 0 +#define ENABLE 1 +#define DISABLE 0 +#define SPI_OFF 1 +#define SPI_ON 0 +#define CE_OFF 0 +#define CE_ON 1 + +#define CONFIG_REGISTER_DEFAULT 0X08 +#define EN_AA_REGISTER_DEFAULT 0X3F +#define EN_RXADDR_REGISTER_DEFAULT 0X00 +#define SETUP_AW_REGISTER_DEFAULT 0X03 +#define SETUP_RETR_REGISTER_DEFAULT 0X03 +#define RF_CH_REGISTER_DEFAULT 0X02 +#define RF_SETUP_REGISTER_DEFAULT 0X0E +#define STATUS_REGISTER_DEFAULT 0X0E +#define MAXIMUM_NUMBER_OF_DATAPIPES 6 + +#define POWER_DOWN 0X00 +#define STANDBYI 0X01 +#define STANDBYII 0X02 +#define PTX 0X03 +#define PRX 0X04 +#define DEVICE_NOT_INITIALIZED 0X05 + +#define TRANSMITTER 0X00 +#define RECEIVER 0X01 +#define POWER_SAVING 0X02 +#define TURN_OFF 0X03 + +#define RESET 1 +#define NO_RESET 0 +#define NO_ACK_MODE 1 +#define ACK_MODE 0 +#define TRANSMIT_BEGIN 1 +#define TRANSMIT_FAIL 0 +#define TRANSMIT_IN_PROGRESS 0 +#define TRANSMIT_DONE 1 +#define TRANSMIT_FAILED 0XFF +#define OPERATION_DONE 1 +#define OPERATION_ERROR 0 +#define RECEIVE_FIFO_EMPTY 2 +#define TX_BUFFER 1 +#define RX_BUFFER 0 +// return states for nrf24_rf_channel_test_busy +#define CHANNEL_CLEAR 0 +#define CHANNEL_BUSY 1 + +/*bits definition section*/ +#define MASK_RX_DR 6 /*mask interrupt caused by RX_DR: 1 interrupt not reflected on IRQ pin (IRQ is active low), inside CONFIG register*/ +#define MASK_TX_DS 5 /*mask interrupt caused by TX_DS: 1 interrupt not reflected on IRQ pin (IRQ is active low), inside CONFIG register*/ +#define MASK_MAX_RT 4 /*mask interrupt caused by MAX_RT means maximum number of retransmissions reached: 1 interrupt not reflected on IRQ pin (IRQ is active low), inside CONFIG register*/ +#define EN_CRC 3 /*enale CRC, forced high if one of the bits in EN_AA is high, inside CONFIG register*/ +#define CRCO 2 /*CRC encoding scheme, 0 is 1 byte, 1 is 2 bytes, inside CONFIG register*/ +#define PWR_UP 1 /*1 is power up, inside CONFIG register*/ +#define PRIM_RX 0 /*RX/TX control, 1: PRX, inside CONFIG register*/ +#define ENAA_P5 5 /*enable auto acknowledgement data pipe 5*/ +#define ENAA_P4 4 +#define ENAA_P3 3 +#define ENAA_P2 2 +#define ENAA_P1 1 +#define ENAA_P0 0 +#define ERX_P5 5 /*part of EN_RXADDR, enable data pipe 5*/ +#define ERX_P4 4 +#define ERX_P3 3 +#define ERX_P2 2 +#define ERX_P1 1 +#define ERX_P0 0 +#define AW_1 1 /*RX/TX address field width, 00 illegal, 01 3 bytes, 10 4 bytes, 11 5 bytes*/ +#define AW_0 0 +#define ARD_3 7 /*auto retransmit delay, 0000 250us, 0001 500us ...> 1111 4000us*/ +#define ARD_2 6 +#define ARD_1 5 +#define ARD_0 4 +#define ARC_3 3 /*auto retransmit count, 0000 retransmit deisabled, 1111 up to 15 retransmit on failure of AA. (inside SETUP_RETR register)*/ +#define ARC_2 2 +#define ARC_1 1 +#define ARC_0 0 +#define RF_CH_6 6 /*sets the frequencvy channel nRF24L01+ operates on*/ +#define RF_CH_5 5 +#define RF_CH_4 4 +#define RF_CH_3 3 +#define RF_CH_2 2 +#define RF_CH_1 1 +#define RF_CH_0 0 +#define CONT_WAVE 7 /*enables continuous carrier transmit when high*/ +#define RF_DR_LOW 5 /*sets the RF data rate to 250kbps*/ +#define PLL_LOCK 4 /*force PLL lock signal. used for testing ONLY*/ +#define RF_DR_HIGH 3 /*select between high speed data rates and works ONLY when RF_DR_LOW is 0. 0 for 1Mbps, 1 for 2Mbps*/ +#define RF_PWR_1 2 +#define RF_PWR_0 1 +#define RX_DR 6 /*IRQ for new packet in RX FIFO (newly received)*/ +#define TX_DS 5 /*IRQ for ACK received in TX mode*/ +#define MAX_RT 4 +#define RX_P_NO_2 3 +#define RX_P_NO_1 2 +#define RX_P_NO_0 1 +#define PLOS_CNT_3 7 /*inside OBSERVE_TX register, counts the total number of retransmissions since last channel change. reset by writing to RF_CH*/ +#define PLOS_CNT_2 6 +#define PLOS_CNT_1 5 +#define PLOS_CNT_0 4 +#define ARC_CNT_3 3 /*inside OBSERVE_TX register, counts the number of retransmissions for current transaction. reset by initiating new transaction*/ +#define ARC_CNT_2 2 +#define ARC_CNT_1 1 +#define ARC_CNT_0 0 +#define RPD 0 /*received power detector, if received power is less than -64dbm, RPD = 0*/ +#define TX_REUSE 6 +#define TX_FULL 5 +#define TX_EMPTY 4 +#define RX_FULL 1 +#define RX_EMPTY 0 +#define DPL_P5 5 +#define DPL_P4 4 +#define DPL_P3 3 +#define DPL_P2 2 +#define DPL_P1 1 +#define DPL_P0 0 /*must be set on PTX in dynamic payload length mode*/ +#define EN_DPL 2 /*set to enable dynamic payload length*/ +#define EN_ACK_PAY 1 /*used to enable auto acknowledgement with payload in PRX (inside FEATURE register)*/ +#define EN_DYN_ACK 0 /**/ + +/*registers definition section*/ +#define CONFIG_ADDRESS 0X00 +#define EN_AA_ADDRESS 0X01 /*enable auto acknowledgement feature*/ +#define EN_RXADDR_ADDRESS 0X02 /*register containing bits to enable 6 data pipes individually*/ +#define SETUP_AW_ADDRESS 0X03 /*address field length is configured in here to be 3, 4 or 5 bytes long*/ +#define SETUP_RETR_ADDRESS 0X04 /*setup ARC bits to configure auto retransmission count*/ +#define RF_CH_ADDRESS 0X05 +#define RF_SETUP_ADDRESS 0X06 +#define STATUS_ADDRESS 0X07 /*contains RX_DR, TX_DS, MAX_RT, RX_P_NO, TX_FULL, send R_REGISTER then NOP to read*/ +#define OBSERVE_TX_ADDRESS 0X08 /*contains ARC_CNT and PLOS_CNT, two counters for retransmission. these counters could be used to assess the network quality*/ +#define RPD_REG_ADDRESS 0X09 +#define RX_ADDR_P0_ADDRESS 0X0A /*the address for PRX device. if a packet contains this address, enhanced shockburst starts validating the packet*/ +#define RX_ADDR_P1_ADDRESS 0X0B /*a total of 6 unique addresses could be assigned to a PRX device (Multiceiver feature)*/ +#define RX_ADDR_P2_ADDRESS 0X0C /*these addresses must NOT be the same*/ +#define RX_ADDR_P3_ADDRESS 0X0D +#define RX_ADDR_P4_ADDRESS 0X0E +#define RX_ADDR_P5_ADDRESS 0X0F +#define TX_ADDR_ADDRESS 0X10 /*40 bits long register, transmit address, used for a PTX device only. configure address legth in SETUP_AW register. set RX_ADDR_P0 equal to this address to handle automatic acknowledge*/ +#define RX_PW_P0_ADDRESS 0X11 /*these registers are for setting the static payload length in static payload length mode (receiver side)*/ +#define RX_PW_P1_ADDRESS 0X12 +#define RX_PW_P2_ADDRESS 0X13 +#define RX_PW_P3_ADDRESS 0X14 +#define RX_PW_P4_ADDRESS 0X15 +#define RX_PW_P5_ADDRESS 0X16 +#define FIFO_STATUS_ADDRESS 0X17 +#define DYNPD_ADDRESS 0X1C /*on receiver side (RX mode), this register must be set to enable dynamic payload length. a PTX in dynamic mode, must have the DYNPD_P0 set*/ +#define FEATURE_ADDRESS 0X1D /*contains the EN_DPL bit to enable dynamic payload length*/ + +/*commands definition section*/ +#define R_REGISTER 0X00 /*read commmand and STATUS registers, 5 bit register map address*/ +#define W_REGISTER 0X20 /*write commmand and STATUS registers, 5 bit register map address, executable in POWER DOWN or STANDBY modes only*/ +#define R_RX_PAYLOAD 0X61 /*read RX payload, 1-32 bytes. read operation starts at byte 0. payload is deleted from FIFO after its read*/ +#define W_TX_PAYLOAD 0XA0 /*write TX payload, starts at byte 0, 1-32 bytes*/ +#define FLUSH_TX 0XE1 /*flush TX FIFO, used in TX mode*/ +#define FLUSH_RX 0XE2 /*flush RX FIFO, used in RX mode*/ +#define REUSE_TX_PL 0XE3 /*used for a PTX device, reuse last transmitted payload for an exact number. alternative to auto retransmission*/ +#define R_RX_PL_WID 0X60 /*command for receiver side, in order to read the payload length in dynamic payload length mode*/ +#define W_ACK_PAYLOAD 0XA0 /*used in RX mode, to write payload in TX FIFO and later transmit the payloads along with ACK packet to PTX, if DPL is enabled*/ +#define W_TX_PAYLOAD_NOACK 0XB0 /*used in TX mode, disables AUTOACK on this specific packet. must be first enabled in FEATURE register by setting the EN_DYN_ACK bit. if used, PTX will not wait for ACK and goes directly to standby I*/ +#define NOP_CMD 0XFF /*might be used to read the status register*/ + +void nrf24_reset(); +void nrf24_device(uint8_t device_mode, uint8_t reset_state); +uint8_t SPI_send_command(uint8_t command); +void pinout_Initializer(); +void SPI_Initializer(); +void nrf24_mode(uint8_t mode); +void nrf24_SPI(uint8_t input); +void nrf24_CE(uint8_t input); +void nrf24_address_width(uint8_t address_width); +uint8_t nrf24_rf_channel_read_busy(uint8_t rf_channel); +uint8_t nrf24_rf_channel_test_busy(uint8_t rf_channel, uint16_t ms_to_test); +void nrf24_rf_channel(uint8_t rf_channel); +void nrf24_rf_power(uint8_t rf_power); +void nrf24_rf_datarate(uint16_t rf_datarate); +void nrf24_read(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state); +void nrf24_write(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state); +void delay_function(uint32_t duration_ms); +void nrf24_crc_configuration(uint8_t crc_enable, uint8_t crc_encoding_scheme); +void nrf24_interrupt_mask(uint8_t rx_mask, uint8_t tx_mask, uint8_t max_rt_mask); +void nrf24_datapipe_enable(uint8_t number_of_datapipes); +void nrf24_prx_static_payload_width(uint8_t static_payload_width, uint8_t number_of_datapipes); +void nrf24_datapipe_address_configuration(); +void nrf24_datapipe_ptx(uint8_t datapipe_number); +void nrf24_automatic_retransmit_setup(uint16_t delay_time, uint8_t retransmit_count); +void nrf24_auto_acknowledge_datapipe(uint8_t datapipe); +void nrf24_payload_without_ack(uint8_t state); +void nrf24_payload_with_ack(uint8_t state); +void nrf24_dynamic_payload(uint8_t state, uint8_t datapipe); +void nrf24_device(uint8_t device_mode, uint8_t reset_state); +void nrf24_send_payload(uint8_t *payload, uint8_t payload_width); +uint8_t nrf24_receive(uint8_t *payload, uint8_t payload_width); +uint8_t nrf24_transmit(uint8_t *payload, uint8_t payload_width, uint8_t acknowledgement_state); +uint8_t nrf24_transmit_status(); +void nrf24_dynamic_ack(uint8_t state); +uint8_t nrf24_flush(uint8_t fifo_select); + +#endif diff --git a/examples/spi_24L01_rx/nrf24l01_low_level.c b/examples/spi_24L01_rx/nrf24l01_low_level.c new file mode 100644 index 0000000000000000000000000000000000000000..811fd078f3cf9457ad9dd90046ae671676cd1541 --- /dev/null +++ b/examples/spi_24L01_rx/nrf24l01_low_level.c @@ -0,0 +1,71 @@ +#define SYSTEM_CORE_CLOCK 48000000 +#define APB_CLOCK SYSTEM_CORE_CLOCK +#include "../../ch32v003fun/ch32v003fun.h" + + +#define CH32V003_SPI_SPEED_HZ 1000000 +#define CH32V003_SPI_DIRECTION_2LINE_TXRX +#define CH32V003_SPI_CLK_MODE_POL0_PHA0 //leading = rising trailing = falling sample on leading default if you're unsure +#define CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL // toggle manually! +#define CH32V003_SPI_IMPLEMENTATION +#include "ch32v003_SPI.h" +#include "nrf24l01.h" + +/*start of low level functions, specific to the mcu and compiler*/ + +/*delay in miliseconds*/ +void delay_function(uint32_t duration_ms) +{ + Delay_Ms(duration_ms); +} + +/*contains all SPI configuations, such as pins and control registers*/ +/*SPI control: master, interrupts disabled, clock polarity low when idle, clock phase falling edge, clock up tp 1 MHz*/ +void SPI_Initializer() +{ + SPI_init(); + SPI_begin_8(); +} + +/*contains all CSN and CE pins gpio configurations, including setting them as gpio outputs and turning SPI off and CE '1'*/ +void pinout_Initializer() +{ + // CSN on PC0 + GPIOC->CFGLR &= ~(0xf<<(4*0)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*0); + // CSN high + GPIOC->BSHR = (1<<0); + // CE on PC4 + GPIOC->CFGLR &= ~(0xf<<(4*4)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4); + // CE HIGH + GPIOC->BSHR = (1<<4); +} + +/*CSN pin manipulation to high or low (SPI on or off)*/ +void nrf24_SPI(uint8_t input) +{ + if (input > 0) { + GPIOC->BSHR = (1<<(0+0)); + } + else { + GPIOC->BSHR = (1<<(16+0)); + } +} + +/*1 byte SPI shift register send and receive routine*/ +uint8_t SPI_send_command(uint8_t command) +{ + return SPI_transfer_8(command); +} + +/*CE pin maniplation to high or low*/ +void nrf24_CE(uint8_t input) +{ + if (input > 0) { + GPIOC->BSHR = (1<<(0+4)); + } + else { + GPIOC->BSHR = (1<<(16+4)); + } +} diff --git a/examples/spi_24L01_rx/spi_24L01_rx.c b/examples/spi_24L01_rx/spi_24L01_rx.c new file mode 100644 index 0000000000000000000000000000000000000000..0b20192b71d7272843f9ab316fbfed82b1f9d9d7 --- /dev/null +++ b/examples/spi_24L01_rx/spi_24L01_rx.c @@ -0,0 +1,136 @@ +/* + * Example for 24L01+ over SPI, using https://github.com/ebrezadev/nRF24L01-C-Driver + * 04-26-2023 recallmenot + */ + +#define SYSTEM_CORE_CLOCK 48000000 +#define APB_CLOCK SYSTEM_CORE_CLOCK + +#include "../../ch32v003fun/ch32v003fun.h" +#include <stdio.h> +#include "nrf24l01.h" + + + +#define TIME_GAP 300 +uint8_t ascending_number = 0; +char txt[16]; + + + +//######### debug fn + +void uint8_to_binary_string(uint8_t value, char* output, int len) { + for (int i = 0; i < len; i++) { + output[len - i - 1] = (value & 1) ? '1' : '0'; + value >>= 1; + } + output[len] = '\0'; +} + + +void print_reg(char* name, uint8_t addr) { + char str[9]; + uint8_t REG; + nrf24_read(addr, ®, 1, CLOSE); + uint8_to_binary_string(REG, str, 8); + printf(" %s register: %s\n\r", name, str); +} + + +void print_debug() { + print_reg("FEATURE ", FEATURE_ADDRESS); + print_reg("TX OBSERVE ", OBSERVE_TX_ADDRESS); + print_reg("STATUS ", STATUS_ADDRESS); + print_reg("RX_PW_P0 ADDR", RX_ADDR_P0_ADDRESS); + print_reg("TX ADDR ", TX_ADDR_ADDRESS); + print_reg("EN_AA ", EN_AA_ADDRESS); + print_reg("EN_RXADDR ", EN_RXADDR_ADDRESS); +} + + + +//######### LED fn + +// wire PD4 to LED1 on the dev board (-) +inline void led_on() { + GPIOD->BSHR = 1<<(16+4); +} + +inline void led_off() { + GPIOD->BSHR = 1<<4; +} + + + +//######### RX fn + +uint8_t recvnumber() { + return nrf24_receive(&ascending_number, 1); +} + +uint8_t recvstr() { + return nrf24_receive((uint8_t*)&txt, 16); +} + +void receive() { + // to switch between sending an uint8_t and a 16-byte-char-array, just uncomment one of these two: + //uint8_t result = recvnumber(); + uint8_t result = recvstr(); + // also uncomment the corresponding one for case OPERATION_DONE + + //print_debug(); + switch(result) { + case OPERATION_ERROR: + printf("EEE RX operation error\n\r"); + break; + case RECEIVE_FIFO_EMPTY: + printf(" RX empty\n\r"); + //printf(" RX empty, last received: %u", ascending_number); + break; + case OPERATION_DONE: + led_on(); + // pick one of these two: + //printf("*** RX success, received: %u\n\r", ascending_number); + printf("*** RX success, received: %s\n\r", txt); + led_off(); + break; + } + Delay_Ms(TIME_GAP); +} + + + +//######### MAIN + +int main() +{ + SystemInit48HSI(); + + // start serial @ default 115200bps + SetupUART( UART_BRR ); + Delay_Ms( 100 ); + + printf("\r\r\n\nspi_24L01_RX\n\r"); + + printf("initializing radio as RX..."); + nrf24_device(RECEIVER, RESET); + nrf24_rf_power(18); //default TX power is -6dB, pretty strong, reduce to -18dBm for one room (ACK = TX) + //nrf24_automatic_retransmit_setup(RETRANSMIT_DELAY_DEFAULT, 0); + printf("done.\n\r"); + + print_debug(); + + // GPIO D0 Push-Pull for RX notification + RCC->APB2PCENR |= RCC_APB2Periph_GPIOD; + GPIOD->CFGLR &= ~(0xf<<(4*4)); + GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4); + + Delay_Ms(1000); + + printf("looping...\n\r"); + while(1) + { + receive(); + } +} diff --git a/examples/spi_24L01_tx/Makefile b/examples/spi_24L01_tx/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..6ad3d51350ca002205f5ad25ed107cfdf4086b2a --- /dev/null +++ b/examples/spi_24L01_tx/Makefile @@ -0,0 +1,11 @@ +all : flash + +TARGET:=spi_24L01_tx +ADDITIONAL_C_FILES+=nrf24l01_low_level.c nrf24l01.c + +CFLAGS+=-DSTDOUT_UART + +include ../../ch32v003fun/ch32v003fun.mk + +flash : cv_flash +clean : cv_clean diff --git a/examples/spi_24L01_tx/NRF24L01_TX_Arduino/NRF24L01_TX_Arduino.ino b/examples/spi_24L01_tx/NRF24L01_TX_Arduino/NRF24L01_TX_Arduino.ino new file mode 100644 index 0000000000000000000000000000000000000000..5ccd6804c8a6415670fec30539e3b32ad867c505 --- /dev/null +++ b/examples/spi_24L01_tx/NRF24L01_TX_Arduino/NRF24L01_TX_Arduino.ino @@ -0,0 +1,140 @@ +//transmitter code example, transmits an ascending number every TIME_GAP milliseconds in NO_ACK_MODE. can be switched to ACK_MODE +//payload length of 1 or 16 byte, 1Mbps datarate, -6 dbm rf transmit power, channel 32 of 125 + +extern "C"{ + #include "nrf24l01.h" +} + + + +#define TIME_GAP 1000 +uint8_t ascending_number = 0; +char txt[16]; + + + +//######### debug fn + +void uint8_to_binary_string(uint8_t value, char* output, int len) { + for (int i = 0; i < len; i++) { + output[len - i - 1] = (value & 1) ? '1' : '0'; + value >>= 1; + } + output[len] = '\0'; +} + +void print_reg(char* name, uint8_t addr) { + Serial.print(" "); + Serial.print(name); + Serial.print(" register:"); + char str[9]; + uint8_t REG; + nrf24_read(addr, ®, 1, CLOSE); + uint8_to_binary_string(REG, str, 8); + Serial.println(str); +} + +void print_debug() { + print_reg("FEATURE ", FEATURE_ADDRESS); + print_reg("TX OBSERVE ", OBSERVE_TX_ADDRESS); + print_reg("STATUS ", STATUS_ADDRESS); + print_reg("RX_PW_P0 ADDR", RX_ADDR_P0_ADDRESS); + print_reg("TX ADDR ", TX_ADDR_ADDRESS); + print_reg("EN_AA ", EN_AA_ADDRESS); + print_reg("EN_RXADDR ", EN_RXADDR_ADDRESS); +} + + + +//######### LED fn + +//LED_BUILTIN is pin 13 is SCK of SPI, already using that +void led_on() { + digitalWrite(4, HIGH); +} + +void led_off() { + digitalWrite(4, LOW); +} + + +//######### TX fn + +uint8_t sendnumber() { + return nrf24_transmit(&ascending_number, 1, ACK_MODE); +} + +uint8_t sendstr() { + sprintf(txt, "Hello, %u", ascending_number); + return nrf24_transmit(reinterpret_cast<uint8_t*>(&txt), 16, ACK_MODE); +} + +void send() { + //uint8_t tx_cmd_status = sendnumber(); + uint8_t tx_cmd_status = sendstr(); + switch (tx_cmd_status) { + case TRANSMIT_BEGIN: + led_on(); + Serial.println("*** sending package"); + break; + case TRANSMIT_FAIL: + Serial.println("EEE unable to send package"); + break; + } + + delay(50); // give the nRF some time to send + print_debug(); + + switch (nrf24_transmit_status()) { + case TRANSMIT_DONE: + Serial.print("*OK sent: "); + char str[4]; + sprintf(str, "%u", ascending_number); + Serial.println(str); //payload is sent without acknowledge successfully + led_off(); + break; + case TRANSMIT_FAILED: + Serial.println("EEE no ACK received!!"); + break; + case TRANSMIT_IN_PROGRESS: + Serial.println("EEE still transmitting???"); + break; + } +} + + + +//######### MAIN + +void setup() +{ + Serial.begin(115200); + + Serial.print("\r\r\n\nspi_24L01_TX\n\r"); + + Serial.print("initializing radio as TX..."); + nrf24_device(TRANSMITTER, RESET); //initializing nrf24l01+ as a transmitter using one simple function + nrf24_rf_power(18); //default TX power is -6dB, pretty strong, reduce to -18dBm for one room + Serial.print("done.\n\r"); + + pinMode(4, OUTPUT); + + print_debug(); + + delay(1000); + + Serial.print("entering loop\n\r"); +} + +void loop() +{ + delay(TIME_GAP); + send(); + + ascending_number++; + + char str[4]; + Serial.print("*** next number: "); + sprintf(str, "%u", ascending_number); + Serial.println(str); +} diff --git a/examples/spi_24L01_tx/NRF24L01_TX_Arduino/nrf24l01.c b/examples/spi_24L01_tx/NRF24L01_TX_Arduino/nrf24l01.c new file mode 100644 index 0000000000000000000000000000000000000000..9f3b0e34c9b3849e16b42e55a9cb245b32e55c83 --- /dev/null +++ b/examples/spi_24L01_tx/NRF24L01_TX_Arduino/nrf24l01.c @@ -0,0 +1,614 @@ +#include "nrf24l01.h" + +/*nRF24L01+ features, enable / disable as needed*/ +static uint8_t NRF24_en_ack = ENABLE; +static uint8_t NRF24_en_no_ack = ENABLE; +static uint8_t NRF24_en_dynamic_payload = ENABLE; + +/*global variables related to this file*/ +static uint8_t SPI_command; /*1 byte spi command*/ +static uint8_t register_current_value; /*in order to change some bits of internal registers or to check their content*/ +static uint8_t register_new_value; /*used to write new value to nrf24l01+ registers*/ +static uint8_t write_pointer; /*used as an input for read and write functions (as a pointer)*/ +static uint8_t current_address_width; /*current address width for receiver pipe addresses (up to 6 pipes), from 3 to 5 bytes*/ +static uint8_t reset_flag = 0; /*reset flag lets the software know if the nrf24l01+ has ever been reset or not*/ +static uint8_t current_mode = DEVICE_NOT_INITIALIZED; /*current mode of operation: DEVICE_NOT_INITIALIZED, PRX, PTX, STANDBYI, STANDBYII, POWER_DOWN*/ +static uint8_t current_payload_width; /*payload width could be from 1 to 32 bytes, in either dynamic or static forms*/ +static uint8_t current_acknowledgement_state = NO_ACK_MODE; + +/*2 dimensional array of pipe addresses (5 byte address width) by default. you can change addresses using a new array later. + Pipe 1 address could be anything. pipe 3 to 6 addresses share the first 4 bytes with pipe 2 and only differ in byte 5*/ +uint8_t datapipe_address[MAXIMUM_NUMBER_OF_DATAPIPES][ADDRESS_WIDTH_DEFAULT] = { + {0X20, 0XC3, 0XC2, 0XC1, 0XA0}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA1}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA2}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA3}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA4}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA5} +}; + +/*function for PTX device to transmit 1 to 32 bytes of data, used for both dynamic payload length + and static payload length methods. acknowledgemet state could be NO_ACK_MODE or ACK_MODE*/ +uint8_t nrf24_transmit(uint8_t *payload, uint8_t payload_width, uint8_t acknowledgement_state) +{ + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*in order to check TX_FIFO status*/ + if ((!(register_current_value & (1 << TX_FULL))) && (current_mode == PTX)) + { + current_acknowledgement_state = acknowledgement_state; /*setting the acknowledgement state to either NO_ACK or ACK, based on input*/ + if (NRF24_en_dynamic_payload == ENABLE) + current_payload_width = payload_width; + nrf24_send_payload(payload, payload_width); /*the actual function to send data*/ + return (TRANSMIT_BEGIN); /*TX FIFO is not full and nrf24l01+ mode is standby ii or ptx*/ + } + else + { + return (TRANSMIT_FAIL); /*TX FIFO full or wrong mode*/ + } +} + +/*used by nrf24_transmit function to send the actual data*/ +void nrf24_send_payload(uint8_t *payload, uint8_t payload_width) +{ + nrf24_SPI(SPI_ON); + if (current_acknowledgement_state == NO_ACK_MODE) + SPI_command = W_TX_PAYLOAD_NOACK; + else + SPI_command = W_TX_PAYLOAD; + SPI_send_command(SPI_command); + for (; payload_width; payload_width--) + { + SPI_command = *payload; + SPI_send_command(SPI_command); + payload++; + } + nrf24_SPI(SPI_OFF); +} + +/*reports back transmit status: TRANSMIT_DONE, TRANSMIT_FAILED (in case of reaching maximum number of retransmits in auto acknowledgement mode) + and TRANSMIT_IN_PROGRESS, if neither flags are set. automatically resets the '1' flags.*/ +uint8_t nrf24_transmit_status() +{ + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*status register is read to check TX_DS flag*/ + if (register_current_value & (1 << TX_DS)) /*if the TX_DS == 1, */ + { + nrf24_write(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*reseting the TX_DS flag. as mentioned by datasheet, writing '1' to a flag resets that flag*/ + return TRANSMIT_DONE; + } + else if (register_current_value & (1 << MAX_RT)) + { + nrf24_write(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*reseting the MAX_RT flag. as mentioned by datasheet, writing '1' to a flag resets that flag*/ + return TRANSMIT_FAILED; + } + else + return TRANSMIT_IN_PROGRESS; +} + +/*the receive function output is used as a polling method to check the received data inside RX FIFOs. +If there is any data available, it will be loaded inside payload array*/ +uint8_t nrf24_receive(uint8_t *payload, uint8_t payload_width) +{ + if (current_mode == PRX) + { + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); + if (register_current_value & (1 << RX_DR)) /*if received data is ready inside RX FIFO*/ + { + if(NRF24_en_dynamic_payload == DISABLE) /*if dynamif payload width is disabled, use the static payload width and ignore the input*/ + payload_width = current_payload_width; + + nrf24_SPI(SPI_ON); /*sending the read payload command to nrf24l01+*/ + SPI_command = R_RX_PAYLOAD; + SPI_send_command(SPI_command); + + for (; payload_width; payload_width--) + { + SPI_command = NOP_CMD; + *payload = SPI_send_command(SPI_command); + payload++; + } + nrf24_SPI(SPI_OFF); + nrf24_read(FIFO_STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*in order to check the RX_EMPTY flag*/ + if(register_current_value & (1 << RX_EMPTY)) /*if the RX FIFO is empty, reset the RX_DR flag inside STATUS register*/ + { + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << RX_DR); + nrf24_write(STATUS_ADDRESS, ®ister_new_value, 1, CLOSE); + } + return OPERATION_DONE; + } + else + { + return RECEIVE_FIFO_EMPTY; + } + } + else + return OPERATION_ERROR; +} + +/*function which uses TX_FLUSH or RX_FLUSH command to flush the fifo buffers. if successful, output is OPERATION_DONE. + if not successful (wrong input or wrong mode of operation) output will be OPERATION_ERROR*/ +uint8_t nrf24_flush(uint8_t fifo_select) +{ + switch (fifo_select) + { + case TX_BUFFER: + if (current_mode == PTX) + { + nrf24_SPI(SPI_ON); + SPI_command = FLUSH_TX; + SPI_send_command(SPI_command); + nrf24_SPI(SPI_OFF); + return OPERATION_DONE; + } + else + return OPERATION_ERROR; + case RX_BUFFER: + if (current_mode == PRX) + { + nrf24_SPI(SPI_ON); + SPI_command = FLUSH_RX; + SPI_send_command(SPI_command); + nrf24_SPI(SPI_OFF); + return OPERATION_DONE; + } + else + return OPERATION_ERROR; + default: + return OPERATION_ERROR; + } +} + +/*must be called atleast once, which happens with calling nrf24_device function*/ +void nrf24_reset() +{ + reset_flag = RESET; + nrf24_CE(CE_OFF); + register_new_value = CONFIG_REGISTER_DEFAULT; + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = EN_AA_REGISTER_DEFAULT; + nrf24_write(EN_AA_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = EN_RXADDR_REGISTER_DEFAULT; + nrf24_write(EN_RXADDR_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = SETUP_AW_REGISTER_DEFAULT; + nrf24_write(SETUP_AW_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = RF_CH_REGISTER_DEFAULT; + nrf24_write(RF_CH_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = RF_SETUP_REGISTER_DEFAULT; + nrf24_write(RF_SETUP_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = STATUS_REGISTER_DEFAULT; + nrf24_write(STATUS_ADDRESS, ®ister_new_value, 1, CLOSE); + + nrf24_mode(PTX); + nrf24_flush(TX_BUFFER); + nrf24_mode(PRX); + nrf24_flush(RX_BUFFER); + + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << RX_DR) | (1 << TX_DS) | (1 << MAX_RT); + nrf24_write(STATUS_ADDRESS, ®ister_new_value, 1, CLOSE); + + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + nrf24_crc_configuration(ENABLE, 1); + nrf24_address_width(ADDRESS_WIDTH_DEFAULT); + nrf24_rf_datarate(RF_DATARATE_DEFAULT); + nrf24_rf_power(RF_PWR_DEFAULT); + nrf24_rf_channel(RF_CHANNEL_DEFAULT); + nrf24_datapipe_enable(NUMBER_OF_DP_DEFAULT); + /*nrf24_datapipe_address_configuration();*/ + /*nrf24_datapipe_ptx(1);*/ + nrf24_prx_static_payload_width(STATIC_PAYLOAD_WIDTH_DEFAULT, NUMBER_OF_DP_DEFAULT); + nrf24_automatic_retransmit_setup(RETRANSMIT_DELAY_DEFAULT, RETRANSMIT_COUNT_DEFAULT); + nrf24_auto_acknowledgment_setup(NUMBER_OF_DP_DEFAULT); + nrf24_dynamic_payload(NRF24_en_dynamic_payload, NUMBER_OF_DP_DEFAULT); + nrf24_payload_without_ack(NRF24_en_no_ack); + nrf24_payload_with_ack(NRF24_en_ack); +} + +/*used by firmware to set the nrf24 mode in TRANSMITTER, RECEIVER, POWER_SAVING or TURN_OFF states, and reseting the device + if it has not been done yet. This is the initializer, and everything starts by calling nrf24_device first.It has a higher + level of abstraction than nrf24_mode and must be used by user*/ +void nrf24_device(uint8_t device_mode, uint8_t reset_state) +{ + SPI_Initializer(); + pinout_Initializer(); + delay_function(STARTUP_DELAY); + + if ((reset_state == RESET) || (reset_flag == 0)) + { + nrf24_reset(); + } + + switch (device_mode) + { + case TRANSMITTER: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, DISABLE, DISABLE); /*disabling tx interrupt mask*/ + nrf24_mode(PTX); + break; + case RECEIVER: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(DISABLE, ENABLE, ENABLE); /*disabling rx interrupt mask*/ + nrf24_mode(PRX); + delay_function(PRX_MODE_DELAY); /*100ms for PRX mode*/ + break; + case POWER_SAVING: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + nrf24_mode(STANDBYI); + break; + case TURN_OFF: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + break; + default: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + break; + } +} + +/*setting automatic retransmit delay time and maximum number of retransmits*/ +void nrf24_automatic_retransmit_setup(uint16_t delay_time, uint8_t retransmit_count) +{ + register_new_value = 0x00; + for (; (delay_time > 250) && (register_new_value < 0X0F); delay_time -= 250) + register_new_value++; + register_new_value <<= ARD_0; + if ((retransmit_count > 0) && (retransmit_count < 16)) + register_new_value |= retransmit_count; + else + register_new_value |= 0; + nrf24_write(SETUP_RETR_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*setting auto acknoledgement on datapipes*/ +void nrf24_auto_acknowledgment_setup(uint8_t datapipe) +{ + if (datapipe < 7) + register_new_value = (1 << datapipe) - 1; + nrf24_write(EN_AA_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*turns on or off the dynamic payload width capability*/ +void nrf24_dynamic_payload(uint8_t state, uint8_t datapipe) +{ + nrf24_auto_acknowledgment_setup(datapipe); /*setting auto acknowledgment before setting dynamic payload*/ + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + if (state == ENABLE) + { + register_new_value = register_current_value | (1 << EN_DPL); /*EN_DPL bit turns dynamic payload width on or off on all datapipes*/ + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + if (datapipe < 7) + register_new_value = (1 << datapipe) - 1; /*turning on dynamic payload width on chosen datapipes, using DYNPD register*/ + nrf24_write(DYNPD_ADDRESS, ®ister_new_value, 1, CLOSE); + NRF24_en_dynamic_payload = ENABLE; + } + else + { + register_new_value = register_current_value & (~(1 << EN_DPL)); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + NRF24_en_dynamic_payload = DISABLE; + } +} + +/*function to enable or disable sending without acknowledge. + if disabled, TX must send a payload with ACK-request and receiver must be able to answer it. + manipulates EN_DYN_ACK inside FEATURE*/ +void nrf24_payload_without_ack(uint8_t state) +{ + if (state == ENABLE) + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << EN_DYN_ACK); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + } + else + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value & (~(1 << EN_DYN_ACK)); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + } +} + +/*function to enable or disable sending with acknowledge. + if disabled, the payload can be sent only without ACK-request. + manipulates EN_ACK_PAY and EN_DPL inside FEATURE as Dynamic Payload Length is required.*/ +void nrf24_payload_with_ack(uint8_t state) +{ + if (state == ENABLE) + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << EN_ACK_PAY) | (1 << EN_DPL); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + nrf24_read(DYNPD_ADDRESS, ®ister_current_value, 1, CLOSE); + // enable dynamic payload for all pipes + register_new_value = register_current_value | 0b111111; + nrf24_write(DYNPD_ADDRESS, ®ister_new_value, 1, CLOSE); + } + else + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value & (~((1 << EN_ACK_PAY) | (1 << EN_DPL))); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + } +} + +/*on nrf24l01+ there is only one address for PTX device which must be the same as PRX data pipe address 0*/ +void nrf24_datapipe_ptx(uint8_t datapipe_number) +{ + nrf24_write(TX_ADDR_ADDRESS, &datapipe_address[datapipe_number - 1][0], current_address_width, CLOSE); +} + +/*setting the 6 datapipe addresses using the datapipe_address[][]*/ +void nrf24_datapipe_address_configuration() +{ + uint8_t address = RX_ADDR_P0_ADDRESS; + for (uint8_t counter = 0; counter < 6; counter++) + { + nrf24_write(address, &datapipe_address[counter][0], current_address_width, CLOSE); + address++; + } +} + +/*function to change static payload width, from 1 to 32 bytes in each payload*/ +void nrf24_prx_static_payload_width(uint8_t static_payload_width, uint8_t number_of_datapipes) +{ + for (uint8_t address = RX_PW_P0_ADDRESS; number_of_datapipes; number_of_datapipes--) + { + nrf24_write(address, &static_payload_width, 1, CLOSE); + address++; + } + current_payload_width = static_payload_width; +} + +/*datapipes are turned on and off using EN_RXADDR register, PRX datapipe addresses are located in RX_ADDR_Pn, TX address is located inside TX_ADDR*/ +void nrf24_datapipe_enable(uint8_t datapipe) +{ + nrf24_read(EN_RXADDR_ADDRESS, ®ister_current_value, 1, CLOSE); + if (NRF24_en_ack) { + register_current_value |= (1 << ERX_P0); + } + register_new_value = register_current_value | (1 << datapipe); + nrf24_write(EN_RXADDR_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*function to set the nrf24l01+ address width, from 3 to 5 bytes*/ +void nrf24_address_width(uint8_t address_width) +{ + if ((address_width <= 5) && (address_width >= 3)) + { + write_pointer = address_width - 2; + } + else + { + write_pointer = 3; + } + nrf24_write(SETUP_AW_ADDRESS, &write_pointer, 1, CLOSE); /*5 bytes is the maximum address width available*/ + current_address_width = address_width; +} + +/*datarate settings, you can choose between 2mbps, 1mbps, 250kbps*/ +void nrf24_rf_datarate(uint16_t rf_datarate) +{ + nrf24_read(RF_SETUP_ADDRESS, ®ister_current_value, 1, CLOSE); + register_current_value &= ~((1 << RF_DR_LOW) | (1 << RF_DR_HIGH)); + switch (rf_datarate) + { + case 2000: + register_new_value = register_current_value | (1 << RF_DR_HIGH); + break; + case 1000: + register_new_value = register_current_value; + break; + case 250: + register_new_value = register_current_value | (1 << RF_DR_LOW); + break; + default: + register_new_value = register_current_value; + break; + } + nrf24_write(RF_SETUP_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*nrf24l01+ RF power settings. 0dbm, -6dbm, -12dbm, -18dbm*/ +void nrf24_rf_power(uint8_t rf_power) +{ + nrf24_read(RF_SETUP_ADDRESS, ®ister_current_value, 1, CLOSE); + register_current_value &= ~((1 << RF_PWR_1) | (1 << RF_PWR_0)); + switch (rf_power) + { + case 0: + register_new_value = register_current_value | ((1 << RF_PWR_1) | (1 << RF_PWR_0)); + break; + case 6: + register_new_value = register_current_value | (1 << RF_PWR_1); + break; + case 12: + register_new_value = register_current_value | (1 << RF_PWR_0); + break; + case 18: + register_new_value = register_current_value; + break; + default: + register_new_value = register_current_value | (1 << RF_PWR_1); + break; + } + nrf24_write(RF_SETUP_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*read whether the current channel is busy (has traffic), needs to be called from RX mode*/ +uint8_t nrf24_rf_channel_read_busy(uint8_t rf_channel) +{ + uint8_t signals_detected; + nrf24_read(RPD_REG_ADDRESS, &signals_detected, 1, CLOSE); + if (signals_detected) { + return CHANNEL_BUSY; + } + else { + return CHANNEL_CLEAR; + } +} + +/*test whether a channel is busy (has traffic), waiting for ms_to_test*/ +uint8_t nrf24_rf_channel_test_busy(uint8_t rf_channel, uint16_t ms_to_test) +{ + if ((rf_channel <= 125) && (rf_channel >= 1)) + { + // back up old channel + uint8_t previous_channel; + nrf24_read(RF_CH_ADDRESS, &previous_channel, 1, CLOSE); + // back up old mode + uint8_t previous_mode = current_mode; + // switch to new channel + nrf24_rf_channel(rf_channel); + // switch to RX, Received Power Detector is set to 0 and begins sampling + if (previous_mode != PRX) { + nrf24_mode(PRX); + } + // wait at least 1 ms before declaring channel clear + delay_function(1 > ms_to_test ? 1 : ms_to_test); + // Received Power Detector latches to 1 if there was a signal >-64dBm for at least 40 uS consecutively since RX mode was enabled + uint8_t signals_detected = nrf24_rf_channel_read_busy(rf_channel); + // switch back to old channel + nrf24_rf_channel(previous_channel); + // switch back to old mode + if (previous_mode != PRX) { + nrf24_mode(previous_mode); + } + if (signals_detected) { + return CHANNEL_BUSY; + } + else { + return CHANNEL_CLEAR; + } + } + else + { + return CHANNEL_BUSY; + } +} + +/*nrf24l01+ RF channel selection, from 1 to 125*/ +void nrf24_rf_channel(uint8_t rf_channel) +{ + if ((rf_channel <= 125) && (rf_channel >= 1)) + { + uint8_t write_pointer = rf_channel; + nrf24_write(RF_CH_ADDRESS, &write_pointer, 1, CLOSE); + } + else + { + uint8_t write_pointer = 1; + nrf24_write(RF_CH_ADDRESS, &write_pointer, 1, CLOSE); + } +} + +/*interrupt mask settings. 3 seperate masks for RX, TX, and RT (maximum numbers of retransmission reached*/ +void nrf24_interrupt_mask(uint8_t rx_mask, uint8_t tx_mask, uint8_t max_rt_mask) +{ + nrf24_read(CONFIG_ADDRESS, ®ister_current_value, 1, CLOSE); + if (rx_mask) + register_new_value = (register_current_value) | (1 << MASK_RX_DR); + else + register_new_value &= (~(1 << MASK_RX_DR)); + if (tx_mask) + register_new_value |= (1 << MASK_TX_DS); + else + register_new_value &= (~(1 << MASK_TX_DS)); + if (max_rt_mask) + register_new_value |= (1 << MASK_MAX_RT); + else + register_new_value &= (~(1 << MASK_MAX_RT)); + + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*enabling or disabling crc in payload; setting crc encoding scheme between 1 or 2 bytes*/ +void nrf24_crc_configuration(uint8_t crc_enable, uint8_t crc_encoding_scheme) +{ + nrf24_read(CONFIG_ADDRESS, ®ister_current_value, 1, CLOSE); + if (crc_enable) + register_new_value = (register_current_value) | (1 << EN_CRC); + else + register_new_value &= (~(1 << EN_CRC)); + if (crc_encoding_scheme == 2) + register_new_value |= (1 << CRCO); + else + register_new_value &= (~(1 << CRCO)); + + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*mode selector: power down, standby i, standby ii, ptx, prx. used by nrf24_device function*/ +void nrf24_mode(uint8_t mode) +{ + nrf24_read(CONFIG_ADDRESS, ®ister_current_value, 1, CLOSE); + switch (mode) + { + case POWER_DOWN: + nrf24_CE(CE_OFF); + register_new_value = (register_current_value) & (~(1 << PWR_UP)); + delay_function(POWER_DOWN_DELAY); + break; + case STANDBYI: /*standby I is defined by 'PWR_UP = 1' and 'CE pin LOW'*/ + nrf24_CE(CE_OFF); + register_new_value = (register_current_value) | (1 << PWR_UP); + delay_function(STANDBYI_DELAY); + break; + case STANDBYII: /*standby ii is related to a ptx device*/ + nrf24_CE(CE_ON); + register_new_value = ((register_current_value) | (1 << PWR_UP)) & (~(1 << PRIM_RX)); + delay_function(STANDBYI_DELAY); + break; + case PTX: + nrf24_CE(CE_ON); + register_new_value = ((register_current_value) | (1 << PWR_UP)) & (~(1 << PRIM_RX)); + delay_function(STANDBYI_DELAY); + break; + case PRX: + nrf24_CE(CE_ON); + register_new_value = (register_current_value) | (1 << PWR_UP) | (1 << PRIM_RX); + delay_function(STANDBYI_DELAY); + break; + default: + nrf24_CE(CE_OFF); + register_new_value = (register_current_value) & (~(1 << PWR_UP)); + delay_function(POWER_DOWN_DELAY); + break; + } + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); + current_mode = mode; +} + +/*reads the number of bytes (data_length) from the register in nrf24l01+ (address) and stores them inside an array (value), + then closes the spi connection (spi_state = CLOSE) or leaves it open (spi_state = OPEN)*/ +void nrf24_read(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state) +{ + nrf24_SPI(SPI_ON); + SPI_command = R_REGISTER | address; /*in order to read CONFIG, then change one bit*/ + SPI_send_command(SPI_command); + SPI_command = NOP_CMD; + for (; data_length ; data_length--) + { + *value = SPI_send_command(SPI_command); + value++; + } + if (spi_state == CLOSE) + nrf24_SPI(SPI_OFF); +} + +/*writes the number of bytes (data_length) from an array (value) inside registers in nrf24l01+ (address), + then closes the spi connection (spi_state = CLOSE) or leaves it open (spi_state = OPEN)*/ +void nrf24_write(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state) +{ + nrf24_SPI(SPI_ON); + SPI_command = W_REGISTER | address; /*in order to read CONFIG, then change one bit*/ + SPI_send_command(SPI_command); + for (; data_length ; data_length--) + { + SPI_command = *value; + value++; + SPI_send_command(SPI_command); + } + if (spi_state == CLOSE) + nrf24_SPI(SPI_OFF); +} diff --git a/examples/spi_24L01_tx/NRF24L01_TX_Arduino/nrf24l01.h b/examples/spi_24L01_tx/NRF24L01_TX_Arduino/nrf24l01.h new file mode 100644 index 0000000000000000000000000000000000000000..20876f691b44710ef4d034c78458623cbed827dc --- /dev/null +++ b/examples/spi_24L01_tx/NRF24L01_TX_Arduino/nrf24l01.h @@ -0,0 +1,219 @@ +#ifndef NRF24L01_H +#define NRF24L01_H +/*nrf24l01: MSbit to LSbit, LSbyte to MSbyte*/ +#include <stdio.h> +#include <stdint.h> + +#define STARTUP_DELAY 150 /*in milliseconds*/ +#define POWER_DOWN_DELAY 2 +#define STANDBYI_DELAY 2 +#define PRX_MODE_DELAY 100 +#define ADDRESS_WIDTH_DEFAULT 5 /*address width in bytes, for default value*/ +#define RF_CHANNEL_DEFAULT 32 +#define RF_DATARATE_DEFAULT 1000 /*250, 1000, 2000*/ +#define RF_PWR_DEFAULT 6 /*0, -6, -12, -18*/ +#define STATIC_PAYLOAD_WIDTH_DEFAULT 1 /*for static payload mode, configurable between 1 and 32 bytes for PRX device ONLY (RX_PW_Pn, n for data pipe n)(no register for payload length in PTX device)*/ +#define NUMBER_OF_DP_DEFAULT 1 /*number of datapipes, 1 to 6*/ +#define RETRANSMIT_DELAY_DEFAULT 500 /*in uS*/ +#define RETRANSMIT_COUNT_DEFAULT 3 + +#define OPEN 1 +#define CLOSE 0 +#define ENABLE 1 +#define DISABLE 0 +#define SPI_OFF 1 +#define SPI_ON 0 +#define CE_OFF 0 +#define CE_ON 1 + +#define CONFIG_REGISTER_DEFAULT 0X08 +#define EN_AA_REGISTER_DEFAULT 0X3F +#define EN_RXADDR_REGISTER_DEFAULT 0X00 +#define SETUP_AW_REGISTER_DEFAULT 0X03 +#define SETUP_RETR_REGISTER_DEFAULT 0X03 +#define RF_CH_REGISTER_DEFAULT 0X02 +#define RF_SETUP_REGISTER_DEFAULT 0X0E +#define STATUS_REGISTER_DEFAULT 0X0E +#define MAXIMUM_NUMBER_OF_DATAPIPES 6 + +#define POWER_DOWN 0X00 +#define STANDBYI 0X01 +#define STANDBYII 0X02 +#define PTX 0X03 +#define PRX 0X04 +#define DEVICE_NOT_INITIALIZED 0X05 + +#define TRANSMITTER 0X00 +#define RECEIVER 0X01 +#define POWER_SAVING 0X02 +#define TURN_OFF 0X03 + +#define RESET 1 +#define NO_RESET 0 +#define NO_ACK_MODE 1 +#define ACK_MODE 0 +#define TRANSMIT_BEGIN 1 +#define TRANSMIT_FAIL 0 +#define TRANSMIT_IN_PROGRESS 0 +#define TRANSMIT_DONE 1 +#define TRANSMIT_FAILED 0XFF +#define OPERATION_DONE 1 +#define OPERATION_ERROR 0 +#define RECEIVE_FIFO_EMPTY 2 +#define TX_BUFFER 1 +#define RX_BUFFER 0 +// return states for nrf24_rf_channel_test_busy +#define CHANNEL_CLEAR 0 +#define CHANNEL_BUSY 1 + +/*bits definition section*/ +#define MASK_RX_DR 6 /*mask interrupt caused by RX_DR: 1 interrupt not reflected on IRQ pin (IRQ is active low), inside CONFIG register*/ +#define MASK_TX_DS 5 /*mask interrupt caused by TX_DS: 1 interrupt not reflected on IRQ pin (IRQ is active low), inside CONFIG register*/ +#define MASK_MAX_RT 4 /*mask interrupt caused by MAX_RT means maximum number of retransmissions reached: 1 interrupt not reflected on IRQ pin (IRQ is active low), inside CONFIG register*/ +#define EN_CRC 3 /*enale CRC, forced high if one of the bits in EN_AA is high, inside CONFIG register*/ +#define CRCO 2 /*CRC encoding scheme, 0 is 1 byte, 1 is 2 bytes, inside CONFIG register*/ +#define PWR_UP 1 /*1 is power up, inside CONFIG register*/ +#define PRIM_RX 0 /*RX/TX control, 1: PRX, inside CONFIG register*/ +#define ENAA_P5 5 /*enable auto acknowledgement data pipe 5*/ +#define ENAA_P4 4 +#define ENAA_P3 3 +#define ENAA_P2 2 +#define ENAA_P1 1 +#define ENAA_P0 0 +#define ERX_P5 5 /*part of EN_RXADDR, enable data pipe 5*/ +#define ERX_P4 4 +#define ERX_P3 3 +#define ERX_P2 2 +#define ERX_P1 1 +#define ERX_P0 0 +#define AW_1 1 /*RX/TX address field width, 00 illegal, 01 3 bytes, 10 4 bytes, 11 5 bytes*/ +#define AW_0 0 +#define ARD_3 7 /*auto retransmit delay, 0000 250us, 0001 500us ...> 1111 4000us*/ +#define ARD_2 6 +#define ARD_1 5 +#define ARD_0 4 +#define ARC_3 3 /*auto retransmit count, 0000 retransmit deisabled, 1111 up to 15 retransmit on failure of AA. (inside SETUP_RETR register)*/ +#define ARC_2 2 +#define ARC_1 1 +#define ARC_0 0 +#define RF_CH_6 6 /*sets the frequencvy channel nRF24L01+ operates on*/ +#define RF_CH_5 5 +#define RF_CH_4 4 +#define RF_CH_3 3 +#define RF_CH_2 2 +#define RF_CH_1 1 +#define RF_CH_0 0 +#define CONT_WAVE 7 /*enables continuous carrier transmit when high*/ +#define RF_DR_LOW 5 /*sets the RF data rate to 250kbps*/ +#define PLL_LOCK 4 /*force PLL lock signal. used for testing ONLY*/ +#define RF_DR_HIGH 3 /*select between high speed data rates and works ONLY when RF_DR_LOW is 0. 0 for 1Mbps, 1 for 2Mbps*/ +#define RF_PWR_1 2 +#define RF_PWR_0 1 +#define RX_DR 6 /*IRQ for new packet in RX FIFO (newly received)*/ +#define TX_DS 5 /*IRQ for ACK received in TX mode*/ +#define MAX_RT 4 +#define RX_P_NO_2 3 +#define RX_P_NO_1 2 +#define RX_P_NO_0 1 +#define PLOS_CNT_3 7 /*inside OBSERVE_TX register, counts the total number of retransmissions since last channel change. reset by writing to RF_CH*/ +#define PLOS_CNT_2 6 +#define PLOS_CNT_1 5 +#define PLOS_CNT_0 4 +#define ARC_CNT_3 3 /*inside OBSERVE_TX register, counts the number of retransmissions for current transaction. reset by initiating new transaction*/ +#define ARC_CNT_2 2 +#define ARC_CNT_1 1 +#define ARC_CNT_0 0 +#define RPD 0 /*received power detector, if received power is less than -64dbm, RPD = 0*/ +#define TX_REUSE 6 +#define TX_FULL 5 +#define TX_EMPTY 4 +#define RX_FULL 1 +#define RX_EMPTY 0 +#define DPL_P5 5 +#define DPL_P4 4 +#define DPL_P3 3 +#define DPL_P2 2 +#define DPL_P1 1 +#define DPL_P0 0 /*must be set on PTX in dynamic payload length mode*/ +#define EN_DPL 2 /*set to enable dynamic payload length*/ +#define EN_ACK_PAY 1 /*used to enable auto acknowledgement with payload in PRX (inside FEATURE register)*/ +#define EN_DYN_ACK 0 /**/ + +/*registers definition section*/ +#define CONFIG_ADDRESS 0X00 +#define EN_AA_ADDRESS 0X01 /*enable auto acknowledgement feature*/ +#define EN_RXADDR_ADDRESS 0X02 /*register containing bits to enable 6 data pipes individually*/ +#define SETUP_AW_ADDRESS 0X03 /*address field length is configured in here to be 3, 4 or 5 bytes long*/ +#define SETUP_RETR_ADDRESS 0X04 /*setup ARC bits to configure auto retransmission count*/ +#define RF_CH_ADDRESS 0X05 +#define RF_SETUP_ADDRESS 0X06 +#define STATUS_ADDRESS 0X07 /*contains RX_DR, TX_DS, MAX_RT, RX_P_NO, TX_FULL, send R_REGISTER then NOP to read*/ +#define OBSERVE_TX_ADDRESS 0X08 /*contains ARC_CNT and PLOS_CNT, two counters for retransmission. these counters could be used to assess the network quality*/ +#define RPD_REG_ADDRESS 0X09 +#define RX_ADDR_P0_ADDRESS 0X0A /*the address for PRX device. if a packet contains this address, enhanced shockburst starts validating the packet*/ +#define RX_ADDR_P1_ADDRESS 0X0B /*a total of 6 unique addresses could be assigned to a PRX device (Multiceiver feature)*/ +#define RX_ADDR_P2_ADDRESS 0X0C /*these addresses must NOT be the same*/ +#define RX_ADDR_P3_ADDRESS 0X0D +#define RX_ADDR_P4_ADDRESS 0X0E +#define RX_ADDR_P5_ADDRESS 0X0F +#define TX_ADDR_ADDRESS 0X10 /*40 bits long register, transmit address, used for a PTX device only. configure address legth in SETUP_AW register. set RX_ADDR_P0 equal to this address to handle automatic acknowledge*/ +#define RX_PW_P0_ADDRESS 0X11 /*these registers are for setting the static payload length in static payload length mode (receiver side)*/ +#define RX_PW_P1_ADDRESS 0X12 +#define RX_PW_P2_ADDRESS 0X13 +#define RX_PW_P3_ADDRESS 0X14 +#define RX_PW_P4_ADDRESS 0X15 +#define RX_PW_P5_ADDRESS 0X16 +#define FIFO_STATUS_ADDRESS 0X17 +#define DYNPD_ADDRESS 0X1C /*on receiver side (RX mode), this register must be set to enable dynamic payload length. a PTX in dynamic mode, must have the DYNPD_P0 set*/ +#define FEATURE_ADDRESS 0X1D /*contains the EN_DPL bit to enable dynamic payload length*/ + +/*commands definition section*/ +#define R_REGISTER 0X00 /*read commmand and STATUS registers, 5 bit register map address*/ +#define W_REGISTER 0X20 /*write commmand and STATUS registers, 5 bit register map address, executable in POWER DOWN or STANDBY modes only*/ +#define R_RX_PAYLOAD 0X61 /*read RX payload, 1-32 bytes. read operation starts at byte 0. payload is deleted from FIFO after its read*/ +#define W_TX_PAYLOAD 0XA0 /*write TX payload, starts at byte 0, 1-32 bytes*/ +#define FLUSH_TX 0XE1 /*flush TX FIFO, used in TX mode*/ +#define FLUSH_RX 0XE2 /*flush RX FIFO, used in RX mode*/ +#define REUSE_TX_PL 0XE3 /*used for a PTX device, reuse last transmitted payload for an exact number. alternative to auto retransmission*/ +#define R_RX_PL_WID 0X60 /*command for receiver side, in order to read the payload length in dynamic payload length mode*/ +#define W_ACK_PAYLOAD 0XA0 /*used in RX mode, to write payload in TX FIFO and later transmit the payloads along with ACK packet to PTX, if DPL is enabled*/ +#define W_TX_PAYLOAD_NOACK 0XB0 /*used in TX mode, disables AUTOACK on this specific packet. must be first enabled in FEATURE register by setting the EN_DYN_ACK bit. if used, PTX will not wait for ACK and goes directly to standby I*/ +#define NOP_CMD 0XFF /*might be used to read the status register*/ + +void nrf24_reset(); +void nrf24_device(uint8_t device_mode, uint8_t reset_state); +uint8_t SPI_send_command(uint8_t command); +void pinout_Initializer(); +void SPI_Initializer(); +void nrf24_mode(uint8_t mode); +void nrf24_SPI(uint8_t input); +void nrf24_CE(uint8_t input); +void nrf24_address_width(uint8_t address_width); +uint8_t nrf24_rf_channel_read_busy(uint8_t rf_channel); +uint8_t nrf24_rf_channel_test_busy(uint8_t rf_channel, uint16_t ms_to_test); +void nrf24_rf_channel(uint8_t rf_channel); +void nrf24_rf_power(uint8_t rf_power); +void nrf24_rf_datarate(uint16_t rf_datarate); +void nrf24_read(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state); +void nrf24_write(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state); +void delay_function(uint32_t duration_ms); +void nrf24_crc_configuration(uint8_t crc_enable, uint8_t crc_encoding_scheme); +void nrf24_interrupt_mask(uint8_t rx_mask, uint8_t tx_mask, uint8_t max_rt_mask); +void nrf24_datapipe_enable(uint8_t number_of_datapipes); +void nrf24_prx_static_payload_width(uint8_t static_payload_width, uint8_t number_of_datapipes); +void nrf24_datapipe_address_configuration(); +void nrf24_datapipe_ptx(uint8_t datapipe_number); +void nrf24_automatic_retransmit_setup(uint16_t delay_time, uint8_t retransmit_count); +void nrf24_auto_acknowledgment_setup(uint8_t datapipe); +void nrf24_payload_without_ack(uint8_t state); +void nrf24_payload_with_ack(uint8_t state); +void nrf24_dynamic_payload(uint8_t state, uint8_t datapipe); +void nrf24_device(uint8_t device_mode, uint8_t reset_state); +void nrf24_send_payload(uint8_t *payload, uint8_t payload_width); +uint8_t nrf24_receive(uint8_t *payload, uint8_t payload_width); +uint8_t nrf24_transmit(uint8_t *payload, uint8_t payload_width, uint8_t acknowledgement_state); +uint8_t nrf24_transmit_status(); +void nrf24_dynamic_ack(uint8_t state); +uint8_t nrf24_flush(uint8_t fifo_select); + +#endif diff --git a/examples/spi_24L01_tx/NRF24L01_TX_Arduino/nrf24l01_low_level.c b/examples/spi_24L01_tx/NRF24L01_TX_Arduino/nrf24l01_low_level.c new file mode 100644 index 0000000000000000000000000000000000000000..412a98c533e10b11e6ef5b9e8a9259ea85997786 --- /dev/null +++ b/examples/spi_24L01_tx/NRF24L01_TX_Arduino/nrf24l01_low_level.c @@ -0,0 +1,70 @@ +/*low level api example for avr/arduino*/ + +#include <Arduino.h> +#include "nrf24l01.h" + +/*macros for SPI, CE and CSN pin configuration, change pins # according to mcu*/ +#define MOSI_PIN 11 +#define MISO_PIN 12 +#define SCK_PIN 13 +#define SS_PIN 10 +#define NRF24_CSN 9 /*to enable SPI on nrf24, active LOW*/ +#define NRF24_CE 8 /*active HIGH, activate chip in RX or TX mode*/ + +/*start of low level functions, specific to the mcu and compiler*/ + +/*delay in miliseconds*/ +void delay_function(uint32_t duration_ms) +{ + delay(duration_ms); +} + +/*contains all SPI configuations, such as pins and control registers*/ +/*SPI control: master, interrupts disabled, clock polarity low when idle, clock phase falling edge, clock up tp 1 MHz*/ +void SPI_Initializer() +{ + pinMode(MOSI_PIN, OUTPUT); + pinMode(MISO_PIN, INPUT); + pinMode(SCK_PIN, OUTPUT); + pinMode(SS_PIN, OUTPUT); + + SPCR = 0X51; /*master, interrupt disabled, spi enabled, clock polarity low when idle, clock phase falling edge, 1 MHz clock*/ +} + +/*contains all CSN and CE pins gpio configurations, including setting them as gpio outputs and turning SPI off and CE '1'*/ +void pinout_Initializer() +{ + pinMode(NRF24_CSN, OUTPUT); + pinMode(NRF24_CE, OUTPUT); + + digitalWrite(NRF24_CSN, SPI_OFF); /*nrf24l01 is not accepting commands*/ + nrf24_CE(HIGH); /*no need to change this line*/ +} + +/*CSN pin manipulation to high or low (SPI on or off)*/ +void nrf24_SPI(uint8_t input) +{ + if (input > 0) + digitalWrite(NRF24_CSN, HIGH); + else + digitalWrite(NRF24_CSN, LOW); +} + +/*1 byte SPI shift register send and receive routine*/ +uint8_t SPI_send_command(uint8_t command) +{ + SPDR = command; + while ((SPSR & (1 << SPIF)) == 0) {} + return SPDR; +} + +/*CE pin maniplation to high or low*/ +void nrf24_CE(uint8_t input) +{ + if (input > 0) + digitalWrite(NRF24_CE, HIGH); + //digitalWrite(NRF24_CE, LOW); + else + digitalWrite(NRF24_CE, LOW); + //digitalWrite(NRF24_CE, HIGH); +} diff --git a/examples/spi_24L01_tx/README.md b/examples/spi_24L01_tx/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6f60c9e64eec56a09450331570aed041d4b8420d --- /dev/null +++ b/examples/spi_24L01_tx/README.md @@ -0,0 +1,65 @@ +# SPI nRF24L01+ demo +This "example" consists of three parts: +A SPI Library for the ch32v003, a platform-agnostic library for the nRF24L01(+) and the actual example. + +## SPI library +The SPI library helps you configure the SPI registers. +Initially the idea was to make it Arduino-compatible but GPL. +This is free-er. +Currently, only master-mode is supported, either R+W or W-only. +Then you can start talking on the SPI bus through SPI_read_x(), SPI_write_x() and, most importantly SPI_transfer_x(), where the x can be a 8 or 16 bit word (this is C, so no overloading). +When you're done you can even shut down the SPI peripheral to reduce current consumption. + +I welcome any improvements you may choose to make, it's far from complete or good. +CRC, for example, is not implemented yet and I'm incapable of making DMA easy, as the spi dac example showed the processor can be mostly "braindead" and the tiny chip can still output loads of data. + +## nRF24L01(+) library +This is the doing of [Reza Ebrahimi](https://github.com/ebrezadev), I've just included a copy of [his library](https://github.com/ebrezadev/nRF24L01-C-Driver) here and made some modifications. + +## example +The example shows how to send either a uint8_t or a char[16]. +To choose, move the comments. +The receiver-part is spi_24L01_rx, split in two because I don't know how to have the unified makefile recurse any differently. +You can enable / disable acknowledge mode by using "ACK_MODE" or "NO_ACK_MODE" in the call to nrf24_transmit(). + +### nRF module +Never connect the nRF module to 5V, it operates at 1.9-3.3V, much past that it will probably die! +Also, I've become convinced the briliant engineers at nordic are evil!! +Thanks to nordics specifications in the datasheet, the chinese module manufacturers don't even pack 100nF onto the board as bypass caps! +Four hours of trouble-shooting later I stumbled upon the magic of adding a capacitor!! +For you see, as the IC transmits its packets, it draws huge amounts.. wrong! +Even in RX-mode, additional capacitance is essential for basic operation, let alone the acknowledge mode where they need to switch roles within 500uS to exchange a "roger roger". +To the VCC and GND pins I added a 33uF 16V each. Though a 10uF ceramic capacitor would likely have been better due to lower ESR. The blue polyester caps are 220nF, straight onto the 10nF(!!) bypass cap, but I doubt they are strictly necessary. +Can you imagine they saved 0.006$ and now we have to get out ye olde soldering iron so these work properly!! + +#### pinout for ch32v003 +Please check whether your module looks like [THIS](https://www.circuitspecialists.com/content/552219/NRF24L01-RF-2.jpg) before using this pinout! +perspective: looking at the underside of the module, pin header in the top right corner + +nRF | ch32 | nRF | ch32 +--------|---------|-------|------ +VCC | 3V3 | GND | GND +CSN/SS | C0 | CE | C4 +MOSI | C6 | SCK | C5 +IRQ | NC | MISO | C7 + +And then wire D4 to LED1 on the evaluation board. + +#### Arduino example +As I currently only have the one ch32v003 evaluation board, I had to talk to an Arduino. +That's why there is a dir with Arduino files lurking in here on our christian server. + +#### pinout for Arduino +same perspective +nRF | UNO | nRF | UNO +--------|---------|-------|------- +VCC | 3V3 | GND | GND +CSN/SS | 9 | CE | 8 +MOSI | 11 | SCK | 13 +IRQ | NC | MISO | 12 + +And an LED on pin 4, for current-limiting resistor 1k is usually safe with 5V. + +## license +Any code I provide here is use-as-you-please and contains 0 guarantees! +No "but you have to publish source code"-deathgrip like GPL! diff --git a/examples/spi_24L01_tx/ch32v003_SPI.h b/examples/spi_24L01_tx/ch32v003_SPI.h new file mode 100644 index 0000000000000000000000000000000000000000..7f30a051583a07ed3f417202fbd54ebefd7395b3 --- /dev/null +++ b/examples/spi_24L01_tx/ch32v003_SPI.h @@ -0,0 +1,339 @@ +//######## necessities + +// include guards +#ifndef CH32V003_SPI_H +#define CH32V003_SPI_H + +// includes +#include<stdint.h> //uintN_t support +#include"../../ch32v003fun/ch32v003fun.h" + + + +/*######## library usage and configuration + +in the .c files that use this library, you'll need to #define some configuration options _before_ the #include "ch32v003_SPI.h" + +SYSTEM_CORE_CLOCK and APB_CLOCK should be defined already as APB_CLOCK is used by this library + +to enable using the functions of this library: +#define CH32V003_SPI_IMPLEMENTATION + +to configure the settings of the SPI bus, first, declare the desired bus speed + +#define CH32V003_SPI_SPEED_HZ 1000000 + +then pick the desired setting of each group: + +#define CH32V003_SPI_DIRECTION_2LINE_TXRX +#define CH32V003_SPI_DIRECTION_1LINE_TX + +#define CH32V003_SPI_CLK_MODE_POL0_PHA0 //leading = rising trailing = falling sample on leading default if you're unsure +#define CH32V003_SPI_CLK_MODE_POL0_PHA1 //leading = rising trailing = falling sample on trailing +#define CH32V003_SPI_CLK_MODE_POL1_PHA0 //leading = falling trailing = rising sample on leading +#define CH32V003_SPI_CLK_MODE_POL1_PHA1 //leading = falling trailing = rising sample on trailing + +#define CH32V003_SPI_NSS_HARDWARE_PC0 // _NSS toggled by hardware, automatic +#define CH32V003_SPI_NSS_HADRWARE_PC1 // NSS toggled by hardware, automatic +#define CH32V003_SPI_NSS_SOFTWARE_PC3 // PC3 toggled by software, automatic, manual setters available +#define CH32V003_SPI_NSS_SOFTWARE_PC4 // PC4 toggled by software, automatic, manual setters available +#define CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL // toggle manually! +*/ + + + +//######## function overview (declarations): use these! +// initialize and configure the SPI peripheral +static inline void SPI_init(); + +// establish / end a connection to the SPI device +static inline void SPI_begin_8(); +static inline void SPI_begin_16(); +static inline void SPI_end(); + +// manually set the NSS (chip select) pin high / low +// "SPI_NSS_HIGH_FN" and "SPI_NSS_LOW_FN" only become available functions if the selected NSS is software PC3 or PC4 +#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) +static inline void SPI_NSS_software_low(); +static inline void SPI_NSS_software_high(); +#endif + +// read / write the SPI device +// these commands are raw, you'll have to consider all other steps in SPI_transfer! +static inline uint8_t SPI_read_8(); +static inline uint16_t SPI_read_16(); +static inline void SPI_write_8(uint8_t data); +static inline void SPI_write_16(uint16_t data); + +// send a command and get a response from the SPI device +// you'll use this for most devices +static inline uint8_t SPI_transfer_8(uint8_t data); +static inline uint8_t SPI_transfer_16(uint16_t data); + +// SPI peripheral power enable / disable (default off, init() automatically enables) +// send SPI peripheral to sleep +static inline void SPI_poweroff(); +// wake SPI peripheral from sleep +static inline void SPI_poweron(); + +// helper: kill / restore all interrupts on the CH32V003 +static inline void kill_interrrupts(); +static inline void restore_interrupts(); + + + +//######## internal function declarations +static inline void SPI_wait_TX_complete(); +static inline uint8_t SPI_is_RX_empty(); +static inline void SPI_wait_RX_available(); + + + +//######## internal variables +static uint16_t EXT1_INTENR_backup; + + + +//######## preprocessor macros +// min and max helper macros +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +// stringify for displaying what #defines evaluated to at preprocessor stage +#define VALUE_TO_STRING(x) #x +#define VALUE(x) VALUE_TO_STRING(x) +#define VAR_NAME_VALUE(var) #var "=" VALUE(var) + +//compile-time log2 +#define LOG2(x) ((x) == 0 ? -1 : __builtin_ctz(x)) + +// compile-time clock prescaler calculation: log2(APB_CLOCK/SPEED_BUS) +#define SPI_CLK_RATIO (APB_CLOCK / CH32V003_SPI_SPEED_HZ) +#define SPI_CLK_PRESCALER LOG2(SPI_CLK_RATIO) + +// ensure that CLOCK_PRESCALER_VALUE is within the range of 0..7 +_Static_assert(SPI_CLK_PRESCALER >= 0 && SPI_CLK_PRESCALER <= 7, "SPI_CLK_PRESCALER is out of range (0..7). Please set a different SPI bus speed. prescaler = log2(f_CPU/f_SPI)"); +//#pragma message(VAR_NAME_VALUE(SPI_CLK_PRESCALER)) + + + +//######## preprocessor #define requirements + +#if !defined(CH32V003_SPI_DIRECTION_2LINE_TXRX) && !defined(CH32V003_SPI_DIRECTION_1LINE_TX) + #warning "none of the CH32V003_SPI_DIRECTION_ options were defined!" +#endif +#if defined(CH32V003_SPI_DIRECTION_2LINE_TXRX) && defined(CH32V003_SPI_DIRECTION_1LINE_TX) + #warning "both CH32V003_SPI_DIRECTION_ options were defined!" +#endif + +#if ((defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) ? 1 : 0) + \ + (defined(CH32V003_SPI_CLK_MODE_POL0_PHA1) ? 1 : 0) + \ + (defined(CH32V003_SPI_CLK_MODE_POL1_PHA0) ? 1 : 0) + \ + (defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) > 1 + #warning "more than one of the CH32V003_SPI_CLK_MODE_ options were defined!" +#endif +#if ((defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) ? 1 : 0) + \ + (defined(CH32V003_SPI_CLK_MODE_POL0_PHA1) ? 1 : 0) + \ + (defined(CH32V003_SPI_CLK_MODE_POL1_PHA0) ? 1 : 0) + \ + (defined(CH32V003_SPI_CLK_MODE_POL1_PHA1) ? 1 : 0)) == 0 + #warning "none of the CH32V003_SPI_CLK_MODE_ options were defined!" +#endif + +#if ((defined(CH32V003_SPI_NSS_HARDWARE_PC0) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) > 1 + #warning "more than one of the CH32V003_SPI_NSS_ options were defined!" +#endif +#if ((defined(CH32V003_SPI_NSS_HARDWARE_PC0) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_HARDWARE_PC1) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_SOFTWARE_PC3) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_SOFTWARE_PC4) ? 1 : 0) + \ + (defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) ? 1 : 0)) == 0 + #warning "none of the CH32V003_SPI_NSS_ options were defined!" +#endif + + + +//######## small function definitions, static inline +static inline void SPI_init() { + SPI_poweron(); + + // reset control register + SPI1->CTLR1 = 0; + + // set prescaler + SPI1->CTLR1 |= SPI_CTLR1_BR & (SPI_CLK_PRESCALER<<3); + + // set clock polarity and phase + #if defined(CH32V003_SPI_CLK_MODE_POL0_PHA0) + SPI1->CTLR1 |= (SPI_CPOL_Low | SPI_CPHA_1Edge); + #elif defined (CH32V003_SPI_CLK_MODE_POL0_PHA1) + SPI1->CTLR1 |= (SPI_CPOL_Low | SPI_CPHA_2Edge); + #elif defined (CH32V003_SPI_CLK_MODE_POL1_PHA0) + SPI1->CTLR1 |= (SPI_CPOL_High | SPI_CPHA_1Edge); + #elif defined (CH32V003_SPI_CLK_MODE_POL1_PHA1) + SPI1->CTLR1 |= (SPI_CPOL_High | SPI_CPHA_2Edge); + #endif + + // configure NSS pin, master mode + #if defined(CH32V003_SPI_NSS_HARDWARE_PC0) + // _NSS (negative slave select) on PC0, 10MHz Output, alt func, push-pull1 + SPI1->CTLR1 |= SPI_NSS_Hard; // NSS hardware control mode + GPIOC->CFGLR &= ~(0xf<<(4*0)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*0); + AFIO->PCFR1 |= GPIO_Remap_SPI1; // remap NSS (C1) to _NSS (C0) + SPI1->CTLR2 |= SPI_CTLR2_SSOE; // pull _NSS high + #elif defined(CH32V003_SPI_NSS_HADRWARE_PC1) + // NSS (negative slave select) on PC1, 10MHz Output, alt func, push-pull1 + SPI1->CTLR1 |= SPI_NSS_Hard; // NSS hardware control mode + GPIOC->CFGLR &= ~(0xf<<(4*1)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*1); + SPI1->CTLR2 |= SPI_CTLR2_SSOE; // pull _NSS high + #elif defined(CH32V003_SPI_NSS_SOFTWARE_PC3) + SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode + GPIOC->CFGLR &= ~(0xf<<(4*3)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*3); + #elif defined(CH32V003_SPI_NSS_SOFTWARE_PC4) + SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode + GPIOC->CFGLR &= ~(0xf<<(4*4)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*4); + #elif defined(CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL) + SPI1->CTLR1 |= SPI_NSS_Soft; // SSM NSS software control mode + #endif + + // SCK on PC5, 10MHz Output, alt func, push-pull + GPIOC->CFGLR &= ~(0xf<<(4*5)); + GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF)<<(4*5); + + // CH32V003 is master + SPI1->CTLR1 |= SPI_Mode_Master; + + // set data direction and configure data pins + #if defined(CH32V003_SPI_DIRECTION_2LINE_TXRX) + SPI1->CTLR1 |= SPI_Direction_2Lines_FullDuplex; + + // MOSI on PC6, 10MHz Output, alt func, push-pull + GPIOC->CFGLR &= ~(0xf<<(4*6)); + GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF)<<(4*6); + + // MISO on PC7, 10MHz input, floating + GPIOC->CFGLR &= ~(0xf<<(4*7)); + GPIOC->CFGLR |= GPIO_CNF_IN_FLOATING<<(4*7); + #elif defined(CH32V003_SPI_DIRECTION_1LINE_TX) + SPI1->CTLR1 |= SPI_Direction_1Line_Tx; + + // MOSI on PC6, 10MHz Output, alt func, push-pull + GPIOC->CFGLR &= ~(0xf<<(4*6)); + GPIOC->CFGLR |= (GPIO_Speed_50MHz | GPIO_CNF_OUT_PP_AF)<<(4*6); + #endif +} + +static inline void SPI_begin_8() { + SPI1->CTLR1 |= SPI_DataSize_8b; // DFF 16bit data-length enable, writable only when SPE is 0 + SPI1->CTLR1 |= CTLR1_SPE_Set; +} +static inline void SPI_begin_16() { + SPI1->CTLR1 |= SPI_DataSize_16b; // DFF 16bit data-length enable, writable only when SPE is 0 + SPI1->CTLR1 |= CTLR1_SPE_Set; +} +static inline void SPI_end() { + SPI1->CTLR1 &= CTLR1_SPE_Reset; +} + +#if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) +static inline void SPI_NSS_software_high() { + GPIOC->BSHR = (1<<3); +} +static inline void SPI_NSS_software_low() { + GPIOC->BSHR = (1<<(16+3)); +} +#elif defined(CH32V003_SPI_NSS_SOFTWARE_PC4) +static inline void SPI_NSS_software_high() { + GPIOC->BSHR = (1<<4); +} +static inline void SPI_NSS_software_low() { + GPIOC->BSHR = (1<<(16+4)); +} +#endif + +static inline uint8_t SPI_read_8() { + return SPI1->DATAR; +} +static inline uint16_t SPI_read_16() { + return SPI1->DATAR; +} +static inline void SPI_write_8(uint8_t data) { + SPI1->DATAR = data; +} +static inline void SPI_write_16(uint16_t data) { + SPI1->DATAR = data; +} +static inline uint8_t SPI_transfer_8(uint8_t data) { + #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) + SPI_NSS_software_high(); + #endif + SPI_write_8(data); + SPI_wait_TX_complete(); + asm volatile("nop"); + SPI_wait_RX_available(); + #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) + SPI_NSS_software_low(); + #endif + return SPI_read_8(); +} +static inline uint8_t SPI_transfer_16(uint16_t data) { + #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) + SPI_NSS_software_high(); + #endif + SPI_write_16(data); + SPI_wait_TX_complete(); + asm volatile("nop"); + SPI_wait_RX_available(); + #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) + SPI_NSS_software_low(); + #endif + return SPI_read_16(); +} + +static inline void SPI_poweroff() { + SPI_end(); + RCC->APB2PCENR &= ~RCC_APB2Periph_SPI1; +} +static inline void SPI_poweron() { + RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1; +} + +static inline void kill_interrrupts() { + EXT1_INTENR_backup = EXTI->INTENR; + // zero the interrupt enable register to disable all interrupts + EXTI->INTENR = 0; +} +static inline void restore_interrupts() { + EXTI->INTENR = EXT1_INTENR_backup; +} + + + +//######## small internal function definitions, static inline +static inline void SPI_wait_TX_complete() { + while(!(SPI1->STATR & SPI_STATR_TXE)) {} +} +static inline uint8_t SPI_is_RX_empty() { + return SPI1->STATR & SPI_STATR_RXNE; +} +static inline void SPI_wait_RX_available() { + while(!(SPI1->STATR & SPI_STATR_RXNE)) {} +} + + + +//######## implementation block +//#define CH32V003_SPI_IMPLEMENTATION //enable so LSP can give you text colors while working on the implementation block, disable for normal use of the library +#if defined(CH32V003_SPI_IMPLEMENTATION) + +//no functions here because I think all of the functions are small enough to static inline + +#endif // CH32V003_SPI_IMPLEMENTATION +#endif // CH32V003_SPI_H diff --git a/examples/spi_24L01_tx/nrf24l01.c b/examples/spi_24L01_tx/nrf24l01.c new file mode 100644 index 0000000000000000000000000000000000000000..75a2111beb5593bcad897f410cd03315d2fb06cf --- /dev/null +++ b/examples/spi_24L01_tx/nrf24l01.c @@ -0,0 +1,616 @@ +#include "nrf24l01.h" + +/*nRF24L01+ features, enable / disable as needed*/ +static uint8_t NRF24_en_ack = ENABLE; +static uint8_t NRF24_en_no_ack = ENABLE; +static uint8_t NRF24_en_dynamic_payload = ENABLE; + +/*global variables related to this file*/ +static uint8_t SPI_command; /*1 byte spi command*/ +static uint8_t register_current_value; /*in order to change some bits of internal registers or to check their content*/ +static uint8_t register_new_value; /*used to write new value to nrf24l01+ registers*/ +static uint8_t write_pointer; /*used as an input for read and write functions (as a pointer)*/ +static uint8_t current_address_width; /*current address width for receiver pipe addresses (up to 6 pipes), from 3 to 5 bytes*/ +static uint8_t reset_flag = 0; /*reset flag lets the software know if the nrf24l01+ has ever been reset or not*/ +static uint8_t current_mode = DEVICE_NOT_INITIALIZED; /*current mode of operation: DEVICE_NOT_INITIALIZED, PRX, PTX, STANDBYI, STANDBYII, POWER_DOWN*/ +static uint8_t current_payload_width; /*payload width could be from 1 to 32 bytes, in either dynamic or static forms*/ +static uint8_t current_acknowledgement_state = NO_ACK_MODE; + +/*2 dimensional array of pipe addresses (5 byte address width) by default. you can change addresses using a new array later. + Pipe 1 address could be anything. pipe 3 to 6 addresses share the first 4 bytes with pipe 2 and only differ in byte 5*/ +uint8_t datapipe_address[MAXIMUM_NUMBER_OF_DATAPIPES][ADDRESS_WIDTH_DEFAULT] = { + {0X20, 0XC3, 0XC2, 0XC1, 0XA0}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA1}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA2}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA3}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA4}, + {0X20, 0XC3, 0XC2, 0XC1, 0XA5} +}; + +/*function for PTX device to transmit 1 to 32 bytes of data, used for both dynamic payload length + and static payload length methods. acknowledgemet state could be NO_ACK_MODE or ACK_MODE*/ +uint8_t nrf24_transmit(uint8_t *payload, uint8_t payload_width, uint8_t acknowledgement_state) +{ + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*in order to check TX_FIFO status*/ + if ((!(register_current_value & (1 << TX_FULL))) && (current_mode == PTX)) + { + current_acknowledgement_state = acknowledgement_state; /*setting the acknowledgement state to either NO_ACK or ACK, based on input*/ + if (NRF24_en_dynamic_payload == ENABLE) + current_payload_width = payload_width; + nrf24_send_payload(payload, payload_width); /*the actual function to send data*/ + return (TRANSMIT_BEGIN); /*TX FIFO is not full and nrf24l01+ mode is standby ii or ptx*/ + } + else + { + return (TRANSMIT_FAIL); /*TX FIFO full or wrong mode*/ + } +} + +/*used by nrf24_transmit function to send the actual data*/ +void nrf24_send_payload(uint8_t *payload, uint8_t payload_width) +{ + nrf24_SPI(SPI_ON); + if (current_acknowledgement_state == NO_ACK_MODE) + SPI_command = W_TX_PAYLOAD_NOACK; + else + SPI_command = W_TX_PAYLOAD; + SPI_send_command(SPI_command); + for (; payload_width; payload_width--) + { + SPI_command = *payload; + SPI_send_command(SPI_command); + payload++; + } + nrf24_SPI(SPI_OFF); +} + +/*reports back transmit status: TRANSMIT_DONE, TRANSMIT_FAILED (in case of reaching maximum number of retransmits in auto acknowledgement mode) + and TRANSMIT_IN_PROGRESS, if neither flags are set. automatically resets the '1' flags.*/ +uint8_t nrf24_transmit_status() +{ + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*status register is read to check TX_DS flag*/ + if (register_current_value & (1 << TX_DS)) /*if the TX_DS == 1, */ + { + nrf24_write(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*reseting the TX_DS flag. as mentioned by datasheet, writing '1' to a flag resets that flag*/ + return TRANSMIT_DONE; + } + else if (register_current_value & (1 << MAX_RT)) + { + nrf24_write(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*reseting the MAX_RT flag. as mentioned by datasheet, writing '1' to a flag resets that flag*/ + return TRANSMIT_FAILED; + } + else + return TRANSMIT_IN_PROGRESS; +} + +/*the receive function output is used as a polling method to check the received data inside RX FIFOs. +If there is any data available, it will be loaded inside payload array*/ +uint8_t nrf24_receive(uint8_t *payload, uint8_t payload_width) +{ + if (current_mode == PRX) + { + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); + if (register_current_value & (1 << RX_DR)) /*if received data is ready inside RX FIFO*/ + { + if(NRF24_en_dynamic_payload == DISABLE) /*if dynamif payload width is disabled, use the static payload width and ignore the input*/ + payload_width = current_payload_width; + + nrf24_SPI(SPI_ON); /*sending the read payload command to nrf24l01+*/ + SPI_command = R_RX_PAYLOAD; + SPI_send_command(SPI_command); + + for (; payload_width; payload_width--) + { + SPI_command = NOP_CMD; + *payload = SPI_send_command(SPI_command); + payload++; + } + nrf24_SPI(SPI_OFF); + nrf24_read(FIFO_STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); /*in order to check the RX_EMPTY flag*/ + if(register_current_value & (1 << RX_EMPTY)) /*if the RX FIFO is empty, reset the RX_DR flag inside STATUS register*/ + { + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << RX_DR); + nrf24_write(STATUS_ADDRESS, ®ister_new_value, 1, CLOSE); + } + return OPERATION_DONE; + } + else + { + return RECEIVE_FIFO_EMPTY; + } + } + else + return OPERATION_ERROR; +} + +/*function which uses TX_FLUSH or RX_FLUSH command to flush the fifo buffers. if successful, output is OPERATION_DONE. + if not successful (wrong input or wrong mode of operation) output will be OPERATION_ERROR*/ +uint8_t nrf24_flush(uint8_t fifo_select) +{ + switch (fifo_select) + { + case TX_BUFFER: + if (current_mode == PTX) + { + nrf24_SPI(SPI_ON); + SPI_command = FLUSH_TX; + SPI_send_command(SPI_command); + nrf24_SPI(SPI_OFF); + return OPERATION_DONE; + } + else + return OPERATION_ERROR; + case RX_BUFFER: + if (current_mode == PRX) + { + nrf24_SPI(SPI_ON); + SPI_command = FLUSH_RX; + SPI_send_command(SPI_command); + nrf24_SPI(SPI_OFF); + return OPERATION_DONE; + } + else + return OPERATION_ERROR; + default: + return OPERATION_ERROR; + } +} + +/*must be called atleast once, which happens with calling nrf24_device function*/ +void nrf24_reset() +{ + reset_flag = RESET; + nrf24_CE(CE_OFF); + register_new_value = CONFIG_REGISTER_DEFAULT; + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = EN_AA_REGISTER_DEFAULT; + nrf24_write(EN_AA_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = EN_RXADDR_REGISTER_DEFAULT; + nrf24_write(EN_RXADDR_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = SETUP_AW_REGISTER_DEFAULT; + nrf24_write(SETUP_AW_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = RF_CH_REGISTER_DEFAULT; + nrf24_write(RF_CH_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = RF_SETUP_REGISTER_DEFAULT; + nrf24_write(RF_SETUP_ADDRESS, ®ister_new_value, 1, CLOSE); + register_new_value = STATUS_REGISTER_DEFAULT; + nrf24_write(STATUS_ADDRESS, ®ister_new_value, 1, CLOSE); + + nrf24_mode(PTX); + nrf24_flush(TX_BUFFER); + nrf24_mode(PRX); + nrf24_flush(RX_BUFFER); + + nrf24_read(STATUS_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << RX_DR) | (1 << TX_DS) | (1 << MAX_RT); + nrf24_write(STATUS_ADDRESS, ®ister_new_value, 1, CLOSE); + + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + nrf24_crc_configuration(ENABLE, 1); + nrf24_address_width(ADDRESS_WIDTH_DEFAULT); + nrf24_rf_datarate(RF_DATARATE_DEFAULT); + nrf24_rf_power(RF_PWR_DEFAULT); + nrf24_rf_channel(RF_CHANNEL_DEFAULT); + nrf24_datapipe_enable(NUMBER_OF_DP_DEFAULT); + /*nrf24_datapipe_address_configuration();*/ + /*nrf24_datapipe_ptx(1);*/ + nrf24_prx_static_payload_width(STATIC_PAYLOAD_WIDTH_DEFAULT, NUMBER_OF_DP_DEFAULT); + nrf24_automatic_retransmit_setup(RETRANSMIT_DELAY_DEFAULT, RETRANSMIT_COUNT_DEFAULT); + nrf24_auto_acknowledge_datapipe(NUMBER_OF_DP_DEFAULT); + nrf24_auto_acknowledge_datapipe(0); + nrf24_dynamic_payload(NRF24_en_dynamic_payload, NUMBER_OF_DP_DEFAULT); + nrf24_payload_without_ack(NRF24_en_no_ack); + nrf24_payload_with_ack(NRF24_en_ack); +} + +/*used by firmware to set the nrf24 mode in TRANSMITTER, RECEIVER, POWER_SAVING or TURN_OFF states, and reseting the device + if it has not been done yet. This is the initializer, and everything starts by calling nrf24_device first.It has a higher + level of abstraction than nrf24_mode and must be used by user*/ +void nrf24_device(uint8_t device_mode, uint8_t reset_state) +{ + SPI_Initializer(); + pinout_Initializer(); + delay_function(STARTUP_DELAY); + + if ((reset_state == RESET) || (reset_flag == 0)) + { + nrf24_reset(); + } + + switch (device_mode) + { + case TRANSMITTER: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, DISABLE, DISABLE); /*disabling tx interrupt mask*/ + nrf24_mode(PTX); + break; + case RECEIVER: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(DISABLE, ENABLE, ENABLE); /*disabling rx interrupt mask*/ + nrf24_mode(PRX); + delay_function(PRX_MODE_DELAY); /*100ms for PRX mode*/ + break; + case POWER_SAVING: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + nrf24_mode(STANDBYI); + break; + case TURN_OFF: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + break; + default: + nrf24_mode(POWER_DOWN); + nrf24_interrupt_mask(ENABLE, ENABLE, ENABLE); + break; + } +} + +/*setting automatic retransmit delay time and maximum number of retransmits*/ +void nrf24_automatic_retransmit_setup(uint16_t delay_time, uint8_t retransmit_count) +{ + register_new_value = 0x00; + for (; (delay_time > 250) && (register_new_value < 0X0F); delay_time -= 250) + register_new_value++; + register_new_value <<= ARD_0; + if ((retransmit_count > 0) && (retransmit_count < 16)) + register_new_value |= retransmit_count; + else + register_new_value |= 0; + nrf24_write(SETUP_RETR_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*setting auto acknoledgement on datapipes*/ +void nrf24_auto_acknowledge_datapipe(uint8_t datapipe) +{ + if (datapipe < 7) + register_new_value = (1 << datapipe); + nrf24_write(EN_AA_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*turns on or off the dynamic payload width capability*/ +void nrf24_dynamic_payload(uint8_t state, uint8_t datapipe) +{ + nrf24_auto_acknowledge_datapipe(datapipe); /*setting auto acknowledgment before setting dynamic payload*/ + nrf24_auto_acknowledge_datapipe(0); + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + if (state == ENABLE) + { + register_new_value = register_current_value | (1 << EN_DPL); /*EN_DPL bit turns dynamic payload width on or off on all datapipes*/ + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + if (datapipe < 7) + register_new_value = (1 << datapipe) - 1; /*turning on dynamic payload width on chosen datapipes, using DYNPD register*/ + nrf24_write(DYNPD_ADDRESS, ®ister_new_value, 1, CLOSE); + NRF24_en_dynamic_payload = ENABLE; + } + else + { + register_new_value = register_current_value & (~(1 << EN_DPL)); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + NRF24_en_dynamic_payload = DISABLE; + } +} + +/*function to enable or disable sending without acknowledge. + if disabled, TX must send a payload with ACK-request and receiver must be able to answer it. + manipulates EN_DYN_ACK inside FEATURE*/ +void nrf24_payload_without_ack(uint8_t state) +{ + if (state == ENABLE) + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << EN_DYN_ACK); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + } + else + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value & (~(1 << EN_DYN_ACK)); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + } +} + +/*function to enable or disable sending with acknowledge. + if disabled, the payload can be sent only without ACK-request. + manipulates EN_ACK_PAY and EN_DPL inside FEATURE as Dynamic Payload Length is required.*/ +void nrf24_payload_with_ack(uint8_t state) +{ + if (state == ENABLE) + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value | (1 << EN_ACK_PAY) | (1 << EN_DPL); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + nrf24_read(DYNPD_ADDRESS, ®ister_current_value, 1, CLOSE); + // enable dynamic payload for all pipes + register_new_value = register_current_value | 0b111111; + nrf24_write(DYNPD_ADDRESS, ®ister_new_value, 1, CLOSE); + } + else + { + nrf24_read(FEATURE_ADDRESS, ®ister_current_value, 1, CLOSE); + register_new_value = register_current_value & (~((1 << EN_ACK_PAY) | (1 << EN_DPL))); + nrf24_write(FEATURE_ADDRESS, ®ister_new_value, 1, CLOSE); + } +} + +/*on nrf24l01+ there is only one address for PTX device which must be the same as PRX data pipe address 0*/ +void nrf24_datapipe_ptx(uint8_t datapipe_number) +{ + nrf24_write(TX_ADDR_ADDRESS, &datapipe_address[datapipe_number - 1][0], current_address_width, CLOSE); +} + +/*setting the 6 datapipe addresses using the datapipe_address[][]*/ +void nrf24_datapipe_address_configuration() +{ + uint8_t address = RX_ADDR_P0_ADDRESS; + for (uint8_t counter = 0; counter < 6; counter++) + { + nrf24_write(address, &datapipe_address[counter][0], current_address_width, CLOSE); + address++; + } +} + +/*function to change static payload width, from 1 to 32 bytes in each payload*/ +void nrf24_prx_static_payload_width(uint8_t static_payload_width, uint8_t number_of_datapipes) +{ + for (uint8_t address = RX_PW_P0_ADDRESS; number_of_datapipes; number_of_datapipes--) + { + nrf24_write(address, &static_payload_width, 1, CLOSE); + address++; + } + current_payload_width = static_payload_width; +} + +/*datapipes are turned on and off using EN_RXADDR register, PRX datapipe addresses are located in RX_ADDR_Pn, TX address is located inside TX_ADDR*/ +void nrf24_datapipe_enable(uint8_t datapipe) +{ + nrf24_read(EN_RXADDR_ADDRESS, ®ister_current_value, 1, CLOSE); + if (NRF24_en_ack) { + register_current_value |= (1 << ERX_P0); + } + register_new_value = register_current_value | (1 << datapipe); + nrf24_write(EN_RXADDR_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*function to set the nrf24l01+ address width, from 3 to 5 bytes*/ +void nrf24_address_width(uint8_t address_width) +{ + if ((address_width <= 5) && (address_width >= 3)) + { + write_pointer = address_width - 2; + } + else + { + write_pointer = 3; + } + nrf24_write(SETUP_AW_ADDRESS, &write_pointer, 1, CLOSE); /*5 bytes is the maximum address width available*/ + current_address_width = address_width; +} + +/*datarate settings, you can choose between 2mbps, 1mbps, 250kbps*/ +void nrf24_rf_datarate(uint16_t rf_datarate) +{ + nrf24_read(RF_SETUP_ADDRESS, ®ister_current_value, 1, CLOSE); + register_current_value &= ~((1 << RF_DR_LOW) | (1 << RF_DR_HIGH)); + switch (rf_datarate) + { + case 2000: + register_new_value = register_current_value | (1 << RF_DR_HIGH); + break; + case 1000: + register_new_value = register_current_value; + break; + case 250: + register_new_value = register_current_value | (1 << RF_DR_LOW); + break; + default: + register_new_value = register_current_value; + break; + } + nrf24_write(RF_SETUP_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*nrf24l01+ RF power settings. 0dbm, -6dbm, -12dbm, -18dbm*/ +void nrf24_rf_power(uint8_t rf_power) +{ + nrf24_read(RF_SETUP_ADDRESS, ®ister_current_value, 1, CLOSE); + register_current_value &= ~((1 << RF_PWR_1) | (1 << RF_PWR_0)); + switch (rf_power) + { + case 0: + register_new_value = register_current_value | ((1 << RF_PWR_1) | (1 << RF_PWR_0)); + break; + case 6: + register_new_value = register_current_value | (1 << RF_PWR_1); + break; + case 12: + register_new_value = register_current_value | (1 << RF_PWR_0); + break; + case 18: + register_new_value = register_current_value; + break; + default: + register_new_value = register_current_value | (1 << RF_PWR_1); + break; + } + nrf24_write(RF_SETUP_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*read whether the current channel is busy (has traffic), needs to be called from RX mode*/ +uint8_t nrf24_rf_channel_read_busy(uint8_t rf_channel) +{ + uint8_t signals_detected; + nrf24_read(RPD_REG_ADDRESS, &signals_detected, 1, CLOSE); + if (signals_detected) { + return CHANNEL_BUSY; + } + else { + return CHANNEL_CLEAR; + } +} + +/*test whether a channel is busy (has traffic), waiting for ms_to_test*/ +uint8_t nrf24_rf_channel_test_busy(uint8_t rf_channel, uint16_t ms_to_test) +{ + if ((rf_channel <= 125) && (rf_channel >= 1)) + { + // back up old channel + uint8_t previous_channel; + nrf24_read(RF_CH_ADDRESS, &previous_channel, 1, CLOSE); + // back up old mode + uint8_t previous_mode = current_mode; + // switch to new channel + nrf24_rf_channel(rf_channel); + // switch to RX, Received Power Detector is set to 0 and begins sampling + if (previous_mode != PRX) { + nrf24_mode(PRX); + } + // wait at least 1 ms before declaring channel clear + delay_function(1 > ms_to_test ? 1 : ms_to_test); + // Received Power Detector latches to 1 if there was a signal >-64dBm for at least 40 uS consecutively since RX mode was enabled + uint8_t signals_detected = nrf24_rf_channel_read_busy(rf_channel); + // switch back to old channel + nrf24_rf_channel(previous_channel); + // switch back to old mode + if (previous_mode != PRX) { + nrf24_mode(previous_mode); + } + if (signals_detected) { + return CHANNEL_BUSY; + } + else { + return CHANNEL_CLEAR; + } + } + else + { + return CHANNEL_BUSY; + } +} + +/*nrf24l01+ RF channel selection, from 1 to 125*/ +void nrf24_rf_channel(uint8_t rf_channel) +{ + if ((rf_channel <= 125) && (rf_channel >= 1)) + { + uint8_t write_pointer = rf_channel; + nrf24_write(RF_CH_ADDRESS, &write_pointer, 1, CLOSE); + } + else + { + uint8_t write_pointer = 1; + nrf24_write(RF_CH_ADDRESS, &write_pointer, 1, CLOSE); + } +} + +/*interrupt mask settings. 3 seperate masks for RX, TX, and RT (maximum numbers of retransmission reached*/ +void nrf24_interrupt_mask(uint8_t rx_mask, uint8_t tx_mask, uint8_t max_rt_mask) +{ + nrf24_read(CONFIG_ADDRESS, ®ister_current_value, 1, CLOSE); + if (rx_mask) + register_new_value = (register_current_value) | (1 << MASK_RX_DR); + else + register_new_value &= (~(1 << MASK_RX_DR)); + if (tx_mask) + register_new_value |= (1 << MASK_TX_DS); + else + register_new_value &= (~(1 << MASK_TX_DS)); + if (max_rt_mask) + register_new_value |= (1 << MASK_MAX_RT); + else + register_new_value &= (~(1 << MASK_MAX_RT)); + + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*enabling or disabling crc in payload; setting crc encoding scheme between 1 or 2 bytes*/ +void nrf24_crc_configuration(uint8_t crc_enable, uint8_t crc_encoding_scheme) +{ + nrf24_read(CONFIG_ADDRESS, ®ister_current_value, 1, CLOSE); + if (crc_enable) + register_new_value = (register_current_value) | (1 << EN_CRC); + else + register_new_value &= (~(1 << EN_CRC)); + if (crc_encoding_scheme == 2) + register_new_value |= (1 << CRCO); + else + register_new_value &= (~(1 << CRCO)); + + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); +} + +/*mode selector: power down, standby i, standby ii, ptx, prx. used by nrf24_device function*/ +void nrf24_mode(uint8_t mode) +{ + nrf24_read(CONFIG_ADDRESS, ®ister_current_value, 1, CLOSE); + switch (mode) + { + case POWER_DOWN: + nrf24_CE(CE_OFF); + register_new_value = (register_current_value) & (~(1 << PWR_UP)); + delay_function(POWER_DOWN_DELAY); + break; + case STANDBYI: /*standby I is defined by 'PWR_UP = 1' and 'CE pin LOW'*/ + nrf24_CE(CE_OFF); + register_new_value = (register_current_value) | (1 << PWR_UP); + delay_function(STANDBYI_DELAY); + break; + case STANDBYII: /*standby ii is related to a ptx device*/ + nrf24_CE(CE_ON); + register_new_value = ((register_current_value) | (1 << PWR_UP)) & (~(1 << PRIM_RX)); + delay_function(STANDBYI_DELAY); + break; + case PTX: + nrf24_CE(CE_ON); + register_new_value = ((register_current_value) | (1 << PWR_UP)) & (~(1 << PRIM_RX)); + delay_function(STANDBYI_DELAY); + break; + case PRX: + nrf24_CE(CE_ON); + register_new_value = (register_current_value) | (1 << PWR_UP) | (1 << PRIM_RX); + delay_function(STANDBYI_DELAY); + break; + default: + nrf24_CE(CE_OFF); + register_new_value = (register_current_value) & (~(1 << PWR_UP)); + delay_function(POWER_DOWN_DELAY); + break; + } + nrf24_write(CONFIG_ADDRESS, ®ister_new_value, 1, CLOSE); + current_mode = mode; +} + +/*reads the number of bytes (data_length) from the register in nrf24l01+ (address) and stores them inside an array (value), + then closes the spi connection (spi_state = CLOSE) or leaves it open (spi_state = OPEN)*/ +void nrf24_read(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state) +{ + nrf24_SPI(SPI_ON); + SPI_command = R_REGISTER | address; /*in order to read CONFIG, then change one bit*/ + SPI_send_command(SPI_command); + SPI_command = NOP_CMD; + for (; data_length ; data_length--) + { + *value = SPI_send_command(SPI_command); + value++; + } + if (spi_state == CLOSE) + nrf24_SPI(SPI_OFF); +} + +/*writes the number of bytes (data_length) from an array (value) inside registers in nrf24l01+ (address), + then closes the spi connection (spi_state = CLOSE) or leaves it open (spi_state = OPEN)*/ +void nrf24_write(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state) +{ + nrf24_SPI(SPI_ON); + SPI_command = W_REGISTER | address; /*in order to read CONFIG, then change one bit*/ + SPI_send_command(SPI_command); + for (; data_length ; data_length--) + { + SPI_command = *value; + value++; + SPI_send_command(SPI_command); + } + if (spi_state == CLOSE) + nrf24_SPI(SPI_OFF); +} diff --git a/examples/spi_24L01_tx/nrf24l01.h b/examples/spi_24L01_tx/nrf24l01.h new file mode 100644 index 0000000000000000000000000000000000000000..f3552406a2500078ccae4c6495cf957161efc398 --- /dev/null +++ b/examples/spi_24L01_tx/nrf24l01.h @@ -0,0 +1,219 @@ +#ifndef NRF24L01_H +#define NRF24L01_H +/*nrf24l01: MSbit to LSbit, LSbyte to MSbyte*/ +#include <stdio.h> +#include <stdint.h> + +#define STARTUP_DELAY 150 /*in milliseconds*/ +#define POWER_DOWN_DELAY 2 +#define STANDBYI_DELAY 2 +#define PRX_MODE_DELAY 100 +#define ADDRESS_WIDTH_DEFAULT 5 /*address width in bytes, for default value*/ +#define RF_CHANNEL_DEFAULT 32 +#define RF_DATARATE_DEFAULT 1000 /*250, 1000, 2000*/ +#define RF_PWR_DEFAULT 6 /*0, -6, -12, -18*/ +#define STATIC_PAYLOAD_WIDTH_DEFAULT 1 /*for static payload mode, configurable between 1 and 32 bytes for PRX device ONLY (RX_PW_Pn, n for data pipe n)(no register for payload length in PTX device)*/ +#define NUMBER_OF_DP_DEFAULT 1 /*number of datapipes, 1 to 6*/ +#define RETRANSMIT_DELAY_DEFAULT 500 /*in uS*/ +#define RETRANSMIT_COUNT_DEFAULT 3 + +#define OPEN 1 +#define CLOSE 0 +#define ENABLE 1 +#define DISABLE 0 +#define SPI_OFF 1 +#define SPI_ON 0 +#define CE_OFF 0 +#define CE_ON 1 + +#define CONFIG_REGISTER_DEFAULT 0X08 +#define EN_AA_REGISTER_DEFAULT 0X3F +#define EN_RXADDR_REGISTER_DEFAULT 0X00 +#define SETUP_AW_REGISTER_DEFAULT 0X03 +#define SETUP_RETR_REGISTER_DEFAULT 0X03 +#define RF_CH_REGISTER_DEFAULT 0X02 +#define RF_SETUP_REGISTER_DEFAULT 0X0E +#define STATUS_REGISTER_DEFAULT 0X0E +#define MAXIMUM_NUMBER_OF_DATAPIPES 6 + +#define POWER_DOWN 0X00 +#define STANDBYI 0X01 +#define STANDBYII 0X02 +#define PTX 0X03 +#define PRX 0X04 +#define DEVICE_NOT_INITIALIZED 0X05 + +#define TRANSMITTER 0X00 +#define RECEIVER 0X01 +#define POWER_SAVING 0X02 +#define TURN_OFF 0X03 + +#define RESET 1 +#define NO_RESET 0 +#define NO_ACK_MODE 1 +#define ACK_MODE 0 +#define TRANSMIT_BEGIN 1 +#define TRANSMIT_FAIL 0 +#define TRANSMIT_IN_PROGRESS 0 +#define TRANSMIT_DONE 1 +#define TRANSMIT_FAILED 0XFF +#define OPERATION_DONE 1 +#define OPERATION_ERROR 0 +#define RECEIVE_FIFO_EMPTY 2 +#define TX_BUFFER 1 +#define RX_BUFFER 0 +// return states for nrf24_rf_channel_test_busy +#define CHANNEL_CLEAR 0 +#define CHANNEL_BUSY 1 + +/*bits definition section*/ +#define MASK_RX_DR 6 /*mask interrupt caused by RX_DR: 1 interrupt not reflected on IRQ pin (IRQ is active low), inside CONFIG register*/ +#define MASK_TX_DS 5 /*mask interrupt caused by TX_DS: 1 interrupt not reflected on IRQ pin (IRQ is active low), inside CONFIG register*/ +#define MASK_MAX_RT 4 /*mask interrupt caused by MAX_RT means maximum number of retransmissions reached: 1 interrupt not reflected on IRQ pin (IRQ is active low), inside CONFIG register*/ +#define EN_CRC 3 /*enale CRC, forced high if one of the bits in EN_AA is high, inside CONFIG register*/ +#define CRCO 2 /*CRC encoding scheme, 0 is 1 byte, 1 is 2 bytes, inside CONFIG register*/ +#define PWR_UP 1 /*1 is power up, inside CONFIG register*/ +#define PRIM_RX 0 /*RX/TX control, 1: PRX, inside CONFIG register*/ +#define ENAA_P5 5 /*enable auto acknowledgement data pipe 5*/ +#define ENAA_P4 4 +#define ENAA_P3 3 +#define ENAA_P2 2 +#define ENAA_P1 1 +#define ENAA_P0 0 +#define ERX_P5 5 /*part of EN_RXADDR, enable data pipe 5*/ +#define ERX_P4 4 +#define ERX_P3 3 +#define ERX_P2 2 +#define ERX_P1 1 +#define ERX_P0 0 +#define AW_1 1 /*RX/TX address field width, 00 illegal, 01 3 bytes, 10 4 bytes, 11 5 bytes*/ +#define AW_0 0 +#define ARD_3 7 /*auto retransmit delay, 0000 250us, 0001 500us ...> 1111 4000us*/ +#define ARD_2 6 +#define ARD_1 5 +#define ARD_0 4 +#define ARC_3 3 /*auto retransmit count, 0000 retransmit deisabled, 1111 up to 15 retransmit on failure of AA. (inside SETUP_RETR register)*/ +#define ARC_2 2 +#define ARC_1 1 +#define ARC_0 0 +#define RF_CH_6 6 /*sets the frequencvy channel nRF24L01+ operates on*/ +#define RF_CH_5 5 +#define RF_CH_4 4 +#define RF_CH_3 3 +#define RF_CH_2 2 +#define RF_CH_1 1 +#define RF_CH_0 0 +#define CONT_WAVE 7 /*enables continuous carrier transmit when high*/ +#define RF_DR_LOW 5 /*sets the RF data rate to 250kbps*/ +#define PLL_LOCK 4 /*force PLL lock signal. used for testing ONLY*/ +#define RF_DR_HIGH 3 /*select between high speed data rates and works ONLY when RF_DR_LOW is 0. 0 for 1Mbps, 1 for 2Mbps*/ +#define RF_PWR_1 2 +#define RF_PWR_0 1 +#define RX_DR 6 /*IRQ for new packet in RX FIFO (newly received)*/ +#define TX_DS 5 /*IRQ for ACK received in TX mode*/ +#define MAX_RT 4 +#define RX_P_NO_2 3 +#define RX_P_NO_1 2 +#define RX_P_NO_0 1 +#define PLOS_CNT_3 7 /*inside OBSERVE_TX register, counts the total number of retransmissions since last channel change. reset by writing to RF_CH*/ +#define PLOS_CNT_2 6 +#define PLOS_CNT_1 5 +#define PLOS_CNT_0 4 +#define ARC_CNT_3 3 /*inside OBSERVE_TX register, counts the number of retransmissions for current transaction. reset by initiating new transaction*/ +#define ARC_CNT_2 2 +#define ARC_CNT_1 1 +#define ARC_CNT_0 0 +#define RPD 0 /*received power detector, if received power is less than -64dbm, RPD = 0*/ +#define TX_REUSE 6 +#define TX_FULL 5 +#define TX_EMPTY 4 +#define RX_FULL 1 +#define RX_EMPTY 0 +#define DPL_P5 5 +#define DPL_P4 4 +#define DPL_P3 3 +#define DPL_P2 2 +#define DPL_P1 1 +#define DPL_P0 0 /*must be set on PTX in dynamic payload length mode*/ +#define EN_DPL 2 /*set to enable dynamic payload length*/ +#define EN_ACK_PAY 1 /*used to enable auto acknowledgement with payload in PRX (inside FEATURE register)*/ +#define EN_DYN_ACK 0 /**/ + +/*registers definition section*/ +#define CONFIG_ADDRESS 0X00 +#define EN_AA_ADDRESS 0X01 /*enable auto acknowledgement feature*/ +#define EN_RXADDR_ADDRESS 0X02 /*register containing bits to enable 6 data pipes individually*/ +#define SETUP_AW_ADDRESS 0X03 /*address field length is configured in here to be 3, 4 or 5 bytes long*/ +#define SETUP_RETR_ADDRESS 0X04 /*setup ARC bits to configure auto retransmission count*/ +#define RF_CH_ADDRESS 0X05 +#define RF_SETUP_ADDRESS 0X06 +#define STATUS_ADDRESS 0X07 /*contains RX_DR, TX_DS, MAX_RT, RX_P_NO, TX_FULL, send R_REGISTER then NOP to read*/ +#define OBSERVE_TX_ADDRESS 0X08 /*contains ARC_CNT and PLOS_CNT, two counters for retransmission. these counters could be used to assess the network quality*/ +#define RPD_REG_ADDRESS 0X09 +#define RX_ADDR_P0_ADDRESS 0X0A /*the address for PRX device. if a packet contains this address, enhanced shockburst starts validating the packet*/ +#define RX_ADDR_P1_ADDRESS 0X0B /*a total of 6 unique addresses could be assigned to a PRX device (Multiceiver feature)*/ +#define RX_ADDR_P2_ADDRESS 0X0C /*these addresses must NOT be the same*/ +#define RX_ADDR_P3_ADDRESS 0X0D +#define RX_ADDR_P4_ADDRESS 0X0E +#define RX_ADDR_P5_ADDRESS 0X0F +#define TX_ADDR_ADDRESS 0X10 /*40 bits long register, transmit address, used for a PTX device only. configure address legth in SETUP_AW register. set RX_ADDR_P0 equal to this address to handle automatic acknowledge*/ +#define RX_PW_P0_ADDRESS 0X11 /*these registers are for setting the static payload length in static payload length mode (receiver side)*/ +#define RX_PW_P1_ADDRESS 0X12 +#define RX_PW_P2_ADDRESS 0X13 +#define RX_PW_P3_ADDRESS 0X14 +#define RX_PW_P4_ADDRESS 0X15 +#define RX_PW_P5_ADDRESS 0X16 +#define FIFO_STATUS_ADDRESS 0X17 +#define DYNPD_ADDRESS 0X1C /*on receiver side (RX mode), this register must be set to enable dynamic payload length. a PTX in dynamic mode, must have the DYNPD_P0 set*/ +#define FEATURE_ADDRESS 0X1D /*contains the EN_DPL bit to enable dynamic payload length*/ + +/*commands definition section*/ +#define R_REGISTER 0X00 /*read commmand and STATUS registers, 5 bit register map address*/ +#define W_REGISTER 0X20 /*write commmand and STATUS registers, 5 bit register map address, executable in POWER DOWN or STANDBY modes only*/ +#define R_RX_PAYLOAD 0X61 /*read RX payload, 1-32 bytes. read operation starts at byte 0. payload is deleted from FIFO after its read*/ +#define W_TX_PAYLOAD 0XA0 /*write TX payload, starts at byte 0, 1-32 bytes*/ +#define FLUSH_TX 0XE1 /*flush TX FIFO, used in TX mode*/ +#define FLUSH_RX 0XE2 /*flush RX FIFO, used in RX mode*/ +#define REUSE_TX_PL 0XE3 /*used for a PTX device, reuse last transmitted payload for an exact number. alternative to auto retransmission*/ +#define R_RX_PL_WID 0X60 /*command for receiver side, in order to read the payload length in dynamic payload length mode*/ +#define W_ACK_PAYLOAD 0XA0 /*used in RX mode, to write payload in TX FIFO and later transmit the payloads along with ACK packet to PTX, if DPL is enabled*/ +#define W_TX_PAYLOAD_NOACK 0XB0 /*used in TX mode, disables AUTOACK on this specific packet. must be first enabled in FEATURE register by setting the EN_DYN_ACK bit. if used, PTX will not wait for ACK and goes directly to standby I*/ +#define NOP_CMD 0XFF /*might be used to read the status register*/ + +void nrf24_reset(); +void nrf24_device(uint8_t device_mode, uint8_t reset_state); +uint8_t SPI_send_command(uint8_t command); +void pinout_Initializer(); +void SPI_Initializer(); +void nrf24_mode(uint8_t mode); +void nrf24_SPI(uint8_t input); +void nrf24_CE(uint8_t input); +void nrf24_address_width(uint8_t address_width); +uint8_t nrf24_rf_channel_read_busy(uint8_t rf_channel); +uint8_t nrf24_rf_channel_test_busy(uint8_t rf_channel, uint16_t ms_to_test); +void nrf24_rf_channel(uint8_t rf_channel); +void nrf24_rf_power(uint8_t rf_power); +void nrf24_rf_datarate(uint16_t rf_datarate); +void nrf24_read(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state); +void nrf24_write(uint8_t address, uint8_t *value, uint8_t data_length, uint8_t spi_state); +void delay_function(uint32_t duration_ms); +void nrf24_crc_configuration(uint8_t crc_enable, uint8_t crc_encoding_scheme); +void nrf24_interrupt_mask(uint8_t rx_mask, uint8_t tx_mask, uint8_t max_rt_mask); +void nrf24_datapipe_enable(uint8_t number_of_datapipes); +void nrf24_prx_static_payload_width(uint8_t static_payload_width, uint8_t number_of_datapipes); +void nrf24_datapipe_address_configuration(); +void nrf24_datapipe_ptx(uint8_t datapipe_number); +void nrf24_automatic_retransmit_setup(uint16_t delay_time, uint8_t retransmit_count); +void nrf24_auto_acknowledge_datapipe(uint8_t datapipe); +void nrf24_payload_without_ack(uint8_t state); +void nrf24_payload_with_ack(uint8_t state); +void nrf24_dynamic_payload(uint8_t state, uint8_t datapipe); +void nrf24_device(uint8_t device_mode, uint8_t reset_state); +void nrf24_send_payload(uint8_t *payload, uint8_t payload_width); +uint8_t nrf24_receive(uint8_t *payload, uint8_t payload_width); +uint8_t nrf24_transmit(uint8_t *payload, uint8_t payload_width, uint8_t acknowledgement_state); +uint8_t nrf24_transmit_status(); +void nrf24_dynamic_ack(uint8_t state); +uint8_t nrf24_flush(uint8_t fifo_select); + +#endif diff --git a/examples/spi_24L01_tx/nrf24l01_low_level.c b/examples/spi_24L01_tx/nrf24l01_low_level.c new file mode 100644 index 0000000000000000000000000000000000000000..811fd078f3cf9457ad9dd90046ae671676cd1541 --- /dev/null +++ b/examples/spi_24L01_tx/nrf24l01_low_level.c @@ -0,0 +1,71 @@ +#define SYSTEM_CORE_CLOCK 48000000 +#define APB_CLOCK SYSTEM_CORE_CLOCK +#include "../../ch32v003fun/ch32v003fun.h" + + +#define CH32V003_SPI_SPEED_HZ 1000000 +#define CH32V003_SPI_DIRECTION_2LINE_TXRX +#define CH32V003_SPI_CLK_MODE_POL0_PHA0 //leading = rising trailing = falling sample on leading default if you're unsure +#define CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL // toggle manually! +#define CH32V003_SPI_IMPLEMENTATION +#include "ch32v003_SPI.h" +#include "nrf24l01.h" + +/*start of low level functions, specific to the mcu and compiler*/ + +/*delay in miliseconds*/ +void delay_function(uint32_t duration_ms) +{ + Delay_Ms(duration_ms); +} + +/*contains all SPI configuations, such as pins and control registers*/ +/*SPI control: master, interrupts disabled, clock polarity low when idle, clock phase falling edge, clock up tp 1 MHz*/ +void SPI_Initializer() +{ + SPI_init(); + SPI_begin_8(); +} + +/*contains all CSN and CE pins gpio configurations, including setting them as gpio outputs and turning SPI off and CE '1'*/ +void pinout_Initializer() +{ + // CSN on PC0 + GPIOC->CFGLR &= ~(0xf<<(4*0)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*0); + // CSN high + GPIOC->BSHR = (1<<0); + // CE on PC4 + GPIOC->CFGLR &= ~(0xf<<(4*4)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4); + // CE HIGH + GPIOC->BSHR = (1<<4); +} + +/*CSN pin manipulation to high or low (SPI on or off)*/ +void nrf24_SPI(uint8_t input) +{ + if (input > 0) { + GPIOC->BSHR = (1<<(0+0)); + } + else { + GPIOC->BSHR = (1<<(16+0)); + } +} + +/*1 byte SPI shift register send and receive routine*/ +uint8_t SPI_send_command(uint8_t command) +{ + return SPI_transfer_8(command); +} + +/*CE pin maniplation to high or low*/ +void nrf24_CE(uint8_t input) +{ + if (input > 0) { + GPIOC->BSHR = (1<<(0+4)); + } + else { + GPIOC->BSHR = (1<<(16+4)); + } +} diff --git a/examples/spi_24L01_tx/spi_24L01_tx.c b/examples/spi_24L01_tx/spi_24L01_tx.c new file mode 100644 index 0000000000000000000000000000000000000000..46c5f519431ebfe6d690b4a313e15088dd8b8ecf --- /dev/null +++ b/examples/spi_24L01_tx/spi_24L01_tx.c @@ -0,0 +1,150 @@ +/* + * Example for 24L01+ over SPI, using https://github.com/ebrezadev/nRF24L01-C-Driver + * 04-26-2023 recallmenot + */ + +#define SYSTEM_CORE_CLOCK 48000000 +#define APB_CLOCK SYSTEM_CORE_CLOCK + +#include "../../ch32v003fun/ch32v003fun.h" +#include <stdio.h> +#include "nrf24l01.h" + + + +#define TIME_GAP 1000 +uint8_t ascending_number = 0x00; +char txt[16]; + + + +//######### debug fn + +void uint8_to_binary_string(uint8_t value, char* output, int len) { + for (int i = 0; i < len; i++) { + output[len - i - 1] = (value & 1) ? '1' : '0'; + value >>= 1; + } + output[len] = '\0'; +} + + +void print_reg(char* name, uint8_t addr) { + char str[9]; + uint8_t REG; + nrf24_read(addr, ®, 1, CLOSE); + uint8_to_binary_string(REG, str, 8); + printf(" %s register: %s\n\r", name, str); +} + + +void print_debug() { + print_reg("FEATURE ", FEATURE_ADDRESS); + print_reg("TX OBSERVE ", OBSERVE_TX_ADDRESS); + print_reg("STATUS ", STATUS_ADDRESS); + print_reg("RX_PW_P0 ADDR", RX_ADDR_P0_ADDRESS); + print_reg("TX ADDR ", TX_ADDR_ADDRESS); + print_reg("EN_AA ", EN_AA_ADDRESS); + print_reg("EN_RXADDR ", EN_RXADDR_ADDRESS); +} + + + +//######### LED fn + +// led is PD4 to LED1 on board, which is (-) +inline void led_on() { + GPIOD->BSHR = 1<<(16+4); +} + +inline void led_off() { + GPIOD->BSHR = 1<<4; +} + + + +//######### TX fn + +uint8_t sendnumber() { + return nrf24_transmit(&ascending_number, 1, ACK_MODE); +} + +// function prototype (declaration), definition in "ch32v003fun.c" +int mini_snprintf(char* buffer, unsigned int buffer_len, const char *fmt, ...); + +uint8_t sendstr() { + mini_snprintf(txt, sizeof(txt), "Hello, %u", ascending_number); + printf("\n\rsending %s\n\r", txt); + return nrf24_transmit((uint8_t*)txt, 16, ACK_MODE); +} + +void send() { + // to switch between sending an uint8_t and a 16-byte-char-array, just uncomment one of these two: + //uint8_t tx_cmd_status = sendnumber(); + uint8_t tx_cmd_status = sendstr(); + switch (tx_cmd_status) { + case TRANSMIT_BEGIN: + led_on(); + printf("*** sending package\n\r"); + break; + case TRANSMIT_FAIL: + printf("EEE unable to send package\n\r"); + break; + } + + Delay_Ms(50); // give the nRF some time to send + print_debug(); + + switch (nrf24_transmit_status()) { + case TRANSMIT_DONE: + printf("*OK sent: %u\n\r", ascending_number); + led_off(); + break; + case TRANSMIT_FAILED: + printf("EEE no ACK received!!\n\r"); + break; + case TRANSMIT_IN_PROGRESS: + printf("EEE still transmitting???\n\r"); + break; + } +} + + + +//######### MAIN + + +int main() +{ + SystemInit48HSI(); + + // start serial @ default 115200bps + SetupUART( UART_BRR ); + + printf("\r\r\n\nspi_24L01_TX\n\r"); + + printf("initializing radio as TX..."); + nrf24_device(TRANSMITTER, RESET); + nrf24_rf_power(18); //default TX power is -6dB, pretty strong, reduce to -18dBm for one room + printf("done.\n\r"); + + print_debug(); + + // GPIO D4 Push-Pull for foreground blink + RCC->APB2PCENR |= RCC_APB2Periph_GPIOD; + GPIOD->CFGLR &= ~(0xf<<(4*4)); + GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4); + + Delay_Ms(1000); + + printf("looping...\n\r"); + while(1) + { + Delay_Ms( TIME_GAP ); + send(); + + ascending_number++; + + printf("*** next number: %u\n\r", ascending_number); + } +}