STM32与串口屏交互(USART HMI)

不管是备战电赛还是准备毕设,一块能与单片机交互的屏幕显得尤为重要,相较于传统的SPI,IIC通信的0.96寸OLED还是管脚较多的TFT屏幕,串口屏综合了以上屏幕的特点,即尺寸大、管脚少,能够充分减少占用单片机的I/O资源,且支持触摸。

串口屏可作为输出设备(显示)以及输入设备(按键),开发难度小,操作简单,软件要求低且拥有专门的上位机辅助开发。

一、前期准备

1. USB TO TTL模块

串口屏顾名思义是通过串口通信的方式来实现数据的传输,所以我们要准备一个USB TO TTL模块。作用是用来上位机(电脑)与串口屏的通讯。
在这里插入图片描述2.STM32单片机(以stm32f103c8t6为例)
3.HMI USART串口屏
本人使用使用的是陶晶驰的串口屏。型号为TJC4832T135_011
TTTTTTT这里插入图片描述
电阻式触摸屏比电容式体验效果稍差一点不过并无大碍,3.5寸的屏幕尺寸足够我们的项目需求。人机交互时,大小也正合适。串口屏共有4根线分别是5V、TX、RX、GND.当串口屏与上位机通讯时串口屏的RX接USB转TTL模块的TXD,串口屏的TX接串口模块的RX。当串口屏与单片机(stm32f103c8t6)通讯时,我们要软件配置使用哪个串口从而决定硬件连接。
若使用USART1
RX------------PA9
TX------------PA10
若使用USART2
RX------------PA2
TX------------PA3
若使用USART3
RX------------PB10
TX------------PB11

二、串口屏上位机使用方法以及界面设计

当我们在做小项目时,我们要根据项目的需求去设计对应的界面。以达到我们预期的效果。我们使用官方给出的上位机USART HMI软件去设计界面。

上位机下载:USART HMI 资料中心

(1)打开USART HMI 软件,点击新建
这里需要我们选择对应串口屏的型号,本人的是TJC4832T135_011。
显示方向我们一般默认90(横屏)。串口屏默认波特率为9600。如需要改变请查阅资料编写代码。
(2)从工具箱中拖需要使用的组件。比较常用就是文本、按钮,曲线等。去设计我们想要的界面例如背景色,控件的大小等等。
(3)点击软件左上方工具按钮,我们要提前导入字体也就是字库制作,我们选择我们想要的字体以及大小即可以生成字库。每生成一个字库并且添加到工程中都会有一个字体ID。
(4)当我们使用文本或者数字控件时,我们需要不同的字体或大小时,我们可以再右下角属性一栏中修改font参数,修改为你需要的字体ID即可。如下图所示:
在这里插入图片描述
(5)我们可以在软件的右上角页面加入新的界面,通过按钮来实现页面的切换。这里我们需要查阅软件中最上方帮助中的指令集以及资料中心,在软件中写所需要的代码。(很短,很容易上手)
(6)上位机编程中常用的指令汇总(实现小项目绰绰有余)
eg:
切换页面: page 页面名称 (page page1)即切换到page1页面
发送十六进制指令 : printh 01 即发送0x01
两者相结合即可完成我们独立按键的功能。

以19年电赛D题简易电路特性测试仪为例
初步设计首页如下:
在这里插入图片描述
首页只添加了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);
    HMISendb(0xff);

五、总结

串口屏的功能远不止如此,大佬们有兴趣可以多去研究研究,熟练使用串口屏基本能够替代oled、tft等各大屏幕。通过上位机以及代码的配合可以实现更多的功能,开发简单,易上手。以上是我的各人理解,分享我在使用过程中的一些小技巧,如果有问题的话大家可以一起讨论。
本人开学大三学生一枚,备战电赛中,之后会陆续写一些小项目的教程如风力摆,平衡车,纸张计数等如果有需要请关注我谢谢!😁🤗

 

版权声明:本文为CSDN博主「GuYH_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_46336588/article/details/119424722

生成海报
点赞 0

GuYH_

我还没有学会写个人说明!

暂无评论

相关推荐

趣聊51之串口通信(概念篇)

对于刚刚接触单片机的同学们来说,串口通信似乎是一个神秘感十足的东西,笔者在刚刚开始学习51单片机时,读的是郭天祥先生的那本著名的《新概念51单片机教程》,贼厚的一本书,但是等

Lin总线通信在STM32作为主机代码以及从机程序

距离上次做资料准备已经过去六天了。最近在学车,上周末就没有开电脑。这周开始进行了Lin通信的代码整理,目前是可以正常通信的了,采用的是增强型校验方式。后期再进一步跟进研究。。。更新一博,留