【蓝桥杯嵌入式·真题解析】第十届蓝桥杯嵌入式设计与开发项目省赛——程序设计试题
摘要:本文为作者对第十届蓝桥杯嵌入式设计与开发项目省赛——程序设计试题的解析。本文包括“题目要求”、“程序设计”、“效果展示”3个部分。供复盘使用。若发现错误之处,请不吝赐教。
摘要:本文为作者对第十届蓝桥杯嵌入式设计与开发项目省赛——程序设计试题的解析。本文包括“题目要求”、“程序设计”、“效果展示”3个部分。供复盘使用。若发现错误之处,请不吝赐教。
链接:蓝桥杯嵌入式方向备赛记录(STM32G431)为作者备赛蓝桥杯嵌入式过程中,整理的学习总结。包括各模块使用要点、各模块程序等,基本搭建好工程框架,给出了各模块处理程序。
目 录
一、题目要求




二、程序设计
- 工程可以按照几条主线进行下去:
模拟电压输入(ADC):要采集电压,要判断实时电压和阈值电压的关系。
按键:首先要能判断哪个按键按下(即按键检测),然后对应按键有何功能。B1:切换界面,程序上即为翻转界面状态标志位;B2:切换参数选择项并高亮显示:可以扩展界面状态标志位,在LCD处理函数中,根据标志位变量值,高亮显示对应行;B3:加按键;B4:减按键。可以通过自加++、自减--运算符改变相应变量值。
按键部分涉及3点要解决好:(1)高亮显示行,即状态标志位如何改变?(2)加减功能,要处理好,不能出现冲突,即电压上限阈值>电压下限阈值,和,上限下限指示灯不能相同。这2点归根结底,还是变量设置的问题。(3)按按键不要影响到LED,即按键要以非阻塞方式检测按键,并最好进行消抖处理。这1点是所有试题都要解决的。
LCD:2个界面。主要有2点要解决好:(1)高亮显示;(2)界面切换时,不要有上一界面的数据/背景遗留。
LED:首先要封装好LED显示的函数,然后就是LED处理。要解决好LED某些位,按一定频率闪烁的问题。即通过Systic实现。程序有2种写法。
其他注意点:(1)串口重定向后,要勾选“魔术棒”里的“Use MicroLiE”选项,不然屏幕显示不了;(2)EEPROM问题。
总的来说,我认为变量/状态标志位的设置非常关键。妥善地设置变量,能快速解决一些难点。
上述思路对应程序,即为程序实现的框架。
1、ADC
//ADC处理函数
void ADC_Proc(void)
{
//读取ADC,计算电压
uint16_t adc = 0;
HAL_ADC_Start(&hadc2);
adc = HAL_ADC_GetValue(&hadc2);
V = adc*3.3/4096;
//判断电压状态
ucStatus_Flag = 0; //Normal
if(V*10 > ucMaxVolt) ucStatus_Flag = 1;//Upper
if(V*10 < ucMinVolt) ucStatus_Flag = 2;//Lower
}
2、按键
非阻塞方式检测按键(并消抖)部分,见作者博客。这里给出按键处理函数:
void KEY_Proc(void) //注意要清除标志位
{
if(key[0].ucJudgeKeyState == 1) //如果K1短按 切换选择界面
{
if(!ucState)
{
ucState = 1;
}
else
{
// 若要存储,则使用这部分
// x24c02_write(0,ucMaxVolt);
// x24c02_write(1,ucMinVolt);
// x24c02_write(2,ucUpperLED);
// x24c02_write(3,ucLowerLED);
ucState = 0;
}
key[0].ucJudgeKeyState = 0;
}
if(key[1].ucJudgeKeyState == 1) //如果K2短按。切换选择参数项 注意这里不能像51那里用else if 否则短按后,长按后无效
{
if(ucState)
{
if(++ucState == 5)//ucState 2~5 分别代表4个选项
ucState = 1;
}
key[1].ucJudgeKeyState = 0;
}
if(key[2].ucJudgeKeyState == 1) //如果K3短按
{
switch(ucState)
{
case 1:
if(ucMaxVolt <= 30) ucMaxVolt += 3;
break;
case 2:
if((ucMaxVolt - ucMinVolt) > 3) ucMinVolt += 3;
break;
case 3:
//先灭刚才闪的灯
ucLed &=~ 1<<(ucUpperLED-1);
Led_Disp(ucLed);
if(++ucUpperLED > 8) ucUpperLED = 1;
if(ucUpperLED == ucLowerLED)
{
if(++ucUpperLED > 8) ucUpperLED = 1;
}
break;
case 4:
//先灭刚才闪的灯
ucLed &=~ 1<<(ucLowerLED-1);
Led_Disp(ucLed);
if(++ucLowerLED > 8) ucLowerLED = 1;
if(ucUpperLED == ucLowerLED)
{
if(++ucLowerLED > 8) ucLowerLED = 1;
}
break;
default: break;
}
key[2].ucJudgeKeyState = 0;
}
if(key[3].ucJudgeKeyState == 1) //如果K4短按
{
switch(ucState)
{
case 1:
if((ucMaxVolt - ucMinVolt) > 3) ucMaxVolt -= 3;
break;
case 2:
if(ucMinVolt > 3) ucMinVolt -= 3;
break;
case 3:
//先灭刚才闪的灯
ucLed &=~ 1<<(ucUpperLED-1);
Led_Disp(ucLed);
if(--ucUpperLED == 0) ucUpperLED = 8;
if(ucUpperLED == ucLowerLED)
if(--ucUpperLED == 0) ucUpperLED = 8;
break;
case 4:
//先灭刚才闪的灯
ucLed &=~ 1<<(ucLowerLED-1);
Led_Disp(ucLed);
if(--ucLowerLED == 0) ucLowerLED = 8;
if(ucUpperLED == ucLowerLED)
if(--ucLowerLED == 0) ucLowerLED = 8;
break;
default: break;
}
key[3].ucJudgeKeyState = 0;
}
Led_Disp(ucLed);
}
3、LCD
界面Main里:注意Status显示,设置了数组char* Status[]存放3个状态,并可通过状态标志位ucStatus_Flag索引相应状态。
界面Setting里:根据ucState (1~5)的不同值,即代表不同选项,高亮显示相应行。
char buf1[20];
uint8_t ucState=0;//界面状态:0—Main界面 1—setting界面(MaxVolt) 2—setting界面(MaxVolt) 3—setting界面(MinVolt) 4—setting界面(Upper) 5—setting界面(Lower)
uint8_t ucMaxVolt = 24;
uint8_t ucMinVolt = 12;
uint8_t ucUpperLED = 1;
uint8_t ucLowerLED = 2;
char* Status[]={"Normal","Upper","Lower"};//状态
uint8_t ucStatus_Flag = 0;//索引数组
void LCD_Proc(void)
{
if(!ucState)
{
LCD_DisplayStringLine(Line3,(unsigned char*)" Main ");
sprintf(buf1," Volt:%.2fV ",V);
LCD_DisplayStringLine(Line4,(unsigned char*)buf1);
sprintf(buf1," Status:%s ",Status[ucStatus_Flag]);
LCD_DisplayStringLine(Line5,(unsigned char*)buf1);
LCD_DisplayStringLine(Line6,(unsigned char*)" ");
LCD_DisplayStringLine(Line7,(unsigned char*)" ");
LCD_DisplayStringLine(Line8,(unsigned char*)" ");
LCD_DisplayStringLine(Line9,(unsigned char*)" ");
}
else
{
LCD_DisplayStringLine(Line3,(unsigned char*)" Setting ");
if(ucState == 1) LCD_SetBackColor(Green);
sprintf((char*)buf1," MaxVolt:%4.2f ",ucMaxVolt/10.0);
LCD_DisplayStringLine(Line4,(unsigned char*)buf1);
LCD_SetBackColor(White);
if(ucState == 2) LCD_SetBackColor(Green);
sprintf((char*)buf1," MinVolt:%4.2f ",ucMinVolt/10.0);
LCD_DisplayStringLine(Line5,(unsigned char*)buf1);
LCD_SetBackColor(White);
if(ucState == 3) LCD_SetBackColor(Green);
sprintf(buf1," Upper:LD%d ",ucUpperLED);
LCD_DisplayStringLine(Line6,(unsigned char*)buf1);
LCD_SetBackColor(White);
if(ucState == 4) LCD_SetBackColor(Green);
sprintf(buf1," Lower:LD%d ",ucLowerLED);
LCD_DisplayStringLine(Line7,(unsigned char*)buf1);
LCD_SetBackColor(White);
}
}
4、LED
利用Systic处理LED按0.2秒闪烁问题。程序有2种写法。
2024/2/18:LED这里发现bug~ , 已解决
BUG1:按B3、B4,对指示灯调整时,假设LD1亮(还未熄灭时),按下B3,指示灯序号递增,LD2闪烁时,LD1还在亮,继续按B3,后面都是如此。
解决方法:在B3、B4按下后,ucUpperLED、ucLowerLED加减前,熄灭刚才闪的指示灯。
解决过程:(1)在B3、B4功能处理内,调用 Led_Disp(0); 熄灭所有灯。发现,未奏效。(2)原因是只在表面上熄灭了,没有改变ucLed,因为全局Led_Disp();都是对ucLed位带操作的。所以必须改变ucLed 。添加语句如下所示(已添加到2、按键处理函数中):
//B3里 ucLed &=~ 1<<(ucUpperLED-1); Led_Disp(ucLed); //B4里 ucLed &=~ 1<<(ucLowerLED-1); Led_Disp(ucLed);上述BUG1解决后,出现BUG2:
BUG2 : 高压下(>2.4),上电后,高压下,LD2会常亮,直到退出高压状态。BUG2暂未解决,如有朋友知晓,请不吝赐教,不胜感激。
方法1:
//main.c里
void LED_Proc(void)
{
//方法1
if(ucStatus_Flag == 0)
ucLed=0;
else if(ucStatus_Flag == 1)//上
{
if(usTms==200 )
{
ucLed ^= 1<<(ucUpperLED-1);
usTms=0;
}
}
else if(ucStatus_Flag == 2)//下
{
if(usTms==200 )
{
ucLed ^= 1<<(ucLowerLED-1);
usTms=0;
}
}
else
ucLed = 0;
Led_Disp(ucLed);
}
//it.c里
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
//方法1
if(usTms<200) //计数小于200ms,ms加
{
usTms++;
}
/* USER CODE END SysTick_IRQn 1 */
}
方法2:
//在it.c里
uint16_t usTms = 0;
extern uint8_t ucUpperLED;
extern uint8_t ucLowerLED;
extern uint8_t ucState;
extern uint8_t ucStatus_Flag;
extern uint8_t ucLed;
extern void LED_Disp(uint8_t);
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
//方法2
usTms++;
switch(ucStatus_Flag)
{
case 0:
ucLed = 0;
break;
case 1:
if(usTms%200 == 0) ucLed ^= 1<<(ucUpperLED-1);
break;
case 2:
if(usTms%200 == 0) ucLed ^= 1<<(ucLowerLED-1);
break;
default: break;
}
Led_Disp(ucLed);
/* USER CODE END SysTick_IRQn 1 */
}
5、整合(其他部分)
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim4);
LCD_Init();
LCD_Clear(White);
LCD_SetBackColor(White);
LCD_SetTextColor(Blue);
I2CInit();
// 若要存储,则使用这部分
// ucMaxVolt = x24c02_read(0);
// ucMinVolt = x24c02_read(1);
// ucUpperLED = x24c02_read(2);
// ucLowerLED = x24c02_read(3);
//设备应具备错误设置的保护功能
if(ucMaxVolt >= 33) ucMaxVolt = 24;
if(ucMinVolt >= 33) ucMinVolt = 12;
if(ucUpperLED >= 9) ucUpperLED = 1;
if(ucLowerLED >= 9) ucLowerLED = 2;
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
ADC_Proc();
KEY_Proc();
LCD_Proc();
LED_Proc();
}
/* USER CODE END 3 */
三、效果展示
视频后续上传。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐



所有评论(0)