Funpack5-2 基于PIC32CM LS00的触摸输入与LED控制
一、项目简介
本项目基于PIC32CM LS00 Curiosity Nano+ Touch Evaluation Kit开发板,实现了通过板载电容触摸按键控制板载 LED 开关 和 亮度调节的功能。项目从最基础的 GPIO 点灯开始,逐步完成触摸输入识别、PWM 输出控制亮度、触摸短按开关 LED和长按连续调节亮度等功能。整个实现过程覆盖了 MPLAB X IDE 工程创建、MCC 图形化配置、Harmony Touch Library 添加、PTC 触摸通道配置、TC0 定时器 PWM 配置、程序下载运行和实际调试验证等环节。
二、硬件简介
Microchip EV41C56A 开发板简介:
PIC32CM LS00 Curiosity Nano+ Touch Evaluation Kit 是一款专为评估 PIC32CM5164LS00048 微控制器而设计的硬件平台。该套件基于安全且超低功耗的 ARM® Cortex®-M23 内核,集成了丰富的安全功能和触摸控制能力。
本项目没有使用外接传感器或显示模块,仅利用 EV41C56A 开发板自带的板载 LED、板载电容触摸按键和 USB 调试下载接口,以及 PIC32CM LS00 MCU 的片上外设完成所有实验。硬件连接保持开发板默认状态,重点放在 MCU 外设配置、触摸状态识别、按键交互逻辑和 PWM 亮度控制上。
使用到的开发板硬件资源如下:
硬件资源 | 连接关系 | 项目中的作用 |
|---|---|---|
板载 LED | 连接到 MCU 的 PA15 引脚 | 作为输出对象,用于显示开关状态和亮度变化 |
板载电容触摸按键 | 连接到 MCU 的 PA22 引脚 | 作为用户输入,完成短按、长按等交互操作 |
USB 调试下载接口 | 连接 PC 与开发板 | 用于 MPLAB X IDE 编译下载程序,并为开发板供电 |
PIC32CM5164LS00048 MCU | 开发板主控芯片 | 负责触摸检测、逻辑判断和 LED 输出控制 |
对应的 MCU 片上外设如下:
MCU 片上外设 | 使用方式 | 说明 |
|---|---|---|
GPIO | PA15 配置为普通输出 | 在基础点灯阶段控制 LED 翻转、点亮和熄灭 |
PTC | PA22 配置为触摸输入通道 | 用于电容触摸按键采样和触摸状态判断 |
RTC | 作为 Touch Library 相关时间基准 | 触摸库初始化和运行过程中需要配合 RTC 使用 |
SysTick | 提供毫秒级延时 | 用于 LED 闪烁、按键扫描节奏和调光步进间隔 |
TC0 | 配置为 Match PWM 输出 | 通过 PWM 占空比控制 LED 亮度 |
GCLK | 为 TC0、PTC 等外设提供时钟 | TC0 使用 12 MHz 时钟生成约 10 kHz PWM |
简单来说,PA22 负责触摸输入,PA15 负责 LED 输出。项目初期将 PA15 作为 GPIO 使用,用于验证 LED 控制;后期将 PA15 复用为 TC0_WO1,由 TC0 输出 PWM 信号实现亮度调节。PTC 和 RTC 主要服务于触摸检测,SysTick 提供扫描与调光节奏,TC0 负责生成占空比可调的 PWM 波形。
三、系统框图
本节的系统框图主要用于体现硬件资源、片上外设和软件部分之间的关系。
用户通过板载触摸按键实现输入操作,程序通过 GPIO 输出控制 LED 亮灭,通过 PWM 波的占空比控制 LED 亮度。MCU 内部的 PTC 外设负责采集 PA22 触摸信号,Touch Library 对触摸数据进行处理,应用控制逻辑再根据短按、长按等状态决定 LED 的开关和亮度变化。
输出部分分为两条链路:GPIO 输出链路主要用于项目早期验证,通过 PA15 直接控制板载 LED 亮灭,确认工程、引脚和板载 LED 工作正常;PWM 输出链路是最终功能使用的主要链路,PA15 复用为 TC0_WO1,由 TC0 输出 PWM 波形,通过调节占空比实现 LED 亮度控制。
系统框图如下:

