A ZigBee driver for SynthOS - Embedded.com

A ZigBee driver for SynthOS

In a previous article we described our project to take an off-the-shelf kit robot kit, developed control algorithms, and use SynthOS to create a real-time application specific operating system (ASOS) to schedule and coordinate the various robot tasks. The requirement was to build a robot that can move around an obstacle course while avoiding walls and other objects and not getting trapped in narrow places. We also wanted it to be able to adjust its speed for the left and right tracks independently, and if anything fails, such as one of the tracks getting stuck, it should give an indication (a beep) and shut down its own power. We were happy with the results.

Figure 1. Our robot

The second phase of the project involved adding wireless communication to the robot to enable remote communication for debug, monitoring, and control. In this article, we talk about the unique issues in the development of a ZigBee device driver in particular and with the development of drivers for communication devices in general using SynthOS.

What is ZigBee?
We chose ZigBee because it is an open standard that is used worldwide and has found significant use in the Internet of Things. ZigBee is particularly focused on the requirements of sensors for use in consumer, commercial, and industrial applications. It is supported by the ZigBee Alliance, an open, non-profit association with about 400 members driving the development of the ZigBee standards.

ZigBee is a low power, low cost wireless mesh network standard, operating in the 2.4 MHz unlicensed spectrum. The raw, over the air data rate is 250 Kbit/s per channel in the 2.4 GHz band, and the indoor effective transition distance is 10-20 m depending on the environment contraction materials. ZigBee devices are required to conform to IEEE 802.15.4-2003 Low-Rate Personal Area Network (LP-WPAN). This standard specifies the physical layer Media Access Control and the data link layer.

Figure 2. ZigBee OSI Model (courtesy of The ZigBee Alliance)

The particular implementation of ZigBee that we chose is XBee, which is the brand name from Digi International for a family of form-factor -ompatible radio modules based on the 802.15.4-2003 standard designed for point-to-point and star communications at over-the-air baud rates of 250 Kbit/s. We chose XBee because of the low cost and ease of ordering from Digi. The particular module that we chose was the XBee ZB low power ZigBee Module with integrated wire antenna, product #XB24-Z7WIT-004, shown in Figure 3.

Figure 3. XBee XB24-Z7WIT-004 from Digi

The Hardware Platform
The hardware platform for the project was an off-the-shelf DFRobot Rover v2 kit from RobotShop Distribution Inc. The Rover uses a Tamiya twin motor gearbox and the Tamiya track and wheel set. An on-board buzzer that can generate a variety of tones is used for simple notifications such as failure modes. A rechargeable 3.7V LiPo battery is included and can be recharged using the onboard charger.

This particular autonomous version of the DFRobotShop Rover includes a an add-on Dagu module consisting of a PCB controlled by a mini servo-based pan/tilt system for object tracking, an infrared “compound eye” for distance sensing comprising four infrared LEDs and six infrared sensors, and an ultrasound sensor for distance sensing consisting of dual ultrasonic transducers.

The main control board of the robot uses an Arduino UNO architecture with an Atmel ATmega328p microprocessor. To this platform we added a wireless capability for monitoring and debugging the robot and a wireless adapter to the PC to communicate with the robot. On the robot we used an XBee and an embedded XBee ZB module. On the PC we used an XStick, a USB peripheral adapter from Digi International that provides short-range wireless ZigBee connectivity. The XStick contains an XBee module and plugs into the USB port of a PC.

Next page

Article page index:

  1. Introduction
  2. How does SynthOS work?
  3. XBee Driver Architecture
  4. Non-blocking Calls

How does SynthOS work?
Our SynthOS tool allowed us to develop the system while writing it in C. SynthOS defines 5 types of tasks.

  • Init Task. Runs once when system is initialized.
  • Loop Task. Runs by the RTOS at regular intervals.
  • Call Task. Runs when called by another task.

SynthOS uses special statements called “primitives” to describe theOS functionality. When one task needs to call another task or waits foranother task to complete, we insert one of the special primitives thatis recognized by SynthOS. The SynthOS primitives are:

  • SynthOS_call() . Call a task to run concurrently with the current task. This is a non-blocking call.
  • SynthOS_check() . Check whether a task is currently running.
  • SynthOS_sleep() . Suspend the current task.
  • SynthOS_start() . Suspend the current task and start the new task. This is a blocking call.
  • SynthOS_wait() . Suspend the current task until a specified task ends or until a specific condition is met.

To define the project and the OS high-level behavior, we created asimple project file to specify the parameters of each task such as thetask’s priority and its frequency. SynthOS was then run on all of thesystem and library source code. SynthOS creates the appropriatesemaphores and flags for each task and inserts the appropriate code atthe appropriate points in the task code. SynthOS also creates taskmanagement code to manage the tasks and their associated flags andsemaphores — a micro kernel that we call an application specificoperating system (ASOS). A generic diagram of the resulting code isgiven in Figure 4. Note that each task is mostly written by us, butSynthOS inserts the code required by the ASOS into each task, andgenerates the ASOS that controls execution of each task.

