搜索
bottom↓
回复: 32

STM32F205RG 单片机 SPI 不能全速收发

[复制链接]

出0入0汤圆

发表于 2018-11-14 17:42:46 | 显示全部楼层 |阅读模式
本帖最后由 meirenai 于 2018-11-14 17:55 编辑

现在用STM32F205RGT6 的 SPI1 总线去驱动 SD卡,模拟USB大容量存储器,发现枚举的时候非常慢,大概20多秒才会枚举成功,显示出盘符。
就用示波器抓了下SPI 操作SD卡的波形,发现不能跑满SPI的全速。(全速是30MHz)

于是就写了一个测试程序,专门测试SPI速度,测试程序如下。

  1. while(1) {
  2.   while(!LL_SPI_IsActiveFlag_TXE(SPI1));
  3.   LL_SPI_TransmitData8(SPI1, 0xff);
  4.   while(!LL_SPI_IsActiveFlag_RXNE(SPI1));
  5.   LL_SPI_ReceiveData8(SPI1);
  6. }
复制代码




请教大拿,这种情况是怎么回事?

我的理解是 收发都是 master 来控制时钟的,发完直接就应该 RXNE 置位吧。 应该不会用中间那么长的间隔。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2018-11-14 17:52:33 | 显示全部楼层
用DMA就可以了,没有间隔

出0入0汤圆

 楼主| 发表于 2018-11-14 17:54:14 | 显示全部楼层
gentlerain 发表于 2018-11-14 17:52
用DMA就可以了,没有间隔

DMA可以实现吗?全速收发?不是单独发送奥?

这个是什么原因呢,为什么轮询就不行?

出0入0汤圆

发表于 2018-11-14 17:57:40 | 显示全部楼层
meirenai 发表于 2018-11-14 17:54
DMA可以实现吗?全速收发?不是单独发送奥?

这个是什么原因呢,为什么轮询就不行? ...

轮询得再快,还是软件执行,需要时间的。DMA是硬件执行,没有延时

出0入0汤圆

 楼主| 发表于 2018-11-14 17:59:52 | 显示全部楼层
gentlerain 发表于 2018-11-14 17:57
轮询得再快,还是软件执行,需要时间的。DMA是硬件执行,没有延时

你看我的测试程序和测试图片啊,200+纳秒的间隔,单片机几千条指令都跑过去了。
我主程序里没有东西,全速跑啊,主频120MHz

出10入12汤圆

发表于 2018-11-14 18:11:34 | 显示全部楼层
用DMA!!!!

出0入24汤圆

发表于 2018-11-14 19:05:51 | 显示全部楼层
用DMA
没有上千条指令,三四十条而已

出0入0汤圆

 楼主| 发表于 2018-11-14 20:13:11 | 显示全部楼层
本帖最后由 meirenai 于 2018-11-15 00:55 编辑
20061002838 发表于 2018-11-14 19:05
用DMA
没有上千条指令,三四十条而已


从参考手册上看到的一个图

从这个图上看 TXE 和 RXNE 只间隔7个 SPI 时钟周期,没道理轮询不到吧

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2018-11-14 23:12:27 来自手机 | 显示全部楼层
meirenai 发表于 2018-11-14 17:59
你看我的测试程序和测试图片啊,200+纳秒的间隔,单片机几千条指令都跑过去了。
我主程序里没有东西,全 ...

算一算就知道200纳秒只能跑20几条指令,不要假设单片机跟你电脑cpu一样快。楼上各位提醒的用DMA是对的,实在不想就直接写寄存器、展开循环、开最高优化

出0入0汤圆

 楼主| 发表于 2018-11-15 00:53:59 | 显示全部楼层
本帖最后由 meirenai 于 2018-11-15 00:57 编辑
eleqian 发表于 2018-11-14 23:12
算一算就知道200纳秒只能跑20几条指令,不要假设单片机跟你电脑cpu一样快。楼上各位提醒的用DMA是对的, ...


我用的是 LL 库,直接操作寄存器,而且大部分都是内联函数。

看了下汇编代码,
while(1) 里一共这四条语句,还是没看懂为啥会有 400ns+ 的间隔

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入24汤圆

发表于 2018-11-15 01:27:49 | 显示全部楼层
meirenai 发表于 2018-11-14 20:13
从参考手册上看到的一个图

从这个图上看 TXE 和 RXNE 只间隔7个 SPI 时钟周期,没道理轮询不到吧