从实现路径上看,项目先用 GPIO 点灯验证 PA15 和板载 LED,再加入 PA22 触摸输入,实现触摸按键对 LED 状态的控制;最后将 PA15 切换为 TC0_WO1,通过 PWM 占空比调节 LED 亮度。这样可以先确认基础硬件和工程环境,再逐步叠加触摸识别和亮度控制功能。
四、软件代码
本项目使用 MPLAB X IDE 进行工程创建、代码编辑、编译和下载,使用 MCC 完成外设图形化配置和初始化代码生成。软件部分没有复杂算法,核心就是按阶段配置好 GPIO、Touch Library、PTC、RTC、TC0 等外设,然后在 main() 主循环中读取触摸状态并更新 LED 输出。
阶段1——GPIO点灯
GPIO 点灯阶段主要用于验证 PA15 和板载 LED 是否工作正常。MCC 中将 PA15 配置为 GPIO 输出,并将 Custom Name 设置为 LED;同时开启 Secure SysTick,方便后续使用毫秒级延时函数。


对应代码如下:
int main ( void )
{
/* Initialize all modules */
SYS_Initialize ( NULL );
/* Start SysTick timer */
SYSTICK_TimerStart();
while ( true )
{
LED_Toggle();
SYSTICK_DelayMs(500);
}
return 0;
}
这段代码每 500 ms 翻转一次 LED 状态,是后续触摸和 PWM 功能之前的基础验证程序。
阶段2——触摸输入
触摸输入阶段需要在 MCC 中添加 Touch Library,并配置 PA22 作为触摸 Button 的信号来源。添加 Touch Library 后,工程中会同时出现 RTC、PTC 等相关组件,因此还需要确认 PTC 时钟和初始化顺序。


基础触摸测试代码如下:
int main ( void )
{
uint8_t key_status0 = 0;
/* Initialize all modules */
SYS_Initialize ( NULL );
RTC_Initialize();
touch_init();
while ( true )
{
touch_process(); // Continuously process touch events
key_status0 = get_sensor_state(0) & 0x80;
if(0u != key_status0)
{
LED_Set(); // Turn LED on if measurement is signaled
}
else
{
LED_Clear(); // Turn LED off if measurement is not signaled or reset
}
}
return 0;
}
如果要实现“单次触摸切换 LED 开关”,需要增加边沿检测,避免手指按住不放时主循环重复触发:
int main(void)
{
uint8_t key_status = 0;
uint8_t key_status0 = 0;
SYS_Initialize(NULL);
RTC_Initialize();
touch_init();
SYSTICK_TimerStart();
while (true)
{
touch_process();
key_status = get_sensor_state(0) & KEY_TOUCHED_MASK;
if (key_status && !key_status0)
{
LED_Toggle();
}
key_status0 = key_status;
SYSTICK_DelayMs(10);
}
return 0;
}
阶段3——PWM调光
PWM 阶段使用 TC0 输出波形控制 LED 亮度。MCC 中添加 TC0 外设,将 PA15 引脚功能改为 TC0_WO1,再将 TC0 配置为 Compare 模式和 Match PWM 模式。项目中 Compare 0 Value 设置为 1199,在 12 MHz 时钟下 PWM 周期约为 100 us,对应频率约为 10 kHz。



