Tuto. 13 - RFM12 Radio
PIC tutorial 13 - SPI Radio tranceiver
Fig. 1a Board 1 with RFM12 board
Description:
We will now look into wireless communication, by implementint the very affordable RFM12 transceiver module from Hoperf. The tutorial sends a sentence from one emitter, and it is received on the receiver. The RFM12 library used for the transmitter is using polling, and is fairly simple to use. It allows to easily connect the RFM12 transceiver to any PIC based project.
Also the RFM12 module doesn't have "channels", channels are implemented as frequencies. The default number of channel is 16, and the default channel used is channel 0.
The library can work with 433, 868 or 915 MHz modules.
The RFM12 modules are connected to the host processor with a SPI connector.
The emitter will transmit the sentence "Hello World !" 4 times a second. The receiver waits to receive a sentence on the same channel, and display the result on a graphic LCD screen. It displays the channel used, the number of bytes received, the type/address of the sentence, and the first 13 characters of the sentence.
The led on the transmitter board lights up everytime a sentence is received (4 times a second).
The boards can be powered by one of the PSU boards (powered from USB port), or between 2.5 to 5V.
Requirements:
Processor board2 boards, (boards 1 and 4 in example)
Ideally with hardware SPI interface
Extension(s)2 RFM12 board, graphic LCD
Power supplyPSU 1, 2 or 3. Depends on board used.
LanguageCCS C
ProgrammerPicKIT 2
Program:
Emitter with board 1 and RFM12 module
This is the board sending the sentence over the radio module. The program is extremely simple.
#define board_id 1
#define RF12_TRANSMIT_ONLY 1 // Do not enable receiver on transmitter
#include "lib\mainboard.c"
#include "lib\SPI_lib.c"
#include "lib\RF12_lib_1.0.c"
int8 packet[14]={"Hello world !"};
void main() {
boot_up();
Init_SPI(); // Init SPI interface
delay_ms(500); // startup delay for tranmitter
rf12_init(); // Init RF12 transceiver
while (TRUE) {
if (rf12_buffer.status == rf12buf_Free) {
rf12_transmit(1, sizeOf(packet)-1, packet);
}
delay_ms(250);
}
}
As usual, the program defined the processor board used, and loads the library files.
Then, a variable is defined to tell the RFM12 library to exclude the receive functions. It saves memory space, and disable the receiver on the module.
We then load the SPI library and the RFM12 library, which defines functions to communicate with the SPI interface and control the RFM12 module.
Library 1.0 is used, as it is the one using polling. Version 2.0 will use interrupt, and will be explained in a future tutorial.
The sentence to transmit is declared as packet. It is an array of 13 characters. The array is 14 bytes long to allow for the null character at the end of it.
The main program initialises the board with the function boot_up, , initialises the SPI interface and waits 500ms to give time to the radio module to start up.
The radio module is then initialised, which configures it and set it for the predefined channel (Channel 0 if not declared before).
The main loop checks if the transmitter buffer is free (not sending or receiving). If so, the module is ready to send the sentence. It uses the function rf12_transmit, which takes the type/address of the sentence, the number of bytes to send and what to send. Packet is sent without its last byte (null).
We then wait 250ms before looping again.
The type/address is an byte to specify what type of packet is transmitted. It is not necessary here, but can be used to specify the address of the module that the sentence is for, or to specify what type of packet is transmitted to know how to process it.
The board I used for the emitter is board 1, with a PIC 16F819 processor.
Now to the RFM12 library:
#ifndef _SPI_LIB
#error SPI library not loaded
#else
#ifndef _RF12_LIB
#define _RF12_LIB
#ifndef DEBUG_MODE
#define DEBUG_MODE 0
#endif
// Status
enum RF12_ERROR{rf12_NoError, rf12_Busy, rf12_Tx_over, rf12_Rx_chk, rf12_Rx_ovf, rf12_BadChannel}; // No error, Packet to transmit too big, received checksum mismatch, receive overflow
enum RF12_BUF_STATUS{rf12buf_Free, rf12buf_Busy, rf12buf_Ready, rf12buf_Error};
enum RF12_STATUS{rf12_Off, rf12_Idle, rf12_Tx, rf12_Rx};
// Commands
#define RF12_CMD_CFG 0x8000 // 1 - Configuration settings
#define RF12_CFG_EL 0x80 // Enable data register
#define RF12_CFG_EF 0x40 // FIFO enable
#define RF12_BAND_433 0x10 // Frequency bands
#define RF12_BAND_868 0x20
#define RF12_BAND_915 0x30
#define RF12_XTAL_12PF 0x07 // Crystal load capacitance
#define RF12_CMD_PWRMGT 0x8200 // 2 - Power management
#define RF12_PWRMGT_ER 0x80 // Receiver chain (ebb, es, ex set)
#define RF12_PWRMGT_EBB 0x40 // Receiver baseband
#define RF12_PWRMGT_ET 0x20 // Amplifier and PLL to start tx
#define RF12_PWRMGT_ES 0x10 // Synthesizer
#define RF12_PWRMGT_EX 0x08 // Crystal oscillator
#define RF12_PWRMGT_EB 0x04 // Low battery detector
#define RF12_PWRMGT_EW 0x02 // Wake up timer
#define RF12_PWRMGT_DC 0x01 // Clock output disable
#define RF12_CMD_FREQUENCY 0xA000 // 3 - Frequency setting
#define RF12_FREQUENCY_CALC_433(f) (((f)-430000000UL)/2500UL) // Macros for calculating frequency value out of frequency in Hz
#define RF12_FREQUENCY_CALC_868(f) (((f)-860000000UL)/5000UL)
#define RF12_FREQUENCY_CALC_915(f) (((f)-900000000UL)/7500UL)
#define RF12_CMD_DATARATE 0xC600 // 4 - Data rate
#define RF12_DATARATE_CS 0x80
#define RF12_DATARATE_CALC_HIGH(d) ((int8)((10000000.0/29.0/d)-1)) //calculate setting for datarates >= 2700 Baud
#define RF12_DATARATE_CALC_LOW(d) (RF12_DATARATE_CS|(int8)((10000000.0/29.0/8.0/d)-1)) //calculate setting for datarates < 2700 Baud
#define RF12_CMD_RXCTRL 0x9000 // 5 - Receiver control
#define RF12_RXCTRL_P16_VDI 0x400 // VDI enable
#define RF12_RXCTRL_VDI_FAST 0x000 // VDI response times
#define RF12_RXCTRL_VDI_MEDIUM 0x100
#define RF12_RXCTRL_VDI_SLOW 0x200
#define RF12_RXCTRL_VDI_ALWAYS_ON 0x300
#define RF12_RXCTRL_BW_400 0x20 // Bandwidths
#define RF12_RXCTRL_BW_340 0x40
#define RF12_RXCTRL_BW_270 0x60
#define RF12_RXCTRL_BW_200 0x80
#define RF12_RXCTRL_BW_134 0xA0
#define RF12_RXCTRL_BW_67 0xC0
#define RF12_RXCTRL_LNA_0 0x00 // LNA gain (-dB)
#define RF12_RXCTRL_LNA_6 0x08
#define RF12_RXCTRL_LNA_14 0x10
#define RF12_RXCTRL_LNA_20 0x18
#define RF12_RXCTRL_RSSI_103 0x00 // RSSI threshold (-dBm)
#define RF12_RXCTRL_RSSI_97 0x01
#define RF12_RXCTRL_RSSI_91 0x02
#define RF12_RXCTRL_RSSI_85 0x03
#define RF12_RXCTRL_RSSI_79 0x04
#define RF12_RXCTRL_RSSI_73 0x05
#define RF12_CMD_DATAFILTER 0xC228 // 6 - Filter
#define RF12_DATAFILTER_AL 0x80 // Clock recovery auto lock
#define RF12_DATAFILTER_ML 0x40 // Clock recovery lock control mode
#define RF12_DATAFILTER_S 0x10 // Analog or digital filter
#define RF12_CMD_FIFORESET 0xCA00 // 7 - Fifo and reset mode
#define RF12_FIFORESET_SP 0x08 // Synch pattern length
#define RF12_FIFORESET_AL 0x04 // Synch pattern mode
#define RF12_FIFORESET_FF 0x02 // FIFO fill enabled
#define RF12_FIFORESET_DR 0x01 // Disable highly sensitive reset
#define RF12_CMD_READ 0xB000 // 8 - FIFO read command (with bit ef set)
#define RF12_CMD_AFC 0xC400 // 9 - AFC
#define RF12_AFC_AUTO_OFF 0x00 // AFC auto mode
#define RF12_AFC_AUTO_ONCE 0x40
#define RF12_AFC_AUTO_VDI 0x80
#define RF12_AFC_AUTO_KEEP 0xC0
#define RF12_AFC_LIMIT_OFF 0x00 // Range limit
#define RF12_AFC_LIMIT_16 0x10
#define RF12_AFC_LIMIT_8 0x20
#define RF12_AFC_LIMIT_4 0x30
#define RF12_AFC_ST 0x08 // control bits
#define RF12_AFC_FI 0x04
#define RF12_AFC_OE 0x02
#define RF12_AFC_EN 0x01
#define RF12_CMD_TXCONF 0x9800 // 10 - Tx configuration
#define RF12_TXCONF_MP 0x100
#define RF12_TXCONF_POWER_0 0x00 // Output power (-dB)
#define RF12_TXCONF_POWER_3 0x01
#define RF12_TXCONF_POWER_6 0x02
#define RF12_TXCONF_POWER_9 0x03
#define RF12_TXCONF_POWER_12 0x04
#define RF12_TXCONF_POWER_15 0x05
#define RF12_TXCONF_POWER_18 0x06
#define RF12_TXCONF_POWER_21 0x07
#define RF12_TXCONF_FS_CALC(f) (((f/15000UL)-1)<<4) // formula to calculate FSK frequency offset
#define RF12_CMD_TX 0xB800 // 11 - Writes 8 bits to Tx register (when bit el set)
#define RF12_CMD_WAKEUP 0xE000 // 12 - Wake up timer
#define RF12_CMD_DUTYCYCLE 0xC800 // 13 - Low duty cycle
#define RF12_DUTYCYCLE_ENABLE 0x01 // Enable low duty cycle mode when set
#define RF12_CMD_LBDMCD 0xC000 // 14 - Low battery level and clock divider
#define RF12_CMD_PLL 0xCC02 // PLL setting
#define RF12_PLL_DDY 0x08
#define RF12_PLL_DDIT 0x04
#define RF12_PLL_BW0 0x01
#define RF12_CMD_SYNCPATTERN 0xCE00 // Sync pattern programming
#define RF12_CMD_STA 0x0000 // Command to read status
// Status register
// Following events generate interrupt (depending on mode)
#define RF12_STATUS_RGIT 0x8000 // Tx register ready to receive byte (in Tx mode)
#define RF12_STATUS_FFIT 0x8000 // Rx FIFO received number of programmed bits (in Rx mode, ER set)
#define RF12_STATUS_POR 0x4000 // Power On Reset
#define RF12_STATUS_RGUR 0x2000 // Tx register under run (in Tx mode)
#define RF12_STATUS_FFOV 0x2000 // Rx FIFO overflow
#define RF12_STATUS_WKUP 0x1000 // Wake up timer overflow
#define RF12_STATUS_EXT 0x0800 // Int pin changed to low
#define RF12_STATUS_LBD 0x0400 // Low Battery Detected
// Status bits
#define RF12_STATUS_FFEM 0x0200 // FIFO empty
#define RF12_STATUS_ATS 0x0100 // Antenna detected strong enough signal (when bit er cleared)
#define RF12_STATUS_RSSI 0x0100 // Signal received above programmed level (in Rx mode, er set)
#define RF12_STATUS_DQD 0x0080 // Data quality detector
#define RF12_STATUS_CRL 0x0040 // Clock recovery locked
#define RF12_STATUS_ATGL 0x0020 // Toggling with each AFC cycle
// Syncronisation
#define RF12_PREAMBLE 0xAA // Default preamble
#define RF12_SYNCMSB 0x2D // Default synchronization pattern
#define RF12_SYNCLSB 0xD4
// Packet header length in bytes
#define RF12_PACKET_OVERHEAD 3 // bytes length, type and checksum before data, to calculate total lenght of packet to receive
// set default settings if not declared
#ifndef RF12_CS
#define RF12_CS CS_SPI0 // Define CS pin for RF12 module
#endif
// Channels implementation
#ifndef RF12_NB_CHANNELS
#define RF12_NB_CHANNELS 16 // define number of channels managed by the driver
#endif
#ifndef RF12_CHANNEL
#define RF12_CHANNEL 0 // default channel
#endif
#ifndef RF12_BASEBAND // Baseband of the module (RFM12_BAND_433, RFM12_BAND_868 or RFM12_BAND_912)
#define RF12_BASEBAND RF12_BAND_433 // Default to 433MHz modules
#endif
#if (RF12_BASEBAND) == RF12_BAND_433
#define RF12_FREQUENCY_CALC(x) RF12_FREQUENCY_CALC_433(x)
#define RF12_CHANNEL_FREQ_CALC(x) RF12_FREQUENCY_CALC(430240000UL+((9510000UL/(RF12_NB_CHANNELS-1))*(x)))
#elif (RF12_BASEBAND) == RF12_BAND_868
#define RF12_FREQUENCY_CALC(x) RF12_FREQUENCY_CALC_868(x)
#define RF12_CHANNEL_FREQ_CALC(x) RF12_FREQUENCY_CALC(860480000UL+((19030000UL/(RF12_NB_CHANNELS-1))*(x)))
#elif (RF12_BASEBAND) == RF12_BAND_915
#define RF12_FREQUENCY_CALC(x) RF12_FREQUENCY_CALC_915(x)
#define RF12_CHANNEL_FREQ_CALC(x) RF12_FREQUENCY_CALC(900720000UL+((28550000UL/(RF12_NB_CHANNELS-1))*(x)))
#else
#error "Unsupported RF12 baseband selected."
#endif
#ifndef RF12_FREQ // Center frequency to use (+-125kHz FSK frequency shift)
#if (RF12_BASEBAND) == RF12_BAND_433
#define RF12_FREQ (430240000UL+((RF12_CHANNEL-1)*9510000UL/(RF12_NB_CHANNEL-1)))UL
#elif (RF12_BASEBAND) == RF12_BAND_868
#define RF12_FREQ (860480000UL+((RF12_CHANNEL-1)*19030000UL/(RF12_NB_CHANNEL-1)))UL
#elif (RF12_BASEBAND) == RF12_BAND_915
#define RF12_FREQ (900720000UL+((RF12_CHANNEL-1)*28550000UL/(RF12_NB_CHANNEL-1)))UL
#else
#error "Unsupported RF12 baseband selected."
#endif
#endif
#ifndef RF12_DATARATE_VALUE
#define RF12_DATARATE_VALUE RF12_DATARATE_CALC_HIGH(9600.0) // Use this for datarates >= 2700 Baud
//#define RF12_DATARATE_VALUE RF12_DATARATE_CALC_LOW(1200.0) // Use this for 340 Baud < datarate < 2700 Baud
#endif
#ifndef RF12_TRANSMIT_ONLY
#define RF12_TRANSMIT_ONLY 0 // Off by default
#endif
#ifndef RF12_LOW_BATT_DETECTOR // Off by default
#define RF12_LOW_BATT_DETECTOR 0
#endif
#if RF12_LOW_BATT_DETECTOR
#define RF12_PWRMGMT_LOW_BATT (RF12_PWRMGT_EB)
#ifdef RF12_PWRMGT_DEFAULT
#if !((RF12_PWRMGT_DEFAULT) & RF12_PWRMGT_EB)
#warning "You want to use the RF12 low battery detector, but RF12_PWRMGT_DEFAULT has the low battery detector bit unset."
#endif
#endif
#else
#define RF12_PWRMGMT_LOW_BATT 0
#endif
#define RF12_PWRMGMT_WKUP 0
#ifndef RF12_PWRMGT_DEFAULT
#define RF12_PWRMGT_DEFAULT (RF12_PWRMGT_DC | RF12_PWRMGMT_WKUP | RF12_PWRMGMT_LOW_BATT)
#endif
#if RF12_TRANSMIT_ONLY
#define RF12_PWRMGT_RECEIVE (RF12_CMD_PWRMGT | RF12_PWRMGT_DEFAULT) // If transmit only, er bit cleared
#else
#define RF12_PWRMGT_RECEIVE (RF12_CMD_PWRMGT | RF12_PWRMGT_DEFAULT | RF12_PWRMGT_ER) // else, er set to enable receiver chain
#endif
#ifndef RF12_BUFFER_SIZE // buffer size
#define RF12_BUFFER_SIZE 60
#endif
// Macros to manage the RF12 fifo
// Default fiforeset is: 2 Bytes Sync Pattern, disable sensitive reset, fifo filled interrupt at 8 bits
#define RF12_ACCEPT_DATA (RF12_CMD_FIFORESET | RF12_FIFORESET_DR | (8<<4) | RF12_FIFORESET_FF) // Fifo fill bit enable
#define RF12_CLEAR_FIFO (RF12_CMD_FIFORESET | RF12_FIFORESET_DR | (8<<4)) // Fifo fill disable
// RF12 structures (buffer and control)
typedef struct { // The transmission buffer structure
int8 sync[2]; // Sync bytes for receiver to start filling fifo
int8 len; // Length - number of bytes in buffer
int8 type; // Type of packet (or module address)
int8 checksum; // Checksum
int8 buffer[RF12_BUFFER_SIZE]; // Buffer for the raw bytes to be transmitted
int8 status; // Buffer status (Free, busy, Ready, Error)
} rf12_buffer_t;
typedef struct { // Control and status structure
int8 status; // Module status (Off, Ready, Sending, Receiving)
int8 num_bytes; // Number of bytes to transmit or receive
int8 bytecount; // Counter for the byte we are transmitting or receiving at the moment
int8 error; // Error code
#if RF12_LOW_BATT_DETECTOR
int8 low_batt; // Low bat indicator
#endif
#if !(RF12_TRANSMIT_ONLY)
int8 checksum; // Checksum
#endif
} rf12_control_t;
rf12_buffer_t rf12_buffer; // Communication buffer
rf12_control_t rf12_ctrl;
static int8 rf12_cur_channel=RF12_CHANNEL;
// function prototypes
void rf12_init(void); // Initialize RF12
int8 rf12_ready(void); // Wait for RF12 to be ready to transmit next byte (RGIT set), or fifo full (FFIT set)
int8 rf12_set_channel(int8 channel); // Set frequency of module for specified channel
#if !(RF12_TRANSMIT_ONLY)
void rf12_rx_clear(void); // Free receiver buffer
int8 rf12_receive(void); // Wait for and receive a packet
#endif
int8 rf12_transmit(int8 address, int8 nbbytes, int8 *pdata); // Transmit data
#if RF12_LOW_BATT_DETECTOR
#define RF12_BATT_OK 0
#define RF12_BATT_LOW 1
void rf12_set_batt_detector(int16 val);
char rf12_get_batt_status(void); // Return the battery status
#endif
void rf12_set_rate (int16 in_datarate);
void rf12_set_frequency (int16 in_freq);
// spi functions
#define rf12_assert() output_low(RF12_CS) // RF12 module active
#define rf12_release() output_high(RF12_CS) // RF12 module inactive
void rf12_write(int16 d) { // transmit data d to RF12 module
rf12_assert();
SPI_out16(d);
rf12_release();
}
int16 rf12_read(int16 c) { // Reads 16bits from RF12 while transmitting 16bits
int16 retval;
rf12_assert();
retval = SPI_in16(c);
rf12_release();
return retval;
}
int8 rf12_int_flags() { // Reads the Int bits from the status register
int16 status;
int8 int_bits=0;
status = rf12_read(0);
int_bits = status>>8;
return (int_bits&0xfc); // Keep 6 upper bits of status
}
void rf12_write_inline(int8 cmd, int8 d) { // Send byte d after command cmd (16 bits cmd - d)
rf12_assert();
SPI_out(cmd); // Sends cmd as msb
SPI_out(d); // Sends d as lsb
rf12_release();
}
int8 rf12_ready() { // Wait for transceiver to be ready
int8 retval = 0;
while (!(rf12_read(0)&RF12_STATUS_FFIT));
return retval;
}
int8 rf12_read_fifo() { // Reads the fifo
int16 retval;
rf12_ready(); // Wait for FFIT flag
retval = rf12_read(RF12_CMD_READ);
return (retval&0xff);
}
void rf12_set_status(int8 module, int8 buffer, int8 error) { // Set status of module and buffers
rf12_ctrl.error = error;
rf12_ctrl.status = module;
rf12_buffer.status = buffer;
}
// control functions
// RF12_DATARATE_CALC_HIGH(x) for rates >= 2700 Baud, RF12_DATARATE_CALC_LOW(x) for rates from 340 to < 2700 Baud
void rf12_set_rate (int16 in_datarate) { // Set the data rate
rf12_write(RF12_CMD_DATARATE | in_datarate );
}
void rf12_set_frequency (int16 in_freq) { // Set the frequency of the rf12
rf12_write(RF12_CMD_FREQUENCY | in_freq );
}
// rf12 low battery detector mode
#if RF12_LOW_BATT_DETECTOR
void rf12_set_batt_detector(int16 val) {
rf12_write(RF12_CMD_LBDMCD | (val & 0x01FF));
}
int8 rf12_get_batt_status(void) {
return rf12_ctrl.low_batt;
}
#endif
// rf12 action functions
void rf12_init(void) { // Initialise module with parameters defined
rf12_release(); // Makes sure RF12 inactive
#if (getenv("SPI"))
setup_spi(SPI_MASTER | SPI_MODE_0_0 | SPI_CLK_DIV_64);
#else
#use spi(DI=MISO, DO=MOSI, CLK=SCK, BITS=8, MODE=0)
#endif
rf12_set_status(rf12_Off, rf12buf_Free, rf12_NoError);
rf12_write(RF12_CMD_CFG | RF12_CFG_EL | RF12_CFG_EF | RF12_BASEBAND | RF12_XTAL_12PF); // Setup configuration (internal register, fifo mode, base band and load crystal) - 80D7
rf12_write(RF12_CMD_PWRMGT | RF12_PWRMGT_DEFAULT); // Power default (rx and tx off, clock output disabled by default) - 8201
rf12_write(RF12_CMD_DATARATE | RF12_DATARATE_VALUE ); // Set data rate (9600 Default) - C622
rf12_write(RF12_CMD_RXCTRL | RF12_RXCTRL_P16_VDI | RF12_RXCTRL_VDI_SLOW | RF12_RXCTRL_BW_200 | RF12_RXCTRL_LNA_0 | RF12_RXCTRL_RSSI_91 ); // Receiver control: VDI on + slow, bandwidth 200KHz, LNA 0dB, 91dBm RSSI threshold - 942C
rf12_write(RF12_CMD_DATAFILTER | RF12_DATAFILTER_AL | 3); // Data filter: clock recovery auto lock control, digital filter, DQD threshold to 3 - C2AB
rf12_write(RF12_CMD_AFC | RF12_AFC_AUTO_KEEP | RF12_AFC_LIMIT_4 | RF12_AFC_FI | RF12_AFC_OE | RF12_AFC_EN); // AFC: Keep offset, +7.5 to -10KHz max deviation, hich accuracy, auto offset active - C4F7
rf12_write(RF12_CMD_TXCONF | RF12_TXCONF_POWER_0 | RF12_TXCONF_FS_CALC(125000) ); // Tx configuration: Max power, FSK modulation: 125KHz - 9870
rf12_write(RF12_CMD_DUTYCYCLE); // Low duty cycle disabled - C800
rf12_write(RF12_CMD_WAKEUP); // Wakeup timer disabled - E000
rf12_set_channel(RF12_CHANNEL);
rf12_buffer.sync[0] = RF12_SYNCMSB; // Fill first 2 bytes of tx buffer with synchro pattern
rf12_buffer.sync[1] = RF12_SYNCLSB;
#if RF12_LOW_BATT_DETECTOR
rf12_ctrl.low_batt = RF12_BATT_OKAY; // Battery flag good
#endif
rf12_read(RF12_CMD_STA); // Interrupt flags cleared in status register
rf12_write(RF12_CLEAR_FIFO); // Fifo fill disabled
}
int8 rf12_set_channel(int8 channel) {
if(rf12_ctrl.status == rf12_Off || rf12_ctrl.status == rf12_Idle) {
if(channel > RF12_NB_CHANNELS-1) {
rf12_ctrl.error = rf12_BadChannel;
} else {
rf12_write(RF12_CMD_FREQUENCY | RF12_CHANNEL_FREQ_CALC(channel)); // Set frequency defined by channel
rf12_write(RF12_CMD_FIFORESET | RF12_FIFORESET_DR | (8<<4)); // reset fifo to force PLL re-lock
delay_us(20); // Delay for PLL
rf12_ctrl.error = rf12_NoError;
rf12_cur_channel = channel;
}
} else {
rf12_ctrl.error = rf12_Busy;
}
return rf12_ctrl.error;
}
int8 rf12_transmit(int8 address, int8 nbbytes, int8 *pdata) { // Send nbbytes stored at pointer pdata, with address/type set
if (nbbytes > RF12_BUFFER_SIZE) { // Buffer too small to hold data
rf12_set_status(rf12_Off, rf12buf_Free, rf12_Tx_over);
} else if (rf12_ctrl.status == rf12_Off || rf12_ctrl.status == rf12_Idle) { // If module is not receiving or transmitting (is off or free)
rf12_set_status(rf12_Tx, rf12buf_Busy, rf12_NoError);
memcpy ( rf12_buffer.buffer, pdata, nbbytes ); // buffer free, fill it with data
rf12_buffer.len = nbbytes; // initialise counters/pointers
rf12_buffer.type = address;
rf12_buffer.checksum = nbbytes ^ address ^ 0xff; // Checksum to send
rf12_write(RF12_CMD_PWRMGT | RF12_PWRMGT_DEFAULT); // Disable receiver
rf12_ctrl.num_bytes = nbbytes + 6; // Set number of bytes to process: 2 sync bytes + len byte + type byte + checksum + message length + 1 dummy byte
rf12_ctrl.bytecount = 0; // Reset byte counter
rf12_write(RF12_CMD_TX | RF12_PREAMBLE); // Send 2 preamble bytes
rf12_write(RF12_CMD_TX | RF12_PREAMBLE);
rf12_write(RF12_CMD_PWRMGT | RF12_PWRMGT_DEFAULT | RF12_PWRMGT_ET); // Set ET in power register to enable transmission (hint: TX starts now)
delay_us(80); // Startup delay for PLL
rf12_write(RF12_CMD_PWRMGT | RF12_PWRMGT_DEFAULT | RF12_PWRMGT_ET | RF12_PWRMGT_ES); // Set ES to enable synthetiser
while(rf12_ctrl.bytecount < rf12_ctrl.num_bytes) { // If more bytes to send, load the next byte from tx buffer to module
rf12_ready(); // Wait for RGIT flag
rf12_write_inline( (RF12_CMD_TX>>8), rf12_buffer.sync[rf12_ctrl.bytecount++]);
}
rf12_ready(); // Wait for transmission to finish
rf12_write(RF12_CMD_TX | RF12_PREAMBLE); // Send 2 preamble bytes to make sure sentence is transmitted properly.
rf12_ready();
rf12_write(RF12_CMD_TX | RF12_PREAMBLE); rf12_ready();
rf12_set_status(rf12_Idle, rf12buf_Free, rf12_NoError);
rf12_write(RF12_CMD_PWRMGT | RF12_PWRMGT_DEFAULT); // Turn off the transmitter
rf12_write(RF12_CMD_TX | RF12_PREAMBLE); // Clear int status
rf12_set_status(rf12_Off, rf12buf_Free, rf12_NoError);
}
return rf12_ctrl.error;
}
#if !(RF12_TRANSMIT_ONLY)
void rf12_rx_clear() {
rf12_write(RF12_CMD_PWRMGT | RF12_PWRMGT_DEFAULT); // Turn off the receiver
if (rf12_buffer.status != rf12buf_Ready) rf12_buffer.status = rf12buf_Free;
}
int8 rf12_receive() { // Receive packet in buffer
int8 datain;
if(rf12_buffer.status == rf12buf_Free) {
if(rf12_ctrl.status == rf12_Off || rf12_ctrl.status == rf12_Idle) {
rf12_set_status(rf12_Rx, rf12buf_Busy, rf12_NoError);
rf12_write(RF12_CMD_PWRMGT | RF12_PWRMGT_RECEIVE); // Turn off the transmitter and enable receiver
rf12_write(RF12_CLEAR_FIFO); // Fifo fill disabled
rf12_write(RF12_ACCEPT_DATA); // Fifo fill enabled
rf12_write_inline( (RF12_CMD_TX>>8), 0xaa); // Clear int status
rf12_ctrl.bytecount = 1; // Init the bytecounter (byte being currently processed)
rf12_ctrl.checksum = rf12_read_fifo(); // First byte received is the length byte, write it to the checksum
rf12_ctrl.num_bytes = rf12_ctrl.checksum + RF12_PACKET_OVERHEAD; // Calculate total number of bytes to receive (nbbytes, addr/type, chk, data...)
rf12_buffer.len = rf12_ctrl.checksum; // Store the received length into the packet buffer
while(rf12_ctrl.bytecount < rf12_ctrl.num_bytes) { // Check if more bytes to receive
datain = rf12_read_fifo();
if (rf12_ctrl.bytecount == 1) { // Received address/type
rf12_buffer.type = datain;
rf12_ctrl.checksum ^= datain; // Update checksum with address/type
} else if (rf12_ctrl.bytecount == 2) { // Received checksum
rf12_buffer.checksum = datain;
if ((rf12_ctrl.checksum ^ 0xff) != datain) { // Checksum does not match, reset the fifo and return error
rf12_rx_clear(); // Reset fifo and module to idle
rf12_set_status(rf12_Off, rf12buf_Free, rf12_Rx_chk);
goto END_RX;
}
} else if(rf12_ctrl.bytecount < (RF12_BUFFER_SIZE + 3)) { // Put next byte into buffer, if there is enough space
rf12_buffer.buffer[rf12_ctrl.bytecount-3] = datain;
} else {
rf12_set_status(rf12_Off, rf12buf_Free, rf12_Rx_ovf);
goto END_RX;
}
rf12_ctrl.bytecount++; // Increment bytecount
}
rf12_set_status(rf12_Off, rf12buf_Ready, rf12_NoError); // Mark the current buffer as ready (packet received)
}
} else {
rf12_ctrl.error = rf12_Busy;
}
END_RX: rf12_rx_clear(); // Reset fifo and module to idle
return rf12_ctrl.error;
}
#endif
#endif
#endif
It is a bit long, but most of it is declaring the variables used in the various registers of the module. The RFM12 module are based around the Silicon Labs Si4420 chip. Its datasheet is available here.
Firstly, we check that the SPI library is loaded. then we declare the various states of errors, buffer and module.
The status are:
- Errors:
- rf12_NoError: no error.
- rf12_Busy: The module is busy sending or receiving a packet
- rf12_Tx_over: Packet to transmit too long, and doesn't fit in RFM12 library buffer.
- rf12_Rx_chk: The checksum received doesn't match the cheksum calculated.
- rf12_Rx_ovf: The packet to receive will not fit in the RFM12 library buffer. Buffer overflow
- rf12_BadChannel: the channel passed to the rf12_set_channel function is greater than the number of channel supported by the library.
- Buffer:
- rf12buf_Free: the library buffer is free, and can be used to receive or send data.
- rf12buf_Busy: the buffer contains data being sent or received. A receive or transmit operation is active.
- rf12buf_Ready: the buffer is ready. It contains the packet received, and is ready to be processed.
- rf12buf_Error: the buffer is in error state.
- Module:
- rf12_Off: the module is off (Both receiver and transmitter chain)
- rf12_Idle: the module is in idle mode. The receiver chain is on, but the module is not actually receiving, just waiting for a packet
- rf12_Tx: the module is transmitting a packet
- rf12_Rx: the module is receiving a module
Follows a lenghty list of declaration for all the registers of the chip, and their relevant bits. The default CS line is set to CS0, the number of channels to 16, and the channel number to 0.
The base band for the module is then set with RF12_BASEBAND. It can be either RF12_BAND_433, RF12_BAND_868 or RF12_BAND_915, depending of the module used. It also defines a few functions to calculate the values to put is some registers based on the baseband and channel used. The datarate is set to 9600 bauds.
Somw default values are calculated, like the default power state. The buffer size is set to 60 bytes. It can be defined before calling the library to another value with #define RF12_BUFFER_SIZE size.
The data types for the buffer and the control of the RFM12 module is defined. The buffer is used to hold the data recevied, or the data to send. The structure starts with the 2 sync bytes, used only for transmitting packets. The next byte is the lenght of the packet, followed by the type/address of the packet. This byte can be used to check if the packet is address for a specific node for example. Or that the data is a specific type of packet, the the receiver knows how to process it. The checksum contains the cheksum of the lenght and type bytes. When receiving we check that the checksum of the first 2 bytes received is equal to the 3rd byte received. If not, an error occured. The actual packet to transmit or received starts after the checksum byte. It is an array of a size determined by RF12_BUFFER_SIZE. The last byte of the structure is the status of the buffer.
The control structure contains the number of bytes to process, a counter with the number of the byte being processed, the error code, the low_batt flag, and the byte to calculate the checksum.
The functions are then defined with their prototypes. Basic functions are included: communication with the module with the SPI interface, configure the module...
The main 4 functions are rf12_init, rf12_set_channel, rf12_transmit, rf12_receive.
- rf12_init: it initialise the SPI interface for the module (mode and speed), set each register with the proper value for the frequency used, datarate... and initialise the buffer.
- rf12_set_channel(int8 channel): resets the PLL and module to the channel specified. Does it only if the module is not transmitting or receiving.
- rf12_transmit(int8 address, int8 nbbytes, int8 *pdata): sends nbbytes pointed by pdata, assigning an address/type to the packet. The code is commented in detail to explain the process.
- rf12_receive: in polling mode, the module listen on the channel selected until it receives a packet. It puts it in the buffer. The buffer is in ready status when the function returns, indicating that it is ready to be processed.
Fig. 1b Board 4 with RFM12 and graphic LCD boards
Receiver with board 4 and RFM12 module
This is the board receiving the sentence over the radio module. The program is remains quite simple.
#define board_id 4
#include "lib\mainboard.c"
#include "lib\nok3310_lib.c"
#include "lib\SPI_lib.c"
#include "lib\RF12_lib_1.0.c"
const char cstring[5]={0x2d,0x5c,0x7c,0x2f};
void main() {
int8 i, j, k;
boot_up();
Init_SPI(); // Init SPI interface
delay_ms(500);
rf12_init(); // Init RF12 transceiver
Init_display();
i = 0;
display_gotoxy(0,0);
printf(display_putc, "RFM12 Chan. %d ", rf12_cur_channel);
while (TRUE) {
rf12_receive();
if (rf12_ctrl.error == rf12_NoError) { // sentence received
display_gotoxy(0,1);
printf(display_putc, "%d Bytes in ", rf12_buffer.len);
display_gotoxy(0,2);
printf(display_putc, "Addr/Type: %X", rf12_buffer.type);
display_gotoxy(0,3);
k = rf12_buffer.len;
if (k>13) k = 13; // output 1st 13 characters received
for(j=0;j<k;j++) {
display_putc(rf12_buffer.buffer[j]);
}
display_gotoxy(13,3);
display_putc(cstring[i]);
i++;
if (i>3) i=0;
rf12_buffer.status = rf12buf_Free; // Update status of module once received buffer processed
} else { // Error occured
display_gotoxy(0,1);
printf(display_putc, "*** Error *** ");
display_gotoxy(0,3);
printf(display_putc, " ");
display_gotoxy(0,2);
switch (rf12_ctrl.error) {
case rf12_Busy:printf(display_putc, " Module busy "); break;
case rf12_Tx_over:printf(display_putc, "Tx data 2 long"); break;
case rf12_Rx_chk:printf(display_putc, "Rx chk error "); break;
case rf12_Rx_ovf:printf(display_putc, "Rx buffer ovf "); break;
case rf12_BadChannel:printf(display_putc, "Bad channel nb"); break;
default:printf(display_putc, " unknown "); break;
}
display_gotoxy(12,3);
printf(display_putc, "%X", rf12_ctrl.error);
rf12_buffer.status = rf12buf_Free; // Mark the current buffer as ready (packet received)
rf12_ctrl.error = rf12_NoError;
rf12_ctrl.status = rf12_Off;
}
}
}
Like on the emitter, the program defined the processor board used, and loads the library files.
We then load the nokia 3310 graphic LCD display library, the SPI library and the RFM12 library.
Again, library 1.0 is used, as we're using polling.
A small string is declared with 4 characters to display a rotating bar when receiving.
The main program declares some variables, used as counter, initialises the board with the function boot_up, initialises the SPI interface, waits 500ms, initialises the RFM12 module and the graphic display.
Counter i is set to 0. It is used to display one of the 4 characters of the rotating bar.
We display on the first line the channel used. The channel used is the one defined in the program. It doesn't "scan" the radio frequencies to get the channel. When compiling, it is necessary to set the emitter and receiver on the same channel.
If the channel needs to be specified, then declare #define RF12_CHANNEL channel number. If not specified, channel 0 is used.
We then enter the main loop. First we call the rf12_receive function. This polls the module and waits for a valid reception of a packet. When it returns, a packet was received.
If there was no error, the we display the number of bytes received on the second line, the type/address of the packet on line 3 and first 13 characters of the packet received.
We also display the rotating bar, updating it with one of the 4 characters: | / - \. Lastly, the buffer's status is set from ready to free, to indicate the received packet was processed.
If an error occured, an error message is displayed. Depending on the error code returned, the specific error is displayed. (It could be: the module is busy (transmitting or receiving), the packet to transmit is too big for the module buffer, a checksum error occured when receiving, the packet received doesn't fit in the module buffer or the channel requested is incorrect (<0 or greater than the number of channel managed)).
Once the error details are displayed, the status of the module and buffer are reset.
The program loops again to receive the next packet.
Compile the project and program the target with a PicKIT 2 programmer for example. Connect the processor boards to the RFM12 boards on the SPI connector, and the graphic LCD display on the receiver on port B. Make sure CS line on both RFM12 board are setup up on CS0 with JP1.
Power up the boards. The order is not important. When both boards are on, the led on the receiver should start blinking. The display should also display 13 bytes in and "Hello world!".
You can test the range of the radio link. With the built-in antenna on the RFM12 board, I get about 20m indoor. I get a link from any room to any other room in the house, from both floor.
Files and links:
Tutorial 13 source files.
Processor boards.
RFM12 transceiver.
RF12 C library for CCS compiler, compatible with Microchip MiMac.
Graphic Lcd board.

