STM32使用大彩串口屏程序框架使用总结
扫描二维码
随时随地手机看文章
微信公众号:morixinguan
关注可了解更多的教程。问题或建议,请公众号留言;
如果你觉得本文对你有帮助,欢迎赞赏
▲长按图片保存可分享至朋友圈
大彩科技是专注做串口屏的厂家,网址如下:
http://www.gz-dc.com/
指令格式如下:
一般情况下,采用的是CRC格式校验的指令。
处理指令方面,大彩提供了一个例程,主要用一个队列来维护。
数据结构:
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; //当前指令指针位置
操作队列的接口有:
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即可,实现如下:
1void queue_reset() 2{ 3 que._head = que._tail = 0; 4 cmd_pos = cmd_state = 0; 5}
添加指令数据操作,其实就是入队的操作,也就是把数据源源不断的放到队列的缓存区中去:
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}
从指令队列中取出一条完整的指令其实就是出队操作,先将数据出队,然后根据指令格式帧进行分割处理。
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//防止缓冲区溢出 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}
那么具体在哪里入队呢?在大彩提供的例程中,入队操作是在串口中断服务函数中进行的:
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函数给我们做取数据,完成拼接指令的过程。
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这个数据结构,主要为:
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函数的实现:
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}
这里学习到了一个编程的小技巧,将数据强转为一个结构体,再利用结构体的偏移特性来获得数据。
这个函数的作用就显而易见了,通过一条指令得知当前使用的是什么控件等等。。。
发送指令就很简单了,其实就是直接给串口发数据,这里是实现如何发送数据给串口的定义:
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位整数
这里是参考手册发送的指令:
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 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 (;i83 { 84 TX_8(pData[i]); 85 } 86} 87 88void SendNU16(uint16 *pData,uint16 nDataLen) 89{ 90 uint16 i = 0; 91 for (;i92 { 93 TX_16(pData[i]); 94 } 95}
长期商务合作服务:
免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!