打印

usart1实现DMA出现的接收问题

[复制链接]
12834|24
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
humanking7|  楼主 | 2013-3-6 17:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
小弟刚上手stm32不久,我现在搞了一个用usart1实现了DMA的发送和接收功能。已经成功了,但是我设置的是一次接收32

个字符,如果接收的超过或者少于32个字符,就会出现问题,就无法接收了(我们实现的是用ZigBee无线传输,一次定义32

个字符为一个数据包,但是就怕zigbee的接收的问题,使得DMA无法收到定义好的32个字符,这样就会无法完成后续的任务



我的思路是这样的:
1.先配置USART1的时钟以及相应的GPIO,初始化USART1,使能USART1;
2.0使能DMA时钟
2.1配置DMA发送:
----配置DMA发送中断(DMA1_Channel4)
----DMA发送设置,DMA_BufferSize设为32,都是8bit(DMA_PeripheralDataSize_Byte,DMA_MemoryDataSize_Byte),DMA_Mode为循环模式,然后:
        DMA_Cmd (DMA1_Channel4,ENABLE);        
        DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
2.2配置DMA接收:
----配置DMA接收中断(DMA1_Channel5)
----DMA接收设置,DMA_BufferSize设为32,都是8bit(DMA_PeripheralDataSize_Byte,DMA_MemoryDataSize_Byte),DMA_Mode为循环模式,然后:
        DMA_Cmd (DMA1_Channel5,ENABLE);        
        DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);
2.3串口向 DMA发出请求(先让DMA接收,接收到了在发送)
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
3.1在DMA接收中断函数中(void DMA1_Channel5_IRQHandler(void))
----如果接收完成 //if(DMA_GetFlagStatus(DMA1_FLAG_TC5)==SET)
----就先给一个全局变量flag=1(为了在main函数中的循环里识别)
----再关闭串口的DMA接收请求  //USART_DMACmd(USART1, USART_DMAReq_Rx, DISABLE);
----清除标志        //DMA_ClearFlag(DMA1_FLAG_TC5);
3.2在main函数的while循环中 如果发现flag==1,说明接收完成,可以传想要传输的数据了;
----于是,打开串口的DMA发送请求  //USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);        
----同时flag=0
3.3在DMA发送中断函数中(void DMA1_Channel4_IRQHandler(void))
----如果发送完成  //if(DMA_GetFlagStatus(DMA1_FLAG_TC4)==SET)
----就关闭串口的DMA发送请求,再打开串口的DMA接收请求:
        USART_DMACmd(USART1, USART_DMAReq_Tx, DISABLE);
        USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
----清除标志        //DMA_ClearFlag(DMA1_FLAG_TC4);
//////////////////////////////////////////////////////////////////////////////////////////////
这就是整个函数,函数实现的很好,接了32个字符(PC给stm32 发送32个字符),马上就发回32个字符,看似已经ok,但是遇到两种蛋疼的情况:

1.pc给stm32 发送的字符少于32个;
2.发送的多于32个;
这样都会导致,stm32无法在继续接收和发送字符了,即使我在向stm32发送正确的32个字符,必须复位才行,这样的话,我们的stm32就无法接收后续的数据了,这怎么办,我想了让它在先关闭串口的DMA请求,在打开,可惜还是不行,请求高人解答。
希望高人谈一下引起这种情况的原因,以及详细的解答方案,最好能有关键性的代码和解释。
谢谢。
沙发
humanking7|  楼主 | 2013-3-6 17:33 | 只看该作者
感觉,stm32接收一旦出错,就无法继续好好工作了,作为被动的接收者,这样如何实现解决这种情况

使用特权

评论回复
板凳
cjhk| | 2013-3-6 21:13 | 只看该作者
不是很了解   刚刚开始了解stm芯片   不是很了解   帮你顶一个   呵呵

使用特权

评论回复
地板
humanking7|  楼主 | 2013-3-6 23:18 | 只看该作者
谢谢你,纠结死了,dma这么好,就是不会解决这个问题,没有鲁棒性的数据操作宁愿舍弃,难道一定要用中断做吗,希望能有这方面的专家指导....

使用特权

