【蓝桥杯嵌入式】第十四届蓝桥杯嵌入式省赛[第一场]程序设计题以及详细题解

慈云数据 1年前 (2024-03-12) 技术支持 140 0

文章目录

  • 原题展示
  • 原题分析
  • 原题题解
    • LED相关
    • LCD相关
    • 按键相关
    • ADC相关
    • 定时器相关
      • PWM
      • 输入捕获
      • 小结
      • 文章福利

        原题展示

        在这里插入图片描述

        在这里插入图片描述

        在这里插入图片描述

        在这里插入图片描述

        在这里插入图片描述

        原题分析

          今年的第一场比赛绝对np,官方将串口直接省掉了,将其替换成很多小功能,如:切换计时、频率均匀变化、锁机制等等,总的来说本届赛题的难度提升了不少。

          本届试题需要用到的功能模块有LCD、LED、按键、定时器输入捕获、定时器PWM输出、ADC获取,虽然这届试题模块简单,但是功能实现一点也不简单,感觉跟十二届省赛一样😂😂😂。

          还值得注意的是:本届试题有三个地方需要计时,即模式切换、LED闪烁与长按键,,这可能是蓝桥杯为了提升难度的一个方向。(小编感觉这计时真的是恶心🤣🤣🤣)

        原题题解

        LED相关

          通过查询产品手册知,LED的引脚为PC8~PC15,外加锁存器74HC573需要用到的引脚PD2。(由于题目要求除题目要求需要使用的LED外其他LED都处于熄灭状态,此处特意将所有的LED都初始化以便于管理其他的LED灯)

        CubeMX配置:

        代码样例

          由于G431的所有LED都跟锁存器74HC573连接,因此每次更改LED状态时都需要先打开锁存器,写入数据后再关闭锁存器。

        /*****************************************************
        * 函数功能:改变所有LED的状态
        * 函数参数:
        *			char LEDSTATE: 0-表示关闭 1-表示打开
        * 函数返回值:无
        ******************************************************/
        void changeAllLedByStateNumber(char LEDSTATE)
        {
        	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_8
        					|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET));
        	//打开锁存器    准备写入数据
        	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
        	//关闭锁存器 锁存器的作用为 使得锁存器输出端的电平一直维持在一个固定的状态
        	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
        }
        /*****************************************************
        * 函数功能:根据LED的位置打开或者是关闭LED
        * 函数参数:
        *			uint16_t LEDLOCATION:需要操作LED的位置
        *			char LEDSTATE: 0-表示关闭 1-表示打开
        * 函数返回值:无
        ******************************************************/
        void changeLedStateByLocation(uint16_t LEDLOCATION,char LEDSTATE)
        {
        	HAL_GPIO_WritePin(GPIOC,LEDLOCATION,(LEDSTATE==1?GPIO_PIN_RESET:GPIO_PIN_SET));
        	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
        	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
        }
        

          试题要求的LED显示其条件都比较单一,在满足点亮条件时直接点亮,否则,就直接熄灭即可,至于闪烁的周期控制,可以借助与sysTick中断实现,如果就此再开一个定时器就有点浪费资源了。(虽然小编以前经常这样子干🤣🤣🤣)

          小编写的LED工作逻辑函数:

        /***************************************
        * 函数功能:LED显示逻辑函数
        * 函数参数:无
        * 函数返回值:无
        ***************************************/
        static void ledWork(void)
        {
        	// 数据界面LED1电量
        	if(mod == 0)
        	   changeLedStateByLocation(LED1,1);
        	else
        	   changeLedStateByLocation(LED1,0);
        	
        	// 切换期间 LED2闪烁
        	if(LED2Flag && sysCount[1] >= 100)
        	{
        		rollbackLedByLocation(LED2);
        		sysCount[1] = 0;
        	}
        	else if(!LED2Flag)		
        	   changeLedStateByLocation(LED2,0);
        	
        	// 锁定模式下 LED3电量
        	if(lock)
        	   changeLedStateByLocation(LED3,1);
        	else
        	   changeLedStateByLocation(LED3,0);
        }
        

        LCD相关

        样例代码

            由于LCD的相关代码在官方给的比赛资源数据包中存在,因此,可以直接调用资源包中的.c、.h文件来完成LCD的相关初始化以及显示。这是一个简单的LCD初始化函数,其功能是将LCD显示屏初始化为一个背景色为黑色、字体颜色为白色的屏幕,具体代码如下:

        /******************************************************************************
        * 函数功能:LCD初始化
        * 函数参数:无
        * 函数返回值:无
        *******************************************************************************/
        void lcdInit(void)
        {
        	//HAL库的初始化
        	LCD_Init();
        	//设置LCD的背景色
        	LCD_Clear(Black);
        	//设置LCD字体颜色
        	LCD_SetTextColor(White);
        	//设置LCD字体的背景色
        	LCD_SetBackColor(Black);
        }
        

            在显示时,可以借助于sprintf()函数将需要显示的数据格式成一个字符串,再在LCD上显示这个字符串。

        	char temp[20];
        	sprintf(temp,"     M=%c     ",mTable[M]);
        	LCD_DisplayStringLine(Line3,(u8*)temp);
        

            为了操作LED与LCD显示方便,不让其相互干扰,小编这里对LCD进行了部分源码改写,使得每次LCD显示时不改变LED的显示状态,具体的方法各位可以点击查看【蓝桥杯】一文解决蓝桥杯嵌入式开发板(STM32G431RBT6)LCD与LED显示冲突问题,并讲述LCD翻转显示。

            下面附上小编完成的LCD部分的详细代码:

         /***************************************
        * 函数功能:LCD显示数据
        * 函数参数:无
        * 函数返回值:无
        ***************************************/
        static void lcdDisplay()
        {
        	char temp[20];
        	extern uint32_t  cclValue ;
        	// 数据显示界面
        	if(mod == 0)
        	{
        	    LCD_DisplayStringLine(Line1,(u8*)"        DATA  ");
        		sprintf(temp,"     M=%c     ",mTable[M]);
        		LCD_DisplayStringLine(Line3,(u8*)temp);
        		sprintf(temp,"     P=%d%%     ",pa1Zhan[1]);
        		LCD_DisplayStringLine(Line4,(u8*)temp);
        		sprintf(temp,"     V=%.1f     ",V);
        		LCD_DisplayStringLine(Line5,(u8*)temp);
        		
        	}
        	// 参数显示界面
        	else if(mod == 1)
        	{
        		LCD_DisplayStringLine(Line1,(u8*)"        PARA  ");
        		sprintf(temp,"     R=%d     ",RK[0]);
        		LCD_DisplayStringLine(Line3,(u8*)temp);
        		sprintf(temp,"     K=%d     ",RK[1]);
        		LCD_DisplayStringLine(Line4,(u8*)temp);
        		LCD_ClearLine(Line5);
        	}
        	// 统计界面
        	else if(mod == 2)
        	{
        		LCD_DisplayStringLine(Line1,(u8*)"        RECD   ");
        		sprintf(temp,"     N=%d    ",N);
        		LCD_DisplayStringLine(Line3,(u8*)temp);
        		sprintf(temp,"     MH=%.1f     ",MH);
        		LCD_DisplayStringLine(Line4,(u8*)temp);
        		sprintf(temp,"     ML=%.1f     ",ML);
        		LCD_DisplayStringLine(Line5,(u8*)temp);
        	}
        }
        

        (是不是非常简单粗暴。哈哈哈哈)

        按键相关

            通过查询产品手册知,开发板上的四个按键引脚为PB0~PB2、PA0。

        CubeMX配置

        代码样例

          由于题中涉及到长按键,因此此处将不再使用延时消抖,可以使用三行按键完成长按键与短按键设计,其核心代码就是三行逻辑运算完成消抖等一系列操作,但是在轮询系统中可能会存在漏检的问题。其完整代码如下:

        // 声明获取按键的状态值
        #define getKeysState()    (	HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) 
        	// 获取按键状态
        	uint16_t state = getKeysState();
        	// 对按键的值进行异或处理 一次判断
        	uint8_t key_temp = 0xFF ^ (0xF0 | state);
        	
        	/*** 通过逻辑运算处理消抖 **/
        	// 按下状态
        	keyFalling = key_temp & (key_temp ^ keyOldState);
        	// 松开状态
        	keyRising = ~key_temp & (key_temp ^ keyOldState);
        	// 保存本次按键的值
        	keyOldState = key_temp;
        }
        
        	signed char i = 0;
        	// 按键扫描
        	keyRefresh();
        	
        	// 按键按下 并且按键的值等于8也就是B4
        	if(keyFalling == 8)
        		uwKeyTick = HAL_GetTick();
        	
        	switch(keyRising)
        	{
        		// 按键B1
        		case 1:
        			mod++;
        		  // 每次进去参数界面默认参数为R
        		  if(mod == 1) rkCount = 0;
        			// 退出参数界面 
        			if(mod != 1 )
        			{
        				// 遍历参数 并且刷新
        				for(i=0;i
        				sysCount[0] = 0;
        				LED2Flag = 1;
        			}
        			// 参数界面
        			if(mod == 1)
        						rkCount ^= 1;
        			break;
        		// 按键B3
        		case 4:
        			// 参数界面 加1
        			if(mod == 1)
        				if(++RKOld[rkCount] == 11) RKOld[rkCount] = 1;
        			break;
        		// 按键B4
        		case 8:
        			// 数据界面 
        			if(mod == 0 )
        				// 长按键锁住
        				if(HAL_GetTick() - uwKeyTick  2000)
        					 lock = 1;
        			  // 短按键解锁
        				else
        					lock = 0;
        			// 参数界面 减1
        			else if(mod == 1)
        				if(--RKOld[rkCount] == 0) RKOld[rkCount] = 10;
        			break;
        		// 其他
        		default:
        			break;
        	}
        	
        	// 延时5秒切换
        	if(LED2Flag&& sysCount[0]=5000)
        	{
        		M ^= 1;
        		// 切换次数增加 
        		N++;
        		LED2Flag = 0;
        	}
        }
        
        	unsigned int value = 0,i = 0;
        	//开启转换ADC并且获取值
        	HAL_ADC_Start(hadc);
        	for(i=0;i
        	    HAL_ADC_PollForConversion(hadc,10);
        		value += HAL_ADC_GetValue(hadc);
        	}
        	//ADC值的转换 3.3V是电压 4096是ADC的精度为12位也就是2^12=4096
        	return value/10*3.3/4096;
        }
        
        		if(0
        			__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,pa1F[M]*pa1Zhan[1]/100);
        			pa1Zhan[0] = pa1Zhan[1];
        		}
        	}
        
        	// 保存TIMx_CCR的值
        	uint32_t  cclValue = 0;
        	// 定时器3时执行该段
        	if(htim-Instance == TIM3)
        	{
        		cclValue = __HAL_TIM_GET_COUNTER(&htim3);
        		__HAL_TIM_SetCounter(&htim3, 0);
        		f = 1000000 / cclValue;
        		HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2);
        	}
        }
        
        		V = (f*2*3.14*RK[0]*1.0)/(100*RK[1]);
        		fOld = f;
        		sysCount[2] = 0;
        	}
        
微信扫一扫加客服

微信扫一扫加客服