<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

FreeRTOS串口DMA收發不定長數據

碼農愛學習 ? 來源:碼農愛學習 ? 作者:碼農愛學習 ? 2022-09-26 09:08 ? 次閱讀

基礎知識點

DMA

DMA(Direct Memory Access),即直接內存存儲,在一些數據的傳輸中,如串口、SPI等,采用DMA方式,傳輸過程不需要CPU參與,可用讓CPU有更多的時間處理其他的事情。

STM32F4的DMA通道選擇如下:

pYYBAGMwZCmADMBdAAOMBqmQXto194.png

接下來的程序思路如下:

pYYBAGMwZDSAJNdMAADbHRWS2Ks224.png

編程要點

DMA發送

串口DMA發送配置

由于是發送不定長的數據,先不需要配置發送的長度,在每次的發送時,再配置。

//=======================================
//串口DMA發送配置
//=======================================
void dma_uart_tx_init()
{
    DMA_InitTypeDef  DMA_InitStructure;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2時鐘使能 
    
    DMA_DeInit(Uart_Tx_DMAStream);//使用----->DMA2_Stream7
    while (DMA_GetCmdStatus(Uart_Tx_DMAStream) != DISABLE){}//等待DMA可配置 

    /* 配置 DMA Stream */
    DMA_InitStructure.DMA_Channel            = DMA_Channel_4;              //通道選擇
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;           //目的:DMA外設地址
    DMA_InitStructure.DMA_Memory0BaseAddr    = (u32)SendBuff;              //源:DMA存儲器0地址
    DMA_InitStructure.DMA_DIR                = DMA_DIR_MemoryToPeripheral; //方向:存儲器到外設模式
    //DMA_InitStructure.DMA_BufferSize       = BUF_SIZE;                   //長度:數據傳輸量(先不配置)
    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;  //外設非增量模式
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;       //存儲器增量模式
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外設數據長度:8位
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;    //存儲器數據長度:8位
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Normal;            //使用普通模式 
    DMA_InitStructure.DMA_Priority           = DMA_Priority_Medium;        //DMA優先級:中等優先級
    DMA_InitStructure.DMA_FIFOMode           = DMA_FIFOMode_Disable;       //FIFO模式 
    DMA_InitStructure.DMA_FIFOThreshold      = DMA_FIFOThreshold_Full;     //FIFO大小
    DMA_InitStructure.DMA_MemoryBurst        = DMA_MemoryBurst_Single;     //存儲器單次傳輸
    DMA_InitStructure.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single; //外設單次傳輸
    DMA_Init(Uart_Tx_DMAStream, &DMA_InitStructure);//初始化DMA Stream
    
    //中斷配置
    DMA_ITConfig(Uart_Tx_DMAStream,DMA_IT_TC,ENABLE);  //配置DMA發送完成后產生中斷
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;//
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=7;//搶占優先級8
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;       //子優先級0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器
    
    USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);  //使能串口1的DMA發送
    DMA_Cmd (Uart_Tx_DMAStream,DISABLE);//先不要使能DMA!           
}

DMA發送完成中斷

DMA發送完成后,觸發DMA發送完成中斷,這里可用釋放自定義的DMA發送完成信號量,表明下次的DMA傳輸可用進行。

//=======================================
//DMA發送完成中斷服務程序
//=======================================
void DMA2_Stream7_IRQHandler(void)
{
    BaseType_t xHigherPriorityTaskWoken;
    //printf("ooooooorn");
    if(DMA_GetITStatus(Uart_Tx_DMAStream,DMA_IT_TCIF7)!= RESET) //檢查DMA傳輸完成中斷 DMA_IT_TCIF7
    {
        DMA_ClearITPendingBit(Uart_Tx_DMAStream,DMA_IT_TCIF7); 
        //printf("dma tx okrn");
        if(uartDMATCSemaphore!=NULL)
        {
            //釋放二值信號量
            xSemaphoreGiveFromISR(uartDMATCSemaphore,&xHigherPriorityTaskWoken);    //釋放DMA傳輸完成二值信號量
        }
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的話進行一次任務切換
    }
}
 