评论回复
5
pybieku| | 2013-3-7 12:17 | 只看该作者
      如果你已经知道USART接收的最大数据包(以字节为单位)为N, 那可以设定DMA最大接收数量为M,M>N,要留有一定的空余。然后使能USART的接收完成标志(被动接收情况下个人感觉最好的方法是总线空闲中断!!!),这样在完成一个完整数据包的接收后就可以把包搬走,或是进行校验等环节。当然,如果RAM空间足够,可以考虑使用双向循环链表完成数据接收,然后设定完成标志,在其他程序中进行解包操作。
      这样的接收效率很高,而且不浪费CPU时间。代码搜索一下吧,很多实现方法的,我已经按照上面的方法实现了两个USART同时DMA接收发送,很方便,由于公司规定,不能在这里公开代码了,见谅
   

使用特权

评论回复
6
humanking7|  楼主 | 2013-3-8 23:54 | 只看该作者
pybieku 发表于 2013-3-7 12:17
如果你已经知道USART接收的最大数据包(以字节为单位)为N, 那可以设定DMA最大接收数量为M,M>N,要 ...

赞一个,我现在用的是中断解决的,确实是无奈之举,我也关注过之类的帖子,但是我的问题是,dma的初始化都搞不定,我希望您给我说一下如何DMA重新初始化接收,我自己初始化了,但是都失败,希望您能指导一下

使用特权

评论回复
7
明月小厨| | 2013-3-9 03:19 | 只看该作者
DMA比较死,最好是固定长度的数据;不然,DMA也不知道自己的工作完成了没有;
有多少数据量多少也有些规律的吧;如4,8,16个,32个;等等;
你可以设循环模式为最短的,如4个;这样,只要有4个数据,就通知你一下;你去处理;

使用特权

评论回复
8
ABCDELF| | 2013-3-9 15:47 | 只看该作者
用DMA_CNDTRx控制每次传输的数据个数
把数据量控制为32个

使用特权

评论回复
9
humanking7|  楼主 | 2013-3-10 00:24 | 只看该作者
明月小厨 发表于 2013-3-9 03:19
DMA比较死,最好是固定长度的数据;不然,DMA也不知道自己的工作完成了没有;
有多少数据量多少也有些规律的吧; ...

这样太被动了,DMA的中断标志我总感觉不够强大

使用特权

评论回复
10
humanking7|  楼主 | 2013-3-10 00:27 | 只看该作者
ABCDELF 发表于 2013-3-9 15:47
用DMA_CNDTRx控制每次传输的数据个数
把数据量控制为32个

我就是这么设置的,关键是错误之后如何初始化dma,将dma的指针置为起始状态

使用特权

评论回复
11
明月小厨| | 2013-3-10 06:07 | 只看该作者
关于初始化并不困难;
1.DMA通道的初始化例程到处都是;都一样;
2.外设的DMA使能(收,发)都一样,一个函数解决;
3.关键点,先后的顺序是有要求的;

使用特权

评论回复
12
明月小厨| | 2013-3-10 06:24 | 只看该作者
接收数据为例;
1.这个比较简单,应该是先配置好DMA,然后配置好接收外设,并使能外设DMA;
外设单元发送请求,DMA单元应答,解除外设请求(清外设请求标志位),然后从外设传数据到DMA;

2.发送方也是这样顺序,要知道是外设方会发请求,然后DMA一方被动回应请求,并产生读写动作;
我说的对不对?


发送数据:
我理解为,正常情况下我们手动写入一数据到发送寄存器就完成任务了;当然心细的还知道先检测一下标志位;
但DMA什么时候会知道自己要写入一个数据到发送寄存器呢?

这个任务让外设发送方先动作,外设方知道发送寄存器为空,就会主动去通知DMA,
DMA接到通知后,会说,我现在有空处理,你先把请求标志位拆掉,我就发送数据过来;
接下来,一个解除请求;另一个如约送数据过来;
如果外设收到数据后,立即把数据发送到移位寄存器,此时数据寄存器又空了,又会出现数据空标志;
只要出现数据寄存器为空,就会请求DMA给个数据;

DMA发送N个数据后,自己的标志位也出现了(任务完成,完成一半....);你可以自己检测这个标志,也可以在中断中去检测并处理;

象SPI一收一发的情况下,主控方先设好接收再设发送通道;
前面的初始化工作全部完成,最后就几个使能操作;

使用特权

