任务介绍
本项目实现了 Funpack 活动 EV41C56A 板卡的自命题任务,基于 Microchip PIC32CM LS00 开发板,利用片上 TCC(Timer/Counter for Control)外设输出三路独立 PWM 信号,驱动外部 RGB 三色灯循环展示红、绿、蓝、紫、白五种颜色,并通过串口实时回显当前颜色状态,充分展示了 X-Macro 元编程技巧在嵌入式 C 开发中的工程实践价值。
硬件平台
本次使用 Microchip 推出的 EV41C56A 开发板,搭载 PIC32CM5164LS00048 微控制器,该芯片基于 ARM Cortex-M23 内核,内置 TrustZone 安全隔离机制,具备丰富的片上外设资源,包括多路 SERCOM 接口、TCC 定时器、ADC 等模块,非常适合面向 IoT 与低功耗嵌入式场景的开发。开发板集成了 PKOB nano 调试器,支持 SWD 在线烧录与调试,便于快速搭建原型系统。
在软件方面,本项目采用 MPLAB Harmony v3 框架作为底层驱动支撑,通过 MCC(MPLAB Code Configurator)图形化配置工具完成外设初始化代码的自动生成,并结合 XC32 编译器完成工程构建与烧录。

主控设备:Microchip EV41C56A 开发板
- 搭载 PIC32CM5164LS00048(ARM Cortex-M23)
- 集成 PKOB nano 调试器,支持 SWD 接口(2.000 MHz)
- 支持 Arm TrustZone for Armv8-M 安全分区
- 内置 XC32 v5.00 编译工具链支持
外部执行器
- RGB 三色 LED 灯(共阳极/共阴极均可适配)
- 通过 PA08 / PA09 / PA10 三路 GPIO 连接至 TCC0 的 WO0 / WO1 / WO2 输出
任务分析与实现
本系统实现了基于 TCC0 外设三路 PWM 输出的 RGB 彩灯循环控制,主要功能包括:
三通道 PWM 输出:
- R 通道(TCC0_CHANNEL0)
- 对应引脚:PA08 / TCC0_WO0
- 占空比范围:0% ~ 100%
- G 通道(TCC0_CHANNEL1)
- 对应引脚:PA09 / TCC0_WO1
- 占空比范围:0% ~ 100%
- B 通道(TCC0_CHANNEL2)
- 对应引脚:PA10 / TCC0_WO2
- 占空比范围:0% ~ 100%
颜色循环控制:
- 依次呈现 RED → GREEN → BLUE → PURPLE → WHITE 五色
- 每色驻留时间:3000 ms(由
SYSTICK_DelayMs实现) - 串口实时原地刷新显示当前颜色名称
方案框图:
TCC 外设配置过程
本项目基于 Microchip EV41C56A 开发板,借助 MPLAB Harmony v3 MCC 完成了 TCC0 外设的 PWM 模式配置,并适配了 TrustZone 安全分区,最终实现三通道 PWM 驱动 RGB 灯的完整功能。以下详细描述了配置的关键流程和核心要点。
一、配置前的准备
1. 硬件环境
- 主控芯片:Microchip PIC32CM5164LS00048(ARM Cortex-M23 内核)
- 开发板:EV41C56A
- 外设资源:
- TCC0 用于输出三路独立 PWM 信号驱动 RGB LED
- SERCOM3 用于 UART 串口调试输出
- SYSTICK 定时器用于延时控制
2. 软件环境
- IDE:MPLAB X IDE
- 代码生成工具:MPLAB Harmony v3 MCC(Melody)
- 编译工具链:XC32 v5.00
- 底层支持库:Harmony v3 提供 HAL 及 PLIB 驱动支持
二、配置的核心步骤
1. Project Graph 外设添加
在 MCC 的 Project Graph 界面中,添加所需的外设库组件。本项目核心外设包括:
- TCC0(Peripheral Library):提供 TMR 与 PWM 双功能节点,是驱动 RGB 灯的核心模块
- SERCOM3(Peripheral Library):配置为 UART 模式,连接至 STDIO 组件,实现串口输出
- NVMCTRL / PM / EVSYS:作为系统支撑外设自动加入
如图所示,TCC0 组件已被选中(蓝色高亮边框):

