前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >STM32使用大彩串口屏程序框架使用总结

STM32使用大彩串口屏程序框架使用总结

作者头像
杨源鑫
发布2019-07-04 16:22:55
2.4K0
发布2019-07-04 16:22:55
举报
文章被收录于专栏:嵌入式开发圈嵌入式开发圈

大彩科技是专注做串口屏的厂家,网址如下:

http://www.gz-dc.com/

指令格式如下:

一般情况下,采用的是CRC格式校验的指令。

处理指令方面,大彩提供了一个例程,主要用一个队列来维护。

数据结构:

代码语言:javascript
复制
 1#define QUEUE_MAX_SIZE 128   /*!< 指令接收缓冲区大小,根据需要调整,尽量设置大一些*/
 2typedef struct _QUEUE
 3{
 4    qsize _head; //队列头
 5    qsize _tail;  //队列尾
 6    qdata _data[QUEUE_MAX_SIZE];    //队列数据缓存区
 7}QUEUE;
 8
 9static QUEUE que = {0,0,0};  //指令队列
10static uint32 cmd_state = 0;  //队列帧尾检测状态
11static qsize cmd_pos = 0;  //当前指令指针位置

操作队列的接口有:

代码语言:javascript
复制
 1/*! 
 2 *  \brief  清空指令数据
 3 */
 4extern void queue_reset(void);
 5
 6/*! 
 7 * \brief  添加指令数据
 8 * \detial 串口接收的数据,通过此函数放入指令队列 
 9 *  \param  _data 指令数据
10 */
11extern void queue_push(qdata _data);
12
13/*! 
14 *  \brief  从指令队列中取出一条完整的指令
15 *  \param  cmd 指令接收缓存区
16 *  \param  buf_len 指令接收缓存区大小
17 *  \return  指令长度,0表示队列中无完整指令
18 */
19extern qsize queue_find_cmd(qdata *cmd,qsize buf_len);

队列清空的实现很简单,只要把队列头和队队列尾检查状态、当前指针的位置置为0即可,实现如下:

代码语言:javascript
复制
1void queue_reset()
2{
3    que._head = que._tail = 0;
4    cmd_pos = cmd_state = 0;
5}

添加指令数据操作,其实就是入队的操作,也就是把数据源源不断的放到队列的缓存区中去:

代码语言:javascript
复制
1void queue_push(qdata _data)
2{
3    qsize pos = (que._head+1)%QUEUE_MAX_SIZE;
4    if(pos!=que._tail)//非满状态
5    {
6        que._data[que._head] = _data;
7        que._head = pos;
8    }
9}

从指令队列中取出一条完整的指令其实就是出队操作,先将数据出队,然后根据指令格式帧进行分割处理。

代码语言:javascript
复制
 1//从队列中取一个数据
 2static void queue_pop(qdata* _data)
 3{
 4    if(que._tail!=que._head)//非空状态
 5    {
 6        *_data = que._data[que._tail];
 7        que._tail = (que._tail+1)%QUEUE_MAX_SIZE;
 8    }
 9}
10
11qsize queue_find_cmd(qdata *buffer,qsize buf_len)
12{
13    qsize cmd_size = 0;
14    qdata _data = 0;
15    while(queue_size()>0)
16    {
17        //取一个数据
18        queue_pop(&_data);
19
20        if(cmd_pos==0&&_data!=CMD_HEAD)//指令第一个字节必须是帧头,否则跳过
21            continue;
22
23        if(cmd_pos<buf_len)//防止缓冲区溢出
24            buffer[cmd_pos++] = _data;
25
26        cmd_state = ((cmd_state<<8)|_data);//拼接最后4个字节,组成一个32位整数
27
28        //最后4个字节与帧尾匹配,得到完整帧
29        if(cmd_state==CMD_TAIL)
30        {
31            cmd_size = cmd_pos; //指令字节长度
32            cmd_state = 0;  //重新检测帧尾巴
33            cmd_pos = 0; //复位指令指针
34
35#if(CRC16_ENABLE)
36            //去掉指令头尾EE,尾FFFCFFFF共计5个字节,只计算数据部分CRC
37            if(!CheckCRC16(buffer+1,cmd_size-5))//CRC校验
38                return 0;
39
40            cmd_size -= 2;//去掉CRC16(2字节)
41#endif
42
43            return cmd_size;
44        }
45    }
46
47    return 0;//没有形成完整的一帧
48}

