先說(shuō)串口,這個(gè)應(yīng)該都知道吧(不知道的童鞋,先把基本功學(xué)好),大部分單片機(jī)或者處理器都會(huì)帶一個(gè)或者多個(gè)串口,方便進(jìn)行數(shù)據(jù)的通信。
那么,串口的循環(huán)隊(duì)列是什么?這里以STM32的串口為例,進(jìn)行解釋說(shuō)明。
假設(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ù)組清零?
先說(shuō)一次讀完,然后清零的這個(gè)方法為什么不行。
1、讀的時(shí)候,里面的數(shù)據(jù)不一定是完整的。有可能某組數(shù)據(jù)剛接收到一半兒。
2、讀完以后,清零之前,如果進(jìn)來(lái)新的數(shù)據(jù)怎么辦?
所以,比較穩(wěn)妥的方法是,一次只讀一個(gè)數(shù)據(jù),讀一次,清除該數(shù)據(jù)所占的位置。所以這需要一個(gè)變量,來(lái)記錄數(shù)據(jù)頭在這個(gè)數(shù)組中的位置。
第二,當(dāng)有新數(shù)據(jù)來(lái)的時(shí)候,要知道它能放在哪,所以要有一個(gè)變量,來(lái)記錄數(shù)據(jù)尾在哪。
第三,如果有必要,你可以定義一個(gè)變量來(lái)記錄數(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.
如此一來(lái),相當(dāng)于把這個(gè)數(shù)組的頭和尾連了起來(lái),成了一個(gè)封閉的環(huán),這種處理方式,就叫做串口的循環(huán)隊(duì)列。只要確保數(shù)組夠大,處理速度夠快,那么頭和尾就不會(huì)撞上。當(dāng)然,程序上也要對(duì)這種意外情況做一個(gè)處理。以下圖片來(lái)自網(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;
}
}
然后就能用了,這是寫(xiě)操作:
write_ringbuff(USART_ReceiveData(USART1));
這是讀操作:
read_ringbuff(&uart_buf[0]);
讀操作的函數(shù)里,還可以增加一個(gè)操作,就是讀出以后,把該位置數(shù)據(jù)清零,這個(gè)看個(gè)人需要。
以上,就是串口循環(huán)隊(duì)列的一個(gè)簡(jiǎn)介,如果有寫(xiě)的不好的,歡迎留言指正。當(dāng)然,方法千千萬(wàn),不一定只能用這種。最后,借用流浪地球的一句經(jīng)典臺(tái)詞作為結(jié)尾:
方法千萬(wàn)條,穩(wěn)定第一條。
代碼不規(guī)范,碼農(nóng)兩行淚。
-
STM32
+關(guān)注
關(guān)注
2291文章
11026瀏覽量
363775 -
串口
+關(guān)注
關(guān)注
15文章
1586瀏覽量
79668 -
代碼
+關(guān)注
關(guān)注
30文章
4895瀏覽量
70498
原文標(biāo)題:基于STM32的串口循環(huán)隊(duì)列
文章出處:【微信號(hào):gh_c472c2199c88,微信公眾號(hào):嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
RDMA簡(jiǎn)介5之RoCE V2隊(duì)列分析
NVME控制器之隊(duì)列管理模塊
NVME控制器之隊(duì)列管理模塊

技術(shù)干貨驛站 ▏深入理解C語(yǔ)言:嵌套循環(huán)與循環(huán)控制的底層原理

可靠性溫度循環(huán)試驗(yàn)至少需要幾個(gè)循環(huán)?

JavaWeb消息隊(duì)列使用指南
探索字節(jié)隊(duì)列的魔法:多類(lèi)型支持、函數(shù)重載與線程安全

為什么同一個(gè)隊(duì)列引用的全局變量,運(yùn)行在兩個(gè)子vi中發(fā)現(xiàn)隊(duì)列數(shù)據(jù)丟失了
兩個(gè)循環(huán)里后臺(tái)讀取串口信息了,和前面板有一個(gè)按鈕 怎么去控制另外的同一個(gè)動(dòng)作呢
嵌入式環(huán)形隊(duì)列與消息隊(duì)列的實(shí)現(xiàn)原理
玩轉(zhuǎn)RT-Thread之消息隊(duì)列的應(yīng)用

評(píng)論