DMA發送函數接口

//=======================================
//串口DMA發送函數
//======================================= 
void uart_DMA_send(u8 *str,u16 ndtr)
{
    u8 i;
    u8 *p=str;
    
    while(xSemaphoreTake(uartDMATCSemaphore,2)!=pdTRUE);//獲取信號量,等待DMA發送可用
    
    DMA_Cmd(Uart_Tx_DMAStream, DISABLE);                      //關閉DMA傳輸 
    while (DMA_GetCmdStatus(Uart_Tx_DMAStream) != DISABLE){}    //確保DMA可以被設置  
    DMA_SetCurrDataCounter(Uart_Tx_DMAStream,ndtr);          //數據傳輸量 
    for(i=0;i;i++)>

DMA接收

串口DMA接收配置

需要配置一個接收地址和一個接收長度,用于DMA接收數據的暫存。

//=======================================
//串口DMA接收配置
//=======================================
void dma_uart_rx_init()
{
    DMA_InitTypeDef  DMA_InitStructure;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2時鐘使能 
    
    DMA_DeInit(Uart_Rx_DMAStream);//使用----->DMA2_Stream5
    while (DMA_GetCmdStatus(Uart_Rx_DMAStream) != DISABLE){}//等待DMA可配置 

    /* 配置 DMA Stream */
    DMA_InitStructure.DMA_Channel            = DMA_Channel_4;              //通道選擇
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;           //源:DMA外設地址
    DMA_InitStructure.DMA_Memory0BaseAddr    = (u32)ReceiveBuff;           //目的:DMA存儲器0地址
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralToMemory; //方向:外設到存儲器模式
    DMA_InitStructure.DMA_BufferSize         = BUF_SIZE;                   //長度:數據傳輸量
    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;  //外設非增量模式
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;       //存儲器增量模式
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外設數據長度:8位
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;    //存儲器數據長度:8位
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Normal;            //使用普通模式 
    DMA_InitStructure.DMA_Priority           = DMA_Priority_Medium;        //DMA優先級:中等優先級
    DMA_InitStructure.DMA_FIFOMode           = DMA_FIFOMode_Disable;       //FIFO模式 
    DMA_InitStructure.DMA_FIFOThreshold      = DMA_FIFOThreshold_Full;     //FIFO大小
    DMA_InitStructure.DMA_MemoryBurst        = DMA_MemoryBurst_Single;     //存儲器單次傳輸
    DMA_InitStructure.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single; //外設單次傳輸
    DMA_Init(Uart_Rx_DMAStream, &DMA_InitStructure);//初始化DMA Stream
    
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);  //使能串口1的DMA接收
    DMA_Cmd (Uart_Rx_DMAStream,ENABLE);//使能          
}

串口空閑中斷

串口空閑中斷的作用與上一篇介紹的一樣,都是在發送完一串字符后被觸發,這次由于使用了DMA接收,所以接收的數據在DMA緩沖區,且接收的數據長度可用根DMA接收通道的總長度與剩余長度的差值來計算,將接收的數據復制出來使用即可,同時釋放自定義的串口空閑信號量,以便其它任務可用及時獲取串口接收到的數據。

//=======================================
//串口1空閑中斷服務程序,用于DMA接收
//=======================================
void USART1_IRQHandler(void)                    
{
    uint8_t data;//接收數據暫存變量
    BaseType_t xHigherPriorityTaskWoken;
    
    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)//空閑中斷
    {
        data = USART1->SR;
        data = USART1->DR;
        
        DMA_Cmd(Uart_Rx_DMAStream,DISABLE);//關閉DMA接收
        while (DMA_GetCmdStatus(Uart_Rx_DMAStream) != DISABLE){}    //確保DMA可以被設置 
        rx_cnt = BUF_SIZE - DMA_GetCurrDataCounter(Uart_Rx_DMAStream);//得到真正接收數據個數  
        DMA_SetCurrDataCounter(Uart_Rx_DMAStream,BUF_SIZE);//重新設置接收數據個數    
        //printf("rx_cnt:%drn",rx_cnt);
        memcpy(rxbuf,ReceiveBuff,rx_cnt);//先復制出來,防止下次的數據來了之后將其覆蓋
        DMA_ClearFlag(Uart_Rx_DMAStream,DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5);//這里的各種標志還沒搞懂
        DMA_Cmd(Uart_Rx_DMAStream,ENABLE); //開啟DMA接收
            
        if(uartRxIDLESemaphore!=NULL)
        {
            //printf("nnnnnnnrn");
            //釋放二值信號量
            xSemaphoreGiveFromISR(uartRxIDLESemaphore,&xHigherPriorityTaskWoken);//釋放串口空閑中斷二值信號量
        }
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的話進行一次任務切換
    }
} 

