STM32
直播中

笑过就走

9年用户 905经验值
擅长:可编程逻辑 电源/新能源 MEMS/传感技术
私信 关注
[问答]

STM32单片机和中显串口屏之间的通信该如何去实现呢

串口屏和单片机之间的通信过程是怎样的?
STM32单片机和中显串口屏之间的通信该如何去实现呢?

回帖(1)

吴觅

2021-12-7 13:40:16
STM32与中显串口屏的通信

本文将简要介绍STM32单片机和武汉中显串口屏之间的通信实现过程。不过使用大彩、迪文这些公司的串口屏的同学也能参考一下,它们的通信协议好像一样。  注意:我代码用的arduino框架,但其实在keil里面写都一样,只需要注意串口发送和接收那块的语句就OK了。
前言
本文主要介绍一下串口屏和单片机之间的通信过程,包括单片机向串口屏发送指令显示特定的文字、数据,绘制数据曲线,单片机接收串口屏上发来的信息等。   下面就串口屏通信协议、实现代码两个方面阐述具体实现过程。
一、中显串口屏的通信协议

  我用的中显串口屏的具体开发指南可以移步此链接详细了解,看看和您用的是不是相似。
  本文主要实现能向串口屏发送显示文字,数据,绘制数据曲线,并且单片机能够接收处理串口屏发来的信息。 下面简要介绍一下在串口屏上实现以上功能的需求,也就是串口屏和单片机之间的通信协议,这个是屏幕厂商制定好的,可以在该屏的开发指南中找。开发指南参考见上文链接。
1.串口屏显示文字

  在串口屏上显示中英文字符串需要通过向串口屏发送指令。以发送汉字“中国”为例,其发送的顺序是这样的:
  |帧头| 指令长度 |写变量存储器指令|变量地址|数据内容|结束符号|
|A5 5A|07|82|00 10|D6 D0 B9 FA |FF FF|
  其中:
   A5 5A:帧头
   09:指令字节长度,82 01 40 D6 D0 B9 FA FF FF(不含帧头)
   82:写变量存储器指令
   00 10:变量地址,在控件属性中设置
   D6 D0 B9 FA:数据内容,“中国”的汉字内码
   FF FF:文本结束符
  因此单片机向串口屏发送信息时应严格按照此格式。
2.串口屏显示数据

  在串口屏上显示数据的指令格式和发送文字字符是很像的,只是更改了数据内容,将数据内容替换为该数据的16进制码即可。并去除掉结束符号FF FF。以发送1234为例:
  A5 5A:帧头
  05:指令字节长度,,82 00 20 04 D2 共 5 字节(不含帧头)
  82:写变量存储器指令
  00 20:变量地址,在控件属性中设置
  04 D2:数据内容,1234 的十六进制数
3.串口屏绘制数据曲线

  在串口屏上绘制上传的数据的趋势曲线。其实和其他的也差不离,只是指令有所变化,变量地址也变为通道号。该屏一个曲线框可以实现8条曲线,每条曲线有不同的通道,发送时要设定通道号。
  A5 5A:帧头
  04:指令字节长度,,82 00 04 D2 共 4字节(不含帧头)
  84:写变量存储器指令
  00:曲线通道号,在控件属性中设置
  04 D2:数据内容,1234 的十六进制数
4.单片机分析处理串口屏指令

  在串口屏使用中,需要使用到串口屏上的按钮,按下按钮会发送一串信息到单片机串口,这段信息有固定格式: A5 5A 06 83 00 00 01 02 22
其中:

  A5 5A:帧头
06:指令字节长度,83 00 00 01 02 22 共 6 字节(不含帧头)
83:读变量存储器指令
00 00:变量存储器地址
01:数据字长度, 02 22 共 1 字长
02 22:数据内容,设置的键值,在控件属性中设置
  因此在单片机中分析每一次发送过来的这串数据,其中变量存储器地址、数据内容用户在制作串口屏显示界面时可以直接设定,因此的那篇及可以根据判断这几个数据来执行不同的命令。
下面展示代码部分:
二、实现代码

1.文本变量

