题目要求做一个电压测量监控设备,各个模块的使用都比较简单,但是当要利用各个模块完成一个简单的小项目,就要理清楚整个程序的大致框架。这里将整个功能分为三大部分显示界面,设置界面,串口接发。
1.界面显示界面由外设读取显示,设置界面设置自动上报时间的。
界面的控制用一个变量LCD_GUI来控制。 若变量为0x00表示显示界面;若变量为0x1_,则为设置界面,并且0x10设置时,0x11设置分,0x12设置秒。
if(unKey_Down == 2) //界面控制
{
if(LCD_GUI == 0x00) //若是显示界面切换为设置界面
{
LCD_Clear(White); // 清屏
LCD_GUI = 0x10;
}
else //返回显示界面
{
LCD_Clear(White);
LCD_GUI=0x00;
time_ctrl[0] = time_disp[0]; //再次返回时,设置更新上报时间
time_ctrl[1] = time_disp[1];
time_ctrl[2] = time_disp[2];
}
}
在设置界面时,选择时分秒,并且闪烁选中的。这里用一个变量set_ctrl,当为1是将要显示字符串对于的时分秒设置为空字符,为0就为原来的。并且该变量在一定时间类翻转来达到闪烁效果。
if(LCD_GUI>>4==1 && unKey_Down ==3) //进入设置界面并且按下按键3
{
if(LCD_GUI==0x12)
{
LCD_GUI=0x10;
}
else
LCD_GUI++;
}
//设置界面
sprintf((char *)Lcd_Disp_String, "Setting");
LCD_DisplayStringLine(Line2, Lcd_Disp_String);
sprintf((char *)Lcd_Disp_String, "T:%02d-%02d-%02d",time_disp[0],time_disp[1],time_disp[2]);
LCD_DisplayStringLine(Line6, Lcd_Disp_String);
if((uwTick - uwTick_Set_time_Set_Point)>=500) //控制设置时分秒的闪烁时间
{
uwTick_Set_time_Set_Point = uwTick;
set_ctrl ^= 0x01;
}
if(set_ctrl == 0x01) // 将相应的时分秒设置为空字符
{
if(LCD_GUI == 0X10)
{
Lcd_Disp_String[2] = ' ';
Lcd_Disp_String[3] = ' ';
}
else if(LCD_GUI == 0X11)
{
Lcd_Disp_String[5] = ' ';
Lcd_Disp_String[6] = ' ';
}
else if(LCD_GUI == 0X12)
{
Lcd_Disp_String[8] = ' ';
Lcd_Disp_String[9] = ' ';
}
}
LCD_DisplayStringLine(Line6, Lcd_Disp_String);
设置更新时间,用两个数值来存储(用一个其实就够了的),一个专门用来显示,一个用来控制上报时间的。当按键4按下时,修改显示变量即可,然后再界面返回时,将显示变量值赋给控制变量
uint8_t time_disp[3]={0};
uint8_t time_ctrl[3]={0};
if(unKey_Down == 4) //值修改
{
if(LCD_GUI == 0X10) // hour
{
if(time_disp[0] >=23)time_disp[0] = 0;
else
time_disp[0]++;
}
else if(LCD_GUI == 0X11) // min
{
if(time_disp[1] >=59)time_disp[1] = 0;
else
time_disp[1]++;
}
else if(LCD_GUI == 0X12) // sed
{
if(time_disp[2]>=59)time_disp[2] = 0;
else
time_disp[2]++;
}
....
else //返回显示界面
{
LCD_Clear(White);
LCD_GUI=0x00;
time_ctrl[0] = time_disp[0]; //再次返回时,设置更新上报时间
time_ctrl[1] = time_disp[1];
time_ctrl[2] = time_disp[2];
}
....
界面的难点基本上就是上面的了,其他细节看后面的完整程序。
2.串口的接发自动上报,直接在相等时刻发给串口即可,这里要控制在相等时刻只发一次,用一个变量来控制即可。(直接在发送字符后面适当的延时即可)
// 自动上报
if(time_ctrl[0]==sTime.Hours && time_ctrl[1]==sTime.Minutes && time_ctrl[2]==sTime.Seconds)
{
if(Ctrl_Uart_Send_Time_Data_Times ==0)
{
Ctrl_Uart_Send_Time_Data_Times=1;
sprintf(str, "%4.2f+%2.1f+%02d%02d%02d\n",v1, (k_int/10.),sTime.Hours,sTime.Minutes,sTime.Seconds);
HAL_UART_Transmit(&huart1,(unsigned char *)str, strlen(str), 50);
}
}
else
Ctrl_Uart_Send_Time_Data_Times = 0; //当时间变化或者控制值变化,两者不等的时候,恢复下一次数据发送允许。
本题的难点就是串口的接收,在发生错误的时候,设备不相应。发生的数据格式k0.x\n。转换为十六进制为:k0.1\n ==>6B 30 2E 31 5C 6E 。这里在串口中"\n"会被当成两个字符处理
发生错误可以从两种角度考虑:数据内容不匹配和数据长度不为6。
因此,可以在中断接收函数里面以字节为单位接收数据,若是第一个字节并且为6b,于是才开始接收下一个字节。
//串口接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(rx_buf == 0x6b && rx_buf_index ==0)//若数据为6b,并且是第一个数据
{
uwTick_Uart_TI_time_Set_Point= uwTick;//接收到第一个数据启动计时
Start_Flag = 1; //接收下一个字节
}
if(Start_Flag == 1) //满足则把数据接收完
{
rx_buffer[rx_buf_index++] = rx_buf;
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&rx_buf, 1);
}
但是如果上一串数据是错的,但是下一串数据是对的,我们必须又恢复为原来还没开始接收数据前的样子,即需要在一个时间段内将数组的索引归0和开始接收标志归0,为一下接收做准备。所以用一个时间变量来控制串口接收处理时间,数据错误或者数据正确并处理完,时间到了就清0,为下一次接收做准备
// 设置k
// k0.1\n ==>6B 30 2E 31 5C 6E
200ms~300ms之内处理数据
if((uwTick - uwTick_Uart_TI_time_Set_Point) = 200)
{
if(rx_buf_index==6) //判断命令长度
{
// 比较内容处第三个
if(rx_buffer[0] == 0x6b && rx_buffer[1] == 0x30 && rx_buffer[2] == 0x2e && rx_buffer[4] == 0x5c && rx_buffer[5] == 0x6e)
{
// 第三个字节范围 1-9
if(rx_buffer[3]>=0x30 && rx_buffer[3]4==1 && unKey_Down ==3) //进入设置界面并且按下按键3
{
if(LCD_GUI==0x12)
{
LCD_GUI=0x10;
}
else
LCD_GUI++;
}
if(unKey_Down == 4) //值修改
{
if(LCD_GUI == 0X10) // hour
{
if(time_disp[0] >=23)time_disp[0] = 0;
else
time_disp[0]++;
}
else if(LCD_GUI == 0X11) // min
{
if(time_disp[1] >=59)time_disp[1] = 0;
else
time_disp[1]++;
}
else if(LCD_GUI == 0X12) // sed
{
if(time_disp[2]>=59)time_disp[2] = 0;
else
time_disp[2]++;
}
}
}
//***LED扫描子函数
void Led_Proc(void)
{
if((uwTick - uwTick_Led_Set_Point)3.3*k_int*0.1) //亮
{
if((uwTick - uwTick_Led_Point)>=200)
{
uwTick_Led_Point = uwTick;
ucLed ^= 0x01;
}
}
else
ucLed = 0;
}
LED_Disp(ucLed);
}
void Lcd_Proc(void)
{
if((uwTick - uwTick_Lcd_Set_Point)=500) //控制设置时分秒的闪烁时间
{
uwTick_Set_time_Set_Point = uwTick;
set_ctrl ^= 0x01;
}
if(set_ctrl == 0x01) // 将相应的时分秒设置为空字符
{
if(LCD_GUI == 0X10)
{
Lcd_Disp_String[2] = ' ';
Lcd_Disp_String[3] = ' ';
}
else if(LCD_GUI == 0X11)
{
Lcd_Disp_String[5] = ' ';
Lcd_Disp_String[6] = ' ';
}
else if(LCD_GUI == 0X12)
{
Lcd_Disp_String[8] = ' ';
Lcd_Disp_String[9] = ' ';
}
}
LCD_DisplayStringLine(Line6, Lcd_Disp_String);
}
}
void Usart_Proc(void)
{
if((uwTick - uwTick_Usart_Set_Point)6B 30 2E 31 5C 6E
200ms~300ms之内处理数据
if((uwTick - uwTick_Uart_TI_time_Set_Point) = 200)
{
if(rx_buf_index==6) //判断命令长度
{
// 比较内容处第三个
if(rx_buffer[0] == 0x6b && rx_buffer[1] == 0x30 && rx_buffer[2] == 0x2e && rx_buffer[4] == 0x5c && rx_buffer[5] == 0x6e)
{
// 第三个字节范围 1-9
if(rx_buffer[3]>=0x30 && rx_buffer[3]
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?