Xplained Board Peer to Peer RF Comm using RZ600

Created by Scott Schmit, last modified on Dec 03, 2013

Goals of this Demo

  • Demonstrate how to interface an AT86RF231 radio transceiver to an ATxmega microcontroller
  • Demonstrate how to incorporate a basic user application into Atmel’s Lightweight Mesh (ALM) wireless software stack
  • Achieve basic wireless communication between two Xmega Xplained boards

Project Requirements

Atmel Studio 6 - This is a free software development tool from Atmel for ARM and AVR devices. The user must download Atmel Studio 6 to view this project.
Atmel Lightweight Mesh (ALM) - This is a wireless software stack that can be used to implement a mesh network or simple Peer to Peer communication. This is a proprietary network stack from Atmel and does not comply with other standard wireless network protocols (Zigbee, Bluetooth, etc.). ALM v1.0.1 was used for this project.
XMEGA-B1 Xplained - This is an Atmel development board for Xmega microcontrollers, specifically the B-family of devices, which includes an integrated LCD controller. The board is loaded with the ATxmega128B1 device. Two of these boards are required for this demo.
ATAVRRZ600 - This is a development kit for Atmel’s RF transceivers (RF230, RF231, RF212). The transceiver boards in this kit provide a convenient hardware interface between the Xmega controller and the RF transceiver. This kit comes with a pair of RF231 radios.
Debugger/Programmer - The JTAGICE3 was used for this project. However, any Atmel programmer that supports PDI programming will work for this project.
USB Cable - The XMEGA-B1 Xplained board can be easily powered through its USB 2.0 mini B connector. Consult the XMEGA-B1 Xplained Hardware User Guide for additional power up options. Two USB cables were used in this demo (one for each board).

Project Brief

Demo Functionality

This project demonstrates how to acheive basic Peer to Peer wireless communication between two Xmega-B1 Xplained boards using the AT86RF231 transceiver boards that come with the ATAVRRZ600 wireless eval kit from Atmel. A prewritten wireless software stack (ALM) was used to handle the wireless networking tasks of the application. Each device uses its ADC to measure the voltage across the board-mounted potentiometer. Upon power-up, each node’s pot voltage is displayed on the board’s LCD in mV. When both nodes are powered up, and within range of each other, they are capable of transmitting data to each other. By nature of the ALM, there is no special procedure for a node to join the network. As long as it has the correct network configuration (hard-coded in firmware), it will be capable of communicating with other nodes in the network as soon as it’s powered up. The capacitive touch sensing buttons on the board are used to scroll through a node’s pot value, it’s Peer’s (2nd node’s) pot value, and toggle the backlight of the LCD screen.

Project Background

This project builds on the ADC Example and the LCD Example for Xmega. The intent of this project is not to easily demonstrate ADC or LCD operation. Instead, the goal is to demonstrate how to interface a basic user application with a prewritten wireless software stack and achieve Peer to Peer communication between two Xplained boards. The reader is encouraged to review the ADC and LCD example projects for background on the application, as the code in those two projects will appear throughout this demo. Additionally, the Zigbit Peer to Peer Communication page demonstrates basic functionality of the ALM, on a different platform (Zigbit modules). The reader is encouraged to review the Zigbit project for an introduction to the ALM with a very basic project.

Platform Considerations

Finally, the platform for this demo (XMEGA-B1 Xplained board interfaced with AT86RF231 transceiver board) was chosen because it provided a very convenient way to demonstrate basic RF communication between two nodes with various board-mounted ways to source and display data. Also, one of the platform-specific ALM projects provided by Atmel is for this exact platform. Therefore, the port interface between the micro and the radio is already achieved in the Hardware Abstraction Layer (HAL) of the stack. This allows us to easily incorporate user application code on top of the prewritten stack. Once the basic concepts of the stack and its operation are mastered, the user can adjust the stack to fit any platform desired.

Documentation

Xmega/Xplained Docs

