蓝桥杯嵌入式总结和考前冲刺详细

前言

笔者是今年蓝桥杯嵌入式的考生之一,在备战蓝桥杯的过程中,将遇到的难点以及经验总结分享给大家,其中包含基础模块的使用,和每个模块所对应的难点,以及各个模块配置的注意事项,无论是以后的参赛者或是今年的参赛者,这个教程对大家有所帮助,希望大家可以耐心看完,因为肯定会有所收获,希望大家都能猛猛拿奖,上岸上岸!!!

Part1led模块控制

1.1 CubeMX初始化配置

led相关引脚有九个,其中八个为led驱动引脚,另外一个为led锁存引脚。

由板子的原理图中可以看到,因为led驱动引脚接了led灯的负级,所以CubeMX中将每个引脚全部设为high,让灯初始化状态全为灭。锁存引脚也默认为高电平,表示不锁存。

1.2 实现代码

void led_disp(uint16_t led)
{
	HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOC,led<<8,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

1.3 注意事项

不知道大家会不会遇到灯在加了lcd之后,灯会不受控制的乱跳,这是因为led的引脚和lcd的引脚发生了冲突,为了解决这个问题,只需要在执行lcd函数前面加上一句:

HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);

这句代码是为了让led锁存引脚有效,由于CubeMX初始化led为全灭的状态,只需要在lcd与led冲突之前,将锁存引脚锁住,即使lcd会改变led的引脚,但是锁存引脚会让led始终保持原状态。

led一大难点(如何让他不断闪烁):

目前,很多题都会设置某个led在0.2s之内不断地闪烁,其实这个也是有套路的。

前面可以知道,我们的led的控制函数为:

void led_disp(uint16_t led);

我们控制特定的特定的灯进行点亮的步骤为:

  1. 设置一个全局变量,我这里用的是led_mark。
  2. 通过对全局变量led_mark不断与或来控制某个灯的点亮和熄灭。
  3. 之后只需要将全局变量放入函数中即可。
led_disp(led_mark);

知道了如何点亮一个特定的灯之后,接下来就是如何让它进行闪烁:

这里可以举一个例子:led1每过0.2s闪烁一次。

  1. 让led1点亮的话,此时要将全局变量进行如下操作:

    led_mark=led_mark|0x01;
  2. 让led2点亮的话,此时要将全局变量进行如下操作:

    led_mark=led_mark&0xfe;
    
  3. 要让他不断闪烁,只需要每过0.2s就改变一次led_mark。

这个时候,问题来了,该如何让他每过0.s就改变一次led_mark呢?方法很简单,只需要采用定时器中断。

使用cubemx将定时器4初始化,切记要开NVIC全局中断,不然定时器不会进入中断回调函数。

代码如下:

uint32_t time;
uint8_t cnt;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{

if(htim->Instance==TIM4)
{

time++;
if(time&20==0)
{

cnt++;
if(cnt%2==0)
{
led_mark=led_mark|0x01;
}
else
{
led_mark=led_mark&0xfe;
}

}

}

代码解析:

  1. 设置两个变量,一个为time一个为cnt;
  2. 由定时器初始化配置可以知道,定时器4为每0.01s进一次中断,为了让0.2s就改变一次led,这个时候需要进20次定时器中断,对time进行%20的操作,相当于每0.2s改变一次led;
  3. cnt的作用是为了让每0.2s翻转一次led,因为我们每0.2s改变一次led。设置个cnt,就可以让每次改变led的值不一样;
  4. 只需要这样,就能让led1进行周期为0.2s的闪烁;
  5. 再提醒一次,在使用定时器的基本中断功能的时候,要在main函数中调用;
HAL_TIM_Base_Start_IT(&htim4);

来开启定时器和中断。

Part2按键(key)控制

2.1 CubeMX初始化配置:

按键可以说是每年必考的,这个模块十分重要,大家一定要理解透彻和熟练。

因为按键有4个,分别对应4个引脚,只需将4个对应的GPIO引脚设置为输入模式。

2.2 实现代码

struct keys 
{
	char key_sta;
	char judge_sta;
	char single_sta;
};

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM4)
{
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(uint8_t i=0;i<4;i++)
{
switch(key[i].judge_sta)
{
case 0:
if(key[i].key_sta==0)
{
key[i].judge_sta=1;
}
break;
case 1:
if(key[i].key_sta==0)
{
key[i].judge_sta=2;
key[i].single_sta=1;
}
else
{
key[i].judge_sta=0;
}
break;
case 2:
if(key[i].key_sta==1)
{
key[i].judge_sta=0;
}
break;
}

}
}
}

代码解析:

  1. 以定时器4为基础,每过10ms对按键各个引脚进行一次判断。
  2. 如果检测到特定引脚变为低电平(按键被按下),key[i].single_sta=1;
  3. 接下来只需要不断在主函数检测key[i].single_sta是否为1,就可以知道哪个按键被按下。
void key_scan(void)
{
	if(key[0].single_sta==1)
	{		
		key[0].single_sta=0;
	}
	if(key[1].single_sta==1)
	{
		key[1].single_sta=0;
	}
	if(key[2].single_sta==1)
	{
		key[2].single_sta=0;
	}
	if(key[3].single_sta==1)
	{
		key[3].single_sta=0;
	}
}

如发现key[i].single_sta==1,记得将它变回0,不然单片机会认为一直被按下。

