STM32F407 USART6串口使用DMA接收不定长数据和DMA中断发送

本文介绍了STM32F407单片机利用DMA进行串口USART6的高效数据传输,包括TX和RX的DMA配置,中断接收和发送处理,以及如何避免中断冲突。通过实例展示了如何初始化DMA通道、设置传输参数和处理中断,确保了数据传输的高效率和单片机资源的有效利用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言    

        使用DMA通信的好处是,不占用单片机资源(不像普通串口中断,发送一个字节触发一次中断,发送100个字节触发100次中断;接收一个字节触发一次中断,接收200个字节触发200次中断),数据接收完毕触发一次DMA中断;发送数据完毕触发一次DMA中断。

        下图是STM32F407单片机DMA通道关系图。

 

#define USART6_DMA_RX_BUFFER_MAX_LENGTH		(255)
#define USART6_DMA_TX_BUFFER_MAX_LENGTH		(255)
uint8_t USART6_DMA_RX_Buffer[USART6_DMA_RX_BUFFER_MAX_LENGTH];
uint8_t USART6_DMA_TX_Buffer[USART6_DMA_TX_BUFFER_MAX_LENGTH];

 1、USART6  TX DMA初始化程序

////////////////////////////////////////////////////////////////////////////////////////////
//是否可以这么认为,USART6如果DMA的TX使用DMA2_Stream6,则USART6如果DMA的RX必须使用DMA2_Stream1
//                 USART6如果DMA的TX使用DMA2_Stream7,则USART6如果DMA的RX必须使用DMA2_Stream2
////////////////////////////////////////////////////////////////////////////////////////////
void USART6_DMA_Tx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;
	
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 , ENABLE);					//DMA2时钟使能
	DMA_DeInit(DMA2_Stream6);
	while (DMA_GetCmdStatus(DMA2_Stream6) != DISABLE);						//等待DMA可配置
	DMA_InitStructure.DMA_Channel = DMA_Channel_5; 							//DMA通道配置
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART6->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)USART6_DMA_TX_Buffer;	//发送缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;					//DMA传输方向:内存--->外设
	DMA_InitStructure.DMA_BufferSize = USART6_DMA_TX_BUFFER_MAX_LENGTH;		//数据传输字节数量
	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_Priority_High
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA2_Stream6 , &DMA_InitStructure);							//初始化DMA Stream
	DMA_Cmd(DMA2_Stream6 , DISABLE); 										//开启DMA传输
}

2、USART6  RX DMA初始化程序

void USART6_DMA_Rx_Configuration(void)
{
	DMA_InitTypeDef  DMA_InitStructure;

	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 , ENABLE);					//DMA2时钟使能
	DMA_DeInit(DMA2_Stream1);
	while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE);						//等待DMA可配置  
	DMA_InitStructure.DMA_Channel = DMA_Channel_5;  						//通道选择
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART6->DR;		//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)USART6_DMA_RX_Buffer;	//接收缓存指针
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ;				//DMA传输方向:外设到存储器模式:外设--->内存
	DMA_InitStructure.DMA_BufferSize = USART6_DMA_RX_BUFFER_MAX_LENGTH;		//缓冲大小 
	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_Priority_VeryHigh
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;				//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;		//外设突发单次传输
	DMA_Init(DMA2_Stream1 , &DMA_InitStructure);							//初始化DMA_Stream5	
	DMA_Cmd(DMA2_Stream1 , ENABLE);  										//开启DMA传输
}

 3、USART6  启动DMA发送初始化程序


void USART6_DMA_Begin_Send(uint8_t *send_buffer , uint16_t nSendCount)
{	
	if (nSendCount < USART6_DMA_TX_BUFFER_MAX_LENGTH)
	{
		memcpy(USART6_DMA_TX_Buffer , send_buffer , nSendCount);
		DMA_Cmd(DMA2_Stream6 , DISABLE);                    //关闭DMA传输
		while (DMA_GetCmdStatus(DMA2_Stream6) != DISABLE);	//确保DMA可以被设置
		DMA_SetCurrDataCounter(DMA2_Stream6 , nSendCount);  //数据传输量
		DMA_Cmd(DMA2_Stream6 , ENABLE);               		//开启DMA传输
	}
}

4、USART6  DMA方式端口初始化程序(包含DMA配置)