串口配置與測試任務

串口配置

基礎的GPIO配置,以及串口空閑中斷配置,并調用上面的串口DMA發送與接收配置。

//=======================================
//串口配置
//=======================================
void uart_init(u32 bound)
{
    //GPIO端口設置
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA時鐘
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1時鐘

    //串口1對應引腳復用映射
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9復用為USART1
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10復用為USART1

    //USART1端口配置
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9與GPIOA10
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;             //復用功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;         //速度50MHz
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;            //推挽復用輸出
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;             //上拉
    GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10

    //USART1 初始化設置
    USART_InitStructure.USART_BaudRate            = bound;                         //波特率設置
    USART_InitStructure.USART_WordLength          = USART_WordLength_8b;           //字長為8位數據格式
    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(USART1, &USART_InitStructure); //初始化串口1

    //DMA Config
    dma_uart_tx_init();//串口DMA發送配置
    dma_uart_rx_init();//串口DMA接收配置
    
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//開啟串口空閑中斷
    //Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;       //串口1中斷通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=6; //搶占優先級8
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;       //子優先級0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器
    
    USART_Cmd(USART1, ENABLE);  //使能串口1 
}

測試任務

創建DMA發送完成信號量和串口空閑信號量,并先釋放DMA發送完成信號量,用于第一次DMA發送時獲取信號量。然后測試兩條DMA發送不定長字符串,最后測試DMA接收不定長字符串。

//打印任務函數(測試任務)
void print_task(void *pvParameters)
{
    //創建二值信號量
    uartDMATCSemaphore = xSemaphoreCreateBinary();
    uartRxIDLESemaphore = xSemaphoreCreateBinary();
    xSemaphoreGive(uartDMATCSemaphore);
    
    u8 str1[]="ma nong ai xue xirn";
    uart_DMA_send(str1,sizeof(str1));
    
    u8 str2[]="xxpcb.github.iorn";
    uart_DMA_send(str2,sizeof(str2));
    
    BaseType_t err = pdFALSE;
    while(1)
    {
        err=xSemaphoreTake(uartRxIDLESemaphore,5);  //獲取信號量
        if(err==pdTRUE)                         //獲取信號量成功
        {  
            uart_DMA_send("receive:",sizeof("receive:"));
            uart_DMA_send(rxbuf,rx_cnt);
            uart_DMA_send("rn",sizeof("rn"));
            
            rx_cnt=0;
        }
    }
}

實驗結果

通過串口助手,可以先接收到DMA發送的兩個字符串(第一條hello是測試串口的,不是DMA發的),然后通過串口調試助手發送兩次nice to meet you,測試DMA接收。

hello
ma nong ai xue xi
xxpcb.github.io
receive:nice to meet you
receive:nice to meet you

審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 串口
    +關注

    關注

    14

    文章

    1501

    瀏覽量

    74687
  • dma
    dma
    +關注

    關注

    3

    文章

    539

    瀏覽量

    99290
  • RTOS
    +關注

    關注

    20

    文章

    780

    瀏覽量

    118888
  • FreeRTOS
    +關注

    關注

    12

    文章

    474

    瀏覽量

    61461
