基于STM32与Proteus的篮球计时记分器:HAL库驱动与仿真实践
2026/9/5 6:35:24 网站建设 项目流程

简介:这是一份面向嵌入式初学者与高校课程设计学生的STM32实战项目资源,完整实现篮球比赛专用计时记分功能,覆盖LCD动态显示、矩阵按键扫描、HAL库定时器控制、蜂鸣器与LED声光反馈等核心知识点。资源包共183个文件,含24个C源文件(如stm32f1xx_hal_tim.c等外设驱动)、53个头文件(h)、26个编译中间文件(o)及工程配置文件(ioc、uvprojx、uvoptx等),结构清晰,适配Keil MDK开发环境与Proteus仿真平台。7.7MB压缩包已提供可直接编译运行的完整工程,包含本节倒计时、进攻时限双时间显示、两队比分实时更新、1/2/3分按键逻辑及得分声光提示等全部功能模块。目前已有2623人学习下载,配套代码注释详尽、模块划分合理,特别适合单片机课程设计、电赛备赛及HAL库入门实践参考。

1. 项目概述与核心价值

最近在整理一些嵌入式课程设计的资料,发现很多同学对STM32的综合应用项目很感兴趣,但又苦于硬件成本或调试难度。正好,我之前用STM32F103C8T6为核心,结合LCD1602和矩阵键盘,完整地实现过一个篮球比赛的计时记分器。这个项目麻雀虽小,五脏俱全,涵盖了GPIO控制、定时器中断、按键扫描、LCD驱动、状态机设计等多个嵌入式开发的核心知识点。更重要的是,我不仅完成了Keil MDK下的HAL库代码编写,还同步搭建了Proteus仿真环境。这意味着,即使你手头没有开发板和元器件,也能在电脑上完整地运行、调试和观察整个系统的行为,非常适合学习和验证。

这个篮球计时记分器能做什么?简单说,它模拟了一场篮球比赛的核心计时与计分功能。系统可以设置比赛总时长(如4节,每节10分钟),实现启动、暂停、复位等计时控制;同时能为A、B两队分别计分,支持1分、2分、3分等不同分值的加分操作;所有信息都实时显示在LCD屏幕上。整个交互通过一个4x4的矩阵键盘完成,成本低廉,逻辑清晰。对于初学者而言,这是一个绝佳的从理论到实践的桥梁项目。你不仅能学到如何用HAL库快速搭建工程,还能深入理解如何将具体的业务逻辑(篮球规则)转化为单片机中可靠运行的代码,更可以通过Proteus仿真直观地看到程序如何“驱动”虚拟硬件,这种“软硬结合”的体验对理解嵌入式系统至关重要。

2. 系统整体设计与方案选型

2.1 核心功能需求与设计思路

在设计之初,我首先梳理了篮球比赛计时记分器的核心需求。它不是一个简单的秒表,而是一个具有特定规则的状态机。主要功能点包括:

  1. 比赛计时:支持设置比赛节数、每节时间。计时器需要能启动、暂停、继续,并在每节结束时给出提示(如蜂鸣器响,仿真中用LED代替)。
  2. 队伍计分:为A、B两队独立计分,计分范围通常为0-999。需要提供便捷的加分操作,如+1分(罚球)、+2分(两分球)、+3分(三分球)。
  3. 信息显示:在有限的LCD1602(16字符x2行)屏幕上,清晰、实时地展示比赛时间、节次、两队比分等关键信息。
  4. 用户交互:通过一个4x4矩阵键盘,实现所有功能的控制,包括计时控制、分数修改、菜单设置等。

基于这些需求,我的设计思路是采用“状态机(State Machine)”作为程序的核心框架。整个系统会处于不同的状态,例如“计时运行”、“计时暂停”、“设置模式”等。按键事件会触发状态迁移,并执行相应的动作(如修改时间、增加分数、刷新显示)。这种设计将复杂的业务逻辑分解为离散的状态和明确的转移条件,使得程序结构清晰,易于维护和调试。

2.2 硬件方案选型与考量

