请选择 进入手机版 | 继续访问电脑版

安富莱电子论坛

 找回密码
 立即注册

扫一扫,访问微社区

查看: 566|回复: 25
收起左侧

加快SPI连续读写速度的配置方式

  [复制链接]

5275

主题

3万

帖子

4万

积分

管理员

做人第一,工作第二

Rank: 9Rank: 9Rank: 9

积分
42670
QQ
发表于 2019-1-8 01:12:51 | 显示全部楼层 |阅读模式
一般都是下面这种,比较啰嗦
  1. /* 等待发送缓冲区空 */
  2.         while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);

  3.         /* 发送一个字节 */
  4.         SPI_I2S_SendData(SPI1, _ucByte);

  5.         /* 等待数据接收完毕 */
  6.         while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

  7.         /* 读取接收到的数据 */
  8.         SPI_I2S_ReceiveData(SPI1);
复制代码

下面这种可以有效加速,特别是连续读写的时候
下面的函数SendBuf 隐藏了两个关键的知识点,看看有没有坛友能看出来的(答案已经揭晓到下面)

  1. /* SPI_SR位定义 */
  2. #define RXNE    0x01
  3. #define TXE     0x02
  4. #define BSY     0x80
  5. #define FPCLK   (__FPCLK/1000)

  6. /* 串行Flsh的片选GPIO端口  */
  7. #define SPI_SelectHard            SPI1
  8. #define SF_RCC_CS                         RCC_AHB1Periph_GPIOD
  9. #define SF_PORT_CS                        GPIOD
  10. #define SF_PIN_CS                        GPIO_Pin_13
  11. #define SF_CS_0()                        SF_PORT_CS->BSRRH = SF_PIN_CS
  12. #define SF_CS_1()                        SF_PORT_CS->BSRRL = SF_PIN_CS

  13. /*
  14. *********************************************************************************************************
  15. *        函 数 名: Send
  16. *        功能说明: 发送一个字节数据
  17. *        形    参: 无
  18. *        返 回 值: SPI接口返回值
  19. *********************************************************************************************************
  20. */
  21. static U8 Send (U8 outb)
  22. {
  23.         /* 通过SPI接口读写一个字节 */
  24.         SPI_SelectHard->DR = outb;

  25.         /* 等待数据接收完毕 */
  26.         while (!(SPI_SelectHard->SR & RXNE));
  27.         return (SPI_SelectHard->DR);
  28. }

  29. /*
  30. *********************************************************************************************************
  31. *        函 数 名: SendBuf
  32. *        功能说明: 通过SPI接口发送多个字节
  33. *        形    参: buf 数据地址
  34. *             sz  发送数据大小
  35. *        返 回 值: __TRUE
  36. *********************************************************************************************************
  37. */
  38. static BOOL SendBuf (U8 *buf, U32 sz)
  39. {
  40.         U32 i;

  41.         for (i = 0; i < sz; i++)
  42.         {
  43.                 SPI_SelectHard->DR = buf[i];
  44.                 /* 等待发送完成 */
  45.                 while (!(SPI_SelectHard->SR & TXE));
  46.                 SPI_SelectHard->DR;
  47.         }
  48.         
  49.         /* 等待接收完成 */
  50.         while (SPI_SelectHard->SR & (BSY | RXNE))
  51.         {
  52.                 SPI_SelectHard->DR;
  53.         }
  54.         return (__TRUE);
  55. }
复制代码


由于SPI是全双工通信,发送完还要等带接收完成,这里做一个巧妙的处理。

发送的时候仅判断TEX发送空中断,发送完毕后,再等待RXNE,这样就不需要每次TEX都等待RXNE。

另外就是我们要清除TEX和RXNE标志,清除方法也比较简单,仅需读取DR寄存器就可以清除了,比较方便。

淘宝小店: https://armfly.taobao.com/
专注,努力,用心的做好每一件事情,Fighting!
回复

使用道具 举报

1

主题

431

帖子

433

积分

高级会员

积分
433
发表于 2019-1-8 08:26:52 | 显示全部楼层
用寄存器写,加速。*((u32 *)0x12345678)=0x12345678;有些时候我会尝试把代码全部写成这种风格,在加速的同时,还起到了不可读的效果!
回复

使用道具 举报

4

主题

162

帖子

170

积分

初级会员

积分
170
发表于 2019-1-8 08:32:26 | 显示全部楼层
我抓一个关键词 “buffer”
回复

