自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

STM32串口開發(fā)之環(huán)形緩沖區(qū)

開發(fā) 前端
緩沖區(qū)看名字就知道,是緩沖數(shù)據(jù)用的。實現(xiàn)緩沖區(qū)最簡單的辦法時,定義多個數(shù)組,接收一包數(shù)據(jù)到數(shù)組A,就把接收數(shù)據(jù)的地址換成數(shù)組B,每個數(shù)據(jù)有個標(biāo)記字節(jié)用于表示這個數(shù)組是否收到數(shù)據(jù),收到數(shù)據(jù)是否處理完成。

[[407451]]

01簡介

在之前的文章《stm32 串口詳解》中,我們講解了串口的基本應(yīng)用,使用串口中斷接收數(shù)據(jù),串口中斷發(fā)送回包(一般可以使用非中斷形式發(fā)送回包,在數(shù)據(jù)接收不頻繁的應(yīng)用中。串口接收中斷保證串口數(shù)據(jù)及時響應(yīng),使用非中斷方式發(fā)送回包即可)。

后面的文章《STM32使用DMA接收串口數(shù)據(jù)》和《STM32使用DMA發(fā)送串口數(shù)據(jù)》講解了如何使用DMA輔助串口收發(fā)數(shù)據(jù),使用DMA的好處在于不用CPU即可完成串口收發(fā)數(shù)據(jù),減輕CPU負(fù)擔(dān),在串口通信頻繁且不想頻繁中斷的應(yīng)用中非常有用。

除了上述兩種場景,還有一種應(yīng)用場景:串口接收數(shù)據(jù)長度位置,頻率未知,不要求實時處理的場景。如果采用上述方案,接收一幀數(shù)據(jù)立即處理,那么在處理的時候來的數(shù)據(jù)包就“丟失”了。這個時候就需要緩沖隊列來解決這個問題。

02緩沖區(qū)

緩沖區(qū)看名字就知道,是緩沖數(shù)據(jù)用的。實現(xiàn)緩沖區(qū)最簡單的辦法時,定義多個數(shù)組,接收一包數(shù)據(jù)到數(shù)組A,就把接收數(shù)據(jù)的地址換成數(shù)組B,每個數(shù)據(jù)有個標(biāo)記字節(jié)用于表示這個數(shù)組是否收到數(shù)據(jù),收到數(shù)據(jù)是否處理完成。

上述方案是完全可行的,但有缺點:

①緩沖數(shù)據(jù)組數(shù)一定,且有多變量,代碼結(jié)構(gòu)不太清晰。

②接收數(shù)據(jù)長度可能大于數(shù)組大小,也可能小于數(shù)組大小。不靈活,需要接收數(shù)據(jù)很長時容易出錯,且內(nèi)存利用率低。

解決這個問題的好辦法是:環(huán)形緩沖區(qū)。

環(huán)形緩沖區(qū)就是一個帶“頭指針”和“尾指針”的數(shù)組。“頭指針”指向環(huán)形緩沖區(qū)中可讀的數(shù)據(jù),“尾指針”指向環(huán)形緩沖區(qū)中可寫的緩沖空間。通過移動“頭指針”和“尾指針”就可以實現(xiàn)緩沖區(qū)的數(shù)據(jù)讀取和寫入。在通常情況下,應(yīng)用程序讀取環(huán)形緩沖區(qū)的數(shù)據(jù)僅僅會影響“頭指針”,而串口接收數(shù)據(jù)僅僅會影響“尾指針”。當(dāng)串口接收到新的數(shù)組,則將數(shù)組保存到環(huán)形緩沖區(qū)中,同時將“尾指針”加1,以保存下一個數(shù)據(jù);應(yīng)用程序在讀取數(shù)據(jù)時,“頭指針”加1,以讀取下一個數(shù)據(jù)。當(dāng)“尾指針”超過數(shù)組大小,則“尾指針”重新指向數(shù)組的首元素,從而形成“環(huán)形緩沖區(qū)”!,有效數(shù)據(jù)區(qū)域在“頭指針”和“尾指針”之間。如下圖

如上面說的,環(huán)形緩沖區(qū)其實就是一個數(shù)組,將其“剪開”,然后“拉直”后如下圖

環(huán)形緩沖區(qū)的特性

1、先進(jìn)新出。

2、當(dāng)緩沖區(qū)被使用完,且又有新的數(shù)據(jù)需要存儲時,丟掉歷史最久的數(shù)據(jù),保存最新數(shù)據(jù)。

03代碼實現(xiàn)

環(huán)形緩沖區(qū)的實現(xiàn)很簡單,只需要簡單的幾個接口即可。

首先需要創(chuàng)建一個環(huán)形緩沖區(qū)

  1. #define  RINGBUFF_LEN          (500)     //定義最大接收字節(jié)數(shù) 500 
  2. #define  RINGBUFF_OK           1      
  3. #define  RINGBUFF_ERR          0    
  4. typedef struct 
  5.     uint16_t Head;            
  6.     uint16_t Tail; 
  7.     uint16_t Lenght; 
  8.     uint8_t  Ring_data[RINGBUFF_LEN]; 
  9. }RingBuff_t; 
  10. RingBuff_t ringBuff;//創(chuàng)建一個ringBuff的緩沖區(qū) 

