PSoC5LP RTOS
Lab 10 — Event Groups & System Coordination
FreeRTOS on PSoC 5LP · Lab Series. Platform: CY8CKIT-059 (PSoC 5LP, Cortex-M3) · PSoC Creator 4.4 · FreeRTOS Kernel V11. I/O: NTC (temperature) + PIR (motion) → Relay. Pairs with Chapter 12 — Timers & Event Groups.
1. Introduction & Objectives
A semaphore or notification signals one condition. But sometimes a task must wait until several conditions are true — "temperature high and motion detected", or "sensor ready and UART ready and calibration done". An event group is a set of bits that different tasks and ISRs can set; a waiting task blocks until the bits it cares about are met, as an AND (all) or an OR (any).
After this lab you can:
- Create an event group and set bits from a task (xEventGroupSetBits) and an ISR (xEventGroupSetBitsFromISR).
- Block a task on several bits with xEventGroupWaitBits, choosing wait-for-ALL (AND) or wait-for-ANY (OR).
- Control the clear-on-exit behaviour and explain why leftover bits re-trigger.
- Build a multi-condition trigger (temp-high AND motion → relay) and a system-startup gate.
2. Prerequisites
Hardware: CY8CKIT-059 PSoC 5LP Prototyping Kit; RobotSoC Kit MF52D NTC thermistor + 10 kΩ, HC-SR501 PIR, 5 V relay.
Software: PSoC Creator 4.4 with the FreeRTOS V11 BaseProject.
Chapters (sync progress): Required — Chapter 12 (Timers & Event Groups: the event-group half); Peripheral prerequisite — the NTC + ADC is covered in the separate (non-FreeRTOS) Lab 05 (Thermometer); PIR and relay are assumed known.
3. Reading Materials / References
- Chapter 12 slides — Timers & Event Groups (wait on several bits, AND vs OR, clear-on-exit).
- FreeRTOS API: xEventGroupCreate, xEventGroupSetBits, xEventGroupSetBitsFromISR, xEventGroupWaitBits, xEventGroupClearBits.
4. Background & Theory of Operation
An event group is a set of flags. xEventGroupCreate() returns a group of bits (flags). Any task can set bits with xEventGroupSetBits(eg, bits); an ISR uses xEventGroupSetBitsFromISR. Each bit is an independent condition — here BIT_TEMP (temperature high) and BIT_PIR (motion detected).
Waiting on several bits. A task blocks with xEventGroupWaitBits(eg, bitsToWaitFor, clearOnExit, waitForAllBits, timeout):
- waitForAllBits = pdTRUE → AND: the task unblocks only when every requested bit is set.
- waitForAllBits = pdFALSE → OR: the task unblocks as soon as any requested bit is set.
- clearOnExit = pdTRUE → the requested bits are cleared when the task unblocks, so the conditions must be set again next time.
AND vs OR — pick deliberately. For the smart trigger we want the relay to fire only when it is hot and someone is there — that is AND. If instead you wanted "react to either sensor", that is OR. The same call does both; the fourth argument chooses.
Clear-on-exit timing. With clearOnExit = pdTRUE, the bits that satisfied the wait are reset the instant the task proceeds, so the next alarm needs the conditions set afresh. If you leave bits set (clear = pdFALSE), the wait is satisfied again immediately on the next loop and the task re-triggers continuously — a common bug. For a condition that is a level (temperature still high), the Temp task should clear BIT_TEMP itself when the temperature drops, so the group reflects the real state.
From an ISR. xEventGroupSetBitsFromISR does not set the bits directly; it asks the timer/daemon task to do it, and takes a pxHigherPriorityTaskWoken to pass to portYIELD_FROM_ISR. Keep the ISR tiny and at priority 2–7, as always.
A second use — system startup. Event groups also coordinate a start-up sequence: set BIT_SENSOR_READY, BIT_UART_READY, BIT_CAL_DONE from three init tasks, and have the Control task WaitBits(ALL) so it proceeds only when the whole system is ready. Same mechanism, AND mode (Exp03).
5. RTOS Task Architecture
The Temp Task sets BIT_TEMP when the NTC is hot; the PIR ISR sets BIT_PIR on motion. The Alarm Task blocks on WaitBits(BIT_TEMP | BIT_PIR, …) — AND to require both, OR to accept either — and fires the Relay when the condition is met, clearing the bits on exit.
6. Wait for ALL (AND) vs. Wait for ANY (OR)
AND — the relay fires only when BOTH bits are set:
BIT_TEMP set first, then BIT_PIR; the task proceeds when the second bit arrives, clears both, fires the relay.
OR — the first bit alone unblocks the task:
The earliest bit unblocks the task; the later bit is irrelevant to this trigger.
The only difference is the waitForAllBits argument. AND is a conjunction of conditions; OR is a disjunction. Choosing wrong is a logic bug, not a crash — so choose deliberately.
7. Schematic Circuit & Pin Configuration
- NTC divider → ADC input (as in Lab 05 / Lab 07).
- PIR output → an interrupt pin (isr_Pir).
- Relay control → a GPIO (through its driver transistor).
- Assign the pins in <project>.cydwr → Pins.
8. PSoC Creator Configuration
- An ADC_SAR for the NTC; an Interrupt component on the PIR edge; a Digital Output Pin for the relay.
- Set the PIR interrupt priority to 3 (within 2–7), or isr_Pir_SetPriority(3) at startup (the template does this).
- FreeRTOSConfig.h (already set): configUSE_EVENT_GROUPS = 1, configUSE_TIMERS = 1 (the daemon task carries the FromISR set), heap_4.
9. Provided Template
You are given Lab10_Template_main.c:
- Given: all includes (note #include "FreeRTOS_PSoC.h"), the event-group handle, task handles, bit definitions, and a complete main() that creates the event group, sets the PIR ISR priority into 2–7, starts it, and creates the tasks. Placeholders keep the project building and running. A WAIT_FOR_ALL macro selects AND (Exp01) or OR (Exp02).
- You write: vTempTask() (set/clear BIT_TEMP), Pir_ISR() (set BIT_PIR FromISR), and vAlarmTask() (WaitBits → relay).
- Do not change the boot sequence in main().
#define WAIT_FOR_ALL pdTRUE /* AND; pdFALSE = OR for Exp02 */
static void vAlarmTask(void *arg)
{
for (;;) {
xEventGroupWaitBits(xEvents, BOTH,
pdTRUE, /* clear on exit */
WAIT_FOR_ALL, /* pdTRUE=ALL, pdFALSE=ANY */
portMAX_DELAY);
Relay_Write(1); vTaskDelay(pdMS_TO_TICKS(2000)); Relay_Write(0);
}
}
10. Lab Experiments
Exp01 — Basic: smart trigger (AND). With WAIT_FOR_ALL = pdTRUE, the Temp task sets BIT_TEMP when hot and the PIR ISR sets BIT_PIR on motion. The Alarm task fires the relay only when both are set, clearing on exit. Expected: the relay fires only when it is hot and there is motion; warming alone or motion alone does nothing.
Exp02 — Advanced: wait for ANY (OR), and clear-on-exit. Set WAIT_FOR_ALL = pdFALSE and show the relay now fires on either condition. Then experiment with clearOnExit: leave the bits set and show the alarm re-triggers continuously; clear them and show one trigger per event. Expected: a working OR trigger and a clear explanation of the clear-on-exit timing.
Exp03 — Challenge: system-startup gate. Add three "ready" bits — BIT_SENSOR_READY, BIT_UART_READY, BIT_CAL_DONE — set by three short init tasks. Have a Control task WaitBits(ALL three) so it proceeds only when the whole system is ready. Expected: the Control task stays blocked until all three bits are set, in any order, then proceeds once.
11. Testing & Verification
| # | Pass condition |
|---|---|
| Exp01 | Relay fires only when temp-high AND motion (AND); neither alone triggers it. |
| Exp02 | OR mode fires on either; clear-on-exit gives one trigger per event vs continuous. |
| Exp03 | Control task proceeds only after all three ready-bits are set, in any order. |
Submit: your three handlers, the Exp02 AND-vs-OR and clear-on-exit observations, and the Exp03 startup gate.
12. Q&A & Troubleshooting
- Relay fires on temperature alone → you used OR (or the wrong bits); set WAIT_FOR_ALL = pdTRUE and wait on BOTH.
- Alarm re-triggers forever → bits not cleared; set clearOnExit = pdTRUE, or clear a level-bit when its condition ends.
- PIR never sets its bit → ISR not started, source not cleared, or you used xEventGroupSetBits (task form) in the ISR.
- Relay stuck on → BIT_TEMP never cleared when the temperature drops; the Temp task must clear it.
- Nothing ever proceeds → you waited on bits that are never all set together; check your AND set.
13. Analysis Questions & Lab Report
- What is an event group, and how does it differ from a semaphore?
- Explain wait-for-ALL vs wait-for-ANY with the relay example.
- What does clear-on-exit do, and what bug appears if you forget it?
- Why must a level condition (temperature high) be cleared by its producer, not only on exit?
Your report should include your handlers, the Exp02 observations, and answers to the four questions.