PSoC5LP RTOS
Lab 20 — Task Synchronization Patterns

FreeRTOS on PSoC 5LP · Lab Series. Platform: CY8CKIT-059 (PSoC 5LP, Cortex-M3) · PSoC Creator 4.4 · FreeRTOS Kernel V11. Patterns: Rendezvous · Barrier (xEventGroupSync) · Gatekeeper. Pairs with Chapter 7 — Task Notifications & Chapter 12 — Timers & Event Groups.


1. Introduction & Objectives

The earlier labs taught the synchronization primitives — notifications, semaphores, mutexes, queues, event groups. This lab teaches the patterns you build from them when several tasks must work in step. Three sensor tasks each take a reading and meet at a barrier; only when all three have arrived does a fusion step run, so it never mixes a fresh reading with a stale one. All text output flows through a single gatekeeper task that owns the UART, so prints never interleave and no lock is needed. Along the way you will see why a real handshake uses blocking, not vTaskSuspend/vTaskResume.

After this lab you can:

  • Build a rendezvous (two tasks wait for each other) and a barrier (N tasks meet) with xEventGroupSync.
  • Build a gatekeeper: one task owns a resource, others send requests through a queue — serialized with no mutex.
  • Choose the right pattern by the shape of the synchronization.
  • Explain why vTaskSuspend/vTaskResume is the wrong tool for a task-to-task handshake.

2. Prerequisites

Hardware: CY8CKIT-059 PSoC 5LP Prototyping Kit; RobotSoC Kit up to three sensor sources (or simulated readings); UART for the gatekeeper output.

Software: PSoC Creator 4.4 with the FreeRTOS V11 BaseProject; add event_groups.c to the build.

Chapters (sync progress): Required — Chapter 12 (Event Groups — the barrier) and Chapter 7 (Task Notifications — the rendezvous signal); Related — Lab 10 (event groups AND/OR), Lab 14/17 (queues and the bus-server idea), Lab 05 (task states — why not suspend/resume for sync).

3. Reading Materials / References

  • Chapter 12 slides — Event Groups (bits, waiting for AND/OR, xEventGroupSync).
  • Chapter 7 slides — Task Notifications (the fast one-to-one signal).
  • FreeRTOS API: xEventGroupCreate, xEventGroupSync, xEventGroupSetBits, xTaskNotifyGive/ulTaskNotifyTake, xQueueSend/xQueueReceive.

4. Background & Theory of Operation

A handshake is symmetric — so use blocking, not suspend/resume. Synchronising two tasks means each waits for a signal from the other; neither races ahead. The right tools are the blocking primitives — a task notification, a semaphore, or event-group bits — where a task chooses to block until the condition holds. vTaskSuspend/vTaskResume looks superficially similar but is the wrong tool: it is asymmetric (one task reaches into another and freezes it) and racy (you can suspend a task in the middle of a critical section or while it holds a mutex, stalling everyone that needs that lock). Reserve suspend/resume for coarse, deliberate on/off of a whole task (Lab 05); never use it as a task-to-task handshake.

Rendezvous — two tasks meet. The simplest pattern: task A signals B and waits for B's signal; B signals A and waits for A's. Both pass the rendezvous only after both have arrived, so neither proceeds with the other's stale data. With task notifications it is two xTaskNotifyGive + two ulTaskNotifyTake; conceptually it is a two-task barrier.

Barrier — N tasks meet. Generalise the rendezvous to N tasks with an event group. Give each task a bit. xEventGroupSync(group, myBit, allBits, timeout) does two things atomically: it sets this task's bit and blocks until all the required bits are set. The task that sets the last bit releases every waiting task at once, and the group is cleared for the next round. That is exactly "all arrive, then all go" — ideal when a later step needs each task's fresh result together (sensor fusion, a synchronised phase change, a coordinated actuation).

Gatekeeper — one task owns a resource. When many tasks need a shared resource (a UART, a display, a log), you can protect it with a mutex (Lab 17) — or you can give it an owner. A gatekeeper task is the only code that touches the resource; every other task sends a request (a line to print, a value to display) through a queue, and the gatekeeper serves them one at a time. Benefits: outputs never interleave, there is no lock and therefore no priority-inheritance concern, and the senders never block on each other — they drop a request and move on (back-pressure is just the queue depth). It is the bus-server idea from Lab 17, applied to any shared resource.

