单片机
返回首页

STM32F103基于DMA接收不定帧长USART数据

2017-10-09 来源:eefocus

DMA是一种不使用CPU而将数据从一片地址空间复制到另一片地址空间的总线,这样就减少了CPU的负担,使其能够更加专注于数据运算。为了能够减少CPU的负担,DMA应该采取中断方式而非查询模式。但是非常不幸的是,STM32F103只为DMA提供了三种中断:半步中断、完成中断和错误中断。如果UART接收的是定帧长的数据,则可以开启DMA半步中断,并且目标地址长度为帧长两倍。这样每接收完一帧进一次中断,进行某些操作,是很理想的。然而当遇到如同GPS一样不定帧长的数据时,如果仍用半步中断则难以确定目标地址的长度。所以在此放弃使用DMA的中断,转而使用的是另一种比较特别的中断:UART空闲中断。

先来介绍一下UART空闲中断。UART常用的接收中断响应有:接收数据 就绪可读中断RXNE(这是最常用的)、数据溢出中断(ORE)、奇偶校验错中断(PE)和空闲中断(IDLE)。空闲中断是指当总线检测到一帧发完后, 总线空闲则会将此位置一,如果USART_CR1中的IDLEIE为’1’,则产生中断。空闲中断有两个比较有意思的特点:

1、清零方式:软件清零,先读USART_SR,然后读USART_DR

2、直到RXNE被置一后IDLE才能被重读置一,即IDLE被置一并软件清零后,只有之后再次接收到数据,IDLE才能被置一。这样就防止了总线长时间空闲而多次引发空闲中断。

好的废话不多说上例程。


以下都是通过DMA接收GPS串口的子模块程序

#define UART_RX_LEN     128
static char Uart_Rx[UART_RX_LEN];//GPS接收数据


void GPS_Init(void)
{
   RCC_Configuration();//时钟打开
   GPIO_Configuration();//GPIO配置   
   DMA_Configuration();//DMA配置
   UART_Configuration();//UART配置
   NVIC_Configuration();//中断优先级配置
}

 

void RCC_Configuration(void)
{
   //打开串口对应的外设时钟   
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);     
    //启动DMA时钟 
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); 
}

void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

 
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
 
 
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void DMA_Configuration(void)
{
    DMA_InitTypeDef DMA_InitStructure;
 //DMA1通道5配置 
    DMA_DeInit(DMA1_Channel5); 
    //外设地址 
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DR); 
    //内存地址 
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Uart_Rx; 
    //dma传输方向单向 
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; 
    //设置DMA在传输时缓冲区的长度 
    DMA_InitStructure.DMA_BufferSize = UART_RX_LEN; 
    //设置DMA的外设递增模式,一个外设 
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 
    //设置DMA的内存递增模式 
    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的优先级别 
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; 
    //设置DMA的2个memory中的变量互相访问 
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; 
    DMA_Init(DMA1_Channel5,&DMA_InitStructure); 
 
    //使能通道5 
    DMA_Cmd(DMA1_Channel5,ENABLE);
}

void UART_Configuration(void)
{
    USART_InitTypeDef USART_InitStructure;
 //初始化参数      
    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_InitStructure.USART_BaudRate = 9600;  
    //初始化串口  
    USART_Init(USART1,&USART_InitStructure);   
     
    //中断配置 
    USART_ITConfig(USART1,USART_IT_TC,DISABLE); 
    USART_ITConfig(USART1,USART_IT_RXNE,DISABLE); 
    USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);
 //采用DMA方式接收 
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
}


void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
 //配置UART1中断   
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3); 
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;               //通道设置为串口1中断   
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;       //中断占先等级0   
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;              //中断响应优先级0   
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                 //打开中断   
    NVIC_Init(&NVIC_InitStructure); 
}

 

void USART1_IRQHandler(void)                                
{    
    uint32_t Length = 0;//数据长度    
    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) 
    { 
        DMA_Cmd(DMA1_Channel5,DISABLE);
        Length = USART1->SR; 
        Length = USART1->DR; //清USART_IT_IDLE标志 
        Length = UART_RX_LEN - DMA_GetCurrDataCounter(DMA1_Channel5); 

//设置传输数据长度 
        DMA1_Channel5->CNDTR = UART_RX_LEN;//重装填,并让接收地址偏址从0开始
        DMA_Cmd(DMA1_Channel5, ENABLE);//处理完,重开DMA  
    }  
    __nop();  
}

void GPS_Cmd(FunctionalState NewState)
{
 USART_Cmd(USART1, NewState);
}

 

以下是调用函数,一般在main函数中

GPS_Init();    
GPS_Cmd(ENABLE);


进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

最新器件
精选电路图
  • 光控音效发生器电路

  • 非常简单的150W功放电路图

  • 一个简单的警笛电路图

  • 基于IC555的可变PWM振荡器电路

  • 分享一个电网倾角计电路

  • 电谐波图形均衡器示意图

    相关电子头条文章