Open RF Prototyping

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.

ECApp environment

Figure 1: ECApp 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.

Table 1: ECApp firmware commands

Command

Description

W<bytes>:

Write bytes to SPI

R:

Read a byte from SPI

S<num>:

Set SPI clock rate

C:

Read SPI clock rate

L<pin>:

Set pin LOW

H<pin>:

Set pin HIGH

T<pin>:

Toggle pin

P<pin>:

Sample pin

O<pin>:

Set pin direction to output

I<pin>:

Set pin direction to input

M<port><mask><bytes>:

Set port pins

D<num>:

Delay by specified microseconds

A<bytes>:

Pass data to currently running PiPico app

V

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 pin D1 high and then briefly toggling pin D2 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 pin D1 low. The third command in the series sets pin C5 as an input. Finally, the byte 00 is sent via the SPI. (This has the effect of toggling the SPI SCK 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 and PB6:

    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.

Table 2: ECApp timing comparisons for the Pico and Atmega32u2 controller boards

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.

Table 3: ECApp firmware limits

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

ECApp with Zephyr

Figure 2: ECApp and 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 - the cmd_exec_thread.

  • The cmd_exec_thread receives commands from the process_cmds function running in the cmd_proc_thread via a message queue - the cmd_msgq. The execute_cmd function then parses and executes the command via a set of API functions. Access to these functions is controlled by the cmd_execution_sem semaphore.

  • The cmd_proc_thread in turn waits on the availability of command characters placed in the recvbuf ring buffer by the USB serial device interrupt handler implemented in the interrupt_handler function. The process_cmds function builds complete commands and then places them on the cmd_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 ECApp A 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 to app_thread by placing them on the app_msgq queue.

  • app_thread can make use of the API functions by first claiming the cmd_execution_sem semaphore.

  • app_thread may also make use of the execute_cmd functionality by placing ECApp commands on the cmd_msgq queue. Responses to these would be passed back via the app_respbuf ring buffer. However, note that it is more efficient to make use of the API functions directly from within app_thread.

Source code for the Zephyr version of ECApp

Useful links to Zephyr documentation:

  1. Zephyr Message Queues

  2. Zephyr Threads

  3. Zephyr Semaphores

  4. Zephyr Cooperative Time Slicing

Building the Firmware

Check out the source code:

git clone git@gitlab.com:dyadic/ecapp.git
cd ecapp
git branch
* master

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:

cd pico/apps/cmdproc
mkdir build
cd build
cmake ..
make

The resulting file cmdproc.uf2 can now be loaded onto the target Pico board. This is done as follows:

  1. 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:

    $ ls /Volumes/
    RPI-RP2  SSD
    
  2. Copy the firmware binary image to the RPI-RP2 file system:

    $ cp cmdproc.uf2 /Volumes/RPI-RP2
    
  3. 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.)

  1. Install required dependencies. For older versions of MacOS it is best to use MacPorts since it has better support:

    sudo port install cmake ninja gperf python38 ccache qemu dtc wget libmagic
    

    For newer versions of MacOS, HomeBrew is probably better:

    brew install cmake ninja gperf python3 ccache qemu dtc wget libmagic
    
  2. 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.)

    pyenv virtualenv 3.8.8 zephyr
    

    Activate the new virtual environment

    pyenv activate zephyr
    

    Install west:

    pip install west
    

    Get the Zephyr source code

    cd ${RFBLOCKS_ROOT}/software
    mkdir zephyrproject
    cd zephyrproject
    west update
    west zephyr-export
    

    Install Zephyr's Python dependencies

    pip install -r ./zephyr/scripts/requirements.txt
    
  3. 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 with aarch64 in order to download the 64-bit ARM macOS SDK. Extract the Zephyr SDK bundle archive:

    tar xvf zephyr-sdk-0.16.1_macos-x86_64.tar.xz
    

    Run the Zephyr SDK bundle setup script:

    cd zephyr-sdk-0.16.1
    ./setup.sh
    

The Zephyr version of ECApp can now be built as follows:

  1. Clone the ECApp source code:

    git clone git@gitlab.com:dyadic/ecapp.git
    cd ecapp/zephyr/ecapp
    
  2. Ensure that the environment is set appropriately:

    source ${RFBLOCKS_ROOT}/software/zephyrproject/zephyr/zephyr-env.sh
    
  3. Configure the board:

    pyenv activate zephyr
    west config build.board rpi_pico
    
  4. Build the firmware:

    west build -t pristine
    west build -b rpi_pico
    
  5. Load the firmware onto the target board:

    cp build/zephyr/zephyr.uf2 /Volumes/RPI-RP2/
    

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:

$ make TARGET=../../../src/app/usbcmd/cmdproc help

The firmware can now be uploaded to the target Atmega32u2 board using avrdude:

$ make TARGET=../../../src/app/usbcmd/cmdproc 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:

cd ecapp/src/util/Bootloaders/DFU
make clean
make

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:

make avrdude

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.