PSoC5LP RTOS
Lab 12 — Interrupts & Deferred Processing
FreeRTOS on PSoC 5LP · Lab Series Platform: CY8CKIT-059 (PSoC 5LP, Cortex-M3) · PSoC Creator 4.4 · FreeRTOS Kernel V11 Source: PSoC Hardware Timer interrupt → notification → handler task Pairs with: Chapter 6 — Binary Semaphores (first ISR) & Chapter 15 — Interrupts on PSoC 5LP
1. Introduction & Objectives
Earlier labs signaled a task from an ISR. This lab studies the interrupt side properly: the deferred-interrupt pattern (tiny ISR, work in a task), the two priority systems that run in opposite directions, the 2–7 syscall safety line, and how to measure interrupt-entry and task-wake latency.
After this lab you can:
- Write a correct deferred ISR: clear the source, notify, portYIELD_FROM_ISR.
- Distinguish NVIC interrupt priority (0–7, smaller = higher) from task priority (0–4, larger = higher).
- Honor the safety line: only NVIC 2–7 ISRs may call the FreeRTOS API; 0–1 may not.
- Measure interrupt-entry latency and task-wake latency with timing pins.
- Explain how a critical section masks 2–7 but never 0–1.
- (Extension) Defer short ISR work to the timer daemon with xTimerPendFunctionCallFromISR, instead of a dedicated task.
2. Prerequisites
Hardware - CY8CKIT-059 PSoC 5LP Prototyping Kit - RobotSoC Kit: 1× LED; a logic analyzer (or scope) on two timing pins
Software - PSoC Creator 4.4 with the FreeRTOS V11 BaseProject
Chapters (sync progress) - Required: Chapter 6 (the first ISR + *_FromISR rules) and Chapter 15 (NVIC priorities, the <<5 shift, the safety line). - Peripheral prerequisite: basic GPIO / interrupt is covered in the separate (non-FreeRTOS) Lab 02.
3. Reading Materials / References
- Chapter 15 slides — Interrupts on PSoC 5LP (two priority systems, the (2<<5) safety line, nesting & masking).
- Chapter 6 slides — Binary Semaphores (the deferred-interrupt pattern, portYIELD_FROM_ISR).
- FreeRTOS API: vTaskNotifyGiveFromISR, ulTaskNotifyTake, portYIELD_FROM_ISR.
- (Extension) xTimerPendFunctionCallFromISR, xTimerPendFunctionCall (defer to the timer daemon; see Lab 11).
4. Background & Theory of Operation
Deferred interrupt processing. The single most important ISR habit: do almost nothing in the interrupt and hand the real work to a task. The ISR clears the source, signals a task (a notification here), and yields. The task — which can block, use the full API, and run at a chosen priority — does the parsing, filtering, logging, or display. The payoff: a short ISR means low latency for every interrupt, and the heavy work is scheduled, not forced to run at interrupt level.
Two priority systems, opposite directions. Interrupts are where RTOS events come from, but "interrupt priority" and "task priority" are two separate systems: - NVIC interrupt priority — 0–7 on this chip, and smaller means more urgent (handled first). - FreeRTOS task priority — 0–4 here, and larger means more urgent — the opposite direction.
The syscall safety line. Only interrupts at or below a certain urgency may call FreeRTOS at all. In this config configMAX_SYSCALL_INTERRUPT_PRIORITY = (2 << 5). So an ISR that calls any *_FromISR function must be NVIC priority 2–7 (numerically ≥ 2). A priority 0 or 1 ISR is above the line — it has the lowest latency, but it must not touch the kernel, or it corrupts it / trips configASSERT.
The << 5 shift. PSoC 5LP's Cortex-M3 implements only the top 3 bits of each 8-bit NVIC priority field (8 levels, 0–7). FreeRTOS wants the value in those top bits, so a level is written shifted left by (8 − 3) = 5: (2 << 5). Writing the bare 2 lands in the wrong bits and the masking breaks silently.
Nesting and the masking boundary. A critical section (taskENTER_CRITICAL) raises the mask only up to the syscall line — it holds off NVIC 2–7 ISRs, but can never block 0–1. So a priority-1 ISR still fires inside a critical section (true hard real-time, but no kernel calls); a priority-3 ISR is deferred until the section exits.
Measuring latency. Pulse a pin in the ISR (interrupt entry) and another in the task (task wake). On an analyzer: - entry latency = timer edge → ISR pin rising, - wake latency = ISR pin → task pin rising (the notify + context switch).
Extension — deferring without a dedicated task. The pattern above spends a whole task (its TCB and stack) on the deferred work. When the work is occasional and short, there is a lighter option: xTimerPendFunctionCallFromISR(fn, arg1, arg2, &woken) asks the timer/daemon task (the one from Lab 11) to run your function fn(arg1, arg2) in task context soon after the ISR returns. The ISR still does the minimum and yields; the daemon runs your callback at the daemon's priority, where it may use the full API — no handler task of your own to create. Use it for infrequent deferred jobs (log this event, kick a state change); keep a dedicated handler task when the work is frequent, long, or needs its own priority and stack. (It needs configUSE_TIMERS = 1 and a configTIMER_QUEUE_LENGTH with room; the non-ISR form xTimerPendFunctionCall exists for task context.)
5. RTOS Task Architecture
The HW Timer fires a periodic interrupt. The Timer ISR (NVIC 2–7) clears the source, notifies the task, and yields — nothing slow. The Handler Task blocks on the notification and does the real work. The reference panels show the two opposite priority systems, the safety line, and why deferring pays off.
6. Latency & the Safety Line
The deferred-interrupt latency breakdown (what you measure):
Edge → entry latency → tiny ISR → wake latency → the task does the deferred work.
The syscall safety line — a critical section masks 2–7, not 0–1:
A priority-1 ISR fires even inside a critical section (and must not call the kernel); a priority-3 ISR is held off until the section exits.
The lesson: NVIC 0–1 buys the lowest latency at the cost of no kernel access; NVIC 2–7 is kernel-safe but subject to critical-section masking. Put RTOS-aware interrupts at 2–7.
7. Schematic Circuit & Pin Configuration
- Timer → a Timer/Counter component with an Interrupt on terminal-count (isr_Timer).
- TP_ISR / TP_TASK → two timing pins to the logic analyzer.
- LED → GPIO (optional activity indicator).
- Assign the pins in <project>.cydwr → Pins.
8. PSoC Creator Configuration
- A Timer/Counter + Interrupt component; two Digital Output Pins for the timing pins.
- Set the timer interrupt's NVIC priority to 3 (within 2–7), or isr_Timer_SetPriority(3) at startup (the template does this).
- FreeRTOSConfig.h (already set): configPRIO_BITS = 3, configMAX_SYSCALL_INTERRUPT_PRIORITY = (2<<5), configKERNEL_INTERRUPT_PRIORITY = (7<<5), heap_4.
9. Provided Template
You are given Lab12_Template_main.c:
- Given: all includes (note #include "FreeRTOS_PSoC.h"), the handler handle, and a complete main() that starts the HW timer, sets the ISR into the safe 2–7 range, starts it, and creates the handler task. Placeholders keep the project building and running. An ISR_NVIC_PRIORITY macro lets you change the priority for Exp03.
- You write: Timer_ISR() (pulse TP_ISR, clear, notify, yield) and vHandlerTask() (block, pulse TP_TASK, do the work).
- Do not change the boot sequence in main().
CY_ISR(Timer_ISR)
{
BaseType_t woken = pdFALSE;
TP_ISR_Write(1); /* mark interrupt entry */
Timer_ClearInterrupt(Timer_INTR_MASK_TC);
vTaskNotifyGiveFromISR(hHandler, &woken);
TP_ISR_Write(0);
portYIELD_FROM_ISR(woken); /* run the task now */
}
10. Lab Experiments
Exp01 — Basic: deferred processing
Write the ISR and handler. The timer interrupt notifies the task, which does the work. Expected: the ISR is tiny (TP_ISR a short pulse); the task does the processing (TP_TASK), one response per interrupt.
Exp02 — Advanced: measure the latency
With TP_ISR and TP_TASK on an analyzer, measure entry latency (timer edge → TP_ISR) and wake latency (TP_ISR → TP_TASK). Repeat at a couple of NVIC priorities within 2–7. Expected: a small table of entry/wake latency, and the observation that the ISR itself is far shorter than the deferred work.
Exp03 — Challenge: the safety line
Set ISR_NVIC_PRIORITY = 1 and show that calling vTaskNotifyGiveFromISR from it faults / trips configASSERT — priority 0–1 is above the syscall line. Restore to 2–7. (Optional: a second priority-1 ISR that only toggles a GPIO fires even inside a taskENTER_CRITICAL window, while a priority-3 ISR is deferred — the masking boundary.) Expected: you can explain why 0–1 must avoid the kernel and what the critical section masks.
Exp04 — Extension: defer without a handler task
Replace the notify-a-task path with xTimerPendFunctionCallFromISR: the ISR asks the timer daemon to run a small callback in task context instead of waking your own handler task. Compare the two — RAM (no extra TCB/stack), latency (the daemon's priority and queue), and when each is the right choice. Expected: a working daemon-deferred path and a clear rule for "pend-function-call for occasional short work, dedicated task for frequent/long work." (Needs configUSE_TIMERS = 1 and adequate configTIMER_QUEUE_LENGTH.)
11. Testing & Verification
| # | Pass condition |
|---|---|
| Exp01 | Timer IRQ → tiny ISR → notify → handler does the work; one response per interrupt. |
| Exp02 | Entry and wake latency measured on the analyzer; ISR ≪ deferred work. |
| Exp03 | A priority-1 ISR calling the kernel faults/asserts; 2–7 works; masking explained. |
| Exp04 | A xTimerPendFunctionCallFromISR path defers work to the daemon; trade-off vs a task stated. |
Submit: your ISR and handler, the Exp02 latency measurements, your Exp03 safety-line explanation, and (extension) your pend-function-call path.
12. Q&A & Troubleshooting
- HardFault / assert on the notify → the ISR is at NVIC priority 0 or 1; set it to 2–7.
- Interrupt fires forever → the source was not cleared first.
- Task reacts only at the next tick → portYIELD_FROM_ISR(woken) missing.
- Masking "doesn't work" → you wrote the bare 2 instead of (2<<5); the value lands in the wrong bits.
- Latency looks huge → you did real work in the ISR; move it to the task.
- Nothing happens → the timer or its interrupt component is not started (isr_Timer_StartEx).
13. Analysis Questions & Lab Report
- Why is the deferred-interrupt pattern (tiny ISR + handler task) better than doing the work in the ISR?
- The two priority systems run in opposite directions — state each direction and the safety line between them.
- Why must an RTOS-aware ISR be NVIC priority 2–7, and what happens at 0–1?
- What does a critical section mask, and what does it not? Why is (2<<5) written shifted?
- (Extension) When is xTimerPendFunctionCallFromISR a better fit than a dedicated handler task, and what does it cost?
Your report should include your ISR and handler, the latency measurements, your extension path, and answers to the questions.