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