PSoC5LP RTOS
Lab 15 — ADC Monitoring & Filtering
FreeRTOS on PSoC 5LP · Lab Series Platform: CY8CKIT-059 (PSoC 5LP, Cortex-M3) · PSoC Creator 4.4 · FreeRTOS Kernel V11 Pipeline: ADC → Sampler task (periodic, filtered) → Queue → Monitor task (threshold + hysteresis) Pairs with: Chapter 8 — Queues & Chapter 2 — Timing & Priority
1. Introduction & Objectives
Reading a raw ADC and comparing it to a threshold is a recipe for noise: the value jitters, the comparison flaps, and an LED or relay chatters. The RTOS fix is a small pipeline — sample on a drift-free grid, filter the noise, then decide with hysteresis. A Sampler task acquires and smooths at a fixed rate; a queue carries the clean values; a Monitor task applies a two-level threshold and acts.
After this lab you can:
- Acquire on a deterministic grid with xTaskDelayUntil so the sample rate never drifts.
- Apply a moving average (FIR) and an IIR/EWMA filter, and explain the smoothing-vs-lag trade-off.
- Pass filtered values between tasks through a queue.
- Implement hysteresis (trip HIGH / release LOW) so a noisy crossing produces one clean alert.
- Split acquisition from decision so neither stalls the other.
- (Extension) Share the latest reading with a mailbox (length-1 queue + xQueueOverwrite/xQueuePeek).
2. Prerequisites
Hardware - CY8CKIT-059 PSoC 5LP Prototyping Kit - RobotSoC Kit: an analog source — the NTC thermistor (MF52D-103F-3950) or a potentiometer; 1× LED (alert); optional buzzer; UART for logging
Software - PSoC Creator 4.4 with the FreeRTOS V11 BaseProject
Chapters (sync progress) - Required: Chapter 8 (Queues) and Chapter 2 (Timing & Priority — the periodic grid). - Peripheral prerequisite: ADC configuration is covered in the separate (non-FreeRTOS) Lab 22 (ADC); the thermistor front end and the Beta-equation conversion in non-FreeRTOS Lab 05 (Thermometer).
3. Reading Materials / References
- Chapter 2 slides — Timing & Priority (vTaskDelay drifts vs xTaskDelayUntil absolute grid).
- Chapter 8 slides — Queues (by-value items, decoupling producer and consumer).
- Lab 05 (non-RTOS) — the NTC Beta-equation helper, if you convert counts to °C.
- FreeRTOS API: xTaskDelayUntil, xTaskGetTickCount, xQueueSend, xQueueReceive.
- (Extension) xQueueOverwrite, xQueuePeek (the length-1 mailbox pattern).
4. Background & Theory of Operation
Sample on a grid. Filters assume a constant sample interval. vTaskDelay measures from when the task runs, so any jitter accumulates and the interval drifts — and drift quietly invalidates the filter's time constant. Seed xLastWakeTime once with xTaskGetTickCount(), then call xTaskDelayUntil(&xLastWakeTime, period): each wake is placed on the absolute grid, so the average rate is exact even if one cycle runs late.
Filter the noise — two ways. - Moving average (FIR, window N): output = mean of the last N samples. Strong, linear-phase smoothing; the cost is lag ≈ N/2 samples and a small ring buffer. Doubling N halves the noise power but doubles the lag. - IIR / EWMA: y += α·(x − y). One multiply and a single state variable — cheap and stateless of history. A smaller α smooths harder but lags more. In fixed point, y += (α_q8 · (x − y)) >> 8.
Both say the same thing: more smoothing always costs response time. Choose the filter and its strength from how noisy the signal is and how fast you must react.
Decide with hysteresis. Comparing the filtered value to a single threshold still flaps when the signal hovers near it. Hysteresis uses two levels: the alert trips when the value rises past THRESH_HIGH and releases only when it falls back below THRESH_LOW. The gap between them — the deadband — must be wider than the residual noise. The alert is a latched state, updated on the crossings, not a fresh per-sample comparison. Filtering and hysteresis are two independent defences: the filter removes most of the noise, hysteresis absorbs what is left.
Split the stages. The Sampler runs at a strict rate and must never be blocked by slow reporting; the Monitor may log over UART, drive a relay, or print, at its own priority. A queue between them decouples the rates — the Sampler drops a value in and moves on; the Monitor consumes when it can.
Extension — the mailbox pattern: share the latest value. A queue keeps every item until it is read — right when each reading must be acted on once. But often several consumers (a display, a logger, an alarm) each just want the most recent value, and an old reading is worthless. That is a mailbox: a length-1 queue written with xQueueOverwrite (which always succeeds, replacing whatever was there) and read with xQueuePeek (which returns the value without removing it, so many readers each see it). The Sampler overwrites the mailbox each sample; any consumer peeks the latest at its own pace, never blocking the Sampler and never seeing a stale backlog. Use a queue when every item must be processed, a mailbox when only the newest value matters.
5. RTOS Task Architecture
The Sampler task wakes on the xTaskDelayUntil grid, reads the ADC, filters (moving average or IIR), and sends the clean value through the readings queue. The Monitor/Alert task receives each value, applies the two-level hysteresis threshold, and drives the LED/buzzer and UART log. The panels summarise the filter trade-off and why hysteresis needs a deadband wider than the noise.
6. Signal & Decision Over Time
Raw vs. filtered — smoothing trades noise for a little lag:
The moving average and the IIR both tame the noise; the filtered curve trails the signal by the filter's lag.
Threshold with hysteresis — two levels turn a noisy crossing into one clean alert:
The alert trips when the value rises past HIGH and clears only after it falls below LOW — no chatter in the deadband.
Together: filter first, then apply hysteresis. The filter keeps the value from wandering; the deadband keeps the decision from flapping. Either alone is weaker than the two in series.
7. Schematic Circuit & Pin Configuration
- Analog in → the NTC divider output or a potentiometer wiper → the ADC input pin.
- LED (alert) → GPIO + 220 Ω → GND; optional buzzer on a second GPIO.
- UART → optional logging of the filtered value and alert state.
- Assign the pins in <project>.cydwr → Pins.
8. PSoC Creator Configuration
- An ADC (SAR or DelSig) on the analog input; a Digital Output Pin for the alert LED; optional UART.
- FreeRTOSConfig.h (already set): configUSE_TIME_SLICING = 1 (course default), heap_4, tick rate adequate for the sample period.
- Match the ADC resolution/range to the thresholds (the template uses raw counts; convert to °C with the Lab 05 helper if you prefer engineering units).
9. Provided Template
You are given Lab15_Template_main.c:
- Given: all includes (note #include "FreeRTOS_PSoC.h"), the readings queue, the handles, an IIR helper and a moving-average helper, the periodic xTaskDelayUntil skeleton, and a complete main() that starts the ADC and both tasks.
- You write: vSamplerTask() (read ADC → filter → enqueue) and vMonitorTask() (hysteresis → alert).
- Do not change the boot sequence in main().
static int32_t iir_step(int32_t x, int32_t *y) /* y += alpha*(x - y), Q8 */
{
*y += (IIR_ALPHA_Q8 * (x - *y)) >> 8;
return *y;
}
/* hysteresis in the monitor */
if (alert == 0u && v >= THRESH_HIGH) { alert = 1u; LED_Write(1); }
else if (alert == 1u && v <= THRESH_LOW) { alert = 0u; LED_Write(0); }
10. Lab Experiments
Exp01 — Basic: sample, filter, alert
Fill in the Sampler (periodic read → iir_step → enqueue) and the Monitor (hysteresis → LED). Warm the thermistor (or turn the pot) past the HIGH level and back. Expected: the LED trips once above HIGH and releases once below LOW, with no flicker near the thresholds.
Exp02 — Advanced: compare the two filters
Swap the IIR for the moving average (ma_step) and compare. Log the raw and filtered values over UART and look at the noise and the lag of each. Vary α and MA_WINDOW. Expected: a clear description of the smoothing-vs-lag trade-off, with your numbers, and a reasoned choice for this signal.
Exp03 — Challenge: tune the deadband, and prove no chatter
Shrink the deadband (THRESH_HIGH − THRESH_LOW) until the alert begins to chatter near the threshold, then widen it just past the residual noise. Optionally hold the signal right at the edge and count transitions with and without hysteresis. Expected: a deadband chosen from the measured noise, and evidence that hysteresis removes the chatter a single threshold shows.
Exp04 — Extension: publish the latest reading as a mailbox
Add a length-1 mailbox alongside (or instead of) the queue: the Sampler xQueueOverwrites the filtered value each sample, and two consumers — the Monitor and a second "display" task — each xQueuePeek the latest value at their own rate. Show that a slow consumer never sees a backlog and never blocks the Sampler, and contrast this with the queue (where every reading is kept). Expected: a working latest-value mailbox and a clear rule for queue (process every item) vs mailbox (only the newest matters).
11. Testing & Verification
| # | Pass condition |
|---|---|
| Exp01 | Fixed-rate sampling; filtered value drives a hysteresis alert; no flicker at the thresholds. |
| Exp02 | Moving average and IIR compared with logged data; smoothing-vs-lag trade-off explained. |
| Exp03 | Deadband tuned to the noise; hysteresis shown to remove the chatter a single threshold produces. |
| Exp04 | A length-1 mailbox shares the latest reading; peekers never block the Sampler or see a backlog. |
Submit: your Sampler and Monitor code, your Exp02 filter comparison with data, your Exp03 deadband choice with the noise measurement, and (extension) your mailbox publish/peek path.
12. Q&A & Troubleshooting
- Alert chatters near the threshold → deadband too small or no filtering; widen THRESH_HIGH − THRESH_LOW and filter first.
- Filtered value lags too much → window too large or α too small; trade some smoothing for response.
- Sample rate looks irregular → you used vTaskDelay; seed xLastWakeTime once and use xTaskDelayUntil.
- Queue overflows / xQueueSend fails → the Monitor is slower than the sample rate; raise READ_Q_LEN or lighten the Monitor.
- Fixed-point filter stuck / overflows → check the >> 8 scaling and the signed types; int32_t throughout.
- Values pinned at 0 or full-scale → ADC not started, wrong input pin, or the source outside the ADC range.
13. Analysis Questions & Lab Report
- Why must the sample interval be constant, and how does xTaskDelayUntil guarantee it where vTaskDelay does not?
- Compare the moving average and the IIR/EWMA: smoothing, lag, memory, and cost. When would you pick each?
- What is hysteresis, and how do you choose the deadband from the signal's noise?
- Why filter and use hysteresis, rather than relying on either one alone?
- (Extension) When is a length-1 mailbox the right tool instead of a queue, and how do xQueueOverwrite/xQueuePeek make it work?
Your report should include your two tasks, your filter comparison data, your extension path, and answers to the questions.