娇小w搡bbbb搡bbb,《第一次の人妻》,中国成熟妇女毛茸茸,边啃奶头边躁狠狠躁视频免费观看

歷史上的今天

今天是:2025年01月07日(星期二)

正在發生

2019年01月07日 | STM32之串口DMA接收不定長數據

發布者:EnigmaticCharm 來源: eefocus關鍵字:STM32  串口DMA  不定長數據 手機看文章 掃描二維碼
隨時隨地手機看文章

引言

在使用stm32或者其他單片機的時候,會經常使用到串口通訊,那么如何有效地接收數據呢?假如這段數據是不定長的有如何高效接收呢?


同學A:數據來了就會進入串口中斷,在中斷中讀取數據就行了!


中斷就是打斷程序正常運行,怎么能保證高效呢?經常把主程序打斷,主程序還要不要運行了?


同學B:串口可以配置成用DMA的方式接收數據,等接收完畢就可以去讀取了!


這個同學是對的,我們可以使用DMA去接收數據,不過DMA需要定長才能產生接收中斷,如何接收不定長的數據呢?


DMA簡介

題外話:其實,上面的問題是很有必要思考一下的,不斷思考,才能進步。


什么是DMA

DMA:全稱Direct Memory Access,即直接存儲器訪問


DMA 傳輸將數據從一個地址空間復制到另外一個地址空間。CPU只需初始化DMA即可,傳輸動作本身是由 DMA 控制器來實現和完成。典型的例子就是移動一個外部內存的區塊到芯片內部更快的內存區。這樣的操作并沒有讓處理器參與處理,CPU可以干其他事情,當DMA傳輸完成的時候產生一個中斷,告訴CPU我已經完成了,然后CPU知道了就可以去處理數據了,這樣子提高了CPU的利用率,因為CPU是大腦,主要做數據運算的工作,而不是去搬運數據。DMA 傳輸對于高效能嵌入式系統算法和網絡是很重要的。


在STM32的DMA資源

STM32F1系列的MCU有兩個DMA控制器(DMA2只存在于大容量產品中),DMA1有7個通道,DMA2有5個通道,每個通道專門用來管理來自于一個或者多個外設對存儲器的訪問請求。還有一個仲裁器來協調各個DMA請求的優先權。


STM32F1
STM32F1


而STM32F4/F7/H7系列的MCU有兩個DMA控制器總共有16個數據流(每個DMA控制器8個),每一個DMA控制器都用于管理一個或多個外設的存儲器訪問請求。每個數據流總共可以有多達8個通道(或稱請求)。每個通道都有一個仲裁器,用于處理 DMA 請求間的優先級。 


STM32F4 
STM32F4


DMA接收數據

DMA在接收數據的時候,串口接收DMA在初始化的時候就處于開啟狀態,一直等待數據的到來,在軟件上無需做任何事情,只要在初始化配置的時候設置好配置就可以了。等到接收到數據的時候,告訴CPU去處理即可。


判斷數據接收完成

那么問題來了,怎么知道數據是否接收完成呢?


其實,有很多方法: 

- 對于定長的數據,只需要判斷一下數據的接收個數,就知道是否接收完成,這個很簡單,暫不討論。 

- 對于不定長的數據,其實也有好幾種方法,麻煩的我肯定不會介紹,有興趣做復雜工作的同學可以在網上看看別人怎么做,下面這種方法是最簡單的,充分利用了stm32的串口資源,效率也是非常之高。


DMA+串口空閑中斷


這兩個資源配合,簡直就是天衣無縫啊,無論接收什么不定長的數據,管你數據有多少,來一個我就收一個,就像廣東人吃“山竹”,來一個吃一個~(最近風好大,我好怕)。


可能很多人在學習stm32的時候,都不知道idle是啥東西,先看看stm32串口的狀態寄存器: 


idle
idle說明

當我們檢測到觸發了串口總線空閑中斷的時候,我們就知道這一波數據傳輸完成了,然后我們就能得到這些數據,去進行處理即可。這種方法是最簡單的,根本不需要我們做多的處理,只需要配置好,串口就等著數據的到來,dma也是處于工作狀態的,來一個數據就自動搬運一個數據。


