🎊【蓝桥杯嵌入式】专题正在持续更新中,原理图解析✨,各模块分析✨以及历年真题讲解✨都在这儿哦,欢迎大家前往订阅本专题,获取更多详细信息哦🎏

🎏【蓝桥杯嵌入式】蓝桥杯第十届省赛真题

🎏【蓝桥杯嵌入式】蓝桥杯第十二届省赛程序真题

🪔本系列专栏 -   蓝桥杯嵌入式_勾栏听曲_0的博客

🍻欢迎大家  🏹  点赞👍  评论📨  收藏⭐️

📌个人主页 -  勾栏听曲_0的博客📝

🔑希望本文能对你有所帮助,如有不足请指正,共同进步吧🏆

🎇涉浅水者见虾,其颇深者察鱼鳖,其尤深者观蛟龙。📈

目录

赛题

硬件框图

功能要求

 赛题分析

代码实现


赛题

硬件框图

功能要求

功能概述

        1)通过E2PROM完成商品库存数量以及商品单价的存储。

        2)通过串口打印输出购买商品购买数量、总金额等信息。

        3)依试题要求,通过按键,实现界面切换与控制功能。

        4)依试题要求,通过LCD、LED完成数据显示和指示功能。

性能要求

        1)按键响应时间:≤0.1秒。

        2)指示灯动作响应时间:≤0.2秒。(条件触发后0.2秒内)

LCD显示界面

        1)商品购买界面
        在商品购买界面下,通过LCD显示界面名称(SHOP)、商品名称以及商品数量。

        2)商品价格界面
        在商品价格界面下,通过LCD显示界面名称(PRICE)、商品名称以及商品价格。

         商品价格范围:1.0 - 2.0。保留小数点后1位有效数字。

        3)库存信息界面
        在库存信息界面下,通过LCD显示界面名称(REP)、商品名称以及当前库存数量。

        4)LCD通用显示要求
        显示背景色(BackColor):黑色

        显示前景色(TextColor):白色

        请严格按照图示2、3、4要求设计各个信息项的名称(区分字母大小写)和行列位置。

按键功能 

        1)B1:定义为界面切换按键,按下B1按键可以往复切换商品购买、商品价格、库存显示三个界面,切换模式如下图所示:

        2)B2:定义为“商品X”。
        在商品购买界面下,按下B2,商品X购买数量加1。购买数量调整模式:
                0  1  2  3  …  商品X库存数量  0  1  2  …
        在商品价格界面下,按下B2按键,商品X单价加0.1。商品单价调整模式:
                1.0  1.1  …  2.0  1.0  1.1  …

        在库存信息界面下,按下B2按键,商品X库存数量加1。

        3)B3:定义为“商品Y”。
                在商品购买界面下,按下B3,商品Y购买数量加1。购买数量调整模式:
                         0  1  2  3  …  商品X库存数量  0  1  2  …
                在商品价格界面下,按下B3按键,商品Y单价加0.1。商品单价调整模式:
                        1.0  1.1  …  2.0  1.0  1.1  …
                在库存信息界面下,按下B3按键,商品Y库存数量加 1。

        4)B4:定义为“确认”按键。
                在商品购买界面下,按下B4按键,确认购买信息,商品购买界面下的X、Y值重置为0,库存减少相应数量。

        注意:
                按键应进行有效的防抖处理,避免出现一次按下功能多次触发等情形。

                按键动作不应影响数据采集过程和屏幕显示效果。

                价格调整区间:1.0 - 2.0。

                购买数量调整区间:0-商品当前库存数量。

E2PROM存储功能

        通过竞赛平台上的 E2PROM (AT24CO2)保存商品库存数量和价格信息,存储位置要求如下:

                商品X库存数量存储地址:E2PROM内部地址0

                商品Y库存数量存储地址:E2PROM内部地址1

                商品X单价存储地址:E2PROM内部地址2

                商品Y单价存储地址:E2PROM内部地址3

        **注意:
                库存数量或价格发生变动时,数据写入到E2PROM中,无变化时不写入。

                设备重新上电,能够从E2PROM相应地址中载入商品库存数量和价格。

                严格按照试题要求的E2PROM地址写入并保存数据。

串口输出功能        

        使用竞赛板上的USB转串口功能完成以下要求,串口通信波特率设置为9600。

        1)打印输出总价及购买信息
                在商品购买界面下,B4按键按下后,设备串口输出购买商品数量和总价格。数据格式要求:

                X:2,Y:2,Z:4.0
                示例字符串表示购买了2个商品X,2个商品Y,总价为4.0元。总价保留小数点后1位有效数字,输出信息为ASCII 编码字符串。

        2)查询当前单价信息
                在任意界面下,通过串口调试助手,从 PC端向设备发送查询字符‘?’,设备返回当前各类商品单价。
                X:1.0,Y:1.0
                示例字符串表示商品X单价为1.0,商品Y为1.0。
                商品价格保留小数点后1位有效数字,输出信息为ASCII编码字符串。

