Home / Study Guide Comments

1 Section: Creating a command queue (pg 346)

2 Section: Deciding on queue contents (pg 346)

3 Section:  The USB virtual comm driver (pg 356)

4 Section: Using the code (pg 357)


Chapter 13, part 2: 

Clarifications and Additional Info



Chapter 13: Creating Loose Coupling with Queues (page 343)


1  Section: Creating a command queue (pg 346)

      Additional info, page 346

o      Chapter 13's example programs include a "PWM implementation".  It's in pwmImplementation.c.  It provides functions for setting the LEDs' brightness levels, i.e., 0 to 100%.

o      How the PWM implementation works is not described in the book.  This study-guide provides a tutorial description, in Chapter 13, part 1.

2  Section: Deciding on queue contents (pg 346)

      Clarification, page 346

o      This entry provides additional info about the "interface definition" iPWM.  This is info that was helpful to me in understanding the abstraction being used, and the code.  Some of the perceived problems could be due to inexperience on my part.

o      The iPWM "interface definition" is used for calling functions that set a particular LED's brightness-level.

      For example, the function SetBlueDuty(100.0) sets the blue LED's brightness to 100%. 

      The iPWM "interface definition" is an abstraction, and it is intended for calling different types of functions that set LED brightness-levels.

o      I found that the interface-definition was difficult to make sense of, as an abstraction.

      Abstractions are generalizations of specific concrete instances.  Typically, understanding an abstraction requires knowing concrete instances, and knowing the abstraction's purpose or use.

      When reading the chapter and code, it was not clear to me what different types of LED functions would be supported by this interface-definition.  The book has some discussion of that, but not until the end of the chapter.  Just one additional LED type is described there, but it wasn't clear to me how the iPWM interface definition would help with it.

      For instance, the iPWM "interface definition" is implemented as a struct typedef, named iPWM.  The struct has one element, which is a function pointer.  The book says other elements could be added to the struct, to extend the interface, but I could not envision what those elements would be. ("Wrapping the function pointer in a struct such as iPWM provides the flexibility of adding additional functions while keeping refactoring to a minimum.")

      In retrospect, I can see how using function pointers could be a useful interface.  For example, one function-pointer could be used for functions that turn on a green LED.  The pointer could be set to a particular function that turns on a particular green LED.  This would give more flexibility than directly calling a particular function by name.

o      In reading the code, I didn't know of other ways the interface definition might be used, in addition to calling the three functions for the existing LEDs, e.g., SetBlueDuty().  This made it harder to make sense of the interface's code.

      If the board's 3 LEDs are all that needs to be supported, the extra code for the interface-definition is superfluous and nonsensical.  For instance, SetBlueDuty() could just be called directly.


      Additional info, page 346

o      This entry describes how the iPWM struct is declared and initialized.

o      Regarding the iPWM struct, the book states, "This struct only (currently) consists of a single function pointer: iPwmDutyCycleFunc. iPwmDutyCycleFunc is defined as a constant pointer—after the initialization of the iPWM struct, the pointer can never be changed. This helps guarantee the pointer won't be overwritten..."

o      The typedefs for the iPWM struct are defined in iPWM.h:

typedef void (*iPwmDutyCycleFunc)( float DutyCycle );

typedef struct


     const iPwmDutyCycleFunc SetDutyCycle;


o      The iPWM structs are declared as global variables, and initialized, in pwmImplementation.c

      Storage for the struct is in the executable's header (ELF?).  The struct is initialized in the header by the compiler and linker.

iPWM BluePWM = {.SetDutyCycle = SetBlueDuty};


3  Section:  The USB virtual comm driver (pg 356)

      Additional info, page 356

o      This entry provides info about CDC_Receive_FS(), in usbd_cdc_if.c