那么具体在哪里入队呢?在大彩提供的例程中,入队操作是在串口中断服务函数中进行的:

代码语言:javascript
复制
1void USART1_IRQHandler(void)
2{
3    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
4    {
5        uint8_t data = USART_ReceiveData(USART1);
6        queue_push(data);
7    }
8}

在这期间主要发生两个操作:

1、串口通过中断接收一个字节

2、将接收到的每一个字节放入队列缓存区中

那么又具体怎么知道串口屏给我回复的指令呢,然后发生一系列动作呢?

这时候,程序里需要有一个while(1),源源不断的等待queue_find_cmd函数给我们做取数据,完成拼接指令的过程。

代码语言:javascript
复制
 1....
 2while(1)
 3{
 4        size = queue_find_cmd(cmd_buffer,CMD_MAX_SIZE); //从缓冲区中获取一条指令        
 5        if(size>0)//接收到指令
 6        {
 7            ProcessMessage((PCTRL_MSG)cmd_buffer, size);//指令处理
 8        }
 9}
10....

cmd_buffer在这里就是一条完整的指令,再将这条完整的指令传入ProcessMessage函数,对指令进行处理,其中将数据强转为PCTRL_MSG这个数据结构,主要为:

代码语言:javascript
复制
 1typedef struct
 2{
 3    uint8    cmd_head;  //帧头
 4
 5    uint8    cmd_type;  //命令类型(UPDATE_CONTROL)  
 6    uint8    ctrl_msg;   //CtrlMsgType-指示消息的类型
 7    uint8    screen_id_high;  //产生消息的画面ID
 8    uint8    screen_id_low;
 9    uint8    control_id_high;  //产生消息的控件ID
10    uint8    control_id_low;
11    uint8    control_type; //控件类型
12
13    uint8    param[256];//可变长度参数,最多256个字节
14
15    uint8  cmd_tail[4];   //帧尾
16}CTRL_MSG,*PCTRL_MSG;

在这里接收到的cmd_buffer里的指令是把头尾去掉的,这时候我们明白了,接收过来的指令需要赋给它一定的含义,于是看ProcessMessage函数的实现:

代码语言:javascript
复制
 1/*! 
 2 *  \brief  消息处理流程,此处一般不需要更改
 3 *  \param msg 待处理消息
 4 *  \param size 消息长度
 5 */
 6void ProcessMessage( PCTRL_MSG msg, uint16 size )
 7{
 8    uint8 cmd_type = msg->cmd_type;//指令类型
 9    uint8 ctrl_msg = msg->ctrl_msg;   //消息的类型
10    uint8 control_type = msg->control_type;//控件类型
11    uint16 screen_id = PTR2U16(&msg->screen_id_high);//画面ID
12    uint16 control_id = PTR2U16(&msg->control_id_high);//控件ID
13    uint32 value = PTR2U32(msg->param);//数值
14
15    switch(cmd_type)
16    {       
17    case NOTIFY_TOUCH_PRESS://触摸屏按下
18    case NOTIFY_TOUCH_RELEASE://触摸屏松开
19        NotifyTouchXY(cmd_buffer[1],PTR2U16(cmd_buffer+2),PTR2U16(cmd_buffer+4));
20        break;  
21    case NOTIFY_WRITE_FLASH_OK://写FLASH成功
22        NotifyWriteFlash(1);
23        break;
24    case NOTIFY_WRITE_FLASH_FAILD://写FLASH失败
25        NotifyWriteFlash(0);
26        break;
27    case NOTIFY_READ_FLASH_OK://读取FLASH成功
28        NotifyReadFlash(1,cmd_buffer+2,size-6);//去除帧头帧尾
29        break;
30    case NOTIFY_READ_FLASH_FAILD://读取FLASH失败
31        NotifyReadFlash(0,0,0);
32        break;
33    case NOTIFY_READ_RTC://读取RTC时间
34        NotifyReadRTC(cmd_buffer[1],cmd_buffer[2],cmd_buffer[3],cmd_buffer[4],cmd_buffer[5],cmd_buffer[6],cmd_buffer[7]);
35        break;
36    case NOTIFY_CONTROL:
37        {
38            if(ctrl_msg==MSG_GET_CURRENT_SCREEN)//画面ID变化通知
39            {
40                NotifyScreen(screen_id);
41            }
42            else
43            {
44                switch(control_type)
45                {
46                case kCtrlButton: //按钮控件
47                    NotifyButton(screen_id,control_id,msg->param[1]);
48                    break;
49                case kCtrlText://文本控件
50                    NotifyText(screen_id,control_id,msg->param);
51                    break;
52                case kCtrlProgress: //进度条控件
53                    NotifyProgress(screen_id,control_id,value);
54                    break;
55                case kCtrlSlider: //滑动条控件
56                    NotifySlider(screen_id,control_id,value);
57                    break;
58                case kCtrlMeter: //仪表控件
59                    NotifyMeter(screen_id,control_id,value);
60                    break;
61                case kCtrlMenu://菜单控件
62                    NotifyMenu(screen_id,control_id,msg->param[0],msg->param[1]);
63                    break;
64                case kCtrlSelector://选择控件
65                    NotifySelector(screen_id,control_id,msg->param[0]);
66                    break;
67                case kCtrlRTC://倒计时控件
68                    NotifyTimer(screen_id,control_id);
69                    break;
70                default:
71                    break;
72                }
73            }           
74        }
75        break;
76    default:
77        break;
78    }
79}

