2026寒假练-基于RP2350B核心板设计LED与数码管多功能计时器
该项目使用了VSCode软件、C语言,实现了一个RP2350B核心板的设计,它的主要功能为:实现一个多功能计时器:支持正计时、倒计时和随机数显示三种模式,通过按键控制计时开始、暂停、清零和模式切换,拨码开关直接选择工作模式;数码管实时显示数值,单色 LED 矩阵以进度条形式指示进度,WS2812 LED 用不同颜色标识当前模式,并通过 USB 虚拟串口输出调试信息。
标签
RP2350B
计时器
NYSL
更新2026-03-24
北京理工大学
25

1. 所选任务介绍

本项目的核心任务是基于 STEP-RP2350B 核心板,设计并实现一个多功能计时器演示系统。系统需满足以下功能需求:

输入控制:利用核心板上的 4 个轻触按键(KEY0~KEY3)和 4 个拨码开关(SW0~SW3)实现交互,其中按键需支持短按与长按两种操作,分别触发不同功能;拨码开关用于直接切换系统的工作模式。

工作模式:支持三种计时模式 —— 正计时(0→99 循环)、倒计时(99→0,归零暂停)以及计次随机数显示(每 3 秒生成一个 0~99 的随机数并计数生成次数)。

状态显示:

两位七段数码管实时显示当前计数值或随机数;

8 颗单色 LED 以进度条形式直观反映正/倒计时的进度(0~99 对应 0~8 颗 LED 点亮);

2 颗 WS2812 RGB LED 用不同颜色指示当前模式(正计时红色、倒计时黄色、随机数蓝色);

独立 LED(GPIO3)常亮作为电源指示。

调试输出:通过 USB 虚拟串口输出系统状态、ADC 采样值、按键/开关事件及模式切换日志,便于开发和调试。

该任务综合运用了 RP2350B 的 GPIO、ADC、定时器、PIO(通过软件模拟)以及多种外设驱动技术,并针对硬件实际特性进行了多项优化,具有较高的工程实践价值。

2. 项目介绍

本项目是为 STEP-RP2350B 核心板定制的综合演示固件。该核心板基于 Raspberry Pi RP2350B 微控制器,集成了丰富的板载外设,包括数码管、单色 LED 矩阵、RGB LED、按键、拨码开关、姿态传感器等。项目充分利用了核心板的硬件资源,编写了一个既展示基础外设驱动,又解决实际工程问题的示例程序。在实现基本计时器功能的同时,我重点攻克了以下技术难点:

LED 矩阵的鬼影(微亮/串扰)问题:由于 8 颗 LED 仅由 4 个 GPIO 以矩阵方式驱动,切换时引脚残留电荷会导致不该点亮的 LED 微微发亮。我们独创了“放电 + 高阻 + 目标驱动”的驱动方法,彻底消除了这一现象。

WS2812 的时序干扰导致闪绿:在高速系统下,WS2812 的 0 码高电平时间过短容易受到干扰,表现为 LED 随机闪烁绿色。通过精细调节延时并增强引脚驱动能力,显著提高了通信可靠性。

ADC 多路复用输入的准确识别:4 个按键和 4 个拨码开关共用同一个 ADC 引脚(GPIO47),通过分压电阻网络产生不同电压。实际操作时通过实测校准了每个开关/按键的 ADC 阈值,并结合软件滤波和消抖,实现了稳定准确的识别。

多任务实时调度:系统需同时处理按键扫描、数码管动态刷新、LED 矩阵扫描、WS2812 刷新、计时更新等任务,且不能使用阻塞式延时。我们设计了基于时间戳的非阻塞轮询调度器,保证了所有任务的实时性和流畅性。

3. 使用到的硬件介绍

3.1 主控芯片:RP2350B

双核 Arm Cortex-M33 处理器,主频可达 150MHz;

内置 520KB SRAM 和 2MB 板载 Flash;

48 个 GPIO 引脚,支持多种功能复用;

丰富的片内外设:ADC、PWM、I2C、SPI、UART、PIO 等。

3.2 核心板板载外设

两位七段数码管:由两片 74HC595 移位寄存器驱动,仅占用 3 个 GPIO(SER、RCLK、SCLK),实现串行转并行输出,控制数码管的段选和位选。

8 颗单色 LED:采用 4×2 矩阵连接方式,由 GPIO24~27 控制。每颗 LED 对应一对 GPIO(一个高电平、一个低电平),利用人眼视觉暂留通过快速扫描实现“常亮”效果。