2. TCC0 PWM 参数配置
双击 TCC0 组件进入详细配置界面,按照 RGB 灯驱动需求完成以下关键参数设置:
- Operating Mode:选择
PWM - Select PWM Type:选择
DSTOP(双斜坡单触发模式) - Period Value:设置为
240,对应 PWM 频率 100000 Hz(100 kHz) - Channel Configurations:启用 Channel 0 / Channel 1 / Channel 2,初始 Duty Value 均设为
0 - Output Polarity:每通道设置为
Output is DIR when counter matches CCx value - Invert Output 0 / 1 / 2:均保持不勾选,确保输出极性与 LED 硬件匹配
如图所示,绿色高亮项目为本次配置的关键参数:

3. TrustZone 外设安全属性配置
由于 PIC32CM LS00 集成了 Arm TrustZone for Armv8-M,所有外设在使用前须在 MCC 的 Arm TrustZone 配置界面中明确其安全属性。
本项目将 TCC0 及 SERCOM3 设置为 Non-Secure(橙色),以便在 Non-Secure 应用代码中直接访问:
- TCC0 → Non-Secure(橙色)
- SERCOM3 → Non-Secure(橙色)
- 其余外设保持 Secure 默认状态(绿色)
如图所示,TCC0 与 SERCOM3 均以橙色标注,表示已配置为 Non-Secure 区域:

4. 引脚映射配置(Pin Configuration)
在 MCC 的 Pin Configuration Table View 中,将 TCC0 的三路 PWM 输出映射至对应 GPIO 引脚:
引脚编号 | Pin ID | 功能 | 安全属性 |
13 | PA08 | TCC0_WO0 | NON-SECURE |
14 | PA09 | TCC0_WO1 | NON-SECURE |
15 | PA10 | TCC0_WO2 | NON-SECURE |
7 | PB08 | SERCOM3_PAD0 | NON-SECURE |
三路 PWM 输出引脚(PA08 / PA09 / PA10)均设置为 NON-SECURE,与 TrustZone 分区保持一致,如下图红框标注所示:

代码详解
整体软件流程
一、X-Macro 颜色数据表设计
本项目的核心亮点在于采用 X-Macro 元编程技巧,将颜色数据集中定义在一张"大表"中,通过宏展开自动生成枚举、switch-case 分支及主循环体,彻底消除重复代码,保证颜色新增或修改时只需改动一处。
#define FOREACH_COLOR(X) \
X(RED, 100, 0, 0, "RED") \
X(GREEN, 0, 100, 0, "GREEN") \
X(BLUE, 0, 0, 100, "BLUE") \
X(PURPLE, 60, 0, 60, "PURPLE") \
X(WHITE, 100, 100, 100, "WHITE")
展开用途一:自动生成颜色枚举
#define GENERATE_ENUM(enum_name, r, g, b, str) COLOR_##enum_name,
typedef enum {
FOREACH_COLOR(GENERATE_ENUM)
COLOR_MAX
} RGB_Color_T;
// 展开结果:COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_PURPLE, COLOR_WHITE, COLOR_MAX
展开用途二:switch-case 自动生成
#define GENERATE_CASE(enum_name, r, g, b, str) \
case COLOR_##enum_name: RGB_Set_Duty(r, g, b); break;
void Set_Static_Color(RGB_Color_T color) {
switch (color) {
FOREACH_COLOR(GENERATE_CASE)
default: RGB_Set_Duty(0, 0, 0); break;
}
}
二、PWM 占空比计算与设置
RGB_Set_Duty 函数负责将百分比形式的 RGB 亮度值转换为 TCC0 比较寄存器的实际计数值,并写入三个通道:
void RGB_Set_Duty(uint8_t r_pct, uint8_t g_pct, uint8_t b_pct) {
uint32_t period = TCC0_PWM24bitPeriodGet(); // 读取当前周期值(=240)
uint32_t r_cc = (r_pct * period) / 100; // 红色通道比较值
uint32_t g_cc = (g_pct * period) / 100; // 绿色通道比较值
uint32_t b_cc = (b_pct * period) / 100; // 蓝色通道比较值
TCC0_PWM24bitDutySet(TCC0_CHANNEL0, r_cc); // 写入 R 通道
TCC0_PWM24bitDutySet(TCC0_CHANNEL1, g_cc); // 写入 G 通道
TCC0_PWM24bitDutySet(TCC0_CHANNEL2, b_cc); // 写入 B 通道
}
以 Period Value = 240、PWM 频率 100 kHz 为基准,各颜色对应的占空比与比较值如下:
颜色 | R(%) | G(%) | B(%) | R_CC | G_CC | B_CC |
RED | 100 | 0 | 0 | 240 | 0 | 0 |
GREEN | 0 | 100 | 0 | 0 | 240 | 0 |
BLUE | 0 | 0 | 100 | 0 | 0 | 240 |
PURPLE | 60 | 0 | 60 | 144 | 0 | 144 |
WHITE | 100 | 100 | 100 | 240 | 240 | 240 |
三、串口原地刷新显示
为避免串口输出逐行滚动,代码采用退格符(\b)覆盖上一行内容,实现原地刷新效果:
#define GENERATE_LOOP(enum_name, r, g, b, str) \
Set_Static_Color(COLOR_##enum_name); \
for (int i = 0; i < last_len; i++) { \
printf("\b \b"); /* 逐字符退格清除 */ \
} \
printf("Current Color: %s", str); \
fflush(stdout); \
last_len = strlen("Current Color: ") + strlen(str); \
SYSTICK_DelayMs(3000);
last_len 是静态全局变量,记录上一次输出字符串的总长度,保证下一次能精准退格覆盖,不产生残留字符。
四、主函数
int main(void) {
SYS_Initialize(NULL); // Harmony v3 统一初始化入口
SYSTICK_TimerStart(); // 启动 SysTick
TCC0_PWMStart(); // 启动 PWM 输出
printf("\r\n");
printf("================================\r\n");
printf(" PIC32CM LS00 Task2\r\n");
printf("================================\r\n\r\n");
while (true) {
FOREACH_COLOR(GENERATE_LOOP) // X-Macro 自动展开五色循环
SYS_Tasks();
}
return (EXIT_FAILURE);
}
效果展示
程序运行后,RGB 灯依次点亮红、绿、蓝、紫、白五种颜色,每色持续 3 秒后自动切换;串口终端同步显示当前颜色名称,并在同一行原地刷新,无滚动干扰。
红色

绿色

蓝色

如图所示,串口终端(COM20,115200 波特率)显示当前颜色为 GREEN,输出格式为 Current Color: 当前颜色。

遇到的难题与解决办法
问题一:TCC0 PWM 输出无信号,RGB 灯不亮
解法:检查 TrustZone 配置后发现 TCC0 默认属于 Secure 区域,而应用代码运行在 Non-Secure 区域,导致外设访问被拦截。在 MCC 的 Arm TrustZone Peripheral Configuration 界面中将 TCC0 切换为 Non-Secure,同时将对应 GPIO 引脚(PA08 / PA09 / PA10)的 Security Mode 也设为 NON-SECURE,问题解决。
问题二:串口输出乱码或无输出
解法:SERCOM3 同样需要设置为 Non-Secure 并在 Pin Configuration 中正确映射 PAD0(PB08),确保 STDIO 组件能正常调用 UART 发送接口。同时确认波特率配置与上位机终端(115200)一致。
问题三:PWM 占空比调节无效,颜色无变化
解法:排查发现 TCC0_PWM24bitDutySet 需在 PWM 已启动(TCC0_PWMStart() 调用之后)才能生效;另需确认 Period Value 与实际系统时钟匹配,避免因分频配置错误导致计算出的 CC 值溢出或为零。
活动感想
通过本项目实践,深入掌握了 Microchip MPLAB Harmony v3 框架下 TCC 外设的 PWM 配置方法,以及 Arm TrustZone 安全分区机制在实际工程中的配置要点。EV41C56A 开发板集成的 PKOB nano 调试器极大简化了烧录与调试流程,MCC 图形化配置工具使外设初始化代码的生成变得直观高效。
在代码设计层面,X-Macro 元编程技巧的引入让颜色表的维护成本降至最低——新增一种颜色只需在 FOREACH_COLOR 宏中追加一行,枚举、switch-case 及主循环均自动同步更新,体现了嵌入式 C 开发中"数据驱动代码生成"的工程哲学。整个项目过程中,深刻体会到硬件安全配置、外设时钟正确性与应用层逻辑三者缺一不可,每个环节的细致配置都直接决定了最终功能能否正常运行。
感谢硬禾学堂和得捷电子联合举办的 Funpack 活动,祝硬禾的活动越办越好!