Embedded Device Control With ECApp
Software Overview
The ECApp firmware is designed to provide simple, low level access to the underlying controller hardware. The controller hardware consists of the following:
Host computer communications device. This is provided by the USB communications device class (CDC) with the ECApp firmware appearing as an RS-232 serial device on the USB host.
Serial Peripheral Interface (SPI) and GPIO pins provide the required control access for communicating with and configuring the connected
rfblocks
hardware modules.
Figure 1 illustrates the overall environment.
The host computer communicates with the embedded control software using simple ASCII protocol commands. These commands are used by application level Python framework which provides a more convenient way to control the target hardware. For more details for this application software please consult Software Support section of the RF Blocks - Open RF System Prototyping document.
Command summary
The ECApp commands are summarized in Table 1. Each command starts with a single character which may then be followed by zero or more command parameters (depending on the specific command) and terminated by the ':' (colon) character.
Command |
Description |
---|---|
|
Write bytes to SPI |
|
Read a byte from SPI |
|
Set SPI clock rate |
|
Read SPI clock rate |
|
Set pin LOW |
|
Set pin HIGH |
|
Toggle pin |
|
Sample pin |
|
Set pin direction to output |
|
Set pin direction to input |
|
Set port pins |
|
Delay by specified microseconds |
|
Pass data to currently running PiPico app |
|
Return firmware version information |
In Table 1 command parameters are described using the following abbreviations:
<port>
-
This is a single character specifying a microcontroller I/O port. For the Atmega32U2 Board an I/O port is defined as a collection of up to 8 I/O pins on the microcontroller board as specified in the Atmega32U2 data sheet. For example, 'B' would indicate port PB. In the case of the Raspberry Pi Pico board, the I/O port consists of a single port 'G' with 21 pins. For the purposes of backwards compatibility Atmega32U2 type ports have also been mapped to Pico board GPIO pins.
<pin>
-
This is a character string specifying the port and number of a microcontroller I/O pin. For example, 'G15' indicates GPIO 15 on the Pico board (pin 20) and 'B5' would indicate pin 5 on port PB of the the Atmega32U2 board (if the Pico board is in use this is also mapped to 'G20' or pin 26).
<bytes>
-
This is a comma separated sequence of hex formatted ASCII byte values. An an example, the sequence of decimal numbers 12,220,78,167,9 would be: 'C,DC,4E,A7,9'.
<mask>
-
This is a single hex formatted ASCII byte value. For example, the mask
0b1100_0100
would be 'C4'. <num>
-
A numeric string.
Command examples
In almost all cases the application level rfblocks Python package is used to interact with the ECApp controller firmware. However in some circumstances it may be useful to interact directly with the firmware. This can be done using a terminal emulator or by using the Python pySerial package.
-
The following series of commands sets the frequency tuning word for the AD9913. The first command sets pin
D1
low, the next command sends 5 bytes via the SPI followed by the final three commands which set pinD1
high and then briefly toggling pinD2
high.LD1:W03,0A,3D,70,A4:HD1:HD2:LD2:
-
The following commands initialize the microcontroller pin states for communicating with the HMC833.
OD1:LD1:IC5:W00:
The first command sets pin
D1
as an output followed by the command to set pinD1
low. The third command in the series sets pinC5
as an input. Finally, the byte00
is sent via the SPI. (This has the effect of toggling the SPISCK
line which will put the connected HMC833 into 'open' mode.) -
The following series of commands sets the AD9552 output frequency and then checks the status of the PLL lock.
LD2:W00,0E,74:HD2:LD2:W00,05,01:HD2:LD2:W60,19,80,39,00,00,00,FE,FF,FF,AF:HD2:LD2:W00,05,01:HD2:LD2:W00,0E,F4:HD2:LD2:W00,05,01:HD2: PC6:
-
The following command toggles the high state on pins
PB7
andPB6
:MB,C0,00,C0,00:
For more examples of using the ECApp commands it's worth looking at the code for the rfblocks device API.
Example SPI command timings
Table 2 shows timing comparisons between the Raspberry Pi Pico board and the Atmega32u2 board. The comparisons are for various kinds of PLO output frequency updates for the HMC833 synthesizer module. As would be expected, the Pico board is between 5 and 10 faster than the Atmega32u2 board for SPI hardware register updates. All the timing comparisons were carried out with an SPI clock speed to 4MHz.
Operation |
Pico board (uS) |
Atmega32u2 board (uS) |
---|---|---|
int->int or frac->frac |
30 |
440 |
int->frac or frac->int |
160 |
1620 |
VCO divider on/off |
50 |
270 |
VCO doubler on/off |
50 |
440 |
Limitations
The firmware has some limitations which arise either from the limitations of the microcontroller hardware on which it executes or compile time limits. The primary limitations are on memory buffer sizes. These are listed in Table 3.
Limit |
Atmega32U2 |
Pico |
---|---|---|
Command length |
512 |
1024 |
Command values |
32 |
128 |
Response length |
16 |
16 |
In the case of the Pico board the limits are fairly arbitrary since the available SRAM (256kB) is much larger than the Atmega. If longer commands are anticipated then it's a simple matter of rebuilding the Pico firmware with larger values for the buffers.
Using the ECApp with Zephyr
This is a version of the ECApp embedded command processor designed for use on the Raspberry Pi Pico controller in a multithreaded environment. Figure 2 illustrates the arrangement of the components of the firmware.
Cooperative time slicing is used as the thread scheduling algorithm.
At it's core is the 'vanilla' command interpreter implemented by the
execute_cmd
function. The command interpreter runs in a separate thread - thecmd_exec_thread
.The
cmd_exec_thread
receives commands from theprocess_cmds
function running in thecmd_proc_thread
via a message queue - thecmd_msgq
. Theexecute_cmd
function then parses and executes the command via a set of API functions. Access to these functions is controlled by thecmd_execution_sem
semaphore.The
cmd_proc_thread
in turn waits on the availability of command characters placed in therecvbuf
ring buffer by the USB serial device interrupt handler implemented in theinterrupt_handler
function. Theprocess_cmds
function builds complete commands and then places them on thecmd_msgq
queue.After execution of a command completes a response can be passed back to the controlling host by placing it on the
respbuf
ring buffer and enabling the serial transmit interrupt.Application specific functionality is implemented in
app_thread
. The ECAppA
command is provided to allow commands to be passed to the application specific thread for processing.The contents of application specific commands received by
execute_cmd
are made available toapp_thread
by placing them on theapp_msgq
queue.app_thread
can make use of the API functions by first claiming thecmd_execution_sem
semaphore.app_thread
may also make use of theexecute_cmd
functionality by placing ECApp commands on thecmd_msgq
queue. Responses to these would be passed back via theapp_respbuf
ring buffer. However, note that it is more efficient to make use of the API functions directly from withinapp_thread
.
Source code for the Zephyr version of ECApp
Useful links to Zephyr documentation:
Building the Firmware
Check out the source code:
The sections below provide details for building firmware for the Raspberry Pi Pico board and the Atmega32u2.
Raspberry Pi Pico board
The ECApp control software is written using the standard RaspberryPi Pico C SDK. These build instructions assume that the SDK is installed on the build host. Documents describing how to install and use the SDK are available from the Raspberry Pi Pico web page.
The following commands will build the firmware:
The resulting file cmdproc.uf2
can now be loaded onto the target Pico
board. This is done as follows:
-
While holding down the white reset button on the PiPico board, plug the USB cable into the host computer then release the button. The PiPico board will appear as an
RP2 Boot
USB device:$ ioreg -p IOUSB +-o Root <class IORegistryEntry, id 0x100000100, retain 17> +-o AppleUSBXHCI Root Hub Simulation@14000000 <class AppleUSBRootHubDevice, id 0x100000357, registered, matched, active, busy 0 $ +-o Bluetooth USB Host Controller@14300000 <class AppleUSBDevice, id 0x1000070f4, registered, matched, active, busy 0 (0 ms), $ +-o RP2 Boot@14200000 <class AppleUSBDevice, id 0x100007491, registered, matched, active, busy 0 (2 ms), retain 13>
This device will appear as a mounted file system,
RPI-RP2
on the host computer: -
Copy the firmware binary image to the
RPI-RP2
file system: -
The PiPico board will automatically reboot and the usual
Pico
USB device will appear:$ ioreg -p IOUSB +-o Root <class IORegistryEntry, id 0x100000100, retain 17> +-o AppleUSBXHCI Root Hub Simulation@14000000 <class AppleUSBRootHubDevice, id 0x100000357, registered, matched, active, busy 0 $ +-o Bluetooth USB Host Controller@14300000 <class AppleUSBDevice, id 0x1000070f4, registered, matched, active, busy 0 (0 ms), $ +-o Pico@14200000 <class AppleUSBDevice, id 0x1000074c4, registered, matched, active, busy 0 (2 ms), retain 15>
The PiPico board is now ready for use.
(See Section 4.1 (Programming the Flash) in the Raspberry Pi Pico Datasheet for instructions on loading firmware via the USB connection.)
Raspberry Pi Pico board using Zephyr OS
These build instructions assume that the Raspberry Pi Pico SDK is installed on the build host. Documents describing how to install and use the SDK are available from the Raspberry Pi Pico web page.
The following steps are required in order to prepare the development host for building the Zephyr OS version of the ECApp firmware. (For full details see Zephyr Getting Started Guide. Note that directions for other OSs can be found there as well.)
-
Install required dependencies. For older versions of MacOS it is best to use MacPorts since it has better support:
For newer versions of MacOS, HomeBrew is probably better:
-
Install Python dependencies and get the Zephyr source code. It's best to create a Zephyr specific Python virtual environment. We use pyenv to manage Python virtual environments. (A good
pyenv
tutorial and install guide can be found here: Managing Multiple Python Versions With pyenv.)Activate the new virtual environment
Install
west
:Get the Zephyr source code
Install Zephyr's Python dependencies
-
Install the Zephyr SDK. Download and verify the Zephyr SDK bundle:
cd ~/.local wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.1/zephyr-sdk-0.16.1_macos-x86_64.tar.xz wget -O - https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.1/sha256.sum | shasum --check --ignore-missing
replace
x86_64
withaarch64
in order to download the 64-bit ARM macOS SDK. Extract the Zephyr SDK bundle archive:Run the Zephyr SDK bundle setup script:
The Zephyr version of ECApp can now be built as follows:
-
Clone the ECApp source code:
-
Ensure that the environment is set appropriately:
-
Configure the board:
-
Build the firmware:
-
Load the firmware onto the target board:
Atmega32U2 board
The control software makes use of the embedded C++ real-time reference application written by Christopher Kormanyos (https://github.com/ckormanyos/real-time-cpp). The reference application is companion code for the book Real-Time C++: Efficient Object-Oriented and Template Microcontroller Programming.
On the AVR platform, the USB serial communications code makes use of the LUFA (Lightweight USB Framework for AVRs) library. (The LUFA license is included here: LUFA License)
The build commands are:
$ cd target/build/atmega32u2 $ make TARGET=../../../src/app/usbcmd/cmdproc clean $ make TARGET=../../../src/app/usbcmd/cmdproc # build the target app
Brief documentation for the build system can be accessed using the
help
target:
The firmware can now be uploaded to the target Atmega32u2 board using
avrdude
:
This requires the use of an ICSP programmer such as a Buspirate.
A more convenient method for uploading the firmware is to use the USB DFU facility described here: Using the DFU bootloader. This requires that the target board has the LUFA DFU bootloader installed. Details for building and installing the bootloader are given in the next section.
Atmega32U2 bootloader
The standard LUFA DFU bootloader is used on the Atmega32u2 control board. The bootloader code is included in the ECApp source. The build commands are:
The bootloader is installed using the ICSP facility. We make use of the
Buspirate which is connected to the ISP pins on the Atmega32u2 board. The
bootloader makefile
contains the necessary configuration settings for
avrdude
:
AVRDUDE_PORT = /dev/tty.usbserial-A600e0V2 AVRDUDE_PROGRAMMER = buspirate AVRDUDE_FLAGS = # To suppress flash verification after upload: # (comment out the following to enable verification after upload) AVRDUDE_VERIFY = -V
The bootloader is then flashed onto the Atmega32u2 using:
LUFA License
LUFA Library Copyright (C) Dean Camera, 2021. dean [at] fourwalledcubicle [dot] com www.lufa-lib.org Permission to use, copy, modify, and distribute this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that the copyright notice and this permission notice and warranty disclaimer appear in supporting documentation, and that the name of the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The author disclaims all warranties with regard to this software, including all implied warranties of merchantability and fitness. In no event shall the author be liable for any special, indirect or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action of contract, negligence or other tortious action, arising out of or in connection with the use or performance of this software.