硬件选型主要围绕成本、易得性和教学演示目的展开。

  • 主控MCUSTM32F103C8T6(蓝桥杯/核心板常用型号)。选择它是因为其资源足够丰富(72MHz Cortex-M3,64KB Flash,20KB RAM),性价比极高,且HAL库支持完善,资料众多。对于本项目,其GPIO、定时器资源绰绰有余。
  • 显示模块LCD1602字符液晶。虽然像素点阵屏显示效果更炫酷,但LCD1602驱动简单,无需字库,显示字符和数字非常方便,且其并行8位或4位接口模式是学习单片机外设控制的经典案例。在有限的显示面积内,需要通过精心设计显示格式来呈现所有信息。
  • 输入模块4x4矩阵键盘。这是实现16个独立按键功能而只占用8个GPIO口的最佳方案。相比独立按键,它极大地节省了IO资源,其行列扫描原理本身也是一个重要的编程练习点。
  • 仿真环境Proteus 8 Professional。Proteus强大的单片机仿真能力和丰富的元件库,使得在无实物情况下进行系统级调试成为可能。我可以在Proteus中搭建出包含STM32、LCD、键盘、LED、蜂鸣器的完整电路,并加载编译好的Hex文件直接运行,观察波形和器件状态,这对理解硬件工作原理和排查硬件连接错误有巨大帮助。
  • 开发环境Keil MDK-ARM (uVision 5)+STM32CubeMX。这是STM32开发最主流的组合之一。STM32CubeMX用于图形化配置引脚、时钟、外设(如定时器),并生成基于HAL库的初始化代码框架,能大幅降低底层配置的复杂度,让开发者更专注于应用逻辑。Keil则用于代码编写、编译和调试。

注意:选择HAL库而非标准库或LL库,主要是出于教学和快速开发的考虑。HAL库抽象程度高,函数接口统一,跨STM32系列移植性好,虽然效率稍低且代码体积略大,但对于本项目而言完全可接受,且更能让初学者聚焦于功能实现而非底层寄存器操作。

3. 核心模块驱动与实现细节

3.1 HAL库工程创建与基础配置

一切从STM32CubeMX开始。新建一个工程,选择STM32F103C8Tx。

  1. 时钟配置:在RCC中,将高速外部时钟HSE设置为Crystal/Ceramic Resonator。在Clock Configuration标签页,将系统时钟源选为PLLCLK,并配置倍频,使系统时钟SYSCLK达到72MHz。这是STM32F103的典型最高主频,能保证定时器计时的准确性。
  2. GPIO配置
    • LCD1602:需要至少7个引脚(如果使用4位数据模式则是11个)。我选择了4位数据模式以节省IO。配置PB0-PB3GPIO_Output,作为LCD的DB4-DB7PB4(RS)、PB5(RW)、PB6(E)也配置为GPIO_Output。推挽输出,无上拉。
    • 矩阵键盘:需要8个引脚。将PA0-PA3配置为GPIO_Output(推挽输出),作为行线(Row);将PA4-PA7配置为GPIO_Input(上拉输入模式),作为列线(Column)。当行线输出低电平时,如果某个按键被按下,对应的列线就会被拉低,从而被检测到。
    • 状态指示LED:例如用PC13(核心板常带LED)连接一个LED,配置为输出,用于指示计时状态(常亮运行/闪烁暂停)。
  3. 定时器配置:配置一个基本定时器(如TIM2)用于系统时基。设置预分频器PSC和自动重载值ARR,使其产生一个10ms的中断。这个10ms的时基是整个系统的时间“心跳”,用于按键扫描消抖、计时器递减、显示刷新等周期性任务。在NVIC设置中使能TIM2的中断。
  4. 生成工程:在Project Manager中,选择Toolchain/IDEMDK-ARM V5,设置好工程路径和名称。在Code Generator中,选择“为每个外设生成独立的.c/.h文件”,这样代码结构更清晰。最后点击GENERATE CODE,生成Keil工程。

3.2 LCD1602驱动代码编写(4位模式)

CubeMX生成了引脚和时钟的初始化代码,但LCD的驱动函数需要我们自己实现。在生成的lcd.clcd.h文件中添加以下核心函数。

首先定义好引脚映射,方便操作:

// lcd.h 中的宏定义 #define LCD_RS_PIN GPIO_PIN_4 #define LCD_RS_PORT GPIOB #define LCD_RW_PIN GPIO_PIN_5 #define LCD_RW_PORT GPIOB #define LCD_E_PIN GPIO_PIN_6 #define LCD_E_PORT GPIOB #define LCD_D4_PIN GPIO_PIN_0 #define LCD_D4_PORT GPIOB #define LCD_D5_PIN GPIO_PIN_1 #define LCD_D5_PORT GPIOB #define LCD_D6_PIN GPIO_PIN_2 #define LCD_D6_PORT GPIOB #define LCD_D7_PIN GPIO_PIN_3 #define LCD_D7_PORT GPIOB