接收完數據時處理

串口接收完數據是要處理的,那么處理的步驟是怎么樣呢?


暫時關閉串口接收DMA通道,有兩個原因:1.防止后面又有數據接收到,產生干擾,因為此時的數據還未處理。2.DMA需要重新配置。


清DMA標志位。


從DMA寄存器中獲取接收到的數據字節數(可有可無)。


重新設置DMA下次要接收的數據字節數,注意,數據傳輸數量范圍為0至65535。這個寄存器只能在通道不工作(DMA_CCRx的EN=0)時寫入。通道開啟后該寄存器變為只讀,指示剩余的待傳輸字節數目。寄存器內容在每次DMA傳輸后遞減。數據傳輸結束后,寄存器的內容或者變為0;或者當該通道配置為自動重加載模式時,寄存器的內容將被自動重新加載為之前配置時的數值。當寄存器的內容為0時,無論通道是否開啟,都不會發生任何數據傳輸。


給出信號量,發送接收到新數據標志,供前臺程序查詢。


開啟DMA通道,等待下一次的數據接收,注意,對DMA的相關寄存器配置寫入,如重置DMA接收數據長度,必須要在關閉DMA的條件進行,否則操作無效。


注意事項


STM32的IDLE的中斷在串口無數據接收的情況下,是不會一直產生的,產生的條件是這樣的,當清除IDLE標志位后,必須有接收到第一個數據后,才開始觸發,一斷接收的數據斷流,沒有接收到數據,即產生IDLE中斷。如果中斷發送數據幀的速率很快,MCU來不及處理此次接收到的數據,中斷又發來數據的話,這里不能開啟,否則數據會被覆蓋。有兩種方式解決:


在重新開啟接收DMA通道之前,將Rx_Buf緩沖區里面的數據復制到另外一個數組中,然后再開啟DMA,然后馬上處理復制出來的數據。


建立雙緩沖,重新配置DMA_MemoryBaseAddr的緩沖區地址,那么下次接收到的數據就會保存到新的緩沖區中,不至于被覆蓋。


程序實現


實驗效果: 

當外部給單片機發送數 據的時候,假設這幀數據長度是1000個字節,那么在單片機接收到一個字節的時候并不會產生串口中斷,只是DMA在背后默默地把數據搬運到你指定的緩沖區里面。當整幀數據發送完畢之后串口才會產生一次中斷,此時可以利用DMA_GetCurrDataCounter()函數計算出本次的數據接受長度,從而進行數據處理。


串口的配置 

很簡單,基本與使用串口的時候一致,只不過一般我們是打開接收緩沖區非空中斷,而現在是打開空閑中斷——USART_ITConfig(DEBUG_USARTx, USART_IT_IDLE, ENABLE);。


/**

  * @brief  USART GPIO 配置,工作參數配置

  * @param  無

  * @retval 無

  */

void USART_Config(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;

    USART_InitTypeDef USART_InitStructure;


    // 打開串口GPIO的時鐘

    DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);


    // 打開串口外設的時鐘

    DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);


    // 將USART Tx的GPIO配置為推挽復用模式

    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);


  // 將USART Rx的GPIO配置為浮空輸入模式

    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

    GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);


    // 配置串口的工作參數

    // 配置波特率

    USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;

    // 配置 針數據字長

    USART_InitStructure.USART_WordLength = USART_WordLength_8b;

    // 配置停止位

    USART_InitStructure.USART_StopBits = USART_StopBits_1;

    // 配置校驗位

    USART_InitStructure.USART_Parity = USART_Parity_No ;

    // 配置硬件流控制

    USART_InitStructure.USART_HardwareFlowControl = 

    USART_HardwareFlowControl_None;

    // 配置工作模式,收發一起

    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

    // 完成串口的初始化配置

    USART_Init(DEBUG_USARTx, &USART_InitStructure);

    // 串口中斷優先級配置

    NVIC_Configuration();


#if USE_USART_DMA_RX 

    // 開啟 串口空閑IDEL 中斷

    USART_ITConfig(DEBUG_USARTx, USART_IT_IDLE, ENABLE);  

  // 開啟串口DMA接收

    USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Rx, ENABLE); 

    /* 使能串口DMA */

    USARTx_DMA_Rx_Config();

