前言
笔者是今年蓝桥杯嵌入式的考生之一,在备战蓝桥杯的过程中,将遇到的难点以及经验总结分享给大家,其中包含基础模块的使用,和每个模块所对应的难点,以及各个模块配置的注意事项,无论是以后的参赛者或是今年的参赛者,这个教程对大家有所帮助,希望大家可以耐心看完,因为肯定会有所收获,希望大家都能猛猛拿奖,上岸上岸!!!
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);
我们控制特定的特定的灯进行点亮的步骤为:
-
设置一个全局变量,我这里用的是led_mark。 -
通过对全局变量led_mark不断与或来控制某个灯的点亮和熄灭。 -
之后只需要将全局变量放入函数中即可。
led_disp(led_mark);
知道了如何点亮一个特定的灯之后,接下来就是如何让它进行闪烁:
这里可以举一个例子:led1每过0.2s闪烁一次。
-
让led1点亮的话,此时要将全局变量进行如下操作:
led_mark=led_mark|0x01;
-
让led2点亮的话,此时要将全局变量进行如下操作:
led_mark=led_mark&0xfe;
-
要让他不断闪烁,只需要每过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;
}
}
}
代码解析:
-
设置两个变量,一个为time一个为cnt; -
由定时器初始化配置可以知道,定时器4为每0.01s进一次中断,为了让0.2s就改变一次led,这个时候需要进20次定时器中断,对time进行%20的操作,相当于每0.2s改变一次led; -
cnt的作用是为了让每0.2s翻转一次led,因为我们每0.2s改变一次led。设置个cnt,就可以让每次改变led的值不一样; -
只需要这样,就能让led1进行周期为0.2s的闪烁; -
再提醒一次,在使用定时器的基本中断功能的时候,要在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;
}
}
}
}
代码解析:
-
以定时器4为基础,每过10ms对按键各个引脚进行一次判断。 -
如果检测到特定引脚变为低电平(按键被按下),key[i].single_sta=1; -
接下来只需要不断在主函数检测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 注意事项
-
在main函数,要手动打开定时器,调用函数为:HAL_TIM_Base_Start_IT(&htim4);(以定时器4为例子) -
按键在没按下的时候,引脚电平为高,按下后,引脚电平为低。
Part3液晶显示屏使用(lcd)
3.1 lcd初始化配置
由于lcd驱动程序是由官方给予我们的,我们只需要将官方给的文件复制到我们的工程下面。
将lcd.c和lcd.h以及fonts.h复制到我们的工程目录下。千万别忘记将它同时添加到我们的工程下面。
3.2 实现代码
-
对屏幕初始化以及设置背景色和文本色。(一般为黑色背景,白色字体)
LCD_Init(); LCD_Clear(Black); LCD_SetBackColor(Black); LCD_SetTextColor(White);
-
实现屏幕显示:
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); }
-
效果演示
代码解析:
-
设置一个数组来存储一行所显示的字符串。 -
通过sprintf()函数,将字符串写入该数组。 -
最后调用void LCD_DisplayStringLine(u8 Line, u8 *ptr),参数1为Linex(需要第几行显示),参数2为(uint8_t *)text,由于函数里面定义的参数2是uint8_t类型的指针变量,我们的数值是char类型的,所以需要把他强转为uint8_t类型的指针变量。 -
还可以将变量一起显示。
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 注意事项
-
要将特定文件添加到工程里面; -
要记得调用LCD_Init(),来对lcd初始化; -
lcd一共有10行,每行有20列,切记不要写多,不然显示会发生错误。
Part4ADC电压采集
4.1 CubeMX初始化配置
这里以PB15举例。
-
勾选IN15 Single_ended选项。 -
将模式选为independent mode。 -
其他全为默认。
4.2 实现代码
double ADC_Getvalue(ADC_HandleTypeDef *hadc)
{
double adc;
adc=HAL_ADC_GetValue(hadc);
HAL_ADC_Start(hadc);
return adc*3.3/4096;
}
代码解析:
-
ADC转换的代码较为简单,只有短短几行,但是同样不能疏忽。 -
两个关键函数,HAL_ADC_GetValue(hadc);HAL_ADC_Start(hadc);采集电压值和开启ADC转换。 -
由于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