PIC32MX: High-speed Wireless Communication
Original Assignment
Do not erase this section!
Your assignment is to create the circuit and code that allows two PIC32s to use wireless communication at high speeds using the Transceiver nRF24L01+ Module with Chip Antenna from Sparkfun.
Read up on the nRF24L01+ Module commands and create functions to allow a PIC32 to initialize and send and receive data.
Test your code by sending 1000 bytes of data from one PIC32 to the other and echo the data back. How long does this transmission take? How far can you separate the PIC32s before the transmission is unreliable?
Overview
This wiki describes how to wire a nRF24L01+ chip ("wireless chip") to the transmitting PIC32 board ("transmitter") and receiving PIC32 board ("receiver"), and includes annotated code describing the functions that facilitate the wireless communication.
To operate the system, you must properly connect the wireless chips to the PIC32s, load the transmitter code onto the PIC that you wish to act as a transmitter, load the receiver code onto the PIC that you wish to act as a receiver, properly connect an RS232 cable to the PIC and open a serial communication program (e.g. HyperTerminal or putty), and turn the NU32 boards on. If the system works, the transmitting PIC will broadcast any characters received from the PC via RS232, and the receiving PIC will display what it receives from wireless link on the PC via RS232. The payload width can be adjusted using the "[" and "]" keys on both the transmitting and receiving PICs. Both wireless chips must be set to the same payload width in order to communicate. Payload widths may vary from 1 to 32. The transmitting PIC will not transmit until enough characters have been entered to fill a payload. Once enough characters have been entered into the transmitting PIC, the serial display will move to a new line to signal transmission.
Chip Basics
The nrf24L01+ Module is a breakout of the nrf24L01+ 2.4 GHz transceiver, with an on board antenna. In order to get a wireless link up and running, the following parameters must be consistent between chips:
Channels
The channel of the nrf24L01+ chip is the frequency that it broadcasts and receives data from. The channel can range from 2.4 GHz to 2.525 GHz, at intervals no more than 1 MHz. The exact distance between channels is dependent on the on air data rate. Two wireless chips must be tuned to the same channel in order to communicate. See the Air Data Rate section for necessary channel spacing.
Air Data Rate
The air data rate is the rate at which the chip transmits bits wirelessly (and the rate at which it looks for them). The nrf24L01+ chip has three selectable data rates: 250 kbps, 1 Mbps, and 2 Mbps. Faster data rates draw less current, but require a greater distance between channels. Slower data rates give you the option of more channels and better receiver sensitivity, for the obvious trade off of speed. Data rates of 250 kbps and 1 Mbps require channel spacing of at least 1 MHz (allowing for 125 unique channels), and a data rate of 2 Mbps requires spacing of 2 MHz between channels (allowing for 62 channels). Two wireless chips will not be able to communicate if they are not set to the same on air data rate.
Payload Width
The payload is the actual data you are trying to send over a wireless link, and the payload width is how many bytes are contained within each payload. The nfr24L01+ is capable of supporting payloads up to 32 bytes. A transmitter and receiver must be set to the same payload in order to communicate. Transmission speeds for different sizes payloads are analyzed below.
Addresses & Pipes
Every wireless transmission is preceded by the address of the intended receiver. The nrf24L01+ chip has 6 different receiving pipes, which means it may act as 6 receivers with 6 different addresses. Addresses may be three, four, or five bytes long. The addresses for pipe 0 and 1 may be completely different, but the addresses for pipes 2, 3, and 4 may only differ from the address of pipe 1 by their least significant byte. All active pipes feed into the same 3-packet-deep incoming transmission queue. Each pipe may also be set to a expect a different payload width.
Circuit
The wireless chip has 8 pins that need to be wired to the PIC (all I/O are relative to PIC):
- Vcc: +3.3 Vdc supply
- CE: uC pin C1 (input)
- CSN: uC pin C2 (output)
- SCK: uC pin D10, configured as SCK(SPI) (output)
- MOSI: uC pin D0, configured as SDO(SPI) (output)
- MISO: uC pin C4, configured as SDI(SPI) (input)
- IRQ: uC pin B0 (input)
- GND: common digital ground
The +3.3V and ground are provided by the NU32 board. The term "uC" above stands for microcontroller, which is the PIC32 in our case.
Descriptions of the wireless chip's pins can be found in Brennan Ball's DIYembedded Tutorial 0. The SCK, MOSI, and MISO pins interface with the SPI on the PIC32 which allows for communication between the PIC and the wireless chip. The GPIO pins will be initialized as digital inputs or outputs in the code to enable/disable the wireless chip and check the wireless data transfer/receive stats.
In order to print information from the PIC to the PC, we need to wire an PIC_RS232 cable's transfer and receive lines to the UART ports of the NU32 board (F4, F5).
Code
Driver
Besides the main C files for the transmitter and receiver, the project also needs the nRF24L01.h header file and C file. These driver files were created by Brennan Ball, and are explained in detail in his tutorials. The following things must be changed in the header file available on his website to make it compatible with the NU32 board:
- pin registers and masks must be set to the appropriate values
- the "HardwareProfile.h" NU32 specific file must be included
These changes are accounted for the the driver available on this page. All of the SPI functions trace back to a single function to send and read a single byte, which must be defined in the main c file.
This function is available here:
unsigned char spi_send_read_byte(unsigned char byte) {
unsigned short txData, rxData; // transmit, receive characters
int chn = 1; // SPI channel to use (1 or 2)
txData = byte; // take inputted byte and store into txData
SpiChnPutC(chn, txData); // send data
rxData = SpiChnGetC(chn); // retreive over channel chn the received data into rxData
return rxData;
}
Transmitter Code
Wirelessly transmitting data requires three steps:
- initialize nrf24L01+ as transmitter
- transmit packet
- wait for payload to be sent
A basic outline for such a procedure would look like:
char data = 'x'; //data to send
int width = 1; //width of payload
nrf24l01_initialize_debug(false, width, false); //initialize the 24L01 to the debug configuration as TX, 1 data byte, and auto-ack disabled
nrf24l01_write_tx_payload(&data, width, true); //add char to Tx queue, and transmit immediately (char * data, int width, bool transmit_now)
while(!(nrf24l01_irq_pin_active() && nrf24l01_irq_tx_ds_active())); //wait until IRQ status tells us that it is done transmitting
nrf24l01_irq_clear_all(); //clear all interrupts in the 24L01
Receiver Code
Similarly, receiving data from a wireless link requires three steps:
- initialize nrf24L01+ as receiver
- wait for incoming packet
- write packet to memory
That would look like:
char data; //variable to recieve incoming data
int width = 1; //width of payload
nrf24l01_initialize_debug(true, width, false); //initialize the 24L01 to the debug configuration as RX, 1 data byte, and auto-ack disabled
while(!(nrf24l01_irq_pin_active() && nrf24l01_irq_rx_dr_active())); //wait to receive a packet
nrf24l01_read_rx_payload(data, width); //get the payload into data
nrf24l01_irq_clear_all(); //clear interrupts
Data Rates
Basic timed tests were done to find out how long it took to transmit and receive different sized payloads.