使用STM32 F4串口空闲中断和DMA实现数据发送
STM32 F4串口空闲中断 + DMA实现数据发送
前言
最近在做 STM32 + ROS车的项目,STM32与ROS之间通信由于数据量大,所以在 STM32端 使用 空闲中断 + DMA 的方案来减轻 CPU 的压力。
文章目录
一、空闲中断
空闲中断顾名思义为空了,闲了,没事了进的中断,在没有数据流的时候会进入进行读取。
在我们串口进行发送时实则为连续发送,两个字节之间时间间隔非常小,这时串口接收中断未达到空闲的状态,当一组数据发送完成后会进行一系列运行后再次发送,这段时间内没有数据再次接收,这时会进入空闲中断。
这个间隔怎么定义呢?在空闲总线上,空闲的定义是:总线上在一个字节的时间内没有再接收到数据。
综上所诉:空闲中断在总线上没有信息的时候会进入中断,集中处理在这一段时间接收到的数据。在有数据的时候不会进入中断。
优点:通常使用的 USART_IT_RXNE接收中断配置再有数据进入时就会切换到串口进行处理,每次处理一个字节。频繁的进入中断,大量的占用CPU带宽来进行处理,使用空闲中断USART_IT_IDLE后,在接收数据时不进入中断,在接收完一组数据时,进入中断,同时处理这一组数据,大大节省了时间,降低了CPU占用时间,提高了整体性能。代码与正常串口配置大致无异,在最后中断配置处将接收中断修改为空闲中断即可。之后代码中会列出。
二、DMA
DMA 简介:
直接存储器访问 (DMA) 用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据。这样节省的 CPU 资源可供其它操作使用。
DMA分为DMA1与DMA2,
DMA1有两种模式,分为:
1、外设->存储器
2、存储器->外设
DMA2有三种模式,分为:
1、外设->存储器
2、存储器->外设
3、存储器到存储器
之所以有差异在于DMA1控制器AHB外设端口不连接到中线矩阵,因此,仅 DMA2 数据流能够执行存储器到 存储器的传输。
FIFO介绍:
DMA接收有两种模式,一种为直接模式,另一种为FIFO模式
FIFO为缓存区,大小为32位(16个字节,8个半字,4个字),独立的源和目标传输宽度(字节、半字、字),在单位上分为三种读取FIFO的方式,为(字节、半字、字)每个数据流都有一个独立的 4 字 FIFO,阈值级别可由软件配置为 1/4、1/2、3/4 或满。
三、代码部分
1、串口配置
使用正常的串口配置即可
/*******************************************************************************
* Function Name : USART2_Configuration
* Description : 串口二配置
*******************************************************************************/
void USART2_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO初始化结构体
USART_InitTypeDef USART_InitStructure; //定义USART初始化结构体
/*时钟配置不需要配置AFIO 只要配置为复用功能即可*/
// GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_USART2); // tx
// GPIO_PinAFConfig(GPIOD, GPIO_PinSource6, GPIO_AF_USART2); // rx
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); // tx
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); // rx
// GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5 | GPIO_Pin_6; //设置初始化GPIO为PIN5、PIN6
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2 | GPIO_Pin_3; //设置初始化GPIO为PIN5、PIN6
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz; //设置GPIO的速度为100MHz
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF; //设置GPIO模式为复用模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //设置GPIO输出类型为推挽输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //设置GPIO为连接上拉
// GPIO_Init(GPIOD,&GPIO_InitStructure); //初始化
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化
USART_InitStructure.USART_BaudRate=115200; //设置USART的波特率为115200
USART_InitStructure.USART_Parity=USART_Parity_No; //设置USART的校验位为None
USART_InitStructure.USART_WordLength=USART_WordLength_8b; //设置USART的数据位为8bit
USART_InitStructure.USART_StopBits=USART_StopBits_1; //设置USART的停止位为1
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //失能硬件流控制
USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; //设置USART的模式为发送接收模式
USART_Init(USART2, &USART_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //开启接收中断 若要使用 DMA 在 DMA 接收中有关闭中断,开启空闲中断
USART_Cmd(USART2, ENABLE);
}
2、DMA配置
DMA配置时,使用串口的空闲中断,将之前的接收中断失能,使能串口的空闲中断
宏定义:
/* 发送与接收缓存数组 */
#define UART2_DMA_Rx_LEN 100 /* 接收DMA缓存长度配置 */
#define UART2_DMA_Tx_LEN 100 /* 发送DMA缓存长度配置 */
uint8_t UART2_DMA_Rx_Buff[UART2_DMA_Rx_LEN];
uint8_t UART2_DMA_Tx_Buff[UART2_DMA_Tx_LEN];
配置DMA代码:
/*******************************************************************************
* Function Name : USART2_DMA_RX_Confige
* Description : 串口二 DMA 接收配置
*******************************************************************************/
void USART2_DMA_RX_Confige(void)
{
/* 定义DMA初始化结构体 */
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA1_Stream5); //复位DMA状态 清空数据
while(DMA_GetCmdStatus(DMA1_Stream5) != 0) //判断流内数据是否清零
{
printf("DMA1_Stream5 != 0\r\n");
}
DMA_InitStructure.DMA_Channel = DMA_Channel_4; //DMA通道配置
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART2->DR); //DMA外设基地址 源数据地址
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)UART2_DMA_Rx_Buff; //DMA内存基地址 目标地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //DMA数据传输方向 外设->内存 外设为源,内存为目的区
DMA_InitStructure.DMA_BufferSize = UART2_DMA_Rx_LEN; //DMA通道的缓存的大小 数据数目
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //设置DMA的外设递增模式 外设地址寄存器不递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //设置DMA的内存递增模式 内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据字大小(32位),数据宽度为8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //内存数据字大小(32位),数据宽度为8位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //正常工作模式 一次传输模式
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //设置DMA数据流优先级 优先级为最高
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //禁用FIFO模式
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; //指定了FIFO阈值水平 禁用FIFO模式
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //指定的Burst转移配置内存传输 单次模式
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //指定的Burst转移配置外围转移 单次模式
DMA_Init(DMA1_Stream5, &DMA_InitStructure); //配置DMA1的通道 流5
//中断配置
USART_ITConfig(USART2,USART_IT_RXNE,DISABLE); //关闭串口接受中断
USART_ITConfig(USART2,USART_IT_IDLE,ENABLE); //开启串口空闲中断
/* 开启DMA接收 */
DMA_Cmd(DMA1_Stream5,ENABLE); //使能DMA接收通道
USART_DMACmd(USART2,USART_DMAReq_Rx,ENABLE); //采用DMA方式接收
}
配置中断接收代码
uint8_t buf[UART2_DMA_Rx_LEN];
int FlagSize;
void USART2_IRQHandler(void)
{
//接收完成中断处理
if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)
{
USART2->SR;
USART2->DR; //清USART_IT_IDLE标志 不进行此步骤会一直进中断
/*************************************** 开始接收准备 *****************************************/
DMA_Cmd(DMA1_Stream5,DISABLE); /* 关闭DMA */
DMA_ClearFlag(DMA1_Stream5,DMA_FLAG_TCIF5); /* 清除标志位 */
/***************************************** 接收算法 ******************************************/
// UsartReceive(&ReceiveROS);
FlagSize = UART2_DMA_Rx_LEN - DMA_GetCurrDataCounter(DMA1_Stream5); /* 获得接收帧帧长 */
memset(buf,0x0,UART2_DMA_Rx_LEN); /* 对接收buff进行清零 */
memcpy(buf,UART2_DMA_Rx_Buff,FlagSize); /* 数据接收 */
/************************************* 开始下一帧接收准备 *************************************/
USART_ClearFlag(USART_Receive, USART_FLAG_IDLE); /* 清除标志位 */
//设置传输数据长度
DMA_SetCurrDataCounter(DMA1_Stream5,UART2_DMA_Rx_LEN);
//打开DMA
DMA_Cmd(DMA1_Stream5,ENABLE);
}
}
测试时可以使用串口助手给串口二发送数据看是否能够成功接收。
以上代码备注都很清晰完善,我就不过多赘述,感谢观看!!!