void USART6_Configuration(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	
		
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE);		//for USART1 and USART6 

	GPIO_PinAFConfig(GPIOG, GPIO_PinSource14, GPIO_AF_USART6);
	GPIO_PinAFConfig(GPIOG, GPIO_PinSource9, GPIO_AF_USART6);     	

	// 配置 PG14/USART6_TX 为复用功能 
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;	//输出类型为推挽 
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;	//内部上拉电阻使能 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;	//复用模式 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOG, &GPIO_InitStructure);

	// 配置 PG9/USART6_RX 为复用功能 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_Init(GPIOG, &GPIO_InitStructure);

	USART_InitStructure.USART_BaudRate = 115200;
	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(USART6, &USART_InitStructure);
	USART_Cmd(USART6, ENABLE);
		
	USART_ClearFlag(USART6, USART_FLAG_TC); //清除发送完成标志	
	while (USART_GetFlagStatus(USART6, USART_FLAG_TC) == RESET);	//等待空闲帧发送完成后再清零发送完成标志(警告:如果不使能USART_Mode_Tx,会导致单片机在这里死机)
	USART_ClearFlag(USART6, USART_FLAG_TC);	//清除发送完成标志

    USART_ITConfig(USART6, USART_IT_RXNE, DISABLE);				//禁止USART1接收不为空中断
	USART_ITConfig(USART6, USART_IT_TXE, DISABLE);				//禁止USART1发送空中断
	USART_ITConfig(USART6, USART_IT_IDLE, ENABLE);				//开启USART1空闲中断 
	USART_ITConfig(USART6, USART_IT_TC, ENABLE);				//开启USART1传输完成中断 
	
	USART_DMACmd(USART6 ,   USART_DMAReq_Tx,ENABLE);  			//使能串口的DMA发送
	USART_DMACmd(USART6 ,   USART_DMAReq_Rx,ENABLE);  			//使能串口的DMA接收	
}

5、USART6   DMA中断接收和DMA中断发送


void USART6_IRQHandler(void)
{
	int16_t ch;

	
	if (USART_GetITStatus(USART6 , USART_IT_IDLE) != RESET)
	{		
		USART_ClearITPendingBit(USART6 , USART_IT_IDLE);	//必须先清除总线空闲中断标识,然后读一下数据寄存器,DMA接收才会正确(先读SR,然后读DR才能清除空闲中断标识)注意:这句必须要,否则不能够清除中断标志位。
		ch =  USART_ReceiveData(USART6);					//必须先清除总线空闲中断标识,然后读一下数据寄存器,DMA接收才会正确(先读SR,然后读DR才能清除空闲中断标识)注意:这句必须要,否则不能够清除中断标志位。
		
		#ifdef __DEBUG_stm32f407__
			__DEBUG_USART6_IT_IDLE++;
		#endif
		
		DMA_Cmd(DMA2_Stream1 , DISABLE);
		DMA_ClearFlag(DMA2_Stream1 , DMA_FLAG_TCIF1 | DMA_FLAG_FEIF1 | DMA_FLAG_DMEIF1 | DMA_FLAG_TEIF1 | DMA_FLAG_HTIF1);
		ch = USART6_DMA_RX_BUFFER_MAX_LENGTH - DMA_GetCurrDataCounter(DMA2_Stream1);
		if (ch > 0)
		{
			//USART6_Timer_over = TRUE;
			USART6_receCount = ch;			
			WriteBufferTo_ringBuffer(camera_ring , USART6_DMA_RX_Buffer , USART6_receCount);			
		}
		DMA_SetCurrDataCounter(DMA2_Stream1 , USART6_DMA_RX_BUFFER_MAX_LENGTH);
		DMA_Cmd(DMA2_Stream1 , ENABLE);
	}
	
	else if (USART_GetITStatus(USART6 , USART_IT_TC)!= RESET) 
	{
		USART_ClearITPendingBit(USART6 , USART_IT_TC);
		
		#ifdef __DEBUG_stm32f407__
			__DEBUG_USART6_IT_TC++;
		#endif
		

		DMA_ClearFlag(DMA2_Stream6 , DMA_FLAG_TCIF6 | DMA_FLAG_FEIF6 | DMA_FLAG_DMEIF6 | DMA_FLAG_TEIF6 | DMA_FLAG_HTIF6);
		DMA_SetCurrDataCounter(DMA2_Stream6 , 0);
		GPIO_USART6_RS485_RECIVE_enable();
	}	
} 

6、主程序

void main(void)
{
    USART6_Configuration();
	USART6_DMA_Tx_Configuration();
	USART6_DMA_Rx_Configuration();
    
    while (1)
    {
          //在合适的时候调用USART6_DMA_Begin_Send(uint8_t *send_buffer , uint16_t nSendBytes)
          // 通过DMA中断方式将数据发送出去
    }
} 

本程序经过多款产品验证OK

<think>好的,用户需要关于STM32F407使用USART6进行串口通信的配置教程示例代码。首先,我需要回忆STM32F407USART相关资源。STM32F407有多个USART外设,USART6挂在APB2总线上,时钟频率较高,这点需要注意。 接下来,配置步骤通常包括GPIO设置、USART初始化、中断配置(如果需要)以及数据收发函数。用户可能还需要了解如何验证配置是否正确,比如通过回环测试或者发送接收数据。 查看提供的引用,引用1提到USART初始化流程数据发送接收函数的编写,引用2提到外设驱动包括USART,引用3涉及串口协议,引用4是Modbus但可能与USART配置相关。需要结合这些信息来组织回答。 需要确保代码部分使用正确的GPIO引脚,USART6的TXRX对应的是GPIOC的Pin6Pin7。时钟使能部分,USART6在APB2,所以RCC_APB2PeriphClockCmd。GPIO配置为复用推挽输出浮空输入。 初始化结构体USART_InitTypeDef的参数要设置正确:波特率、数据位、停止位、校验、硬件流控制、模式等。例如常见的115200波特率,8位数据,无校验,1停止位。 发送接收函数可以使用轮询方式或者中断。用户可能需要简单的示例,所以先展示轮询方式。中断处理可能在另一个引用中提到,但用户的问题可能不需要深入中断,但可以提到后续步骤。 最后,测试部分需要指导用户如何连接硬件,比如使用USB转TTL模块,并提醒检查引脚连接是否正确,避免硬件问题导致通信失败。同时,引用中的资料可以作为优化深入学习的参考,比如引用1提到的中断处理调试输出,可以引导用户进一步学习。 可能用户的问题还包括如何调试或者处理错误,但当前问题主要集中在配置示例代码,所以保持回答聚焦。需要检查代码语法是否正确,比如使用正确的库函数,如USART_SendData、USART_ReceiveData等,并确保代码结构清晰,注释明确。 总结回答的结构:先介绍步骤,分点说明配置要点,给出代码示例,然后测试方法,最后提供相关问题引用。确保所有数学公式独立公式符合格式要求,但这个问题可能涉及较少数学内容,主要关注代码配置步骤。</think>### STM32F407 USART6 串口通信配置教程 #### 1. 硬件配置要点 - **USART6引脚分配**:STM32F407USART6对应引脚为`PC6(TX)``PC7(RX)`[^2]。 - **时钟使能**:需开启`GPIOC``USART6`的时钟(USART6挂载在APB2总线)。 - **波特率与协议**:需与通信设备保持一致,例如115200波特率、8位数据位、无校验位、1位停止位[^3]。 #### 2. 初始化代码示例 ```c #include "stm32f4xx.h" void USART6_Init(void) { // 1. 使能时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE); // 2. 配置GPIO引脚为复用功能 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // TX=PC6, RX=PC7 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; // 复用模式 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; // 推挽输出 GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // 上拉 GPIO_Init(GPIOC, &GPIO_InitStruct); // 3. 映射GPIO到USART6 GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_USART6); GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_USART6); // 4. USART参数配置 USART_InitTypeDef USART_InitStruct; USART_InitStruct.USART_BaudRate = 115200; USART_InitStruct.USART_WordLength = USART_WordLength_8b; USART_InitStruct.USART_StopBits = USART_StopBits_1; USART_InitStruct.USART_Parity = USART_Parity_No; USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(USART6, &USART_InitStruct); // 5. 使能USART6 USART_Cmd(USART6, ENABLE); } // 发送单个字符(轮询方式) void USART6_SendChar(char c) { while (!USART_GetFlagStatus(USART6, USART_FLAG_TXE)); // 等待发送缓冲区空 USART_SendData(USART6, c); } // 接收字符(轮询方式) char USART6_ReceiveChar(void) { while (!USART_GetFlagStatus(USART6, USART_FLAG_RXNE)); // 等待接收数据 return USART_ReceiveData(USART6); } ``` #### 3. 测试方法 1. **硬件连接**:通过USB转TTL模块连接PC6(TX)PC7(RX)到PC,注意交叉连接(MCU的TX接模块的RX)。 2. **回环测试**:将`PC6`与`PC7`短接,发送数据后检查是否接收相同内容。 3. **串口助手验证**:使用PC端工具(如Putty)发送数据,观察MCU的响应[^1]。 #### 4. 优化方向 - **中断接收**:通过配置USART6中断实现非阻塞接收(参考NVIC设置与中断服务函数)。 - **DMA传输**:大块数据使用DMA降低CPU负载[^2]。 - **协议封装**:可扩展为Modbus等工业协议实现[^4]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值