Pick the pattern by shape. One task telling another "go" → a notification. Many tasks that must meet before proceeding → an event-group barrier. A shared output or resource many tasks use → a gatekeeper. Matching the pattern to the shape is the skill; the primitives are just the building blocks.

5. RTOS Task Architecture

TASK SYNC — MANY TASKS MEET AT A BARRIER, ONE OWNS THE OUTPUT Pitch Task Yaw Task Roll Task each: read · set its bit Barrier xEventGroupSync() all 3 bits → release together Fusion Task combine 3 readings format a message Print Queue Gatekeeper the ONLY task that touches the UART other tasks also send here → serialized, no mutex Three synchronisation patterns Rendezvous: two tasks each signal and then wait — neither passes until both arrive (a 2-task barrier). Barrier: N tasks meet at a point; all released only when the last arrives — xEventGroupSync(). Gatekeeper: one task owns a resource; others send requests via a queue — serialised, no lock needed. Sync with blocking, not suspend/resume · A handshake is symmetric: each side waits for a signal (notification / semaphore / event bits). · vTaskSuspend/Resume is asymmetric and racy: you can suspend a task mid-critical-section or holding a mutex. · Use suspend/resume only for coarse on/off of a whole task, never as a task-to-task handshake. Pick by shape: one-to-one signal → notification; N-way meet → event-group barrier; shared output/resource → gatekeeper task.

The Pitch, Yaw, and Roll tasks each read and set their bit at the barrier (xEventGroupSync); only when all three have arrived are they released together, so the Fusion step sees three fresh, aligned readings. The fused message — and any other task's output — goes to the gatekeeper through the print queue; the gatekeeper is the only task that touches the UART. The panels give the three patterns and why a handshake uses blocking, not suspend/resume.

6. Synchronisation Over Time

Barrier — all tasks wait, then all go together:

Barrier — all tasks wait, then all go together Each task sets its bit and blocks in xEventGroupSync(); the last arrival releases everyone at once Pitch Yaw Roll working set bit blocked proceed working set bit blocked proceed working set bit blocked proceed last task arrives → barrier opens for ALL Rendezvous is this with two tasks: each waits for the other, so neither runs ahead with stale data. Use it when a step needs every task’s fresh result together — e.g. fuse pitch+yaw+roll only once all three are new. Conceptual / idealized

Each task sets its bit and blocks; the last arrival opens the barrier and releases everyone at once. A two-task version of this is a rendezvous.

Gatekeeper — one task owns the resource, requests served in order:

Gatekeeper — one task owns the resource, requests are served in order Several tasks send print requests at once; the gatekeeper dequeues and prints them one at a time, no mutex Task A Task B Task C Print queue Gatekeeper send A1 send B1 send C1 send C2 A1 C1 B1 C2 buffered in arrival order print A1 print C1 print B1 print C2 No interleaving, no mutex: only the gatekeeper touches the UART, so outputs never mix and no lock or priority inheritance is needed. The senders never block on each other — they drop a request and move on; back-pressure is just the queue depth. Conceptual / idealized

Several tasks send print requests concurrently; the gatekeeper dequeues and prints them one at a time — no interleaving, no mutex.

Together the diagrams show the two coordination shapes: meet-then-proceed (barrier/rendezvous) for tasks that must align in time, and own-and-serialise (gatekeeper) for a resource many tasks share.

7. Schematic Circuit & Pin Configuration

  • Sensors → the three analog/digital inputs for pitch/yaw/roll (or simulate in software).
  • UART → the gatekeeper's output channel (the only UART user).
  • Assign the pins in <project>.cydwr → Pins.

8. PSoC Creator Configuration

  • A UART for the gatekeeper; the sensor front ends (or software stubs).
  • FreeRTOSConfig.h (already set): configUSE_EVENT_GROUPS enabled with event_groups.c in the build, configUSE_TASK_NOTIFICATIONS = 1, heap_4, configUSE_TIME_SLICING = 1 (course default).
  • Note the event group has a limited number of usable bits (8 or 24 depending on configUSE_16_BIT_TICKS); three bits is well within range.

