STM32使用大彩串口屏程序框架使用總結(jié)
微信公眾號:morixinguan
關(guān)注可了解更多的教程。問題或建議,請公眾號留言;
如果你覺得本文對你有幫助,歡迎贊賞
▲長按圖片保存可分享至朋友圈
大彩科技是專注做串口屏的廠家,網(wǎng)址如下:
http://www.gz-dc.com/
指令格式如下:
一般情況下,采用的是CRC格式校驗的指令。
處理指令方面,大彩提供了一個例程,主要用一個隊列來維護(hù)。
數(shù)據(jù)結(jié)構(gòu):
1#define QUEUE_MAX_SIZE?128 /*!< 指令接收緩沖區(qū)大小,根據(jù)需要調(diào)整,盡量設(shè)置大一些*/ 2typedef struct _QUEUE 3{ 4 qsize?_head; //隊列頭 5 qsize?_tail; //隊列尾 6 qdata?_data[QUEUE_MAX_SIZE]; //隊列數(shù)據(jù)緩存區(qū) 7}QUEUE; 8 9static QUEUE?que?=?{0,0,0}; //指令隊列 10static uint32?cmd_state?= 0; //隊列幀尾檢測狀態(tài) 11static qsize?cmd_pos?= 0; //當(dāng)前指令指針位置
操作隊列的接口有:
1/*! 2 *??\brief??清空指令數(shù)據(jù) 3 */ 4extern void queue_reset(void); 5 6/*! 7 *?\brief??添加指令數(shù)據(jù) 8 *?\detial?串口接收的數(shù)據(jù),通過此函數(shù)放入指令隊列 9 *??\param??_data?指令數(shù)據(jù) 10 */ 11extern void queue_push(qdata?_data); 12 13/*! 14 *??\brief??從指令隊列中取出一條完整的指令 15 *??\param??cmd?指令接收緩存區(qū) 16 *??\param??buf_len?指令接收緩存區(qū)大小 17 *??\return??指令長度,0表示隊列中無完整指令 18 */ 19extern qsize queue_find_cmd(qdata?*cmd,qsize?buf_len);
隊列清空的實現(xiàn)很簡單,只要把隊列頭和隊隊列尾檢查狀態(tài)、當(dāng)前指針的位置置為0即可,實現(xiàn)如下:
1void queue_reset() 2{ 3 que._head?=?que._tail?= 0; 4 cmd_pos?=?cmd_state?= 0; 5}
添加指令數(shù)據(jù)操作,其實就是入隊的操作,也就是把數(shù)據(jù)源源不斷的放到隊列的緩存區(qū)中去:
1void?queue_push(qdata?_data) 2{ 3 qsize pos =?(que._head+1)%QUEUE_MAX_SIZE; 4 if(pos!=que._tail)//非滿狀態(tài) 5 { 6 que._data[que._head]?=?_data; 7 que._head?= pos; 8 } 9}
從指令隊列中取出一條完整的指令其實就是出隊操作,先將數(shù)據(jù)出隊,然后根據(jù)指令格式幀進(jìn)行分割處理。
1//從隊列中取一個數(shù)據(jù) 2static void queue_pop(qdata*?_data) 3{ 4 if(que._tail!=que._head)//非空狀態(tài) 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 //取一個數(shù)據(jù) 18 queue_pop(&_data); 19 20 if(cmd_pos==0&&_data!=CMD_HEAD)//指令第一個字節(jié)必須是幀頭,否則跳過 21 continue; 22 23 if(cmd_pos//防止緩沖區(qū)溢出 24 buffer[cmd_pos++]?=?_data; 25 26 cmd_state?=?((cmd_state<<8)|_data);//拼接最后4個字節(jié),組成一個32位整數(shù) 27 28 //最后4個字節(jié)與幀尾匹配,得到完整幀 29 if(cmd_state==CMD_TAIL) 30 { 31 cmd_size?=?cmd_pos; //指令字節(jié)長度 32 cmd_state?= 0; //重新檢測幀尾巴 33 cmd_pos?= 0; //復(fù)位指令指針 34 35#if(CRC16_ENABLE) 36 //去掉指令頭尾EE,尾FFFCFFFF共計5個字節(jié),只計算數(shù)據(jù)部分CRC 37 if(!CheckCRC16(buffer+1,cmd_size-5))//CRC校驗 38 return 0; 39 40 cmd_size?-= 2;//去掉CRC16(2字節(jié)) 41#endif 42 43 return cmd_size; 44 } 45 } 46 47 return 0;//沒有形成完整的一幀 48}
那么具體在哪里入隊呢?在大彩提供的例程中,入隊操作是在串口中斷服務(wù)函數(shù)中進(jìn)行的:
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}
在這期間主要發(fā)生兩個操作:
1、串口通過中斷接收一個字節(jié)
2、將接收到的每一個字節(jié)放入隊列緩存區(qū)中
那么又具體怎么知道串口屏給我回復(fù)的指令呢,然后發(fā)生一系列動作呢?
這時候,程序里需要有一個while(1),源源不斷的等待queue_find_cmd函數(shù)給我們做取數(shù)據(jù),完成拼接指令的過程。
1.... 2while(1) 3{ 4 size?=?queue_find_cmd(cmd_buffer,CMD_MAX_SIZE); //從緩沖區(qū)中獲取一條指令 5 if(size>0)//接收到指令 6 { 7 ProcessMessage((PCTRL_MSG)cmd_buffer,?size);//指令處理 8 } 9} 10....
cmd_buffer在這里就是一條完整的指令,再將這條完整的指令傳入ProcessMessage函數(shù),對指令進(jìn)行處理,其中將數(shù)據(jù)強(qiáng)轉(zhuǎn)為PCTRL_MSG這個數(shù)據(jù)結(jié)構(gòu),主要為:
1typedef struct 2{ 3 uint8 cmd_head; //幀頭 4 5 uint8 cmd_type; //命令類型(UPDATE_CONTROL) 6 uint8 ctrl_msg; //CtrlMsgType-指示消息的類型 7 uint8 screen_id_high; //產(chǎn)生消息的畫面ID 8 uint8 screen_id_low; 9 uint8 control_id_high; //產(chǎn)生消息的控件ID 10 uint8 control_id_low; 11 uint8 control_type; //控件類型 12 13 uint8 param[256];//可變長度參數(shù),最多256個字節(jié) 14 15 uint8 cmd_tail[4]; //幀尾 16}CTRL_MSG,*PCTRL_MSG;
在這里接收到的cmd_buffer里的指令是把頭尾去掉的,這時候我們明白了,接收過來的指令需要賦給它一定的含義,于是看ProcessMessage函數(shù)的實現(xiàn):
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);//數(shù)值 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: //進(jìn)度條控件 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}
這里學(xué)習(xí)到了一個編程的小技巧,將數(shù)據(jù)強(qiáng)轉(zhuǎn)為一個結(jié)構(gòu)體,再利用結(jié)構(gòu)體的偏移特性來獲得數(shù)據(jù)。
這個函數(shù)的作用就顯而易見了,通過一條指令得知當(dāng)前使用的是什么控件等等。。。
發(fā)送指令就很簡單了,其實就是直接給串口發(fā)數(shù)據(jù),這里是實現(xiàn)如何發(fā)送數(shù)據(jù)給串口的定義:
1#define TX_8(P1)?SEND_DATA((P1)&0xFF) //發(fā)送單個字節(jié) 2#define TX_8N(P,N)?SendNU8((uint8?*)P,N) //發(fā)送N個字節(jié) 3#define TX_16(P1)?TX_8((P1)>>8);TX_8(P1) //發(fā)送16位整數(shù) 4#define TX_16N(P,N)?SendNU16((uint16?*)P,N) //發(fā)送N個16位整數(shù) 5#define TX_32(P1)?TX_16((P1)>>16);TX_16((P1)&0xFFFF) //發(fā)送32位整數(shù)
這里是參考手冊發(fā)送的指令:
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);//發(fā)送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++; } 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}
長期商務(wù)合作服務(wù):
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!