#else

    // 使能串口接收中斷

    USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);    

#endif


#if USE_USART_DMA_TX 

    // 開啟串口DMA發送

//  USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE); 

    USARTx_DMA_Tx_Config();

#endif


    // 使能串口

    USART_Cmd(DEBUG_USARTx, ENABLE);        

}


串口DMA配置


把DMA配置完成,就可以直接打開DMA了,讓它處于工作狀態,當有數據的時候就能直接搬運了。


#if USE_USART_DMA_RX 


static void USARTx_DMA_Rx_Config(void)

{

    DMA_InitTypeDef DMA_InitStructure;


    // 開啟DMA時鐘

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    // 設置DMA源地址:串口數據寄存器地址*/

    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART_DR_ADDRESS;

    // 內存地址(要傳輸的變量的指針)

    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Usart_Rx_Buf;

    // 方向:從內存到外設    

    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

    // 傳輸大小 

    DMA_InitStructure.DMA_BufferSize = USART_RX_BUFF_SIZE;

    // 外設地址不增       

    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

    // 內存地址自增

    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

    // 外設數據單位   

    DMA_InitStructure.DMA_PeripheralDataSize = 

    DMA_PeripheralDataSize_Byte;

    // 內存數據單位

    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;  

    // DMA模式,一次或者循環模式

    //DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;

    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; 

    // 優先級:中    

    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; 

    // 禁止內存到內存的傳輸

    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

    // 配置DMA通道         

    DMA_Init(USART_RX_DMA_CHANNEL, &DMA_InitStructure);     

    // 清除DMA所有標志

    DMA_ClearFlag(DMA1_FLAG_TC5);

    DMA_ITConfig(USART_RX_DMA_CHANNEL, DMA_IT_TE, ENABLE);

    // 使能DMA

    DMA_Cmd (USART_RX_DMA_CHANNEL,ENABLE);

}

#endif


接收完數據處理


因為接收完數據之后,會產生一個idle中斷,也就是空閑中斷,那么我們就可以在中斷服務函數中知道已經接收完了,就可以處理數據了,但是中斷服務函數的上下文環境是中斷,所以,盡量是快進快出,一般在中斷中將一些標志置位,供前臺查詢。在中斷中先判斷我們的產生在中斷的類型是不是idle中斷,如果是則進行下一步,否則就無需理會。


/**

  ******************************************************************

  * @brief   串口中斷服務函數

  * @author  jiejie

  * @version V1.0

  * @date    2018-xx-xx

  ******************************************************************

  */ 

void DEBUG_USART_IRQHandler(void)

{

#if USE_USART_DMA_RX

    /* 使用串口DMA */

    if(USART_GetITStatus(DEBUG_USARTx,USART_IT_IDLE)!=RESET)

    {       

        /* 接收數據 */

        Receive_DataPack();

        // 清除空閑中斷標志位

        USART_ReceiveData( DEBUG_USARTx );

    }   

#else

  /* 接收中斷 */

    if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)

    {       

    Receive_DataPack();

    }

#endif

}


Receive_DataPack()


這個才是真正的接收數據處理函數,為什么我要將這個函數單獨封裝起來呢?因為這個函數其實是很重要的,因為我的代碼兼容普通串口接收與空閑中斷,不一樣的接收類型其處理也不一樣,所以直接封裝起來更好,在源碼中通過宏定義實現選擇接收的方式!更考慮了兼容操作系統的,可能我會在系統中使用dma+空閑中斷,所以,供前臺查詢的信號量就有可能不一樣,可能需要修改,我就把它封裝起來了。不過無所謂,都是一樣的。


/************************************************************

  * @brief   Uart_DMA_Rx_Data

  * @param   NULL

  * @return  NULL

  * @author  jiejie

  * @github  https://github.com/jiejieTop

  * @date    2018-xx-xx

  * @version v1.0

  * @note    使用串口 DMA 接收時調用的函數

  ***********************************************************/

#if USE_USART_DMA_RX

void Receive_DataPack(void)

