二十、串口 DMA 接收和中断接收
初始化串口
GPIO 配置
首先配置 GPIOA 的 9 号和 10 号引脚作为 USART1 的 TX(发送)和 RX(接收)功能引脚,并设置为复用功能模式,速度为 100MHz,推挽输出,上拉输入。
GPIO_InitTypeDef GPIO_InitStructure; // GPIO结构体
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟
//串口1对应引脚复用映射
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1
//USART1端口配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_9; //GPIOA10与GPIOA9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10
2
3
4
5
6
7
8
9
10
11
12
13
14
15
串口配置
设置 USART1 的波特率、字长(8 位数据格式)、停止位(1 个停止位)、奇偶校验位(无)、硬件流控(无)以及工作模式(收发模式)。
USART_InitTypeDef USART_InitStructure; //串口结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //使能USART1时钟
//USART1 初始化设置
USART_InitStructure.USART_BaudRate = __baud; //波特率设置
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(USART1, &USART_InitStructure); //初始化串口1
2
3
4
5
6
7
8
9
10
11
12
__baud:是形参,方便更改波特率。
中断配置
配置 USART1 的中断优先级,并使能中断。同时,配置 DMA2_Stream5(DMA 接收)和 DMA2_Stream7(DMA 发送)的中断和优先级。
NVIC_InitTypeDef NVIC_InitStructure; // 中断结构体
// 串口中断
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority =1; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
// DMA中断
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream5_IRQn; //嵌套通道为DMA2_Stream5_IRQn
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级为 2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //响应优先级为 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //通道中断使能
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn ;//串口1发送中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); //开启串口空闲中断
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); // 开启串口DMA接收
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); // 开启串口DMA接收
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
DMA 接收和发送配置
开启 DMA 时钟,分别对 DMA2_Stream5 和 DMA2_Stream7 进行配置,包括选择通道、设置外设和内存地址、数据传输方向、数据大小、增量模式、传输模式等。最后,使能 DMA 通道。
/* 配置串口DMA接收*/
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); // 开启DMA时钟
DMA_DeInit(DMA2_Stream5);
DMA_InitStructure.DMA_Channel = DMA_Channel_4; //通道选择
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; //DMA外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DMA_USART1_RX_BUF; //DMA 存储器0地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //存储器到外设模式
DMA_InitStructure.DMA_BufferSize = USART_MAX_LEN; //数据传输量
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_VeryHigh; //高等优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //不开启FIFO模式
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; //FIFO阈值
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输
DMA_Init(DMA2_Stream5, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream5, ENABLE); //使能DMA2_Stream5通道
DMA_DeInit(DMA2_Stream7); //初始化DMA Stream
while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE);//等待DMA可配置
/* 配置DMA2 Stream7,USART1发送 */
DMA_InitStructure.DMA_Channel = DMA_Channel_4; //通道选择
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //DMA外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)DMA_USART1_TX_BUF; //DMA 存储器0地址
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //存储器到外设模式
DMA_InitStructure.DMA_BufferSize = USART_MAX_LEN; //数据传输量
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_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输
DMA_Init(DMA2_Stream7, &DMA_InitStructure); //初始化DMA Stream7
DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE); //DMA2传输完成中断
DMA_Cmd(DMA2_Stream7, DISABLE); //不使能
USART_Cmd(USART1, ENABLE); //使能串口1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
串口中断服务程序
当 USART1 空闲中断发生时,表示接收到一帧数据完成,此时通过 DMA 获取接收到的数据长度,并将接收到的数据通过 DMA 发送出去(回显功能)。然后,重新设置 DMA 接收的数据长度并使能 DMA 接收。
void USART1_IRQHandler(void) //串口1中断服务程序
{
if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET) //空闲中断触发
{
usart1_recv_end_flag = 1; // 接受完成标志位置1
DMA_Cmd(DMA2_Stream5, DISABLE); /* 暂时关闭dma,数据尚未处理 */
usart1_rx_len = USART_MAX_LEN - DMA_GetCurrDataCounter(DMA2_Stream5);/* 获取接收到的数据长度 单位为字节*/
DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5); /* 清DMA标志位 */
DMA_USART1_Send(DMA_USART1_RX_BUF, usart1_rx_len); // 将接收的数据回显
DMA_SetCurrDataCounter(DMA2_Stream5,USART_MAX_LEN); /* 重新赋值计数值,必须大于等于最大可能接收到的数据帧数目 */
DMA_Cmd(DMA2_Stream5, ENABLE); /*打开DMA*/
USART_ReceiveData(USART1); //清除空闲中断标志位(接收函数有清标志位的作用)
}
if(USART_GetFlagStatus(USART1,USART_IT_TXE)==RESET) //串口发送完成
{
USART_ITConfig(USART1,USART_IT_TC,DISABLE);
usart1_rx_len = 0;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
DMA 发送中断服务程序
当 DMA2_Stream7 传输完成中断发生时,清除传输完成标志,并关闭 DMA2_Stream7 的使能,然后使能 USART1 的发送完成中断。
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); //关闭使能
USART_ITConfig(USART1,USART_IT_TC,ENABLE); //打开串口发送完成中断
}
}
2
3
4
5
6
7
8
9
10
发送函数
将要发送的数据复制到 DMA_USART1_TX_BUF 缓冲区,设置 DMA 传输的数据长度,并使能 DMA2_Stream7 开始数据传输。
void DMA_USART1_Send(u8 *data,u16 size)
{
memcpy(DMA_USART1_TX_BUF,data,size); //复制数据到DMA发送缓存区
while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE); //确保DMA可以被设置
DMA_SetCurrDataCounter(DMA2_Stream7,size); //设置数据传输长度
DMA_Cmd(DMA2_Stream7,ENABLE); //打开DMA数据流,开始发送
}
2
3
4
5
6
7
实验现象
关于这一章节的代码,在立创·梁山派·天空星 STM32F407VET6 开发板资料/第 03 章软件资料/代码例程/里面的 。
这一章节的代码
在开发板介绍百度网盘链接中:立创·梁山派·天空星STM32F407VET6开发板资料/第03章软件资料/代码例程/010 串口中断DMA接收二合一。
烧写我们的代码之后,在串口助手中发送对应的数据,将会在串口助手中显示出来。