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

EVENT GROUP — WAIT ON SEVERAL CONDITIONS AT ONCE Temp Task (NTC) hot? SetBits(BIT_TEMP) PIR ISR motion? SetBitsFromISR(BIT_PIR) Event Group (bits) BIT_TEMP BIT_PIR WaitBits(TEMP | PIR, clear, ALL) ALL = AND · ANY = OR Alarm Task blocks until the bits are met Relay fires when met met AND (wait for ALL): relay fires only when temperature is high AND motion is detected — both bits set. OR (wait for ANY): the first bit alone unblocks the task. clearOnExit resets the bits so they must be re-set next time.

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:

xEventGroupWaitBits - wait for ALL (AND) the alarm task proceeds only when BOTH bits are set; it clears them on exit, then fires the relay 0 100 200 300 400 500 600 700 time (ms) BIT_TEMP BIT_PIR ALARM set set blocked on WaitBits (TEMP & PIR) RELAY ON BOTH set -> proceed + clear Conceptual / idealized. waitForAllBits = pdTRUE (AND), clearOnExit = pdTRUE. Only the second bit unblocks the task.

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:

xEventGroupWaitBits - wait for ANY (OR) the alarm task proceeds as soon as EITHER bit is set - the first one unblocks it 0 100 200 300 400 500 600 700 time (ms) BIT_TEMP BIT_PIR ALARM set set blocked on WaitBits (TEMP | PIR) RELAY ON first bit -> proceed Conceptual / idealized. waitForAllBits = pdFALSE (OR). The earliest bit unblocks the task; pick AND vs OR deliberately.

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

  1. What is an event group, and how does it differ from a semaphore?
  2. Explain wait-for-ALL vs wait-for-ANY with the relay example.
  3. What does clear-on-exit do, and what bug appears if you forget it?
  4. 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.

© 2026 Air Supply Information Center (Air Supply BBS)