先說串口,這個(gè)應(yīng)該都知道吧(不知道的童鞋,先把基本功學(xué)好),大部分單片機(jī)或者處理器都會(huì)帶一個(gè)或者多個(gè)串口,方便進(jìn)行數(shù)據(jù)的通信。
那么,串口的循環(huán)隊(duì)列是什么?這里以STM32的串口為例,進(jìn)行解釋說明。
假設(shè)串口一次只發(fā)一個(gè)數(shù)據(jù),這倒是簡(jiǎn)單了,每次只對(duì)這一個(gè)數(shù)據(jù)進(jìn)行判斷,然后處理相關(guān)指令。但現(xiàn)實(shí)不會(huì)一直都這么美好,很多時(shí)候你收到的可能是一大串?dāng)?shù)據(jù),你要先小心翼翼的把它們存好,然后再依次判斷這里面有哪些指令要處理。
假設(shè)你定義了一個(gè)30個(gè)元素的數(shù)組a[30],每次串口收到數(shù)據(jù)都往里面存,存的時(shí)候地址加一。這個(gè)操作很簡(jiǎn)單吧,應(yīng)該是都會(huì)的。
但是取的時(shí)候怎么???你收到的指令可能是2個(gè)數(shù)據(jù),也可能是3個(gè)數(shù)據(jù),幾種長(zhǎng)度不一樣的指令混在一起。
一次從數(shù)組里讀出幾個(gè)數(shù)據(jù)?怎么快速騰出已讀數(shù)據(jù)的位置?還是一次都讀完,然后整個(gè)數(shù)組清零?
先說一次讀完,然后清零的這個(gè)方法為什么不行。
1、讀的時(shí)候,里面的數(shù)據(jù)不一定是完整的。有可能某組數(shù)據(jù)剛接收到一半兒。
2、讀完以后,清零之前,如果進(jìn)來新的數(shù)據(jù)怎么辦?
所以,比較穩(wěn)妥的方法是,一次只讀一個(gè)數(shù)據(jù),讀一次,清除該數(shù)據(jù)所占的位置。所以這需要一個(gè)變量,來記錄數(shù)據(jù)頭在這個(gè)數(shù)組中的位置。
第二,當(dāng)有新數(shù)據(jù)來的時(shí)候,要知道它能放在哪,所以要有一個(gè)變量,來記錄數(shù)據(jù)尾在哪。
第三,如果有必要,你可以定義一個(gè)變量來記錄數(shù)據(jù)長(zhǎng)度,存入的時(shí)候加一,取出的時(shí)候減一。
第四,也是比較重要的,如果數(shù)據(jù)尾已經(jīng)是a[29]了,下一個(gè)數(shù)據(jù)放哪?整個(gè)數(shù)組都清掉?NO,假設(shè)此時(shí)a[0]~a[10]已經(jīng)被取出了,數(shù)據(jù)頭變成了a[11]。那么新的數(shù)據(jù)尾變成a[0],即當(dāng)數(shù)據(jù)尾大于等于30的時(shí)候,變成0.
如此一來,相當(dāng)于把這個(gè)數(shù)組的頭和尾連了起來,成了一個(gè)封閉的環(huán),這種處理方式,就叫做串口的循環(huán)隊(duì)列。只要確保數(shù)組夠大,處理速度夠快,那么頭和尾就不會(huì)撞上。當(dāng)然,程序上也要對(duì)這種意外情況做一個(gè)處理。以下圖片來自網(wǎng)絡(luò):

