使用这个芯片的串口3进行dma接收的时候发现一个问题。
现象:一旦接收到数据,便会一直进入串口3的中断回调handle函数(我使用的空闲中断,理论上不应该这样),主函数一条都不会执行。
发现过程:在USART3_IRQHandler里面设置串口打印步骤,如下图
图中的printf就是用来打印函数步骤,当收到一个1,串口界面就会显示如下r
如上,一直在中断里面循环,直到我的看门狗生效复位(喂狗函数在主程序,所以我推测循环进入中断后根本不会执行主函数)
后来我查阅了很多,加了一个步骤后就解决了。也就是如下的关键代码
temp = USART3->SR;
temp = USART3->DR;
当我设置一个temp读取串口3的这两个寄存器后,串口回调就正常了。具体原因我也不清楚,希望懂的大佬可以回答一下。
以下是串口3dma收发源码
宏定义
#define USART3_DMA_RX_BUFFER_MAX_LENGTH (255)
#define USART3_DMA_TX_BUFFER_MAX_LENGTH (255)
uint8_t USART3_DMA_RX_Buffer[USART3_DMA_RX_BUFFER_MAX_LENGTH];
uint8_t USART3_DMA_TX_Buffer[USART3_DMA_TX_BUFFER_MAX_LENGTH];
#define DEBUG_USART_IRQ USART3_IRQn
#define DEBUG_USART_IRQHandler USART3_IRQHandler
USART_STR MB_USART3;
//该变量对应的结构体
typedef struct USART_STR
{
unsigned char receCount; //接收长度
unsigned char mscomm_buffer[256]; //接收缓存区
unsigned char rec_end_flag; //接收完成标志
} USART_STR;
1、串口初始化以及中断初始化
void USART3_Configuration(void);
void USART3_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// config USART3 clock
RCC_APB1PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB1Periph_USART3, ENABLE); // 使能 GPIOB 和 USART3 的时钟
// Configure USART3 Tx (PB.10) as alternate function push-pull
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // 设置 GPIOB 的 Pin 10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 设置 GPIOB 的模式为复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置 GPIOB 的输出速度为 50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); // 使用上述配置初始化 GPIOB
// Configure USART3 Rx (PB.11) as input floating
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; // 设置 GPIOB 的 Pin 11
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 设置 GPIOB 的模式为浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure); // 使用上述配置初始化 GPIOB
// USART3 mode config
USART_InitStructure.USART_BaudRate = 115200; // 设置 USART 的波特率为 115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 设置 USART 的数据位长度为 8 位
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 设置 USART 的停止位为 1 位
USART_InitStructure.USART_Parity = USART_Parity_No; // 设置 USART 的奇偶校验位为无校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 设置 USART 的硬件流控制为无流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 设置 USART 的工作模式为同时支持接收和发送
USART_Init(USART3, &USART_InitStructure); // 使用上述配置初始化 USART3 控制器
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置串口的 NVIC设置*/
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ; // 设置调试串口的中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 设置中断抢占优先级为 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 设置中断子优先级为 0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道
NVIC_Init(&NVIC_InitStructure); // 使用上述配置初始化中断控制器
//CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去如下语句解决第1个字节无法正确发送出去的问题
//USART_ClearFlag(UART4, USART_FLAG_TC); //清发送完成标志,Transmission Complete flag
//USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);
//USART_Cmd(UART4, ENABLE);
USART_Cmd(USART3, ENABLE);
USART_ClearFlag(USART3, USART_FLAG_TC); //清除发送完成标志
while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);//等待空闲帧发送完成后再清零发送完成标志(警告:如果不使能USART_Mode_Tx,会导致单片机在这里死机)
USART_ClearFlag(USART3, USART_FLAG_TC); //清除发送完成标志
USART_ITConfig(USART3, USART_IT_RXNE, DISABLE); // 禁用接收中断
USART_ITConfig(USART3, USART_IT_TXE, DISABLE); // 禁用发送中断
USART_ITConfig(USART3, USART_IT_IDLE, ENABLE); // 打开接收空闲中断
USART_ITConfig(USART3, USART_IT_TC, ENABLE); // 打开发送完成中断
USART_DMACmd(USART3, USART_DMAReq_Tx, ENABLE); // 使能发送DMA请求
USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE); // 使能接收DMA请求
//错误:
//1、以下情况初始化STM32F013VC单片机串口3,则单片机会死机
// USART_ClearFlag(USART3, USART_FLAG_TC); //清除发送完成标志
// while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET); //等待空闲帧发送完成后再清零发送完成标志(警告:如果不使能USART_Mode_Tx,会导致单片机在这里死机)
// USART_ClearFlag(USART3, USART_FLAG_TC); //清除发送完成标志
// USART_Cmd(USART1, ENABLE);
//正确:
//2、以下情况初始化STM32F013VC单片机串口3,则正常运行
// USART_Cmd(USART3, ENABLE);
// USART_ClearFlag(USART3, USART_FLAG_TC); //清除发送完成标志
// while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET); //等待空闲帧发送完成后再清零发送完成标志(警告:如果不使能USART_Mode_Tx,会导致单片机在这里死机)
// USART_ClearFlag(USART3, USART_FLAG_TC); //清除发送完成标志
//总结:
//3、因此USART_Cmd(USART3, ENABLE);必须放在while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);前面执行
}
2、DMA初始化
2.1、TX初始化
void USART3_DMA_Tx_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE); //DMA1时钟使能
DMA_DeInit(DMA1_Channel2);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART3->DR; //DMA外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART3_DMA_TX_Buffer; //发送缓存指针
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //传输方向
DMA_InitStructure.DMA_BufferSize = USART3_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; //外设数据宽度:BYTE
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //内存数据宽度:BYTE
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //循环模式:否//(注:DMA_Mode_Normal为正常模式,DMA_Mode_Circular为循环模式)
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //优先级:高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //内存:内存(都)
DMA_Init(DMA1_Channel2 , &DMA_InitStructure); //初始化DMA1_Channel2
DMA_ClearFlag(DMA1_FLAG_GL2);
DMA_Cmd(DMA1_Channel2 , DISABLE); //开启DMA传输
}
2.2 RX初始化
//USART3 RX DMA初始化程序
void USART3_DMA_Rx_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE); //DMA1时钟使能
DMA_DeInit(DMA1_Channel3); // DMA1 通道3 复位为默认值
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART3->DR); // DMA 的外设基地址为 USART3 的数据寄存器地址。
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART3_DMA_RX_Buffer; //接收缓存指针
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //设置数据传输方向为从外设到存储器。
DMA_InitStructure.DMA_BufferSize = USART3_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; //设置外设数据和存储器数据的大小为字节。
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //设置外设数据和存储器数据的大小为字节。
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //(注:DMA_Mode_Normal为正常模式,DMA_Mode_Circular为循环模式)
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //设置 DMA 的优先级为非常高。
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁用存储器到存储器的传输。
DMA_Init(DMA1_Channel3, &DMA_InitStructure); //初始化 DMA1 通道3
DMA_ClearFlag(DMA1_FLAG_GL3); //清除 DMA 标志位
DMA_Cmd(DMA1_Channel3 , ENABLE); //使能 DMA1 通道3
}
3、串口3DMA发送
//USART3 启动DMA发送初始化程序
void USART3_DMA_Begin_Send(uint8_t *send_buffer , uint16_t nSendBytes)
{
if (nSendBytes < USART3_DMA_TX_BUFFER_MAX_LENGTH)
{
memcpy(USART3_DMA_TX_Buffer , send_buffer , nSendBytes);
DMA_Cmd(DMA1_Channel2 , DISABLE); //关闭DMA传输
DMA_SetCurrDataCounter(DMA1_Channel2 , nSendBytes); //数据传输量
DMA_Cmd(DMA1_Channel2 , ENABLE); //开启DMA传输
}
}
串口3中断回调函数
void USART3_IRQHandler(void)
{
int temp = 0;
if (USART_GetITStatus(USART3, USART_IT_IDLE) != RESET) //接收处理
{
temp = USART3->SR;
temp = USART3->DR;//读数值,防止死机
// 清除空闲中断标志位
USART_ClearITPendingBit(USART3, USART_IT_IDLE);
// 关闭 DMA1 通道3
DMA_Cmd(DMA1_Channel3, DISABLE);
// 获取接收到的数据长度
uint16_t receivedLength = USART3_DMA_RX_BUFFER_MAX_LENGTH - DMA_GetCurrDataCounter(DMA1_Channel3);
if (receivedLength > 0)
{
// 执行接收数据的处理操作
memcpy(MB_USART3.mscomm_buffer, USART3_DMA_RX_Buffer, receivedLength);
MB_USART3.receCount = receivedLength;
//printf("U3Res: ->%s<-\r\n", MB_USART3.mscomm_buffer);
MB_USART3.rec_end_flag = 1;
}
// 重置 DMA1 通道3 的数据计数器
DMA_SetCurrDataCounter(DMA1_Channel3, USART3_DMA_RX_BUFFER_MAX_LENGTH);
// 启用 DMA1 通道3
DMA_Cmd(DMA1_Channel3, ENABLE);
}else if (USART_GetITStatus(USART3,USART_IT_TC)!= RESET) //发送处理
{
USART_ClearITPendingBit(USART3, USART_IT_TC);
DMA_ClearFlag(DMA1_FLAG_GL2 | DMA1_FLAG_TC2 | DMA1_FLAG_HT2 | DMA1_FLAG_TE2);
DMA_SetCurrDataCounter(DMA1_Channel2 , 0);
}else if(USART_GetITStatus(USART3, USART_IT_PE | USART_IT_FE | USART_IT_NE |USART_IT_ORE) != RESET)//出错清除
{
USART_ClearITPendingBit(USART3, USART_IT_PE | USART_IT_FE | USART_IT_NE | USART_IT_ORE);
//读SR后读DR清除ORE 以前以为清除中断就可以了,芯片资料要求需要读数据寄存器的值,不然出错后也会死机
temp = USART3->SR;
temp = USART3->DR;
}
temp=temp;//避免编译器警告
}
主程序示范
main.c
extern USART_STR MB_USART3;
int main()
{
USART3_Configuration();
USART3_DMA_Tx_Configuration();
USART3_DMA_Rx_Configuration();
while(1)
{
if(MB_USART3.rec_end_flag == 1)//如果接收到了标志位
{
USART3_DMA_Begin_Send("Rec-OK\r\n\0",strlen("Rec-OK\r\n\0")+1);//串口3返回确认
printf("MAIN_PUSH:->%s<-\r\n",MB_USART3.mscomm_buffer);//将数据打印给串口1
MB_USART3.rec_end_flag = 0; //清除标志位
}
}
}
代码中涉及到的pribtf为串口1输出调试重定义后的写法。如报错可注释。