本帖最后由 D5Power 于 2017-11-16 16:46 编辑 0 a4 Z. M f; d* h, _( u2 e 为了实现可变长度数据的发送,必须要自己定义协议。我们先定义一个比较简单的协议: 数据包头:2字节,第一字节用来存储命令码,第二字节用来存储整个数据包的长度(不含包头部分) 数据包包体:N字节,根据不同的命令码发送不同的数据。 设置为1个字节中断,并在中断时移动指针并再次打开中断,从而保证接受到的数据按顺序排列在缓冲区中。然后再对缓冲区的内容进行分析。 为了保证解包的过程不会占用中断进程,解包操作放在了主循环中,并设置10毫秒的延迟。程序整体思路如下: 在开始之前,先做了个小测试,确认收到N个字节的时候,1字节的接受中断会被触发N次。底层确认没问题后,开始撸代码咯。先来看一下头文件里的各种声明: 1 M& z* p: b2 {0 V+ u// 包头尺寸 #define MIN_HEAD_SIZE 2 // 最大包体尺寸,清注意匹配你的开发板最大内存。否则会分配失败的 #define MAX_PACK_SIZE 16 // 包头结构体 struct __head_buff { char cmd; char length; }; // 完整数据包结构体 struct __d5pack { char cmd; char length; char data[14]; // 根据最大包体尺寸,扣除包头长度 }; ) i. J; \$ q0 n u, \ Y// 数据包待处理队列 struct __d5pack *d5list[64]; //待处理数据包个数 int d5list_count = 0; 然后在主循环前,处理上述声明中需要初始化的变量。 // 将pBuffer指向buffer首地址 pBuffer = (uint8_t *)&buffer; // 将pPack指向buffer首地址 pPack = (uint8_t *)&buffer; // 打开1字节接收中断 HAL_UART_Receive_IT(&huart2,pBuffer,1); 6 B6 K r- W; K3 B5 n, O e. X接下来是中断部分,处理的核心逻辑。每收到一个字节,pBuffer就会自增,从而保证接收到的字节按顺序推入到缓冲区中。recived_count也会自增,用来提供后续的数据接受数量的判断。 pBuffer++; recived_count++; # f% H, }9 x! m- G) b由于每收到一个字节,recived_count就会自增,因此可以根据recived_count来判断缓冲区的字节是否满足解包要求。由于数据包可能是分多次发过来的,因此我们需要知道,现在我们是已经开始接受一个数据包的包体了,还是连包头都没收到。这两个部分的处理是有区别的。 ' M+ r9 D9 [7 E! t0 Q3 C( g鉴于此,我设置了一个head_is_ready的变量,当2字节的包头发送完,即会置为1: if(head_is_ready==0) { if(recived_count>=MIN_HEAD_SIZE) { head = (struct __head_buff *)buffer; head_is_ready = 1; packsize = head->length+MIN_HEAD_SIZE; } }... / I: _9 |1 A) d7 C- b在以上代码中,当缓冲区中放了超过2个字节,就会进行解包操作。通过结构体__head_buff,将缓冲区中的内容格式化,并获取到当前的数据包的总尺寸(length+MIN_HEAD_SIZE)。 5 V. g7 P" w5 u, n: b...else if(recived_count>=packsize){ //printf("got full package!"); // PART I recived_count-=packsize; head_is_ready=0; // PART II4 C( `$ [6 X* w+ [ struct __d5pack * pack = malloc(sizeof(struct __d5pack)); memcpy(pack,pPack,packsize); d5list_push(pack); // PART III memcpy(pPack,pPack+packsize,MAX_PACK_SIZE-packsize); pBuffer -=packsize; } / L1 G) E1 q4 [& `当包头被解析后,再收到数据就不会再进行包头的处理了,而是通过已经获取好的packagesize来检查是否把完整的数据包给接收完了。下一次再有数据,就应该是下一个数据包的包头了。因此head_is_ready需要置0,同时recived_count扣除掉当前已经收到的数据包长度(PART I)。 ! ~" w$ T. n* z% a6 G# l }同时,把当前收到的完整数据包,通过结构体__d5pack进行格式化,并将指针推入指针数组d5list中。(推入方法为d5list_push,自己写的,d5list[count++] = p就好了)。等待通过主循环进行处理。上述代码见PART II 4 M/ @6 @4 q6 c" b0 G/ Z: p当一个数据包解析完后,这段数据即可废弃。但是缓冲区后后面的数据可能是有效的,因此需要把后面的数据挪到前面来,替换掉已经用过的数据,同时,移动指针pBuffer到有效数据的末尾,接下来继续接受数据: 2 ^5 t$ t" k& D( e1 d" {, N6 \HAL_UART_Receive_IT(&huart2,pBuffer,1); * f" j* q0 p% x5 r" e而在主循环中,则定期去处理已经放在待处理队列d5list while(d5list_length()>0) { struct __d5pack * p = d5list_pop(); uint8_t cmd = p->cmd; // ... ... 以下根据cmd进行对应的功能处理即可 } HAL_Delay(10); 如以上代码所示,每10毫秒,如果队列不为空,则程序会依次取出指针,并获取其对应的命令码,再根据命令码进行后续的功能处理就可以了。 ----------------我是淫荡的分割线---------------- 编写中的笔记会在我个人的公众号进行,请各位高手斧正。 ; e C5 ~9 ? M6 m. `: \ 关于单片机、H5游戏开发和微信小程序 |
关于以下这部分的叙述 请问中断部分是写在哪里的?回调函数里吗?
”HAL_UART_Receive_IT(&huart2,pBuffer,1);; \' p# n* U/ I5 \' O: C* q! Z
; {. e$ u$ h6 L5 o' n/ k1 ]
接下来是中断部分,处理的核心逻辑。每收到一个字节,pBuffer就会自增,从而保证接收到的字节按顺序推入到缓冲区中。recived_count也会自增,用来提供后续的数据接受数量的判断。, A9 d6 F* X" W1 {0 e8 c6 V5 _: D
0 x% Z3 g+ [' A1 N, X" F9 b* Z5 o+ a
pBuffer++;2 R8 u% S# F3 c1 N
recived_count++; ”& e- a' D) ]8 l: Y) b# F/ E; n