第一个区别当然是名字:
SPI(Serial Peripheral Interface:串行外设接口);
I2C(INTER IC BUS)
UART(Universal Asynchronous Receiver Transmitter:通用异步收发器)
第二,区别在电气信号线上:
SPI总线由三条信号线组成:串行时钟(SCLK)、串行数据输出(SDO)、串行数据输入(SDI)。SPI总线可以实现 多个SPI设备互相连接。提供SPI串行时钟的SPI设备为SPI主机或主设备(Master),其他设备为SPI从机或从设备(Slave)。主从设备间可以实现全双工通信,当有多个从设备时,还可以增加一条从设备选择线。
如果用通用IO口模拟SPI总线,必须要有一个输出口(SDO),一个输入口(SDI),另一个口则视实现的设备类型而定,如果要实现主从设备,则需输入输出口,若只实现主设备,则需输出口即可,若只实现从设备,则只需输入口即可。
I2C总线是双向、两线(SCL、SDA)、串行、多主控(multi-master)接口标准,具有总线仲裁机制,非常适合在器件之间进行近距离、非经常性的数据通信。在它的协议体系中,传输数据时都会带上目的设备的设备地址,因此可以实现设备组网。
如果用通用IO口模拟I2C总线,并实现双向传输,则需一个输入输出口(SDA),另外还需一个输出口(SCL)。(注:I2C资料了解得比较少,这里的描述可能很不完备)
UART总线是异步串口,因此一般比前两种同步串口的结构要复杂很多,一般由波特率产生器(产生的波特率等于传输波特率的16倍)、UART接收器、UART发送器组成,硬件上由两根线,一根用于发送,一根用于接收。
显然,如果用通用IO口模拟UART总线,则需一个输入口,一个输出口。
第三,从第二点明显可以看出,SPI和UART可以实现全双工,但I2C不行;
第四,看看牛人们的意见吧!
wudanyu:I2C线更少,我觉得比UART、SPI更为强大,但是技术上也更加麻烦些,因为I2C需要有双向IO的支持,而且使用上拉电阻,我觉得抗干扰能力较弱,一般用于同一板卡上芯片之间的通信,较少用于远距离通信。SPI实现要简单一些,UART需要固定的波特率,就是说两位数据的间隔要相等,而SPI则无所谓,因为它是有时钟的协议。
quickmouse:I2C的速度比SPI慢一点,协议比SPI复杂一点,但是连线也比标准的SPI要少。
#include #define WRITE 0xA0 #define READ 0xA1 #define BLOCK_SIZE 100 #define uchar unsigned char #define HIGH 1 #define LOW 0 #define FALSE 0 #define TRUE ~FALSE sbit SCL =P3^4; //T0 sbit SDA =P3^5; //T1 uchar xdata EAROMImage[BLOCK_SIZE]={0}; void delayi2c( void ) { ; } void I_start( void ) { SCL = HIGH ; delayi2c() ; SDA = LOW ; delayi2c() ; SCL = LOW ; delayi2c() ; } void I_stop( void ) { SDA = LOW ; delayi2c() ; SCL = HIGH ; delayi2c() ; SDA = HIGH ; delayi2c() ; SCL = LOW ; delayi2c() ; } //初始化 void I_init( void ) { SCL = LOW ; I_stop() ; } bit I_clock( void ) { bit sample ; SCL = HIGH ; delayi2c() ; sample = SDA ; SCL = LOW ; delayi2c() ; return ( sample ) ; } //发送8位数据 bit I_send( uchar I_data ) { uchar i ; for ( i=0 ; i<8 ; i++ ) { SDA = (bit)( I_data & 0x80 ) ; I_data = I_data << 1 ; I_clock() ; } SDA = HIGH ; return ( ~I_clock() ); } //接受8位数据 uchar I_receive( void ) { uchar I_data = 0 ; register uchar i ; for ( i=0 ; i<8 ; i++ ) { I_data *= 2 ; if (I_clock()) I_data++ ; } return ( I_data ) ; } //应答 void I_Ack( void ) { SDA = LOW; I_clock(); SDA = HIGH; } [page] void wait_5ms( void ) { int i ; for ( i=0 ; i<1000 ; i++ ) { ; } } //向24C04写入器件地址和一个指定的字节地址。 bit E_address(uchar page ,uchar Address ) { I_start() ; if ( I_send( WRITE +page) ) return ( I_send( Address ) ) ; else return ( FALSE ) ; } //参数的含义:从第几个模块(不超过3),模块中第几个字节(不超过255) // 写到RAM映象的第几个字节和读的长度 bit E_read_block(uchar page, uchar addr,uchar arraypoint,uchar longth) { uchar i ; if ( E_address(page, addr ) ) { I_start() ; if ( I_send( READ+page ) ) { for ( i=0; i<=longth ;i++ ) { EAROMImage[arraypoint+i] =I_receive(); if ( i != longth ) I_Ack() ; else { I_clock() ; I_stop() ; } } return ( TRUE ) ; } else { I_stop() ; return ( FALSE ) ; } } else I_stop() ; return ( FALSE ) ; } bit E_write_block(uchar page,uchar addr,uchar arraypoint,uchar longth) { uchar i ; for ( i=addr; i<=addr+longth ; i++ ) { if ( E_address(page,i) && I_send( EAROMImage[arraypoint+i-addr] ) ) { I_stop() ; wait_5ms(); } else return ( FALSE ) ; } return ( TRUE ) ; } //test void main() { EAROMImage[39]=0xfe; SCON = 0x5a; TMOD = 0x20; TCON = 0x69; TH1 = 0xfd; I_init(); // I2C 总线初始化 P1=0xFF; if (E_write_block(0,8,39,1)) P1=0xFE;//p10 else {} if (E_read_block(0,8,55,1)) {} else P1=P1&0xFD; if(EAROMImage[55]==0xfe) P1=P1&0x0FB; while(1); }