1所选任务介绍
我选择的任务是基于RP2350B核心板LED与数码管多功能计时器,该项目的要求是使用4个按键实现计时开始、暂停、清零与模式切换,要求按键消抖与长按短按区分。通过4个拨码开关选择三种工作模式:正计时、倒计时、计次随机数显示。双位七段数码管(74HC595驱动)显示当前数值,8颗单色LED显示进度条,2颗WS2812显示当前模式颜色。并通过USB虚拟串口输出模式切换与数值变化日志。
2项目介绍及成品说明书
2.1功能简介
本系统基于RP2350微控制器设计,实现计时控制与状态显示功能。
设备通过ADC7电阻网络实现多按键与拨码开关的复用输入,支持启动、暂停、复位以及模式配置等操作。
系统提供三种运行模式:
正计时模式(UP):时间递增计数
倒计时模式(DOWN):从预置值递减至零
随机模式(RAND):周期生成随机数
当前数值通过两位数码管动态扫描显示,同时利用Charlieplex结构的8段LED以进度条形式反映当前百分比。
此外,系统使用两颗WS2812 RGB LED作为模式指示灯,不同模式对应不同颜色,在配置模式下显示白色提示。
系统支持按键短按与长按识别,并通过串口输出运行状态,便于调试与监控。
2.2系统框图

2.3设计思路
系统采用“定时扫描+事件驱动”的结构。
在1ms定时器中完成数码管动态刷新与进度条扫描,同时每5ms对ADC输入进行采样,实现按键消抖与拨码累积。主循环负责处理按键事件、模式切换以及计时逻辑,从而保证显示稳定与响应可靠。
WS2812采用原子发送方式,仅在模式变化时更新颜色,避免对实时任务造成干扰。该设计实现了输入、显示与控制的分离,提高了系统的稳定性与可扩展性。
3使用到的硬件介绍
3.1 RP2350B核心板
本次使用的板件是以树莓派rp2350为核心,具有48个io引脚,可以实现多种控制功能,板子上还配备了四个按键、四个拨码开关,2个ws2812rgb灯,8个led灯,两个7段数码管,一个姿态传感器。板件通过短接BOOT和gnd进入bootsel模式,在电脑上拖入.uf2文件实现烧录。
3.2数据线
USB A to C数据线,注意不能是单纯的充电线。
4编程语言说明及软件介绍
4.1编程语言及平台
本项目使用了C语言和Ubnutu平台进行编程,采用比较基础的命令行形式。确保了电脑和板件的稳定链接。
4.2软件流程图

