STM32 串口接受不定長數據_DMA+空閑中斷_環形緩沖區(環形隊列)_485_232


串口接收不定長數據

 

  • 應用場景
    • 搬運串口外設中長度未知的數據
    • 通常用於下位機串口發送一幀的場合
  • 問題:中斷發送數據幀的速率很快,MCU來不及處理此次接收到的數據
    •  在重新開啟接收DMA通道之前,將LumMod_Rx_Buf緩沖區里面的數據復制到另外一個數組中, 然后再開啟DMA,然后馬上處理復制出來的數據。
    •   建立雙緩沖,在LumMod_Uart_DMA_Rx_Data函數中,重新配置DMA_MemoryBaseAddr 的緩沖區地址,那么下次接收到的數據就會保存到新的緩沖區中,不至於被覆蓋。
  • 原理
    • 當串口在一定的、很短的單位時間以后沒有接收到新的數據,就觸發中斷
  • 方式1:DMA+串口空閑中斷
/*****************************bsp_usart_dma.h**************************/
//bsp_usart_dma.h
#ifndef __USARTDMA_H
#define    __USARTDMA_H

#include "stm32f10x.h"
#include <stdio.h>

// 串口工作參數宏定義
#define  DEBUG_USARTx                   USART1
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200


// USART GPIO 引腳宏定義
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10


//    串口中斷宏定義
#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler


//    DMA中斷宏定義
#define     DMA_TX_FLG_TC    DMA1_IT_TC4
#define     DMA_TX_FLG_ALL    DMA1_IT_GL4
#define  DMA_RX_FLG_ALL DMA1_IT_GL5


// 串口對應的DMA請求通道
#define  USART_TX_DMA_CHANNEL     DMA1_Channel4
#define  USART_RX_DMA_CHANNEL     DMA1_Channel5


// 外設寄存器地址
#define  USART_DR_ADDRESS        (USART1_BASE+0x04)
// 一次發送的數據量
#define  SENDBUFF_SIZE            5000

void USART_Config(void);
void USARTx_DMA_Config(void);

#endif /* __USARTDMA_H */


/****************************bsp_usart_dma.c*************************/
//bsp_usart_dma.c
#include "bsp_usart_dma.h"


uint8_t SendBuff[SENDBUFF_SIZE];
/**
  * @brief  USART GPIO 配置,工作參數配置
  * @param  無
  * @retval 無
  */
void USART_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 打開時鍾
    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);    

    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);      //開啟空閑中斷
    USART_Cmd(DEBUG_USARTx, ENABLE);    
}


/**
  * @brief  USARTx TX DMA 配置,內存到外設(USART1->DR)
  * @param  無
  * @retval 無
  */
void USARTx_DMA_Config(void)
{
        DMA_InitTypeDef DMA_InitStructure;
        // 開啟DMA時鍾
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
        //DMA發送    通道4
        DMA_DeInit(USART_TX_DMA_CHANNEL);         // 恢復缺省值
        DMA_Cmd(USART_TX_DMA_CHANNEL,DISABLE);    //關閉DMA
        DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;    // 設置DMA源地址:串口數據寄存器地址
        DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;           // 內存地址(指針)
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;        // 方向:從內存到外設    發送
        DMA_InitStructure.DMA_BufferSize = SENDBUFF_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_InitStructure.DMA_Mode = DMA_Mode_Circular ;                       // 模式,一次或者循環模式
        DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                  // 優先級:中    
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                           // 禁止內存到內存的傳輸
        
        // 配置DMA發送 通道4          
        DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);        
        DMA_ClearFlag(DMA_TX_FLG_ALL);
        DMA_Cmd(USART_TX_DMA_CHANNEL, DISABLE);
        DMA_ITConfig(USART_TX_DMA_CHANNEL, DMA_IT_TC, ENABLE);
        
        //DMA接收  通道5
        DMA_Cmd(USART_RX_DMA_CHANNEL,DISABLE);
        DMA_DeInit(USART_RX_DMA_CHANNEL);
        DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
        DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
        //配置DMA通道
        DMA_Init(USART_RX_DMA_CHANNEL, &DMA_InitStructure);        
        DMA_ClearFlag(DMA_RX_FLG_ALL);
        DMA_Cmd(USART_RX_DMA_CHANNEL, ENABLE);
        
        USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); // 開啟串口DMA發送
        USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); // 開啟串口DMA接收
}


/**************************bsp_nvic.c**************************************/

void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 配置NVIC為優先級組1 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

    //DMA1通道4發送中斷
  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}


/*********************************主函數**********************/

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_nvic.h"
#include "bsp_usart_dma.h"

/*    緩沖區,簡單軟件標志位    */
extern uint8_t SendBuff[SENDBUFF_SIZE];
extern int Key1Flg,Key2Flg,DMA1Flg,Usart1_FLg;    //標志位

/*    中斷事件    */
void Usart1_Event(void);
void DMA1_Event(void);

int main(void)
{
    uint_8 a;
    NVIC_Configuration();
    USART_Config();
    USARTx_DMA_Config();

    /*填充將要發送的數據*/
  for(a=0;a<SENDBUFF_SIZE;a++)
  {
    SendBuff[a] = 'P';
  }
    
    /* 等待中斷,由於使用中斷方式,CPU不用輪詢按鍵,標志位多的時候也可以換成狀態機使用swtch */
    while(1)                            
    {
        if (DMA1Flg == 1)
        {
            DMA1_Event();        
            DMA1Flg = 0;
        }
        if(Usart1_FLg ==1)
        {
            Usart1_Event();
            Usart1_FLg = 0;
        }
    }
}


/*********************************中斷事件******************************************/


