STM32——采用DMA的方式实现串口收发数据

STM32——采用DMA的方式实现串口收发数据


概述

想必看到这篇博客的你已经知道了DMA的好处了吧,所以这儿就不过多地讲述DMA对于缓解MCU压力有多么重要的用途,DMA在很多方面都可以使用,如IIC,SPI,USART等,这儿主要给出DMA在USART上面的一个实例。


代码实现

主要代码直接在一个程序中实现


全局变量以及宏定义

#define DEFAULT_BAUD 115200
#define UART_RX_LEN		128

/*串口接收DMA缓存*/
uint8_t Uart_Rx[UART_RX_LEN] = {0};

/*串口发送DMA缓存*/
uint8_t Uart_Send_Buffer[100]={0};

uint8_t Data_Receive_Usart=0;

DMA和USART的初始化的函数

void usart_dma_init(void)
{
    GPIO_InitTypeDef	GPIO_InitStructure;
    USART_InitTypeDef	USART_InitStructure;
    DMA_InitTypeDef		DMA_InitStructure;
    NVIC_InitTypeDef	NVIC_InitStructure;


	
    /*  配置GPIO的模式和IO口 */	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);  
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;					//TX
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;			//复用推挽输出
    GPIO_Init(GPIOA,&GPIO_InitStructure);					//初始化串口输入IO
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;				//RX
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;		//模拟输入
    GPIO_Init(GPIOA,&GPIO_InitStructure); 	


	
    /*初始化串口接收和发送函数*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);
    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 = DEFAULT_BAUD; 
	
    /*初始化串口*/
    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); 
	
    //配置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);



    /*DMA发送中断设置*/
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
	
	
    /*DMA1通道4配置发送*/
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    DMA_DeInit(DMA1_Channel4);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Uart_Send_Buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = 100;
    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_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4,&DMA_InitStructure);
    DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
    //DMA_Cmd(DMA1_Channel4, ENABLE);//使能通道4,一般发送的时候再使能
 
 

    /*DMA1通道5配置接收*/
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    DMA_DeInit(DMA1_Channel5);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Uart_Rx;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = UART_RX_LEN;
    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_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel5,&DMA_InitStructure);
 
    /*使能通道5*/
    DMA_Cmd(DMA1_Channel5,ENABLE);

 
        
    //采用DMA方式发送
    USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
	
    //采用DMA方式接收
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
	
    //启动串口  
    USART_Cmd(USART1, ENABLE);
}

DMA发送使能函数

/**@ brief 使能发送数据
 *
 * 启动DMA数据发送功能
 * 
 * @param size表示需要发送的DMA中数据的个数
 */
void uart_dma_send_enable(uint16_t size)
{
    DMA1_Channel4->CNDTR = (uint16_t)size; 
    DMA_Cmd(DMA1_Channel4, ENABLE);       
}	

串口接收的中断函数

/**@ brief串口1接收中断
 *
 * 收到一帧数据进入一次,进行DMA的读取
 * 
 */
void USART1_IRQHandler(void)                               
{   
    uint32_t temp = 0;
    uint16_t i = 0;
	
    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
    {
        //USART_ClearFlag(USART1,USART_IT_IDLE);
        temp = USART1->SR;
        temp = USART1->DR; //清USART_IT_IDLE标志
		
        DMA_Cmd(DMA1_Channel5,DISABLE);
 
        temp = UART_RX_LEN - DMA_GetCurrDataCounter(DMA1_Channel5);
        for (i = 0;i < temp;i++)
        {
            Data_Receive_Usart = Uart_Rx[i];
			
            //+++对收到的数据加一后回发出去		
            Uart_Send_Buffer[i]=Data_Receive_Usart+1;						
            uart_dma_send_enable(temp);
            //+++			
        }
		
        //设置传输数据长度
        DMA_SetCurrDataCounter(DMA1_Channel5,UART_RX_LEN);
		
        //打开DMA
        DMA_Cmd(DMA1_Channel5,ENABLE);
    } 
} 

DMA发送中断

/**@ brief DMA发送中断
 *
 * 发送数据,将DMA中的数据发送出去
 * 
 */
void DMA1_Channel4_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_FLAG_TC4)==SET)
    {
        DMA_ClearFlag(DMA1_FLAG_GL4);        
        DMA_Cmd(DMA1_Channel4, DISABLE);  
    }
}

到此为止主要代码就已经结束了,基本上能够满足测试要求


项目工程下载

串口1的DMA实现 点击下载

串口2的DMA实现 点击下载

串口3的DMA实现 点击下载

