When using queues as a way of passing commands to different parts of the system, it is important to think about what the queue should actually hold, instead of what might be coming across the wire in a physical sense. Even though a queue might be used to hold payloads from a datastream with header and footer information, the actual contents of the queue will usually only contain the parsed payload, rather than the entire message.
Using this approach allows more flexibility in the future to retarget the queue to work over other physical layers.
Since LedCmdExecution() will be operating primarily on the iPWM pointers to interface with the LEDs, it is convenient for the queue to hold a data type that can be used directly by iPWM.
The iPWM definition from Chapter13/Inc/iPWM.h is as follows:
typedef void (*iPwmDutyCycleFunc)( float DutyCycle );
typedef struct
{
const iPwmDutyCycleFunc SetDutyCycle;
}iPWM;
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, so constantly checking to ensure it isn't NULL won't be necessary.
Wrapping the function pointer in a struct such as iPWM provides the flexibility of adding additional functions while keeping refactoring to a minimum. We'll be able to pass a single pointer to the iPWM struct to functions, rather than individual function pointers.
The DutyCycle argument is defined as float, which makes it easy to keep the interface consistent when interfacing with hardware that has different underlying resolutions. In our implementation, the MCU's timer (TIM) peripherals will be configured to have a 16-bit resolution, but the actual code interfacing to iPWM doesn't need to be concerned with the available resolution; it can simply map the desired output from 0.00 (off) to 100.00 (on).
There are two main considerations when deciding on what data LedCmd contains:
- ledCmdExecutor will be dealing with iPWM directly, so it makes sense to store floats in LedCmd.
- We'd also like our LED controller to have different modes of operation, so it will also need a way of passing that information. We'll only have a handful of commands here, so a uint8_t 8-bit unsigned integer is a good fit. Each cmdNum case will be represented by enum (shown later).
This results in the following structure for LedCmd:
typedef struct
{
uint8_t cmdNum;
float red;
float green;
float blue;
}LedCmd;
The LED Cmd Executor's primary interface will be a queue of LedCmds. State changes will be performed by writing new values in the queue.
Now that we have a data model defined, we can look at the remaining components of this application.