写作目的昨天参加了蓝桥杯嵌入式国赛对我来说蓝桥杯正式落幕写个省赛一等奖方案纪念一下作为收尾。程序题功能实现了大部分有些要求比赛时没来得及写功能算不上十全十美代码和解析仅供参考希望看到我这篇博客并且正在准备蓝桥杯嵌入式比赛的同学能取得好成绩。省一奖状题目一、点灯cubemx初始配置PD2、PC8~PC15设置为GPIO_Output。代码uint8_t led_state0x00; void led_show(uint8_t num,uint8_t mode) { if(mode){ led_state | 1(num-1); }else{ led_state ~(1(num-1)); } HAL_GPIO_WritePin(GPIOC,0xff00,GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOC,led_state8,GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET); }二、按键cubemx初始配置PB0、PB1、PB2、PA0设置为GPIO_Input。代码float v_R37,P,F,add_F0.0,V0.0,TarP5.0,FH20.0,FL10.0,PL1.0,k0; uint16_t Q0,D5,TarD5,DH65; int page 0,M_flag 1,Tar_flag 0,para_flag 0,F_flag 0,duty_flag0; void key_scan() { static uint32_t key_press_cnt[4]{0}; static uint32_t key_long_flag[4]{0}; uint16_t key_state[4]; key_state[0] HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0); key_state[1] HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1); key_state[2] HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2); key_state[3] HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0); for(int i0;i4;i) { if(key_state[i] 0){ if(key_press_cnt[i] 0){ key_press_cnt[i] HAL_GetTick(); } if(HAL_GetTick()-key_press_cnt[i]2000 key_long_flag[i]0){//长按 key_long_flag[i] 1; if(i 0){ //led_show(1,0); }else if(i 1){ //led_show(2,0); }else if(i 2){ //led_show(3,0); V v_R37; }else if(i 3){ //led_show(4,0); Q 0; } } }else{ if(key_press_cnt[i] ! 0 (HAL_GetTick()-key_press_cnt[i]2000) (HAL_GetTick()-key_press_cnt[i]20)){//短按 if(i 0){ //led_show(1,1); page ; }else if(i 1){ //led_show(2,1); if(page %3 0){ M_flag ; }else if(page %3 1){ Tar_flag ; }else if(page %3 2){ para_flag ; } }else if(i 2){ //led_show(3,1); if(page %3 1){ if(Tar_flag %2 0){ if(TarD 95){ TarD 5; }else{ TarD 5; } }else if(Tar_flag %2 1){ if(TarP 9.5){ TarP 0.5; }else{ TarP 1.0; } } }else if(page %3 2){ if(para_flag %4 0){ if(FH 40){ FH 1; }else{ FH 4; } }else if(para_flag %4 1){ if(FL 40){ FL 1; }else{ FL 4; } }else if(para_flag %4 2){ if(PL 9.5){ PL 0.5; }else{ PL 1.0; } }else if(para_flag %4 3){ if(DH 95){ DH 5; }else{ DH 5; } } } }else if(i 3){ //led_show(4,1); if(page %3 1){ if(Tar_flag %2 0){ if(TarD 5){ TarD -5; }else{ TarD 95; } }else if(Tar_flag %2 1){ if(TarP 1.0){ TarP - 0.5; }else{ TarP 9.5; } } }else if(page %3 2){ if(para_flag %4 0){ if(FH 4){ FH -1; }else{ FH 40; } }else if(para_flag %4 1){ if(FL 4){ FL -1; }else{ FL 40; } }else if(para_flag %4 2){ if(PL 1.0){ PL - 0.5; }else{ PL 9.5; } }else if(para_flag %4 3){ if(DH 5){ DH - 5; }else{ DH 95; } } } } } key_long_flag[i] 0; key_press_cnt[i] 0; } } }三、adc采样cubemx初始配置PB15设置为ADC_IN15代码float v_R37 void adc_getvalue() { uint16_t temp; HAL_ADC_Start(hadc2); HAL_ADC_PollForConversion(hadc2,10); temp HAL_ADC_GetValue(hadc2); v_R37 temp *3.3/4096.0; }四、测量输入频率cubemx初始配置PB4设置为TIM3_CH1具体配置如下代码在main.c打开TIM3HAL_TIM_IC_Start_IT(htim3,TIM_CHANNEL_1);在stm32g4xx_it.c获取TIM3计数值uint16_t cnt_R39; uint16_t cnt_a3; void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if(htim-Instance TIM3){ cnt_R39 HAL_TIM_ReadCapturedValue(htim3,TIM_CHANNEL_1); } cnt_a3 HAL_TIM_ReadCapturedValue(htim15,TIM_CHANNEL_2); }在自建的fun.c里计算捕获的输入频率float fre_R39; int16_t fre_a3_ic; extern uint16_t cnt_R39; extern uint16_t cnt_a3; void incap_fre() { if(cnt_R39 ! 0){ fre_R39 1000000/cnt_R39; } else{ fre_R39 -1; } if(cnt_a3 ! 0){ fre_a3_ic 1000000/cnt_a3; } else{ fre_a3_ic -1; } }五、生成PWM方波cubemx初始设置PA1引脚设置为TIM2_CH2具体配置如下代码uint16_t fre_A1_out,arr_A1; uint16_t duty_A16,comp_A1; void pwm_out() { fre_A1_out 10000; //duty_A1 5; if(M_flag %2 1){ if(page %3 ! 1){ if(duty_A1 TarD){ if(duty_flag 1){ duty_A1 1; duty_flag 0; led_show(4,1); } }else if(duty_A1 TarD){ if(duty_flag 1){ duty_A1 - 1; duty_flag 0; led_show(4,1); } }else{ led_show(4,0); } } }else{ if(page %3 ! 1){ if(P (TarP - 0.5)){ if(duty_A1 95){ if(duty_flag 1){ duty_A1 1; duty_flag 0; led_show(4,1); } }else{ led_show(4,0); } }else if(P (TarP 0.5)){ if(duty_A15){ if(duty_flag 1){ duty_A1 - 1; duty_flag 0; led_show(4,1); } }else{ led_show(4,0); } }else{ led_show(4,0); } } } arr_A1 1000000/fre_A1_out -1; comp_A1 (arr_A11) * duty_A1 / 100; if(__HAL_TIM_GET_COUNTER(htim2)(arr_A11)){ __HAL_TIM_SET_COUNTER(htim2,0); } __HAL_TIM_SET_AUTORELOAD(htim2,arr_A1); __HAL_TIM_SET_COMPARE(htim2,TIM_CHANNEL_2,comp_A1); }六、LCD显示cubemx初始配置PA8、PB5、PB8、PB9、PC0~PC15设置为GPIO_Output代码char text[20]; void lcd_main() { sprintf(text, MAIN ); LCD_DisplayStringLine(Line1,(uint8_t *)text); if(M_flag %2 0){ sprintf(text, MAUTO ); }else{ sprintf(text, MMAN ); } LCD_DisplayStringLine(Line3,(uint8_t *)text); sprintf(text, P%.1fBAR ,P); LCD_DisplayStringLine(Line4,(uint8_t *)text); sprintf(text, F%.1fL/M ,F); LCD_DisplayStringLine(Line5,(uint8_t *)text); sprintf(text, Q%dL ,Q); LCD_DisplayStringLine(Line6,(uint8_t *)text); sprintf(text, D%d%% ,D); LCD_DisplayStringLine(Line7,(uint8_t *)text); sprintf(text, V%.1fV ,V); LCD_DisplayStringLine(Line8,(uint8_t *)text); } void lcd_outp() { sprintf(text, OUTP ); LCD_DisplayStringLine(Line1,(uint8_t *)text); sprintf(text, TarD%d%% ,TarD); LCD_DisplayStringLine(Line3,(uint8_t *)text); sprintf(text, TarP%.1fBAR,TarP); LCD_DisplayStringLine(Line4,(uint8_t *)text); LCD_ClearLine(Line5); LCD_ClearLine(Line6); LCD_ClearLine(Line7); LCD_ClearLine(Line8); } void lcd_para() { sprintf(text, PARA ); LCD_DisplayStringLine(Line1,(uint8_t *)text); sprintf(text, FH%.1fL/M ,FH); LCD_DisplayStringLine(Line3,(uint8_t *)text); sprintf(text, FL%.1fL/M ,FL); LCD_DisplayStringLine(Line4,(uint8_t *)text); sprintf(text, PL%.1fBAR ,PL); LCD_DisplayStringLine(Line5,(uint8_t *)text); sprintf(text, DH%d%% ,DH); LCD_DisplayStringLine(Line6,(uint8_t *)text); } void lcd_judge() { main_look(); Dduty_A1; if(page%3 0){ lcd_main(); Tar_flag 0; }else if(page%3 1){ lcd_outp(); para_flag 0; }else if(page%3 2){ lcd_para(); } }完整代码//仅展示自己写的部分cubemx生成的不做展示main.c#include fun.h int main(void) { LCD_Init(); LCD_Clear(Black); LCD_SetBackColor(Black); LCD_SetTextColor(White); for(int i1;i8;i){ led_show(i,0); } HAL_ADCEx_Calibration_Start(hadc2,ADC_SINGLE_ENDED); HAL_TIM_IC_Start_IT(htim3,TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim2,TIM_CHANNEL_2); HAL_TIM_IC_Start_IT(htim15,TIM_CHANNEL_2); uint32_t tick 0; while (1) { key_scan(); if(HAL_GetTick()-tick20){ tick HAL_GetTick(); lcd_judge(); } } }stm32g4xx_it.c#include fun.h uint16_t cnt_R39; uint16_t cnt_a3; uint16_t ms_1000; uint16_t ms_10000; uint16_t plus_flag 0; extern int duty_flag; void SysTick_Handler(void) { HAL_IncTick(); ms_100 ; ms_1000 ; if(ms_100 100){ ms_100 0; plus_flag 1; } if(ms_1000 1000){ ms_1000 0; duty_flag 1; } } void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if(htim-Instance TIM3){ cnt_R39 HAL_TIM_ReadCapturedValue(htim3,TIM_CHANNEL_1); } cnt_a3 HAL_TIM_ReadCapturedValue(htim15,TIM_CHANNEL_2); }fun.c//自建的专门用来放自己写的代码#include fun.h char text[20]; uint8_t led_state0x00; float v_R37,P,F,add_F0.0,V0.0,TarP5.0,FH20.0,FL10.0,PL1.0,k0; uint16_t Q0,D5,TarD5,DH65; float fre_R39; uint16_t fre_A1_out,arr_A1; uint16_t duty_A16,comp_A1; extern uint16_t cnt_R39; int16_t fre_a3_ic; extern uint16_t cnt_a3; int page 0,M_flag 1,Tar_flag 0,para_flag 0,F_flag 0,duty_flag0; extern uint16_t ms_100; extern uint16_t plus_flag; void led_show(uint8_t num,uint8_t mode) { if(mode){ led_state | 1(num-1); }else{ led_state ~(1(num-1)); } HAL_GPIO_WritePin(GPIOC,0xff00,GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOC,led_state8,GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET); } void key_scan() { static uint32_t key_press_cnt[4]{0}; static uint32_t key_long_flag[4]{0}; uint16_t key_state[4]; key_state[0] HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0); key_state[1] HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1); key_state[2] HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2); key_state[3] HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0); for(int i0;i4;i) { if(key_state[i] 0){ if(key_press_cnt[i] 0){ key_press_cnt[i] HAL_GetTick(); } if(HAL_GetTick()-key_press_cnt[i]2000 key_long_flag[i]0){//长按 key_long_flag[i] 1; if(i 0){ //led_show(1,0); }else if(i 1){ //led_show(2,0); }else if(i 2){ //led_show(3,0); V v_R37; }else if(i 3){ //led_show(4,0); Q 0; } } }else{ if(key_press_cnt[i] ! 0 (HAL_GetTick()-key_press_cnt[i]2000) (HAL_GetTick()-key_press_cnt[i]20)){//短按 if(i 0){ //led_show(1,1); page ; }else if(i 1){ //led_show(2,1); if(page %3 0){ M_flag ; }else if(page %3 1){ Tar_flag ; }else if(page %3 2){ para_flag ; } }else if(i 2){ //led_show(3,1); if(page %3 1){ if(Tar_flag %2 0){ if(TarD 95){ TarD 5; }else{ TarD 5; } }else if(Tar_flag %2 1){ if(TarP 9.5){ TarP 0.5; }else{ TarP 1.0; } } }else if(page %3 2){ if(para_flag %4 0){ if(FH 40){ FH 1; }else{ FH 4; } }else if(para_flag %4 1){ if(FL 40){ FL 1; }else{ FL 4; } }else if(para_flag %4 2){ if(PL 9.5){ PL 0.5; }else{ PL 1.0; } }else if(para_flag %4 3){ if(DH 95){ DH 5; }else{ DH 5; } } } }else if(i 3){ //led_show(4,1); if(page %3 1){ if(Tar_flag %2 0){ if(TarD 5){ TarD -5; }else{ TarD 95; } }else if(Tar_flag %2 1){ if(TarP 1.0){ TarP - 0.5; }else{ TarP 9.5; } } }else if(page %3 2){ if(para_flag %4 0){ if(FH 4){ FH -1; }else{ FH 40; } }else if(para_flag %4 1){ if(FL 4){ FL -1; }else{ FL 40; } }else if(para_flag %4 2){ if(PL 1.0){ PL - 0.5; }else{ PL 9.5; } }else if(para_flag %4 3){ if(DH 5){ DH - 5; }else{ DH 95; } } } } } key_long_flag[i] 0; key_press_cnt[i] 0; } } } void adc_getvalue() { uint16_t temp; HAL_ADC_Start(hadc2); HAL_ADC_PollForConversion(hadc2,10); temp HAL_ADC_GetValue(hadc2); v_R37 temp *3.3/4096.0; } void incap_fre() { if(cnt_R39 ! 0){ fre_R39 1000000/cnt_R39; } else{ fre_R39 -1; } if(cnt_a3 ! 0){ fre_a3_ic 1000000/cnt_a3; } else{ fre_a3_ic -1; } } void pwm_out() { fre_A1_out 10000; //duty_A1 5; if(M_flag %2 1){ if(page %3 ! 1){ if(duty_A1 TarD){ if(duty_flag 1){ duty_A1 1; duty_flag 0; led_show(4,1); } }else if(duty_A1 TarD){ if(duty_flag 1){ duty_A1 - 1; duty_flag 0; led_show(4,1); } }else{ led_show(4,0); } } }else{ if(page %3 ! 1){ if(P (TarP - 0.5)){ if(duty_A1 95){ if(duty_flag 1){ duty_A1 1; duty_flag 0; led_show(4,1); } }else{ led_show(4,0); } }else if(P (TarP 0.5)){ if(duty_A15){ if(duty_flag 1){ duty_A1 - 1; duty_flag 0; led_show(4,1); } }else{ led_show(4,0); } }else{ led_show(4,0); } } } arr_A1 1000000/fre_A1_out -1; comp_A1 (arr_A11) * duty_A1 / 100; if(__HAL_TIM_GET_COUNTER(htim2)(arr_A11)){ __HAL_TIM_SET_COUNTER(htim2,0); } __HAL_TIM_SET_AUTORELOAD(htim2,arr_A1); __HAL_TIM_SET_COMPARE(htim2,TIM_CHANNEL_2,comp_A1); } void lcd_test() { adc_getvalue(); sprintf(text,adc37:%.1f,v_R37); LCD_DisplayStringLine(Line0,(uint8_t *)text); incap_fre(); sprintf(text,fre39:%f ,fre_R39); LCD_DisplayStringLine(Line1,(uint8_t *)text); pwm_out(); sprintf(text,fre_a3:%d ,fre_a3_ic); LCD_DisplayStringLine(Line2,(uint8_t *)text); } void lcd_main() { sprintf(text, MAIN ); LCD_DisplayStringLine(Line1,(uint8_t *)text); if(M_flag %2 0){ sprintf(text, MAUTO ); }else{ sprintf(text, MMAN ); } LCD_DisplayStringLine(Line3,(uint8_t *)text); sprintf(text, P%.1fBAR ,P); LCD_DisplayStringLine(Line4,(uint8_t *)text); sprintf(text, F%.1fL/M ,F); LCD_DisplayStringLine(Line5,(uint8_t *)text); sprintf(text, Q%dL ,Q); LCD_DisplayStringLine(Line6,(uint8_t *)text); sprintf(text, D%d%% ,D); LCD_DisplayStringLine(Line7,(uint8_t *)text); sprintf(text, V%.1fV ,V); LCD_DisplayStringLine(Line8,(uint8_t *)text); } void lcd_outp() { sprintf(text, OUTP ); LCD_DisplayStringLine(Line1,(uint8_t *)text); sprintf(text, TarD%d%% ,TarD); LCD_DisplayStringLine(Line3,(uint8_t *)text); sprintf(text, TarP%.1fBAR,TarP); LCD_DisplayStringLine(Line4,(uint8_t *)text); LCD_ClearLine(Line5); LCD_ClearLine(Line6); LCD_ClearLine(Line7); LCD_ClearLine(Line8); } void lcd_para() { sprintf(text, PARA ); LCD_DisplayStringLine(Line1,(uint8_t *)text); sprintf(text, FH%.1fL/M ,FH); LCD_DisplayStringLine(Line3,(uint8_t *)text); sprintf(text, FL%.1fL/M ,FL); LCD_DisplayStringLine(Line4,(uint8_t *)text); sprintf(text, PL%.1fBAR ,PL); LCD_DisplayStringLine(Line5,(uint8_t *)text); sprintf(text, DH%d%% ,DH); LCD_DisplayStringLine(Line6,(uint8_t *)text); } void lcd_judge() { main_look(); Dduty_A1; if(page%3 0){ lcd_main(); Tar_flag 0; }else if(page%3 1){ lcd_outp(); para_flag 0; }else if(page%3 2){ lcd_para(); } } void main_look() { k10/(3.3-V); adc_getvalue(); if(v_R37 V){ P 0.0; }else{ P k * v_R37; } incap_fre(); if(fre_R39 800){ F 0; led_show(1,0); }else if(fre_R39 8000){ F 40; F_flag 1; led_show(1,1); }else{ F fre_R39 / 200; led_show(1,0); } if(add_F 1){ if(plus_flag 1){ add_F F; }else{ } plus_flag 0; }else{ Q add_F; add_F 0; } pwm_out(); }fun.h//自建的专门用来收录自己写的函数和调用的库起到一个万能头文件的作用#ifndef _fun_h #define _fun_h #include stm32g4xx.h #include stdio.h #include string.h #include stdint.h #include gpio.h #include adc.h #include tim.h #include lcd.h void led_show(uint8_t num,uint8_t mode); void key_scan(void); void adc_getvalue(void); void incap_fre(void); void lcd_test(void); void pwm_out(void); void lcd_main(void); void lcd_outp(void); void lcd_para(void); void lcd_judge(void); void main_look(void); #endif整体工程详细解析1、硬件资源配置与外设初始化main.c负责系统启动与外设初始化。系统依次初始化 GPIO、ADC2、TIM2、TIM3 和 TIM15对应的功能分别是 LED 与按键控制、电位器 R37 的模拟采集、PA1 的 PWM 输出、R39 的输入捕获测频以及 A3 通道的输入捕获测频。LCD 在外设初始化完成后立即初始化并配置为黑底白字。主循环以 20ms 为刷新周期轮询调用key_scan()和lcd_judge()兼顾按键响应实时性与显示刷新稳定性。2、中断服务与计时机制stm32g4xx_it.c承担所有中断逻辑。SysTick 中断每 1ms 触发一次其中累积到 100ms 时置位plus_flag用于驱动累计流量的定周期采样累积到 1000ms 时置位duty_flag用于驱动 PWM 占空比的 1%/秒平滑调节。TIM3 和 TIM15 的输入捕获回调函数HAL_TIM_IC_CaptureCallback分别读取 R39 和 A3 的捕获计数值存入cnt_R39和cnt_a3供主循环计算频率使用。3、压力监测与零点校准adc_getvalue()通过 HAL 库对 ADC2 进行单次触发采样将 12 位 ADC 原始值线性换算为实际电压v_R37量程 0 ~ 3.3V。压力计算在main_look()中完成。系统维护一个零点偏置电压V映射斜率k根据公式k 10 / (3.3 - V)动态计算使压力值在[V, 3.3V]输入范围内线性映射至[0, 10.0] BAR。当实时电压低于偏置电压时压力直接取 0避免负压输出。长按 B3 键 2 秒触发校准将当时的v_R37赋值给V并即时更新斜率满足题目零点校准要求。4、流量监测与累计流量计算incap_fre()根据输入捕获计数值计算频率以定时器时钟基准1MHz 计数频率除以捕获周期计数cnt_R39得到 R39 信号的瞬时频率fre_R39。在main_look()中瞬时流量 F 按F fre_R39 / 200计算频率低于 800Hz 时 F 取 0超过 8000Hz 时 F 固定为 40 并点亮 LD1 报警。累计流量 Q 采用 100ms 定周期采样累加每次plus_flag置位时将本周期的流量增量F/10即 100ms 对应的升数累加到中间变量add_F当add_F满 1L 后整体加入 Q 并清零确保 Q 以整升为单位更新避免频繁写入产生显示噪点。长按 B4 键 2 秒可将 Q 清零。5、PWM 输出控制pwm_out()控制 TIM2 通道 2PA1输出固定 10kHz 的 PWM 信号。函数通过运算得到 ARR 和 CCR 寄存器的值并使用__HAL_TIM_SET_AUTORELOAD和__HAL_TIM_SET_COMPARE在运行时动态更新无需重启定时器。占空比平滑调节依赖duty_flag每隔 1 秒该标志由 SysTick 中断置位一次pwm_out()检测到标志后才对duty_A1执行 ±1% 的步进实现题目要求的 1%/秒平滑过渡速率。手动模式下M_flag为奇数duty_A1向TarD方向步进自动模式下以实时压力 P 与目标压力TarP的偏差为依据P 低于目标区间则增大占空比P 高于目标区间则减小占空比处于±0.5 BAR容差带内则保持不变。占空比硬件上限和下限分别为 95% 和 5%。6、三界面 LCD 显示lcd_judge()根据page % 3的值分发到三个显示函数分别对应监控界面MAIN、输出配置界面OUTP和运行参数界面PARA每 20ms 刷新一次。lcd_main()显示模式MAN/AUTO、实时压力 P、瞬时流量 F、累计流量 Q、当前占空比 D 和零点偏置电压 Vlcd_outp()显示目标占空比 TarD 和目标压力 TarP并清除无关行lcd_para()显示四个安全参数 FH、FL、PL 和 DH。所有数值格式严格按照题目图示要求使用sprintf格式化后通过LCD_DisplayStringLine定行输出。7、 按键扫描与参数管理key_scan()使用静态计时数组实现全软件消抖与长短按识别无需额外硬件资源。短按通过比较HAL_GetTick()差值是否在 20ms 至 2000ms 之间判定长按则在持续超过 2000ms 时触发且通过key_long_flag保证每次长按只响应一次。B1 控制page自增实现三界面循环切换B2 在不同界面分别切换工作模式、输出配置选项Tar_flag和运行参数选项para_flagB3/B4 对应当前选中参数的加减操作。参数预设不立即生效的逻辑通过界面切换时在lcd_judge()中重置Tar_flag和para_flag来实现——用户在 OUTP 或 PARA 界面调整的值在变量中实时保存退出界面时自动生效进入时重置选项指针符合题目对配置仅在退出界面时触发全局更新的要求。8、代码结构总结stm32g4xx_it.c专职处理中断与基础计时fun.c集中实现所有业务逻辑main.c仅负责初始化与主循环调度。三者通过少量extern变量共享状态耦合度低便于调试与功能扩展。