### 回答1: STM32F4系列微控制器具有丰富的外设接口,其中包括多个串口接口以实现串口通信的功能。使用DMA(直接内存访问)模式可以提高串口通信的效率,实现数据的高速收发。 首先,需要初始化串口DMA相关的寄存器。通过配置相关的寄存器,设置波特率、数据位、停止位等参数,并使能串口接口和DMA功能。接着,为DMA配置通道和相关的内存地址。通常情况下,DMA的通道与串口接口对应,可以通过寄存器的设置来实现。 在接收数据时,可以配置DMA串口接收到的数据直接传输到指定的内存地址。通过设置DMA接收通道的内存地址寄存器,将接收到的数据直接存储到指定的内存空间中。此外,还需要设置DMA传输的数据大小和传输完成后的操作。 在发送数据时,类似地,可以将需要发送的数据存储在指定的内存地址中,然后通过配置DMA发送通道的内存地址寄存器,将数据从内存传输到串口发送寄存器中,实现数据的发送。 当收发数据完成后,可以通过DMA传输完成中断来通知处理器,以进行后续的数据处理操作。 总之,使用STM32F4系列微控制器的串口DMA功能,可以实现高效、稳定的串口通信。此外,对于更高级的应用,还可以使用DMA的双缓冲区功能来同时实现并行的数据收发。 ### 回答2: STM32F4系列的微控制器具有强大的DMA(直接内存访问)功能,可以实现高效的串口数据收发。对于串口数据收发,我们通常会使用USART(通用同步/异步收发器)模块,并结合DMA实现数据传输的快速和可靠。 首先,我们需要初始化USART模块,设置相应的波特率、数据位、停止位等参数。然后,我们需要启用DMA功能,并配置DMA通道的源和目标地址。在收发数据时,我们可以通过修改对应的寄存器,向USART发送数据或从USART接收数据。 使用DMA来进行串口数据收发时,我们可以在初始化时设置好DMA通道的源和目标地址,然后通过修改USART的寄存器来触发数据传输。在数据传输过程中,DMA将自动将数据从源地址传输到目标地址,无需CPU的干预。这样可以大大减少了CPU的负担,提高了数据传输的效率。 在使用DMA进行串口数据收发时,我们还可以利用DMA的中断功能,实现接收完成中断或发送完成中断的回调函数。这样我们可以及时地处理接收到的数据或发送完成的状态。 总之,使用STM32F4系列微控制器的串口DMA功能可以实现快速、可靠的数据收发。合理配置和使用DMA通道,结合中断回调函数的实现,可以进一步提高串口数据传输的效率和可靠性。 ### 回答3: STM32F4系列微控制器具有内置的DMA (直接存储器访问) 控制器,可以在串口通信中使用DMA实现数据收发。 在STM32F4中,我们可以使用USART(通用同步/异步收发器)模块来实现串口通信。USART模块具有多个寄存器用于配置和控制串口的工作方式。 首先,需要配置USART模块进行串口通信的初始化设置。例如,可以选择串口的波特率、数据位数、停止位数和校验位等参数。初始化完成后,可以使能USART模块。 然后,可以配置DMA控制器来进行串口数据收发。首先,需要选择合适的DMA通道,并配置其源地址(发送数据的存储器地址)和目的地址(接收数据的存储器地址)。然后,配置DMA的传输大小,即每次传输的字节长度。可以选择单个字节、半字或全字等传输大小。 接下来,需要配置DMA的传输模式。在串口收发中,常用的传输模式为循环模式,即当一次传输完成后,自动重新开始下一次传输。可以选择DMA的循环模式、传输方向(发送或接收)和传输方式(单次传输或连续传输)。 此外,还可以选择DMA的传输触发方式。可以选择硬件触发,即由外部事件触发DMA传输,例如USART的发送或接收完成事件;或者选择软件触发,即由软件控制手动触发DMA传输。 最后,使能USART的发送(TXE)和接收(RXNE)中断,并在中断服务函数中进行数据的处理。当USART发送或接收到数据时,会触发相应的中断并执行中断服务函数。 通过上述的配置和设置,可以实现串口通信中的数据收发操作。使用DMA进行数据传输可以提高系统的效率,减少CPU的负载。同时,可以利用USART的中断功能实时处理收发数据。 需要注意的是,具体的配置方法和步骤可能会因具体的STM32F4系列微控制器型号和开发环境的不同而略有差异,需要参考相应的技术手册和开发工具的文档进行详细设置。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iot 小胡

从未指望过会有人打赏...

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值