Xmega B Manual - This document provides a summary of Xmega-B features along with all register descriptions used in firmware development.
ATxmega128B1 Complete - This document provides a summary of features of ATxmega128B1 specifically, along with pin assignments and electrical characteristics.
XMEGA-B1 Xplained Hardware User Guide - This document provides a summary of the hardware features available on the XMEGA-B1 Xplained board.
XMEGA-B1 Xplained Board Design Files - This pdf contains the schematic and board layout drawings for the XMEGA-B1 Xplained board.

RZ600/Radio Docs

AVR2064: RZ600 HW Manual - This document provides a summary of features of the RZ600 along with hardware interface specifications.
AT86RF231 Complete - This is the datasheet for the RF231 transceiver from Atmel. It describes radio functionality and provides register details.

ALM Docs

Atmel Lightweight Mesh Developer Guide - Provides background on the ALM stack and how it functions.

Hardware Connections

Specific Devices Involved:

  • ATxmega128B1 - 8-bit AVR microcontroller from Atmel
  • AT86RF231 - 802.15.4 RF Transceiver from Atmel. Operates in the 2.4GHz band.

The AT86RF231 transceiver boards that come in the RZ600 kit should be connected to the Xplained boards through the PORTC 10-pin header. Ensure the smaller antennas in the RZ600 kit are connected to the SMA ports of the transceiver boards. The USB cables can be used to power the boards. The transceiver boards draw power through the 10-pin header. The RF transceiver becomes a slave device of the Xmega. Communication between the micro and the radio takes place through the SPI interface.

Figure 1 illustrates what is meant by “Xmega128B1 / RF231 Platform”. It describes the hardware that executes the application. The physical connections between the microcontroller (xmega) and radio (RF231) for each node are shown below.

image
Figure 1: Xmega128B1/RF231 Hardware Platform

Figure 2 illustrates how two of these platforms can be used to implement a 2-node wireless link using Peer to Peer communication.

image
Figure 2: Peer 2 Peer RF Communication using Xmega128B1/RF231 Platform

Design Considerations

System Clock Options

The System Clock of the Xmega can be sourced from an internal 2MHz RC oscillator, an internal 32MHz RC oscillator, an internal 32.768kHz oscillator, an external clock source, or the PLL. Additionally, if the PLL is selected as the System Clock source, the PLL Clock source must be specified, along with a non-zero division and multiplication factor. The PLL can use the internal 2MHz RC oscillator, the internal 32MHz RC oscillator, or an external clock as its source. It’s up to the designer to select the proper system clock source for a specific application while taking into account necessary speed, accuracy, acceptable power consumption, temperature effects, board space, and cost.

By default, the ALM sets up a CPU clock frequency of 8MHz. However, the user can select 4, 8, 12, 16, or 32MHz as the CPU frequency. Also by default, the ALM assigns the PLL as the source to the System Clock with the internal 2MHz RC oscillator as the PLL clock source. A multiplication factor of 8 is used which creates a frequency of 16MHz. The CPU frequency is then set to 8 MHz through the Clock Prescaler Control Register.

The ADC was used as the data source for this demo. Therefore, the ADC clock needed to be setup. The ADC clock is sourced from the Clk_per peripheral clock and it uses a prescaler register to configure the actual ADC clock rate. According to AVR1300, the ADC clock should be in the 100kHz ~ 1.4 MHz range. 125kHz was used for this demo.

For this demo, the LCD was used to display ADC results. The LCD controller within the Xmega-B devices uses the same clock source as the RTC. For this demo, the 32kHz internal ULP oscillator was used as the RTC source, and therefore, the LCD clock source as well. The frame rate of the LCD was then set to 62.5Hz.

Data transmission timing is up to the designer to determine. For this demo, a software timer was setup to transmit ADC results every 20ms.

Interrupt Configuration

Xmega devices include a Programmable Multilevel Interrupt Controller (PMIC) which allows the designer to assign a priority level to individual interrupts. It also allows round-robin or vector address based interrupt execution. The designer should customize interrupt execution priority based on the needs of the specific application.

The ALM relies on interrupts and embedded state machines to operate. Generally with a wireless stack, it’s a good idea to never branch off into a function that takes an extended amount of time to execute. Quick functions and interrupts should be implemented to ensure the stack never bogs down and prevents the system from functioning properly.

The ADC Conversion Complete interrupt was used to keep track of ADC results rather than polling to shorten execution time of the ADC task handler.

