PSoC5LP RTOS
Lab 19 — Low-Power & Tickless Idle
FreeRTOS on PSoC 5LP · Lab Series Platform: CY8CKIT-059 (PSoC 5LP, Cortex-M3) · PSoC Creator 4.4 · FreeRTOS Kernel V11 Theme: a long-period task (e.g. measure every 30 min) that lets the MCU sleep almost the whole interval Pairs with: Chapter 2 — Timing & Priority & Chapter 18 — Reliability & Power
1. Introduction & Objectives
A task that measures every 30 minutes should use xTaskDelayUntil, not vTaskSuspend — the kernel wakes a delayed task by itself, while a suspended task needs someone else to resume it. But correctness is only half the story. A task blocked in vTaskDelay is idle, yet the default 1 ms SysTick keeps firing — about 1.8 million interrupts across a 30-minute wait — so the CPU never sleeps deeply and a battery drains for nothing. This lab makes the system sleep between samples: keep the cadence on an absolute grid, then let tickless idle suppress the tick and (optionally) drop the PSoC into a real Sleep mode until the next deadline.
After this lab you can:
- Explain why xTaskDelayUntil (not vTaskSuspend) is correct for a periodic task.
- Explain why a blocked task still wastes power, and what tickless idle does about it.
- Enable configUSE_TICKLESS_IDLE and measure the drop in average current.
- (Advanced) Implement a deep-sleep pre/post-sleep hook that uses a low-power timer to wake, and correct the kernel tick on return.
- Reason about energy as duty cycle, and estimate battery life from per-mode currents.
- (Extension) Use the idle hook (vApplicationIdleHook) and tick hook (vApplicationTickHook), and know their constraints.
- (Extension) Keep wall-clock time with the PSoC RTC: timestamp each sample and schedule to a real time-of-day, independent of the tick.
2. Prerequisites
Hardware - CY8CKIT-059 PSoC 5LP Prototyping Kit - RobotSoC Kit: a sensor to sample (NTC / analog); for Exp02/03, a current meter or power profiler in the board's supply (a µA-capable meter is ideal)
Software - PSoC Creator 4.4 with the FreeRTOS V11 BaseProject
Chapters (sync progress) - Required: Chapter 2 (Timing & Priority — the absolute grid) and Chapter 18 (reliability & power). - Related: Lab 03/15 (xTaskDelayUntil), Lab 05 (task states, suspend/resume), Lab 18 (the WDT and reset reason).
3. Reading Materials / References
- Chapter 2 slides — Timing & Priority (vTaskDelay drift vs xTaskDelayUntil).
- Chapter 18 slides — Reliability & Power (PSoC power modes, wake sources).
- FreeRTOS docs — Low Power / Tickless Idle (configUSE_TICKLESS_IDLE, vPortSuppressTicksAndSleep, configPRE_SLEEP_PROCESSING, vTaskStepTick).
- PSoC API: CyPmSaveClocks, CyPmSleep, CyPmRestoreClocks; the ILO-driven CTW and the RTC as wake sources.
- (Extension) vApplicationIdleHook (configUSE_IDLE_HOOK), vApplicationTickHook (configUSE_TICK_HOOK).
- (Extension) PSoC RTC component: RTC_Start, RTC_WriteTime/RTC_ReadTime, the RTC_TIME_DATE struct, and the RTC alarm as a wake source.
4. Background & Theory of Operation
xTaskDelayUntil, not vTaskSuspend. The two put a task to "rest" very differently. vTaskDelay/xTaskDelayUntil Block the task with a timeout — the kernel wakes it automatically when the time expires, so it is self-timed and needs no one else. vTaskSuspend puts the task in the Suspended state with no timeout; it stays there until another task or ISR calls vTaskResume. For a fixed cadence that means inventing a separate timer just to resume it — more code, and a single point of failure. vTaskSuspend is for event-driven, indefinite pauses ("stop logging until the user sends START"), not for a clock-driven period. (And it is blunt: suspending a task that holds a mutex can stall others — prefer a blocking primitive for synchronisation.)
Why a blocked task still costs power. When the Measure task is Blocked, the scheduler runs the Idle task. But the FreeRTOS tick — a periodic SysTick interrupt, 1 ms by default — keeps firing to service delays and time slicing. Every tick wakes the core from its WFI, so the "idle" current sits at a raised floor. Over a 30-minute delay that is ~1.8 million needless wake-ups. The work you do (one short ADC burst) is tiny; the energy is dominated by this idle floor.
Tickless idle — Layer 2. configUSE_TICKLESS_IDLE = 1 turns on the port's vPortSuppressTicksAndSleep: when the Idle task sees no ready task and the next wake is far enough away, it suppresses the periodic tick, programs a single wake for the next deadline, and sleeps. On wake it corrects the kernel's tick count for the time that passed, so delays and timers stay accurate. The stock ARM_CM3 version uses WFI (a light sleep that still runs on the core clock), which already removes the per-millisecond wake-ups — a large, almost-free win.
Deep sleep — Layer 3 (advanced). To go below WFI into the PSoC's Sleep mode (core clock stopped, RAM retained), SysTick can no longer be the timer, because its clock stops. You provide the sleep yourself through the hooks configPRE_SLEEP_PROCESSING / configPOST_SLEEP_PROCESSING (or by overriding vPortSuppressTicksAndSleep): arm a low-power wake from the ILO-driven CTW or the RTC for the idle window, call CyPmSaveClocks() and CyPmSleep(...), and on wake CyPmRestoreClocks() and vTaskStepTick(ticksSlept) to advance the kernel clock by however long you actually slept. Pick CTW/RTC for the cadence; keep the WDT (Lab 18) for faults, not for timing.
Energy is duty cycle. Energy over an interval is Σ(mode current × time in that mode). With a 30-minute period and a few-millisecond burst, the chip is active for well under 0.01 % of the time — so battery life is set almost entirely by the sleep-mode current, not the active current. The design lever is the duty cycle: sleep as deep as you can, for as long as you can, and make the active burst short (start a peripheral, use it, Stop it). A guard matters too: if the next deadline is very close, skip sleeping — the save/restore overhead would cost more than it saves (configEXPECTED_IDLE_TIME_BEFORE_SLEEP).
Extension — wall-clock time with the RTC. The kernel tick count answers "how long since boot?", not "what time is it?". It is relative, it resets on every reset, and it is exactly what tickless idle stops advancing during sleep. When a sample must carry a real timestamp ("23.4 °C at 14:30:00") or the cadence must land on a wall-clock boundary ("sample on every minute, on the minute"), use the PSoC RTC component instead: a separate counter on a 1 Hz clock that keeps hours/minutes/seconds (and date) in an RTC_TIME_DATE struct, read with RTC_ReadTime(). It runs independently of the SysTick, so it keeps correct time straight through tickless deep sleep — the very window where the kernel clock is frozen. Two uses pair naturally with this lab: timestamping (read the RTC at each wake and attach the time to the logged sample), and scheduling to the clock (wake slightly before the target wall-time via xTaskDelayUntil, then read the RTC and, for exactness, use the RTC alarm as the low-power wake source so the chip sleeps until, say, exactly 00 seconds). Keep the roles clear: the RTC is the calendar/wall-clock, the CTW/RTC-alarm is the wake source, xTaskDelayUntil is the tick-based cadence, and the WDT (Lab 18) is for faults — four timers, four jobs.
Extension — the idle hook and the tick hook. FreeRTOS gives two standing callbacks you can use here. The idle hook vApplicationIdleHook() (configUSE_IDLE_HOOK = 1) runs whenever the Idle task runs — a simpler, coarser alternative to full tickless idle for entering a low-power wait: put a WFI (or a light sleep) in it so the core naps between ticks without the custom vPortSuppressTicksAndSleep machinery. It must never block and must return quickly, because the idle task must stay runnable. The tick hook vApplicationTickHook() (configUSE_TICK_HOOK = 1) runs inside the tick interrupt every tick — use it only for tiny, ISR-safe periodic housekeeping (bump a counter, sample a soft watchdog); it must be very short and may call only …FromISR APIs. Between the two: the idle hook is for "what to do when there's nothing to do" (sleep), the tick hook for "something tiny on every tick". Tickless idle (Layers 2–3) supersedes the idle hook for serious power saving, but the idle hook is the quickest first step.
5. RTOS Task Architecture
The Measure task is Blocked on xTaskDelayUntil almost the whole interval. With no ready task, the Idle task hands control to the tickless idle hook, which suppresses the tick, arms a CTW/RTC wake, and drops the chip into PSoC Sleep; on wake it restores clocks and corrects the tick, and the task runs one short active burst before blocking again. The panels give the three layers of power saving and the duty-cycle view of energy.
6. Power Over Time
Current over one interval — the tick is what costs you:
The same short active burst on both; without tickless, the idle current stays high because the CPU wakes ~1000×/s for the tick; with tickless deep sleep it falls near zero until the next deadline.
How tickless idle works — suppress, sleep, correct on wake:
The chip is actually asleep between CyPmSleep() and the wake; on return vTaskStepTick advances the kernel clock by the time slept so delays and timers stay accurate.
The lesson both diagrams carry: you are not saving the work, you are saving the wait. Make the active burst short, then let the long idle window be true sleep rather than a million tiny tick wake-ups.
7. Schematic Circuit & Pin Configuration
- Sensor → the analog input to the ADC (powered down between bursts).
- Supply → break the board's supply with a current meter / profiler for Exp02/03.
- Keep unused pins and peripherals off; a floating input can leak current.
- Assign the pins in <project>.cydwr → Pins.
8. PSoC Creator Configuration
- The sensor front end; start peripherals only around the burst (ADC_Start … ADC_Stop).
- FreeRTOSConfig.h: configUSE_TICKLESS_IDLE = 1, configUSE_16_BIT_TICKS = 0 (32-bit tick, so 30 min fits), configEXPECTED_IDLE_TIME_BEFORE_SLEEP ≥ 3, heap_4, configUSE_TIME_SLICING = 1 (course default).
- For Layer 3, define configPRE_SLEEP_PROCESSING / configPOST_SLEEP_PROCESSING to the hooks, and configure a CTW/RTC wake source.
9. Provided Template
You are given Lab19_Template_main.c:
- Given: all includes (note #include "FreeRTOS_PSoC.h"), a complete main() that creates the Measure task, the periodic xTaskDelayUntil skeleton (Layer 1), and pre/post-sleep hook stubs for the optional Layer 3.
- You write: the short burst in vMeasureTask(); then enable tickless (Exp02); then, optionally, the deep-sleep hooks (Exp03).
- Do not change the boot sequence in main().
static void vMeasureTask(void *arg)
{
(void)arg;
TickType_t wake = xTaskGetTickCount();
for (;;)
{
xTaskDelayUntil(&wake, pdMS_TO_TICKS(30u*60u*1000u)); /* 30 min grid */
ADC_Start(); ADC_StartConvert();
ADC_IsEndConversion(ADC_WAIT_FOR_RESULT);
int16 t = ADC_GetResult16();
ADC_Stop(); /* power the analog block back down */
/* log / transmit t, then block again */
}
}
10. Lab Experiments
Exp01 — Basic: short burst, long block
Write the Measure task so the active work is as short as possible and the task spends the rest of the interval Blocked on xTaskDelayUntil (shorten the period to seconds for testing). Expected: a correct, drift-free cadence with the CPU idle between samples — the groundwork for sleeping.
Exp02 — Advanced: enable tickless idle and measure
Set configUSE_TICKLESS_IDLE = 1. With a current meter in the supply, compare the average current before and after, for an idle-heavy schedule. Expected: a clear drop in idle current once the per-millisecond tick wake-ups are suppressed, with no change to the measured values or cadence.
Exp03 — Challenge: deep sleep and a battery-life estimate (optional, needs an ammeter)
Fill the pre/post-sleep hooks to put the PSoC into Sleep with a CTW/RTC wake, and vTaskStepTick the kernel on return. Measure the active current and the sleep current. Then, for the 30-minute cadence, compute the average current and estimate battery life (e.g. from a coin-cell's mAh). Compare three cases: vTaskDelay with no tickless, tickless WFI, and deep sleep. Expected: a measured sleep current far below the tickless-WFI floor, and a battery-life estimate dominated by the sleep mode — the duty-cycle lesson, quantified.
Exp04 — Extension: the idle hook as a first-step low-power entry
Enable configUSE_IDLE_HOOK = 1 and put a WFI (or a light sleep) in vApplicationIdleHook(); measure the average current against the plain no-tickless build. Then enable configUSE_TICK_HOOK = 1 and use vApplicationTickHook() for a tiny periodic counter, proving it must stay ISR-short. Expected: a measurable drop from the idle-hook WFI alone, a statement of why it must never block, and how it compares to full tickless idle. (The idle hook is the quick first step; tickless idle, Exp02/03, goes much further.)
Exp05 — Extension: wall-clock timestamps and on-the-minute sampling
Add the RTC component; set the time once with RTC_WriteTime. In the Measure task, read RTC_ReadTime() at each wake and attach HH:MM:SS to the logged sample. Then change the cadence from "every N ms" to "on every minute, on the minute": wake a little early with xTaskDelayUntil, and (if doing Exp03 deep sleep) arm the RTC alarm as the wake source so the chip sleeps until exactly 00 seconds. Verify over a tickless-sleep interval that the timestamps stay correct — proving the RTC kept time while the kernel tick was suppressed. Expected: samples carry real wall-clock times, the cadence lands on the minute boundary, and the RTC time is accurate across deep sleep where the tick count is not.
11. Testing & Verification
| # | Pass condition |
|---|---|
| Exp01 | Drift-free cadence; the Measure task is Blocked between short bursts. |
| Exp02 | configUSE_TICKLESS_IDLE = 1 lowers the measured average current; cadence and readings unchanged. |
| Exp03 | Deep-sleep hooks work with a CTW/RTC wake; tick corrected; battery life estimated from measured mode currents. |
| Exp04 | An idle-hook WFI lowers idle current; the tick hook runs tiny periodic work; both constraints explained. |
| Exp05 | Samples carry RTC wall-clock timestamps; the cadence lands on a clock boundary; RTC time stays correct across tickless sleep. |
Submit: your Measure task, your Exp02 before/after current measurement, (if done) your Exp03 deep-sleep hooks with the battery-life calculation, and (extension) your idle/tick-hook results and your RTC timestamp / on-the-minute scheduling.
12. Q&A & Troubleshooting
- Why not vTaskSuspend for the 30-min period? It has no timeout — something else must resume it; xTaskDelayUntil self-times. Use suspend only for indefinite, event-driven pauses.
- Idle current still high with tickless on → the port only reaches WFI (light sleep); for a deeper drop implement the Layer-3 deep-sleep hooks.
- Delays/timers run slow after sleeping → the tick was not corrected on wake; call vTaskStepTick(ticksSlept) in the post-sleep hook.
- Chip never sleeps → a task is busy-looping or polling, so the Idle task never runs; or configEXPECTED_IDLE_TIME_BEFORE_SLEEP is too high for the idle window.
- Wakes too early / at the wrong time → the CTW/RTC interval was mis-programmed, or another enabled interrupt is waking the chip; gate unused wake sources.
- 30-min delay seems to overflow → configUSE_16_BIT_TICKS must be 0; a 16-bit tick caps at ~65 s.
- Current spikes between samples → a peripheral left running; Stop the ADC/analog blocks after each burst.
13. Analysis Questions & Lab Report
- Why is xTaskDelayUntil the right tool for a 30-minute cadence, and when is vTaskSuspend appropriate instead?
- Why does a task blocked in vTaskDelay still consume power, and what does tickless idle change?
- What must the post-sleep hook do to keep delays and timers accurate, and why?
- Why is energy governed by the duty cycle rather than the active current? Show it with your Exp03 numbers.
- (Extension) What may the idle hook and the tick hook each do, what may they not, and how does the idle hook compare to tickless idle?
- (Extension) Why keep wall-clock time in the RTC rather than deriving it from the tick count, and why does the RTC stay correct through tickless deep sleep when the tick does not?
Your report should include your Measure task, your current measurements, your deep-sleep hooks (if done), your extension results, and answers to the questions.