2 颗 WS2812 RGB LED:通过单线串行数据线(GPIO46)级联,内置驱动 IC,可独立显示 24 位真彩色。本项目中用于指示工作模式。

4 个轻触按键(KEY0~KEY3) 与 4 个拨码开关(SW0~SW3):通过电阻分压网络共同连接到 GPIO47(ADC 通道 7)。每个按键/开关闭合时接入不同阻值的下拉电阻,产生唯一的 ADC 采样值。软件根据预设阈值区分具体操作。
独立电源指示 LED:直接由 GPIO3 驱动,高电平点亮,用于指示系统已上电运行。

USB Type-C 接口:提供 5V 电源,并连接至 RP2350 的 USB 控制器,支持虚拟串口(CDC)功能,用于打印调试信息。

其他:板载 12MHz 晶振、2MB Flash、XT3406 稳压芯片(5V→3.3V)、姿态传感器(I2C 接口,本项目未使用)等。

3.3 硬件连接总结

image.png

4. 方案框图和项目设计思路

4.1 系统框图

4.2 设计思路

本项目的主要思路如下:

外设驱动抽象:将每个外设(数码管、LED矩阵、WS2812、ADC)封装成独立的模块,提供初始化函数和周期性更新函数。各模块之间通过全局变量(如 counter、timer_mode)共享状态,降低耦合度。

非阻塞时间片轮询:主循环采用基于系统滴答计时器的轮询方式,为每个任务分配固定的执行间隔(如按键20ms、数码管10ms、LED矩阵2ms等)。通过比较当前时间与上次执行时间决定是否运行任务,避免了 sleep() 造成的阻塞,保证了系统的实时响应能力。

硬件缺陷的软件补偿:

LED矩阵鬼影:分析电路原理后发现,直接切换引脚电平会导致电荷残留。因此设计“放电→高阻→目标驱动”的驱动序列,确保每次点亮前所有引脚被强制拉低放电,然后全部设为高阻,最后只将需要的两个引脚设为输出并驱动。此方法彻底消除了微亮现象。

WS2812时序优化:根据数据手册,0码和1码的时序容差有限。在RP2350高速运行时,简单的 busy_wait 可能因指令流水线导致偏差。我们通过调整0码中高电平的延时(使用四个 busy_wait_us_32(0) 延长至约0.5μs)并启用12mA驱动强度,使信号边沿更陡峭,抗干扰能力增强。

ADC多路复用识别:由于多个开关/按键共用同一ADC通道,其电压值会相互影响。我们首先通过实测获得每个开关/按键单独按下时的ADC原始值范围,并预留足够余量。软件中采用4次采样取平均滤波,并结合状态机进行消抖(按键连续稳定30ms判定按下,开关连续5次采样一致判定切换),从而准确识别。

状态机设计:

计时器状态:包括停止、运行、暂停三种状态,由按键控制切换。

模式状态:由拨码开关直接设定或由按键循环切换。当拨码开关变化时,通过 switch_mode_changed 标志通知主循环应用新模式,并同步更新WS2812颜色和进度条。

按键长按/短按:每个按键维护按下时间、长按标记等变量,识别是否需要消抖后,通过时间差区分短按(30ms~2000ms)和长按(≥2000ms),并执行对应回调。

5. 调试软件、编程语言及关键代码

5.1 开发环境与工具

编程语言:C语言。

SDK:官方 Raspberry Pi Pico SDK 2.2.0,包含硬件抽象层、标准库及外设驱动。

IDE/编辑器:Visual Studio Code,配合 Raspberry Pi Pico VS Code Extension,实现一键编译、烧录与调试。

构建系统:CMake,通过 CMakeLists.txt 管理项目,链接所需库。

调试工具:通过 USB 虚拟串口输出日志,使用串口终端实时查看。

5.2 软件流程

5.3 关键代码分析

5.3.1 LED 矩阵驱动:消除鬼影

void pico_multi_led_control(uint8_t led_num) {
// 1. 放电:所有引脚输出低电平,持续3μs
for (int i = 0; i < 4; i++) {
gpio_set_dir(led_pins[i], GPIO_OUT);
gpio_put(led_pins[i], 0);
}
busy_wait_us(3);
// 2. 高阻:所有引脚设为输入,禁用上下拉
for (int i = 0; i < 4; i++) {
gpio_set_dir(led_pins[i], GPIO_IN);
gpio_disable_pulls(led_pins[i]);
}
// 3. 目标驱动:仅设置需要点亮的两个引脚为输出并赋予正确电平
switch(led_num) {
case LED3: // IO1高, IO2低
gpio_set_dir(LED_IO1, GPIO_OUT); gpio_put(LED_IO1, 1);
gpio_set_dir(LED_IO2, GPIO_OUT); gpio_put(LED_IO2, 0);
break;
// ... 其他LED类似
default: break;
}
}