/* 在目的变量地址写入文字,中英文均可 */
        void wText( int idCtrl, const char *msg )               /* 显示中英文文本 */
        {
                int textLen = strlen( msg );
                array[0]        = USER_R3;
                array[1]        = USER_RA;
                array[2]        = 1 + 2 + textLen;              /* 命令占1字节,地址占2字节,再加上数据长度4字节 */
                array[3]        = 0x82;
                array[4]        = (idCtrl >> 8);                /* 取地址的高8位 */
                array[5]        = idCtrl;                       /* 取地址的低8位 */


                memcpy( array + 6, msg, textLen );


                array[6 + textLen]        = 0xFF;
                array[7 + textLen]        = 0xFF;


                for ( int i = 0; i < (8 + textLen); i++ )       /* 通过串口发送指令 */
                {
                        ser->write( array );
                }
        }
在代码中,第1行的函数参数,idCtrl为屏上指定文本变量框的存储地址,msg即为要输入的字符串,可以在调用函数时直接写入。第2-16行表示将该字符串按照串口屏文本变量的通信格式上传到设定变量地址处。
需要注意的是,这里如果要显示汉字,那么你在串口屏的文本变量控件中使用了什么样的编码方式(GBK、GB2312等),那么编译器也要用相同的编码方式,这样转化出来的汉字才会正确显示。
使用时,调用函数完成文本显示。例如要在串口屏上地址为0x1020的文本显示控件中显示“中国人民万岁 CHINA”,调用函数,参数设置为:
wText(0x1020,”中国人民万岁 CHINA”);
2.数据变量

        void wNum_4B( int idCtrl, float val, int digi = 3 )     /* 绘制4字节数据 */
        {
                for ( int i = 0; i < digi; i++ )
                        val *= 10;
                long        dat        = val;
                uint8_t *pc        = (uint8_t *) &dat;
                array[0]        = USER_R3;
                array[1]        = USER_RA;
                array[2]        = 1 + 2 + 4;            /* 命令占1字节,地址占2字节,再加上数据长度4字节 */
                array[3]        = 0x82;
                array[4]        = (idCtrl >> 8);        /* 取地址的高8位 */
                array[5]        = idCtrl;               /* 取地址的低8位 */
                array[6]        = pc[3];                /* 取数据的高8位 */
                array[7]        = pc[2];                /* 取数据的低8位 */
                array[8]        = pc[1];                /* 取数据的高8位 */
                array[9]        = pc[0];                /* 取数据的低8位 */


                for ( int i = 0; i < 10; i++ )          /* 通过串口发送指令 */
                {
                        ser->write( array );
                }
        }
在代码中,第1行的函数参数,idCtrl表示串口屏上设定的数据变量控件的变量地址,val表示要发送的数据变量,didi表示该数据小数点后的位数。其余的代码作用为将该数据以串口屏通信格式发送到串口屏上显示。
使用时,可以直接调用该函数,例如要将一个保留了两位小数的变量a显示在串口屏上地址为0x1010的变量框内,调用函数,参数设置为:
wNum_4B(0x1010, a,2);
需要注意的是,因为该串口屏的数据变量控件的属性设置有2B和4B之分,有时候数据可能会比较大,因此本文此处代码采用4B方式。
3.绘制数据曲线

//自定义曲线框参数绘制曲线
        // idCtrl:曲线通道  curveId:曲线描述指针 val:变量 vdCentral:曲线中心位置变量值 max:数据范围 digi:数据小数位数       
        void wCurve( int idCtrl, int curveId, float val, int vdCentral, int max ,int digi)
        {
                for ( int i = 0; i < digi; i++ )
                {
                        val *= 10;
                        vdCentral *= 10;
                        max *= 10;
                }


                wNum_2B( curveId + 0x06, vdCentral, 0 );        /* 曲线中心位置的变量数据值 */
                int mulY = 235 * 256 / (max);
                wNum_2B( curveId + 0x08, mulY, 0 );             /* 曲线纵轴放大倍数 */


                uint16_t        dat        = val;
                uint8_t                *pc        = (uint8_t *) &dat;
                array[0]        = USER_R3;
                array[1]        = USER_RA;
                array[2]        = 1 + 1 + 2;                    /* 命令占1字节,地址占2字节,再加上数据长度4字节 */
                array[3]        = 0x84;
                array[4]        = idCtrl;
                array[5]        = pc[1];                        /* 取数据的高8位 */
                array[6]        = pc[0];                        /* 取数据的低8位 */


                for ( int i = 0; i < 7; i++ )                   /* 通过串口发送指令 */
                {
                        ser->write( array );
                }
        }
