关注+星标公众号,及时获取更多技术分享~
作者 | 冰茶奥利奥
微信公众号 | 嵌入式电子创客街
以前在使用stm32F103系列单片机时,若要将串口配置成DMA发送,只需要正确配置DMA通道,然后发送数据时候,给DMA通道的CNDTR寄存器赋以发送长度,再使能DMA即可。
如下代码所示:F103版本
while (DMA_GetCurrDataCounter(DMA1_Channel7));// 检查DMA发送通道内是否还有数据
memcpy(DMASendBuf, Buffer, (ucSend_num > 1024 ? 1024 : ucSend_num));
//DMA发送数据-要先关 设置发送长度 开启DMA
DMA_Cmd(DMA1_Channel7, DISABLE);
DMA1_Channel7->CNDTR = ucSend_num;// 设置发送长度
DMA_Cmd(DMA1_Channel7, ENABLE); // 启动DMA发送
这次在F407上使用,发现像上面一样设置并不能用,DMA只能发送一次,后面直接不发送了。
如下代码所示:F407 错误配置DMA
while (DMA_GetCurrDataCounter(DEBUG_USART_DMA_STREAM));// 检查DMA发送通道内是否还有数据
memcpy((uint8_t *)SendBuff, Buf,(Len > 1024 ? 1024 : Len));
//DMA发送数据 设置发送长度 开启DMA
DMA_Cmd(DEBUG_USART_DMA_STREAM, DISABLE); // 关闭DMA发送
DEBUG_USART_DMA_STREAM->NDTR = Len;
DMA_Cmd(DEBUG_USART_DMA_STREAM, ENABLE); // 启动DMA发送
后来查阅手册,发现串口使用DMA通信的话,官方要求使能DMA发送完成中断,并在中断中清除TC标志位。
如下代码所示:官方手册中要求的DMA发送过程
// 配置DMA
void USART_DMA_Init()
{
//...
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ITConfig(DMA2_Stream7,DMA_IT_TC,ENABLE);
}
// DMA中断
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传输完成标志
}
}
void Send_Data(u8 *Buf, uint16_t Len)
{
//...
//发送函数 此时不用配置DMA TC中断
while (DMA_GetCurrDataCounter(DMA2_Stream7));// 检查DMA发送通道内是否还有数据
memcpy((uint8_t *)SendBuff, Buf,(Len > 1024 ? 1024 : Len));
//DMA发送数据 设置发送长度 开启DMA
DMA_Cmd(DMA2_Stream7, DISABLE); // 关闭DMA发送
DMA2_Stream7->NDTR = Len;
DMA_Cmd(DMA2_Stream7, ENABLE); // 启动DMA发送
}
我认为此举没有必要,便关闭了DMA TC中断,再每次发送前清除TC标志位即可。
如下代码所示:在之前代码基础上增加一条清除DMA TC标志位的指令。
//发送函数 此时不用配置DMA TC中断
while (DMA_GetCurrDataCounter(DMA2_Stream7));// 检查DMA发送通道内是否还有数据
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) /*确保发送结束*/
DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);
if(Buf)
{
memcpy((uint8_t *)Rs485No1DmaSendBuf, Buf,(Len > 1024 ? 1024 : Len));
}
//DMA发送数据 设置发送长度 开启DMA
DMA_Cmd(DMA2_Stream7, DISABLE); // 关闭DMA发送
while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE){} //确保DMA可以被设置
DMA_SetCurrDataCounter(DMA2_Stream7, Len);// 设置发送长度
DMA_Cmd(DMA2_Stream7, ENABLE); // 启动DMA发送
但是发现一个问题,如果不使用DMA TC中断,在DMA在初始化配置时,缓存区大小不能设置为0,即:
DMA_Initstructure1.DMA_BufferSize = 1; //此值随便赋,但是不能为0。
我猜测这可能是芯片的一个BUG,着实有点坑。
以下是Stm32F1手册中关于串口DMA发送的介绍
以下是Stm32F4手册中关于串口DMA发送的介绍
如果您觉得这篇文章帮到了你,请点赞或者留下您的评论,您的鼓励是我前进的动力~
关注博主公众号 “嵌入式电子创客街” 获取更多及时技术分享~
关注+星标公众号,及时获取更多技术分享~