
蓝桥杯嵌入式第十四届省赛
我本溪流,奈何心有不甘,心向大海。固想改变自己,虽是小比赛,也是想记录自己成长的过程,行之所行,也是想与诸君探讨。
·
目录
前言
我本溪流,奈何心有不甘,心向大海。固想改变自己,虽是小比赛,也是想记录自己成长的过程,行之所行,也是想与诸君探讨。
一、分析题型
第十四届省赛其实题目考的很常规,只不过刚接触这个开发板,所以花的时间久了点,不说废话了上正题。回归题目,考了定时器的pwm和输入捕获频率,考了adc的采集,还有按键的长按短按,等等,挺适合练手。系统框图如下。
二、解题步骤
1.大体思路
显示界面->输入数据(按键,adc采集,频率采集)->逻辑处理->输出数据(pwm,led)
2.难点分析
1.长短按键处理
题目要求:
在数据界面下,长按 B4 按键超过 2 秒后松开(长按键),可以“锁定” 占空比调整功能,此时输出信号占空比保持不变,不受 R37 电位器输出电压控制;处于“锁定”状态后,再次按下 B4 按键(短按键),实现“解锁” 功能,恢复 R37 电位器对输出信号占空比的控制。
解析:
在使用按键时,在进行消抖处理时,我们一般用定时器(10ms)加状态机的思路来做的处理,那么思路打开一点,在结构体里面加上判断上下沿以及上下沿所用的时间,那么就可以通过时间长短来确定长(短)按。
代码如下(按键处理):
//按键判断消抖 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { uchar i; if(htim->Instance == TIM6) { /*------------------------- 按键识别与判断 --------------------*/ 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(i=0;i<4;i++) { switch(key[i].key_juadg_sta)//通过标志位key[i].key_juadg_sta的状态来实现状态机的思路 { case 0: if(key[i].key_sta==GPIO_PIN_RESET) { key[i].key_juadg_sta=1; key[i].key_time=0; } break; case 1: if(key[i].key_sta==GPIO_PIN_RESET) { key[i].key_juadg_sta=2; } else key[i].key_juadg_sta=0; break; case 2: if(key[i].key_sta==GPIO_PIN_SET) { key[i].key_juadg_sta=0; if(key[i].key_time<70)//短按 key[i].key_down=1; }else { key[i].key_time++; if(key[i].key_time>200) { key[i].key_up=1;//长按 } } break; } } } }
2.pwm控制(频率的变换)及输出
题目要求:
在保证占空比不变的前提下,频率在 5 秒内均匀的升高或降低 到目标频率,要求频率步进值小于 200Hz。
解析:
我认为这个是本题最难的部分,先知道相关知识点:adc采集R37的电压,以及pwm修改占空比和修改arr的值
公式:频率 = 主频 / (预分频系数+1) / (重装载值+1)
相关函数:
__HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz);//设置arr的值
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100);//设置占空比
在不改变占空比,那么在变化时就要不断设置占空比以及arr的值才能保证不变,
频率在 5 秒内均匀的升高或降低 到目标频率,要求频率步进值小于 200Hz。这个就要考验做题人对定时器的掌握了,我们可以通过滴答计时器来定时,通过40ms的进行一次设置占空比以及arr的值,40ms*100=4s<5s且
频率步进值小于 200Hz。符合要求,
代码如下(pwm处理)static float pwm_bjz=125-1;//步进量 static uint16_t pwm_bjs=0;//步进次数 /*-------------------------获取adc的值 ------------------*/ if(!pwm_lolk)//非锁定 { //边界处理 /*y=75/2x-55/2*/ pwm_duty=(uint8_t)(75*Get_adc_R37(&hadc2)/2-55/2); if(pwm_duty>85) pwm_duty=85; if(pwm_duty<10) pwm_duty=10; } /*-------------------------设置占空比 ------------------*/ if(time5s_flag)//模式切换 频率在 5 秒内均匀的升高或降低到目标频率 { if(uwTick-time_40ms>40) //40ms进行一次步进 { if(pwm_mode==0) pwm_bjz+=1.25f;//步进量=125/100=1.25 else pwm_bjz-=1.25f; __HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz);//设置arr的值 __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100);//设置占空比 time_40ms=uwTick; pwm_bjs++; } if(pwm_bjs>=100) { if(!pwm_mode) pwm_bjz=249; else pwm_bjz=125-1; __HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz); __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100); pwm_mode_disp=pwm_mode; pwm_bjs=0; time5s_flag=0; } }else//普通模式 { if(!pwm_mode) pwm_bjz=250-1; else pwm_bjz=125-1; __HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz); __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100); } /*-------------------------处理速度 ------------------*/ if(pwm_mode)//高频 { if(times_2s_mh_flag==0&&MH_val<real_temp_v) { times_2s_mh_flag=1; time_2s=uwTick; }else { if(MH_val<real_temp_v) { if(uwTick-time_2s>2000) { MH_val=real_temp_v; times_2s_mh_flag=1; } } else times_2s_mh_flag=0; } }else//低频 { if(times_2s_mh_flag==0&&real_temp_v>ML_val) { times_2s_mh_flag=1; time_2s=uwTick; }else { if(real_temp_v>ML_val) { if(uwTick-time_2s>2000) { ML_val=real_temp_v; times_2s_mh_flag=1; } } else times_2s_mh_flag=0; } } }
3.ADC的设置占空比
题目要求:
解析:
经典3段函数
纯代数问题
注意函数式换成c语言代码我的建议哈这样写
/*y=75/2x-55/2*/
pwm_duty=(uint8_t)(75*Get_adc_R37(&hadc2)/2-55/2);
还有记得边界处理喔!!
if(pwm_duty>85) pwm_duty=85;
if(pwm_duty<10) pwm_duty=10;
4.led的设置
其实我觉得单独控制led简单。这个需要不改变其他led的变化来改变其中一个led的状态还是有点意识我的实现方法,设置参数:进行&|运算在改变gpio如
led_sta=(led_sta&0xfe)|0x01;
3.详细代码及注释
1,变量
/*------------------------- 模式切换区 --------------------*/
uint8_t lcd_mode=0;//lcd界面切换
_Bool pwm_mode=0;//pwm模式切换
_Bool pwm_mode_disp=0;
_Bool pwm_lolk=0;//是否锁定
_Bool RK_mode_flag=0;//rk设置大小切换
_Bool time5s_flag=0;//时间_5s定时
_Bool time2s_flag=0;
_Bool times_2s_mh_flag=0;
_Bool led_2_flag=0;
extern IN_PUT_KEY key[4];
/*------------------------- 数据区 --------------------*/
uint8_t pwm_duty=0;//实时占空比
float real_v=0.0f;//实时速度(V)。
float real_temp_v;//测量速度
uint8_t R_val=1;//实际R 值和 K 值有效范围 1-10,整数。
uint8_t K_val=1;
uint8_t R_temp_val=1;//显示R 值和 K 值有效范围 1-10,整数。
uint8_t K_temp_val=1;
uint8_t N_val=0;//PWM 输出模式切换次数(N)
uint8_t led_sta=0;
float MH_val=0.0f;//高频和低频模式下的速度最大值。
float ML_val=0.0f;
uint32_t pwm_out_hz=4000;//输出频率
/*------------------------- 时间区 --------------------*/
uint32_t time_40ms=0;
uint32_t time_2s;
uint32_t time_100ms;
2,初始化
/*所有逻辑的初始化*/
void My_Init(void)
{
LCD_Init();
Led_turn_off();//记得使用lcd初始化完成之后关闭led喔
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
HAL_TIM_Base_Start_IT(&htim6);
HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED);//ADC R37
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);//pwm输出 PA1
HAL_TIM_IC_Start_IT(&htim17,TIM_CHANNEL_1);//输入捕获PA7
time_40ms=uwTick;
time_2s=uwTick;
time_100ms=uwTick;
}
3,lcd_pro
void lcd_pro(void)
{
char text[60];
switch (lcd_mode)
{
case 0:/*数据界面
显示要素包括界面名称(DATA)、PWM 输出模式(M)、实时占空比(P)、实
时速度(V)*/
sprintf(text," DATA");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
if(pwm_mode_disp) {//PWM 输出模式切换
sprintf(text, " M=H");
LCD_DisplayStringLine(Line3, (uint8_t *) text);
} else
{
sprintf(text, " M=L");
LCD_DisplayStringLine(Line3, (uint8_t *) text);
}
sprintf(text," P=%d%% ",pwm_duty);
LCD_DisplayStringLine(Line4, (uint8_t *)text);
sprintf(text," V=%.1f ",real_temp_v);
LCD_DisplayStringLine(Line5, (uint8_t *)text);
break;
case 1:/*参数界面
显示要素包括界面名称(PARA)、参数 R 和 K 的当前值,R 值和 K 值有效范
围 1-10,整数。*/
sprintf(text," PARA");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
if(R_temp_val==1)//边界处理
{
sprintf(text, " R=%d ",R_temp_val);//R
LCD_DisplayStringLine(Line3, (uint8_t *) text);
}else if(R_temp_val==9)//边界处理
{
sprintf(text, " R=%d ",R_temp_val);//R
LCD_DisplayStringLine(Line3, (uint8_t *) text);
}
sprintf(text, " R=%d",R_temp_val);//R
LCD_DisplayStringLine(Line3, (uint8_t *) text);
if(K_temp_val==1)
{
sprintf(text, " K=%d ",K_temp_val);//R
LCD_DisplayStringLine(Line4, (uint8_t *) text);
}else if(K_temp_val==9)
{
sprintf(text, " K=%d ",K_temp_val);//R
LCD_DisplayStringLine(Line4, (uint8_t *) text);
}
sprintf(text," K=%d",K_temp_val);//K
LCD_DisplayStringLine(Line4, (uint8_t *)text);
break;
case 2:/*统计界面
显示要素包括:界面名称(RECD)、PWM 输出模式切换次数(N)、高频和低
频模式下的速度最大值*/
sprintf(text," RECD");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
sprintf(text, " N=%d",N_val);//R
LCD_DisplayStringLine(Line3, (uint8_t *) text);
sprintf(text," MH=%.1f",MH_val);
LCD_DisplayStringLine(Line4, (uint8_t *)text);
sprintf(text," MH=%.1f",ML_val);
LCD_DisplayStringLine(Line5, (uint8_t *)text);
break;
}
}
4,key_pro
void key_pro(void)
{
/*-------------------------短按逻辑处理 ------------------*/
/*定义为“界面”按键,按下 B1 按键可以往复切换数据、参数和记录三
个界面,*/
if(key[0].key_down==1)//b1逻辑
{
LCD_Clear(Black);
if(++lcd_mode==3)
lcd_mode=0;
if(lcd_mode==1)
RK_mode_flag=0;
if(lcd_mode==2)
{
R_val=R_temp_val;
K_val=K_temp_val;
}
key[0].key_down=0;
}
if(key[1].key_down==1)//b2逻辑
{
/*在数据界面下,用于切换选择低频或高频模式。按键按下后,5 秒内不
可再次触发切换功能。*/
if(lcd_mode==0&&time5s_flag==0)
{
time5s_flag=1;
pwm_mode^=1;
N_val++;
}
if(lcd_mode==1)//切换选择R或K参数。
RK_mode_flag^=1;
key[1].key_down=0;
}
if(key[2].key_down==1)//b3逻辑
{
if(lcd_mode==1)/*在参数界面下,按下 B3 按键,当前可调整的参数加 1,参数调整模式:*/
{
if(!RK_mode_flag) {
if (++R_temp_val == 11) {
R_temp_val = 1;
}
}
else {
if (++K_temp_val == 11) {
K_temp_val = 1;
}
}
}
key[2].key_down=0;
}
if(key[3].key_down==1)//b4逻辑
{
if(lcd_mode==1)/*在参数界面下,按下 B4 按键,当前可调整的参数减 1,参数调整模式:*/
{
if(!RK_mode_flag) {
if (--R_temp_val == 0) {
R_temp_val = 10;
}
}
else {
if (--K_temp_val == 0) {
K_temp_val = 10;
}
}
}
if(lcd_mode==0&&time2s_flag==1)
{
time2s_flag=0;//解锁
pwm_lolk=0;
}
key[3].key_down=0;
}
if(key[3].key_up==1)
{
time2s_flag=1;//锁定状态
key[3].key_up=0;
pwm_lolk=1;
}
}
5,pwm_pro
void pwm_pro(void)
{
static float pwm_bjz=125-1;//步进量
static uint16_t pwm_bjs=0;//步进次数
/*-------------------------获取adc的值 ------------------*/
if(!pwm_lolk)//非锁定
{
//边界处理
/*y=75/2x-55/2*/
pwm_duty=(uint8_t)(75*Get_adc_R37(&hadc2)/2-55/2);
if(pwm_duty>85) pwm_duty=85;
if(pwm_duty<10) pwm_duty=10;
}
/*-------------------------设置占空比 ------------------*/
if(time5s_flag)//模式切换 频率在 5 秒内均匀的升高或降低到目标频率
{
if(uwTick-time_40ms>40) //40ms进行一次步进
{
if(pwm_mode==0)
pwm_bjz+=1.25f;//步进量=125/100=1.25
else
pwm_bjz-=1.25f;
__HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz);//设置arr的值
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100);//设置占空比
time_40ms=uwTick;
pwm_bjs++;
}
if(pwm_bjs>=100)
{
if(!pwm_mode) pwm_bjz=249;
else
pwm_bjz=125-1;
__HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz);
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100);
pwm_mode_disp=pwm_mode;
pwm_bjs=0;
time5s_flag=0;
}
}else//普通模式
{
if(!pwm_mode)
pwm_bjz=250-1;
else
pwm_bjz=125-1;
__HAL_TIM_SetAutoreload(&htim2,(uint16_t)pwm_bjz);
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,(uint16_t)pwm_bjz*pwm_duty/100);
}
/*-------------------------处理速度 ------------------*/
if(pwm_mode)//高频
{
if(times_2s_mh_flag==0&&MH_val<real_temp_v)
{
times_2s_mh_flag=1;
time_2s=uwTick;
}else
{
if(MH_val<real_temp_v)
{
if(uwTick-time_2s>2000)
{
MH_val=real_temp_v;
times_2s_mh_flag=1;
}
}
else
times_2s_mh_flag=0;
}
}else//低频
{
if(times_2s_mh_flag==0&&real_temp_v>ML_val)
{
times_2s_mh_flag=1;
time_2s=uwTick;
}else
{
if(real_temp_v>ML_val)
{
if(uwTick-time_2s>2000)
{
ML_val=real_temp_v;
times_2s_mh_flag=1;
}
}
else
times_2s_mh_flag=0;
}
}
}
6,led_pro
/*-------------------------led灯的处理函数 ------------------*/
void led_pro(void)
{
static uint8_t led_sta=0;
/*LD1:处于数据界面,指示灯 LD1 点亮,否则熄灭。*/
if(lcd_mode==0)
led_sta=(led_sta&0xfe)|0x01;
else
led_sta=(led_sta&0xfe);
LED_Disp(led_sta,1);
/*LD2:低频模式、高频模式切换期间,指示灯 LD2 以 0.1 秒为间隔切换亮、
灭状态,模式切换完成后熄灭*/
if(time5s_flag)
{
led_sta=(led_sta&0xfd);
if(uwTick-time_100ms>100)
{
time_100ms=uwTick;
led_2_flag^=1;
if(led_2_flag)led_sta=(led_sta&0xfd)|0x02;
else
led_sta=(led_sta&0xfd);
}
}
else
led_sta=(led_sta&0xfd);
/*LD3:占空比调整处于“锁定”状态时,指示灯 LD3 点亮,否则熄灭。*/
if(pwm_lolk)
led_sta=(led_sta&0xfb)|0x04;
else
led_sta=(led_sta&0xfb);
}
7,输入库
#include "lq_14_input.h"
/*typedef struct
{
uint8_t key_juadg_sta;
_Bool key_down;
_Bool key_up;
_Bool key_sta;
uint32_t key_time;
}IN_PUT_KEY*/;//(我放在.h)
IN_PUT_KEY key[4];
extern uint8_t R_val;//实际R 值和 K 值有效范围 1-10,整数。
extern uint8_t K_val;
extern float real_temp_v;//测量速度
//按键判断消抖
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
uchar i;
if(htim->Instance == TIM6)
{
/*------------------------- 按键识别与判断 --------------------*/
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(i=0;i<4;i++)
{
switch(key[i].key_juadg_sta)
{
case 0:
if(key[i].key_sta==GPIO_PIN_RESET)
{
key[i].key_juadg_sta=1;
key[i].key_time=0;
}
break;
case 1:
if(key[i].key_sta==GPIO_PIN_RESET)
{
key[i].key_juadg_sta=2;
}
else
key[i].key_juadg_sta=0;
break;
case 2:
if(key[i].key_sta==GPIO_PIN_SET)
{
key[i].key_juadg_sta=0;
if(key[i].key_time<70)//短按
key[i].key_down=1;
}else
{
key[i].key_time++;
if(key[i].key_time>200)
{
key[i].key_up=1;//长按
}
}
break;
}
}
}
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
uint16_t pwm_arr = 0;
uint16_t pwm_frq=0;
if(htim->Instance == TIM17)
{
pwm_arr = HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
__HAL_TIM_SetCounter(htim,0);
pwm_frq = 1000000/pwm_arr/100;
real_temp_v= pwm_frq/K_val*R_val*6.28;
HAL_TIM_IC_Start_IT(htim, TIM_CHANNEL_1);
}
}
double Get_adc_R37(ADC_HandleTypeDef *pin)
{
uint16_t adc=0;
HAL_ADC_Start(pin);
adc=HAL_ADC_GetValue(pin);
return adc*3.3/4096;
}
8,led(这个要开启Pd2喔)
#include "lq_14_led.h"
uint8_t led_on[]={0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
void Led_turn_off(void)
{
uint32_t led_val = 0xff00;
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
GPIOC->ODR= led_val;
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
void LED_Disp(uint8_t led,_Bool flag)
{
Led_turn_off();
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
if(flag)
HAL_GPIO_WritePin(GPIOC,led<<8,GPIO_PIN_RESET);
else
HAL_GPIO_WritePin(GPIOC,led<<8,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
void LED_toop(uint8_t led)
{
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_TogglePin(GPIOC,led<<8);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
总结
岁月漫长,心怀热爱,携手共赴星辰大海,内容结尾愿诸君保持热爱,一起努力,感谢各位看到这里,也希望在我大学生涯中,依旧保持对嵌入式的热爱,当然我也是小白,互相进步,感谢帮助我的人们,我也想将这份热情传承大家,以上就是蓝桥杯嵌入式第十四届省赛的题目解析了,如果有什么问题和建议都欢迎在评论区提出来喔。
点击阅读全文
更多推荐
社区排行榜
目录
所有评论(0)