总线(Bus)是计算机各种功能部件之间传送信息的公共通信干线,它是由导线组成的传输线束, 按照计算机所传输的信息种类,计算机的总线可以划分为数据总线、地址总线和控制总线,分别用来传输数据、数据地址和控制信号。
内总线,又称系统总线或板级总线,它是计算机各功能部户之间的传输通路,微型计算机总线通常称为内总线。外总线,又称通信总线,它是计算机系统之间,或者是计算机主机与外围设备之间的传输通路。摘自总线_百度百科。
通俗的说,总线是一种规定好的通信形式,内总线是指CPU与其他芯片的通信,如I2C,SPI,PCI-e;外总线是计算机与其他设备连接的通信,如SATA,USB。
当然,使用哪种总线形式需要根据使用场景来选择。根据不同的传输速率、传输距离、设备成本来选择总线。在51单片机与其他模块进行通信时,经常采用I2C和SPI两种串行通信方式传输数据。
I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。摘自I2C_百度百科。
特征 | 参数 | 解释 |
---|---|---|
线制 | 两线制 | 时钟线SCL,数据线SDA |
传输速率 | 400k | 快速模式最高可达400k |
传输距离 | 小于5m | 最好不要超过30cm,否则信号会被干扰 |
时钟信号 | 同步通信 | 收发双方具有同频同相的同步时钟信号 |
数据信号 | 串行通信 | - |
信号类型 | 单端输入 | 便宜,方便,抗干扰能力差 |
传送方向 | 半双工 | 数据可以在两个方向上传输,但是不能同时传输 |
SDA和SCL两条线分别接了一个上拉电阻,说明闲时为高电平,有信号时为低电平。
一次I2C总线通信的流程大致可分为:
注:根据I2C总线规定,部分要求高电平/低电平持续时间在5us左右,因此以下代码中的Delay()函数都是延时10us。
I2C总线协议规定,在SCL为高电平期间,产生SDA下降沿为启动信号。
void IIC_Start() //总线启动信号
{
SDA=1;
Delay(); //延时约10us左右即可
SCL=1; //时钟线为高电平期间
Delay(); //建立时间是SDA保持时间>4.7us
SDA=0; //数据线产生一个下降沿信号
Delay(); //保持时间是>4us
SCL=0; //时钟线拉低,准备发送数据
Delay();
}
I2C总线规定,在时钟信号位高电平期间,数据线要求必须保持稳定,在时钟信号在低电平期间,数据线上的电平才允许变化。
根据函数的封装规则(高内聚低耦合),可以把发送数据和接收应答信号两个功能防到一个函数中。以下为普中科技的I2C总线发送数据的源码,代码写的非常优美,仅略作修改方便理解。
该函数功能是向I2C总线上的设备发送一个字节数据,参数为需要发送的数据,返回值用于判断是否发送成功;发送成功为1,发送失败为0。
unsigned char IIC_WriteByte(unsigned char dat)
{
unsigned char i=0; //循环变量
unsigned char count=0; //最大255,一个机器周期为1us,最大延时255us
for(i=0;i<8;i++) //要发送8位,从最高位开始
{
//起始信号之后SCL=0,所以可以直接改变SDA信号
SDA=dat>>7; //这里设计由char类型到bit类型的转换,SDA只能读取dat最低位 因此右移7
dat=dat<<1; //将下一次要发送的数据传到最高位
Delay();
SCL=1;
Delay(); //建立时间>4.7us
SCL=0;
Delay(); //建立时间>4us
}
SDA=1;
Delay();
SCL=1; //两个线都拉高,表示总线空闲,等待应答信号
while(SDA) //等待应答,也就是等待从设备把SDA拉低
{
count++;
if(count>200) //如果超过200us没有应答发送失败,或者为非应答,表示接收结束
{
SCL=0;
Delay();
return 0; //返回0,表示发送失败
}
}
SCL=0;
Delay();
return 1; //返回1,表示发送成功
}
在郭天祥的书中,是通过状态寄存器PSW来发送的信号。将要发送的数据左移(temp=temp<<1),会移入PSW寄存器的最高位CY,再将CY中的值赋给SDA(SDA=CY)。
在查阅了相关资料后发现,CY位来源于最近一次算术指令或逻辑指令执行时软硬件的改写,表示加法运算中有进位或减法运算中有借位则CY位置为1,否则为0。
通俗来说,在上述情景I2C通信中,CY暂存了temp=temp<<1这句中被位移出去的最高位;如果temp=temp>>1,那么CY位暂存被位移出去的最低位。而所谓的加法进位减法借位,与计算机的补码储存方式有关,通过CY位能实现相应的加减法算法。
unsigned char IIC_ReadByte()
{
unsigned char i=0; //循环变量
unsigned char dat=0;//用于接收传送的数据
SDA=1; //起始和发送一个字节之后SCL都是0
Delay();
for(i=0;i<8;i++) //接收8个字节
{
SCL=1;
Delay();
dat<<=1; //dat左移一位
dat|=SDA; //同样涉及bit类型与char类型的强转,最低位与SDA按位取与后赋值
Delay();
SCL=0;
Delay();
}
return dat;
}
I2C总线规定,SCL为高电平期间,SDA产生一个上升沿为终止信号。
void IIC_Stop() //总线结束信号
{
SDA=0;
Delay();
SCL=1;
Delay(); //建立时间>4.7us
SDA=1;
Delay();
}
几种常用I2C通信的模块有:
引脚 | 名称 | 解释 |
---|---|---|
1-3 | A0-A2 | 可编程地址输入端 |
4 | GND | 接地端 |
5 | SDA | 数据信号线 |
6 | SCL | 时钟信号线 |
7 | WP | 写保护输入端 |
8 | Vcc | 电源端 |
AT24C02的芯片地址为1010, 其地址控制字格式为。
位序号 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|
控制字格式 | 1 | 0 | 1 | 0 | A2 | A1 | A0 | R/W |
其中A2,A1,A0为可编程地址选择位。A2,A1,A0引脚接高、低电平后得到确定的三位编码,与1010形成7位编码,即为该器件的地址码。R/W为读写控制位,该位为0,表示对芯片进行写操作;该位为1,表示对芯片进行读操作。
如果A2,A1,A0引脚接低电平时候,发送数据地址为A0,接收数据地址为A1。
AT24C02的存储容量为2KB,内部分成32页,每页8B,共256B,芯片寻址可对内部256B中的任一个进行读/写操作,其寻址范围为00~FF,共256个寻址单元。
串行EEPROM有字节写入,页写入,指定地址读操作,指定页读操作。
以下伪代码假设AT24C02地址为1010 000X。
IIC_Start();//发送起始信号
IIC_WriteByte(0xA0);//发送寻址信号
IIC_WriteByte(addr);//发送需要写入的片内地址
IIC_WriteByte(date);//发送需要在addr写入的信息
IIC_Stop(); //结束本次发送
IIC_Start();//发送起始信号
IIC_WriteByte(0xA0);//发送寻址信号
IIC_WriteByte(addr);//发送需要写入的片内地址
IIC_WriteByte(date1);//发送需要在addr写入的信息
IIC_WriteByte(date2);//发送需要在addr+1写入的信息
.
.
.
IIC_WriteByte(dateN);//发送需要在addr+n写入的信息
IIC_Stop(); //结束本次发送
需要注意的是,n最大为7,因为单片机在一个数据周期内,只能访问一页(8个)储存单元。
相对于字节写入模式,速度较快。
IIC_Start(); //发送起始信号
IIC_WriteByte(0xA0);//发送写命令
IIC_WriteByte(addr);//发送需要写入的片内地址
IIC_Start(); //发送起始信号
IIC_WriteByte(0xA1);//发送读命令
date=IIC_ReadByte();//接受数据
IIC_Stop(); //结束本次发送
IIC_Start(); //发送起始信号
IIC_WriteByte(0xA0);//发送写命令
IIC_WriteByte(addr);//发送需要写入的片内地址
IIC_Start(); //发送起始信号
IIC_WriteByte(0xA1);//发送读命令
date1=IIC_ReadByte();//接受数据
date2=IIC_ReadByte();//接受数据
.
.
.
dateN=IIC_ReadByte();
IIC_Stop(); //结束本次发送
注:一般使用AT24C02储存数据,使用I2C总线传输是为了节约IO口,而不是追求传输速率,为了数据稳定起见,尽量采用字节传输方式。
特征 | 参数 | 解释 |
---|---|---|
线制 | 四线制 | MISO,MOSI,SCLK,CS |
传输速率 | 1-25M不等 | 与主机晶振频率和收发双方的处理速度有关 |
传输距离 | 小于5m | 最好不要超过30cm,否则信号会被干扰 |
时钟信号 | 同步通信 | 收发双方具有同频同相的同步时钟信号 |
数据信号 | 串行通信 | - |
信号类型 | 单端输入 | 便宜,方便,抗干扰能力差 |
传送方向 | 全双工 | 数据可以同时双向传输 |
需要注意的是,SPI总线没有应答机制,贸然提高传输速率可能会导致接收方缓冲区数据未处理,造成数据丢失。
传输线名称 | 解释 |
---|---|
SCLK/CK/SLK | 时钟信号,只能由主设备发出 |
MOSI/SDIN | 主设备数据输出/从机输入端 |
MISO/SDOUT | 主设备数据输入/从机输出端 |
CS/SS | 使能信号/片选信号 |
简单概况SPI总线的时序为:时钟线上升沿发送、下降沿接收、高位先发送。
typedef unsigned int u16;
typedef unsigned char u8;
sbit DOUT = P3^7; //输出
sbit SCLK = P3^6; //时钟
sbit DIN = P3^4; //输入
sbit CS = P3^5; //片选
void SPI_Write(u8 dat)//写数据
{
u8 i;
SCLK = 0;
for(i=0; i<8; i++)
{
DIN = dat >> 7; //放置最高位
dat <<= 1; //移位,把下一次需要写入的数据放到最高位
SCLK = 0; //上升沿放置数据
SCLK = 1;
}
}
u8 SPI_Read(void)
{
u8 i,dat=0; //存放需要接收的数据
SCLK = 0;
for(i=0; i<8; i++) //接收8位数据
{
dat <<= 1;
SCLK = 1;
SCLK = 0; //时钟信号下降沿读取数据
dat |= DOUT; //最低位赋值
}
return dat;
}