9. Provided Template

You are given Lab20_Template_main.c:

  • Given: all includes (note #include "FreeRTOS_PSoC.h"), the event group, the print queue, a gk_print() helper that sends a line to the gatekeeper (never touches the UART), and a complete main() that creates the event group, the queue, the three sensor tasks, and the gatekeeper.
  • You write: the xEventGroupSync barrier in each sensor task, the fuse-and-print on release, and the vGatekeeperTask() receive-and-print loop (the only UART access). Exp02 adds a two-task rendezvous with notifications.
  • Do not change the boot sequence in main().
/* each sensor task, after taking its reading: */
xEventGroupSync(egBarrier, BIT_PITCH, ALL_BITS, portMAX_DELAY);  /* set bit + wait for all */
/* ...returns only when all three bits are set; readings are fresh and aligned... */

/* the gatekeeper is the ONLY UART user: */
if (xQueueReceive(qPrint, &ln, portMAX_DELAY) == pdTRUE) {
    UART_PutString(ln.text); UART_PutString("\r\n");
}

10. Lab Experiments

Exp01 — Basic: the barrier + the gatekeeper. Complete the three sensor tasks with xEventGroupSync, have one of them fuse and gk_print on release, and complete the gatekeeper. Expected: the fusion only ever prints a set of three readings taken in the same round (never a mix of old and new), and the output lines never interleave even under load.

Exp02 — Advanced: a two-task rendezvous. Add two tasks that must hand off in lock-step (e.g. a producer and a consumer that must alternate exactly). Implement the rendezvous with task notifications: each signals the other and waits. Expected: strict alternation with no task running two steps ahead — and an explanation of why it is a two-task barrier.

Exp03 — Challenge: compare the patterns (and reject suspend/resume). Deliberately try to coordinate the sensor tasks with vTaskSuspend/vTaskResume instead of the barrier, and document what breaks or becomes fragile (missed resumes, suspending a task mid-work, no atomic "all arrived"). Then contrast protecting the UART with a mutex (Lab 17) versus the gatekeeper here. Expected: a clear, evidence-based argument for blocking-based sync over suspend/resume, and a statement of when a mutex vs a gatekeeper is the better choice.

11. Testing & Verification

#Pass condition
Exp01 Fusion runs only on three same-round readings; gatekeeper output never interleaves.
Exp02 Two tasks alternate strictly via a notification rendezvous; neither runs ahead.
Exp03 Suspend/resume shown to be fragile for sync; mutex-vs-gatekeeper trade-off stated.

Submit: your barrier + gatekeeper code, your rendezvous, and your Exp03 comparison write-up.

12. Q&A & Troubleshooting

  • Fusion sometimes sees a stale reading → a task did not pass through the barrier, or you read the shared values before xEventGroupSync returned; fuse only after the sync.
  • One task blocks forever at the barrier → another task never reaches xEventGroupSync (it hung or has a different period); all participants must arrive each round.
  • Output lines interleave → some task prints directly instead of via gk_print; the gatekeeper must be the only UART user.
  • Barrier never releases → the uxBitsToWaitFor mask does not match the set of bits actually set; check ALL_BITS.
  • xEventGroupSync unavailable / link error → event_groups.c is not in the project.
  • Rendezvous deadlocks → both tasks wait before either signals; signal first, then wait (or use the sync primitive that does both atomically).

13. Analysis Questions & Lab Report

  1. What does xEventGroupSync do atomically, and why is that the heart of a barrier?
  2. How is a rendezvous a two-task barrier, and when do you need one?
  3. What does a gatekeeper give you that a mutex does not, and when would you still prefer the mutex?
  4. Why is vTaskSuspend/vTaskResume the wrong tool for a task-to-task handshake? Give a concrete failure.

Your report should include your barrier, gatekeeper, and rendezvous code, and answers to the four questions.

© 2026 Air Supply Information Center (Air Supply BBS)