The capacitive touch sensing buttons were incorporated in the user interface for this demo. The Atmel QTouch controller AT42QT1040-MMH is loaded onto the Xmega-B1 Xplained Board and interfaces the capacitive sensing buttons to Port E of the micro. Port E of the Xmega micro simply sees a high or low digital signal from the output of this device. The two available PORTE pin change interrupts were used to detect changes from the CS0 and CS1 buttons.

ADC Configuration

For this demo, the ADC was setup to perform a single conversion. It was configured to operate in signed, single-ended mode. Therefore, the MS bit represents the sign bit and the result has effectively 11-bit resolution. The potentiometer on the XMEGA-B1 Xplained board is hard-wired to PB1 of the Xmega. Therefore, PB1 needed to be configured as an input to capture the analog data.

The Xmega ADC module is calibrated at production time and the calibration value is stored in internal EEPROM. This value is meant to improve non-linearities in the ADC conversions. Compensating for DC offsets and gain error is out of the scope of this project. Refer to AVR120 for documentation on this topic.

The reference voltage for the ADC in this demo was set to Vcc/1.6. Since Vcc is 3.3V, the reference voltage for the ADC becomes approximately 2.06V. With effecively 11-bits ADC resolution, this means each digital value represents approximately 1mV. This makes handling the ADC results very straightforward. Calibration would enhance the accuracy of the results, but for this demo, no further ADC result manipulation was performed. The value in the ADC result register was displayed on the LCD (in mV) without additional processing.

LCD Configuration

The c42048A LCD that comes standard with the XMEGA-B1 Xplained board uses 40 segment and 4 common lines. Therefore all 40 of the available LCD segment pins on the Xmega were required for proper LCD functionality.

On the XMEGA-B1 Xplained board, PE5 of the Xmega serves as a gate driver for a MOSFET controlling the LCD backlight. Pulse-width-modulation (PWM) can be used to finely control the intensity of the LCD backlight. However, PWM control is out of the scope of this project. For this demo, PE5 was used to simply turn the backlight on full-bright, or completely off.

The ADC result was displayed on the LCD using the four available 7-segment digits. The seven available 14-segment digits were used to display alpha character strings. The mV icon was used to identify the scale of the ADC results.

Reducing Power Consumption

Power consumption wasn’t a huge design consideration and is out of the scope of this demo to a certain extent, since the board was powered through a USB cable. However, wireless applications are typically battery-powered and power consumption becomes, in many cases, the most important design factor. Power reduction becomes a game of trade-offs. The designer must constantly ask if each individual trade-off is worth the increase in power reduction (i.e. battery life). For this specific demo, there are a number of ways to reduce the power consumption.

The current consumption of the ADC can be reduced by limiting the max sample rate. The designer can reduce the max sample rate to 225kSPS, 150kSPS, or 75kSPS. The designer must decide how much the max sample rate can be reduced while still maintaining proper operation of the application.

The current consumption of the LCD can be reduced by turning off the backlight (or reducing the intensity using PWM). Reducing the frame rate of the LCD can be used to minimize power consumption. Also, using the low-power waveform and lowering the contrast of the LCD pixels will save some power as well. This demo does implement the low-power waveform and gives the user the option of turning off the backlight. The tradeoff that the designer must consider would be how well the user can actually see the display and interpret the results.

The Xmega device itself, along with the RF231 transceiver, can be put in sleep mode and woken up at pre-determined intervals. This is a very common method of saving battery life. However, using sleep-mode is out of the scope of this project. The tradeoff becomes latency between transmissions. Putting a device to sleep and waking it up adds overhead to an application. This is due to the application needing to re-initiallize or restart certain perpherals before actually being able to receive/process incoming data. Most microcontrollers have multiple sleep modes available which presents the designer some additional flexibility in regards to wake-up time vs. power consumption. Keeping certain perpherals running while in sleep mode decreases wake-up time, but obviously increases power consumption. It is up to the designer to determine which peripherals, if any, must be kept awake to ensure the minimum wake-up time is maintained.