收藏 人收藏

    評論

    相關推薦

    FreeRTOS串口中斷接收不定長數據與二值信號量的使用

    FreeRTOS例程,使用串口中斷接收不定長數據,以及二值信號量的使用
    的頭像 發表于 09-26 09:02 ?3571次閱讀
    <b class='flag-5'>FreeRTOS</b><b class='flag-5'>串口</b>中斷接收<b class='flag-5'>不定長</b>的<b class='flag-5'>數據</b>與二值信號量的使用

    stm32串口怎么用DMA接收不定長數據?

    stm32串口怎么用DMA接收不定長數據,求大神點播1. 網上查到,空閑中斷+DMA可以接收不定長
    發表于 08-21 09:21

    DMA如何接收不定長數據?

    STM32串口DMA如何接收不定長數據呢?
    發表于 12-21 07:39

    STM32F103 串口 +DMA中斷實現數據收發 精選資料分享

    01 STM32F103 串口DMA + 空閑中斷 實現不定長數據收發02文章目錄1. 代碼講解1.1 uart_
    發表于 08-13 08:39

    不定長數據接收的原理是什么?怎么實現串口數據不定長接收?

    不定長數據接收的原理是什么?怎么實現串口數據不定長接收?
    發表于 11-16 08:11

    請問串口DMA+環形緩沖區如何實現不定長度的數據收發?

    請問串口DMA+環形緩沖區如何實現不定長度的數據收發?
    發表于 12-08 06:13

    串口DMA+中斷的形式如何接收不定長數據

    串口DMA+中斷的形式如何接收不定長數據
    發表于 12-08 06:07

    如何去實現stm32f405串口DMA+空閑中斷不定長數據收發代碼

    如何去實現stm32f405串口DMA+空閑中斷不定長數據收發代碼?
    發表于 12-08 07:36

    HAL庫的DMA+CobeMx方式下的不定長收發

    STM32L051雙串口DMA方式不定長收發HAL庫的DMA+CobeMx方式下的不定長
    發表于 01-20 06:25

    請問STM32 DMA串口接收不定長數據的過程是怎樣的?

    請問STM32 DMA串口接收不定長數據的過程是怎樣的?
    發表于 02-18 08:00

    STM32 DMA串口接收不定長數據

    STM32 DMA串口接收不定長數據
    發表于 12-24 18:50 ?40次下載
    STM32  <b class='flag-5'>DMA</b><b class='flag-5'>串口</b>接收<b class='flag-5'>不定長</b><b class='flag-5'>數據</b>

    STM32之串口DMA接收不定長數據

    目錄STM32之串口DMA接收不定長數據引言DMA簡介什么是DMA在STM32的
    發表于 12-24 19:03 ?30次下載
    STM32之<b class='flag-5'>串口</b><b class='flag-5'>DMA</b>接收<b class='flag-5'>不定長</b><b class='flag-5'>數據</b>

    STM32F429 標準庫 串口完成中斷+DMA 接收不定長數據

    之前寫過STM32F103的串口+DMA不定長數據接收驅動,近期在設計STM32F429系列單片機標準庫函數 串口+DAM 接收
    發表于 12-24 19:03 ?21次下載
    STM32F429 標準庫  <b class='flag-5'>串口</b>完成中斷+<b class='flag-5'>DMA</b>  接收<b class='flag-5'>不定長</b><b class='flag-5'>數據</b>

    STM32單片機串口空閑中斷+DMA接收不定長數據

    在上一篇文章STM32單片機串口空閑中斷接收不定長數據中介紹了利用串口空閑中斷接收不定長數據,這
    發表于 12-27 19:24 ?18次下載
    STM32單片機<b class='flag-5'>串口</b>空閑中斷+<b class='flag-5'>DMA</b>接收<b class='flag-5'>不定長</b><b class='flag-5'>數據</b>

    STM32CubeMX之串口接收不定長數據

    基本串口通信通常只能接收到定長數據,無法穩定接收不定長數據,本章介紹利用STM32單片機的IDLE空閑中斷,接收
    的頭像 發表于 05-11 09:59 ?2379次閱讀
    STM32CubeMX之<b class='flag-5'>串口</b>接收<b class='flag-5'>不定長</b><b class='flag-5'>數據</b>
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>