打印

一款串口触摸屏与单片机的开发调试记录-四川成都控制器...

[复制链接]
1370|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
yonko|  楼主 | 2018-8-21 19:05 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
文 ❤沙鸥 2018-8-18 成都
在为客户提供单片机系统、控制器、工控PLC自动化传感系统、电路板、电子产品、仪器仪表、装置设备、软件EXE、安卓APP编程等开发设计定制服务的时候(业务Q Q 2531263726),我会经常分享一些开发过程中的技术总结和难点、基础知识和学习笔记发布在微信公众号(永珂在线)与大家共享,一直以来我都力求把**写得更有故事性、更有吸引力,更简单易懂,这是因为看着其他部分作者和老师写的教程和技术文档很枯燥就很恼火,虽然他们功力深厚但是出拳无味,我就想改变。我想,技术文档打破传统写法,是可以写得跟小说一样引人入胜有吸引力的,我的文笔会比较轻快明丽、浅显易懂,甚至扯淡。以前的几篇**写得比较烂,我也不去修改了,此文开始:通俗易懂、引人入胜、循序渐进。当然,我也不是专业作家,不能一蹴而就,只寄望于一次比一次更好。废话就说到这里,故事开始,进入正题:
1.        背景介绍
几个月前,一位尊敬的客户R加我的QQ(业务Q Q 2531263726)好友:
“你好,我们有个控制器的项目需要帮助,请问能来谈谈吗?”
“好,可以,请问地址在哪里?”
“成都市某某县某某路某某号,电话18然后叽里呱啦。”
“嗯,好的,我安排一下时间过来。”
于是我就和客户简单电话沟通了一下任务需求,就是要做什么东西有什么要求,然后约好了时间面谈。本案是为了一款新研制的清洗设备开发一个控制器,从最初到现在几次改进设计最终选定此款串口触摸屏作为显示器件。
最初的设计方案出于成本和其他因素的考虑是利用OLED和LCD作为设备的显示器,显示设备的运行状态、流量、温度和压力参数。这两种显示器价格便宜,但是功能较为单一、美观性稍微差一点,后来有一天,客户甩了一个东西给我,是一个4.5寸的串口触摸屏和一本巴掌那么大小的寥寥几页的说明手册给我,要把所有的显示和按键功能集成到这个触屏上面。简单了解一下,这个屏虽然以前没有使用过,但是串口通信和控制我这里熟悉得很,我想只要厂家能把产品生产出来并且有说明书,我这里就能二次开发出满足要求的功能。
根据说明手册里面提供的信息,去下载了详细的使用说明书、配套软件和参考例程,粗略一看,使用起来似乎不难,但是真正在后面开发的时候却又是一波三折。
2.        串口触摸屏的介绍和开发说明
这款串口触摸屏的尺寸是4.5寸,在横向(宽边)有853个点像素,纵向(窄边)479个点像素,在使用的时候不超出这个尺寸参数都是能够在屏幕上显示。

这个触摸屏完全可以采用串口通信进行操作,包括参数显示、触摸按键控制等,串口通信协议按照图 2设置即可,很简单的几个参数设置,波特率、数据位、校验位、停止位。

在开发中只需要接4根线就可以运行触摸屏,包括:
VCC引脚:自然是接5V或者3.3V的直流电源正极;
GND引脚:接直流电源的负极;
TX引脚: 如果是由单片机控制触摸屏的显示,就接单片机串口的RX引脚;
RX引脚: 如果是由单片机控制触摸屏的显示,就接单片机串口的TX引脚;

2.1        串口触摸屏的程序开发说明
实际上此款触摸屏的显示和控制只需要单片机的串口发送或者接收一些特定的字符串指令即可。
例如,我们想对触摸屏清屏,只需要利用单片机串口向触摸屏发送字符串"CLS(0);\r\n"即可,其中的”CLS(0);”是清屏指令,用0号颜色黑色清除屏幕,\r\n表示让触摸屏立刻执行这一指令。

以上就类似于单片机向触摸屏打了一个招呼,交代一些事情:
“嗨!触摸屏兄弟,把你的屏幕用黑色作为背景清除一下。”
那么一般来说,触摸屏收到指令执行完成后还需要向单片机反馈一个消息,发送字符串“OK”给单片机,就像在说“OK,弄好了!”
这个“OK”字符串只有触屏接收到“\r\n”字符串指令,并且执行完成后才会向单片机反馈。不过大家做事也不能着急,在单片机在向触屏发送指令确认执行后,需要延时200-300毫秒,等触屏兄弟休息一下,收到触屏的“OK”的回话后再接着干活。
又如,我们想要在屏幕上某个位置开始显示12点的汉字“永珂在线”,就只需要利用单片机串口向触摸屏发送字符串"DS12(0, 0,'永珂在线',1); \r\n ",就能在触摸屏的(0,0)位置开始显示字符串“永珂在线”四个汉字,其中的1表示字体的颜色白色。同样地,触屏执行完成后,也会向单片机反馈“OK”。当然,也不是每个指令语句后面都要加“\r\n”,多发送几条指令后再加“\r\n”,将是更好的编程选择。

这款串口屏的使用就是类似这样的,我只是举例两个例子,更多的背景设置、触摸按键配置等等功能,在你购买这块触摸屏的时候,在商家给你的说明书里有详细介绍。
2.2        在单片机里面写程序
触摸屏了解了,线接好了,接下来的工作就是编写单片机的程序。
(1)这里我首先介绍的是一款以51为核心的单片机(STC15F2K60S2)的与触屏通信操作的编程方法。
第一步,既然是串口触摸屏,首先自然是配置单片机的串口,直接一点,上代码吧。
void UartInit(void)                //115200bps@11.0592MHz          串口1初始化
{
        SCON = 0x50;                //8位数据,可变波特率
        AUXR &= 0xBF;                //定时器1时钟为Fosc/12,即12T
        AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
        TMOD &= 0x0F;                //设定定时器1为16位自动重装方式
        TL1 = 0xFE;                //设定定时初值
        TH1 = 0xFF;                //设定定时初值
        ET1 = 0;                //禁止定时器1中断
        TR1 = 1;                //启动定时器1
        ES=1;                        //使能串口中断
        EA=1;                        //打开总中断
}
以上UartInit程序段,就将单片机的串口1配置为波特率为115200bps,8位数据的通信参数。
第二步,编写串口发送的程序段,串口发送的程序段分为两部分,第一个是串口发送一个字符,另一个是串口发送字符串。
void SendData(unsigned char dat)                //(1)发送一个字节(一个字符)
{        unsigned char i=0;
        ACC=dat;
        SBUF=dat;
        do
        { _nop_();
        i++;
        }while(!TI && i<200);
        TI=0;
}
在这里为了防止串口发送进入死循环,我将官方参考例程做了一下小修改,增加了一个计数变量i,当执行了200个指令周期的延时后假如串口还没有发送完一个字符,就强制退出这个串口字符发送程序。
void SendString(unsigned char *s)                //(2)发送字符串
{
unsigned char i=0;
        while(*s)
        {
        SendData(*s++);
        i++;
        if(i>=250)        break;//防止进入死循环,一次最多250个字符
        }
        Delay1ms(10);
}
在这里也将官方参考例程做了一下小修改,同样为了避免进入死循环,增加了一个计数变量i,一个字符串最大只能有250个字符,超过这个数量则退出字符串发送程序。
接下来的操作就是比较简单的,在程序的任意位置调用SendString子函数就能进行触摸屏的操作,例如调用SendString("DS24(510,410,'中',1);");         就能在屏幕的(510,410)位置显示一个白色的24点阵汉字“中”。
第三步,前面说了单片机向触屏发送数据和指令,自然单片机还需要接收触屏返回的反馈信号、按键标号等信息,这里我采用串口中断来接收来自触屏的反馈数据。
unsigned char xdata received_data[4]; //串口接收到的按键编号数据
unsigned char xdata feedback_data[2];//串口屏执行指令后的反馈数据
void Usart() interrupt 4        //串口1的中断
{
unsigned char received_temp;
if(RI)        //假如有数据接收事件,接收到数据
        {RI=0;
        received_temp=SBUF;                                                //串口接收到的数据
        received_data[0]=received_data[1];                
        received_data[1]=received_data[2];
        received_data[2]=received_data[3];
        received_data[3]=received_temp;
        if(received_data[0]=='B' && received_data[1]=='N' && received_data[2]==':' && received_data[3]!='0' )  //触摸按键返回的指令格式是BN:X
                {command_received=1;
                 command_data=received_data[3];                  //返回BN:X指令中的X代码
                }
        feedback_data[0]=feedback_data[1];
        feedback_data[1]=received_temp;
        if (feedback_data[0]=='O' && feedback_data[1]=='K')
                {feedback_ok=1;
                }       
        }
if(TI)
        {TI=0;
        }
}
这里我定义了2个数据缓冲区,received_data是触屏按键编号缓冲,当你用手指在触屏上点击不同的按键区域的时候,触屏会根据预先的配置返回BN:1,BN:2,BN:3等等字符串,其中的数字1,2,3就表示你手指点击的屏幕上哪个编号的区域,BN:1表示手指点击了1号区域,BN:2表示手指点击了2号区域,以此类推。
feedback_data是单片机向触摸屏发送指令后,触屏接收到并执行后向单片机返回的“OK”信息,当单片机收到“OK”就表示触屏把上一条指令执行完成了。
(2)以上是51单片机的,下面讲一讲STM32单片机的部分参考程序,本段程序来自于该款触屏的说明书。
第一步,自然是串口初始化。
void USART1_Init(void)
{
USART_InitTypeDef USART_InitStructure;
USART_ClockInitTypeDef USART_ClockInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE );
USART_ClockInitStructure.USART_Clock = USART_Clock_Disable; // 时钟低电平活劢
USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low; // 时钟低电平
USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge; // 时钟第二个边沿迕行数据捕获
USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable; // 最后一位数据癿时钟脉冲丌从SCLK输出
/* Configure the USART1 synchronous paramters */
USART_ClockInit(USART1, &USART_ClockInitStructure); // 时钟参数刜始化讴置
USART_InitStructure.USART_BaudRate = 115200; // 波特率为:115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 8 位数据
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 在帧结尾传输 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; // 収送使能+接收使能
/* Configure USART1 basic and asynchronous paramters */
USART_Init(USART1, &USART_InitStructure);
/* Enable USART1 */
USART_ClearFlag(USART1, USART_IT_RXNE); //清中断,以免一启用中断后立即产生中断
USART_ITConfig(USART1,USART_IT_RXNE, ENABLE); //使能 USART1 中断源
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
USART_Cmd(USART1, ENABLE); //USART1 总开关:开启
}
第二步,串口发送字符串的子程序。
void GpuSend(char * buf1)
{ u8 i=0;
while (1)
{ if (buf1!=0)
{ USART_SendData(USART1, buf1); //収送一个 byte 刡串口
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){}; //等待収送结束
i++;
}
else return;
}
}
第三步,在任意程序里调用串口发送指令去命令触屏工作。
GpuSend("CLS(13);BOX(0,0,219,175,15);BOX(1,1,218,174,0);BOXF(2,2,217,17,3);PL(2,
18,218,18,0);\r\n");
这里的STM32的程序只是部分程序段,在使用的过程中还有时钟配置、引脚配置等其他程序,这里不一一赘述了。
3.        你需要注意的事
该怎么调试
有时候作为开发者我们的设想是很好的,以为一次就可以写出天衣无缝的代码,然而现实是比较残酷的,有时候以为是严格按照规则和使用说明来写的程序,但是稀里糊涂地就会出现这样或者那样的问题,这时候就需要调试了。
磨刀不误砍柴工,既然是串口通信,那串口助手就是少不了的,如图 2的Commix软件就是不错的选择。将串口数据线(TTL电平的)或者USB转串口TTL数据线插到电脑上,将数据线的TX接单片机的RX,数据线的RX接单片机的TX,然后数据线的地线和单片机共地即可。在串口助手界面你能看到单片机发送出来的数据,你也可以模拟触屏向单片机发送反馈数据。
以上的方式是用串口助手替代触屏与单片机通信,有一定作用但是不能完全反映单片机与触屏的工作情况,因为此时数据链路是没有和触屏连接的,我们要完全监视单片机与触屏的工作状态,就需要对连接线路进行简单搭建。