固定占空比输出代码如下:
int main(void)
{
SYS_Initialize(NULL);
TC0_CompareStart();
TC0_Compare16bitMatch1Set(60);
while (true)
{
}
return 0;
}
呼吸灯代码如下:
#define DUTY_MAX 1199
#define DUTY_STEP 6
int main(void)
{
SYS_Initialize(NULL);
SYSTICK_TimerStart();
TC0_CompareStart();
uint16_t duty = 0;
int8_t direction = 1;
while (true)
{
TC0_Compare16bitMatch1Set(duty);
int16_t next = (int16_t)duty + direction * DUTY_STEP;
if (next >= DUTY_MAX) {
next = DUTY_MAX;
direction = -1;
} else if (next <= 0) {
next = 0;
direction = 1;
}
duty = (uint16_t)next;
SYSTICK_DelayMs(10);
}
return 0;
}
阶段4——短按开关与长按调光
有了前面的基础,就可以完成任务一进阶题了,题目要求如下:
任务1:触摸输入与LED控制
进阶题目:【难度:⭐️⭐️⭐️】
在基础题之上,实现触摸长按调节LED 亮度功能
功能要求:
- 通过 PWM 控制 LED 亮度
- 短按:LED 开 / 关
- 长按:连续调节亮度,并按照【亮度从“灭 → 最亮”线性变化;当到达最亮后,保持长按可实现“最亮 → 灭”反向变化】的逻辑循环
- 亮度变化过程应平滑(无明显闪烁或跳变)
实现代码如下:
#define DUTY_MAX 1199U
#define DUTY_STEP 12U
#define LONG_PRESS_TICK 50U
#define DEFAULT_DUTY 600U
int main(void)
{
// 初始化系统、触摸库、SysTick 和 TC0 PWM 输出
SYS_Initialize(NULL);
RTC_Initialize();
touch_init();
SYSTICK_TimerStart();
TC0_CompareStart();
// last_touched 用于边沿检测,led_on 记录 LED 当前开关状态
bool last_touched = false, led_on = false;
// hold_cnt 记录触摸持续时间,用于区分短按和长按
uint16_t hold_cnt = 0;
// brightness 是当前占空比,saved 用于保存上一次亮度
uint16_t brightness = DEFAULT_DUTY, saved = DEFAULT_DUTY;
// dir 控制亮度变化方向:1 表示变亮,-1 表示变暗
int8_t dir = 1;
while (true) {
// 持续处理触摸库状态,并读取第 0 个触摸按键状态
touch_process();
bool touched = (get_sensor_state(0) & KEY_TOUCHED_MASK) != 0;
// 按下沿:刚开始触摸时,清零长按计数并设置初始调光方向
if (touched && !last_touched) {
hold_cnt = 0;
dir = 1;
}
// 松手沿:根据按住时间判断本次操作是短按还是长按
if (!touched && last_touched) {
if (hold_cnt < LONG_PRESS_TICK) {
// 短按:切换 LED 开关,并恢复保存的亮度
led_on = !led_on;
brightness = led_on ? saved : 0;
TC0_Compare16bitMatch1Set(brightness);
} else {
// 长按结束:保存当前亮度,供下次短按点亮时使用
saved = brightness;
}
}
if (touched) {
hold_cnt++;
if (hold_cnt >= LONG_PRESS_TICK) {
// 长按调光时,如果 LED 原本关闭,则先打开并从 0 开始调节
if (!led_on) {
led_on = true;
brightness = 0;
}
// 按固定步进更新亮度,占空比到达上下限后反向
int16_t next = (int16_t)brightness + dir * (int16_t)DUTY_STEP;
if (next >= (int16_t)DUTY_MAX) {
next = (int16_t)DUTY_MAX;
dir = -1;
} else if (next <= 0) {
next = 0;
dir = 1;
}
brightness = (uint16_t)next;
// 将新的占空比写入 TC0 Compare 1,更新 LED 亮度
TC0_Compare16bitMatch1Set(brightness);
}
}
// 保存本轮触摸状态,供下一轮判断按下沿/松手沿
last_touched = touched;
SYSTICK_DelayMs(10);
}
return 0;
}
这段代码的核心是把一个触摸按键拆成两类操作:短按和长按。last_touched 用来判断触摸状态的变化,避免按住不放时重复触发;hold_cnt 用来统计按下持续时间,小于 LONG_PRESS_TICK 时按短按处理,超过阈值后按长按处理。
短按时,程序只切换 led_on 状态。如果切换为打开,就恢复 saved 中保存的亮度;如果切换为关闭,就把占空比设为 0。长按时,程序持续修改 brightness,并通过 TC0_Compare16bitMatch1Set(brightness) 更新 PWM 占空比。dir 用来控制亮度变化方向,当亮度达到 DUTY_MAX 或 0 时自动反向,因此可以实现“灭到最亮、最亮到灭”的循环调光效果。
五、功能展示
最终实现的功能:
- 短按板载触摸按键时,LED 在点亮和熄灭之间切换。
- 长按板载触摸按键时,调节LED亮度;亮度从“灭 → 最亮”线性变化;当到达最亮后,保持长按可实现“最亮 → 灭”反向变化。
效果演示:

六、重点总结
这里总结一下个人认为整个过程中的一些重难点。
- 工程类型选择
问题描述:Touch Library 需要在安全工程中使用,如果主项目选错,后续配置和调用容易出问题。
解决方法:工程创建后先将安全工程设置为主项目,后续 MCC 配置和代码修改都在安全工程中完成。 - 触摸组件配置
问题描述:添加 Touch Library 后,还需要同时确认 RTC、PTC、时钟源和触摸通道配置。
解决方法:按照 MCC 中的组件关系逐项检查,确认 Touch Library 添加成功,并确认 Button 的信号来源选择为 PA22。 - 初始化顺序
问题描述:触摸功能无响应时,常见原因是 RTC 和 Touch 初始化顺序不正确。
解决方法:确保RTC_Initialize()在touch_init()之前执行。 - 触摸按键重复触发
问题描述:如果直接用当前触摸状态翻转 LED,按住不放时会连续触发。
解决方法:增加上一状态变量,只在按下沿执行一次LED_Toggle()。 - PA15 功能切换
问题描述:GPIO 点灯阶段 PA15 是普通输出,PWM 调光阶段需要切换为TC0_WO1。
解决方法:在 MCC 的 Pin Settings 中重新配置 PA15 功能,并重新生成代码。 - PWM 参数选择
问题描述:PWM 频率过低会闪烁,过高又会影响调光分辨率。
解决方法:使用 12 MHz 时钟,将 Compare 0 Value 设置为 1199,得到约 10 kHz 的 PWM 输出。 - 长按调光状态管理
问题描述:短按、长按、亮度保存和方向反转容易混在一起。
解决方法:拆分状态变量,用last_touched判断边沿,hold_cnt判断长按,brightness保存当前亮度,dir控制调光方向。
七、心得体会
这次项目做下来,最大的感受是:遇到不熟悉的平台时,不要一上来就把所有功能都堆在一起做。先从最简单的 GPIO 点灯开始,把工程创建、程序下载、引脚配置和板载 LED 都确认没问题,再继续加入触摸和 PWM,后面的调试会顺很多。
通过这个项目,我对 MCC 的使用也熟悉了不少。MCC 能自动生成很多初始化代码,确实省事,但不能只点配置界面而不理解背后的外设关系。比如触摸功能不只是添加 Touch Library,还要关注 RTC、PTC、触摸通道和初始化顺序;PWM 调光也不只是启动 TC0,还要记得把 PA15 从普通 GPIO 切换到 TC0 输出功能。
触摸按键的处理也比一开始想的更细。只判断“当前是否被触摸”是不够的,还要区分按下沿、松手沿、短按和长按,否则很容易出现重复触发或者状态混乱。把 last_touched、hold_cnt、brightness 这些变量的职责拆清楚之后,程序逻辑就明显清晰了。
整体来看,这个项目内容不算复杂,但把 GPIO、触摸输入、定时延时和 PWM 输出都串了起来,是一次比较完整的练习。后续如果继续完善,我会考虑加入串口打印,方便观察触摸状态和亮度值;也可以把当前亮度保存下来,让开发板重新上电后恢复上一次的亮度设置。