蓝桥杯嵌入式——第十届蓝桥杯嵌入式国赛

蓝桥杯嵌入式——第十届蓝桥杯嵌入式国赛

一、赛题

上一次做了第十一届的国赛赛题,感觉很容易。然后这两天做了第十届的赛题,感觉第十届的赛题是十一届难度的两倍。这一届的赛题考察的内容特别多,并且逻辑方面相比十一届也是难度大一些的,所以正式比赛的时候,如果不是特别熟悉,还有可能不能把全部的功能都实现。这一届考察的内容大致如下:

LED
LCD,LCD的高亮显示
独立按键,长按
双通道ADC转换
定时器的输入捕获,检测占空比
EEPROM数据的读写,使用EEPEOM存放16位数据
DS18B20,精确到两位小数
USAR串口的收发
数码管显示

这一届的题目还是挺有意义的,难度有一丢丢大,拿来赛前练手挺好的。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、CubeMX模块配置

  1. DMA的配置
    用到一个DMA,用于ADC2的双通道转换
    在这里插入图片描述

  2. GPIO的配置
    在这里插入图片描述

  3. 中断的配置
    打开相应的中断,注意图上标红的地方,要关闭ADC的DMA中断,否则会不断的进入中断,打断CPU的执行。在这里插入图片描述

  4. ADC2的配置
    主要注意一下,我们用到了ADC2的两个通道,所以这里我们使用了DMA,每个通道转换完成的数据就会由DMA直接将对应的数据存放到相应的存储器中,所以我们后面只需要读取对应的存储器的值即可。如果不使用DMA也是可以的, 要麻烦一点,网上有很多资料,这里就不多说。
    ADC配置需要注意的是,要先添加DMA,然后才能在ADC的参数设置中使能ADC DMA转换,除此之外还要使能扫描转换,以及连续转换模式,否则只能自动转换一次ADC的数据,后面的每一次都要重新使能。
    在这里插入图片描述

  5. 定时器输入捕获
    其中81 - 1 分频是将定时器的时钟配置为1MHz。因为在系统时钟为80MHz。重转载计数器一定要设置为最大,对于16位的寄存器最大值也就是65535。
    在这里插入图片描述

  6. 串口的配置
    在这里插入图片描述

三、部分模块代码

  1. 数码管
#define			SEG_H			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET)
#define			SEG_L			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET)
#define			RCK_H			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET)
#define			RCK_L			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET)
#define			SCK_H			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET)
#define			SCK_L			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET)
uint
8_t seg_buf[] = {
    
    0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x39,0x77,0x00};

void seg_set(uint8_t seg1,uint8_t seg2,uint8_t seg3)
{
    
    
	uint32_t seg = (seg_buf[seg3] << 16) | (seg_buf[seg2] << 8) | seg_buf[seg1];
	uint8_t i;
	for(i = 0; i < 24; i++)
	{
    
    
		if(seg & 0x800000)
			SEG_H;
		else
			SEG_L;
		seg <<= 1;
		SCK_H;
		SCK_L;
	}
	RCK_H;
	RCK_L;
}
  1. LCD高亮显示
    关于高亮显示的具体原理可以参考我的另一篇博客https://blog.csdn.net/qq_43715171/article/details/115238360
void highlight(u8 Line, u8 *ptr)
{
    
    
	u32 i = 0;
	u16 refcolumn = 319;//319;

	while ((*ptr != 0) && (i < 20))	 //	20
	{
    
    
		if(((Line == Line3 && select == 0) || (Line == Line4 && select == 1)) && (i > 0 && i < 18))
			LCD_SetBackColor(Yellow);
		else
			LCD_SetBackColor(Black);
		LCD_DisplayChar(Line, refcolumn, *ptr);
		refcolumn -= 16;
		ptr++;
		i++;
	}
}
  1. 按键的长按检测
    这里要解释一下,下面这一段程序是放在key_scan()里面,10ms扫描一次。
    以B3为例,假设此前是松开状态,如果B3按下,key_cnt就加1,加到90的时候,就触发长按,也就是90*10 = 900ms。然后将key_cnt设置为80,等到下一次到达90的时候,经过了(90-80)*10 = 100ms,也就是长按触发之后,没100ms将temp_upper_temp加1。如果没有按键按下要将key_cnt清零,以等待下一次的检测。