o      How CDC_Receive_FS() is created and used:

      Apparently, CDC_Receive_FS() is generated by a tool like STM32CubeMX or STM32CubeIDE.

      CDC_Transmit_FS() can be used to send data over a USB port, and that function is globally available.  For example, in Chapter 11, CDC_Transmit_FS() is called from mainRawCDC.c.

      However, CDC_Receive_FS() is not globally available.  It is a static function defined in usbd_cdc_if.c, so it cannot be used outside of that file.

      CDC_Receive_FS() gets called from a USB ISR, when data is received over a USB port.

      The function's parameters specify a pointer to the received data, and its length.

      The user can add code to CDC_Receive_FS(), to store the received data, e.g., the call to xStreamBufferSendFromISR().

o      User-code added to CDC_Receive_FS():

      These findings include some speculation, so they might not be completely correct.

      All of the code is user-code (i.e., created by Brian Amos, the book's author), except the return.

      These two function calls are needed to set-up for getting the next set of USB data that will be received: 

USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);


      The call to USBD_CDC_ReceivePacket() tells the CDC layer that it's OK to receive the next packet.  So, that call might need to be after any code that uses the current data, e.g., after the call to xStreamBufferSendFromISR().

      In the user-code, USBD_CDC_SetRxBuffer() is called, then xStreamBufferSendFromISR().  This call-order isn't required.  xStreamBufferSendFromISR() could be called first.

o      Reference info for CDC_Receive_FS()

      CDC_Receive_FS() isn't well documented.


      "Send and Receive data to PC without UART (STM32 USB COM)"

o      https://controllerstech.com/send-and-receive-data-to-pc-without-uart-stm32-usb-com/

      "Hints for using the CDC USB Serial"

o      https://hackaday.io/project/20879-notes-on-using-systemworkbench-with-stm32-bluepill/log/57048-hints-for-using-the-cdc-usb-serial

      Forum discussions

      "embedded - STM32 USB CDC Rx Interrupt - Stack Overflow"

o      https://stackoverflow.com/questions/64878727/stm32-usb-cdc-rx-interrupt

      "Read data from PC to STM32 via USB CDC"

o      https://community.st.com/s/question/0D50X00009Xkfd1SAB/read-data-from-pc-to-stm32-via-usb-cdc

      "How best to do CDC Host PC to microcontroller virtual com port communications?"

o      https://www.openstm32.org/forumthread3159


4  Section: Using the code (pg 357)

      Clarification, page 357

o      This section provides info on installing the packages needed to run colorSelector.py.

o      Installing the packages:

      requirements.txt lists the needed packages, including the needed versions, e.g.,


      The packages can be installed via:  pip install -r requirements.txt

      But, read the next bullet before running  pip install

o      Possible installation problems:

      Since requirements.txt specifies the package versions, the pip install will replace packages that are already installed, but have a different version.  This can result in packages being replaced with an older version.

      There's two possible solutions for that problem:

      One solution is to use a Python “Virtual Environment” for colorSelector.py.

      Another solution is to install the most recent version of the required packages. 

o      In requirements.txt, the version numbers would need to be removed, and the "==" symbols.

o      Then run:  pip install -U -r requirements.txt

o      This worked using the current packages on 12/2021.

o      Among the packages listed in requirements.txt, only these are needed.  The others can be removed from the list.














      Additional info, page 357

o      Tera Term can also be used to send commands to the board.

o      In Tera Term, a macro can be used to send binary strings.  An example is below.

o      To run the macro:

      Save it as a file, e.g., colorSelector.ttl

      In Tera Term, run the macro file via:  Control : Macro

o      Tutorial on TeraTerm macros:


o      An example Tera Term macro is below.  The CRC values do not actually get checked by the code on the board, so those values are specified as 0's.


; Command format: <STX><Cmd><red><green><blue><CRCLSB><CRC><CRC><CRCMSB>


; Loop 5 times

for i 1 5


    ; Turn all on


    timeout = 2  ; 2 seconds

    wait "force timeout"  ; wait for timeout


    ; Set intensity


    timeout = 2       

    wait  "force timeout"


    ; Turn all off


    timeout = 2       

    wait  "force timeout"