Overview
Task loop:
- Measure voltage using ADC
- Take mutex
- Write to global voltage variable
- Send a task notification to the Status LED task
- Give the mutex
- Update task health flag to feed the watchdog
Tasks
| Name | Priority | Stack Size | Periodicity/Trigger | Description |
|---|---|---|---|---|
static TaskHandle_t xTaskSensor | Low | 128 words | 100 ms | Monitor voltage using ADC |
staticTaskHandle_t xTaskAlarm | High | 128 words | Event-driven (sensor update) | Control LEDs |
static TaskHandle_t xTaskUART | Medium | 128 words | Event-driven (sensor update) | Transmit data |
- Static: limit variable’s scope to
main.cfile for encapsulation
Concurrency and Synchronization
| Component | Description |
|---|---|
SemaphoreHandle_t xMutex | Manage shared access for fBatteryVoltage |
SemaphoreHandle_t xBinSemaphore | Wake up TaskAlarm only when there is new data |
QueueHandle_t xUARTQueue | Queue 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
| Variable | Description |
|---|---|
volatile float fBatteryVoltage | Battery voltage read by ADC |
const float fThresholdVoltage | Threshold for lighting up green/red LED |
volatile uint32_t currentTime | Button debouncing |
volatile uint32_t prevTime | Button 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
);- Enter the function
xTaskNotifyWait - Clear the bits in the mask
ulBitsToClearOnEntry - 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
- Value was sent from another task and received by the task that calls
- 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
- Read the result and store it in the
- If there is no notification present
- Wait for
xTicksToWaituntil a notification arrives - When it arrives, go to step 3
- If it doesn’t arrive, return
pdFALSE
- Wait for
- Each task has a 32-bit value