使用道具 举报

44

主题

542

帖子

630

积分

金牌会员

积分
630
发表于 2019-1-8 08:55:47 | 显示全部楼层
V7年前出点干货啊。。。过年放假闲着也是闲着,抓紧时间学一下。。。
回复

使用道具 举报

5275

主题

3万

帖子

4万

积分

管理员

做人第一,工作第二

Rank: 9Rank: 9Rank: 9

积分
42670
QQ
 楼主| 发表于 2019-1-8 09:06:05 | 显示全部楼层
leiyitan 发表于 2019-1-8 08:26
用寄存器写,加速。*((u32 *)0x12345678)=0x12345678;有些时候我会尝试把代码全部写成这种风格,在加速的 ...

没说到点上,寄存器操作不是关键^_^
淘宝小店: https://armfly.taobao.com/
专注,努力,用心的做好每一件事情,Fighting!
回复

使用道具 举报

5275

主题

3万

帖子

4万

积分

管理员

做人第一,工作第二

Rank: 9Rank: 9Rank: 9

积分
42670
QQ
 楼主| 发表于 2019-1-8 09:06:12 | 显示全部楼层
廷润 发表于 2019-1-8 08:32
我抓一个关键词 “buffer”

没说到点上,BUF不是关键^_^
淘宝小店: https://armfly.taobao.com/
专注,努力,用心的做好每一件事情,Fighting!
回复

使用道具 举报

0

主题

3

帖子

3

积分

新手上路

积分
3
发表于 2019-1-8 09:21:37 | 显示全部楼层
BUF方式,只要发送缓冲空TXE,就写入发送数据,不用等实际硬件是否完全发送完,这样数据发送是连续的。最后再检查RXNE标志判断实际硬件是否发送完成。
回复

使用道具 举报

13

主题

182

帖子

208

积分

高级会员

积分
208
发表于 2019-1-8 09:32:32 | 显示全部楼层
1、SPI读写过程是同步的,所以发送的时候只需等待TXE,不必每次都对接收完毕进行判断。
回复

使用道具 举报

21

主题

1077

帖子

1119

积分

至尊会员

积分
1119
发表于 2019-1-8 09:50:30 | 显示全部楼层
函数  SPI_SelectHard->DR;   用的巧妙
Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better.
回复

使用道具 举报

21

主题

1077

帖子

1119

积分

至尊会员

积分
1119
发表于 2019-1-8 10:11:04 | 显示全部楼层
可以方便的清除标志。
Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better.
回复

使用道具 举报

4

主题

28

帖子

36

积分

新手上路

积分
36
发表于 2019-1-8 10:40:42 | 显示全部楼层
用DMA更好吧
回复

使用道具 举报

21

主题

1077

帖子

1119

积分

至尊会员

积分
1119
发表于 2019-1-8 10:49:50 | 显示全部楼层

DMA不灵活,特别是控制一些ADC,DAC芯片的时候,不方便。
Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better.
回复

使用道具 举报

8

主题

121

帖子

137

积分

初级会员

积分
137
发表于 2019-1-8 13:08:16 | 显示全部楼层
没看出来……
回复

使用道具 举报

0

主题

9

帖子

9

积分

新手上路

积分
9
发表于 2019-1-8 15:55:37 | 显示全部楼层
在发送指定长度数据时,只写数据,不执行读。到写完时,判断一些读寄存器并处理。
回复

使用道具 举报

4

主题

107

帖子

115

积分

初级会员

积分
115
发表于 2019-1-8 16:09:49 | 显示全部楼层
Oenomaus 发表于 2019-1-8 15:55
在发送指定长度数据时,只写数据,不执行读。到写完时,判断一些读寄存器并处理。

执行了读的,我感觉时发送定长数据时,TXE为空就继续写入,不等待发送完成,同时读一下DR,以便清除可能的RXNE标志位而不实现检查RXNE标志位
回复

使用道具 举报

44

主题

542

帖子

630

积分

金牌会员

积分
630
发表于 2019-1-8 16:48:51 | 显示全部楼层
难道SPI返回的数据又放在了buf里面?
回复

使用道具 举报

5275

主题

3万

帖子

4万

积分

管理员

做人第一,工作第二

Rank: 9Rank: 9Rank: 9

积分
42670
QQ
 楼主| 发表于 2019-1-9 00:24:22 | 显示全部楼层