LED指示灯功能

        1)LD1:在购买界面下,按下B4按键确认购买后,LD1点亮5秒后熄灭。

        2)LD2:若商品X、Y库存数量均为0,指示灯LD2以0.1秒为间隔切换亮灭状
态。

        3)LD3-LD8指示灯始终处于熄灭状态。

PWM输出功能
        在商品购买界面下,通过B4按键确认购买信息后,5秒内通过PAl引脚输出频率为2KHz,占空比为30%的脉冲信号,其余时间频率不变,占空比为5%。

初始状态说明

        请严格按照下列要求设计作品上电后的初始状态:

                1)商品X:库存数量10,单价1.0。
                2)商品Y:库存数量10,单价1.0。
                3)上电后,处于商品购买界面,商品X、Y购买数量为0。


真题讲解系列文章重点关注顶层逻辑代码编写,各模块的代码编写大家可点击蓝桥杯嵌入式专题,里面有各个模块的详细解析与代码编写 

 赛题分析

        首先观察硬件框图,同样的,除了老三样(LED,按键,LCD)以外,就是本届赛题的重点考试内容了。显然本次考察重点是串口通信与EEPROM。这里插一个题外话,将近几年的省赛真题都做了一遍的小伙伴一个都能发现,最常见的组合就是串口通信+其他,本届又考了EEPROM,大胆预测,2023年可能就是考串口通信加PWM或模数转换了。

        回归正题,已经知道本届赛题考核的内容后,我们接着看功能要求中的功能概述。串口通信主要用到的是串口的发送,根据按键信号还发送商品购买数量,总金额,当然,往下仔细看,会发现串口还有一个功能要实现,就是接收串口数据,识别为"?"后,再通过串口发送商品单价。而EEPROM就是存储商品库存与商品单价了。其他比如购买,加库存等操作都是通过按键来实现的。

代码实现

按键\EEPROM模块

        按键的操作比较多,除了必有的界面切换,就是在特定界面下,商品的购买数量与单价的加与减。题目中的每个模块中的主要事项是一定要去仔细看的。还包括了按下购买按键后,使用串口发送数据。

        赛题还要求,将商品的库存与商品单价存储到EEPROM中,并且在每次有数值改变时,就要将改变后的数值存入EEPROM中。而数值的改变都是通过按键实现的,因此我们直接在按键判断中,改变数值的后面加上EEPROM的写入,在每次程序运行之初读取即可完成这项功能。

void key_proc()
{
	uint q = 0 ,w = 0;		//记录B4按键按下时刻count的值,与0.1秒闪烁的计时
	
	if(key[0].key_flag == 1)
	{
		view++;
		if(view==3) view=0;
		LCD_Clear(Black);
		key[0].key_flag = 0;
	}
	if(key[1].key_flag == 1)
	{
		if(0 == view)
		{
			if(X_shop < X_rep)	//购买数小于库存数
			{
				X_shop += 1;
			}
		}
		if(1 == view)
		{
			X_price = X_price + 0.1;
			if(X_price > 2.1)			//价格在1.0到2.0之间
			{
				X_price = 1.0;
			}			
			
			uchar x_p = X_price*10;
			eeprom_write(2,x_p);
		}
		if(2 == view)
		{
			X_rep ++;
			eeprom_write(0,X_rep);
		}
		key[1].key_flag = 0;
	}
	if(key[2].key_flag == 1)
	{
		if(0 == view)
		{
			if(Y_shop < Y_rep)	//购买数小于库存数
			{
				Y_shop += 1;
			}
		}
		if(1 == view)
		{
			Y_price = Y_price + 0.1;
			if(Y_price > 2.1)			//价格在1.0到2.0之间	//为什么是2.1呢? 因为double类型的变量,1.9+0.1,有可能等于2.000000001之类的值
			{
				Y_price = 1.0;
			}
			uchar y_p = (int)(Y_price*10);
			eeprom_write(3,y_p);
			/*xp.val = Y_price;			//使用共用体将浮点数存储在EEPROM中
			for(int i = 0; i < sizeof(float); i++)
			{
			 eeprom_write(0x10 + i, xp.data[i]);
			 HAL_Delay(5);
			}*/
		}
		if(2 == view)
		{
			Y_rep ++;
			eeprom_write(1,Y_rep);
		}
		key[2].key_flag = 0;
	}

	if(key[3].key_flag == 1)
	{
		if(0 == view)
		{
			q = count;		//记录按下的那一刻的数值
			TurnOn_LED(1);
			
			double z = X_shop*X_price + Y_shop*Y_price;
							/*串口发送*/
			char temp[20];
			sprintf(temp,"X:%d , Y:%d , Z:%.2f\r\n",X_shop,Y_shop,z);
			HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);	//参数:串口号,要发送的字符,要发送的字符长度
			
			X_rep -= X_shop;
			Y_rep -= Y_shop;
			
			eeprom_write(0,X_rep);
			HAL_Delay(5);
			eeprom_write(1,Y_rep);
			
			X_shop=0,Y_shop=0;
			
			LED_Disp(0x01);
			__HAL_TIM_SetCompare(&htim16,TIM_CHANNEL_1,30);		//设置占空比pa6_duty,然后通过定时器五秒后“30”恢复为“5”
			falg_d = 1;
			
		}
		
		key[3].key_flag = 0;
	}
	
	if(q > count-4 && q < count)		//一个循环后,也就是5秒之后
	{
		TurnOff_LED(1);
		__HAL_TIM_SetCompare(&htim16,TIM_CHANNEL_1,5);
	}
	
	if(X_rep == 0 && Y_rep == 0)
	{
		w = count;
		if(count-w == 10)
		{
			Toogle_LED(2);    //翻转LED2的状态
		}
	}
	else
	{
		TurnOff_LED(2);
	}
}