从FLASH取指令的速度跟不上CPU的速度,要等待
读写RAM和寄存器的速度也要等待
前一条指令的结果如果是下一条指令的输入,流水线会暂停,依然等待
影响因素太多

要测试的话,你可以while循环之前写一次DR寄存器,while循环之后读一次DR寄存器
确保TXE标志位不会长时间置位

出0入24汤圆

发表于 2018-11-15 01:39:15 | 显示全部楼层
  1. while(1) {
  2.   while(!LL_SPI_IsActiveFlag_TXE(SPI1));
  3.   LL_SPI_TransmitData8(SPI1, 0xff);
  4.   while(!LL_SPI_IsActiveFlag_RXNE(SPI1));
  5.   LL_SPI_ReceiveData8(SPI1);
  6. }
复制代码

这种写法,TX寄存器为空之后发送暂停了,一直等待
直到读取DR寄存器,进行跳转、while(1)和while(!LL_SPI_IsActiveFlag_TXE(SPI1))判断,然后才再次写DR寄存器,并且写完DR寄存器还不是立刻开始发送数据
这会造成很大的浪费

  1.   while(!LL_SPI_IsActiveFlag_TXE(SPI1));
  2.   LL_SPI_TransmitData8(SPI1, 0xff);
  3. while(1) {
  4.   while(!LL_SPI_IsActiveFlag_TXE(SPI1));
  5.   LL_SPI_TransmitData8(SPI1, 0xff);
  6.   while(!LL_SPI_IsActiveFlag_RXNE(SPI1));
  7.   LL_SPI_ReceiveData8(SPI1);

  8.  while(!LL_SPI_IsActiveFlag_RXNE(SPI1));
  9.   LL_SPI_ReceiveData8(SPI1);
复制代码

试试这样操作,在做跳转和while判断的时候,数据发送持续在工作,减小浪费

出0入0汤圆

发表于 2018-11-15 08:50:41 | 显示全部楼层
楼上的戴佬分析的跟inc一样一样的

出0入0汤圆

 楼主| 发表于 2018-11-15 09:08:31 | 显示全部楼层
WM_CH 发表于 2018-11-15 08:50
楼上的戴佬分析的跟inc一样一样的

意思是三条指令花费了 400ns+ 的时间来执行?

出40入42汤圆

发表于 2018-11-15 09:42:24 | 显示全部楼层
meirenai 发表于 2018-11-15 00:53
我用的是 LL 库,直接操作寄存器,而且大部分都是内联函数。

看了下汇编代码,

CPU是完全按照程序执行的,波形没有问题。
如果想要把那空出来的一段填满,那就把等待接收和接收数据的那两行代码去掉就行了。
理由:
1.楼主认为“TXE 和 RXNE 只间隔7个 SPI 时钟周期”,其实是不准确的,两者间隔最短的只有1个SPI时钟周期多一点
2.从写数据到DR,到硬件发送是需要一点点硬件操作时间的,写程序的时候要考虑这个时间,这个时间间隔楼主8楼的图就可以看出来
3.因为进行了接收标志位RXNE的等待,所以从RXNE置位被程序读取到-->读取SPI接收缓冲数据-->循环判断-->等待TXNE置位,这个过程的
  第一步“等待RXNE置位”到最后一步“等待TXNE置位”,留给程序检测的时间只有大概2个SPI时钟周期,按30MHz来算,就66ns(纳秒)左右,
  看波形结果应该是不够程序“正确”运行的检测时间的

出0入0汤圆

 楼主| 发表于 2018-11-15 09:58:48 | 显示全部楼层
落叶知秋 发表于 2018-11-15 09:42
CPU是完全按照程序执行的,波形没有问题。
如果想要把那空出来的一段填满,那就把等待接收和接收数据的那 ...

》 1.楼主认为“TXE 和 RXNE 只间隔7个 SPI 时钟周期”,其实是不准确的,两者间隔最短的只有1个SPI时钟周期多一点
》 2.从写数据到DR,到硬件发送是需要一点点硬件操作时间的,写程序的时候要考虑这个时间,这个时间间隔楼主8楼的图就可以看出来

我感觉层主说的 最短1个SPI时钟周期多一点 忽略了,发送过程中的一个环节。

数据发送时,首先把 TX_DR 内的数据转入 移位寄存器,此时TX_DR 寄存器空,在经过一个SPI 时钟以后, 置位 TXE 标志,随后经过 7 个时钟周期以后发送完成,
同步的,在数据的最后一个边沿 接收移位寄存器接收到最后一个bit数据,将数据转存到 RX_DR中,并且置位 RXNE 标志。
也就是说在 TXE 标志置位 到 RXNE 置位 中间至少会有 7 个时钟周期,如图40楼所示。

从图上可以更清楚的看到这个过程。

出40入42汤圆

发表于 2018-11-15 10:24:54 | 显示全部楼层
meirenai 发表于 2018-11-15 09:58
》 1.楼主认为“TXE 和 RXNE 只间隔7个 SPI 时钟周期”,其实是不准确的,两者间隔最短的只有1个SPI时钟 ...
TXE 标志置位 到 RXNE 置位 中间至少会有 7 个时钟周期

这个没错
我说的是
两者间隔最短的只有1个SPI时钟周期多一点

TXE到RXNE大概7个SPI时钟周期
RXNE到TXE大概1个SPI时钟周期多一点

出0入0汤圆

 楼主| 发表于 2018-11-15 10:31:03 | 显示全部楼层
20061002838 发表于 2018-11-15 01:39
这种写法,TX寄存器为空之后发送暂停了,一直等待
直到读取DR寄存器,进行跳转、while(1)和while(!LL_SPI_ ...

有点明白了。

  1. while(1) {
  2.   while(!LL_SPI_IsActiveFlag_TXE(SPI1));
  3.   LL_SPI_TransmitData8(SPI1, 0xff);
  4.   while(!LL_SPI_IsActiveFlag_RXNE(SPI1));
  5.   LL_SPI_ReceiveData8(SPI1);
  6. }
复制代码


也就是说从 while(!LL_SPI_IsActiveFlag_TXE(SPI1)); 运行到  while(!LL_SPI_IsActiveFlag_RXNE(SPI1)); 需要花费大概 400ns 的时间,其实发送早就完成了,RXNE 标志位早已经置位,但是代码还没有运行到监测位置?


是这样理解吗?

出0入4汤圆

发表于 2018-11-15 10:37:35 | 显示全部楼层
前几天spi刷屏也发现问题了,  根本不是什么dma的问题。

我发现给显示屏清屏也就是所有都写同一个字符。  采用hal库直接写 速度很快的。  但是要用for循环去写 就很慢。

差异就在for循环上,每次要进去判断一下for 浪费时间了 。  如果不是这个浪费时间的话  用不用dma肉眼看不出区别。

另外还有一个影响就是cs脚的拉高拉低也会浪费时间。

出0入0汤圆

 楼主| 发表于 2018-11-15 10:41:41 | 显示全部楼层
落叶知秋 发表于 2018-11-15 10:24
这个没错
我说的是


有点明白了。

  1. while(1) {
  2.   while(!LL_SPI_IsActiveFlag_TXE(SPI1));
  3.   LL_SPI_TransmitData8(SPI1, 0xff);
  4.   while(!LL_SPI_IsActiveFlag_RXNE(SPI1));
  5.   LL_SPI_ReceiveData8(SPI1);
  6. }
复制代码



也就是说从 while(!LL_SPI_IsActiveFlag_TXE(SPI1)); 运行到  while(!LL_SPI_IsActiveFlag_RXNE(SPI1)); 需要花费大概 400ns 的时间,其实发送早就完成了,RXNE 标志位早已经置位,但是代码还没有运行到监测位置?


是这样理解吗?

出0入0汤圆

 楼主| 发表于 2018-11-15 10:51:39 | 显示全部楼层
huarana 发表于 2018-11-15 10:37
前几天spi刷屏也发现问题了,  根本不是什么dma的问题。

我发现给显示屏清屏也就是所有都写同一个字符。  ...

你 SPI 速度是多少?单片机主频是多少,从上面的分析来看的话,根本原因还是软件执行速度不够快,导致各种检测位早已经置位但是软件还没有运行到这个监测位置。

出0入0汤圆

发表于 2018-11-15 10:53:53 | 显示全部楼层
spi收发是相关联的,检测收标志就行了,可以优化一点点:

while(1) {
  //while(!LL_SPI_IsActiveFlag_TXE(SPI1));
  LL_SPI_TransmitData8(SPI1, 0xff);
  while(!LL_SPI_IsActiveFlag_RXNE(SPI1));
  LL_SPI_ReceiveData8(SPI1);
}

出40入42汤圆

发表于 2018-11-15 13:58:32 | 显示全部楼层
本帖最后由 落叶知秋 于 2018-11-15 14:03 编辑


找了手册看了下,关于我15楼的回复,理由里面的第3点是错误的,当时我只是根据8楼图来推测,不好意思

其实首先应该问一下楼主:你硬件上面的SPI线是怎么接的?
因为你程序里面是有等待接收的操作的,想知道接收端是怎么接的?

看手册里面是有对全双工模式(Full-duplex)的处理顺序要求的,SPI_DR寄存器又发送又接收,估计不能达到你想要的“全双工”效果

出0入0汤圆

 楼主| 发表于 2018-11-15 14:21:59 | 显示全部楼层
落叶知秋 发表于 2018-11-15 13:58
找了手册看了下,关于我15楼的回复,理由里面的第3点是错误的,当时我只是根据8楼图来推测,不好意思

其 ...

四线制啊

CS
CLK
MISO
MOSI

出40入42汤圆

发表于 2018-11-15 14:29:27 | 显示全部楼层

MISO和MOSI短接进行回环测试?

出0入0汤圆

 楼主| 发表于 2018-11-15 14:41:33 | 显示全部楼层
落叶知秋 发表于 2018-11-15 14:29
MISO和MOSI短接进行回环测试?


不是啊,接的SD卡,但是没有按照时序操作,

程序就是一楼的代码。

出0入0汤圆

 楼主| 发表于 2018-11-15 14:43:31 | 显示全部楼层
落叶知秋 发表于 2018-11-15 14:29
MISO和MOSI短接进行回环测试?


这个回环应该没有影响吧,时钟都是由 master 驱动的,全双工模式下,发送完一个字节就相应的收到一个字节。按照8楼的图片来说的话。

出40入42汤圆

发表于 2018-11-15 14:56:07 | 显示全部楼层
meirenai 发表于 2018-11-15 14:41
不是啊,接的SD卡,但是没有按照时序操作,

程序就是一楼的代码。

那你试一试12楼的那种写法吧,估计就是指令操作时间大约占了时间
就是在while(1){}前面再加一个发送操作

  1. LL_SPI_TransmitData8(SPI1, 0xff);
  2. while(1) {
  3.   while(!LL_SPI_IsActiveFlag_TXE(SPI1));
  4.   LL_SPI_TransmitData8(SPI1, 0xff);
  5.   while(!LL_SPI_IsActiveFlag_RXNE(SPI1));
  6.   LL_SPI_ReceiveData8(SPI1);
  7. }
复制代码

看看手册里面的时序图前面一点点的连续发送和接收流程也是这样推荐的
因为写SPI_DR就直接搬到TxBuff里了,等TXE置位就是TxBuff空了,又可以再写了
如果等到接收完了再写,那发送器就多等了一段时间,因为这时TxBuff是空的
如果让TxBuff一直处于非空状态,就能连续发送了

出0入0汤圆

 楼主| 发表于 2018-11-15 15:33:16 | 显示全部楼层
落叶知秋 发表于 2018-11-15 14:56
那你试一试12楼的那种写法吧,估计就是指令操作时间大约占了时间
就是在while(1){}前面再加一个发送操作
...

嗯,用那个写法确实快了一点,但是还是会有断流,大概没三个字节断 100ns 左右。


这样看来确实是软件指令虽然很少,但是执行却比较慢,无法满足连续发送的要求。是这样理解吧。

出40入42汤圆

发表于 2018-11-15 20:52:28 来自手机 | 显示全部楼层
meirenai 发表于 2018-11-15 15:33
嗯,用那个写法确实快了一点,但是还是会有断流,大概没三个字节断 100ns 左右。



如果还有断流的情况,那应该就是while(1)里面的指令执行时间比spi发送1个字节的时间要长一点,固定次数后就有一次TxBuff空了

出0入0汤圆

发表于 2018-11-22 23:00:52 来自手机 | 显示全部楼层
11楼说的很明白了,ROM太慢,所以单片机有了多级流水线,但是你会打断流水线的运行,你那个while函数我理解为读发送标志位,如果是这个样子的话,去掉while 在这里用nop填充 调整nop的数量 完美了

出0入0汤圆

 楼主| 发表于 2018-11-23 09:07:40 | 显示全部楼层
雨雪随行 发表于 2018-11-22 23:00
11楼说的很明白了,ROM太慢,所以单片机有了多级流水线,但是你会打断流水线的运行,你那个while函数我理解 ...

学习了,以前一直以为指令执行的很快,误以为一直是软件等待硬件执行完成,现在看来是 发送早就完成了,但是软件还没运行到监测位置,导致发送不连续。

出0入0汤圆

发表于 2018-12-28 17:29:45 | 显示全部楼层
同款示波器:)
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-18 23:27

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表