找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 9005|回复: 3
打印 上一主题 下一主题
收起左侧

SPI协议工作过程详解文档,内含实例代码

[复制链接]
跳转到指定楼层
楼主
ID:92536 发表于 2018-7-7 10:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
SPI一种3线(有的没有SS线(选择主或从机模式))或4线的通信方式:
           MOSI:主机出从机进数据线
                 MISO:主机进从机从数据线
                 SCK: 时钟线
                 SS:  主从模式选择线
工作过程的理解:设置好相应的配置后就可以发送或接收数据,当放送或接收完成后就会是SPIF置位,此时如果开了中断就会就入中断服务程序。如果没开中断就可以通过查询该标志位来做相应的事。进入中段服务程序后,或者读(写)SPDR寄存器都可以清零SPIF标志位。从而可以开始下一次的传输
相应的寄存器
1SCPR:控制寄存器:SPI的大部分设置
2SPSR:标志寄存器:(查看一些标志(主要是SPIF位)和设置SPI速度)
3SPDR:数据寄存器:
SS引脚的说明:(实质就是当配置为输入时:是主机还是从机由输入的电平来决定)
从机模式: SPI 配置为从机时,从机选择引脚 SS 总是为输入。SS 为低将激活 SPI 接口, MISO
成为输出 ( 用户必须进行相应的端口配置 ) 引脚,其他引脚成为输入引脚。当 SS 为高时
所有的引脚成为输入, SPI 逻辑复位,不再接收数据。
SS引脚对于数据包/字节的同步非常有用,可以使从机的位计数器与主机的时钟发生器同
步。当SS 拉高时SPI从机立即复位接收和发送逻辑,并丢弃移位寄存器里不完整的数据。

主机模式: SPI 配置为主机时 (SPCR MSTR置位 ),用户可以决定 SS 引脚的方向。
SS 配置为输出,则此引脚可以用作普通的 I/O 口而不影响 SPI 系统。典型应用是用来
驱动从机的 SS 引脚。
如果 SS 配置为输入,必须保持为高以保证 SPI 的正常工作。若系统配置为主机,SS
输入,但被外设拉低,则 SPI 系统会将此低电平解释为有一个外部主机将自己选择为从
机。为了防止总线冲突, SPI 系统将实现如下动作:
1. 清零 SPCR MSTR 位,使 SPI 成为从机,从而 MOSI SCK变为输入。
2. SPSR SPIF 置位。若 SPI 中断和全局中断开放,则中断服务程序将得到执行。
因此,使用中断方式处理 SPI 主机的数据传输,并且存在 SS 被拉低的可能性时,中断服
务程序应该检查 MSTR 是否为 "1”。若被清零,用户必须将其置位,以重新使能 SPI 主机
模式。
管脚配置
一:SCPR:控制寄存器:(SPI的大部分设置都在该寄存器)
SPSR:标志寄存器:(查看一些标志和设置SPI速度,主要是用到SPIF这个标志位,接收完成和发送完成都会置位这个标志,进入中断服务程序或访问SPDR寄存  器都可以清零这个标志
SPDR:数据寄存器:


应用步骤:
1先根据是主还是从设置端口的模式(输入还是输出)
主机:SS   MOSI   SCK 为出 MISO为入
从机:SS   MOSI   SCK 为入 MISO 为出
2配置相应的寄存器(就三个寄存器,注意用到中断时要先开总中断)
3用中断或查询法发送或接收数据
注意:主机或者从机都可以发送或接收。发送完之后就可以马上发下一个数据了。接收也是只要接收完了也可以马上接收下一个数据了
发送:(一般用查询方式发送比较好:此时只要查询发送完毕的标SPIF是否置位
常用:while(!(SPSR & (1<<SPIF))); //等待发送完毕或接收完毕
查询法:实质就是查询SPSRSPIF位是否置位,接收完和发送完都可以置位,(如果开了中断的话就会进入相应的中断)
例子:
  • 主机发送查询方式)(只要发送完毕了就可以继续发送了)
//主机模式下发送1-255
  1. #include <avr/io.h>
  2. #define uint unsigned int
  3. #define uchar unsigned char
  4. void spi_init(void)
  5. {
  6.     PORTB |= (1<<PB4) | (1<<PB5) | (1<<PB6) | (1<<PB7); //
  7.     DDRB |= (1<<DDB5) | (1<<DDB7) | (1<<DDB4);     //Set MOSI, SCK AND SS as outputs
  8.     SPCR = 0x73; //SPI使能,低位首发,主机模式,时钟模式00,上升沿采样;时钟速率Fosc/128
  9.     SPSR = 0x00; //SPI2x=0
  10. }
  11. void SPI_send_data(char cData)
  12. {
  13.               PORTB &=~ (1<<PB4); //强制接收方进入从模式(在接收方的NSS引脚没有接地时)
  14. //              SPCR |= (1<<MSTR); // MSTR有时会被清零,这里强制进入主机模式
  15.               SPDR =cData;
  16.               while(!(SPSR & (1<<SPIF))); //等待发送完毕
  17. //              PORTB |= (1<<PB4); //相当于释放总线(这样可以让别的主机控制从机)

  18. }

  19. void Delay(uint z) //
  20. {
  21.    unsigned int i,j;
  22.    for(i=z;i>0;i--)
  23.     for(j=2000;j>0;j--);

  24. }

  25. int main(void)
  26. {
  27.               unsigned int i=0;
  28.               spi_init();
  29.               while(1)
  30.               {
  31.                             for(i=255;i>0;i--)
  32.                             {
  33.                                 SPI_send_data(i);
  34.                                           Delay(100);
  35.                             }
  36.               }
  37. }
复制代码

2从机接收(查询方式)

  1. #include <avr/io.h>
  2. #include <avr/delay.h>
  3. #define uchar              unsigned char
  4. #define uint              unsigned int
  5. void port_init(void)
  6. {
  7.               PORTA = 0x00;
  8.               DDRA  = 0xFF;
  9.               PORTB = 0xFF;
  10. }

  11. void spi_init(void)
  12. {
  13.     DDRB = (1<<5) |(1<<7);
  14.               DDRB&=~(1<<4)|(1<<6);
  15.               SPCR = 0x63; //                            SPI允许。从机方式
  16.               SPSR = 0x00; //                            SPI倍速.频率为系统时钟
  17. }

  18. void init_devices(void)
  19. {
  20.               port_init();
  21.               spi_init();
  22. }
  23. //主函数
  24. int main(void)
  25. {
  26.               init_devices();
  27.      while(1)
  28.               {            
  29.                             while(!(SPSR & (1<<SPIF)));//等待数据接收完成
  30.                             PORTA=SPDR;//把接收到的数据赋给A口
  31.                             _delay_loop_2(900);
  32.             
  33.               }
  34. }
复制代码

3从机中断方式接收数据(注意:当进入中断服务程序后SPIF会自动清零)
  1. /*中断方式接收数据*/
  2. #include <avr/io.h>
  3. #include <avr/interrupt.h>
  4. #define uchar              unsigned char
  5. #define uint              unsigned int

  6. //端口初始化
  7. void port_init(void)
  8. {
  9.               PORTA = 0xFF;
  10.               DDRA  = 0xFF;
  11. }

  12. void spi_init(void)
  13. {
  14.               PORTB |= (1<<4) | (1<<5) | (1<<6) | (1<<7); //
  15.     DDRB |= (1<<5) | (1<<7) | (1<<4);     //Set MOSI, SCK AND SS as outputs
  16.     SPCR = 0xE3; //SPI中断使能,SPI使能,低位首发,从机模式,时钟模式00,上升沿采样;时钟速率Fosc/128
  17.     SPSR = 0x00; //SPI2x=0
  18. }

  19. void init_devices(void)
  20. {
  21.               port_init();
  22.               spi_init();
  23.               sei();//开全局中断
  24. }

  25. //主函;
  26. int main(void)
  27. {
  28.     init_devices();
  29.               while(1);
  30. }
  31. //SPI中断函数
  32. SIGNAL(SIG_SPI)
  33. {

  34.   PORTA=SPDR;

  35. }
复制代码

完整的Word格式文档51黑下载地址:
SPI.doc (663.5 KB, 下载次数: 41)


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:25103 发表于 2018-9-16 22:32 | 只看该作者
感谢分享!! 共同学习共同进步
回复

使用道具 举报

板凳
ID:264733 发表于 2019-11-2 10:43 | 只看该作者
谢谢分享
回复

使用道具 举报

地板
ID:611955 发表于 2019-11-15 07:13 | 只看该作者
感谢分享
回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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