Funpack5-2 基于PIC32CM LS00的触摸输入与LED控制
该项目使用了PIC32CM LS00开发板,实现了触摸控制LED的设计,它的主要功能为:短按触摸按键开关、长按触摸按键调节亮度。
标签
Funpack活动
PWM
MCU
PIC32
码匠许师傅
更新2026-06-22
37

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 亮度功能

功能要求:

  1. 通过 PWM 控制 LED 亮度
  2. 短按:LED 开 / 关
  3. 长按:连续调节亮度,并按照【亮度从“灭 → 最亮”线性变化;当到达最亮后,保持长按可实现“最亮 → 灭”反向变化】的逻辑循环
  4. 亮度变化过程应平滑(无明显闪烁或跳变)

实现代码如下:

#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亮度;亮度从“灭 → 最亮”线性变化;当到达最亮后,保持长按可实现“最亮 → 灭”反向变化。

效果演示:

六、重点总结

这里总结一下个人认为整个过程中的一些重难点。

  1. 工程类型选择
    问题描述:Touch Library 需要在安全工程中使用,如果主项目选错,后续配置和调用容易出问题。
    解决方法:工程创建后先将安全工程设置为主项目,后续 MCC 配置和代码修改都在安全工程中完成。
  2. 触摸组件配置
    问题描述:添加 Touch Library 后,还需要同时确认 RTC、PTC、时钟源和触摸通道配置。
    解决方法:按照 MCC 中的组件关系逐项检查,确认 Touch Library 添加成功,并确认 Button 的信号来源选择为 PA22。
  3. 初始化顺序
    问题描述:触摸功能无响应时,常见原因是 RTC 和 Touch 初始化顺序不正确。
    解决方法:确保 RTC_Initialize()touch_init() 之前执行。
  4. 触摸按键重复触发
    问题描述:如果直接用当前触摸状态翻转 LED,按住不放时会连续触发。
    解决方法:增加上一状态变量,只在按下沿执行一次 LED_Toggle()
  5. PA15 功能切换
    问题描述:GPIO 点灯阶段 PA15 是普通输出,PWM 调光阶段需要切换为 TC0_WO1
    解决方法:在 MCC 的 Pin Settings 中重新配置 PA15 功能,并重新生成代码。
  6. PWM 参数选择
    问题描述:PWM 频率过低会闪烁,过高又会影响调光分辨率。
    解决方法:使用 12 MHz 时钟,将 Compare 0 Value 设置为 1199,得到约 10 kHz 的 PWM 输出。
  7. 长按调光状态管理
    问题描述:短按、长按、亮度保存和方向反转容易混在一起。
    解决方法:拆分状态变量,用 last_touched 判断边沿,hold_cnt 判断长按,brightness 保存当前亮度,dir 控制调光方向。

七、心得体会

这次项目做下来,最大的感受是:遇到不熟悉的平台时,不要一上来就把所有功能都堆在一起做。先从最简单的 GPIO 点灯开始,把工程创建、程序下载、引脚配置和板载 LED 都确认没问题,再继续加入触摸和 PWM,后面的调试会顺很多。

通过这个项目,我对 MCC 的使用也熟悉了不少。MCC 能自动生成很多初始化代码,确实省事,但不能只点配置界面而不理解背后的外设关系。比如触摸功能不只是添加 Touch Library,还要关注 RTC、PTC、触摸通道和初始化顺序;PWM 调光也不只是启动 TC0,还要记得把 PA15 从普通 GPIO 切换到 TC0 输出功能。

触摸按键的处理也比一开始想的更细。只判断“当前是否被触摸”是不够的,还要区分按下沿、松手沿、短按和长按,否则很容易出现重复触发或者状态混乱。把 last_touchedhold_cntbrightness 这些变量的职责拆清楚之后,程序逻辑就明显清晰了。

整体来看,这个项目内容不算复杂,但把 GPIO、触摸输入、定时延时和 PWM 输出都串了起来,是一次比较完整的练习。后续如果继续完善,我会考虑加入串口打印,方便观察触摸状态和亮度值;也可以把当前亮度保存下来,让开发板重新上电后恢复上一次的亮度设置。



附件下载
Funpack_secure.rar
团队介绍
xusiwei1236
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号