楼主位已经更新答案。
淘宝小店: https://armfly.taobao.com/
专注,努力,用心的做好每一件事情,Fighting!
回复

使用道具 举报

44

主题

542

帖子

630

积分

金牌会员

积分
630
发表于 2019-1-9 08:23:13 | 显示全部楼层
不知道能节约多少? 可能某些场合需要这样的优化,一般项目感觉还是用HAL,程序的健壮性,可读性和可维护性才是第一的。
回复

使用道具 举报

0

主题

5

帖子

5

积分

新手上路

积分
5
发表于 2019-1-9 09:08:46 | 显示全部楼层
eric2013 发表于 2019-1-8 09:06
没说到点上,BUF不是关键^_^

硬汉,请教一个 RTX的疑惑啊,我看到user文件夹内有一个文件 RTX_lib.c , 它不是个头文件,只放在文件夹内,我在工程里没看到你有添加它,有什么作用呢? 能否删除它啊。
回复

使用道具 举报

4

主题

162

帖子

170

积分

初级会员

积分
170
发表于 2019-1-9 09:50:30 | 显示全部楼层
这巧妙的答案,刚好给我一个灵感。去规避量产产品,个别产品发生随机通信异常现象。
回复

使用道具 举报

44

主题

542

帖子

630

积分

金牌会员

积分
630
发表于 2019-1-9 11:14:03 | 显示全部楼层
廷润 发表于 2019-1-9 09:50
这巧妙的答案,刚好给我一个灵感。去规避量产产品,个别产品发生随机通信异常现象。

? 这个灵感好。晚上来我房聊聊。
回复

使用道具 举报

4

主题

162

帖子

170

积分

初级会员

积分
170
发表于 2019-1-10 08:42:16 | 显示全部楼层
roguebear 发表于 2019-1-9 11:14
? 这个灵感好。晚上来我房聊聊。

我不是用贴中的功能。我是 “望文生义” ,激发了自己某款产品的某个 随机缺陷的优化思路。
回复

使用道具 举报

3

主题

26

帖子

32

积分

新手上路

积分
32
发表于 2019-1-10 09:45:55 | 显示全部楼层
这方法的确巧妙,虽然SPI可以全双工,但实际应用中却没能实现真正意义上的全双工,官方的方法可能是对全双工的支持。

最近我发现BSP驱动的中的SPI FLASH驱动的sf_AutoWritePage()函数可以有优化的地方,就是在判别FLASH中的数据没有变化之前,
只做一次读FLASH操作,后续不用再做一次读FLASH操作。因为我看到sf_AutoWritePage()函数是在某个特定扇区内操作,如果写数据
的长度小于FLASH扇区的长度就要做两次读FLASH操作(前提是新数据与FLASH中的数据不一样);既然是在某个特定扇区内操作,何不首先
读出整个扇区数据,根据地址偏移对新数据与FLASH中的数据进行比较和判断需要擦除扇区。这样无论写数据的长度小于或等于FLASH扇区
的长度都只做一次读FLASH操作,还请大神鉴别。
回复

使用道具 举报

5275

主题

3万

帖子

4万

积分

管理员

做人第一,工作第二

Rank: 9Rank: 9Rank: 9

积分
42670
QQ
 楼主| 发表于 2019-1-10 12:19:11 | 显示全部楼层
huohua1991 发表于 2019-1-10 09:45
这方法的确巧妙,虽然SPI可以全双工,但实际应用中却没能实现真正意义上的全双工,官方的方法可能是对全双 ...

谢谢,后面研究下。
淘宝小店: https://armfly.taobao.com/
专注,努力,用心的做好每一件事情,Fighting!
回复

使用道具 举报

0

主题

10

帖子

10

积分

新手上路

积分
10
发表于 2019-1-12 10:22:10 | 显示全部楼层
确实比起之前为了接收一个数据,还要发送一个dummy数据快!
回复

使用道具 举报

44

主题

542

帖子

630

积分

金牌会员

积分
630
发表于 2019-1-12 22:36:02 | 显示全部楼层
Tresordie 发表于 2019-1-12 10:22
确实比起之前为了接收一个数据,还要发送一个dummy数据快!

大概快多少?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|手机版|安富莱电子论坛 ( 鄂ICP备09023347号,公安机关备案号42010602000201 )

GMT+8, 2019-2-19 15:03 , Processed in 0.240780 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

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