目录
前言:代码包下载
一、串口与DMA部分知识讲解
二、串口空闲IDEL中断
三、串口配置、串口空闲中断配置、DMA配置、中断服务函数
1、串口的GPIO配置
2、串口配置
3、串口空闲中断配置
4、DMA直接存储器访问配置
5、使能空闲中断
6、stm32f4xx_it.c 的配置
7、main.c
8、注意:要清空数组!
9、效果展示
前言:代码包下载
下载方式二:微信公众号:暂不提供
一、串口与DMA部分知识讲解
如果大家有什么问题,欢迎在下面评论交流!
串口部分的详解:梳理STM32F429之通信传输部分---NO.1 串口通讯
DMA直接存储器访问:梳理STM32F429之通信传输部分---NO.2 DMA—直接存储区访问
这里补充一下:串口空闲IDEL中断的一些注意事项:
二、串口空闲IDEL中断
1、串口空闲IDEL中断的作用:
现在有很多数据处理都要用到不定长数据,而单片机串口的RXNE中断一次只能接收一个字节的数据,没有缓冲区,无法接收一帧多个数据,利用串口IDLE空闲中断的方式接收一帧数据,方法如下:
单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用DMA_GetCurrDataCounter();函数计算出本次的数据接受长度,从而进行数据处理。
2、注意事项:避免一直进入空闲中断!
先读取USART_SR,再读取USART_DR。需要注意的是,不能采用库函数USART_ClearFlag()或者USART_ClearItPending()来清除IDEL标志位。
USART_ReceiveData(USART1); 清除标志位
三、串口配置、串口空闲中断配置、DMA配置、中断服务函数
1、串口的GPIO配置
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);
/* 使能 GPIOC 时钟 */
/* GPIO初始化 */
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_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_Init(GPIOC, &GPIO_InitStructure);
2、串口配置
USART_InitTypeDef USART_InitStructure;
/* 连接 PXx 到 USARTx_Tx*/
GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_USART6);
/* 连接 PXx 到 USARTx_Rx*/
GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_USART6);
USART_InitStructure.USART_BaudRate = 115200; //串口波特率
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; /* 硬件流控制:不使用硬件流 */
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx ; /* USART模式控制:同时使能接收和发送 */
USART_InitStructure.USART_Parity = USART_Parity_No; /* 校验位选择:不使用校验 */
USART_InitStructure.USART_StopBits = USART_StopBits_1; /* 停止位:1个停止位 */
USART_InitStructure.USART_WordLength = USART_WordLength_8b; /* 字长(数据位+校验位):8 */
USART_Init(USART6, &USART_InitStructure);
3、串口空闲中断配置
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); /* 嵌套向量中断控制器组选择 */
NVIC_InitStructure.NVIC_IRQChannel = USART6_IRQn; /* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; /* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /* 抢断优先级为1 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; /* 子优先级为1 */
NVIC_Init(&NVIC_InitStructure);
4、DMA直接存储器访问配置
(1)开启DMA时钟、复位DMA(根据下图选择DMA?、数据流和通道)
(2)DMA的结构体配置
说明:
- (uint32_t) 是强制转换的意思,因为DMA结构体需要的是32位无符号类型数据;
- &USART6->DR:串口数据寄存器地址(此处需要填数据传来的地址即下图的寄存器,USART_DR寄存器相对于USART6偏移了0x04)
(3)清除DMA所有标志
DMA_ClearFlag(DMA2_Stream1,DMA_FLAG_TCIF5); DMA2 通道5 数据流1
DMA_ITConfig(DMA2_Stream1, DMA_IT_TE, ENABLE); DMA2 数据流1 传输错误使能中断
(4)开启串口DMA接收
USART_DMACmd(USART6, USART_DMAReq_Rx, ENABLE);
(5)使能DMA
DMA_Cmd (DMA2_Stream1,ENABLE);
DMA_InitTypeDef DMA_InitStructure;
// 开启DMA时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
// DMA复位
DMA_DeInit(DMA2_Stream1);
// 设置DMA通道
DMA_InitStructure.DMA_Channel = DMA_Channel_5;
/*设置DMA源:串口数据寄存器地址*/
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART6->DR);
// 内存地址(要传输的变量的指针)
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RevBuff;
// 方向:从外设到内存
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
// 传输大小
DMA_InitStructure.DMA_BufferSize = (uint32_t)5000;
// 外设地址不增
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模式,一次或者循环模式
//DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
// 优先级:中
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
// 禁止内存到内存的传输
/*禁用FIFO*/
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
/*存储器突发传输 1个节拍*/
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
/*外设突发传输 1个节拍*/
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
/*配置DMA2的数据流7*/
DMA_Init(DMA2_Stream1, &DMA_InitStructure);
// 清除DMA所有标志
DMA_ClearFlag(DMA2_Stream1,DMA_FLAG_TCIF2);
DMA_ITConfig(DMA2_Stream1, DMA_IT_TE, ENABLE);
// 开启串口DMA接收
USART_DMACmd(USART6, USART_DMAReq_Rx, ENABLE);
// 使能DMA
DMA_Cmd (DMA2_Stream1,ENABLE);
5、使能空闲中断
USART_IT_IDLE和USART_IT_RXNE区别
当接收到1个字节,会产生USART_IT_RXNE中断
当接收到一帧数据,就会产生USART_IT_IDLE中断
// 开启 串口空闲 IDEL 中断
USART_ITConfig(USART6, USART_IT_IDLE, ENABLE);
// 使能串口
USART_Cmd(USART6, ENABLE);
6、stm32f4xx_it.c 的配置
#include "stm32f4xx_it.h"
#include "bsp_usart_dma.h"
int Flag=0;
void USART6_IRQHandler(void)
{
if(USART_GetITStatus(USART6,USART_IT_IDLE)!=RESET)
{
USART_ReceiveData(USART6); /* 清除标志位 */
Uart_DMA_Rx_Data();
Flag=1;
}
}
7、main.c
/**
******************************************************************************
* @file main.c
* @author Sumjes
* @version V1.0
* @date 2020-02-02
* @brief 串口接发测试,串口接收到数据后马上回传。
******************************************************************************
* @attention
*
* 实验平台: STM32 F429 开发板
* 公众号 : Tech云
*
******************************************************************************
*/
#include "stm32f4xx.h"
#include "bsp_usart_dma.h"
/* 标准库头文件 */
#include <string.h>
extern int Flag;
extern char RevBuff[5000];
int main(void)
{
/*初始化USART6 配置模式为 115200 8-N-1,中断接收*/
usart6_init();
/* 发送一个字符串 */
printf("这是一个串口DMA空闲中断接收回显实验\r\n");
while(1)
{
if(Flag)
{
printf("接收到数据,回发==|%s|==\r\n",RevBuff);
memset(RevBuff,0,5000);/* 清零 */
Flag=0;
}
}
}
/*********************************************END OF FILE***************************************/
8、注意:要清空数组!
memset(RevBuff,0,5000);/* 清零 */
9、效果展示