关键函数解析:

  1. 写命令/数据函数:这是最底层的函数,负责将4位数据分两次(高4位、低4位)送到LCD总线,并模拟使能信号E的脉冲。
    void LCD_Write4Bits(uint8_t data) { // 根据data的位,设置D4-D7引脚电平 HAL_GPIO_WritePin(LCD_D4_PORT, LCD_D4_PIN, (data & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_D5_PORT, LCD_D5_PIN, (data & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_D6_PORT, LCD_D6_PIN, (data & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_D7_PORT, LCD_D7_PIN, (data & 0x08) ? GPIO_PIN_SET : GPIO_PIN_RESET); // 产生使能脉冲 HAL_GPIO_WritePin(LCD_E_PORT, LCD_E_PIN, GPIO_PIN_SET); HAL_Delay(1); // 短暂延时,保持E高电平 HAL_GPIO_WritePin(LCD_E_PORT, LCD_E_PIN, GPIO_PIN_RESET); } void LCD_Send(uint8_t data, uint8_t mode) { // mode: 0 for command, 1 for data HAL_GPIO_WritePin(LCD_RS_PORT, LCD_RS_PIN, mode ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(LCD_RW_PORT, LCD_RW_PIN, GPIO_PIN_RESET); // 始终写操作 LCD_Write4Bits(data >> 4); // 发送高4位 LCD_Write4Bits(data & 0x0F); // 发送低4位 // 检查忙标志的代码此处省略,通常用延时替代 HAL_Delay(1); }
  2. 初始化序列:LCD1602的4位模式初始化有固定的指令序列,必须严格按照时序进行。包括上电延时、功能设置(设置数据位数、显示行数、字体)、显示开关控制、清屏、进入模式设置等。
  3. 显示字符串和数字:基于LCD_Send函数,实现LCD_PrintStringLCD_PrintNumber。显示数字时,需要将整数转换为字符数组。为了在固定位置显示,还需要实现LCD_SetCursor函数来设置显示地址。

实操心得:LCD1602的初始化时序要求比较严格,延时不够可能导致初始化失败,屏幕显示乱码或完全不亮。如果仿真或实物中LCD不工作,第一步就是检查初始化代码的延时是否足够,并确认发送的指令序列是否正确。在Proteus仿真中,可以右键点击LCD元件,选择“属性”查看其接收到的指令,这是一个非常强大的调试手段。

3.3 矩阵键盘扫描实现

矩阵键盘的驱动核心是“行列扫描法”。我们的配置是PA0-PA3为输出(行),PA4-PA7为输入上拉(列)。在定时器中断服务函数中,每隔10ms调用一次键盘扫描函数。

扫描逻辑如下:

  1. 将所有的行线(PA0-PA3)置为低电平。
  2. 依次将其中一行置为低电平,其他行置为高电平(或输入模式)。
  3. 读取所有列线(PA4-PA7)的状态。
  4. 如果某一列为低电平(因为按键按下,将该列通过按键连接到低电平的行),则说明当前扫描行和该列的交点处按键被按下。
  5. 结合当前扫描的行号和检测到的列号,通过一个预定义的二维映射表key_map[4][4],即可得到对应的键值(如‘0’-‘9’, ‘A’-‘D’等)。

为了消除按键抖动,需要在检测到按键后,进行延时再确认,或者使用状态机进行消抖。我通常采用“两次检测法”:在本次扫描中检测到按键,记录键值;在下一个扫描周期(10ms后)再次检测,如果仍然是同一个键被按下,则认为按键有效,并设置一个按键标志位供主程序查询。

// 简化的扫描函数片段 uint8_t KEY_Scan(void) { static uint8_t last_key = 0xFF; static uint8_t debounce_cnt = 0; uint8_t current_key = 0xFF; // 扫描逻辑,获取当前按下的键值 current_key // ... if(current_key != 0xFF) { if(current_key == last_key) { debounce_cnt++; if(debounce_cnt >= 2) { // 连续2次(20ms)检测到同一按键,消抖成功 debounce_cnt = 0; last_key = 0xFF; return current_key; // 返回有效的键值 } } else { last_key = current_key; debounce_cnt = 0; } } else { last_key = 0xFF; debounce_cnt = 0; } return 0xFF; // 无有效按键 }

3.4 定时器中断与系统时基

我们配置的TIM2每10ms产生一次中断。在中断服务函数HAL_TIM_PeriodElapsedCallback中,我们需要完成几件关键事情:

  1. 调用键盘扫描函数:以固定的频率检测按键。
  2. 更新系统时钟:维护一个system_tick变量,每中断一次加一。这个变量可以作为其他需要定时任务的基准,例如实现非阻塞的延时。
  3. 处理计时逻辑:如果比赛计时器处于运行状态,则每100次中断(即1秒)将比赛剩余时间减1秒,并判断是否节次结束、比赛结束。
  4. 处理显示刷新:可以设置一个显示刷新标志,在主循环中根据此标志更新LCD,避免在中断中执行耗时的LCD操作。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM2) { system_tick++; // 1. 按键扫描 key_value = KEY_Scan(); if(key_value != 0xFF) { key_pressed_flag = 1; // 设置按键事件标志 } // 2. 比赛计时处理 if(game_state == GAME_RUNNING) { if(++tick_counter >= 100) { // 10ms * 100 = 1s tick_counter = 0; game_time_seconds--; if(game_time_seconds < 0) { // 一节时间到,处理节次切换等逻辑 } display_update_flag = 1; // 请求更新显示 } } } }

4. 应用层逻辑与状态机设计

4.1 比赛状态机建模

这是项目的业务核心。我将整个系统划分为几个主要状态:

  • MENU:菜单状态,用于设置比赛节数、每节时间等参数。
  • READY:准备状态,计时器复位,显示初始时间和比分,等待开始。
  • RUNNING:比赛运行状态,计时器递减,可以暂停和计分。
  • PAUSED:比赛暂停状态,计时停止,仍可计分(用于死球情况)。
  • BREAK:节间休息状态(如果实现)。
  • END:比赛结束状态。

状态迁移由按键事件触发。例如,在READY状态下按下“开始”键,进入RUNNING状态;在RUNNING状态下按下“暂停”键,进入PAUSED状态;在PAUSED状态下再次按下“开始”键,则返回RUNNING状态。

4.2 计分与计时数据结构

定义清晰的数据结构来管理所有比赛数据:

typedef struct { uint8_t period_total; // 总节数 uint8_t period_current; // 当前节次 uint16_t period_length_sec; // 每节时长(秒) int32_t time_remaining_sec; // 当前节剩余时间(秒) uint16_t score_team_a; // A队得分 uint16_t score_team_b; // B队得分 uint8_t game_state; // 当前游戏状态 } GameControl_t; GameControl_t game;

计时处理需要注意时间格式的转换。我们内部以秒为单位存储,但显示时需要转换为“MM:SS”的格式。计分操作则根据按下的不同分值键(如‘1’, ‘2’, ‘3’),为选中的队伍增加对应分数,并确保分数不超过显示范围(如999)。

4.3 显示界面布局优化

在16x2的LCD上显示所有信息是一个挑战。我的布局方案如下:

  • 第一行P[节次] [剩余时间]
    • 例如:P1 09:45
  • 第二行A[比分]:[比分]B
    • 例如:A 012:024 B
    • 或者更紧凑:A012 B024

在设置模式下,显示内容会变化,例如显示Set Period: 04。通过一个display_update()函数,根据当前game_stategame数据,刷新对应的显示内容。

5. Proteus仿真电路搭建与调试

5.1 元件选取与电路连接

在Proteus中新建工程,选取以下关键元件:

  • 单片机STM32F103C8(在Proteus的ARM Cortex-M3分类下)。
  • 显示屏LM016L(这是Proteus中LCD1602的模型)。
  • 键盘KEYPAD-PHONE(一个4x4矩阵键盘模型,或者用BUTTON自己搭建)。
  • 电阻、LED、蜂鸣器:用于状态指示和提示音仿真。

连接电路:

  1. STM32与LCD:根据代码中的引脚定义,将PB0-PB3连接到LCD的D4-D7,PB4、PB5、PB6分别连接到RS、RW、E。LCD的VSS接地,VDD接5V,VEE(对比度调节)通过一个电位器接VDD和GND。
  2. STM32与键盘:将PA0-PA3连接到键盘的行线(Row1-Row4),将PA4-PA7连接到键盘的列线(Col1-Col4)。注意,Proteus中的键盘模型可能行列顺序与代码假设相反,需要根据实际情况调整扫描逻辑或连线。
  3. 指示电路:将PC13连接一个LED(串联一个220Ω电阻到地),用于视觉指示。可以再连接一个蜂鸣器(驱动电路需要三极管)到另一个GPIO,用于节末提示。

5.2 仿真配置与程序加载

  1. 双击Proteus中的STM32元件,打开属性窗口。
  2. Program File一栏,选择由Keil编译生成的.hex文件(位于Keil工程目录下的Objects文件夹)。
  3. Clock Frequency中填入72MHz,与代码配置一致。
  4. 可以配置Debugging Tools,但基础仿真不需要。

5.3 仿真运行与问题排查

点击Proteus左下角的运行按钮,系统开始仿真。你可以点击虚拟键盘上的按钮,观察LCD显示的变化,以及LED的亮灭。

常见仿真问题与解决

  1. LCD不显示或乱码
    • 检查接线:确认RS、RW、E、数据线连接是否正确,VEE电位器是否调节到合适位置(影响对比度)。
    • 检查初始化代码:在Proteus中,右键LCD选择“属性”,查看其收到的指令序列。对比标准的LCD1602初始化指令,看是否有错误或遗漏。特别注意,Proteus中的LCD模型对时序要求可能比实物更宽松,但如果代码逻辑错误,依然不会显示。
    • 检查延时:尝试增加初始化指令之间的延时。
  2. 按键无反应
    • 检查行列连接:确认GPIO输入输出模式设置与Proteus连线是否匹配。Proteus中键盘的公共端(Common)通常需要接地。
    • 检查扫描逻辑:在Keil中开启软件仿真,单步调试KEY_Scan函数,观察行线输出和列线读取的值是否正确。或者在代码中,当检测到按键时,让一个LED闪烁,观察仿真中LED是否有反应。
    • 检查消抖逻辑:消抖时间过长可能导致反应迟钝,过短可能导致连击。调整消抖的检测周期数。
  3. 计时不准
    • 检查定时器配置:确认TIM2的时钟源、预分频值(PSC)和重载值(ARR)计算是否正确。系统时钟72MHz,若想产生10ms中断,计算公式为:定时器时钟 = 72MHz / (PSC+1),中断时间 =(ARR+1) / 定时器时钟。例如,PSC=7199,ARR=99,则定时器时钟=10kHz,中断周期=100/10kHz=0.01s=10ms。
    • 检查中断服务函数:确保没有在中断中执行过于耗时的操作,导致中断丢失。
  4. 程序运行一次后停止
    • 检查看门狗:STM32CubeMX默认可能开启了独立看门狗(IWDG)或窗口看门狗(WWDG)。如果应用代码中没有定期“喂狗”,看门狗会复位单片机。在仿真中,可以在CubeMX配置里禁用看门狗,或者在代码中正确喂狗。

避坑技巧:Proteus仿真是一个强大的验证工具,但它不能完全替代实物。仿真的时序是理想的,而实物电路存在信号边沿、干扰等实际问题。因此,仿真成功的代码为实物调试打下了坚实基础,但上板后仍需用逻辑分析仪或示波器检查关键信号(如LCD使能E脉冲、键盘扫描波形)是否符合器件手册要求。

6. Keil代码集成与功能测试

6.1 主程序逻辑框架

主函数main.c中的逻辑清晰简洁,遵循“初始化 -> 主循环”的经典嵌入式架构。

int main(void) { HAL_Init(); // HAL库初始化 SystemClock_Config(); // 系统时钟配置(由CubeMX生成) MX_GPIO_Init(); // GPIO初始化 MX_TIM2_Init(); // 定时器初始化 // ... 其他外设初始化 LCD_Init(); // LCD初始化 Game_Init(); // 游戏数据初始化 HAL_TIM_Base_Start_IT(&htim2); // 启动定时器中断 while (1) { // 1. 处理按键事件(非阻塞方式) if(key_pressed_flag) { key_pressed_flag = 0; Game_ProcessKey(key_value); // 根据当前状态处理按键 } // 2. 更新显示(根据需要) if(display_update_flag) { display_update_flag = 0; Display_Refresh(); // 根据当前状态刷新LCD显示 } // 3. 其他后台任务(如LED状态指示) LED_Blink_Task(); // 根据游戏状态控制LED闪烁模式 // 4. 空闲时进入低功耗模式(可选) // __WFI(); } }

Game_ProcessKey函数是状态机的核心,它根据当前的game_state和按下的key_value,决定执行什么动作(如修改分数、切换状态)并迁移到下一个状态。

6.2 功能测试用例

在将程序下载到实物或进行深入仿真前,应在代码层面设计测试用例:

  1. 计时功能测试:启动计时器,观察game_time_remaining_sec是否每秒减1,格式转换MM:SS是否正确,节次切换逻辑是否正常。
  2. 计分功能测试:在RUNNINGPAUSED状态下,按下不同队伍、不同分值的按键,观察对应队伍的分数变量是否正确增加,是否溢出。
  3. 状态迁移测试:依次按下“开始”、“暂停”、“继续”、“复位”等按键,观察game_state变量的变化是否符合预期。
  4. 边界条件测试:测试计时到0时的行为,分数加到999后再加的行为,在MENU设置状态下输入非法值的处理等。

可以在代码中添加调试输出(通过串口打印到Keil的调试窗口),或者利用Keil的软件仿真功能,观察变量值的变化,这是最有效的调试手段之一。

6.3 从仿真到实物的注意事项

当仿真无误,准备移植到真实的STM32核心板和元器件时,有几个关键点需要注意:

  1. 电源与滤波:实物电路中,务必为STM32的电源引脚(VDD/VSS)就近放置0.1uF和10uF的滤波电容,这是稳定运行的基础。
  2. LCD对比度与背光:实物LCD1602的VEE引脚通常连接一个10kΩ的可调电阻来调节对比度,需要调整至字符清晰。背光LED需要串联一个限流电阻(通常100-200Ω)。
  3. 键盘上拉电阻:在实物中,我们将列线配置为上拉输入。STM32的内部上拉电阻约40kΩ,在干扰较大的环境中可能不够强健。如果出现按键误触发,可以考虑在PA4-PA7每个引脚外部增加一个10kΩ的上拉电阻到3.3V。
  4. 延时函数替换:仿真中使用的HAL_Delay依赖于SysTick中断。在实物调试初期,如果其他中断影响了SysTick,可能导致延时不准。可以考虑使用定时器实现更精确的延时,或者检查中断优先级。
  5. 下载与调试接口:确保ST-Link/V2等调试器连接正确(SWDIO, SWCLK, GND, 3.3V),并在Keil的Options for Target -> Debug中正确选择调试器型号。

7. 项目扩展与优化思路

这个基础版本已经实现了核心功能,但还有很大的优化和扩展空间,这可以作为你深入学习的方向:

  1. 增加24秒进攻计时:篮球比赛还有24秒进攻违例规则。可以再增加一个24秒计时器,使用另一个定时器或软件计时,并通过LCD的剩余位置(如第一行右侧)显示。其启动、停止、复位规则需要集成到状态机中。
  2. 改用OLED显示屏:使用I2C或SPI接口的OLED屏(如SSD1306),可以显示更丰富的图形信息,比如绘制简单的记分牌图案、显示队名等。这需要移植相应的OLED驱动库。
  3. 添加声音提示:利用PWM驱动蜂鸣器或连接一个简单的音频模块,为比赛开始、结束、节末、24秒违例等事件添加不同的提示音。
  4. 实现数据存储:利用STM32的内部Flash或外接EEPROM(如AT24C02),在系统断电前保存当前的比赛分数和节次,上电后可以恢复。
  5. 引入RTOS:如果功能越来越复杂,可以考虑引入FreeRTOS等实时操作系统。将按键扫描、显示刷新、计时逻辑、声音控制等任务分别放在不同的线程中,由操作系统调度,使程序结构更模块化,更容易管理复杂的实时性要求。
  6. 设计更友好的UI:通过长按、短按区分不同功能,实现多层菜单系统,用于设置时间、队伍名称、比赛规则等。

这个基于STM32的LCD篮球计时记分器项目,从CubeMX配置到HAL库编程,从Proteus仿真到实物调试,完整地走完了一个嵌入式产品从概念到原型的过程。它不仅仅是一段代码和一个电路,更是一套解决问题的方法论。希望这个详细的拆解,能帮助你不仅做出这个项目,更能理解背后每一个设计决策的原因,从而具备独立开发下一个更复杂嵌入式系统的能力。

本文还有配套的精品资源,点击获取

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

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

立即咨询