该函数每次点亮单个LED前,先强制将所有引脚放电,消除寄生电容上的电荷;然后全部设为高阻,避免漏电;最后只激活目标引脚。配合2ms的扫描周期,8颗LED轮询点亮,视觉上形成连续亮度。

5.3.2 WS2812 优化时序

static inline void ws2812_send_bit(bool bit) {
if (bit) {
gpio_put(WS2812_PIN, 1); busy_wait_us_32(1); // T1H ≈1μs
gpio_put(WS2812_PIN, 0); busy_wait_us_32(1); // T1L ≈1μs
} else {
gpio_put(WS2812_PIN, 1);
busy_wait_us_32(0); // 四个连续延时,累计约0.5μs
busy_wait_us_32(0);
busy_wait_us_32(0);
busy_wait_us_32(0);
gpio_put(WS2812_PIN, 0);
busy_wait_us_32(1); // T0L ≈1μs
}
}

通过将0码的高电平延长至四个极短延时,使其接近0.5μs,更符合WS2812的时序要求,同时避免因指令执行时间波动导致的误码。此外,在初始化时调用 gpio_set_drive_strength(WS2812_PIN, GPIO_DRIVE_STRENGTH_12MA),提高信号驱动能力。

5.3.3 按键消抖与长按短按处理

void process_keys(void) {
uint16_t adc = read_adc_filtered();
uint8_t key_mask = detect_key(adc);
for (int i=0; i<4; i++) {
uint8_t bit = 1<<i;
bool pressed = (key_mask & bit) != 0;
bool was_pressed = (last_key_mask & bit) != 0;
if (pressed && !was_pressed) { // 按下瞬间
key_down_time[i] = current_time;
key_pressed[i] = true;
key_long_press_handled[i] = false;
}
else if (pressed && was_pressed) { // 保持按下
if (!key_long_press_handled[i] &&
(current_time - key_down_time[i] >= LONG_PRESS_TIME_MS)) {
// 长按触发
key_long_press_handled[i] = true;
execute_long_press_action(i);
}
}
else if (!pressed && was_pressed) { // 释放
if (!key_long_press_handled[i] &&
(current_time - key_down_time[i] >= DEBOUNCE_TIME_MS)) {
// 短按触发
execute_short_press_action(i);
}
key_pressed[i] = false;
}
}
last_key_mask = key_mask;
}

该状态机准确区分按下、保持和释放,利用系统滴答计时器记录时间,实现可配置的消抖(30ms)和长按阈值(2s)。同时,每个按键独立维护状态,互不干扰。

6. 功能展示图

由于显示系统采用了视觉暂留效应,八颗单色 LED 通过高速扫描实现多颗 LED 的视觉常亮。然而,拍摄设备的帧率、曝光时间及卷帘快门特性无法与扫描周期同步,导致采集到的图像中 LED 点亮状态不完整或亮度分布不均。同时,WS2812 RGB LED 的亮度调节依赖于脉冲宽度调制,相机传感器在积分时间内可能仅捕获部分调制周期,造成记录亮度与实际感知存在偏差。经实物验证,人眼观察下所有显示功能均正常,上述现象仅为拍摄过程中的采样失真所致。

通电:

c21db8c5da98bd2141aa4d784ad8d662.jpg

正计时、计时开始:

正计时.png开始.png6e59b447a31b5ceb8374aa213dcc4132.jpg

计时暂停:

暂停.png

32e278af253b13ab5a07b463666c5873.jpg

计时清零:

清零.png6602e108e880d75c44d52ed236c36fe9.jpg

倒计时:

倒计时.png

ws2812变为黄色:


3eb050dd5fe59655981fed18bcc01fbe.jpg

随机数:

随机数.png

ws2812变为蓝色:

7ca693bee6cda4ae8c3918e0071edbb8.jpg

随机数计算次数与清零:

随机数计次与归零.png

单个按键切换模式:

按键切换模式.png

备用拨码开关(未赋予其实际功能)识别:

备用拨码开关识别.png

按键消抖:

消抖.png

长按短按区分示例:

屏幕截图 2026-02-28 161422.png