Step by Step Example - Integrate Application into ALM

  1. Download Atmel Studio 6 and ALM (v1.0.1). It may be a good idea to make 2 copies of ALM (v1.0.1) after downloading. Keep one as an original and use the other for editing/prototyping. If you ever decide to start again from scratch, you don’t need to re-download the ALM, just make a new copy of the saved original.
  2. In the ALM download folder, navigate to apps → Peer2Peer → astudio. Open Xplained_ATxmega128b1_Rf231 .
  3. Add ADC and LCD files to the project in Atmel Studio:
  • In the Solution Explorer window, navigate to stack → hal → atxmega128b1 → inc
    Right-click on the “inc” folder and select Add → New Item
    Select “Include File” and rename it “My_ADC.h” and click “Add”
    Copy & Paste the contents of this text document into “My_ADC.h”
    Save and close “My_ADC.h”
  • Add another Include file to the “inc” folder in the “hal” sub-directory
    Name it “My_LCD.h”
    Copy & Paste the contents of this text document into “My_LCD.h”
    Save and close “My_LCD.h”
  • In the Solution Explorer window, navigate to stack → hal → atxmega128b1 → src
    Right-click on the “src” folder and select Add → New Item
    Select “C File” and rename it “My_ADC.c” and click “Add”
    Copy & Paste the contents of this text document into “My_ADC.c”
    Save and close “My_ADC.c”
  • Add another C file to the “src” folder in the “hal” sub-directory
    Name it “My_LCD.c”
    Copy & Paste the contents of this text document into “My_LCD.c”
    Save and close “My_LCD.c”
  • Again, the goal of this project is not to demonstrate LCD and ADC operation, which is why the code is provided here. The goal is to demonstrate how to interface this code with the ALM stack. If the reader reviewed the ADC Example and LCD Example, the contents of these files should look very familiar.
  1. Add the following code to config.h:
#define CONFIG_XMEGA_128B1_REVA //Comment this out if using a later revision`

// Use this definition to assign the max analog value input to the ADC`
// This is used to divide the status bar in equal portions`
// 660mV is the highest voltage output of the potentiometer`
#define MAX_ANALOG_INPUT 660 //mV`

// If user wishes to use Degree C, Degree F, Volt, and/or milliVolt pixels on the LCD,`
// uncomment the following definitions`
// #define USE_ICON_DEGREE_C`
// #define USE_ICON_DEGREE_F`
// #define USE_ICON_VOLT`
#define USE_ICON_MILLIVOLT`
  1. Ensure config.h also has the following definitions:
#define APP_ADDR 0`
#define APP_CHANNEL 0x0f`
#define APP_PANID 0x4567`
#define APP_ENDPOINT 1`
#define APP_SECURITY_KEY "TestSecurityKey0"`
#define APP_FLUSH_TIMER_INTERVAL 20`
  1. Add the following code to hal.h:
#include "halGPIO.h"`

// This is a bool flag used to specify whether to display`
// the local pot val or the Peer's pot val`
extern` `uint8_t display_local_pot_val;`

// Setup LED pin macros`
#if defined(PLATFORM_XPLAINED)`
HAL_GPIO_PIN(LED3, B, 7);`
HAL_GPIO_PIN(LED2, B, 6);`
HAL_GPIO_PIN(LED1, B, 5);`
HAL_GPIO_PIN(LED0, B, 4);`

// Setup backlight pin macro`
HAL_GPIO_PIN(BL_PIN, E, 5);`
#endif`
  1. Add the following code to hal.c:
  • Add the following code somewhere towards the top of the file:
uint8_t display_local_pot_val = 1;  //On power-up, display local pot val

// Setup pin change interrupt sub-routines
ISR(PORTE_INT0_vect) {
    // If CS0 is pressed, display local pot val
    HAL_GPIO_LED3_clr();
    HAL_GPIO_LED2_set();

    display_local_pot_val = 1;

}

ISR(PORTE_INT1_vect) {
    // If CS1 is pressed, display peer pot val
    HAL_GPIO_LED2_clr();
    HAL_GPIO_LED3_set();

    display_local_pot_val = 0;
}
  • Add the following code to the end of the “halSetSystemFrequency()” function:
 CLK.RTCCTRL = CLK_RTCEN_bm; ` `// 0x01
  • Add the following code to the “HAL_Init()” function, after the “halSetSystemFrequency()” function call:
