ADuC7026串行通信接口SPI解析
0赞ADuC7026芯片集成了一个完整的片上硬件外围串行接口(SPI)。SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有DSP和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,比如ADuC70XX全系列MCU.
SPI总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。外围设置FLASHRAM、LCD显示驱动器ADC和MCU等。SPI总线系统可直接与各个厂家生产的多种标准外围器件直接接口,该接口一般使用4条线:串行时钟线(SCLK)、主机输入/从机输出数据线MISO、主机输出/从机输入数据线MOSI和低电平有效的从机选择线SS(有的SPI接口芯片带有中断信号线INT、有的SPI接口芯片没有主机输出/从机输入数据线MOSI)。
SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(用于单向传输时,也就是半双工方式)。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCLK(时钟),CS(片选)。
其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效。这就允许在同一总线上连接多个SPI设备成为可能。
接下来就负责通讯的3根线了。通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCLK时钟线存在的原因,由SCK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。
要注意的是,SCLK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。这样传输的特点:这样的传输方式有一个优点,与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCLK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCLK时钟线的控制可以完成对通讯的控制。SPI还是一个数据交换协议:因为SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。不同的SPI设备的实现方式不尽相同,主要是数据改变和采集的时间不同,在时钟信号上沿或下沿采集有不同定义,具体请参考相关器件的文档。
在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C系统要稍微复杂一些。
下面例子是在两片ADUC7026上的SPI通信,实现主机发送从机接收的功能。
SPI主机程序1和主机程序2,均实现了发送0X00—0X24的功能,只是在SPI主机程序2中利用中断来发送。从机程序1和从机程序2也都实现了接收主机发送数据的功能,只是从机程序2是利用中断程序来接收。
SPI主机程序 #includeunsigned char results[30] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24}; void delay (int length) { while (length >=0) length--; } int main(void) { int i = 0; GP1CON = 0x22220000; //使能SPI引脚 SPIDIV = 0xCC; //设定SPI时钟频率,f=41780000/(2x(1+SPIDIV)), // SPIDIV = 0xCC,对应f=100Kz SPICON = 0x104B; //连续传送数据使能和SPI主机使能,串行时钟 极性模式为低电平 for (i=0;i<30;i++) { SPITX = results[i]; //发送数据 while ((SPISTA & 0x02) != 0x02) ; //等待发送数据完成 delay(100000); } while (1){} } SPI主机程序2 /*#include #define SPI_MASTER_BIT 0x00002000 void IRQ_Handler(void) __irq; // 常规中断子程序 int i = 0; void delay (int length) { while (length >=0) length--; } int main(void) { GP4DAT = 0x04000000; // P4.2 输出低电平, LED 被点亮 IRQEN = SPI_MASTER_BIT; GP1CON = 0x22220000; // 设置SPI各功能引脚 SPIDIV = 0xCC; //设定SPI时钟频率,f=41780000/(2x(1+SPIDIV)), // SPIDIV = 0xCC,对应f=100Kz SPICON = 0x104B; //连续传送数据使能和SPI主机使能,串行时 //钟极性模式为低电平 while (1){ } } /*********************************************************************/ /* 中断服务子程序 */ /********************************************************************/ void IRQ_Handler() __irq { //主机发送 GP4DAT ^= 0x00040000; if ((IRQSTA & SPI_MASTER_BIT) != 0) { SPITX = i; delay(100000); i++; if (i==30) IRQCLR = SPI_MASTER_BIT; } return ; } SPI从机程序1 #include int main(void) { char i; char received_data[30]; GP1CON = 0x22220000; //设置SPI各功能引脚 SPICON = 0x1409; //使能SPI从机模式 for (i=0; i <30; i++) { while (!(SPISTA & 0x08)) ; // 等待SPIRX寄存器满 received_data[i] = SPIRX; //读数据并清零SPISTA的位4 } while (1) { } } SPI从机程序2 #include #define SPI_MASTER_BIT 0x00002000 void IRQ_Handler(void) __irq; // 常规中断子程序 char i; char received_data[30]; int main(void) { GP4DAT = 0x04000000; // 使P4.2输出低电平,LED点亮 IRQEN = SPI_SLAVE_BIT; //使能SPI从机中断 GP1CON = 0x22220000; //设置SPI各功能引脚 SPICON = 0x1409; // 使能SPI从机模式,且从机接收 while (1) { } } /*********************************************************************/ /* 中断服务子程序 */ /********************************************************************/ void IRQ_Handler() __irq { GP4DAT ^= 0x00040000; if ((IRQSTA & SPI_SLAVE_BIT) != 0) { received_data[i] = SPIRX; i++; SPITX = i; if (i==30) i=0; } return ; }