这里学习到了一个编程的小技巧,将数据强转为一个结构体,再利用结构体的偏移特性来获得数据。 这个函数的作用就显而易见了,通过一条指令得知当前使用的是什么控件等等。。。

发送指令就很简单了,其实就是直接给串口发数据,这里是实现如何发送数据给串口的定义:

代码语言:javascript
复制
1#define TX_8(P1) SEND_DATA((P1)&0xFF)  //发送单个字节
2#define TX_8N(P,N) SendNU8((uint8 *)P,N)  //发送N个字节
3#define TX_16(P1) TX_8((P1)>>8);TX_8(P1)  //发送16位整数
4#define TX_16N(P,N) SendNU16((uint16 *)P,N)  //发送N个16位整数
5#define TX_32(P1) TX_16((P1)>>16);TX_16((P1)&0xFFFF)  //发送32位整数

这里是参考手册发送的指令:

代码语言:javascript
复制
 1#if(CRC16_ENABLE)
 2
 3static uint16 _crc16 = 0xffff;
 4static void AddCRC16(uint8 *buffer,uint16 n,uint16 *pcrc)
 5{
 6    uint16 i,j,carry_flag,a;
 7
 8    for (i=0; i<n; i++)
 9    {
10        *pcrc=*pcrc^buffer[i];
11        for (j=0; j<8; j++)
12        {
13            a=*pcrc;
14            carry_flag=a&0x0001;
15            *pcrc=*pcrc>>1;
16            if (carry_flag==1)
17                *pcrc=*pcrc^0xa001;
18        }
19    }
20}
21
22uint16 CheckCRC16(uint8 *buffer,uint16 n)
23{
24    uint16 crc0 = 0x0;
25    uint16 crc1 = 0xffff;
26
27    if(n>=2)
28    {
29        crc0 = ((buffer[n-2]<<8)|buffer[n-1]);
30        AddCRC16(buffer,n-2,&crc1);
31    }
32
33    return (crc0==crc1);
34}
35
36void SEND_DATA(uint8 c)
37{
38    AddCRC16(&c,1,&_crc16);
39    SendChar(c);
40}
41
42void BEGIN_CMD()
43{
44    TX_8(0XEE);
45    _crc16 = 0XFFFF;//开始计算CRC16
46}
47
48void END_CMD()
49{
50    uint16 crc16 = _crc16;
51    TX_16(crc16);//发送CRC16
52    TX_32(0XFFFCFFFF);
53}
54
55#else//NO CRC16
56
57#define SEND_DATA(P) SendChar(P)
58#define BEGIN_CMD() TX_8(0XEE)
59#define END_CMD() TX_32(0XFFFCFFFF)
60
61#endif
62
63void DelayMS(unsigned int n) 
64{
65    int i,j;  
66    for(i = n;i>0;i--)
67        for(j=1000;j>0;j--) ; 
68}
69
70void SendStrings(uchar *str)
71{
72    while(*str)
73    {
74        TX_8(*str);
75        str++;
76    }
77}
78
79void SendNU8(uint8 *pData,uint16 nDataLen)
80{
81    uint16 i = 0;
82    for (;i<nDataLen;++i)
83    {
84        TX_8(pData[i]);
85    }
86}
87
88void SendNU16(uint16 *pData,uint16 nDataLen)
89{
90    uint16 i = 0;
91    for (;i<nDataLen;++i)
92    {
93        TX_16(pData[i]);
94    }
95}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-05-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 嵌入式开发圈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档