前言:
在讲解第十一届的题目之前,强烈建议去看我的《蓝桥杯嵌入式总结和考前冲刺》,涵盖了各个模块的初始化以及基础配置,相关的思路则在我这一篇,如有某个模块不熟悉,请前往查看。
老规矩,这题的难点是根据模拟电压输入的情况来进行计时,废话不多说,直接开始讲解。
真题原题:
CubeMX初始化配置:
在我上一篇博客中已经将蓝桥杯需要用到的每个模块以及如何初始化详细的写了出来,如果想知道哪个模块如果使用CubeMX初始化,请跳转到我上一篇博客:http://t.csdnimg.cn/TvogK
LCD模块:
在比赛当中,首先为了保证看的到明显的现象,首先操作的应该是LCD。
多显示了两个变量,sta和locl用于调试。
uint8_t view=0;//0:数据界面,1:参数界面
uint8_t T;//计时时间
double V;//输出电压值
double Vmax=3,Vmin=1;//真正生效的
void lcd_disp(void)
{
if(view==0)
{
char text[20];
sprintf(text," Data ");
LCD_DisplayStringLine(Line0,(uint8_t *)text);
sprintf(text," V:%.2fV",V);
LCD_DisplayStringLine(Line2,(uint8_t *)text);
sprintf(text," T:%2ds",T);
LCD_DisplayStringLine(Line3,(uint8_t *)text);
sprintf(text,"sta=%d",sta);
LCD_DisplayStringLine(Line4,(uint8_t *)text);
sprintf(text,"lock=%d",lock);
LCD_DisplayStringLine(Line5,(uint8_t *)text);
}
else
{
char text[20];
sprintf(text," Para ");
LCD_DisplayStringLine(Line0,(uint8_t *)text);
sprintf(text," Vmax:%.1fV",temp1);
LCD_DisplayStringLine(Line2,(uint8_t *)text);
sprintf(text," Vmin:%.1fV",temp2);
LCD_DisplayStringLine(Line3,(uint8_t *)text);
}
}
Key(按键模块):
这里按键的难点不单单只是识别出哪个按键按下了,这里需要实现每个按键的功能,由于我上过博客已经详细讲解了按键基本模块如何搭建,这里就直接进入实现功能模式。蓝桥杯嵌入式总结和考前冲刺
double temp1=3,temp2=1;//判断Vmax和Vmin是否合理
void key_scan(void)
{
if(key[0].single_sta==1)
{
view=!view;
LCD_Clear(Black);
if(view==0)
{
if(temp1>=temp2+1.0)
{
Vmax=temp1;
Vmin=temp2;
led_mark=led_mark&0xfd;
}
else
{
temp1=Vmax;
temp2=Vmin;
led_mark=led_mark|0x02;
}
}
key[0].single_sta=0;
}
if(key[1].single_sta==1)
{
if(view==1)
{
temp1+=0.1;
if(temp1>3.3)
{
temp1=0;
}
}
key[1].single_sta=0;
}
if(key[2].single_sta==1)
{
if(view==1)
{
temp2+=0.1;
if(temp2>3.3)
{
temp2=0;
}
}
key[2].single_sta=0;
}
if(key[3].single_sta==1)
{
key[3].single_sta=0;
}
}
代码解析:
这里的关键点是设置了两个变量,temp1和temp2,来取消不合理的变压值改变。
串口通信模块:
我这里废话不多说,直接上思路,如果你对串口基本模块的使用还不够清楚,可以去看我的http://t.csdnimg.cn/cjsNh
void uart_disp(void)
{
double num1,num2;
if(pointer==7)
{
sscanf(receive_dat,"%lf,%lf",&num1,&num2);
if((num1>=num2+1)&&num1<=3.3)
{
Vmax=num1;
Vmin=num2;
temp1=Vmax;
temp2=Vmin;
led_mark=led_mark&0xfb;
}
else
{
led_mark=led_mark|0x04;
}
}
else
{
led_mark=led_mark|0x04;
}
pointer=0;
memset(receive_dat,0,30);
}
代码解析:
1、将接收到的字符串用sscanf分割,并且转换为double类型赋值给num1和num2。
2、如果接收到的值合理,就对接收到的值进行配置。
3、如果串口接收数据格式不对,也不进行配置。
计时模块:
这是这题最难的点,如果你把这个做出来了,那么这题也就全部解决了。
这里提出一个问题,你是否想的是如果在模拟输入V等于最大电压值或则等于电压最小值的时候,来对计时进行处理呢?
其实我开始也是这样想的,但是最后发现程序会出现问题,因为double类型的比较,由于精度的不同,你以为的3.3==3.3是成立的,但其实并不一定成立,这是由于计算机对浮点数的精度进行了判断,精度不一样,3.3这个时候就不等于3.3。这里采用的方法就是,避开将他们两个进行判断是否相等。
void time_record(void)
{
if(V<Vmin)
{
time_clear=1;//对时间清0
lock=1;//开锁
}
else if(V>=Vmin&&V<Vmax)
{
if(lock==1)//防止开始就在计时
{
led_mark=led_mark|0x01;
sta=1;//开始计时
if(time_clear==1)
{
time=0;
time_clear=0;
}
}
}
else if(V>=Vmax)
{
lock=0;//关锁
sta=0;//结束计时
led_mark=led_mark&0xfe;
}
}
代码解析:
1、这里有三个标志位十分重要,time_clear,lock,sta。
2、time_clear表示在每次V小于最小电压值之后,如果在V大于它之后,就会根据这个标志位,把计时清0。
3、lock是为了防止开始V处于最大范围和最小范围之间就开始计时。
4、sta表示计时真正的开始和结束,只有sta为1的时候,计时器才不断的递增。
这个时候,就可以根据sta的值,不断在定时器对时间加加,来获得计时时间。
if(sta==1)
{
time++;
T=time*0.01;
}
else
{
time=0;
}
这样,就把这题最难的计时模块解决了。
LED模块:
led的显示,说白了,就是根据目前运行的状态来不断改变全局变量led_mark,然后在主循环中不断的调用led_disp()函数,实现led所要求的显示功能。
1、led1的控制,用了sta参数来进行控制,sta==1,led1点亮,表示计时正在进行,sta==0,led2熄灭,表示计时结束。
2、若参数设置不合理,led2根据要求进行改变。
if(view==0)
{
if(temp1>=temp2+1.0)
{
Vmax=temp1;
Vmin=temp2;
led_mark=led_mark&0xfd;
}
else
{
temp1=Vmax;
temp2=Vmin;
led_mark=led_mark|0x02;
}
}
3、在串口函数里面,如果真正的设置了电压值,led3会熄灭,否则led3点亮。
结束语:
这就是十一届代码的全部解析,感谢观看。如果觉得我写的还不错,可以给我个点赞和关注,谢谢大家啦!