<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > STM32串口DMA方式接收數(shù)據(jù)

          STM32串口DMA方式接收數(shù)據(jù)

          作者: 時間:2016-11-19 來源:網(wǎng)絡(luò) 收藏
          一直以來都為串口接收數(shù)據(jù)所困擾:
          1:如果用接收中斷的話,每接收1byte就得中斷一次。這樣太消耗CPU資源!
          2:如果用DMA方式接收數(shù)據(jù),那么如何確定接收數(shù)據(jù)的長度又不好確定了。(比如GPRS模塊AT命令的接收!)
          3:DMA方式接收+定時器的超時中斷。這樣處理也比較復(fù)雜,需要開定時器,關(guān)定時器。。。。個人不喜歡?。ˋTMEL的ARM系列的串口倒是有硬件超時中斷可以直接使用。我現(xiàn)在用AT91SAM7系列處理GPRS的AT命令就采用這種方式,挺好用。但是STM32就沒有了,需要自己加定時器,還要硬件處理:RXD連接定時器的一個觸發(fā)引腳?。?br />
          所以之前用STM32接收串口數(shù)據(jù)都是采用接收中斷,然后寫入一個FIFO隊(duì)列。然后在主函數(shù)里面去查詢隊(duì)列緩沖中是否有數(shù)據(jù)需要處理。但是這樣的話,串口中斷服務(wù)函數(shù)始終是很大的硬件開銷。比如我現(xiàn)在用串口下載STM32的升級固件的時候,數(shù)據(jù)量較大。

          廢話完畢,今天突然腦子發(fā)熱想要把DMA和環(huán)形的FIFO隊(duì)列結(jié)合一下使用。把想法跟同事交流一下,覺得有可行性!馬上動手實(shí)驗(yàn)。經(jīng)過半天調(diào)試,結(jié)果令人滿意。
          說說我的思路(本人表達(dá)能力有限,描述不清楚的希望大家跟帖):關(guān)在在于讓DMA來實(shí)現(xiàn)“環(huán)形隊(duì)列中往緩沖區(qū)寫入1byte”的功能!剩下的讀取隊(duì)列就跟普通環(huán)形隊(duì)列沒多大區(qū)別了。這樣我們的程序中擁有了一個不占用CPU資源的“環(huán)形隊(duì)列”后,我們就不用擔(dān)心CPU頻繁中斷,我們只需要在適當(dāng)?shù)臅r間讀取隊(duì)列中的數(shù)據(jù)然后慢慢分析處理數(shù)據(jù)!
          A:串口初始化配置串口為DMA方式接收數(shù)據(jù)。具體配置請看:

          DMA1_Channel5->CCR = DMA_CCR5_PL //通道優(yōu)先級最高
          | DMA_CCR5_MINC //MEM地址增量使能
          | DMA_CCR5_CIRC //接收緩沖區(qū)循環(huán)模式
          | DMA_CCR5_TCIE //傳輸完成中斷
          ;
          DMA1->IFCR |= 0x000F0000;
          DMA1_Channel5->CPAR = USART1_BASE + 4;
          // Enable the DMA1_CH5 Interrupt
          NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQChannel;
          NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
          NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
          NVIC_Init(&NVIC_InitStructure);

          關(guān)鍵,開啟DMA循環(huán)模式,這樣接收完之后會自動回到FIFO緩沖區(qū)開頭地方,這樣能省不少事情。
          當(dāng)然,考慮到可能把緩沖區(qū)撐爆的情況,所以開啟通道傳輸完成標(biāo)志位,在傳輸完成中斷中查詢一下隊(duì)列中有多少數(shù)據(jù)沒有讀取出來,如果太多數(shù)據(jù)沒有讀取,那么在中斷里面處理讀取FIFO數(shù)據(jù)并做相應(yīng)處理!

          B:關(guān)于FIFO的一些聲明:
          #define FIFO_OK 0
          #define FIFO_ERROR_PARAM -1
          #define FIFO_ERROR_MEM -2
          #define FIFO_ERROR_FULL -3
          #define FIFO_ERROR_EMPTY -4
          #define FIFO_ERROR_BUSY -5

          typedef struct _FIFO_TYPE_
          {
          INT32U size; //FIFO緩沖區(qū)大小
          INT32U front; //FIFO下一讀取位置
          INT32U staraddr; //FIFO緩沖區(qū)起始地址
          INT32U endaddr; //FIFO緩沖區(qū)結(jié)束地址
          INT8U buffer[1]; //實(shí)際長度由初始化分配內(nèi)存!(memloc的時候確定)
          }FIFOTYPE;


          C:關(guān)于FIFO隊(duì)列的初始化,具體配置
          //
          //函數(shù):FIFO_Init
          //參數(shù):FIFO類型的指針地址,隊(duì)列大小
          //返回:>=0初始化成功
          //描述:初始化FIFO隊(duì)列
          //
          Int32S FIFO_Init(FIFOTYPE * *fifo,INT32U fifosize)
          {
          volatile INT32U da;
          if(fifo==NULL || fifosize == 0)
          {
          return FIFO_ERROR_PARAM;
          }
          (*fifo) = malloc(16+fifosize);
          if((*fifo) == NULL)
          {
          //已經(jīng)在堆里面申請了地址
          return FIFO_ERROR_MEM;
          }
          (*fifo)->size = fifosize;
          (*fifo)->staraddr = (INT32U)(&(*fifo)->buffer[0]); //記錄FIFO緩沖區(qū)起始地址
          (*fifo)->endaddr = (INT32U)(&(*fifo)->buffer[fifosize-1]); //記錄FIFO緩沖區(qū)結(jié)束地址
          (*fifo)->front = (*fifo)->staraddr; //FIFO下一讀取數(shù)據(jù)地址
          memset((*fifo)->buffer,0,(*fifo)->size); //清除緩沖區(qū)里面的數(shù)據(jù),可省略

          DMA1_Channel5->CCR &= ~DMA_CCR5_EN;
          DMA1_Channel5->CMAR = (INT32U)(*fifo)->staraddr; //配置DMA傳輸?shù)刂?br /> DMA1_Channel5->CNDTR = (*fifo)->size; //配置DMA傳輸數(shù)據(jù)量
          da = USART1->DR;
          da = da;
          DMA1->IFCR |= 0x000F0000;
          DMA1_Channel5->CCR |= DMA_CCR5_EN;

          return FIFO_OK;
          }

          D:清空隊(duì)列緩沖區(qū)函數(shù)
          //
          //函數(shù):FIFO_Clear
          //參數(shù):無
          //返回:無
          //描述:清空FIFO隊(duì)列
          //
          Int32S FIFO_Clear(FIFOTYPE *fifo)
          {
          volatile INT32U da;
          if(fifo == NULL)
          return FIFO_ERROR_PARAM;
          fifo->front = fifo->staraddr; //將下一讀取地址設(shè)置為FIFO緩沖開始
          DMA1_Channel5->CCR &= ~DMA_CCR5_EN;
          DMA1_Channel5->CMAR = fifo->staraddr; //重新配置DMA地址
          DMA1_Channel5->CNDTR = fifo->size; //重新配置DMA傳輸數(shù)據(jù)量
          memset(fifo->buffer,0,fifo->size);
          da = USART1->DR;
          da = da;
          DMA1->IFCR |= 0x000F0000;
          DMA1_Channel5->CCR |= DMA_CCR5_EN;

          return FIFO_OK;
          }

          E:讀取FIFO緩沖區(qū),這個跟標(biāo)準(zhǔn)的環(huán)形隊(duì)列基本沒區(qū)別
          //
          //函數(shù):FIFO_Read
          //參數(shù):隊(duì)列指針,1byte數(shù)據(jù)指針
          //返回:>=0讀取成功
          //描述:從FIFO隊(duì)列中讀出1byte數(shù)據(jù)
          //
          Int32S FIFO_Read(FIFOTYPE *fifo,INT8U *data)
          {
          if(fifo == NULL )
          return FIFO_ERROR_PARAM;
          if(FIFO_Status(fifo)==0)
          {
          return FIFO_ERROR_EMPTY;
          }
          *data = (INT8U)(*((INT8U *)(fifo->front)));
          if(fifo->front == fifo->endaddr)
          {
          fifo->front = fifo->staraddr;
          }
          else
          {
          fifo->front++;
          }
          return FIFO_OK;
          }

          F:獲取緩沖區(qū)的數(shù)據(jù)量
          //
          //函數(shù):FIFO_Status
          //參數(shù):隊(duì)列指針
          //返回:>0隊(duì)列中有未讀出數(shù)據(jù)
          //描述:獲取FIFO隊(duì)列狀態(tài)
          //
          INT32S FIFO_Status(FIFOTYPE *fifo)
          {
          INT32S res;
          INT32S nextsave = (INT32S)fifo->endaddr + 1 - (INT32S)DMA1_Channel5->CNDTR;
          res = nextsave- (INT32S)(fifo->front);
          if(res < 0)
          {
          res = ( (INT32S)(fifo->endaddr)+1 - (INT32S)(fifo->front) ) + (nextsave - (INT32S)fifo->staraddr);
          }
          return res;
          }
          說明:
          1:STM32的DMA_CMAR傳輸?shù)刂芳拇嫫鞑粫S傳輸數(shù)據(jù)量的變化而真正的指向下一個存儲位置(AT91SAM就是總是指向下一個存儲地址的)。所以我需要根據(jù)傳輸數(shù)量寄存器DMA_CNDTR來推算下一傳輸位置寄存器!
          2:需要考慮環(huán)形隊(duì)列寫入指針已經(jīng)重新回到緩沖區(qū)開頭了,而讀取指針還在緩沖區(qū)尾部的情況!


          評論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();