GPIO and Peripherals through a Driver API
Course 1 · Module 3 Suggested time: about 3 hours (reading + lab on the board) Format: a hands-on lesson — calls the Driver API of the TESA Firmware SDK on the project you can build/flash from M02
Lab · Cheatsheet · ← Table of Contents · ← M02 · M04 →
Note: which firmware the code in this lesson is written for (checked on 2026-09-26)
The C code in this lesson calls the API of the TESAIoT Bitstream firmware, called “TESA Firmware SDK” in the original, which is published as a ready-made HEX file (
tesaiot-bitstream-<version>.hex) alongside Bitstream Studio in the TESAIoT_Hackathon lab pack. The source code of this firmware is not yet public. Functions such asled_controller_*,cm55_button_*,cm55_uart_send,sensor_sht40_*,cm55_i2c_manager_i2c_lock,bitstream_led_pwm_*andcm55_adc_*therefore have no header you can open or build yourself. Read the snippets as concepts and a calling order. The calls to FreeRTOS and the Infineon PDL (such asxTaskCreate,vTaskDelay,Cy_GPIO_*) are ordinary public APIs.If you want code you can read and build from open source, see tesaiot-pse84-devkit-sdk (Apache-2.0), which is a different codebase with different API names. Examples already checked to do the same job as this lesson (commit
ef72c1b):
proj_cm33_ns/examples/io/04_gpio_led_button.c— an LED and a button with the PDL’sCy_GPIO_*, on the pinsCYBSP_USER_LED1/CYBSP_USER_BTN1(matches section 2.2)proj_cm33_ns/examples/io/03_read_potentiometers.c— reading a potentiometer through the SAR ADC (potentiometer_read_raw/_percent/_voltage)proj_cm55/examples/io/02_pots_and_capsense.c— reading knobs VR1–VR4 on the CM55 (cm55_pot_read_all)proj_cm33_ns/examples/sensors/04_read_environment.c— reading the SHT40 / DPS368 over I²C, locking the bus withsensor_i2c_lock/sensor_i2c_unlockproj_cm33_ns/examples/sensors/01_i2c_bus_scan.c— scanning the I²C bus under the same mutexNo equivalent found yet in the public SDK: dimming LEDs via PWM (
bitstream_led_pwm_*) andcm55_uart_send
Objectives (Learning Outcomes)
Section titled “Objectives (Learning Outcomes)”By the end of this lesson you should be able to:
- Explain GPIO fundamentals and try it on the board with the course’s API (
led_controller_*,cm55_button_*, orCy_GPIO_*) - Explain the role of the main peripherals: Timer, UART, I²C, SPI, PWM, ADC
- Use the TESA Firmware SDK’s Driver API to control peripherals systematically
- Do exercises block by block, then combine them into a small circuit, noting the real function names called
This module takes you from “a project that runs” in M02 to talking to hardware through the Driver layer, per the map in M01
About the snippets in this lesson The C examples below are drawn from the TESA Firmware SDK (a wrapper on the CM55 + FreeRTOS) In the lab, call these function names per the project you have locked your version to — see more examples on the TESAIoT Developer Hub (Domain: GPIO / Sensors / Embedded)
Naming map (course-facing)
Section titled “Naming map (course-facing)”| Task in the lab | Recommended API to call | Layer underneath (just know it exists) |
|---|---|---|
| LED on/off / toggle | led_controller_set / led_controller_toggle |
Cy_GPIO_* + a BSP pin |
| A button + event | cm55_button_init / cm55_button_on_pressed |
GPIO IRQ + a FreeRTOS task |
| Logging a message | printf / LOG_INFO after bring-up |
init_retarget_io · cm55_uart_* |
| An I²C sensor | sensor_sht40_* (or another sensor in the SDK) |
cm55_i2c_manager_* · mtb_hal_i2c_* |
| Reading a POT (Eval) | cm55_adc_read_pot_mv |
Autonomous Analog SAR |
| Dimming an LED via PWM | bitstream_led_pwm_set_brightness |
Cy_TCPWM_PWM_* |
| Delaying inside a task | vTaskDelay(pdMS_TO_TICKS(...)) |
FreeRTOS (detail in M04) |
| General SPI in a lab | Concept + an Infineon / Hub example | In the current SDK, the main SPI path is on the radar module (Cy_SCB_SPI_*) |
Do not use cyhal_gpio_* as this course’s main path — the TESA stack uses a wrapper + PDL / MTB HAL.
Read alongside this chapter
Section titled “Read alongside this chapter”| Document | Use when |
|---|---|
| TESAIoT Developer Hub | The course’s main code examples + API Reference |
| AN241775 — HAL on PSOC™ Edge (PDF) | PDL / HAL / the Device Configurator |
| AN235935 — Getting started on ModusToolbox™ (PDF) | build / program / a UART terminal |
| mtb-example-psoc-edge-hello-world | LED + UART (extra Infineon example) |
| mtb-example-psoc-edge-gpio-interrupt | GPIO interrupts (extra Infineon example) |
| mtb-example-psoc-edge-spi-dma | An SPI code example, when you want a separate SPI lab |
| retarget-io | The printf → UART concept |
| TESAIoT_Hackathon | HEX / Flasher / web-app |
| Bitstream Studio | Host telemetry (extra) |
1. From Application Down to Driver API
Section titled “1. From Application Down to Driver API”Application → TESA Driver API (led_controller_*, cm55_*, sensor_*) → MTB HAL / Infineon PDL → Hardware pins / blocks| Course principle | Why it matters |
|---|---|
| Call the SDK’s wrapper first | Lab code matches the product / example projects |
| Fully init before reading/writing | The bus and pins are ready |
| Check return values / timeouts | Tells apart a software bug from a hardware one |
| Use a mutex around a shared bus (I²C) | Lets several tasks safely share the same bus |
Key phrase The Application decides — the Driver talks to hardware — don’t jump ahead to touching registers in standard exercises.
2. GPIO Fundamentals
Section titled “2. GPIO Fundamentals”GPIO is a pin that can be set as a digital input or output.
| Mode | What it’s for | In the course’s SDK |
|---|---|---|
| Output | Driving an LED | led_controller_*, or Cy_GPIO_Write / Cy_GPIO_Inv |
| Input / event | Reading a button | cm55_button_* |
| Interrupt | A pin edge → a callback | Inside cm55_button (GPIO IRQ + a task) |
Concepts to know: active-high/low, pull-up/down, debounce (usually handled inside the SDK’s button module).
2.1 LED output (TESA wrapper)
Section titled “2.1 LED output (TESA wrapper)”#include "led_controller.h"
void lab_led_demo(void){ (void)led_controller_init(); /* optional; the BSP usually already inits the pin */ led_controller_set(LED_RED, true); led_controller_toggle(LED_GREEN); led_controller_off_all();}led_id_t: LED_RED, LED_GREEN, LED_BLUE
2.2 Raw GPIO (when you need to touch a pin directly)
Section titled “2.2 Raw GPIO (when you need to touch a pin directly)”#include "cybsp.h"#include "cy_gpio.h"
/* Example: toggle USER LED1 with the PDL */Cy_GPIO_Inv(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN);Cy_GPIO_Write(CYBSP_USER_LED1_PORT, CYBSP_USER_LED1_PIN, 1U);2.3 Button events
Section titled “2.3 Button events”#include "sensor_button.h" /* public API: cm55_button_* */
static void on_btn_pressed(cm55_button_t handle, const button_event_t *evt){ (void)handle; led_controller_toggle(LED_BLUE); printf("button %lu pressed (count=%lu)\r\n", (unsigned long)evt->button_id, (unsigned long)evt->press_count);}
void lab_button_setup(void){ (void)cm55_button_init(); (void)cm55_button_on_pressed(BUTTON_ID_0, on_btn_pressed);}Some kits have a single button (BUTTON_ID_0); the Eval kit may have BUTTON_ID_1 depending on the BSP.
3. Core Peripherals
Section titled “3. Core Peripherals”3.1 Timer / delay (lab mindset)
Section titled “3.1 Timer / delay (lab mindset)”For a lab with several tasks / periodic timing, use FreeRTOS (detail in M04):
#include "FreeRTOS.h"#include "task.h"
void blink_task(void *arg){ (void)arg; for (;;) { led_controller_toggle(LED_RED); vTaskDelay(pdMS_TO_TICKS(500)); }}Low-level TCPWM (Cy_TCPWM_Counter_* / mtb_hal_timer_*) exists in the stack, but a basic lab doesn’t need to call it directly.
3.2 UART / logging
Section titled “3.2 UART / logging”After the app’s bring-up (cm55_initialize in a standard project), retarget IO is usually already set up — use:
#include <stdio.h>#include "app_log.h"
void lab_uart_log(void){ printf("hello from CM55\r\n"); LOG_INFO("LAB", "btn toggled");}Send a raw buffer when needed:
#include "cm55_uart.h"
const char msg[] = "raw uart\r\n";cm55_uart_send((const uint8_t *)msg, sizeof(msg) - 1U);The terminal is usually the KitProg3 port — the baud rate follows the project/manual (Infineon examples are usually 115200 8N1).
3.3 I²C (sensor path)
Section titled “3.3 I²C (sensor path)”#include "sensor_sht40.h"
void lab_i2c_sht40(void){ if (sensor_sht40_startup() != CY_RSLT_SUCCESS) { printf("SHT40 startup failed\r\n"); return; }
sht40_sample_t sample; if (sensor_sht40_read(&sample)) { printf("T=%.2f C RH=%.2f %%\r\n", (double)sample.temperature, (double)sample.humidity); }}When writing your own low-level driver on a shared bus, lock it:
cm55_i2c_manager_i2c_lock();/* mtb_hal_i2c_controller_write / read ... */cm55_i2c_manager_i2c_unlock();Other sensors in the SDK follow the same pattern: sensor_bmi270_*, sensor_dps368_*, sensor_bmm350_* (detail in M05)
3.4 SPI
Section titled “3.4 SPI”In the current product, the clearest SPI path in the library sits on the radar module (Cy_SCB_SPI_Init / Cy_SCB_SPI_Enable / Cy_SCB_SPI_Transfer).
For a general SPI lab: use the examples on the Developer Hub, or mtb-example-psoc-edge-spi-dma
Concepts you need to know: CS, CPOL/CPHA, MOSI/MISO/SCK
3.5 PWM (LED brightness)
Section titled “3.5 PWM (LED brightness)”#include "bitstream_led_pwm.h"
void lab_pwm_brightness(void){ if (bitstream_led_pwm_init() != 0) { return; } (void)bitstream_led_pwm_set_brightness(0 /* led_id */, 20); /* ~20% */ (void)bitstream_led_pwm_set_brightness(0, 80); /* ~80% */}Digital on/off for an LED can still use led_controller_* — use PWM when you need continuous brightness.
3.6 ADC (potentiometer on Eval kit)
Section titled “3.6 ADC (potentiometer on Eval kit)”#include "sensor_adc.h"
void lab_adc_pot(void){ if (!cm55_adc_init()) { printf("ADC init failed or POT N/A on this kit\r\n"); return; }
int32_t counts = cm55_adc_read_pot_counts(); int16_t mv = cm55_adc_read_pot_mv(); printf("POT counts=%ld mV=%d\r\n", (long)counts, (int)mv);}On a kit with no POT (such as some AI kit configurations), the function may be a no-op / return 0 — follow the kit you actually have.
3.7 Peripheral map
Section titled “3.7 Peripheral map”Application ├── led_controller_* / Cy_GPIO_* → LED ├── cm55_button_* → button events ├── printf / LOG_* / cm55_uart_* → UART log ├── sensor_* + cm55_i2c_manager_* → I²C sensors ├── bitstream_led_pwm_* → PWM brightness ├── cm55_adc_* → POT (Eval) └── vTaskDelay / xTaskCreate → timing & tasks (M04)4. How to Use Driver API in Practice
Section titled “4. How to Use Driver API in Practice”The standard sequence:
- Init / configure (
*_init,*_startup) - Enable / start (if separate)
- Transfer (read/write/set)
- Check the return value
- Deinit once done (in a real app)
An API name sheet: peripheral-api-map.md
4.1 Finding more examples
Section titled “4.1 Finding more examples”| Source | How to use it |
|---|---|
| TESAIoT Developer Hub | Domain GPIO / Sensors / Embedded |
| The example project you’re using | The header of the same module as the snippets in this lesson |
| Infineon CE | Compare against a vendor approach (extra) |
5. Worked Integration Scenarios
Section titled “5. Worked Integration Scenarios”Scenario A — Button toggles LED + UART
Section titled “Scenario A — Button toggles LED + UART”static void on_pressed(cm55_button_t h, const button_event_t *evt){ (void)h; (void)evt; led_controller_toggle(LED_GREEN); printf("btn toggled\r\n");}
void app_lab_ab(void){ (void)led_controller_init(); (void)cm55_button_init(); (void)cm55_button_on_pressed(BUTTON_ID_0, on_pressed);}Scenario B — ADC drives PWM brightness
Section titled “Scenario B — ADC drives PWM brightness”void app_lab_adc_pwm(void){ (void)cm55_adc_init(); (void)bitstream_led_pwm_init();
for (;;) { int16_t mv = cm55_adc_read_pot_mv(); /* map 0..1800 mV → 0..100% (adjust to the kit's real Vref) */ uint8_t duty = (uint8_t)((mv * 100) / 1800); if (duty > 100U) { duty = 100U; } (void)bitstream_led_pwm_set_brightness(0, duty); vTaskDelay(pdMS_TO_TICKS(50)); }}Scenario C — I²C sample to UART (preview of M05)
Section titled “Scenario C — I²C sample to UART (preview of M05)”Use sensor_sht40_read + printf per §3.3 — later, arrange data windows in M05 / watch it on Bitstream Studio once the firmware sends telemetry
6. Board Reality Checks
Section titled “6. Board Reality Checks”- Check the LED / button aliases from the BSP of the kit in hand
- Most boards’ I²C already has pull-ups — lock the bus when several tasks use it
- The ADC POT works on the Eval kit, as the SDK supports it
- The HEX / Flasher pack: TESAIoT_Hackathon
The main lab is measured by the LED / UART / values you can read Extra host: Bitstream Studio
7. Module Summary
Section titled “7. Module Summary”- Call the TESA Driver API (
led_controller_*,cm55_*,sensor_*) as the main path - The timer in a basic lab =
vTaskDelay(FreeRTOS) — M04 expands into multi-tasking - UART =
printf/LOG_*/cm55_uart_send - I²C / ADC / PWM have ready-made wrappers in the SDK
- SPI is a concept + Hub/Infineon examples
Next Steps
Section titled “Next Steps”- Do the exercise: Lab
- Note the real API names: Cheatsheet
- When ready, continue to M04 — RTOS Programming (M04 lesson)
References and Further Reading
Section titled “References and Further Reading”Course portals
Section titled “Course portals”Infineon (extra)
Section titled “Infineon (extra)”- AN241775 (PDF)
- AN235935 (PDF)
- mtb-example-psoc-edge-hello-world
- mtb-example-psoc-edge-gpio-interrupt
- mtb-example-psoc-edge-spi-dma
- retarget-io
- KIT_PSE84_EVAL
FreeRTOS (preview for M04)
Section titled “FreeRTOS (preview for M04)”- FreeRTOS task control —
vTaskDelay - FreeRTOS xTaskCreate
Check your understanding
Section titled “Check your understanding”Three short questions in quiz.yaml, one per objective of this lesson. Try answering them yourself first, then compare with the answer key and explanations in the file.
Continue hands-on at Lab: GPIO and peripherals on real hardware
Lab · Cheatsheet · ← Table of Contents · ← M02 · M04 →
Examples on the TESAIoT Developer Hub
Section titled “Examples on the TESAIoT Developer Hub”Try the real thing on the TESAIoT Dev Kit: open examples on the Developer Hub to read the code, download it, or flash ready-made firmware.
- QWA309 — Push Button Monitor — reads buttons SW9 (P17.5) and SW10 (P17.7) as active-low pull-up, showing pressed/released state + a press count on LVGL
- QWA309 — Potentiometer Monitor — reads 4 potentiometers (P15.4–P15.7) through the AUTANALOG SAR ADC, 12-bit (Vref 1.8V), shown as a bar + voltage + percentage in real time — a first practice exercise
- QWA309 — 4-Channel ADC Scope — plots the 4 pot values (P15.4-7, SAR 12-bit) as a scrolling line on an LVGL chart, 0-100% — an analog oscilloscope
Review questions
Answer on your own first, then open the answer.
-
ในสแต็กของบทเรียนนี้ ข้อใดถูกระบุว่า “อย่าใช้เป็นเส้นทางหลัก” ของหลักสูตร (Objective 1)
- `cyhal_gpio_*`
- `led_controller_*`
- `Cy_GPIO_Write` / `Cy_GPIO_Inv`
- `cm55_button_*`
Show answer
Answer: A. `cyhal_gpio_*`
ท้ายตาราง Naming map: สแต็กของหลักสูตรใช้ wrapper + PDL / MTB HAL ไม่ใช้ `cyhal_gpio_*` เป็นเส้นทางหลัก
-
เรียงลำดับการใช้ Driver API มาตรฐานตามหัวข้อ 4 (Objective 2)
- ตรวจค่าคืน
- Init / configure
- Deinit เมื่อเลิกใช้
- Transfer (read / write / set)
- Enable / start
Show answer
Correct order: B. Init / configure → E. Enable / start → D. Transfer (read / write / set) → A. ตรวจค่าคืน → C. Deinit เมื่อเลิกใช้
หัวข้อ 4: Init/configure → Enable/start → Transfer → ตรวจค่าคืน → Deinit
-
ทำไมต้องล็อกบัส I²C รอบการอ่านเขียนระดับต่ำ (Objective 3)
- เพราะหลาย task แชร์บัสเดียวกัน การล็อกทำให้ใช้บัสร่วมกันได้อย่างปลอดภัย
- เพื่อเพิ่มความเร็วของบัส
- เพราะบอร์ดไม่มีตัวต้านทาน pull-up
- เพื่อให้ PWM ทำงานพร้อม I²C ได้
Show answer
Answer: A. เพราะหลาย task แชร์บัสเดียวกัน การล็อกทำให้ใช้บัสร่วมกันได้อย่างปลอดภัย
ตารางหลักการในหัวข้อ 1 และหัวข้อ 3.3: ใช้ mutex รอบบัสร่วม เพื่อให้หลาย task ใช้บัสเดียวกันได้อย่างปลอดภัย
Cite this lesson
If you teach from this lesson or reuse it in slides or documents, credit it with the text below. If you changed it, add (adapted) after the title.
"GPIO and Peripherals through a Driver API" from TESA Open Knowledge by the Thai Embedded Systems Association (TESA), https://github.com/tesaiot/tesa-qualification-program, licensed under CC BY-NC 4.0
Thai attribution: "GPIO และอุปกรณ์ต่อพ่วงผ่าน Driver API" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0
This lesson adapts the source below; keep its credit too.
https://github.com/drsanti/TESAIoT-Courses/blob/287c21814ba8c75f693136616dcd270349a15966/C1/M03/README.md · Original content by Asst. Prof. Dr. Santi Nuratch (ผศ.ดร.สันติ นุราช), KMUTT. Course 1 (C1/) of drsanti/TESAIoT-Courses. TESA funded the work and holds the rights; published here under CC BY-NC 4.0. The upstream repository carries no licence file. Text kept faithful; structure, front matter, quizzes and notes added by TESA Open Knowledge.
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0
Content is licensed CC BY-NC 4.0. Reuse it non-commercially and credit the Thai Embedded Systems Association (TESA) every time. · How to cite TESA