當(dāng)我們發(fā)現(xiàn)環(huán)形緩沖區(qū)被“沖爆”時,也就是緩沖區(qū)滿了,但是還有待緩沖的數(shù)據(jù)時,只需要修改RINGBUFF_LEN的宏定義,增大緩沖區(qū)間即可。

環(huán)形緩沖區(qū)的初始化

  1. /** 
  2. * @brief  RingBuff_Init 
  3. * @param  void 
  4. * @return void 
  5. * @note   初始化環(huán)形緩沖區(qū) 
  6. */ 
  7. void RingBuff_Init(void) 
  8.   //初始化相關(guān)信息 
  9.   ringBuff.Head = 0; 
  10.   ringBuff.Tail = 0; 
  11.   ringBuff.Lenght = 0; 

主要是將環(huán)形緩沖區(qū)的頭,尾和長度清零,表示沒有任何數(shù)據(jù)存入。

環(huán)形緩沖區(qū)的寫入

  1. /** 
  2. * @brief  Write_RingBuff 
  3. * @param  uint8_t data 
  4. * @return FLASE:環(huán)形緩沖區(qū)已滿,寫入失敗;TRUE:寫入成功 
  5. * @note   往環(huán)形緩沖區(qū)寫入uint8_t類型的數(shù)據(jù) 
  6. */ 
  7. uint8_t Write_RingBuff(uint8_t data) 
  8.   if(ringBuff.Lenght >= RINGBUFF_LEN) //判斷緩沖區(qū)是否已滿 
  9.   { 
  10.     return RINGBUFF_ERR; 
  11.   } 
  12.   ringBuff.Ring_data[ringBuff.Tail]=data; 
  13.   ringBuff.Tail = (ringBuff.Tail+1)%RINGBUFF_LEN;//防止越界非法訪問 
  14.   ringBuff.Lenght++; 
  15.   return RINGBUFF_OK; 

這個接口是寫入一個字節(jié)到環(huán)形緩沖區(qū)。這里注意:大家可以根據(jù)自己的實際應(yīng)用修改為一次緩沖多個字節(jié)。并且這個做了緩沖區(qū)滿時報錯且防止非法越界的處理,大家可以自行修改為緩沖區(qū)滿時覆蓋最早的數(shù)據(jù)。

環(huán)形緩沖區(qū)的讀取

  1. /** 
  2. * @brief  Read_RingBuff 
  3. * @param  uint8_t *rData,用于保存讀取的數(shù)據(jù) 
  4. * @return FLASE:環(huán)形緩沖區(qū)沒有數(shù)據(jù),讀取失敗;TRUE:讀取成功 
  5. * @note   從環(huán)形緩沖區(qū)讀取一個u8類型的數(shù)據(jù) 
  6. */ 
  7. uint8_t Read_RingBuff(uint8_t *rData) 
  8.   if(ringBuff.Lenght == 0)//判斷非空 
  9.   { 
  10.     return RINGBUFF_ERR; 
  11.   } 
  12.   *rData = ringBuff.Ring_data[ringBuff.Head];//先進(jìn)先出FIFO,從緩沖區(qū)頭出 
  13.   ringBuff.Head = (ringBuff.Head+1)%RINGBUFF_LEN;//防止越界非法訪問 
  14.   ringBuff.Lenght--; 
  15.   return RINGBUFF_OK; 

讀取的話也很簡單,同樣是讀取一個字節(jié),大家可以自行修改為讀取多個字節(jié)。

04驗證

光說不練假把式,下面我們就來驗證上面的代碼可行性。

串口中斷函數(shù)中緩沖數(shù)據(jù)

  1. void USART1_IRQHandler(void) 
  2.   if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE)) 
  3.   { 
  4.     Write_RingBuff(USART_ReceiveData(USART1)); 
  5.     USART_ClearFlag(USART1, USART_FLAG_RXNE); 
  6.   } 

在主循環(huán)中,讀取緩沖區(qū)的數(shù)據(jù),然后發(fā)送出去,因為是簡單的demo,添加了延時模擬CPU處理其他任務(wù)。

  1. while (1) 
  2.   { 
  3.     if(Read_RingBuff(&data))            //從環(huán)形緩沖區(qū)中讀取數(shù)據(jù) 
  4.     { 
  5.       USART_SendData(USART1, data); 
  6.     } 
  7.     SysCtlDelay(1*(SystemCoreClock/3000)); 
  8.   } 

驗證,間隔100ms發(fā)送數(shù)據(jù)。

結(jié)果顯示沒有出現(xiàn)丟包問題。如果你的應(yīng)用場景串口通信速率快,數(shù)據(jù)量大或處理速度慢導(dǎo)致丟包,建議增大RINGBUFF_LEN的宏定義,增大緩沖區(qū)間即可。

本文轉(zhuǎn)載自微信公眾號「 知曉編程」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系 知曉編程公眾號。

 

責(zé)任編輯:姜華 來源: 知曉編程
點贊
收藏

51CTO技術(shù)棧公眾號