2.3 注意事项

  1. 在main函数,要手动打开定时器,调用函数为:HAL_TIM_Base_Start_IT(&htim4);(以定时器4为例子)
  2. 按键在没按下的时候,引脚电平为高,按下后,引脚电平为低。

Part3液晶显示屏使用(lcd)

3.1 lcd初始化配置

由于lcd驱动程序是由官方给予我们的,我们只需要将官方给的文件复制到我们的工程下面。

将lcd.c和lcd.h以及fonts.h复制到我们的工程目录下。千万别忘记将它同时添加到我们的工程下面。

3.2 实现代码

  1. 对屏幕初始化以及设置背景色和文本色。(一般为黑色背景,白色字体)

    LCD_Init();
    LCD_Clear(Black);
    LCD_SetBackColor(Black);
    LCD_SetTextColor(White);
    
  2. 实现屏幕显示:

    void lcd_disp(void)
    {
    	char text[20];
    	sprintf(text,"       DATA");
    	LCD_DisplayStringLine(Line1,(uint8_t *)text);
    	sprintf(text,"Volt:%.2f",adc_value);
    	LCD_DisplayStringLine(Line3,(uint8_t *)text);
    	sprintf(text,"D:%d%%",duty);
    	LCD_DisplayStringLine(Line4,(uint8_t *)text);
    	sprintf(text,"F:%5dHz",frq);
    	LCD_DisplayStringLine(Line5,(uint8_t *)text);
    }
    
  3. 效果演示

代码解析:

  1. 设置一个数组来存储一行所显示的字符串。
  2. 通过sprintf()函数,将字符串写入该数组。
  3. 最后调用void LCD_DisplayStringLine(u8 Line, u8 *ptr),参数1为Linex(需要第几行显示),参数2为(uint8_t *)text,由于函数里面定义的参数2是uint8_t类型的指针变量,我们的数值是char类型的,所以需要把他强转为uint8_t类型的指针变量。
  4. 还可以将变量一起显示。
float adc_value;
sprintf(text,"Volt:%.2f",adc_value);

这样就可以显示变量的值了。

lcd如何高亮一行?

这是第十届的真题,需要对特定的一行进行高亮,看起来很高级,实际实现的方法很简单。

实现代码:

 
char text[20];
sprintf(text,"      Setting    ");
LCD_DisplayStringLine(Line1,(uint8_t *)text);
if(para_choose==0) LCD_SetBackColor(Green);
sprintf(text," Max Volt:%.1fV       ",Max_Volt);
LCD_DisplayStringLine(Line3,(uint8_t *)text);
LCD_SetBackColor(Black);
if(para_choose==1) LCD_SetBackColor(Green);
sprintf(text," Min Volt:%.1fV       ",Min_Volt);
LCD_DisplayStringLine(Line5,(uint8_t *)text);
LCD_SetBackColor(Black);
if(para_choose==2) LCD_SetBackColor(Green);
sprintf(text," Upper:LD%d          ",Upper_led);
LCD_DisplayStringLine(Line7,(uint8_t *)text);
LCD_SetBackColor(Black);
if(para_choose==3) LCD_SetBackColor(Green);
sprintf(text," Lower:LD%d          ",Lower_led);
LCD_DisplayStringLine(Line9,(uint8_t *)text);
LCD_SetBackColor(Black);

代码解析:

实际上就是不断的改变背景色,在写入行直接,将背景色改为绿色,写入之后,在将背景色改回为黑色,最后就会呈现出特定的一行高亮了。

实现效果图:

3.3 注意事项

  1. 要将特定文件添加到工程里面;
  2. 要记得调用LCD_Init(),来对lcd初始化;
  3. lcd一共有10行,每行有20列,切记不要写多,不然显示会发生错误。

Part4ADC电压采集

4.1 CubeMX初始化配置

这里以PB15举例。

  1. 勾选IN15 Single_ended选项。
  2. 将模式选为independent mode。
  3. 其他全为默认。

4.2 实现代码

double ADC_Getvalue(ADC_HandleTypeDef *hadc)
{
	double adc;
	adc=HAL_ADC_GetValue(hadc);
	HAL_ADC_Start(hadc);
	return adc*3.3/4096;
}

代码解析:

  1. ADC转换的代码较为简单,只有短短几行,但是同样不能疏忽。
  2. 两个关键函数,HAL_ADC_GetValue(hadc);HAL_ADC_Start(hadc);采集电压值和开启ADC转换。
  3. 由于ADC的精度是12位的,和参考电压为3.3V,所以return adc*3.3/4096;

Part5pwm输出(控制占空比和频率)

5.1 pwm输出原理

该图片由江科大stm32入门教程提供,相关知识点可以上网自行查找江科大pwm章节教程学习。

5.2 CubeMX初始化配置

根据题目需要输出pwm的引脚来进行相应引脚的配置,我这里要求为PA1输出可调PWM波。

相应定时器配置为PWM Generation CHX,时基单元设置psc的值和counter值,在pwm配置里设置Pulse的值。

5.3 实现代码

HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);

这样就开启了pwm输出,这个时候输出的频率和占空比为CubeMX配置时所设置的

如何改变pwm的频率和占空比呢?

我们这里就需要两条公式了:

输出波频率:Freq=CK_PSC/(PSC+1)/(ARR+1);

原创文章,作者:guozi,如若转载,请注明出处:https://www.sudun.com/ask/80741.html

(0)
guozi的头像guozi
上一篇 2024年5月31日 上午10:22
下一篇 2024年5月31日

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注