/*    串口接收中斷事件    */
void Usart1_Event()
{    
    //串口接受完成產生空閑中斷
    if(USART_GetITStatus(USART1,USART_IT_IDLE)!= RESET)
    {
        //關閉接受,清除DMA通道5中斷,重啟DMA接受
        DMA_Cmd(USART_RX_DMA_CHANNEL, DISABLE);
        DMA_ClearITPendingBit(DMA_RX_FLG_ALL);
        DMA_Cmd(USART_RX_DMA_CHANNEL, ENABLE);    
        USART_ReceiveData(USART1);//Clear IDLE interrupt flag bit
    }
}

/*    DMA發送中斷事件    */
void DMA1_Event()
{
    int i;
    if(DMA_GetITStatus(DMA_TX_FLG_TC))
    {
        //關閉DMA
        DMA_Cmd(USART_TX_DMA_CHANNEL, DISABLE);
        //測試是否進入通道4中斷
        for(i = 0;i<100;i++)
        {
            USART_SendData(DEBUG_USARTx,i);
        }
        //清除標志位
        DMA_ClearFlag(DMA_TX_FLG_TC);     
    }
}
串口空閑中斷+DMA
  • 方式2:環形隊列
    • 環形隊列FIFO+結束標志(0x0a...)+溢出標志位
      • 當一次接收的數據超出隊列結構長度即隊列滿時,觸發溢出標志位;當上一次的數據處理完畢后,回收內存空間,以用於下次隊列周期的數據存放;判斷一次接收完成的標志是結束標志字節,通常是0x0d或0x0a或0x0d+0x0a。
      • 優勢是能最大限度合理利用內存空間,缺點是必須要加結束標志位,
    • 使用場景
      • 串口沒有DMA和空閑中斷的情況,
      • 按鍵FIFO,
    • 參考文章
    • 代碼部分
      • 待補充,參考安富萊代碼按鍵FIFO
 1 typedef struct
 2 {
 3     u16 Head;
 4     u16 Tail;
 5     u16 Lenght;
 6     u8 Ring_Buff[RINGBUFF_LEN];
 7 } RingBuff_t;
 8 
 9 RingBuff_t ringBuff;//創建一個ringBuff的緩沖區
10 
11 void RingBuff_Init(void)
12 {
13     //初始化相關信息
14     ringBuff.Head = 0;
15     ringBuff.Tail = 0;
16     ringBuff.Lenght = 0;
17 }
18 
19 /**
20 * @brief  Write_RingBuff
21 * @param  u8 data
22 * @return FLASE:環形緩沖區已滿,寫入失敗;    TRUE:寫入成功
23 * @note   往環形緩沖區寫入u8類型的數據
24 */
25 u8 Write_RingBuff(u8 data)
26 {
27     if(ringBuff.Lenght >= RINGBUFF_LEN) //判斷緩沖區是否已滿
28     {
29         return FLASE;
30     }
31     ringBuff.Ring_Buff[ringBuff.Tail]=data;    //寫入數據
32 
33     ringBuff.Tail = ( ringBuff.Tail + 1 ) % RINGBUFF_LEN;//防止越界非法訪問,實現環形隊列.尾指針后移
34     ringBuff.Lenght++;
35     return TRUE;
36 }
37 
38 
39 
40 /**
41 * @brief  Read_RingBuff
42 * @param  u8 *rData,用於保存讀取的數據
43 * @return FLASE:環形緩沖區沒有數據,讀取失敗;TRUE:讀取成功
44 * @note   從環形緩沖區讀取一個u8類型的數據
45 */
46 u8 Read_RingBuff(u8 *rData)
47 {
48     if(ringBuff.Lenght == 0)//判斷非空
49     {
50         return FLASE;
51     }
52     
53     *rData = ringBuff.Ring_Buff[ringBuff.Head];//先進先出FIFO,從緩沖區頭出
54 
55     ringBuff.Head = (ringBuff.Head + 1) % RINGBUFF_LEN;//防止越界非法訪問,實現環形隊列.首指針后移
56     ringBuff.Lenght--;
57 
58     return TRUE;
59 }
環形隊列

 


裸機--RS485通訊
  • 物理層
  • 協議標准對比

 


串口協議部分

RS232協議 

 

      • 串口通信最遠距離是50英尺,
      • 可做到雙向傳輸,全雙工通訊,最高傳輸速率20kbps
      • 邏輯1:-3 ~-15V      

邏輯0:+3~+15V

  • RS485
    • RS-485 只有2 根信號線,所以只能工作在半雙工模式,常用於總線網.
    • 電氣特性:邏輯“1”以兩線間的電壓差為+(2~6)V表示;邏輯“0”以兩線間的電壓差為-(2~6)V表示。接口信號電平比RS-232-C降低了,就不易損壞接口電路的芯片, 且該電平與TTL電平兼容,可方便與TTL電路連接。
    • 數據最高傳輸速率為10Mbps。
    • 接口是采用平衡驅動器和差分接收器的組合,抗共模干擾能力增強,即抗噪聲干擾性好。
    • 最大的通信距離約為1219m,最大傳輸速率為10Mb/S,傳輸速率與傳輸距離成反比,在100Kb/S的傳輸速率下,才可以達到最大的通信距離,RS-485總線一般最大支持32個節點

  • RS422
    • RS-422 有4 根信號線:兩根發送、兩根接收。由於RS-422 的收與發是分開的所以可以同時收和發(全雙工),也正因為全雙工要求收發要有單獨的信道,所以RS-422適用於兩個站之間通信,星型網、環網,不可用於總線網;   

 

 

 

文章參考較多網絡資源,如有侵權等等,請聯系或留言.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM