STM32F407ZG 实现DMA收发数据

正点原子的DMA测试实验,代码只包含了DMA发送数据,而没有接收数据,而我需要实现DMA收发数据,经过查找资料,以及验证,终于实现了DMA发送和接收数据。

本人将dma的驱动代码写入了dma.c和dma.h这两个文件中,而在main.c中运用了自定义的dma的相关函数。文件中附有详细注释。

dma.c

#include "dma.h"

u16 rx_len = 0;                          //DMA接收数据的长度
u8 SendBuffer[30]={0};                   //DMA发送缓冲区
u8 ReceiveBuffer[30]={0};                //DMA接收缓冲区
u16 BufferLen = 30;                      //DMA缓冲区的长度
u8 rx_flag = 0;                         //数据帧接收标志

GPIO_InitTypeDef GPIO_InitStructure;      //定义IO初始化结构体 
USART_InitTypeDef USART_InitStructure;    //定义串口结构体
DMA_InitTypeDef DMA_InitStructure;        //定义DMA结构体
NVIC_InitTypeDef NVIC_InitStructure;      //定义中断结构体

/**************************************
@功能             初始化时钟配置
@调用关系           无
@输入参数           无
@返回值                无
@说明             使能GPIOA、USART1、DMA2
***************************************/
void Init_Clock_Config(void)
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);         //使能GPIOA时钟  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);       //使能USART1时钟 
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);          //DMA2时钟使能 
}

/**************************************
@功能             GPIO初始化配置
@调用关系           无
@输入参数           无
@返回值                无
@说明             PA9 => USART1_TX  PA10 => USART1_RX
***************************************/
void Init_Gpio_Config(void)
{   
  //GPIO引脚复用映射
  GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);       //引脚复用映射 PA9 => USART1_TX
  GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);      //引脚复用映射 PA10 => USART1_RX

  //GPIO结构体设置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;        //管脚指定  PA9  PA10  
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                  //复用功能   
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                //推挽复用输出 
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                  //上拉
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;             //速度50MHz   
  GPIO_Init(GPIOA, &GPIO_InitStructure);                        //初始化       
}

/**************************************
@功能             串口的初始化配置
@调用关系           无
@输入参数           
    @Usartx         表示串口,如 USART1
    @Bound          代表串口的波特率
@返回值                无
@说明             无
***************************************/
void Init_Usart_Config(USART_TypeDef *Usartx, u32 Bound)
{   
  //串口结构体设置
  USART_InitStructure.USART_BaudRate = Bound;                                       //波特率设置,一般设置为9600
  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(Usartx,&USART_InitStructure);                                          //初始化串口

  //使能串口
  USART_Cmd(Usartx, ENABLE);    

  //开启串口空闲中断
  USART_ITConfig(Usartx, USART_IT_IDLE, ENABLE);    
}

/**************************************
@功能             串口的中断初始化配置
@调用关系           无
@输入参数           
    @Usartx_Irqn    串口中断标志,如 USART2_IRQn
@返回值                无
@说明             无
***************************************/
void Init_Usart_Nvic_Config(u32 Usartx_Irqn)
{
    //Usart的 NVIC 配置  
    NVIC_InitStructure.NVIC_IRQChannel = Usartx_Irqn;         //串口中断通道  
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;   //抢占优先级3  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;         //子优先级3  
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           //IRQ通道使能  
    NVIC_Init(&NVIC_InitStructure);                           //根据指定的参数初始化NVIC寄存器
}

/**************************************
@功能             DMA的发送初始化配置
@调用关系           无
@输入参数           
    @Usartx             指定的串口,如 USART1
    @Par                DMA外设地址,如 (u32)&USART1->DR
    @DMA_Streamx_tx     代表DMA1的发送数据流,DMA1_Stream0~DMA1_Stream7
    @DMA_Chx            代表DMA的通道选择,DMA_Channel_0~DMA_Channel_7
    @Tx_Buf             代表DMA的发送缓冲区
    @Tx_Size            代表发送数据个数
    @DMA_Streamx_Irqn   DMA数据流的中断,如 DMA2_Stream7_IRQn
@返回值                无
@说明             无
***************************************/
void Init_Dma_Tx_Config(USART_TypeDef *Usartx,u32 Par, DMA_Stream_TypeDef *DMA_Streamx_Tx, 
                        u32 DMA_Chx,u32 Tx_Buf, u16 Tx_Size, u32 DMA_Streamx_Irqn)
{
    DMA_DeInit(DMA_Streamx_Tx);                                                //将DMA的发送数据流设置为缺省值 
    while (DMA_GetCmdStatus(DMA_Streamx_Tx) != DISABLE) ;                      //等待DMA的发送数据流可配置                      

    //DMA的结构体设置
    DMA_InitStructure.DMA_Channel = DMA_Chx;                                   //通道选择  
    DMA_InitStructure.DMA_PeripheralBaseAddr = Par;                            //DMA外设地址  
    DMA_InitStructure.DMA_Memory0BaseAddr = Tx_Buf;                            //DMA 存储器0地址  
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;                    //存储器到外设模式  
    DMA_InitStructure.DMA_BufferSize = Tx_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_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;                     //初始化DMA_FIFOMode 
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;              //FIFO 阈值选择。
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;                //存储器突发单次传输  
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;        //外设突发单次传输  
    DMA_Init(DMA_Streamx_Tx, &DMA_InitStructure);                              //初始化DMA Stream  

    //DMA 中断设置    
    NVIC_InitStructure.NVIC_IRQChannel = DMA_Streamx_Irqn;                     //DMA发送数据流中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;                  //抢占优先级0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;                         //子优先级0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                            //IRQ通道使能 
    NVIC_Init(&NVIC_InitStructure);                                            //根据指定的参数初始化NVIC寄存器

    USART_DMACmd(Usartx,USART_DMAReq_Tx,ENABLE);                               //使能串口1的DMA发送    
    DMA_Cmd(DMA_Streamx_Tx, DISABLE);                                          //关闭DMA传输 
    DMA_ITConfig(DMA_Streamx_Tx,DMA_IT_TC,ENABLE);                             //使能DMA的发送数据流中断
}

/**************************************
@功能             DMA的接收初始化配置
@调用关系           无
@输入参数           
    @Usartx             指定的串口,如 USART1
    @Par                DMA外设地址,如 (u32)&USART1->DR
    @DMA_Streamx_rx     代表DMA1的接收数据流,DMA1_Stream0~DMA1_Stream7
    @DMA_Chx            代表DMA的通道选择,DMA_Channel_0~DMA_Channel_7
    @Rx_Buf             代表DMA的接收缓冲区
    @Rx_Size            代表接收数据个数
@返回值                无
@说明             无
***************************************/
void Init_Dma_Rx_Config(USART_TypeDef *Usartx, u32 Par, DMA_Stream_TypeDef *DMA_Streamx_Rx, 
                        u32 DMA_Chx,u32 Rx_Buf, u16 Rx_Size)
{
    DMA_DeInit(DMA_Streamx_Rx);                                              //将DMA的接收数据流设置为缺省值
    while (DMA_GetCmdStatus(DMA_Streamx_Rx) != DISABLE);                     //等待DMA的接收数据流可配置 

    //DMA的结构体设置 
    DMA_InitStructure.DMA_Channel = DMA_Chx;                                 //通道选择  
    DMA_InitStructure.DMA_PeripheralBaseAddr = Par;                          //DMA外设地址  
    DMA_InitStructure.DMA_Memory0BaseAddr = Rx_Buf;                          //DMA 存储器0地址  
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ;                 //外设到存储器模式  
    DMA_InitStructure.DMA_BufferSize = Rx_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_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;                   //初始化DMA_FIFOMode 
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;            //FIFO 阈值选择。
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;              //存储器突发单次传输  
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;      //外设突发单次传输  
    DMA_Init(DMA_Streamx_Rx, &DMA_InitStructure);                            //初始化DMA Stream  

    USART_DMACmd(Usartx,USART_DMAReq_Rx,ENABLE);                             //使能串口的DMA接收   
    DMA_Cmd(DMA_Streamx_Rx, ENABLE);                                         //开启DMA传输    
}

/**************************************
@功能             DMA的发送使能
@调用关系           无
@输入参数           
    @DMA_Streamx_tx     代表DMA的发送数据流,DMA1_Stream0~DMA1_Stream7 / DMA2_Stream0~DMA2_Stream7
    @ndtr               代表DMA的发送数据个数
@返回值                无
@说明             无
***************************************/
void Dma_Send_Enable(DMA_Stream_TypeDef *DMA_Streamx_tx,u16 ndtr)
{
    DMA_Cmd(DMA_Streamx_tx, DISABLE);                         //关闭DMA传输     
    while (DMA_GetCmdStatus(DMA_Streamx_tx) != DISABLE);        //确保DMA可以被设置        
    DMA_SetCurrDataCounter(DMA_Streamx_tx,ndtr);              //数据传输量   
    DMA_Cmd(DMA_Streamx_tx, ENABLE);                          //开启DMA传输 
}

/**************************************
@功能             DMA的发送完成中断函数
@调用关系           无
@输入参数           无
@返回值                无
@说明             无
***************************************/