在代码中,第1行函数参数,idCtrl为曲线的数据源通道,用户可以调用时直接写入指定通道。val即为要显示的数据值。curveId为曲线变量框的描述指针,目的是为了能改变曲线变量框的一些属性参数值。vdCentral是用户所要显示的数据在曲线Y轴中心位置的数据值。max是用户所要显示数据最大最小值的差值。写曲线指令为0x84。5-7行是设置曲线框的具体参数,10-20行是将该数据按照串口屏曲线绘制的通信格式上传到屏上的曲线框中。用户调用该函数时,可以指定曲线显示变量,对其纵轴放大倍数以及曲线中心位置的变量值进行设定。
以上具体可以在串口屏开发指南中查阅。
使用时,调用函数写入参数即可。例如要绘制一个数值范围在0-100范围内的变量b的变化趋势曲线,该曲线控件在串口屏上设置描述指针为0x1060,数据源通道设置为0。调用函数,参数设置为:
wCurveDiy(0x01, b, 0x1060, 50, 100);
4.单片机解析串口屏指令



        void dealCom()
        {
                //判断串口缓冲区数据数量是否在20ms内没变,如果没变则开始解析
                nowTime                = millis();     /* 记录当前时间 */
                nowLcdBufNum        = ser->available();
                if ( nowTime - lastTime >= 20 ) /* 检查 20 ms是否通过 */
                {
                        lastLcdBufNum        = ser->available();
                        lastTime        = nowTime;
                        if ( nowLcdBufNum == lastLcdBufNum )
                        {


                                commBuff[talNum] = ser->read();
                                talNum++;
                                if ( talNum == BUFFER_SIZE )
                                        talNum = 0;
                                uint16_t        i, curNum, tem_talNum;
                                uint16_t        nowbuffer, len;
                                len                = startNum;
                                tem_talNum        = talNum;
                                if ( tem_talNum == len )
                                        return;
                                if ( commBuff[startNum] != USER_R3 )
                                {
                                        startNum++;
                                        if ( startNum == BUFFER_SIZE )
                                                startNum = 0;
                                        return;
                                }
                                if ( tem_talNum > len )
                                        nowbuffer = tem_talNum - len;
                                else
                                        nowbuffer = tem_talNum + BUFFER_SIZE - len;
                                if ( nowbuffer < 5 )
                                        return;
                                curNum = startNum + 2;
                                if ( curNum > BUFFER_SIZE - 1 )
                                        curNum -= BUFFER_SIZE;
                                len = commBuff[curNum] + 3;
                                if ( nowbuffer < len )
                                        return;
                                i        = 0;
                                curNum        = startNum;
                                while ( 1 )
                                {
                                        cmdBuf[i++] = commBuff[curNum++];
                                        if ( curNum == BUFFER_SIZE )
                                                curNum = 0;
                                        if ( i == 4 )
                                        {
                                                if ( cmdBuf[0] != USER_R3 || cmdBuf[1] != USER_RA ) /*  */
                                                {
                                                        startNum = curNum;
                                                        return;
                                                }
                                                len = cmdBuf[2];
                                        }else if ( i > 4 )
                                        {
                                                if ( i == len + 3 )     /*  */
                                                {
                                                        startNum = curNum;
                                                        break;
                                                }else if ( i > 255 )    /*  */
                                                {
                                                        startNum = curNum;
                                                        return;
                                                }else if ( curNum == tem_talNum )
                                                        return;
                                        }
                                }
                                cmdtype = cmdBuf[3];
                                exflag = cmdBuf[4];
                                highAdd = cmdBuf[4];
                                lowAdd = cmdBuf[5];
                        }
                        //cmdBuf[4][5]:按钮变量地址  cmdBuf[7][8]:按钮键值
                }


        }
这一大段代码,就起到了串口屏每发一次信息,单片机读取串口缓冲区的内容并将这一串信息分开存入cmdBuf[]中的作用。cmdBuf[]里面就是上文提到的那一串按下屏幕按钮后屏幕发送的信息。根据特定的几个数据的不同,来执行不同的指令。
总结

  以上就是使用中显串口屏实现文本变量显示、数据变量显示、数据趋势曲线绘制、读取屏上指令功能的大致过程。大彩和迪文好像也用的类似的通讯协议,可以参考参考。希望对大家有帮助!  
举报

更多回帖

发帖
×
20
完善资料,
赚取积分