Overview

Task loop:

  1. Measure voltage using ADC
  2. Take mutex
  3. Write to global voltage variable
  4. Send a task notification to the Status LED task
  5. Give the mutex
  6. Update task health flag to feed the watchdog

Tasks

NamePriorityStack SizePeriodicity/TriggerDescription
static TaskHandle_t xTaskSensorLow128 words100 msMonitor voltage using ADC
staticTaskHandle_t xTaskAlarmHigh128 wordsEvent-driven (sensor update)Control LEDs
static TaskHandle_t xTaskUARTMedium128 wordsEvent-driven (sensor update)Transmit data
  • Static: limit variable’s scope to main.c file for encapsulation

Concurrency and Synchronization

ComponentDescription
SemaphoreHandle_t xMutexManage shared access for fBatteryVoltage
SemaphoreHandle_t xBinSemaphoreWake up TaskAlarm only when there is new data
QueueHandle_t xUARTQueueQueue to send messages to TaskUART
  • Why does TaskSensor gives the semaphore before giving the mutex?
    • Prevent tasks from fighting over the mutex before they are “synced” by the semaphore
    • Give the semaphore first to let the scheduler organize the ready tasks
  • Why statically allocate tasks, mutexes, and semaphores?
    • Ensures determinism, no risk of runtime memory allocation failure

Variables

VariableDescription
volatile float fBatteryVoltageBattery voltage read by ADC
const float fThresholdVoltageThreshold for lighting up green/red LED
volatile uint32_t currentTimeButton debouncing
volatile uint32_t prevTimeButton debouncing
  • Volatile: ensure compiler doesn’t optimize read/writes

Messages

typedef enum {
    MSG_TYPE_VOLTAGE,
    MSG_TYPE_BUTTON
} MsgType_t;
 
typedef struct {
    MsgType_t type;    
    float value;       
} UARTMsg_t;

ADC - Ring Buffer

Implemented a ring buffer that saves the last 8 readings. Passes the average value to the alarm task to make it more consistent

Task Notification

  • Clear the value after reading it

Checking a task notification

BaseType_t xTaskNotifyWait(
    uint32_t ulBitsToClearOnEntry, 
    uint32_t ulBitsToClearOnExit, 
    uint32_t *pulNotificationValue,
    TickType_t xTicksToWait
);
  1. Enter the function xTaskNotifyWait
  2. Clear the bits in the mask ulBitsToClearOnEntry
  3. Check the value of the notification
    • Each task has a 32-bit value
      • Value was sent from another task and received by the task that calls xTaskNotifyWait
    • If there is a notification present (check the flag that indicates if there is a notif)
      • Read the result and store it in the pulNotificationValue
      • Clear bits in ulBitsToClearOnExit
      • Return pdTRUE
    • If there is no notification present
      • Wait for xTicksToWait until a notification arrives
      • When it arrives, go to step 3
      • If it doesn’t arrive, return pdFALSE