新聞中心

        EEPW首頁 > 嵌入式系統(tǒng) > 設計應用 > STM32串口DMA方式接收數據

        STM32串口DMA方式接收數據

        作者: 時間:2016-11-19 來源:網絡 收藏
        一直以來都為串口接收數據所困擾:
        1:如果用接收中斷的話,每接收1byte就得中斷一次。這樣太消耗CPU資源!
        2:如果用DMA方式接收數據,那么如何確定接收數據的長度又不好確定了。(比如GPRS模塊AT命令的接收?。?br />3:DMA方式接收+定時器的超時中斷。這樣處理也比較復雜,需要開定時器,關定時器。。。。個人不喜歡?。ˋTMEL的ARM系列的串口倒是有硬件超時中斷可以直接使用。我現在用AT91SAM7系列處理GPRS的AT命令就采用這種方式,挺好用。但是STM32就沒有了,需要自己加定時器,還要硬件處理:RXD連接定時器的一個觸發(fā)引腳!)。

        所以之前用STM32接收串口數據都是采用接收中斷,然后寫入一個FIFO隊列。然后在主函數里面去查詢隊列緩沖中是否有數據需要處理。但是這樣的話,串口中斷服務函數始終是很大的硬件開銷。比如我現在用串口下載STM32的升級固件的時候,數據量較大。

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

        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);

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

        B:關于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ū)結束地址
        INT8U buffer[1]; //實際長度由初始化分配內存!(memloc的時候確定)
        }FIFOTYPE;


        C:關于FIFO隊列的初始化,具體配置
        //
        //函數:FIFO_Init
        //參數:FIFO類型的指針地址,隊列大小
        //返回:>=0初始化成功
        //描述:初始化FIFO隊列
        //
        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)
        {
        //已經在堆里面申請了地址
        return FIFO_ERROR_MEM;
        }
        (*fifo)->size = fifosize;
        (*fifo)->staraddr = (INT32U)(&(*fifo)->buffer[0]); //記錄FIFO緩沖區(qū)起始地址
        (*fifo)->endaddr = (INT32U)(&(*fifo)->buffer[fifosize-1]); //記錄FIFO緩沖區(qū)結束地址
        (*fifo)->front = (*fifo)->staraddr; //FIFO下一讀取數據地址
        memset((*fifo)->buffer,0,(*fifo)->size); //清除緩沖區(qū)里面的數據,可省略

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

        return FIFO_OK;
        }

        D:清空隊列緩沖區(qū)函數
        //
        //函數:FIFO_Clear
        //參數:無
        //返回:無
        //描述:清空FIFO隊列
        //
        Int32S FIFO_Clear(FIFOTYPE *fifo)
        {
        volatile INT32U da;
        if(fifo == NULL)
        return FIFO_ERROR_PARAM;
        fifo->front = fifo->staraddr; //將下一讀取地址設置為FIFO緩沖開始
        DMA1_Channel5->CCR &= ~DMA_CCR5_EN;
        DMA1_Channel5->CMAR = fifo->staraddr; //重新配置DMA地址
        DMA1_Channel5->CNDTR = fifo->size; //重新配置DMA傳輸數據量
        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ū),這個跟標準的環(huán)形隊列基本沒區(qū)別
        //
        //函數:FIFO_Read
        //參數:隊列指針,1byte數據指針
        //返回:>=0讀取成功
        //描述:從FIFO隊列中讀出1byte數據
        //
        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ū)的數據量
        //
        //函數:FIFO_Status
        //參數:隊列指針
        //返回:>0隊列中有未讀出數據
        //描述:獲取FIFO隊列狀態(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傳輸地址寄存器不會隨傳輸數據量的變化而真正的指向下一個存儲位置(AT91SAM就是總是指向下一個存儲地址的)。所以我需要根據傳輸數量寄存器DMA_CNDTR來推算下一傳輸位置寄存器!
        2:需要考慮環(huán)形隊列寫入指針已經重新回到緩沖區(qū)開頭了,而讀取指針還在緩沖區(qū)尾部的情況!


        評論


        技術專區(qū)

        關閉
        主站蜘蛛池模板: 林州市| 崇州市| 姚安县| 泰安市| 平安县| 蒙阴县| 北京市| 繁昌县| 射洪县| 建水县| 广东省| 永定县| 浦江县| 屏南县| 东阿县| 南京市| 涟水县| 泰州市| 南投市| 明星| 色达县| 南安市| 外汇| 安乡县| 漠河县| 安塞县| 凌源市| 封开县| 城市| 铜山县| 安丘市| 临泽县| 文安县| 曲阜市| 定边县| 安龙县| 望都县| 阿拉善盟| 宿州市| 黎川县| 湖州市|