// Disable interrupts
cli();

// Set LED pins as outputs
HAL_GPIO_LED3_out();
HAL_GPIO_LED2_out();
HAL_GPIO_LED1_out();
HAL_GPIO_LED0_out();

// Set backlight pin as output
HAL_GPIO_BL_PIN_out();

// Turn off all LEDs except LED3
HAL_GPIO_LED3_clr();
HAL_GPIO_LED2_set();
HAL_GPIO_LED1_set();
HAL_GPIO_LED0_set();

// Turn on backlight
HAL_GPIO_BL_PIN_set();

// Set the 5 MS bits of PORTE as outputs
PORTE.DIR &= 0xF8;
// Enable pullups on QTouch inputs
// Set interrupt to trigger on falling edge
PORTE.PIN0CTRL = 0x1A;
PORTE.PIN1CTRL = 0x1A;
PORTE.PIN2CTRL = 0x18;
// Set INT0 to trigger on PE0 (CS0)
PORTE.INT0MASK = 0x01;
// Set INT1 to trigger on PE1 (CS1)
PORTE.INT1MASK = 0x02;
// Assign high priority to pin change interrupts
PORTE.INTCTRL = 0x0F;
// Clear interrupt flags
PORTE.INTFLAGS = 0x03;
  1. Add the following code to Peer2Peer.c:
  • Add #include paths to “My_ADC.h” and “My_LCD.h”:
#include "astudio/stack/hal/atxmega128b1/inc/MyADC.h"
#include "astudio/stack/hal/atxmega128b1/inc/My_LCD.h"
  • Add the following variable declarations to the variable declaration section of the file:
static` `SYS_Timer_t BacklightTimerCheck;  // Software timer used to periodically check the state of CS2
uint8_t negEdgeDetShiftReg; // 8-bit shift register used as a work-around to detect a neg edge on CS2

                            // Ran out of pin change interrupts
uint8_t appAdcResultBuffer[2]; ` `//buffer used to break up the ADC result to be transmitted`
uint8_t ADC_flag;
  • In main(), add the following initiallization calls after SYS_Init() and before the while(1) loop:
c42048a_init();  // Initiallize LCD
adc_init(); // Initiallize ADC
  • “HAL_UartInit(38400)” can be commented out since the UART is not used in this demo
  • In main(), add the following functions to the while(1) loop between the SYS_TaskHandler and the APP_TaskHandler:
HAL_AdcTaskHandler();
HAL_LcdTaskHandler();
  • Since all of the PORTE pin change interrupts are in use, we will set up a software timer to periodically check the state of CS2 and use a shift register to keep track of the sample results. In the “appInit()” function, add the following software timer initiallization at the end of the function:
BacklightTimerCheck.interval = 10; //ms
BacklightTimerCheck.mode = SYS_TIMER_PERIODIC_MODE;
BacklightTimerCheck.handler = BacklightTimerHandler;
SYS_TimerStart(&BacklightTimerCheck);
  • We will use this software timer to check the current state of CS2 and shift the result into the LSB of the shift register. If the two least significant bits of the shift register are b’10’ we know the CS2 input went from high to low and we can toggle the LCD backlight. Add the following function after the “appTimerHandler()” function to implement the backlight timer check:
// Both Pin change interrupts are being used for PORTE
// This is a software timer to periodically sample the
// state of the CS2 QTouch input

static void BacklightTimerHandler(SYS_Timer_t *timer) {
    // shift contents 1 bit to the left
    negEdgeDetShiftReg <<= 1;
    if (PORTE.IN & 0x04) {
         // If CS2 input is '1'
         // make sure lsb of the shift reg is '1'
         negEdgeDetShiftReg |= 0x01;
    } else {
        // QTouch input is '0' (button is pressed)
        // make sure lsb is '0'
        negEdgeDetShiftReg &= ~0x01;
    }
    if ( (negEdgeDetShiftReg & 0x02) && !(negEdgeDetShiftReg & 0x01) ) {
         // if bit 1 is '1' and bit 0 is '0'
         // this represents a negative edge on the CS2 QTouch input
         // Handle Backlight toggle
         HAL_GPIO_BL_PIN_toggle();
    }
}
  • Up to this point, we have all of the data source and sampling procedures setup. Now we need to actually transmit the data and handle incoming data. For this demo, we will use the “appTimerHandler()” function to initiate transmission. Modify the “appTimerHandler()” function to the following:
static void appTimerHandler(SYS_Timer_t *timer)
{
    if(!(local_pot_val & 0x800)) {
        // If positive value
        // break up the 12-bit result into 2 separate bytes
        appAdcResultBuffer[0] = (local_pot_val & 0xFF);
        appAdcResultBuffer[1] = ((local_pot_val >> 8) & 0x03);
 
        ADC_flag = 1;
        appSendData(); // Submit a data transmission request
    }else{
        // If negative value, clip at 0
        appAdcResultBuffer[0] = 0;
        appAdcResultBuffer[1] = 0;
 
        ADC_flag = 1;
        appSendData();  // Submit a data transmission request
    }
    (void)timer;
    ADC_flag = 0;
}
  • We have the application setup to transmit the local pot value every 20ms. Now we can remove the rest of the UART functionality, since it is not used in this demo. In “HAL_UartBytesReceived()”, comment out the “appSendData()” function call. In main(), you can also comment the “HAL_UartTaskHandler()” function.
  • We also need to remove the UART code from the “appSendData()” function. Modify the “appSendData()” function to the following:
static void appSendData(void)
{
    if(appDataReqBusy || 0 == ADC_flag) // check ADC flag instead of UART buffer pointer
        return;
 
    memcpy(appDataReqBuffer, appAdcResultBuffer, 2); // copy the ADC result buffer instead of the UART buffer
 
    appDataReq.dstAddr = 1 - APP_ADDR;
    appDataReq.dstEndpoint = APP_ENDPOINT;
    appDataReq.srcEndpoint = APP_ENDPOINT;
    appDataReq.options = NWK_OPT_ENABLE_SECURITY | NWK_IND_OPT_ACK_REQUESTED; // Request ACK signal
    appDataReq.data = appDataReqBuffer;
    appDataReq.size = 2; // Size is no longer dependent on the UART data, change size to 2 bytes
    appDataReq.confirm = appDataConf;
    NWK_DataReq(&appDataReq);
 
    appUartBufferPtr = 0; // This can be commented out or removed if desired
    appDataReqBusy = true;
}
  • The final step is handling incoming data. We will assign the incoming data to the 16-bit “peer_pot_val” variable. Modify the “appDataInd()” function to the following:
static bool appDataInd(NWK_DataInd_t *ind)
{
    // Assign the 1st data byte to the LS byte of peer_pot_val
    // Assign the 2nd data byte to the MS byte of peer_pot_val
    peer_pot_val = ((ind->data[1]) << 8) | (ind->data[0]);
    return true;
}
  1. Connect the 1st Xplained board to the debugger/programmer. Ensure the board is powered up.
  2. Build the project and program the first Xplained board. Verify that the local pot value is displayed on the LCD. Verify CS0, CS1, and CS2 QTouch buttons operate as intended. The Peer pot value should read “0”.
  3. Connect the 2nd Xplained board to the debugger/programmer. Ensure the board is powered up.
  4. Navigate to config.h and make the following change:
  #define APP_ADDR 1
  • All other definitions should be left alone for this demo.
  1. Build the project and program the 2nd Xplained board. Verify that the local pot value is displayed on the LCD. Verify CS0, CS1, and CS2 QTouch buttons operate as intended. The Peer pot value should read properly if the 2 boards are in range. If not, try resetting both boards.

Comments from the Author

The reader is strongly encouraged to review all reference documentation that is mentioned on this page and have it available when developing project code. The intent of this demo was to provide insight on how to integrate an application into a wireless stack (ALM). The reader is encouraged to modify and experiment with the code as you see fit. Try to implement your own application on this same platform, or an Atmel platform of your choosing. Read through the ALM developer guide and see if you can take advantage of other features that are available with the stack, not mentioned in this demo. I hope you enjoy experimenting with Atmel’s RF products!

  • Scott