简介:
串行外围设备接口
全双工三线同步,可以同时发出和接收串行数据
采用主从(Master Slave)架构,支持多Slave模式应用,一般仅支持单Slave
时钟由Master控制,在时钟移位脉冲下,数据按位传输,高位在前,低位在后
目前应用中可以达到几Mbps的水平
优点:与普通的串行设备相比,可以按位传输,甚至可以暂停。当没有时钟跳变时,从设备不采集和传送数据。不需要寻址操作。全双工通信。
缺点:没有应答机制确认。
特点:
提供频率可编程时钟
发送结束、中断标志;写冲突保护
总线竞争保护
SPI总线工作的4种工作方式中,使用最广泛的是SPI0和SPI3方式
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
信号线情况:
SCLK提供时钟脉冲,SDI/SDO基于此脉冲按位传输。当处于上升沿模式时,输出:通过SDO线在时钟上升沿时输出,在紧接着的下降沿被读取。输入同理。
SS/CS是片选信号线,只有片选信号为使能信号时,对芯片的操作才有效,所以可以在同一总线上连接多个SPI设备
SDI:slave → master,从机要发送给主机的数据
SDO:master → slave,主机要发送给从机的数据
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
连接方式:
级联方式:此时所有设备的CS端都连在一起,只要选中一个设备,则全选。可以作为一个设备进行处理。
独立连接方式:设备独立操作,为被选通的从设备均处于高阻隔离状态。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
工作模式:
SPI模式 | CPOL极性 | CPHA相位 | 说明 |
0 | 0 | 0 | 第一个边沿上升沿 |
1 | 0 | 1 | 第二个边沿下降沿 |
2 | 1 | 0 | 第一个边沿下降沿 |
3 | 1 | 1 | 第二个边沿上升沿 |
CPOL=0:SCLK有效时为高电平(active-high)
CPOL=1:SCLK有效时为低电平(active-low)
CPHA=0:表示第一个边沿
CPHA=1:表示第二个边沿
Toggling edge为切换边沿,输出信号
Sampling edge为采样边沿,输入信号
时序图:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SPI协议举例
主机8位寄存器存放的是1010 1010,从机存放的是0101 0101,将主从机数据交换
SDI:slave → master
SDO:master → slave
上升沿发送、下降沿接收
初始化就绪状态:
主机SBUFF = 1010 1010
从机SBUFF = 0101 0101
操作过程:如图所示,经过8个脉冲后,master和slave数据交换
SPI的8个时钟周期的数据:
————————————————————————————————————————————
基于SPI协议,DS1302显示时钟实例
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
实现效果:
实现代码:
1 #include 2 typedef unsigned char uchar; 3 typedef unsigned int uint; 4 //写操作控制字节,D7=1,D0=0 5 uchar code write_address[] = 6 { 7 //秒,分,小时,日,月,星期,年 8 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c 9 }; 10 //读操作,D7=1,D0=1,地址同写操作 11 uchar code read_address[] = 12 { 13 0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d 14 }; 15 uchar code table[] = 16 { 17 //0,1,2,3,4,5,6,7,8,9 18 0xfc, 0x60, 0xda, 0xf2, 0x66, 0xb6, 0xbe, 0xe0, 0xfe, 0xf6 19 }; 20 //dat1和dat2存放读出来的时间,初始值写入12年5月9日1时1分1秒,dat1存放1234位,dat2存放567位 21 uchar dat1[] = {0x01, 0x01, 0x01, 0x09, 0x05, 0x02, 0x12}; 22 uchar dat2[] = {0x01, 0x01, 0x01, 0x09, 0x05, 0x02, 0x12}; 23 sbit rst = P3 ^ 0; 24 sbit scl = P3 ^ 1; 25 sbit sda = P3 ^ 2; 26 sbit ACC7 = ACC ^ 7; 27 void Delay(uint m) 28 { 29 while(m--); 30 } 31 /* SPI协议操作,读字节 */ 32 uchar ReadByte() 33 { 34 uchar i; 35 for (i = 0; i < 8; ++i) 36 { 37 ACC = ACC >> 1; //累加器左移1位,补上未知数x 38 ACC7 = sda; //从sda引脚写入ACC最高位 39 scl = 1; 40 scl = 0; //时钟下降沿读入 41 } 42 return ACC; 43 } 44 /* SPI协议操作,写字节 */ 45 void WriteByte(uchar byte) 46 { 47 uchar i; 48 for (i = 0; i < 8; ++i) 49 { 50 byte >>= 1; //byte左移1位存入CY 51 scl = 0; 52 sda = CY; //从CY移入sda,发送给DS102 53 scl = 1; //时钟上升沿写入 54 } 55 } 56 void Write1302(uchar address, uchar dat) //写地址子程序 57 { 58 rst = 0; 59 scl = 0; 60 rst = 1; //rst上升沿开始写数据 61 WriteByte(address); //先写入地址控制字节 62 WriteByte(dat); //再写入数据字节 63 rst = 0; 64 } 65 uchar Read1302(uchar address) 66 { 67 uchar temp; 68 rst = 0; 69 scl = 0; 70 rst = 1; //读过程中保持rst高电平状态 71 WriteByte(address | 0x01); //写入地址并置R/W位为1(读) 72 temp = ReadByte(); //在单片机写入命令字节的最后一位的第一个下降沿处即读出数据 73 scl = 1; 74 rst = 0; 75 return temp; 76 } 77 void SetRST() 78 { 79 uchar i; 80 Write1302(0x8e, 0x00); //向10001110写保护寄存器,写入指令0x00 81 for (i = 0; i < 7; ++i) 82 Write1302(write_address[i], dat1[i]); //从秒到年各寄存器写入对应初始值 83 Write1302(0x8e, 0x80); //向写保护寄存器,写入数据0x80 84 } 85 void ReadTime() 86 { 87 uchar i, temp1, temp2, temp3; 88 temp3 = 0x80; //temp3存放时间寄存器地址 89 for (i = 0; i < 7; ++i) //分别读出秒分小时日月星期年 90 { 91 temp1 = Read1302(temp3); 92 temp2 = temp1; 93 dat1[i] = (temp1 >> 1) & 0x0f; //读出的数据1234位存入dat1,屏蔽其他位 94 dat2[i] = (temp2 >> 5) & 0x07; //读出的数据567位存入dat2,屏蔽其他位 95 temp3 = temp3 + 0x02; //下一个寄存器地址 96 } 97 } 98 void main() 99 { 100 rst = 0; 101 SetRST(); //时钟建立 102 while(1) 103 { 104 ReadTime(); //读时间 105 P2 = 0xfe; 106 P1 = table[dat1[0] % 10]; 107 Delay(500); 108 P2 = 0xfd; 109 P1 = table[dat2[0] % 10]; 110 Delay(500); 111 P2 = 0xfb; 112 P1 = 0x02; // - 113 Delay(500); 114 P2 = 0xf7; 115 P1 = table[dat1[1] % 10]; 116 Delay(500); 117 P2 = 0xef; 118 P1 = table[dat2[1] % 10]; 119 Delay(500); 120 P2 = 0xdf; 121 P1 = 0x02; // - 122 Delay(500); 123 P2 = 0xbf; 124 P1 = table[dat1[2] % 10]; 125 Delay(500); 126 P2 = 0x7f; 127 P1 = table[dat2[2] % 10]; 128 Delay(500); 129 } 130 }
相关文章