问一下,我现在是一个byte接收产生一次接收中断 。 但是这样中断进入太频繁,那我打算比方8个bytes接收进入一次中断, 是不是此时我必须要用DMA 的方式,多缓冲器 ?
是的,必须要用DMA的方式
“多缓冲器”是什么意思?每个缓冲器有多大?
如果uart有FIFO加上有超时功能就太好了
ATMEL的ARM7就有,用着很爽,省了我一个定时器
不知STM能不能加上去呢?呵呵
usart1的RX 的dma通道是channle5
现在我用DMA方式来进行USART1的数据接收。
我想请问的是: 我最终接收判断应该是在DMA中断里面做的喽,因为DMA_TC表示DMA传输指定的buffer大小完成了,对吧。
那这样的话,我USART1的接收中断还要打开吗?
我现在测试的代码里面是打开的,但是我在DMA中断DMA1_FLAG_TC5=1的情况下,并没有在RX的buffer中看到我从串口发出来的数据,这是为什么呢?
我这样做有问题吗? : 请看下面代码
DMA中断:=========================
void DMA1_Channel5_IRQHandler(void)
{
if(DMA_GetFlagStatus(DMA1_FLAG_TC5) != RESET)
{
RxCounter = 0;
}
if(DMA_GetFlagStatus(DMA1_FLAG_GL5) != RESET)
{
}
if(DMA_GetFlagStatus(DMA1_FLAG_HT5) != RESET)
{
}
}
USART1中断:=====================
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
/* Read one byte from the receive data register */
RxBuffer1[RxCounter++] = USART_ReceiveData(USART1);
if(RxCounter == NbrOfDataToRead)
{
/* Disable the USART2 Receive interrupt */
//USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
RxCounter = 0;
}
}
}
DMA 初始化: =========================
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer1;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = TxBufferSize1;
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_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
main函数中的功能使能:=================
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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);
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA1_Channel5, ENABLE);
USART_Cmd(USART1, ENABLE);
user manual上写--->
在DMA中断程序里,USART1_CR3寄存器的DMAR位应该被软件清零。
这句如果我DMAR清零了,那么接收时的DMA模式就被禁止了啊。这句没想通。
另外,关于DMA中断函数,我参考了I2C的DMA的中断函数,现在DMA中断这么修改的:
void DMA1_Channel5_IRQHandler(void)
{
u32 i;
if(DMA_GetFlagStatus(DMA1_FLAG_TC5) != RESET)
{
DMA_ClearFlag(DMA1_FLAG_TC5);
//USART1->CR3 = 0<<6;
}
if(DMA_GetFlagStatus(DMA1_FLAG_GL5) != RESET)
{
DMA_ClearFlag( DMA1_FLAG_GL5);
}
if(DMA_GetFlagStatus(DMA1_FLAG_HT5) != RESET)
{
DMA_ClearFlag( DMA1_FLAG_HT5);
}
}
但是有个问题,我向串口第一次发一组数据,会进入DMA中断,当我再次发送的时候,便不再进入DMA中断了,我期间并没有做什么关闭DMA中断功能的代码啊。
当我main中初始化,使能DMA,USART1后,便一直while(1);了,实在是想不通。
麻烦版主赐教一下。
谢谢香主答复
噢,这样,那我就用循环模式,这样比较方便。
但是我还是想请教一下版主,4楼的问题。
好的
现在是这样的: 我USART中断接受本来是一个byte产生一次接收中断。 现在我要用DMA方式来进行USART的接收。 所以我现在启动的USART_DMA_RX的功能,我用的是3210E的开发板,USART1。
例子库的USART DMA INTERRUPT的例子是用的DMA发送功能。 所以我在此基础上稍作修改,改成DMA接收的功能。 我设定的RXBuffer大小是24个字节。
我的问题是:
1)初始化的时候,我还需要使能USART1_RX的中断功能吗? 因为我的理解是:USART1_RX的中断启动应该是不需要了。
因为USART1_DR已经跟DMA关联了,当有了DR,RXNE置位后,这个DR的数据会自动传输到RXBuffer上去,一直到24个字节收满后,DMA的TCIF5会置位,进入DMA中断服务程序,此时我应该在RXBuffer上看到这收到的24个字节,是吗?
但是现在当我发送24个字节,从PC到USART1后,进入了DMA中断,并且是TCIF5=1,但是RXBuffer我看到的都是00,这是为何?
2)另外,昨天我说DMA进了一次中断后,就再也不进,那是因为设置了普通模式,所以今天我改成了循环模式,是可以反复进DMA中断了。 但是此时我看到我的串口助手在连续不停的收到数据,即使我debug在暂停状态。
使用DMA操作USART
关于第1个问题:如果使用DMA方式接收,不需要再使能USART1_RX的中断功能。至于RXBuffer中看到的都是00,请检查DMA初始化是否正确,尤其是设备地址。
关于第2个问题:debug在暂停状态时,PC在连续不停的收到数据,是否是因为你发送很多数据并被PC接收到缓冲区,Debug暂停时PC还没有刷新显示完,耐心等一会儿看看。
我看了下,没有什么设置错误啊
1)
这就是我的DMA初始化================================
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
/* DMA1 Channel4 (triggered by USART1 Tx event) Config */
DMA_DeInit(DMA1_Channel5); // usart1 rx dma use dmachannle5
DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer1; //size is 24
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = RxBufferSize1; //size is 24
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_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
}
DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA1_Channel5, ENABLE);
这是USART1的:=================================
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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);
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
//USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
2)我PC只向USART1发了一次24个字节,但是PC接收到的都是好几万次了,导致我的串口助手崩溃了。并且接收到的都是00
你选的是DMA循环模式,请先在DMA完成中断中计数
例如计数5次后停止DMA传输。如果不停止DMA传输,当然PC端会不断地收到数据了。
香主的意思是在DMA中断出来的时候我要DISABLE DMA 功能?
如果是这样的话,还是要后面再打开DMA功能,那对我来说,只能在DMA中断函数里面去进行ENABLE/DISABLE DMA 了
香主能否看一下我11楼的代码和下面的DMA中断程序
这是我的DMA中断处理
void DMA1_Channel5_IRQHandler(void)
{
u32 i;
if(DMA_GetFlagStatus(DMA1_FLAG_TC5) != RESET)
{
//RxBuffer1[RxCounter++] = USART_ReceiveData(USART1);
/* for(i=0;i<24;i++)
hjh=RxBuffer1;
/* Disable the USART2 Receive interrupt */
//USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);*/
DMA_ClearFlag(DMA1_FLAG_TC5);
//USART1->CR3 = 0<<6;
}
if(DMA_GetFlagStatus(DMA1_FLAG_GL5) != RESET)
{
DMA_ClearFlag( DMA1_FLAG_GL5);
}
if(DMA_GetFlagStatus(DMA1_FLAG_HT5) != RESET)
{
DMA_ClearFlag( DMA1_FLAG_HT5);
}
}
6楼给的文档中已经说明了如何重新初始化DMA通道
我在12楼的建议是针对DMA循环模式。
我就是说的你的12楼的建议
首先我想问一下,我这样理解对不对?
比如现在我PC要发24个字节的数据。 对DMA中断来说,我设置的是全部长度接收完产生一次中断,那么DMA中断应该就进入一次,当我进入这个DMA中断后,if(DMA_GetFlagStatus(DMA1_FLAG_TC5) != RESET) 肯定是1,因为TC5这时候应该是1,那么进入这个if里面,我应该可以在原来设置的RXBuffer中看到接收到的24个字节的数据。
其次,你说DMA完成中断中计数。并且计数值到了后要停止DMA传输,这个停止是不是就是DISABLE DMA ? 如果是的话,那我什么时候去打开呢? 如果DISABLE了,我以后的数据怎么进入? 因为是中断触发的,所以如果要打开,只能DMA中断函数了? 那么这个关闭还有什么意义呢?
我贴出来的代码,希望版主能check一下,我是根据例子代码去修改的USART DMA接收功能。 我这么设置对不对? 如果是对的,为何现在进入DMA中断了,TC5=1了,RXBuffer里面还都是00(我PC发送的数据是1234...)
我现在buffer里面是收到了
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; 这句话改了一改
我现在还是用循环模式,并且DIR改了后,也不会导致之前我说的PC狂接到数据了。 这是我自己的问题,是我不好。
但是现在还有一个问题,就是如果发送超时了,怎么办,我看过论坛里面的一些帖子,版主建议过使用TIM来定时,但问题现在我TIM都已经用掉了,没有空闲TIM, Systick也用掉了。 那我这个超时处理如何处理?
如果设置为循环模式,你会在每个循环周期结束时收到一次
因为没有循环次数控制寄存器,所以你需要在每次处理DMA完成中断中计数,当到达规定的循环次数时,关闭DMA通道,即Disable。如果不用循环模式,则完成一次DMA传输后(在你的设置中是24字节),将收到一次DMA完成中断,同时DMA通道会自动停止,不再循环。
还有你的DMA初始化有问题,既然是DMA接收,为什么是从存储器至设备的DMA传输?总不能把发送的例子直接抄过来就用于接收吧?
香主,我们的理解有点差异
我们来捋一下:
我现在测试的时候是24个字节的长度buffer,循环模式,当我PC发送24个字节,没问题。
当我PC发送3个字节,要发8次进入一次DMA中断。这个没问题,因为长度是24个字节,3个字节没满长度值,所以前几次没进,无所谓。
现在如果我发送25个字节(我的消息长度不是唯一的,所以牵涉到超时的问题),那么肯定由于是循环模式,所以前面的数据会被覆盖。 所以对我来说,没有规定的循环次数。
所以我想知道,超时问题如何解决、? (我看到论坛有说用TIM定时的,但是我TIM现在都占用了。有没有不耗其他资源的方法?)
记得以前讨论过这个问题
你的数据包中总应该有一个字段标识了数据包的长度,你应该把这个字段放在数据包中一个固定的位置,即使最短的数据包也应该有这个字段。
这样STM32端的接收过程需要分解为2个阶段,第1个阶段是用DMA或中断接收包括数据包长度的若干个字节,第2个阶段是用DMA接收剩余的字节。这种方式下,第1个阶段的接收长度始终是已知的,在收到第1个阶段的数据后,第2个阶段的接收长度也是已知了,根本不需要什么超时处理,也不需要使用DMA的循环模式。