Here is a sample of a section of the project.sop file specifying a Loop Task and the XBee Call Tasks:

# Each task has an associated [task] section.[task]entry = testtype = loop[task]entry = xbee_requesttype = call[task]entry = xbee_receivetype = call

Figure 4. SynthOS code generation

SynthOS has a built-in capability to support drivers for hardwareperipherals. Its inherent capability to run multiple tasks in parallelenables a user to build a system that monitors multiple devices andactivate the appropriate tasks in response to inputs. SynthOS supportstwo basic schedulers for the system kernel, round robin and thepriority-based, as illustrated in Figure 4. The definition of thearchitecture is static and cannot be changed at runtime, and at thistime we can support only one of them and not a combination of them. Itis defined using the scheduler directive in the configuration file.SynthOS internal inter-task communication will activate the appropriatetask to service the request. In general for embedded systems, interruptmechanisms are the preferred method for monitoring external sensors andinputs, but a timer interrupt with polling can often be as effective.

The Software Solution
First we needed to define three required routines to deal with interrupts. In the SynthOS project file (project.sop ),we set the following parameters to define the user-created interruptfunctions that SynthOS would need to utilize when it synthesizedinterrupt service routines:

[interrupt_global]enable    = ONgetMask   = get_masksetMask   = set_maskenableAll = enable_ints

Then we added the actual code for the relevant interrupt routines to the application to support the interrupt functionality:

int get_mask (void) {    int mask = SREG;    cli ();    return mask;}void set_mask (int mask) {    SREG = (uint8_t) mask;}void enable_ints (void) {	    sei ();}

Starting with SynthOS version 1.7 and up, variadic macros (macrosthat take a variable number of parameters) are supported. This means wecan use the Atmel libraries for interrupt support without anymodification. Another improvement available in version 1.7 is thesupport for the setMaskAndSleep()  functioncall that sets the interrupt and puts the processor into low-powersleep mode when there is no active task in the task queue. Once anexternal device or a timer triggers an interrupt, the processor willwake up from the sleep mode and service the interrupt. If the interruptservice routine changes the status of any of the tasks, by satisfyingthe condition of a SynthOS_wait() statement, the ASOS will put this task into the task execution queueand it will be executed. The following code shows a processor-specificsleep mode routine, assembly wrapped in C code, and the setMaskAndSleep() function that SynthOS uses to call it.

static void inline sei_sleep (void) __attribute__ ((always_inline));static void inline sei_sleep (void){    __asm__ __volatile__ ("sei" ::: "memory");    __asm__ __volatile__ ("sleep" ::: "memory");}void setMaskAndSleep (int mask){    sei_sleep ();}

Article page index:

  1. Introduction
  2. How does SynthOS work?
  3. XBee Driver Architecture
  4. Non-blocking Calls

XBee Driver Architecture
The architecture for theXBee driver is simple and straightforward as shown in Figure 5. The twomain tasks that are supported are transmit and receive tasks. Both ofthem are SynthOS Call Tasks that can be called from any SynthOS LoopTask. In the robot implementation, the main robot , left_motor and right_motor Loop Tasks can send and receive data via XBee by calling the xbee_request() task for transmitted data and the xbee_receive() task for receiving data. Using the SynthOS_call() primitive to invoke these tasks will execute the transmit or receivetask and halt the execution on the calling task until the called task iscompleted. The higher-level calling task can monitor a trigger that isactivated once communication is established with the base station andthe channel is open.

The transmit and receive tasks can be called from multiple Loop Taskssimultaneously but those calls are going to be serialized, and eachtransition is going to be handled independently until it is complete.This architecture enables multiple higher level Loop Tasks to use thesame driver and to communicate through the single XBee hardwarecommunication device. Unfortunately this architecture is not optimized —transmit and receive tasks may have to wait for previous task instancesto complete execution even if they are ready to execute. One way toimprove on this basic architecture without many changes is to run the xbee_receive() and xbee_request() tasks as Loop Tasks and communicate to the rest of the system viashared memory. An even more efficient architecture using non-blockingtask execution is describe later in the article. SynthOS will manage allthe context switching between the high-level tasks and the drivercommunication tasks and synchronize the information being sent andreceived.

Figure 5. ZigBee software architecture

Implementation Considerations
SynthOS enables twobasic ways to implement a device driver in a multitasking environment.The first is a blocking model, where one task calls the driver and haltsexecution of the calling task until the operation of the called task iscomplete. This can be used for critical timing drivers that will use alarge percentage of the CPU power. The blocking model is common indrivers that write to memory and have a strict timing requirements. Theother method is a non-blocking model where one task will call a drivertask to run concurrently. The calling task will continue its executionin parallel with the device driver, and at a later point in theexecution the driver task will communicate back to the calling task andterminate. The non-blocking model is very common in interfacing withslower devices like sensors, analog to digital converters, andcommunication interfaces.

