蓝桥杯 嵌入式赛道 第十六届模拟三(文章更新到到B2键b3键控制lcd第二个屏幕 持续更新中) 万字讲解 保姆级别讲解代码
#include "fun.h" void led_show(uint8_t led, uint8_t mode) { HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET); if(mode) HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8<<(led-1), GPIO_PIN_RESET); else HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8<<(led-1), GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET); } uint8_t led_flag; uint8_t B1_data, B1_last=1, B2_data, B2_last=1, B3_data, B3_last=1, B4_data, B4_last=1; float adc_value; volatile uint16_t free; uint8_t lcd_mode = 0 ; //初始化为0 uint8_t Rate; uint8_t Con_ut; uint8_t Rate_Max; uint8_t Rate_Min; uint8_t high = 100; uint8_t low = 60; uint8_t high_low =0; uint8_t flag_hl =0; void key_pros() { B1_data = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0); B2_data = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1); B3_data = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2); B4_data = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0); // --- B1: 切换模式 --- if(!B1_data && B1_last) { if (lcd_mode == 1) flag_hl = 0; if(++lcd_mode > 2) lcd_mode = 0; // 模式范围:0, 1, 2 } // --- B2: 仅在 PARA 界面(2) 有效 --- if(!B2_data && B2_last) { if(lcd_mode == 2) { high_low ^= 1; } } // --- B3: 修复这里 --- if(!B3_data && B3_last) { // 和 B2 一样在参数界面生效 if(lcd_mode == 2) { flag_hl ^= 1; } } if(!B4_data && B4_last) { } B1_last = B1_data; B2_last = B2_data; B3_last = B3_data; B4_last = B4_data; } float adc_read(ADC_HandleTypeDef *hadc) { HAL_ADC_Start(hadc); uint32_t adc_raw = HAL_ADC_GetValue(hadc); return 3.3f * adc_raw / 4096.0f; } char text[20]; void lcd_show() { uint16_t temp = GPIOC->ODR; switch (lcd_mode) { case 0: sprintf(text, " HEART "); LCD_DisplayStringLine(Line1, (uint8_t*)text); sprintf(text, " RATE:%.d ", Rate); LCD_DisplayStringLine(Line3, (uint8_t*)text); sprintf(text, " Con:%d ", Con_ut); LCD_DisplayStringLine(Line4, (uint8_t*)text); break; case 1: sprintf(text, " RECORD "); LCD_DisplayStringLine(Line1, (uint8_t*)text); sprintf(text, " Max:%d ", Rate_Max); LCD_DisplayStringLine(Line3, (uint8_t*)text); sprintf(text, " Min:%d ", Rate_Min); LCD_DisplayStringLine(Line4, (uint8_t*)text); break; case 2: sprintf(text, " PARA "); LCD_DisplayStringLine(Line1, (uint8_t*)text); sprintf(text, " High:%d ", high); LCD_DisplayStringLine(Line3, (uint8_t*)text); sprintf(text, " Low:%d ", low); LCD_DisplayStringLine(Line4, (uint8_t*)text); break; default: lcd_mode = 0; break; } GPIOC->ODR = temp; } void data_proc(void) { adc_value = adc_read(&hadc2); if(free<1000) Rate = 30; else if (free>2000) Rate = 200; else Rate = 0.17* free -140; if(flag_hl) { if(!high_low) { if(adc_value <1) high = 60; else if(adc_value>3) high=150; else high = 45*adc_value +15; } else{ if(adc_value <1) low = 60; else if(adc_value>3) low=150; else low = 45*adc_value +15; } } } void main_pros() { // 定义一个静态变量来计时 static uint32_t lcd_time = 0; // 1. 按键检测:每次循环都跑,保证反应快 key_pros(); // 2. 数据处理:每次循环都跑 data_proc(); // 3. LCD显示:改为每 100ms 刷新一次 // 如果不加这个if,CPU 99%的时间都在画图,根本检测不到你按键松手的那一瞬间 if(HAL_GetTick() - lcd_time > 100) { lcd_show(); lcd_time = HAL_GetTick(); // 更新计时 } for(int i=1; i<9; i++ ) led_show(i, led_flag); } void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM16) { uint32_t capture_value = TIM16->CCR1 + 1; TIM16->CNT = 0; free = 1000000 / capture_value; } }
下面我罗列 出可能你会有 疑问的地方
1 stm32g431rbt6 蓝桥杯这个板子 我们首先要 打开PD2 号引脚 并且设置为高电平 在最后我们要 关闭 PD2号 并且 设置为低电平
2 只要是中断或者 RTOS 任务里要用的全局变量,直接加上 volatile保平安 在上面的代码里面 我在中断那里面 加了 volatile
3 下面我们讲解 flag hl^= 1;所以这个标志位会自己 找 0还是1 我只要在前面 定义uint8 t flag hl =0; 这正是编程里最“省事”的地方。你只需要做两步,剩下的计算机自己会搞定:
4 if(lcd mode ==1)flag hl = 0;-- 进门前先擦鞋(状态重置)这就好比你从“卧室”去“厨房”之前,必须把手里的脏东西扔掉,保证去下一个房间是干干净净的。现状:当 1cd_mode ==1时,你处于第2个界面(RECORD 界面)。
动作:你按下了 B1,意味着你要离开 RECORD 界面,前往下一个界面(PARA 界面,即lcd mode = 2 )。
目的: flag_h1 是你在 PARA 界面用来控制修改参数的标志位。
这行代码强制把 flag_h1 归零。
为什么?为了防止你上次在 PARA 界面操作了一半(比如 flag_hl 还是1),切走了又切回来时,界面处于一种奇怪的中间状态。
结论:这是为了保证每次进入 PARA 界面(界面2)时,状态都是默认的初始状态(0)。
5, 1cd_mode ==1 是第二个界面(也就是“RECORD”记录界面)。在C语言编程中,计数通常是从0开始的。我们来看一下 lcd_show函数里的对应关系:
6 硬件电路里 LED 是 “阳极接电源、阴极接 GPIO” 还是反过来?(如果是前者,就一定是 “GPIO 低电平点亮”;如果是后者,才是 “GPIO 高电平点亮”)
7 !0 结果是 1(把“没有”变成“有”)
• !1 结果是 0(把“有”变成“没有”)
• !5 结果也是 0(只要不是0,取反后都变成0)! 来救场
加上 ! 之后,逻辑就顺了:
• 当你按下按键:
1. 硬件读到 0。
2. 代码执行 !0。
3. 结果变成 1 (True)。
4. 计算机理解为:是的,有事情发生了!
• 当你松开按键:
1. 硬件读到 1。
2. 代码执行 !1。
3. 结果变成 0 (False)。
4. 计算机理解为:没事发生。
8 初学者 一定对于 中断的 一些名词不理解 现在 我来讲解一下
IC 是你要干什么(输入捕获),IT 是你怎么知道它干完了(通过中断通知)
CaptureCallback 是结果处理。
9.在 main 函数中调用 HAL库函数时,需要取地址(用&)原因:变星是结构体类型,函数需要指针类型参数
在回调函数中,参数已是指针,不需要再加&
简单记忆:
main函数:(取地址)&htim16
回调函数:htim直接用指针)
10
![]()
配上 关系图
更多推荐


所有评论(0)