加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專(zhuān)業(yè)用戶(hù)
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 一、 什么是 DMA
    • 二、串口的問(wèn)題
    • 三、注意事項(xiàng)
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

解放CPU,我用DMA來(lái)做串口緩沖

2023/12/15
2831
閱讀需 12 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

我在我的 BMS 系統(tǒng)中使用了隔離的串口來(lái)進(jìn)行通信,BMS 的主控芯片我選擇了芯源的 CW32L031,這是一款 TSSOP20 封裝的 M0 內(nèi)核的低功耗單片機(jī),資源不多,主頻也不是很高,但是功耗低確是我最重要的需求。

為了降低功耗,我將主頻設(shè)置的比較低,因此在走串口通信的時(shí)候,如果使用串口的中斷來(lái)進(jìn)行發(fā)送和接收,那么每接收一個(gè)字符就會(huì)產(chǎn)生一個(gè)中斷,這樣頻繁的中斷肯定是 CPU 不厭其煩的,于是我選擇使用 DMA 充當(dāng)一次串口的緩沖助手。

一、 什么是 DMA

DMA(Direct Memory Access,直接存儲(chǔ)器訪問(wèn))提供在外設(shè)與內(nèi)存、存儲(chǔ)器和存儲(chǔ)器、外設(shè)與外設(shè)之間的高速數(shù)據(jù)傳輸使用。它允許不同速度的硬件裝置來(lái)溝通,而不需要依賴(lài)于 CPU ,在這個(gè)時(shí)間中,CPU 對(duì)于內(nèi)存的工作來(lái)說(shuō)就無(wú)法使用。

其實(shí),我們可以簡(jiǎn)化一下對(duì) DMA 功能的描述,DMA 的主要工作就是搬磚,我只需要設(shè)置好幾個(gè)簡(jiǎn)單的參數(shù),DMA 就可以幫我們把數(shù)據(jù)從一個(gè)地址搬運(yùn)到另一個(gè)地址,就是這么簡(jiǎn)單。

這里有三種情況,分別是內(nèi)存到內(nèi)存,內(nèi)存到外設(shè),外設(shè)到內(nèi)存。

要想讓這個(gè)這個(gè)蘑菇頭好好的幫我們搬磚,首先我們需要設(shè)置以下幾個(gè)參數(shù):

源地址(從哪里取磚塊)目的地址(把磚塊放到哪里)傳輸帶寬(一次搬幾塊)源地址是否增加(磚塊是排列好的,還是一塊接著一塊吐出來(lái)的)目的地址是否增加(磚塊是排列碼好,還是一塊一塊的丟進(jìn)一個(gè)洞里)觸發(fā)源(誰(shuí)發(fā)指令開(kāi)始搬)一次性傳輸量(一共搬多少次)

下面,我們就從一個(gè)工地的場(chǎng)景來(lái)了解一下,蘑菇頭是如何執(zhí)行任務(wù)來(lái)減輕總工的工作負(fù)荷的。首先,對(duì)于蘑菇頭來(lái)說(shuō),他的主要任務(wù)就是把磚從一個(gè)地方搬運(yùn)到另一個(gè)地方,只要總工開(kāi)始給他講好怎么搬,蘑菇頭就能摒棄一切雜念,任勞任怨的快樂(lè)搬磚。最簡(jiǎn)單的是內(nèi)存搬運(yùn),也就是把磚從一塊空地搬運(yùn)到另一塊空地,這時(shí)候我們只需要跟蘑菇頭說(shuō):蘑菇頭,你過(guò)來(lái),你今天的任務(wù)是把 A 區(qū)域(源地址)的500 塊(傳輸量)磚搬運(yùn)到 B 區(qū)域(目的地址),你每次搬 2 塊(傳輸帶寬),同時(shí)要保證他們?cè)?AB 區(qū)域的碼放是相同的(源和目的地址同步增加)。現(xiàn)在我只要一喊:“開(kāi)始”(軟件觸發(fā)),你就馬上按我說(shuō)的給我搬磚,我會(huì)先去忙點(diǎn)別的事。