Blocking Calls
SynthOS has a build in support forboth those blocking and non-blocking execution, and the infrastructurecode to support both of them is created automatically by using the basicSynthOS primitives. For the blocking method the basic primitive to useis SynthOS_call() .This will start the device driver as a new task and in turn suspend theexecution of the calling task. The calling task will restart itsexecution once the driver has completed its operation and exits. 

void main_task(void){	char *TX_data;	// create data	SynthOS_call(tx_driver(TX_data));	// wait for the driver...	//continue task}

The limitation of the blocking method becomes clear when multipletasks are trying to access the same peripheral through the same devicedriver. Once the first task calls the driver, all other tasks that willcall this driver will stop execution until the device driver completesexecution. This will introduce inefficiencies in the system when allrequests are serialized, and most of the time is spent waiting on theblocking device driver to execute. One way to avoid this is to checkthat the device manager task is free, using the SynthOS_check() primitive, which will return a value of true if the task is not executing.

void main_task(void){	char *TX_data;	bool dataSent = false;	// create data	{		If(SynthOS_check(tx_drive_task)){				SynthOS_call(tx_driver_task(TX_data));			// wait for the driver			dataSent = true;			//continue task		}else{			// do other things		}	}while(dataSent)}

Article page index:

  1. Introduction
  2. How does SynthOS work?
  3. XBee Driver Architecture
  4. Non-blocking Calls

Non-blocking Calls
Non-blocking calls are key forcreating an efficient multitasking architecture. This method enables atask to call another task and allow both of them to execute in parallel.This will minimize the time tasks are waiting for execution by othertasks and can significantly increase CPU utilization.

To support non-blocking task execution, SynthOS uses the SynthOS_start() primitive. One task calls the driver and the calling task will continueexecuting. The calling task can continue operation, ignoring the calledtask until it needs to call it again. If the calling task gets to apoint that it needs to ensure the device driver completed its execution,the main task can use SynthOS_wait() primitive to synchronize with the driver execution. To enable waitingon a specific instance of the transmit or receive tasks, you can create alookup table and wait on a specific entry in this table rather thanwaiting for all tasks to complete.

void main_task(void){	char *TX_data;	bool dataSent = false;		// create data	SynthOS_start(tx_driver(TX_data));	//continue task	//optional, release CPU to enable other tasks	SynthOS_sleep();	// wait for the driver	SynthOS_wait(dataSent == true);	//continue task}

The XBee Code

Figure 6 shows the high-level structure of the code. The main application code can call the receive task xbee_receive() when it is expecting a message or xbee_request() when it is sending a command. If the application needs to continue execution while waiting for the XBee communication, SynthOS_start() can be used to call this tasks. The XBee module will response viainterrupt when new data is received or the transmit buffer is empty. Ifnew data is available, the interrupt will activate the receive function uart_receive_byte() . Once the data is read the receive call task will be notified and the interrupt released.

Figure 6. Conceptual software implementation of the driver

The same mechanism is used for the transmit side. The interrupt is asserted once the output buffer is empty. This will call the uart_transmit_byte() function that will load the buffer, notify the call task xbee_request() , and release the interrupt.

The code for this XBee driver implementation is available for download in Github

In this article we have described thedevelopment of a specific XBee driver for our Arduino robot usingSynthOS. We also have described some of the important considerations fordeveloping device drivers in general. We found that SynthOS handled theinter-task communication and scheduling with little need for us tounderstand the details of the implementation. The software took twoweeks to develop and debug and the entire system fit in 4.5K bytes offlash memory and used only 273 bytes of RAM during execution, making itextremely efficient.

About the Authors
Jacob Harel
isthe Vice President of Business Development/Product Management atZeidman Technologies. Jacob spent the early years of his career atApplied Materials and as a senior consultant for a variety ofsemiconductor fabrication equipment companies. His previous positionbefore joining Zeidman technologies was Senior VP of Product Operationsat Luidia, a leading provider of interactive technology (Luidia was soldto PNF Korea in 2013). He is the named inventor on several patents. Heholds a BA in Computer Science and Economics from Tel Aviv University.

Igor Serikov is the Lead Engineer at Zeidman Technologies. He has devoted his careerto the development of computer algorithms and software developmenttools. He has been a consultant to companies including Sygate, Symantec,NTT Docomo, Wyse Technology, and Ricoh Systems. Igor has a Bachelor’sdegree in computer science from National Taras Shevchenko University ofKyiv.

Article page index:

  1. Introduction
  2. How does SynthOS work?
  3. XBee Driver Architecture
  4. Non-blocking Calls

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.