4.3关键代码及难点介绍
4.3.1
根据电路图,按键和拨码开关共用ADC7,需要避免相互干扰,按键是离散电阻分压,拨码的电压范围又很接近;如果同时采样,会把按键按下时的电压误当成拨码状态,造成模式乱跳。
解决策略:
先判按键:ADC值落入按键区间就认为有键按下;
仅在没有按键按下时累计拨码采样:避免按键电压进入拨码滤波;
拨码再做时间稳定确认(连续一致约1秒)才接受切换。
if ((g_ms % SCAN_MS) == 0) {
uint16_t a = adc_read();
rng_mix(a);
key_id_t k = adc_to_key(a);
key_logic_scan_5ms(&gk, k);
if (k == KEY_NONE) {
dip_sum += a;
dip_cnt++;
}
}
return true;
4.3.2按键消抖+长短按逻辑
ADC采样受噪声影响,电压在阈值附近会跳动;若不消抖,会出现一次按下触发多次事件。
解决方式:5ms扫描一次;
连续一致达到20ms才认为稳定;
按下累计时间达到800ms触发长按;
事件采用evt_key + evt_type单次上报,主循环消费后清零。
static void key_logic_scan_5ms(key_logic_t *k, key_id_t sample_key) {
if (sample_key == k->last_key) {
if (k->stable_cnt < (DEBOUNCE_MS/SCAN_MS)) k->stable_cnt++;
} else {
k->last_key = sample_key;
k->stable_cnt = 0;
}
key_id_t new_stable = k->stable_key;
if (k->stable_cnt >= (DEBOUNCE_MS/SCAN_MS)) new_stable = k->last_key;
if (new_stable != k->stable_key) {
key_id_t old = k->stable_key;
k->stable_key = new_stable;
if (old == KEY_NONE && new_stable != KEY_NONE) {
k->pressing = true;
k->press_ms = 0;
k->long_fired = false;
} else if (old != KEY_NONE && new_stable == KEY_NONE) {
if (k->pressing && !k->long_fired) {
k->evt_key = old;
k->evt_type = EVT_SHORT;
}
k->pressing = false;
k->press_ms = 0;
} else {
if (k->pressing && !k->long_fired) {
k->evt_key = old;
k->evt_type = EVT_SHORT;
}
k->pressing = true;
k->press_ms = 0;
k->long_fired = false;
}
}
if (k->pressing && !k->long_fired && k->stable_key != KEY_NONE) {
k->press_ms += SCAN_MS;
if (k->press_ms >= LONGPRESS_MS) {
k->evt_key = k->stable_key;
k->evt_type = EVT_LONG;
k->long_fired = true;
}
}
}
4.3.3进度条微亮/串亮/顺序混乱
需要将未使用的线保持高阻输入,否则会形成寄生电流导致微亮。
另外LED引脚切换时若残余电荷不释放,也会出现“阴影”。
先把所有线拉低输出放电(2~5us)
再把所有线切为输入高阻,禁用上下拉
最后仅设置目标hi/lo为输出并驱动
板子上灯的物理位置和电路图灯的位置相对应也需要肉眼辨识确认后修改
static const led_pair_t led_table[8] = {
{24,26}, // id0 = LED#1 (bottom) IO4 -> IO2
{26,24}, // id1 = LED#2 IO2 -> IO4
{24,25}, // id2 = LED#3 IO4 -> IO3
{25,26}, // id3 = LED#4 IO3 -> IO2
{26,27}, // id4 = LED#5 IO2 -> IO1
{25,24}, // id5 = LED#6 IO3 -> IO4
{26,25}, // id6 = LED#7 IO2 -> IO3
{27,26}, // id7 = LED#8 (top) IO1 -> IO2
};
static inline void charlie_off(void) {
for (int i = 0; i < 4; i++) {
gpio_set_dir(CPLX_PINS[i], GPIO_OUT);
gpio_put(CPLX_PINS[i], 0);
}
sleep_us(5);
}
static inline void charlie_on(int id) {
// 放电/消影,全低
for (int i = 0; i < 4; i++) {
gpio_set_dir(CPLX_PINS[i], GPIO_OUT);
gpio_put(CPLX_PINS[i], 0);
}
sleep_us(5);
// 其他线高阻隔离
for (int i = 0; i < 4; i++) {
gpio_set_dir(CPLX_PINS[i], GPIO_IN);
gpio_disable_pulls(CPLX_PINS[i]);
}
int hi = led_table[id].hi;
int lo = led_table[id].lo;
gpio_set_dir(hi, GPIO_OUT); gpio_put(hi, 1);
gpio_set_dir(lo, GPIO_OUT); gpio_put(lo, 0);
}
4.3.4点亮ws2812
其他所有功能都正常,唯独rgb灯不亮
原因A:使用了PIO版ws2812驱动,但该板/该灯对该波形不兼容
我们尝试过PIO时序头文件缺失、pio文件缺失、不同T1/T2/T3版本,但最终发现:
bit-bang +原子发送(关中断)稳定可用。
原因B:发送WS2812数据时被1ms定时器/USB等中断打断,波形被破坏
WS2812对时序非常敏感,发送期间必须确保不被打断。
static void ws2812_show_color(uint8_t r, uint8_t g, uint8_t b) {
// Sending must be atomic (no timer/USB interrupts)
uint32_t irq = save_and_disable_interrupts();
for (int i = 0; i < WS2812_COUNT; i++) {
ws_send_pixel(r, g, b);
}
restore_interrupts(irq);
sleep_us(80); // reset latch > 50us
}
5功能展示
5.1功能说明
由于按键和拨码开关都要求添加模式切换功能。因此,设置k4短按直接按顺序切换功能,顺序为正计时,倒计时,随机数,长按进入拨码开关控制模式,拨动拨码1为正计时,拨动拨码2或3为倒计时,拨动拨码4为随机数。
项目要求的三种功能:正计时、倒计时、随机数,两颗ws2812 RGB灯分别显示为绿色、红色和蓝色。进入拨码开关选择模式和初次上电时,显示为白色。

四个按键功能:k1为开始,k2为暂停,k3为归零,k4为切换模式
5.2功能演示及图片
5.2.1正计时
正计时模式下,数码管依次显示00-99,每秒变化一次,rgb灯显示为绿色,并且,系统通过LED指示当前数值所对应的进度
数值越大,点亮的LED数量越多。


当数值归零时,所有LED熄灭。
5.2.2倒计时
倒计时模式时,rgb灯显示为红色,通过长按k3切换30秒和60秒倒计时,同时也配有led进度条显示。


5.2.3随机数
随机数模式时,rgb灯显示为蓝色,两个数码管随机生成0-99的数字


5.2.4串口输出
所有输出和模式切换均在usb串口输出,可以打开串口助手查看当前模式和当前数码管显示的数字。

6心得体会
通过本次基于RP2350的项目,我完成了按键识别、拨码检测、数码管显示、LED进度条以及WS2812模式指示等多个功能模块的设计与调试。开发过程中,最大的收获是学会了利用定时扫描实现多任务并行,以及在硬件驱动中通过逐步验证、最小系统排错来定位问题。尤其是在RGB灯调试阶段,从无法点亮到成功运行,让我深刻理解了时序和底层控制的重要性。本次项目显著提升了我软硬件结合与系统调试的能力。