這不,總工就可以有喝茶的時(shí)間了嘛!似乎只是這么倒騰磚的意義不大,我們假設(shè)現(xiàn)在有一個(gè)機(jī)器來(lái)負(fù)責(zé)把磚頭從車(chē)上卸下來(lái),然后我們讓蘑菇頭把機(jī)器卸下來(lái)的磚整整齊齊的碼放在 B 區(qū)域。這個(gè)時(shí)候我們可以告訴蘑菇頭,要從機(jī)器出口那里搬(源地址不增加),這次因?yàn)闄C(jī)器一次只能吐出一塊磚來(lái),所以你一次只搬一塊(傳輸帶寬),然后按順序碼放到 B 區(qū)域。今天我也不陪你了,機(jī)器出磚的時(shí)候會(huì)鳴笛(硬件觸發(fā)),你聽(tīng)到鳴笛就開(kāi)始搬就可以了。哦,對(duì)了,你搬完 500 塊排滿(mǎn)一層后,就重新開(kāi)始在上面再排一層(循環(huán)模式),我回來(lái)之前你不許停。

二、串口的問(wèn)題

上面我們了解了 DMA 的工作過(guò)程,然后我們就用它來(lái)幫我們解決一下我們的串口問(wèn)題。假設(shè)我們讓蘑菇頭幫我們把串口接收的數(shù)據(jù)先搬運(yùn)到內(nèi)存中的一個(gè)緩沖區(qū),比如這個(gè)緩沖區(qū)是 64 個(gè)字節(jié),那么我們就可以在主循環(huán)中區(qū)查詢(xún)這個(gè)緩沖區(qū)而不用每個(gè)字節(jié)都進(jìn)入中斷處理。即便我們的 DMA 沒(méi)有循環(huán)模式,我們也只需要在 DMA 傳輸完成中斷中區(qū)重新給蘑菇頭發(fā)一遍指令,這樣也可以把系統(tǒng)的中斷頻率降低 64 倍。下面代碼是我在 CW32L031 上實(shí)現(xiàn)的DMA 緩沖接收串口數(shù)據(jù)的代碼。DMA 的初始化:

static void dma_init(void){    DMA_InitTypeDef DMA_InitStructure = {0};
    RCC_AHBPeriphClk_Enable( RCC_AHB_PERIPH_DMA , ENABLE);    //Open DMA   Clk    //初始化DMA RX    DMA_InitStructure.DMA_Mode = DMA_MODE_BLOCK;   //Block 為可被打斷的dma傳輸,bulk為不可被打斷傳輸    DMA_InitStructure.DMA_TransferWidth = DMA_TRANSFER_WIDTH_8BIT; // dma的傳輸帶寬,8bit    DMA_InitStructure.DMA_SrcInc = DMA_SrcAddress_Fix;  //   DMA 源地不變,接受寄存器    DMA_InitStructure.DMA_DstInc = DMA_DstAddress_Increase;  //目的地址增加,緩存    DMA_InitStructure.TrigMode = DMA_HardTrig;               //硬件觸發(fā)模式    DMA_InitStructure.HardTrigSource = USART_RX_SRC;         //UART3作為觸發(fā)源    DMA_InitStructure.DMA_TransferCnt = DMA_BUFFSIZE;    DMA_InitStructure.DMA_SrcAddress = (uint32_t)&USARTX->RDR;    DMA_InitStructure.DMA_DstAddress = (uint32_t)TxRxBuffer;    DMA_Init(USART_RX_DMA, &DMA_InitStructure);    DMA_Cmd(USART_RX_DMA, ENABLE);    DMA_ITConfig(USART_RX_DMA, DMA_IT_TC, ENABLE);  //開(kāi)啟 DMA 的傳輸完成中斷}

DMA 中斷中重置 DMA 參數(shù):

void DMACH1_IRQHandler(void){    /* USER CODE BEGIN */if(DMA_GetITStatus(DMA_IT_TC1) ==  SET){DMA_ClearITPendingBit(DMA_IT_TC1);USART_RX_DMA->CNT_f.CNT = DMA_BUFFSIZE;USART_RX_DMA->DSTADDR_f.DSTADDR = (uint32_t)TxRxBuffer;USART_RX_DMA->CNT_f.REPEAT = 1;USART_RX_DMA->CSR_f.EN = 1;}

    /* USER CODE END */}

主循環(huán)中進(jìn)行查詢(xún)處理:

void uart_check_recv(void){    s32 DAMCnt = 0;    s32 MaxDataLen = DMA_BUFFSIZE;  
    //判斷是否正在接受    if(rx_fifo.bRecving)        return;
    rx_fifo.bRecving = 1; // 加鎖,防止多線程調(diào)用
    if(USART_RX_DMA->CNT_f.CNT >= DMA_BUFFSIZE) //如果 DMA 接收為空,退出, 初始值為最大,接收遞減    {        rx_fifo.bRecving = 0;        return;    }
    DAMCnt = DMA_BUFFSIZE - (USART_RX_DMA->CNT_f.CNT); // 這是 DMA 接收到的數(shù)據(jù)長(zhǎng)度    //LOG("dma  recv %d rn",DAMCnt);    while( rx_fifo.rx_ptr != DAMCnt && MaxDataLen > 0) //緩存中還有數(shù)據(jù)就循環(huán)    {        //LOG("(%d)",rx_fifo.rx_ptr);        parse_data(TxRxBuffer[rx_fifo.rx_ptr]);
        rx_fifo.rx_ptr++;        if( rx_fifo.rx_ptr >= DMA_BUFFSIZE ) //指針循環(huán)        {            //LOG("DMA rn");            rx_fifo.rx_ptr = 0;        }        DAMCnt = DMA_BUFFSIZE - (USART_RX_DMA->CNT_f.CNT); //更新一下緩沖區(qū)剩余待處理的字節(jié)數(shù)        MaxDataLen--;    }    rx_fifo.bRecving = 0;   //解鎖}

三、注意事項(xiàng)

在 CW32L031 平臺(tái)上,其 DMA 設(shè)計(jì)的比較簡(jiǎn)單,他沒(méi)有循環(huán)模式,因此我們需要開(kāi)啟 DMA 的傳輸完成中斷,然后在中斷中重新設(shè)定目的地址和傳輸量,這里一定要注意設(shè)置完整,不然程序很容易跑飛,因?yàn)?蘑菇頭的頭腦還是比較簡(jiǎn)單的,如果地址不重新設(shè)定,那么他會(huì)一直往后累加,把磚搬的工地上到處都是,等總工喝完茶回來(lái)就徹底崩潰了。

另外,蘑菇頭搬磚的時(shí)候,走的路也是工地上的路,因此還是有很大概率會(huì)和總工在路線上起沖突的,因此他和總工也并不是完全不相干了,如果蘑菇頭一次性搬磚數(shù)量太多,也會(huì)堵住工地上的道路,從而阻礙到總工去上個(gè)廁所啥的也不一定哦。

你學(xué)廢了嗎?

推薦器件

更多器件
器件型號(hào) 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊(cè) ECAD模型 風(fēng)險(xiǎn)等級(jí) 參考價(jià)格 更多信息
STM32H743IIT6 1 STMicroelectronics High-performance and DSP with DP-FPU, Arm Cortex-M7 MCU with 2MBytes of Flash memory, 1MB RAM, 480 MHz CPU, Art Accelerator, L1 cache, external memory interface, large set of peripherals

ECAD模型

下載ECAD模型
$17.38 查看
ATMEGA328PB-AUR 1 Atmel Corporation RISC Microcontroller, CMOS,

ECAD模型

下載ECAD模型
$2.65 查看
PIC32MX795F512L-80I/BG 1 Microchip Technology Inc 32-BIT, FLASH, 80 MHz, RISC MICROCONTROLLER, PBGA121, 10 X 10 MM, 1.10 MM HEIGHT, LEAD FREE, PLASTIC, XBGA-121

ECAD模型

下載ECAD模型
$23.36 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

多年硬件從業(yè)經(jīng)驗(yàn),專(zhuān)注分享從研發(fā)到供應(yīng)鏈,再到精益制造過(guò)程中的經(jīng)驗(yàn)和感悟!