{

    /* 接收的數據長度 */

    uint32_t buff_length;


    /* 關閉DMA ,防止干擾 */

    DMA_Cmd(USART_RX_DMA_CHANNEL, DISABLE);  /* 暫時關閉dma,數據尚未處理 */ 


    /* 清DMA標志位 */

    DMA_ClearFlag( DMA1_FLAG_TC5 );  


    /* 獲取接收到的數據長度 單位為字節*/

    buff_length = USART_RX_BUFF_SIZE - DMA_GetCurrDataCounter(USART_RX_DMA_CHANNEL);


    /* 獲取數據長度 */

    Usart_Rx_Sta = buff_length;


    PRINT_DEBUG("buff_length = %d\n ",buff_length);


    /* 重新賦值計數值,必須大于等于最大可能接收到的數據幀數目 */

    USART_RX_DMA_CHANNEL->CNDTR = USART_RX_BUFF_SIZE;    


    /* 此處應該在處理完數據再打開,如在 DataPack_Process() 打開*/

    DMA_Cmd(USART_RX_DMA_CHANNEL, ENABLE);      


    /* (OS)給出信號 ,發送接收到新數據標志,供前臺程序查詢 */


    /* 標記接收完成,在 DataPack_Handle 處理*/

    Usart_Rx_Sta |= 0xC000;


    /* 

    DMA 開啟,等待數據。注意,如果中斷發送數據幀的速率很快,MCU來不及處理此次接收到的數據,中斷又發來數據的話,這里不能開啟,否則數據會被覆蓋。有2種方式解決:

    1. 在重新開啟接收DMA通道之前,將Rx_Buf緩沖區里面的數據復制到另外一個數組中,

    然后再開啟DMA,然后馬上處理復制出來的數據。


    2. 建立雙緩沖,重新配置DMA_MemoryBaseAddr的緩沖區地址,那么下次接收到的數據就會

    保存到新的緩沖區中,不至于被覆蓋。

    */

}


f1使用dma是非常簡單的,我在f4用dma的時候也遇到一些問題,最后看手冊解決了,打算下一篇文章就寫一下調試過程,沒有什么是debug不能解決的,如果有,那就兩次。今天臺風天氣,連著舍友的WiFi更新的文章~中國電信還是強,臺風天氣信號一點都不虛,我的移動卡一動不動-_-

關鍵字:STM32  串口DMA  不定長數據 引用地址:STM32之串口DMA接收不定長數據

上一篇:兩片STM32使用HAL完成SPI全雙工主從通信
下一篇:STM32F103實現DMA接收串口不定長度數據

推薦閱讀

近日,市交通委相關負責人在做客治堵大家談節目時介紹,北京首條自動駕駛測試路將落地亦莊。未來本市會在道路設施上逐步做“車路協同”,把道路上的交通基礎設施設定標準,和無人駕駛車輛“握手”,讓自動駕駛車輛可以識別道路標識。?北京首條無人駕駛道路將落戶亦莊無人駕駛技術正在走進市民視野。去年12月,北京市交通委發布自動駕駛新規,《北京市關于...
  小米買入TCL集團逾6500萬股  1月6日晚間TCL集團發布公告稱,公司接到小米集團通知,基于對公司價值的判斷及對公司經營策略、核心競爭力、行業地位及文化價值觀的高度認同,截至2019年1月4日小米集團通過深交所交易系統在二級市場購入公司股份,購入股數為6516.88萬股,占公司總股本的0.48%。  據介紹,同時雙方為了實現更有效的產業戰略協同,形成...
全球高科技照明企業歐司朗將于CES 2020國際消費電子展宣布其成為著名瑞士創意智庫林斯比得汽車公司(Rinspeed)最新概念車MetroSnap的獨家照明供應商。這是歐司朗連續第四年為林斯比得概念車提供車輛照明與傳感器應用支持,并與其攜手照亮駕駛的未來。全新的MetroSnap概念車將于拉斯維加斯會展中心北廳的8516歐司朗展臺首次亮相。 作為可見光與不可見光領...
RTI Connext Drive軟件現在可以在恩智浦汽車級S32G車輛網絡處理器上實現本地運行,從而通過靈活,模塊化和可擴展的平臺實現域電氣/電子(E / E)體系結構。Connext Drive是RTI的汽車級連接軟件,基于數據分發服務(DDS)標準。兩家公司將共同致力于加速下一代自動駕駛汽車的發展。 恩智浦的S32G車輛網絡處理器旨在滿足所有級別的RTI-NXP自動駕駛汽車...