串口模块

        串口模块主要是接收串口发来的信息,并判断是否为“?”,是则发送商品单价。串口的另外一个功能在按键模块中实现。

void uart_rx_proc()
{
	if(rx_pointer>0)
	{
		if(rx_pointer==1)
		{
			char temp[20];
			sprintf(temp,"X:%.2f,Y:%.2f\n",X_price,Y_price);
			HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);
		}
		else 
		{
				char temp[20];
				sprintf(temp,"Error");
				HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);
		}
		rx_pointer=0;memset(rxdata,0,30);
	}
}

LCD模块

        显示各种参数变量,要注意每一行每一列的显示都要与赛题中的位置一样。

void disp_proc()
{
	if(view==0)
	{
		char text[30];
		sprintf(text,"        SHOP   ");
		LCD_DisplayStringLine(Line1, (uint8_t *)text);
		
		sprintf(text,"     X:%d    ",X_shop);
		LCD_DisplayStringLine(Line3, (uint8_t *)text);
		
		sprintf(text,"     Y:%d    ",Y_shop);
		LCD_DisplayStringLine(Line4, (uint8_t *)text);
	}
	else if(view==1)
	{
		char text[30];
		sprintf(text,"        PRICE    ");
		LCD_DisplayStringLine(Line1, (uint8_t *)text);
		
		sprintf(text,"     X:%.2f    ",X_price);
		LCD_DisplayStringLine(Line3, (uint8_t *)text);
		
		sprintf(text,"     Y:%.2f    ",Y_price);
		LCD_DisplayStringLine(Line4, (uint8_t *)text);
			
	}
	else if(view==2)
	{
		char text[30];
		sprintf(text,"        REP    ");
		LCD_DisplayStringLine(Line1, (uint8_t *)text);
		
		sprintf(text,"     X:%d    ",X_rep);
		LCD_DisplayStringLine(Line3, (uint8_t *)text);
		
		sprintf(text,"     Y:%d    ",Y_rep);
		LCD_DisplayStringLine(Line4, (uint8_t *)text);
	}
}

中断模块

        中断模块中包含按键的扫描,PWM占空比改变,库存为零后的LED灯闪烁与确认购买后LED灯5秒长亮。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)		//定时器回调函数
{
	if(htim->Instance==TIM3)		//判断是定时器3			//分频系数:80,重装载值:1000			//也就是80Mhz的晶振,经过这些数值,这个定时器1秒种会响应100(80M/80/1000)次
	{
		//HAL_UART_RxCpltCallback(&huart1);
		
		key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
		key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
		key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
		key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
		
		for(int i = 0;i < 4; i++ )
		{
			switch (key[i].judge_sta)
			{
				case 0:
				{
					if(key[i].key_sta==0) 
					{
						key[i].judge_sta = 1;	//第一次判断是否按下
						key[i].key_time = 0;
					}
				}
				break;
				case 1:
				{
					if(key[i].key_sta==0)	//进入下一次定时器扫描,按键还是按下状态,那么就确认为按下,以此来消抖
					{
						key[i].judge_sta = 2;
					}
					else
						key[i].judge_sta = 0;
				}
				break;
				case 2:
				{				
					if(key[i].key_sta==1) 		//判断是否松手
					{
						if(key[i].key_time < 100)
						{
							key[i].key_flag = 1;
						}
						key[i].judge_sta = 0;		
					}
					else
					{
						key[i].key_time++;
						if(key[i].key_time > 100)				//一次扫描10毫秒,100次1000毫秒,就是判断是否长按超过1000毫秒//未松手时,就会执行相应反应
						{
							key[i].long_flag = 1;
						}
					}
				}
				break;
			}
		}
		
		if(falg_d > 0)
		{
			falg_d ++;
			LED_Disp(0x01);
			if(falg_d > 500)		//5秒后
			{
				falg_d = 0;
				__HAL_TIM_SetCompare(&htim16,TIM_CHANNEL_1,5);		//设置占空比pa6_duty
				//LED_Dispm(0x01);
				LED_mie();
			}
		}
		
		if(0 == X_rep && 0 == Y_rep)
		{
			s ++;		//0.1秒的循环位
			if(s == 10)
				LED_Disp(0x02);
			if(s == 20)
			{
				s = 0;
				//LED_Dispm(0x02);
				LED_mie();
			}
		}
		count ++;
		if(count == 500)		//每5秒后重新计时
		{
			count = 0;
		}
	}
}

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