void DMA2_Stream7_IRQHandler(void)  
{
    if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET)     //等待DMA2_Steam7传输完成  
    { 
        DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7);           //清除DMA2_Steam7传输完成标志 
        DMA_Cmd(DMA2_Stream7,DISABLE);                        //关闭DMA           
        DMA_ClearITPendingBit(DMA2_Stream7, DMA_IT_TCIF7);    //清除中断屏蔽位     
    }  
}  

/**************************************
@功能             串口1的空闲中断函数
@调用关系           无
@输入参数           无
@返回值                无
@说明             无
***************************************/
void USART1_IRQHandler(void)  
{  
    u16 i;  
    if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)            //如果接到一帧数据 
    {
        DMA_Cmd(DMA2_Stream5, DISABLE);                             //关闭DMA,防止处理其间有数据  

        //先读SR,再读DR,是为了清除IDLE中断
        i = USART1->SR;                                             //读SR寄存器
        i = USART1->DR;                                             //读DR寄存器

        rx_len = BufferLen - DMA_GetCurrDataCounter(DMA2_Stream5);  //获得这一帧数据的长度
        if(rx_len !=0)                                              //这一帧数据长度不为0
        { 
            rx_flag = 1;                                       //数据帧接收标志置1
            for(i=0;i//将接收的数据保存
            }
            for(i=0;i//将接收缓冲区清零
            {
                ReceiveBuffer[i]=0;
            }
        }  

        //清除DMA2_Steam5传输完成标志 
        DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5); 
        DMA_SetCurrDataCounter(DMA2_Stream5, BufferLen);            //设置DMA接收单元的长度
        DMA_Cmd(DMA2_Stream5, ENABLE);                              //打开DMA 

    }  
}   

dma.h

#ifndef DMA_H
#define DMA_H      
#include "sys.h"

extern u16 rx_len;
extern u8 SendBuffer[30];
extern u8 ReceiveBuffer[30];
extern u16 BufferLen;
extern u8 rx_flag; 

extern GPIO_InitTypeDef GPIO_InitStructure;                       //定义IO初始化结构体 
extern USART_InitTypeDef USART_InitStructure;                     //定义串口结构体
extern DMA_InitTypeDef DMA_InitStructure;                         //定义DMA结构体
extern NVIC_InitTypeDef NVIC_InitStructure;                       //定义中断结构体

extern void Init_Clock_Config(void);
extern void Init_Gpio_Config(void);
extern void Init_Usart_Config(USART_TypeDef *Usartx, u32 Bound);
extern void Init_Usart_Nvic_Config(u32 Usartx_Irqn);
extern void Init_Dma_Tx_Config(USART_TypeDef *Usartx,u32 Par, DMA_Stream_TypeDef *DMA_Streamx_Tx, u32 DMA_Chx,u32 Tx_Buf, u16 Tx_Size, u32 DMA_Streamx_Irqn);
extern void Init_Dma_Rx_Config(USART_TypeDef *Usartx, u32 Par, DMA_Stream_TypeDef *DMA_Streamx_Rx, u32 DMA_Chx,u32 Rx_Buf, u16 Rx_Size);
extern void Dma_Send_Enable(DMA_Stream_TypeDef *DMA_Streamx_tx,u16 ndtr);
extern void DMA2_Stream7_IRQHandler(void);
extern void USART1_IRQHandler(void);

#endif

main.c

int main(void)
{     

    Init_Delay(168);          //初始化延时函数
    Init_Led();             //初始化LED端口
    Init_Clock_Config();                     //时钟初始化
    Init_Gpio_Config();                      //GPIO引脚初始化
    Init_Usart_Config(USART1,19200);          //串口初始化
    Init_Usart_Nvic_Config(USART1_IRQn);     //串口中断初始化

    //DMA 发送数据流初始化
    Init_Dma_Tx_Config(USART1, (u32)(&USART1->DR), DMA2_Stream7, DMA_Channel_4, (u32)SendBuffer, BufferLen, DMA2_Stream7_IRQn);
    //DMA 接收数据流初始化
    Init_Dma_Rx_Config(USART1, (u32)(&USART1->DR), DMA2_Stream5, DMA_Channel_4, (u32)ReceiveBuffer, BufferLen);

    while(1)
    {
        //接收到一帧数据
        if(rx_flag)    
        {
            //将接收到的数据发送到设备
            Dma_Send_Enable(DMA2_Stream7, rx_len);                      

            //数据帧接收标志清零
            rx_flag = 0;
        }

        LED1 = !LED1;     //LED灯闪烁,表示程序正常运行
        Delay_ms(300);                    //延时300ms
    }
}

实验现象:
用串口调试工具发送数据给MCU,MCU将接收到的数据返回给串口调试工具

你可能感兴趣的:(STM32,嵌入式)