打印

SPI的一些平时可能不会注意到的疑问

[复制链接]
2337|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
nightingale2003|  楼主 | 2014-11-22 23:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 nightingale2003 于 2014-11-23 00:25 编辑

本来以为自己对SPI有了一定的理解,不管软件模拟,还是硬件SPI都写过程序也调试成功了。但今天重看了一遍SPI,发现有些问题之前一直没考虑到。所以希望各位帮忙解答下。
问题一:一般的从器件(单片机作为从器件暂时不考虑)大多属于 空闲状态低电平,上升沿有效输入,即CPOL=0,CPHA=0;
        那么也就是说第一位的数据先出现在主从器件的MOSI口,然后上升沿到达后传入移位寄存器。
        那么问题来了,既然在空闲时还没时钟信号,主从器件怎么知道什么时候输出第一个位?(这里不讨论软件模拟的情况)
        目前考虑的是两个可能性:
            1.当我们将要发送的数据写入 SPI的移位寄存器时,第一个位就输出到IO口了
            2.SPI有片选端,当片选拉低时,该信号作为第一位数据的输出信号输出到IO口
         不知哪个正确呢?

问题二:SPI的最基础的数据函数就一个uchar SPI_rw(uchar data);(网上大多数例子都这样写,相信函数内容各位也清除吧)
         下面以无线模块的nRF24L01为例。
         一般我们会在这基础上建立对从器件的函数用于写和读寄存器,有时候还要建立连续读、写的函数,如:
         void SPI_write(uchar addr,uchar dat)
        {
               CSN=0;
               SPI_rw(addr);
               SPI_rw(dat);
               CSN=1;
        }
         如果只是单独一个这样的函数还好,如果需要不停的写入读取寄存器时,从我们的角度来看,还比较有条理,比如:
        SPI_write(0x20+EN_AA,0x01);
        SPI_write(0x20+SETUP_RETR,0x0f);
        SPI_write(0x20+EN_RXADDR,0x01);
        但对于IO口来说,其实就是一大堆的SPI_rw();
        这样一来,问题就是,nRF24L01怎么认定哪个是命令,哪个是数据?
        如果说0x20+EN_AA(其实就是0x21)的这个数据nRF24L01认得出来,那万一后面要写的数据也是0x21,那这个是数据要是命令?
        另一种情况是从从器件连续读数据,这时主器件(单片机)要发送8个字节以便读取从器件的数据,一般习惯是发送个0xff或者0x00
        但如果发了个命令,还会继续读取还是执行该命令呢?
        目前的一个考虑的想法是 CSN作为控制信号,拉低后执行的第一个为命令第二个为数据,但记得看过别人写程序把CSN写进了SPI_rw,
        好像也看过直接把片选线接地的情况(当然前提 SPI总线上就一个从器件)

PS1:现在看来 I2C协议虽然麻烦但确实严谨多了
PS2:可能有人觉得做实验比上来问强,不过这两天没板子在手上,做不了实验,还请大神不惜赐教

相关帖子

沙发
airwill| | 2014-11-23 08:24 | 只看该作者
哦, 新人帖的一段编程总结经验,
就是没有看到你这是用的什么 MCU?

         void SPI_write(uchar addr,uchar dat)
        {
               CSN=0;
               SPI_rw(addr);
               SPI_rw(dat);
               CSN=1;
        }

这样写, 应该是模拟 SPI 接口吧. 否则不等数据发送完成, 就 CSN=1; 会有问题了.

使用特权

评论回复
板凳
NE5532| | 2014-11-23 11:12 | 只看该作者
其实SPI的CS线,与其叫“片选”,不如叫“命令同步”,楼主的两个问题就都有解答了。

使用特权

评论回复
地板
nightingale2003|  楼主 | 2014-11-23 18:28 | 只看该作者
本帖最后由 nightingale2003 于 2014-11-23 18:35 编辑
airwill 发表于 2014-11-23 08:24
哦, 新人帖的一段编程总结经验,
就是没有看到你这是用的什么 MCU?

这个用的是硬件的,当然和软件区别只有这个函数的内容。
MCU是STC15F2K系列的。
uchar SPI_rw(uchar dat)
{
        SPSTAT=0xc0;
        _nop_();
        _nop_();
        SPDAT=dat;
        while(!(SPSTAT&0x80));                 //传送完成信号检测,所以不会出现没传完就关闭片选的情况
        return SPDAT;
}

使用特权

评论回复
5
nightingale2003|  楼主 | 2014-11-23 18:32 | 只看该作者
NE5532 发表于 2014-11-23 11:12
其实SPI的CS线,与其叫“片选”,不如叫“命令同步”,楼主的两个问题就都有解答了。 ...

嗯,之前觉得能说的通的就是这个了。也就是说这个“片选”还是要很重视了。感谢指点。

使用特权

评论回复
6
玄德| | 2014-11-23 19:53 | 只看该作者
问题1
如果你自己写主机的底层,只要保证在ck上升沿之前建立首位数据,就可以了,
至于用方法1还是2,没必要追究。我相信,有人用1,有人用2,都没问题。

使用特权

评论回复
7
lxyppc| | 2014-11-23 20:56 | 只看该作者
我来瞎扯一下你说的cpol,cpha这种情况
问题一:数据写入主机的寄存器后,主机会先把第一位数据放到mosi上,再输出时钟上升沿。如果你要研究第一位数据的输出时机,可以用示波器来观察,并把spi配置成dma的模式。
主机的片选为的是告诉从机我要发数据了。从机收到片选信号可以复位自己内部的状态机。也就是你的第二个问题,为啥命令和数据能分辨出来。因为cs复位后第一个字节就是命令,然后根据命令来解析后面动作。这个cs信号对从机而言很关键,如果某种原因导致时钟多了一个或者少了一个,还得靠他来复位。

使用特权

评论回复
8
qqljhang| | 2014-11-23 22:14 | 只看该作者
我觉得,正如楼主所说,其实SPI并不是严谨的应用层通讯协议,它只是物理层的简单协议,也正因此,它是一种可靠的芯片间通讯方式,灵活有余而便利不足,不像CAN的那种完整的应用层协议,只需要设几个简单寄存器,就可以随心所欲地挂上节点进行通讯。但正是因为它与物理层极为接近,因此经常被用于进行快速数据响应和安全监控。   问题1我认为是由CS通知从机,使从机作好接收准备。     问题2,我认为这个问题如果俩芯片只简单靠SPI,是不足以识别哪个UINT8是命令,哪个是数据的,必须还要依靠别的IO信号才行,使如CS的特别应用。     仅仅依靠SPI来进行多命令、多响应的应用,我认为应该在传输数据的本身上作**。    我曾经将SPI设为16位宽度,并利用其高6位作命令区,低10作数据区进行通讯,效果良好。当然这一套数据层协议,是由我们自己定义的。

使用特权

评论回复
9
kseeker| | 2014-11-23 23:30 | 只看该作者
关于第一个问题,答案肯定是2.如果是1的话,多个器件就无法共用一个总线。
关于第二个问题,对于nrf24L01,当CSN为低时,SPI接口开始等待一条指令,任何一条新指令均由CSN的由高到低的转换开始。所以直接把片选线接地对nrf24L01应该是不行。
理论上说,对从设备的角度来说,可以把spi总线看成控制台,然后一个字节一个字节的调用getchar。协议合理的话是可以分出命令和数据的,不需要片选之类的信号的帮助。
不过这么做的缺点是,万一丢了一位或一个字节(比如主机发送一半复位了)就完蛋了。当然这种情况也不是绝对没有办法,比如规定连续20个0后的第一个0xFF代表同步标记。
但弄得这么复杂费力不讨好,远不如用CS同步来的实在。

使用特权

评论回复
10
nightingale2003|  楼主 | 2014-11-24 08:58 | 只看该作者
感谢LS各位解答,特别7 8 9楼的各位,说的很详细。此贴可结。

使用特权

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

本版积分规则

31

主题

105

帖子

2

粉丝