如图 6,这样的接线方式,就能利用电脑上的串口助手完全监视单片机与触屏之间的通信了,不管是单片机串口与触屏通信的发送还是接收过程数据,都能显示在串口助手的界面。
要确定字符串是否结束
可以在2.2中知道,在串口发送的时候是以最后一个字符是不是0来作为结束标志的。曾经我有过这样的经历,定义了一个数组A[5],分别给第0-4为元素赋值,例如我要显示单词“stand”,就令A[0]=’s’; A[1]=’t’; A[2]=’a’; A[3]=’n’; A[4]=’d’;我曾经天真地以为再调用SendString(A)这样子就可以了,可是,这样子做的结果就是屏幕老是进入初始化状态,当时不明原因,后来通过串口助手调试发现,在字符串“stand”后面老是多了一个异常字符,后来,我就重新定义了一下数组为A[6],并令最后一位元素A[5]=0,如此之后触屏恢复正常显示。出现这个问题的原因就是由于之前字符串没有结束标志,导致串口在A发送完后随机发送了几个数据给触屏,导致混乱。
不要轻易使用while循环
在之前的程序设计中,我会使用while来等待ADC转换完成,等温度采集完成,等待串口发送完成。可以后来发现触屏经常死屏,最初怀疑触屏有问题,可是厂家**说没有出现这样的状况,那我就自己先查查程序了,初步是怀疑while的过度使用,所以把所有的while循环都加上限制,例如假如ADC转换等待多少时间后还没有转换完成则跳出循环,假如串口发送多少个字符后还没有完成发送那也跳出循环。当把所有的while都加上跳出的限制后,一切就变得正常了。
好了,这篇**就介绍到这里,本文花了我几天的有空时间,也是写得字数最多的一篇**。谢谢查看,希望有帮助。

相关帖子

沙发
cjseng| | 2018-8-21 22:27 | 只看该作者
这个液晶每次发送命令,要等待200-300ms是个硬伤,实际使用中很不方便。

使用特权

评论回复
板凳
yonko|  楼主 | 2018-8-27 09:29 | 只看该作者
cjseng 发表于 2018-8-21 22:27
这个液晶每次发送命令,要等待200-300ms是个硬伤,实际使用中很不方便。

是有这个问题,低速显示够用。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

116

主题

229

帖子

0

粉丝