屏幕截图 2026-02-28 161517.png

7. 项目中遇到的难题及解决方法

7.1 LED 矩阵的干扰(微亮/串扰)

问题描述:当快速切换点亮不同的LED时,某些本该熄灭的LED会微微发亮,导致显示模糊。这是由于IO引脚在切换过程中,寄生电容上的残留电荷通过LED形成短暂回路。

解决方法:采用“放电 + 高阻 + 目标驱动”三步法。每次点亮前,先将所有控制引脚强制输出低电平并保持3μs,释放电荷;然后将所有引脚设为输入高阻,切断所有可能的漏电路径;最后只将需要点亮的两个引脚设为输出并赋予正确电平。经过测试,此方法彻底消除了鬼影,即使在高亮度下也无任何串扰。

7.2 WS2812 闪绿现象

问题描述:在部分板卡上,WS2812在显示红色或蓝色时会随机闪烁绿色,严重影响视觉效果。分析认为是0码的高电平时间过短或受干扰,导致数据接收错误。

解决方法:重新校准时序,将0码的高电平通过四个 busy_wait_us_32(0) 延长至约0.5μs(原代码中直接 busy_wait_us_32(0) 可能只有几十纳秒),并确保低电平维持在1μs左右。同时,通过 gpio_set_drive_strength 将引脚驱动能力提升至12mA,使信号边沿更陡峭,抗干扰能力增强。优化后,无论颜色如何变化,LED均稳定显示。

7.3 ADC 多路复用输入的准确识别

问题描述:8个输入(4按键+4拨码开关)共用同一个ADC通道,分压网络设计使得每个输入对应的电压范围可能重叠,且易受电源噪声影响,导致误触发或无法识别。

解决方法:

阈值校准:实际测量每个按键/开关单独按下时的ADC值,并取安全范围(例如 KEY0: 0xD90~0xDF0)。代码中保留这些实测值,避免了理论计算的偏差。

软件滤波:每次读取ADC时进行4次采样取平均,降低瞬时噪声。

消抖处理:按键采用30ms持续稳定判定,拨码开关采用连续5次采样一致判定,避免抖动引起的误动作。

分离检测:根据ADC值大小范围,先判断是按键区域还是开关区域(例如 >0xE00 为无操作,0xE00~0xEB0 为开关区域,<0xE00 为按键区域),再进一步细分,减少了相互干扰。

7.4 多任务实时性与资源占用

问题描述:如果使用 sleep_ms() 或忙等待,会导致其他任务响应延迟。例如数码管动态扫描需要10ms刷新,若此时进行长按键处理(2秒),数码管会闪烁。

解决方法:采用时间片轮询架构,所有任务均非阻塞执行。主循环中记录每个任务上次执行的时间,通过 if (now - last_time >= interval) 判断是否执行。这样,即使某个任务耗时较长(如打印日志),也不会影响其他任务的周期。同时,将短延时(如WS2812的比特发送)用 busy_wait_us_32 精确控制,确保时序准确。

7.5 74HC595 驱动时序的稳定性

问题描述:数码管在显示过程中偶有闪烁或乱码,原因是SPI时序中时钟边沿与数据变化配合不佳。

解决方法:严格按照74HC595数据手册,在SCLK上升沿前确保SER数据已稳定,并在SCLK高电平期间保持。代码中先设置SER,再拉高SCLK,延时1μs后拉低,完成一个移位。所有16位数据移完后,再产生RCLK上升沿锁存输出。通过调整延时确保足够建立时间,数码管显示稳定无闪烁。

8. 心得体会

通过本次 STEP-RP2350B 多功能计时器项目的开发,我深刻认识到嵌入式系统设计中软硬件协同的重要性。针对 LED 矩阵的鬼影问题,我通过分析电路原理,采用“放电 + 高阻 + 目标驱动”的驱动序列,有效解决了电荷残留导致的微亮现象。在 WS2812 时序优化中,则通过微调延时并增强引脚驱动能力,消除了高速环境下的信号干扰。项目实践表明,状态机与时间片轮询是实现裸机多任务调度的可靠方法,确保了系统实时性。借助 Raspberry Pi Pico SDK 及 VS Code 开发环境,可以显著提升开发效率,但同时也需注意根据硬件特性调整默认参数。此外,完善的文档与注释对于代码维护与协作也至关重要。此次项目不仅提升了嵌入式编程技能,更培养了系统分析与问题解决能力,为后续探索 RP2350 的高级特性奠定了基础。

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