下面是代碼,核心的部分都在這,復(fù)制粘貼一下,基本就可以了。
首先是定義一個(gè)結(jié)構(gòu)體,關(guān)于數(shù)據(jù)頭、數(shù)據(jù)尾、數(shù)組的:
typedef struct
{
u16 Head;
u16 Tail;
u16 Length;
u8 Rsv_DAT[50];
}ringbuff_t;
ringbuff_t Ringbuff;
然后是結(jié)構(gòu)體初始化:
void ringbuff_init(void)
{
Ringbuff.Head = 0;
Ringbuff.Tail = 0;
Ringbuff.Length = 0;
}
然后是存入數(shù)據(jù)的操作,把這個(gè)函數(shù)放進(jìn)串口接收中斷就行:
u8 write_ringbuff(u8 data)
{
if(Ringbuff.Length >= 50)
{
return FALSE;
}
else
{
Ringbuff.Rsv_DAT[Ringbuff.Tail] = data;
Ringbuff.Tail = (Ringbuff.Tail + 1)% 50;//防止越界
Ringbuff.Length++;
return TRUE;
}
}
然后是取出數(shù)據(jù)的操作:
u8 read_ringbuff(u8 *rdata)
{
if(Ringbuff.Length == 0)
{
return FALSE;
}
else
{
*rdata = Ringbuff.Rsv_DAT[Ringbuff.Head];
Ringbuff.Rsv_DAT[Ringbuff.Head] = 0;
Ringbuff.Head = (Ringbuff.Head + 1)%50;
Ringbuff.Length--;
return TRUE;
}
}
然后就能用了,這是寫操作:
write_ringbuff(USART_ReceiveData(USART1));
這是讀操作:
read_ringbuff(&uart_buf[0]);
讀操作的函數(shù)里,還可以增加一個(gè)操作,就是讀出以后,把該位置數(shù)據(jù)清零,這個(gè)看個(gè)人需要。
以上,就是串口循環(huán)隊(duì)列的一個(gè)簡(jiǎn)介,如果有寫的不好的,歡迎留言指正。當(dāng)然,方法千千萬,不一定只能用這種。最后,借用流浪地球的一句經(jīng)典臺(tái)詞作為結(jié)尾:
方法千萬條,穩(wěn)定第一條。
代碼不規(guī)范,碼農(nóng)兩行淚。
-
STM32
+關(guān)注
關(guān)注
2310文章
11171瀏覽量
373767 -
串口
+關(guān)注
關(guān)注
15文章
1622瀏覽量
82975 -
代碼
+關(guān)注
關(guān)注
30文章
4972瀏覽量
74081
原文標(biāo)題:基于STM32的串口循環(huán)隊(duì)列
文章出處:【微信號(hào):gh_c472c2199c88,微信公眾號(hào):嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
RDMA設(shè)計(jì)43:隊(duì)列刪除及連接斷開功能測(cè)試
RDMA設(shè)計(jì)25:隊(duì)列管理模塊之發(fā)送模塊詳細(xì)設(shè)計(jì)分析
RDMA設(shè)計(jì)26:隊(duì)列管理模塊設(shè)計(jì)之接收隊(duì)列模塊詳細(xì)分析
RDMA設(shè)計(jì)24:隊(duì)列管理模塊設(shè)計(jì)
RDMA設(shè)計(jì)17:隊(duì)列管理模塊設(shè)計(jì)2
C語(yǔ)言的循環(huán)隊(duì)列
NVMe高速傳輸之?dāng)[脫XDMA設(shè)計(jì)54:如何測(cè)試隊(duì)列管理功能2
【瑞薩RA6E2地奇星開發(fā)板試用】+ 4.使用循環(huán)隊(duì)列將串口接收到的數(shù)據(jù)一個(gè)不丟的發(fā)送出去
NVMe高速傳輸之?dāng)[脫XDMA設(shè)計(jì)53:如何測(cè)試隊(duì)列管理功能
優(yōu)先級(jí)隊(duì)列介紹
基于環(huán)形隊(duì)列的UART收發(fā)回顯實(shí)驗(yàn)
人工智能行業(yè)如何使用for循環(huán)語(yǔ)句進(jìn)行循環(huán)
【RA4M2-SENSOR】—— 13.串口實(shí)現(xiàn)循環(huán)隊(duì)列
RabbitMQ消息隊(duì)列解決方案
NVME控制器之隊(duì)列管理模塊
串口的循環(huán)隊(duì)列是什么?
評(píng)論