if(key_state == B3 && interface == PARA && select == 0 && ++key_cnt == 90)
	{
    
    
		key_cnt = 80;
		if(temp_upper_temp < 40)
			temp_upper_temp++;
	}
	if(key_state == B4 && interface == PARA && select == 0 && ++key_cnt == 90)
	{
    
    
		key_cnt = 80;
		if(temp_upper_temp > 20)
			temp_upper_temp--;
	}
	if(key_state == 0)
	{
    
    
		key_cnt = 0;
	}
  1. 占空比检测的代码
    核心程序就在这里回调函数中了。
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    
    
	if(htim -> Instance == TIM3)
	{
    
    
		if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
		{
    
    
			if(CaptureNumber == 0)
			{
    
    
				/* Get the Input Capture value */
				IC3ReadValue1 = TIM3->CCR2;
				CaptureNumber = 1;
				__HAL_TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_2,TIM_INPUTCHANNELPOLARITY_FALLING);
			}
			else if(CaptureNumber == 1)
			{
    
    
				/* Get the Input Capture value */
				IC3ReadValue2 = TIM3->CCR2;
				CaptureNumber = 2;
				__HAL_TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_2,TIM_INPUTCHANNELPOLARITY_RISING);
				if (IC3ReadValue2 > IC3ReadValue1)
				{
    
    
					Capture_High = (IC3ReadValue2 - IC3ReadValue1); 
				}
				else
				{
    
    
					Capture_High = ((0xFFFF - IC3ReadValue1) + IC3ReadValue2); 
				}
				IC3ReadValue1 = IC3ReadValue2;
			}
			else if(CaptureNumber == 2)
			{
    
    
				/* Get the Input Capture value */
				IC3ReadValue2 = TIM3->CCR2;
				CaptureNumber = 0;
				if (IC3ReadValue2 > IC3ReadValue1)
				{
    
    
					Capture_Low = (IC3ReadValue2 - IC3ReadValue1); 
				}
				else
				{
    
    
					Capture_Low = ((0xFFFF - IC3ReadValue1) + IC3ReadValue2); 
				}
				/* Frequency computation */ 
				TIM3Freq = (uint32_t) 1000000 / (Capture_Low + Capture_High);
				TIM3Duty = Capture_High * 1.0 / (Capture_Low + Capture_High);
				pwm2 = TIM3Duty;
			}
		}
	}
}
  1. 串口的不定长数据接收以及数据的处理
    在我最近的一篇博客中,比较详细的讲解了使用IDLE+RXNE实现的串口的不定长数据接收 ,这里就不展开讲解啦。
    https://blog.csdn.net/qq_43715171/article/details/117132151

数据的处理代码如下:


void usart_proc(void)
{
    
    
	if(channel_x == A01 && a01 > pwm2 * 3.3 && usart_flag)
	{
    
    
		usart_flag = 0;
		printf("$%.2f\r\n",temperature);
	}
	else if(channel_x == A02 && a02 > pwm2 * 3.3 && usart_flag)
	{
    
    
		usart_flag = 0;
		printf("$%.2f\r\n",temperature);
	}
	
	if((channel_x == A01 && a01 <= pwm2 * 3.3) || (channel_x == A02 && a02 <= pwm2 * 3.3))
	{
    
    
		usart_flag = 0;
		usart_tick = 0;
	}
	
	if(UART1_IDLE_Flag)
	{
    
    
		UART1_IDLE_Flag = 0;
		
//		memset(lcd_str,0,sizeof(lcd_str));
//		snprintf((char*)lcd_str,20,"%s",RxBuffer);
//		LCD_DisplayStringLine(Line9, lcd_str);
		
		if(strcmp((const char *)RxBuffer,"ST\r\n") == 0)
		{
    
    
			printf("$%.2f\r\n",temperature);
		}
		else if(strcmp((const char *)RxBuffer,"PARA\r\n") == 0)
		{
    
    
			printf("#%d,AO%d\r\n",temp_upper,channel_x + 1);
		}
		
		memset(RxBuffer,0,sizeof(RxBuffer));
		RxCounter = 0;
	}
}

四、完整代码下载

代码使用说明,一定要看

完整代码下载点我

猜你喜欢

转载自blog.csdn.net/qq_43715171/article/details/117157770