用STM32F103C8T6和0.96寸OLED,我花3周复刻了一个带24秒违例的篮球计分器(附完整代码)
2026/6/9 11:37:48 网站建设 项目流程

从零打造STM32篮球计分器:硬件选型、代码解析与实战避坑指南

篮球计分器作为嵌入式开发的经典练手项目,既能巩固STM32基础知识,又能培养完整的产品思维。本文将带你用STM32F103C8T6核心板和0.96寸OLED显示屏,实现一个支持24秒违例规则的专业级篮球计分系统。不同于常见的矩阵键盘方案,我们创新性地采用红外遥控作为输入设备,仅需3个IO口就能实现21个按键功能,大幅降低硬件复杂度。

1. 硬件架构设计与选型策略

1.1 核心器件选型对比

选择STM32F103C8T6(蓝桥杯开发板同款)作为主控,主要基于以下考量:

  • 72MHz主频足够处理计时和显示逻辑
  • 内置定时器资源丰富(TIM3用于倒计时,TIM4捕获红外信号)
  • 成本仅20元左右,学生党友好

显示模块选用0.96寸OLED(SSD1306驱动)而非LCD,优势明显:

特性OLEDLCD
可视角度170°120°
响应速度0.01ms10ms
功耗0.08W(全亮)0.5W
接口I2C/SPI并行
焊接难度4针16针

红外接收方案对比传统矩阵键盘:

// 红外接收仅需1个GPIO配置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; GPIO_Init(GPIOB, &GPIO_InitStructure);

1.2 硬件连接图与功耗优化

实际焊接时注意:

  • OLED的SCL接PA2,SDA接PA3
  • 红外接收器OUT脚接PB9
  • 核心板供电建议加装100μF电容滤波

实测整机工作电流:运行状态12mA,待机状态5mA(可通过关闭未用外设进一步优化)

2. 计时系统实现与中断管理

2.1 多层计时器架构设计

比赛计时需要三个时间维度协同工作:

  1. 节计时(12分钟)
  2. 24秒违例计时
  3. 百分秒精密计时
// 全局时间变量定义 u8 minute = 11, second=59, count=24; u16 ms=100; // 百分秒计数器

2.2 定时器中断配置要点

TIM3配置为10ms中断,通过预分频实现:

TIM_TimeBaseStructure.TIM_Period = 100; // 自动重装载值 TIM_TimeBaseStructure.TIM_Prescaler = 7199; // 72MHz/(7199+1)=10kHz TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

中断服务程序中实现时间递减逻辑:

void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { ms--; if(ms<1) { ms = 100; second--; if(second<0) { second = 59; minute--; } } } TIM_ClearITPendingBit(TIM3, TIM_IT_Update); }

关键点:在暂停状态需要关闭定时器中断,但保持计数器值不变

3. 红外遥控解码与按键优化

3.1 NEC协议解码流程

红外接收使用TIM4输入捕获功能,关键状态机逻辑:

  1. 检测4.5ms引导码
  2. 记录560us/1680us脉冲间隔
  3. 校验地址码和反码
  4. 处理连发码
if(Dval>4200&&Dval<4700) { // 引导码检测 RmtSta|=1<<7; RmtCnt=0; }

3.2 按键防抖与事件处理

原始代码存在的"一次按键多次触发"问题,通过增加状态判断解决:

if(keys&&RmtCnt==1) { // 仅处理首次按键 switch(keys) { case 194: // 开始/暂停键 ID = (ID%2)+1; // 状态切换 TIM_ITConfig(TIM3,TIM_IT_Update,ID==1?ENABLE:DISABLE); break; } }

按键功能映射表:

键值功能键值功能
162主队大比分+98客队大比分+
17624秒复位12214秒复位
82半场交换比分194开始/暂停

4. OLED显示优化与界面设计

4.1 多图层显示管理

采用分区域刷新策略降低闪烁:

  • 球队名称区(每节刷新)
  • 比分区(实时刷新)
  • 计时区(100ms刷新)
  • 24秒环(50ms刷新)
void LCD_time(void) { OLED_ShowNum(20-1,40,minute,2,24,1); OLED_ShowNum(56-1,40,second,2,24,1); OLED_ShowNum(90,47,ms,2,16,1); // 冒号闪烁效果 static u8 blink = 0; if(ID==1) OLED_ShowString(43-1,39,(blink^=1) ? ":" : " ",24,1); }

4.2 自定义字模制作

使用PCtoLCD2002生成球队名称字模:

  1. 选择16x16点阵
  2. 设置取模方式:逐列式、顺向
  3. 导出C语言数组格式
// 物理学院字模示例 const u8 PHYSICS_CHN[] = { 0x00,0x40,0x00,0x20,0x7F,0xFE... // 后续数据省略 };

5. 常见问题与性能优化

5.1 电源干扰解决方案

现象:遥控失灵或显示乱码

  • 在STM32的3.3V引脚加装0.1μF去耦电容
  • 红外接收器VCC串联100Ω电阻
  • 避免杜邦线过长(建议<15cm)

5.2 显示残影消除技巧

修改SSD1306驱动:

void OLED_Refresh(void) { OLED_Write_Cmd(0x21); // 设置列地址 OLED_Write_Cmd(0x00); OLED_Write_Cmd(0x7F); OLED_Write_Cmd(0x22); // 设置页地址 OLED_Write_Cmd(0x00); OLED_Write_Cmd(0x07); // 增加5ms延时 delay_ms(5); }

5.3 代码体积优化

通过以下方式将bin文件控制在32KB以内:

  1. 使用-O2优化等级
  2. 移除未用标准库函数
  3. 将字模数据存储在const区域

实际测试中各模块占用空间:

  • 红外解码:3.2KB
  • OLED驱动:2.8KB
  • 主逻辑:6.4KB
  • 字模数据:18KB

6. 项目扩展与进阶方向

6.1 无线升级功能实现

添加蓝牙模块(HC-05)实现:

  1. 通过串口IAP进行固件更新
  2. 手机APP远程控制计分器
  3. 比赛数据实时上传
// 串口接收中断中处理升级命令 if(RxBuff[0]==0xAA && RxBuff[1]==0x55) { JumpToBootloader(); }

6.2 比赛数据记录功能

扩展SD卡存储:

  • 每节比分变化时间戳
  • 24秒违例统计
  • 球队暂停次数记录

文件存储格式示例:

[Q1] 10:32 A+1 10:15 B+2 [Q2] 08:45 Timeout_A [Q3] 05:21 24s_Violation_B

6.3 低功耗设计方案

针对电池供电场景优化:

  1. 启用STM32的Stop模式(电流<1mA)
  2. OLED动态刷新率调整
  3. 红外唤醒功能实现
void Enter_LowPower(void) { OLED_DisplayTurn(0); // 关闭显示 TIM_Cmd(TIM3,DISABLE); PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); }

在完成基础功能后,尝试增加音效提示功能(使用无源蜂鸣器),当24秒违例或节末时发出不同频率的提示音。实际调试中发现,直接使用延时函数控制蜂鸣器会导致显示卡顿,最终改用PWM+DMA方案解决。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询