首页只添加了3个按钮控件和一个文本控件。文本控件显示我们本次工程的名称,而按键就可控制我们的模式选择。所以我们需要添加3个子页面(测量模式,故障测试模式,幅频特性曲线显示模式)通过主页面的按键来切换页面。所以在主页面下分别点击对应按钮控件,下方会出现事件界面,我们通常在弹起事件中做操作,也可以达到一个防误触的效果。如下图所示,我们在事件内容中写了两行代码。
1.printh 01 即点击该按钮串口屏就会发送0x01(有大作用,我们可以在单片机串口中断中解析该数据,并做相应的操作。具体示例在单片机编程部分)
2page page0 即点击该按钮切换到page 0页面
但我们在切换的页面后如何返回呢?
其实我们的整个界面就如同一个按钮一样,也有弹起操作。那我们要返回主页面是不是就要点击子页面的整个页面,而后在其弹起事件中编写page main即可实现返回的操作。
重要提醒:❗❗❗ 如果我们的文本控件默认设置的是私有变量,那我们切换界面时其显示的文本为你初始的文本。所以我们与单片机通信使其显示数值的文本控件应设置为全局变量(如下图t3,t4,t5文本控件)。这样我们切换页面时数据就不会丢失。
我们尽量将显示的固定文字在上位机中设计完成,单片机和串口屏之间尽量不要传汉字,那样占用资源而且容易出错。
到此上位机所要干的事情我们都已经做完了。当然上位机中还有很多好玩的控件,待网友们去开发去尝试。串口屏可谓功能之强大。
三、STM32软件编程
stm32软件部分总体分为发送数据和接收数据。
发送数据
既然我们要发送数据我们就要符合相应的通讯协议,在我们发送数据,总要有个结尾标志,如果没有的话单片机就不知道你的数据发没发送完,从而卡死出不来。
🔰🔰🔰所以第一个重点就是要有结束符,向串口屏发送数据完要加结束符(连续三个0xff)那么当串口屏读取到连续三个0xff时这次的数据就已经发送完成从而跳出循环。不多说了,直接上代码。
/\*\*
███████╗ ██ ██ ██╗ ██╗
██╔════╝ ║██████║ ██║ ██║
██║ ╚══██╔═╝ ███████║
██║ █████ ████████╗ ██║ ██╔══██║
██╚════██║ ╚═══════╝ ██║ ██║ ██║
█████████║ ██║ ██║ ██║
╚════════╝ ╚═╝ ╚═╝ ╚═╝
\* @brief main
\* @language C
\* @harfware MicroController
\* @version v1.0
\* @date 29-July-2021
\* @author Yuhang Gu
\*
\*/
首先进行串口配置(串口初始化)我使用的是stm32的USART3,所以硬件连接应为
RX------------PB10
TX------------PB11
void uart3\_init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC\_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//时钟GPIOB、USART3
RCC\_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
//USART1\_TX PB10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO\_Init(GPIOB, &GPIO_InitStructure);
//USART1\_RX PB11
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO\_Init(GPIOB, &GPIO_InitStructure);
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC\_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART\_Init(USART3, &USART_InitStructure);
USART\_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启中断
USART\_Cmd(USART3, ENABLE); //使能串口
}
串口初始化完成后波特率为9600,我们即可编写发送数据函数如下所示:
函数功能:发送字符串
void HMISends(char \*buf1) //字符串发送函数
{
u8 i=0;
while(1)
{
if(buf1[i]!=0)
{
USART\_SendData(USART3,buf1[i]); //发送一个字节
while(USART\_GetFlagStatus(USART3,USART_FLAG_TXE)==RESET){};//等待发送结束
i++;
}
else
return ;
}
}
函数功能:连续3次发送一个字节(一般用来发送0xff作为结束符)
void HMISendb(u8 k) //字节发送函数
{
u8 i;
for(i=0;i<3;i++)
{
if(k!=0)
{
USART\_SendData(USART3,k); //发送一个字节
while(USART\_GetFlagStatus(USART3,USART_FLAG_TXE)==RESET){};//等待发送结束
}
else
return ;
}
}
在程序初始化中我们要对串口屏也进行一个初始化防止被之前没有进行完的数据传输所影响。
以下是串口屏的启动函数,写在初始化中即可。
void HMISendstart(void)
{
delay\_ms(200);
HMISendb(0xff);
delay\_ms(200);
}
在所有函数都写好之后,我们即可在keil中编写程序,发送我们想要的数据给串口屏。
一个小技巧✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅
C语言中有一个函数可以将其他类型强制转化为字符串类型,我们知道串口屏通信的话其实都是通过发送字符串来让串口屏显示我们想要的内容的。所以我们必须将我们的内容先转化为字符串。例如单片机自带的ADC所读取到的数值,连接温湿度传感器模块读取到的温湿度等等我们想要实时变化的数据显示到串口屏时我们就要用到sprintf函数。
sprintf函数使用方法如下图所示,
unsigned char buf[64];
sprintf((char \*)buf,"page0.t3.txt=\"%.1f\"",Ri);
我们所要转化成的字符串其实就和普通的printf函数格式相同。若为整形则为%d,浮点型则为%f。Ri就是你所变化的数值,可以为ADC读到的值,温湿度,频率,电压等等
所以我们想要完整的发送数据给串口屏我们就可以这样操作。
sprintf((char \*)buf,"page0.t3.txt=\"%.1f\"",Ri); //强制类型转化,转化为字符串
HMISends((char \*)buf); //发送Ri的数据给page0页面的t3文本控件
HMISendb(0xff);//结束符
这样我们就可以让串口屏显示我们想要的数据了。
接收数据
单片机接收串口屏发送来的指令是在串口中断中进行的,我们在上位机使用时曾在按钮中编写printh 01,printh 函数发送指令后自带0x0d 0x0a结尾。每当按下我们单片机就会收到0x01这个数据,所以我们就是对接收到的消息进行分析做对应的操作就ok了。
void USART3\_IRQHandler(void)
{
u8 Res;
if(USART\_GetITStatus(USART3, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART\_ReceiveData(USART3); //读取接收到的数据
if(Res==0x01) Mode_MeasureFlag=1;
else if(Res==0x02) Mode_CorrectFlag=1;
else if(Res==0x03) Mode_MeasureFlag=0;
else if(Res==0x10) MeasureRi_Flag=1;
else if(Res==0x11) MeasureRo_Flag=1;
else if(Res==0x12) MeasureAv_Flag=1;
}
}
这样我们就通过串口屏来控制我们所要的模式了。例如进入测量模式,进入故障检测模式。
以下是我在备战2021电赛训练时做的19年D题简易电路特性测试仪所编写的部分代码,为的是给大家抛砖引玉一下,知道串口屏应该怎么用,怎么能够通过这小小的屏幕实现很强大的功能。当然数据的接收不止这么简单。我的使用一般就是通过接受的数据来改变功能模式选择的标志位,其实就可以实现大部分想要的功能。串口屏还可以作为键盘输入,以及计算器等,那我们单片机的数据接受就要更加的复杂,要对数据进行位操作解析。感兴趣的可以尝试一下,我就不再赘述。
while(1)
{
if(Mode_MeasureFlag==1)
{
/\*\*\*\*\*\*\*\*\*\*\*\*\*测量Ri\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
if(MeasureRi_Flag==1)
{
Key3=0;
Key1=0;
delay\_ms(1000);
ADC_Temp1 =(float) ADC_ConvertedValue[1]/4096\*3.3;
printf("\r\n ADC\_Temp1 = %f \r\n",ADC_Temp1);
Key1=1;
delay\_ms(5000);
// delay\_ms(7000);
ADC_Temp2 =(float) ADC_ConvertedValue[1]/4096\*3.3;
printf("\r\n ADC\_Temp2 = %f \r\n",ADC_Temp2);
Ri=(ADC_Temp2\*1000)/(ADC_Temp1-ADC_Temp2);
printf("\r\n Ri = %f \r\n",Ri);
sprintf((char \*)buf,"page0.t3.txt=\"%.1f\"",Ri); //强制类型转化,转化为字符串
HMISends((char \*)buf); //发送Ri的数据给page0页面的t3文本控件
HMISendb(0xff);//结束符
Key1=0;
MeasureRi_Flag=0;
}
/\*\*\*\*\*\*\*\*\*\*\*\*\*测量Ro\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
if(MeasureRo_Flag==1)
{
Key1=1;
Key3=1;
delay\_ms(5000);
Key2=0;
ADC_Temp1 =(float) ADC_ConvertedValue[1]/4096\*3.3;
printf("\r\n ADC\_Temp1 = %f \r\n",ADC_Temp1);
Key2=1;
delay\_ms(5000);
ADC_Temp2 =(float) ADC_ConvertedValue[1]/4096\*3.3;
printf("\r\n ADC\_Temp2 = %f \r\n",ADC_Temp2);
Ro=((ADC_Temp1-ADC_Temp2)\*5100)/ADC_Temp2;
printf("\r\n Ro = %f \r\n",Ro);
sprintf((char \*)buf,"page0.t4.txt=\"%.1f\"",Ro);
HMISends((char \*)buf);
HMISendb(0xff);
Key2=0;
Key3=0;
MeasureRo_Flag=0;
}
/\*\*\*\*\*\*\*\*\*\*\*\*\*测量Av\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
if(MeasureAv_Flag==1)
{
Key1=1;
Key3=0;
delay\_ms(5000);
ADC_Temp1 =(float) ADC_ConvertedValue[1]/4096\*3.3;
printf("\r\n ADC\_Temp1 = %f \r\n",ADC_Temp1);
Key3=1;
delay\_ms(5000);
// delay\_ms(7000);
ADC_Temp2 =(float) ADC_ConvertedValue[1]/4096\*3.3;
printf("\r\n ADC\_Temp2 = %f \r\n",ADC_Temp2);
Av=(ADC_Temp2/(ADC_Temp1/105.8));
printf("\r\n Ro = %f \r\n",Av);
sprintf((char \*)buf,"page0.t5.txt=\"%.1f\"",Av);
HMISends((char \*)buf);
HMISendb(0xff);
Key2=0;
Key3=0;
MeasureAv_Flag=0;
}
}
}
四、单片机发送数据的字符串指令汇总
在程序中直接调用即可(把想要显示的变量换掉就ok)
1.文本控件(.txt)
sprintf((char \*)buf,"page0.t1.txt=\"%d\"",num); //显示变化的数值
HMISends((char \*)buf);
HMISendb(0xff);
sprintf((char \*)buf,"page1.t0.txt=\"Wait...\"");//显示字符串
HMISends((char \*)buf);
HMISendb(0xff);
2.数字控件(.val)
sprintf((char \*)buf,"n0.val=%d",Target_Speed);
HMISends((char \*)buf);
HMISendb(0xff);
3.曲线控件(add 控件ID,选择通道,数值)数值取值范围(0-255)通道数量,波形颜色在上位机中设置。
🔲我们所要发送的数据不在此范围内的话要进行数值等比缩小,并且只能发送整数。
sprintf((char \*)buf,"add 1,1,%d",ExchangeSpeed1);
HMISends((char \*)buf);