评论回复
13
明月小厨| | 2013-3-10 06:35 | 只看该作者
void SPI_DMA(void)
{

SPI2_DMA_RD_Config();//配置DMA读通道(DMA1_4)
SPI2_DMA_WD_Config();//配置DMA写通道(DMA1_5)


SPI_CalculateCRC( SPI2,  DISABLE);

SPI_Cmd(SPI2, ENABLE);
PBout(12) = 0;

SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);//把SPI发送设为DMA方式(注,已经开始请求了,但DMA还没准备好)
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Rx,ENABLE);//把SPI接收设为DMA方式


DMA_Cmd(DMA1_Channel5, ENABLE);//使能DMA1,这个时候发数已经上路了;
DMA_Cmd(DMA1_Channel4, ENABLE);//使能DMA1,如果没有被打扰,这一个执行完毕,就等数据到了;如果正好倒霉,被中断抢占,没来及执行,有可能丢数据;


while(!(DMA_GetFlagStatus(DMA1_FLAG_TC4)));//等待通道4传输完成

SPI_Cmd(SPI2, DISABLE);
PBout(12) = 1;

DMA_ClearFlag(DMA1_FLAG_TC4);
DMA_ClearFlag(DMA1_FLAG_TC5);
}

使用特权

评论回复
14
明月小厨| | 2013-3-10 06:44 | 只看该作者
关于SPI心得:一收一发配对最好;不然会给自己添麻烦;那怕是无意义的空操作;为了是清标志位;干净!

使用特权

评论回复
15
明月小厨| | 2013-3-10 06:55 | 只看该作者
DMA_Cmd(DMA1_Channel4, ENABLE);//使能DMA1
DMA_Cmd(DMA1_Channel5, ENABLE);//使能DMA1
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Rx,ENABLE);
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);//这一条是所有工作正式开始;就算前面执行的连续性有影响,也不会有问题;

我刚才测试,把上面几条语句的顺序改变;仍然正常.(不考虑中断影响执行的连续性);

使用特权

评论回复
16
明月小厨| | 2013-3-10 07:01 | 只看该作者
//准备
DMA_Cmd(DMA1_Channel4, ENABLE);//使能DMA1
DMA_Cmd(DMA1_Channel5, ENABLE);//使能DMA1
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Rx,ENABLE);
//SPI使能;
SPI_Cmd(SPI2, ENABLE);
PBout(12) = 0;
//DMA到SPI,开始发送
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);
//等待结束
while(!(DMA_GetFlagStatus(DMA1_FLAG_TC4)));

虽然上面几个顺序没测试出来影响;这个顺序是考虑执行的连续性;应该合理些;

使用特权

评论回复
17
明月小厨| | 2013-3-10 07:03 | 只看该作者
明月小厨 发表于 2013-3-10 06:07
关于初始化并不困难;
1.DMA通道的初始化例程到处都是;都一样;
2.外设的DMA使能(收,发)都一样,一个函数解决; ...

我测试怎么没发现有影响呢?

使用特权

评论回复
18
明月小厨| | 2013-3-10 07:50 | 只看该作者
明月小厨 发表于 2013-3-10 07:01
//准备
DMA_Cmd(DMA1_Channel4, ENABLE);//使能DMA1
DMA_Cmd(DMA1_Channel5, ENABLE);//使能DMA1

SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Rx,ENABLE);
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);
DMA_Cmd(DMA1_Channel4, ENABLE);//使能DMA1_4收
DMA_Cmd(DMA1_Channel5, ENABLE);//使能DMA1_5发


说明:
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;时出现测试异常;按上述顺序设置时恢复正常;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;时没有出现测试异常;多种顺序设置时,都测试正常;
为什么是这样,待分析验证.

使用特权

评论回复
19
pybieku| | 2013-3-13 11:14 | 只看该作者
本帖最后由 pybieku 于 2013-3-13 11:23 编辑
humanking7 发表于 2013-3-8 23:54
赞一个,我现在用的是中断解决的,确实是无奈之举,我也关注过之类的帖子,但是我的问题是,dma的初始化 ...
      DMA初始化都有现成的例子可以参考,你可以搜索一下就能找到解决的办法。

使用特权

评论回复
20
pybieku| | 2013-3-13 11:20 | 只看该作者
本帖最后由 pybieku 于 2013-3-13 11:22 编辑

         代码上传好麻烦

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

16

帖子

0

粉丝