史海拾趣

問答坊 | AI 解惑

電子工程師總結

認為總結的不錯…

查看全部問答∨

閑聊哈希表 (上)

經典數據結構教科書中,“表”是數據結構的一個大家族。其中,有順序表(數組)、單向鏈表、雙向鏈表、循環鏈表等等。我們今天聊的不是這些,而是“表”中的異類——哈希表(Hash Table)。 為什么會有哈希表這種數據結構呢?讓我們用一個通俗的 ...…

查看全部問答∨

測試系統中的復位等待

在verilog中模塊都是全局的,所以可以直接在模塊外使用內部的寄存器,下面的task中實現測試系統中的復位等待   // purpose: procedure to wait until the root port is done being reset   task req_intf_wait_reset_end;  ...…

查看全部問答∨

請教:WINCE5.0上已經可以使用3G上網卡,原來WinMobile基于GPRS的程序如何移植過來?

怎樣才能讓原來基于GPRS的程序運行于3G上網卡上? 我正在WINCE 5.0上做一個3G無線上網卡的項目,已經可以用IE上網了; 但是以前的程序是基于GPRS的,調用的網絡接口不一樣,原來調用的網絡接口有這些: ConnMgrApiReadyEvent、ConnMgrConnection ...…

查看全部問答∨

【請教】高手幫解釋下STM32中的Progress和Thread

                                 PC軟件編程也有Progress和Thread的概念,ARM中這個概念是如何的?…

查看全部問答∨

STM32上使用UC/gui的一些資料

  STM32上使用UC/gui的一些資料 詳情可以聯系 QQ 940436962…

查看全部問答∨

幫忙看看I2C模塊有啥問題,新手求指教!

//所需頭文件 #include  "includes.h"                        // 封裝的頭文件、宏定義、變量、函數聲明 /*************************全局變量 ...…

查看全部問答∨

關于DSP數字鎖相放大器的設計

有木有大神幫忙,我的畢設是關于DSP數字鎖相放大器的設計,我現在編程編不好,達不到效果,很是愁人,望大神解決啊。。。。QQ 870612404…

查看全部問答∨

關于運放最全資料,設計,電路,分析

競賽專用關于運放最全資料,設計,電路,分析…

查看全部問答∨

STM32 Nucleo與其藍牙擴展板X-Nucleo-IDB04A1進程

其實前幾天便收到了電子工程世界郵寄過來的板卡,只是當是項止催得急便沒有進行開發,今天把漂亮的藍牙擴展板與Nucleo板組合在了一起開始研究。根據板卡包裝上的網址,我到ST網站上下載了很多資料,包括板卡簡介,原理圖,BOM表,生產加工文件,還 ...…

查看全部問答∨
小廣播
設計資源 培訓 開發板 精華推薦

最新單片機文章
何立民專欄 單片機及嵌入式寶典

北京航空航天大學教授,20余年來致力于單片機與嵌入式系統推廣工作。

 
EEWorld訂閱號

 
EEWorld服務號

 
汽車開發圈

 
機器人開發圈

電子工程世界版權所有 京ICP證060456號 京ICP備10001474號-1 電信業務審批[2006]字第258號函 京公網安備 11010802033920號 Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
主站蜘蛛池模板: 自贡市| 文昌市| 金塔县| 长宁县| 徐州市| 大埔县| 普陀区| 兴仁县| 叶城县| 高雄县| 泰和县| 巴南区| 镇沅| 灵山县| 九寨沟县| 乌什县| 青铜峡市| 城市| 珠海市| 佛山市| 南城县| 赤壁市| 章丘市| 铅山县| 房产| 台南市| 岗巴县| 纳雍县| 上蔡县| 兴安县| 长葛市| 东明县| 逊克县| 阿巴嘎旗| 乌兰县| 德江县| 潮州市| 普兰店市| 乾安县| 古浪县| 化州市|