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

ADC MONITORING — SAMPLE ON A GRID, FILTER, THEN DECIDE ADC NTC / pot raw, noisy Sampler Task (periodic) xTaskDelayUntil(&wake, PERIOD) read ADC → filter (MA / IIR) xQueueSend(filtered) Readings Queue filtered values Monitor / Alert Task xQueueReceive(reading) threshold + hysteresis report / set alert state LED / Buzzer + UART log alert out Two filters, one trade-off Moving average (FIR, window N): mean of the last N samples. Strong smoothing; lag ≈ N/2 samples; needs a small ring buffer. IIR / EWMA: y += α·(x − y). One multiply, one state variable; smaller α = smoother but laggier. More smoothing always costs response time. Pick for the signal. Hysteresis kills chatter · One threshold near the signal toggles on every wiggle. · Use two levels: trip at HIGH, release at LOW. · The gap (deadband) must exceed the noise amplitude. · The alert is a latched state, not a per-sample compare. Filter first, then hysteresis — two independent defences against noise. Deterministic rate: seed xLastWakeTime once, then xTaskDelayUntil() — the sample grid never drifts, so the filter math stays valid.

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:

Raw ADC vs. filtered — smoothing trades noise for a little lag Moving average (window 8) and IIR/EWMA (α = 0.18) both tame the noise; the filtered curve lags the signal 0 full sample index (time →, fixed-rate grid) raw ADC (noisy) moving average (N=8) IIR / EWMA (α=0.18) Conceptual / idealized

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:

Threshold with hysteresis — two levels turn a noisy crossing into one clean alert A single threshold toggles on every wiggle; tripping at HIGH and releasing at LOW latches a stable alert state HIGH — trip here LOW — release here deadband (> noise) filtered value ALERT One rise above HIGH sets ALERT; it clears only after the value falls back below LOW — no chatter in between. Conceptual / idealized

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

  1. Why must the sample interval be constant, and how does xTaskDelayUntil guarantee it where vTaskDelay does not?
  2. Compare the moving average and the IIR/EWMA: smoothing, lag, memory, and cost. When would you pick each?
  3. What is hysteresis, and how do you choose the deadband from the signal's noise?
  4. Why filter and use hysteresis, rather than relying on either one alone?
  5. (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.

© 2026 Air Supply Information Center (Air Supply BBS)