Hello again, Indy Module fans! We hope you enjoyed the three Indy Module with MSP430 host firmware posts from the last few weeks. One piece of feedback that came up a few times was that you guys wanted examples of firmware where instead of blocking MSP430 code execution while performing inventories, the inventory was polled, and the MSP430 could perform other tasks during the inventory activity. Well, we listened, and put together a couple of examples based on the second post, RS500 With MSP430 IRI-LT Host and USB-UART Printf.
In this bonus post, we'll present the blocking behavior of the second example, and present two new implementations of the inventory behavior that don't block execution for the entire period.
In the interest of brevity, a lot of the previously discussed topics will be glossed over. Check out the rest of the postings in the series here:
Bonus Post 1: Indy Module With MSP430 IRI-LT Host - Non-Blocking
Bonus Post 2: Indy Module With MSP430 IRI Host and USB-UART Printf
Previous Example Behavior
In the previous example, Indy Module With MSP430 IRI-LT Host and USB-UART Printf, inventories were performed using the ipj_util_perform_inventory() function, contained in the ipj_utils.c library. This function starts an inventory using the IRI-LT communication protocol, then waits for the a specific amount of time to pass while processing incoming IRI-LT traffic from the Module, before eventually sending a stop command using IRI-LT and returning. This behavior means that if a 250ms inventory is invoked using this function, it will be around 250ms before firmware execution returns to the context that the function was called in.
This behavior creates latency that is unacceptable in many embedded systems, especially those that need to poll for events or maintain other threads of behaviors.
In order to quantify this latency, we've added a few lines of code to toggle a pin on the MSP430 microcontroller when an inventory is being performed. The logic analyzer screenshot shown in Figure 1 shows the latency during inventories. The "nInventorying" pin goes low when the inventory starts, and high when the inventory ends. The "Serial Host RX" and "... TX" signals show IRI-LT packets going back and forth between MSP430 (host) and Indy Module. It's worth noting that in this screenshot, no tags were present, so EPC reports were not being sent. The only IRI-LT packets that are visible are start and stop commands from the MSP430 host, and acknowledgements from the Indy Module.
Figure 1 clearly shows that the CPU is being occupied for ~250ms at a time when running the blocking inventories. We'll rectify this in the following examples.
Figure 1 - Blocking Inventory Firmware Execution Latency
New Example 1 - Periodic Non-blocking Inventories
The first step towards improving the latency of the firmware shown in the previous example is to make the inventory function ipj_util_perform_inventory() non-blocking. We can do this by splitting the function into two or more functions, using one to start an inventory (in the example this is ipj_util_start_inventory()), and using another to maintain or end the inventory(in the example this is ipj_util_continue_inventory()). We then add a little bit of state logic to decide which function to call at any given time, and we're done.
All three code updates are shown in the Appendix at the end of this blog post. Our infinite while(1) loop is modified as shown in Code 1, ipj_util_start_inventory()is shown in Code 2, and ipj_util_continue_inventory() is shown in Code 3.
This code allows for additional function calls while executing an inventory, as is shown in the logic analyzer diagram shown in Figure 2. The longest latency observed is the ~9ms it takes to process an IRI-LT "stop" packet. Note that this figure was captured with no tags in the field, so again the only IRI-LT packets that are visible are start and stop commands from the MSP430 host, and acknowledgements from the Indy Module. Tag presence will increase the IRI-LT traffic, and also increase firmware latency as the USB transmit function transmitUSBBuffer() is executed.
Figure 2 - Non-Blocking Inventory Firmware Execution Latency
New Example 2 - Continuous Non-blocking Inventory
As discussed in the previous example, the longest period of latency is now due to the constant stopping and starting of inventory rounds. However, if inventory rounds are non-blocking, there may no longer be a reason to start and stop them. Instead, we can perform one continuous inventory, and save ourselves the overhead of constantly sending IRI-LT start and stop instructions. This example does just that.
We do that by modifying the ipj_util_start_inventory() and ipj_util_continue_inventory() functions such that if the inventory duration argument timeout_ms is 0, we never instruct the Indy Module to stop the inventory round.
This behavior provides the latency performance shown in Figures 3, 4, and 5. Figure 3 is on the same scale as Figure 2, but the latency is so low without the IRI-LT stop and start packets that we can't even see any latency. It's worth noting in this figure that there is no IRI-LT traffic, as start and stop commands are unnecessary, and there are no tags in the field. In Figure 4, we zoom in and see that the latency is about 15 us maximum when no tags are in the field. After adding tags, behavior changes as shown in Figure 5, with a latency of about 8ms with each EPC report that comes in.
Figure 3 - Non-Blocking Continuous Inventory Firmware Execution Latency
Figure 4 - Non-Blocking Continuous Inventory Firmware Execution Latency Closeup
Figure 5 - Non-Blocking Continuous Inventory Firmware Execution Latency Closeup with Tags Present
The example setup for these two non-blocking examples is the same as the rest of the examples in the series, with a MSP430 USB LaunchPad evaluation kit connected to the Host UART TX and RX connections of the RS500 Development Kit or RS2000 Development Kit, and both kits connected to a host PC via USB. For more details on the hardware setup, see the parent blog post Indy Module With MSP430 IRI-LT Host and USB-UART Printf.
To configure the MSP430 on the USB Launchpad board, simply program the device with one of the firmware images attached to the page("MSP430 RS500 IRI-LT Host with USB-UART Printf Non-blocking.txt" and "MSP430 RS500 IRI-LT Host with USB-UART Printf Non-blocking Continuous.txt"). These images can be programmed using the standalone MSP430Flasher utility. This utility is included with the MSP-EXP430F5529LP software package in the "Binary" directory. It can be used to program the attached firmware images using the following command-inputs:
MSP430Flasher.exe -n MSP430F5529 -w "MSP430 RS500 IRI-LT Host with USB-UART Printf Non-blocking.txt" -v -z [VCC] -m SBW2 (-i USB) (-e ERASE_ALL)
MSP430Flasher.exe -n MSP430F5529 -w "MSP430 RS500 IRI-LT Host with USB-UART Printf Non-blocking Continuous.txt" -v -z [VCC] -m SBW2 (-i USB) (-e ERASE_ALL)
The source for the TI Code Composer Studio(CCS) projects is available on a separate download page, as it contains contents of the ITK that require agreement to a set of terms and conditions with Impinj. To start the process of getting access to the CCS project and ITK Release Package, simply follow this link and follow the instructions there.
Appendix - Non-Blocking Inventory Functions
Code 1 - Periodic Non-Blocking Inventory while(1) Loop
// Enter infinite loop
//Add your own code to exercise the IRI host here
Code 2 - Periodic Non-Blocking Inventory ipj_util_start_inventory() Function
ipj_error ipj_util_start_inventory(ipj_iri_device* iri_device, uint32_t timeout_ms)
Code 3 - Periodic Non-Blocking Inventory ipj_util_continue_inventory() Function
|ipj_error ipj_util_continue_inventory(ipj_iri_device* iri_device, uint32_t timeout_ms)
static uint8_t inventory_state = INVENTORY_STATE_RUN;
/* Run inventory until end time reached */
if((end_time_ms == 0 )||(platform_timestamp_ms_handler() < end_time_ms))
/* Call ipj_receive to process tag reports */
error = ipj_receive(iri_device);
/* Stop inventory */
error = ipj_stop(iri_device, E_IPJ_ACTION_INVENTORY);
/* Set stop end time */
end_time_ms = platform_timestamp_ms_handler() + timeout_ms;
if(!ipj_stopped_flag && platform_timestamp_ms_handler() < end_time_ms)
/* Call ipj_receive to process tag reports */
error = ipj_receive(iri_device);
inventory_state = INVENTORY_STATE_RUN;