单片机电路中的I2C总线(Inter Integrated Circuit Bus)是一种Philips公司推行的串行总线标准。连接在总线上的外设通过总线寻址,是具有总线仲裁和高低速设备同步等功能的高性能多主机总线。
1、总线由串行数据线SDA和串行时钟线SCL构成,可发送和接受数据。
2、所有连接在I2C总线上的器件和接口电路都必须具有I2C总线接口,且所有的SDL/SCL同名端相互连接。
3、在总线上各个I2C设备之间依靠SDA发送的地址信息进行寻址。
组成I2C系统的电路结构简单(两根线)、占用空间小、可以降低芯片的引脚数量、允许若干兼容器件共享总线。总线长度可达7.6m,传送速度可达400Kbps,标准速率100Kbps。支持多主控器件(某一时刻只能有一个主控器件)。需要注意的是:I2C线上的所有设备SDA、SCL引脚必须要外接上拉电阻(电阻值的大小会影响通信)。
I2C总线上的主器件在进行数据传输前需要通过寻址,选择需要进行通信的从器件。总线上所有外围器件都必须要有唯一的7位地址,由器件地址和引脚地址两部分组成。
器件地址是I2C器件固有的编码地址,器件出厂时已经给定不可更改。
引脚地址由I2C总线外设的地址引脚决定(A2、A1、A0),根据其在电路中的正负极或悬空等状态,形成不同的地址代码。(内部电路)
在实际应用中,往往遇到所使用的单片机没有I2C总线接口,例如典型的51系列单片机。为了让此类单片机用于操作 I2C总线器件的能力,需要在程序模拟I2C总线数据传输协议。
起始信号用于开启I2C总线的通信。其中,起始信号是在时钟线SCL为高电平期间,数据SDA上高电平向低电平变化的下降沿信号。起始信号出现以后,才可以进行后续的I2C总线寻址或数据传输;
终止信号用于终止I2C总线的通信。其中,终止信号是在时钟线SCL为高电平期间,数据线SDA上低电平到高电平变化的上升沿信号。终止信号一出现,所有I2C总线操作都结束,并释放总线控制权;
应答信号用于表明I2C总线数据传输的结束。 I2C总线数据传送时, 一个字节数据传送完毕后都必须由主器件产生应答信号。主器件在第9个时钟位上释放数据总线SDA,使其处于高电平状态,此时从器件输出低电平拉低数据总线SDA为应答信号。
I2C总线的传送格式为主从式,对系统中的某一器件来说有四种工作方式: 主发送方式、从发送方式、主接收方式、从接收方式。
单片机发送 24C02(存储芯片)接收:主器件产生开始信号以后,发送的第一个字节为控制字节。前七位为从器件的地址片选信号。最低位为数据传送方向位(高电平表示读从器件,低电平表示写从器件),然后发送一个选择从器件片内地址的字节,来决定开始读写数据的起始地址。接着再发送数据字节,可以是单字节数据,也可以是一组数据,由主器件来决定。从器件每接收到一个字节以后,都要返回一个应答信号(ASK=0)。主器件在应答时钟周期高电平期间释放SDA线,转由从器件控制,从器件在这个时钟周期的高电平期间必须拉低SDA线,并使之为稳定的低电平,作为有效的应答信号。
#include
#include
#define uchar unsigned char
#define uint unsigned int
#define OP_READ 0xa1 //24c02地址和读取操作
#define OP_WRITE 0xa0//写入操作
sbit SDA=P2^0;//器件数据总线
sbit SCL=P2^1; //器件时钟总线
sbit DS1302=P2^4;//1302片选端
sbit LCDEN=P2^5;
uchar code display[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0xff};
void delay1ms()
{
uchar i,j;
for(i=0;i<10;i++)
for(j=0;j<33;j++);
}
void delaynms(uchar n)
{
uchar i;
for(i=0;i<n;i++)
delay1ms();
}
void delay_ns(uint i)
{
uint y;
for(y=0;y<i;y++)
_nop_();//1us
}
void start() //I2C数据传输起始位
{
SCL=1;
SDA=1;
_nop_();
_nop_();
_nop_();
_nop_();
SDA=0;
_nop_();
_nop_();
_nop_();
_nop_();
SCL=0;
}
void stop()//停止位
{
SCL=1;
SDA=0;
_nop_();
_nop_();
_nop_();
_nop_();
SDA=1;
_nop_();
_nop_();
_nop_();
_nop_();
SDA=0;
SCL=0;
}
uchar ReadDate()//从器件中读数据
{
uchar i,x;
for(i=0;i<8;i++)
{
SCL=1;
x<<=1;
x|=(uchar)SDA;
SCL=0;
}
return x;
}
bit WriteCurrent(uchar y)//写入函数
{
uchar i;
bit ack_bit;
for(i=0;i<8;i++)
{
SDA=(bit)(y&0x80);
_nop_();
SCL=1;
_nop_();
_nop_();
SCL=0;
y<<=1;
}
SDA=1; //发送设备释放SDA线
_nop_();
_nop_();
SCL=1;
_nop_();
_nop_();
_nop_();
_nop_();
ack_bit=SDA;
SCL=0;
return ack_bit;
}
void WriteSet(uchar add,uchar date)//将数据写到add地址
{
start();
WriteCurrent(OP_WRITE); //选择芯片告知操作
WriteCurrent(add); //写入地址
WriteCurrent(date); //向上述地址传输数据
stop();
delaynms(4);
}
uchar ReadCurrent() //读数据后发送回单片机
{
uchar x;
start();
WriteCurrent(OP_READ);
x=ReadDate();
stop();
return x;
}
uchar ReadSet(uchar set_addr) //在指定地址读取数据
{
start();
WriteCurrent(OP_WRITE);
WriteCurrent(set_addr);
return(ReadCurrent());
}
main(void)
{
uchar i;
LCDEN=0;
DS1302=0;
P1=0xfe;
SDA=1;
SCL=1; //空闲状态
for(i=0;i<16;i++)
{
WriteSet(i,display[i]);
}
for(i=0;i<16;i++)
{
P1=ReadSet(i);
delaynms(200);
delaynms(200);
}
}
#include
#include
#define uchar unsigned char
#define uint unsigned int
#define delaynop(); {_nop_();_nop_();_nop_();_nop_();};//延时4us
#define PCF8591_WRITE 0x90
#define PCF8591_READ 0x91//IIC器件PCF5891的写地址和读地址
#define disdata P0
//液晶LCD1602定义
sbit lcd_RS=P1^0;
sbit lcd_RW=P1^1;
sbit lcd_EN=P2^5;
//数码管定义
sbit dula=P2^6;
sbit wela=P2^7;
sbit DS1302=P2^4;
//IIC总线定义
sbit SDA=P2^0;
sbit SCL=P2^1;
uint data dis[4]={0x00,0x00,0x00,0x00};
uchar code dis4[]={"1- . V 2- . V"};
uchar code dis5[]={"3- . V 4- . V"};
bit bdata SystemError;//丛机错误标志位
#define NUM 4 //缓存区深度
uchar idata receivebuf[NUM];//数据接收缓冲区
void stopSMG()//关闭数码管
{
dula=1;
P0=0x00;
dula=0;
}
void delay(int ms)//延时n毫秒
{
while(ms--)
{
uchar i;
for(i=0;i<250;i++)
{
_nop_();
_nop_();
_nop_();
_nop_();
}
}
}
//lcd1602器件设置
bit lcd_busy() //LCD检忙函数
{
bit result;
lcd_RS=0;
lcd_RW=1;
lcd_EN=1;
delaynop();
result=(bit)(P0&0x80);
lcd_EN=0;
return result;
}
void lcd_writezhiling(uchar cmd) //lcd写指令函数
{
while(lcd_busy());
lcd_RS=0;
lcd_RW=0;
lcd_EN=0;
_nop_();
_nop_();
P0=cmd;
delaynop();
lcd_EN=1;
delaynop();
lcd_EN=0;
}
void lcd_writedate(uchar date) //lcd写数据
{
while(lcd_busy());
lcd_RS=1;
lcd_RW=0;
lcd_EN=0;
P0=date;
delaynop();
lcd_EN=1;
delaynop();
lcd_EN=0;
}
void lcd_init() //lcd初始化
{
lcd_writezhiling(0x38);
lcd_writezhiling(0x0c);
lcd_writezhiling(0x06);
lcd_writezhiling(0x01);
lcd_writezhiling(0x80);
}
//AD数据处理与显示
show_value(uchar ad_date)
{
dis[2]=ad_date/51;
dis[2]=dis[2]+0x30;
dis[3]=ad_date%51;
dis[3]*=10;
dis[1]=dis[3]/51;
dis[1]+=0x30;
dis[3]%=51;
dis[3]*=10;
dis[0]=dis[3]/51;
dis[0]+=0x30;
}
//IIC设置
void iic_start() //起始信号
{
SCL=1;
SDA=1;
delaynop();
SDA=0;
delaynop();
SCL=0;
}
void iic_stop() //终止信号
{
SCL=1;
SDA=0;
delaynop();
SDA=1;
delaynop();
SDA=0;
}
void iic_init() //iic 初始化
{
SCL=0;
iic_stop();
}
//从机发送应答信号
void slave_ACK(void)
{
SDA=0;
SCL=1;
delaynop();
SCL=0;
SDA=1;
}
//发送非应答信号
void slave_NOACK(void)
{
SDA=1;
SCL=1;
delaynop();
SCL=0;
SDA=0;
}
//主机应答检查函数
void check_ACK(void)
{
SDA=1;
SCL=1;
F0=0;
delaynop();
if(SDA==1) //非应答,标志位置1
F0=1; //F0全局标志位
SCL=0;
}
//IIC发送单个字节
void IICSendByte(uchar ch)
{
uchar idata n=8; //向SDA上发送数据字节,8位
while(n--)
{
if((ch&0x80)==0x80) //发送数据的最高位为1则发送位1
{
SDA=1;
SCL=1;
delaynop();
SCL=0;
}
else//否则传送位0
{
SDA=0;
SCL=1;
delaynop();
SCL=0;
}
ch=ch<<1;
}
}
//接收单个字节函数
uchar IICreceiveByte(void)
{
uchar idata n=8;
uchar tdata=0;
while(n--)
{
SDA=1;
SCL=1;
tdata=tdata<<1;
if(SDA==1) //如接收到的是1,把数据的最后一位置1
tdata=tdata|0x01;
else //最后一位置0
tdata=tdata&0xfe;
SCL=0;
}
return tdata;
}
//发送n位数据函数
void DAC_PCF8591(uchar controlbyte,uchar w_data)
{
iic_start();
delaynop();
IICSendByte(PCF8591_WRITE);
check_ACK();
if(F0==1)
{
SystemError=1;
return;
}
IICSendByte(controlbyte&0x77);
check_ACK();
if(F0==1)
{
SystemError=1;
return;
}
IICSendByte(w_data);
check_ACK();
if(F0==1)
{
SystemError=1;
return;
}
iic_stop();
delaynop();
}
//连续读入4路通道A/D转化结果到receivebuf(缓冲区)
void ADC_PCF8591(uchar controlbyte)
{
uchar idata receive_da,i=0;
iic_start();
IICSendByte(PCF8591_WRITE);
check_ACK();
if(F0==1)
{
SystemError=1;
return;
}
IICSendByte(controlbyte);
check_ACK();
if(F0==1)
{
SystemError=1;
return;
}
iic_start();
IICSendByte(PCF8591_READ);
check_ACK();
if(F0==1)
{
SystemError=1;
return;
}
IICreceiveByte(); //空读一次
slave_ACK(); //收到一字解后发送一个应答位
while(i<4)
{
receive_da=IICreceiveByte();
receivebuf[i++]=receive_da;
slave_ACK();
}
slave_NOACK();
iic_stop();
}
main()
{
uchar i,l;
delay(10);
stopSMG();
DS1302=0;
lcd_init();
lcd_writezhiling(0);
i=0;
while(dis4[i]!='\0')
{
lcd_writedate(dis4[i]);
i++;
}
lcd_writezhiling(0x80+0x40);
i=0;
while(dis5[i]!='\0')
{
lcd_writedate(dis5[i]);
i++;
}
while(1)
{
iic_init();
ADC_PCF8591(0x04);
if(SystemError==1)
{
iic_init();
ADC_PCF8591(0x04);
}
for(l=0;l<4;l++)
{
//显示通道0
show_value(receivebuf[0]);
lcd_writezhiling(0x80+2);
lcd_writedate(dis[2]);
lcd_writezhiling(0x80+4);
lcd_writedate(dis[1]);
lcd_writezhiling(0x80+5);
lcd_writedate(dis[0]);
//显示通道1
show_value(receivebuf[1]);
lcd_writezhiling(0x80+11);
lcd_writedate(dis[2]);
lcd_writezhiling(0x80+13);
lcd_writedate(dis[1]);
lcd_writezhiling(0x80+14);
lcd_writedate(dis[0]);
//显示通道2
show_value(receivebuf[2]);
lcd_writezhiling(0x80+0x40+2);
lcd_writedate(dis[2]);
lcd_writezhiling(0x80+0x40+4);
lcd_writedate(dis[1]);
lcd_writezhiling(0x80+0x40+5);
lcd_writedate(dis[0]);
//显示通道3
show_value(receivebuf[3]);
lcd_writezhiling(0x80+0x40+11);
lcd_writedate(dis[2]);
lcd_writezhiling(0x80+0x40+13);
lcd_writedate(dis[1]);
lcd_writezhiling(0x80+0x40+14);
lcd_writedate(dis[0]);
iic_init();
DAC_PCF8591(0x40,receivebuf[0]);
if(SystemError==1)
{
iic_init();
DAC_PCF8591(0x40,receivebuf[0]);
}
}
}
}
#include
#include
#define uchar unsigned char
#define uint unsigned int
#define WRITE 0x90
#define READ 0x91
sbit fm=P2^3;
sbit RST=P2^4;
sbit dula=P2^6;
sbit wela=P2^7;
sbit SDA=P2^0;
sbit SCL=P2^1;
bit ADflag;
void delayms(uchar i)
{
uint n;
for(;i>0;i--)
for(n=0;n<125;n++);
}
void timer0_init()
{
TMOD|=0x10;
TH1=0xff;
TL1=0x00;
EA=1;
ET1=1;
TR1=1;
}
void start(void) //IIC起始信号
{
SDA=1;
_nop_();
SCL=1;
_nop_();
SDA=0;
_nop_();
SCL=0;
}
void stop(void) //IIC停止信号
{
SDA=0;
_nop_();
SCL=1;
_nop_();
SDA=1;
_nop_();
SCL=0;
}
void ack(void) //应答信号
{
SDA=0;
_nop_();
SCL=1;
_nop_();
SCL=0;
_nop_();
}
void noack(void) //非应答信号
{
SDA=1;
_nop_();
SCL=1;
_nop_();
SCL=0;
_nop_();
}
void send(uchar Data)//发送一个字节
{
uchar bitcounter=8;
uchar temp;
do{
temp=Data;
SCL=0;
_nop_();
if((temp&0x80)==0x80)
SDA=1;
else
SDA=0;
SCL=1;
temp=Data<<1;
Data=temp;
bitcounter--;
}while(bitcounter);
SCL=0;
}
uchar read(void)//读入一个字节并返回
{
uchar temp=0;
uchar temp1=0;
uchar bitcounter=8;
SDA=1;
do{
SCL=0;
_nop_();
SCL=1;
_nop_();
if(SDA)
temp=temp|0x01;
else
temp=temp&0xfe;
if(bitcounter-1)
{
temp1=temp<<1;
temp=temp1;
}
bitcounter--;
}while(bitcounter);
return(temp);
}
void DAC(uchar Data)//写入DA数模转换值
{
start();
send(WRITE);
ack();
send(0x40); //控制位,使能DAC输出
ack();
send(Data);
ack();
stop();
}
uchar readADC(uchar ch1)//读取模数转换值
{
uchar Data;
start();
send(WRITE);
ack();
send(0x40|ch1);//写入选择通道1,2,3,4
ack();
start();
send(READ);
ack();
Data=read();
SCL=0;
noack();
stop();
return Data;
}
void cmg()
{
dula=1;
P0=0x00;
dula=0;
wela=1;
P0=0x00;
wela=0;
RST=0;
}
void main()
{
uchar num=0;
uchar adtemp;
timer0_init();
cmg();
// DS1302=0;
fm=1;
while(1)
{
for(num=0;num<255;num++)
{
DAC(num);
delayms(10);
}
for(num=255;num>0;num--)
{
DAC(num);
delayms(10);
}
if(ADflag)
{
ADflag=0;
}
}
}
1、单工:单工是指数据传输仅能沿一个方向进行。
2、半双工:半双工是指数据传输可以沿两个方向,但需要分时进行。
3、全双工:全双工是指数据可以同时进行双向传输
如约定采用奇校验:发送数据时,数据位尾随1位奇校验位,奇校验 位可以取0或1,但要保证,数据中“1”的个数与校验位“1”的个数之和 应为奇数;接收数据时,检查数据中“1”的个数与校验位“1”的个数之 和是否仍应为奇数,如不是,则说明数据在传输过程中出现了差错。
如约定采用偶校验:发送数据时,数据位尾随1位偶校验位,偶校验 位可以取0或1,但要保证,数据中“1”的个数与校验位“1”的个数之和 应为偶数;接收数据时,检查数据中“1”的个数与校验位“1”的个数之 和是否仍应为偶数,如不是,则说明数据在传输过程中出现了差错。
简称数传率,指单位时间内传输的信息量,可用比特率和波特率来表示。
⑴、比特率:是数字信号的传输速率,它用单位时间内传输的二进制代码的有效位(bit)数来表示,其单位为每秒比特数bit/s(bps) 。
⑵、波特率:波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,其单位为波特(Baud)。
对于串行通信来说,或者说是对于普通的数字电路来说,都是两相调制,也就是单个调制状态对应1个二进制位,因此: 比特率= 波特率 1bps=1 Baud 计算机中常用的波特率是:110、300、600、1200、2400、4800、9600、 19200、28800、33600,目前最高可达56Kbps. CH340转出的虚拟串口的波特率突破了传统串口的56Kbps限制,最高可达 2Mbps。
1、TX设置为输出、RX设置为输入,也可不设置,采用默认的准双向口模式。
2、设置串口1 的工作模式,SCON 寄存器中的SM0 和SM1 两位决定了串口1 的4 种工作模式。
3、如要串口1 接收,将SCON 寄存器中的REN 位置1 即可。
4、计算BRT的值,并置数;
5、设置独立波特率发生器相关位:BRTx12:=0, S1BRS: =1, SMOD
6、启动独立波特率发生器( BRTR =1);
7、串行口工作在中断方式时,还要设置串口1 的中断优先级 ( PS,PSH ),如果不设置的话,默认是低优先级;打开中断相应的 控制位(ES,EA)。接收完成标志RI,发送完成标志TI,要由软件清0。
#include
#define uchar unsigned char
#define uint unsigned int
uchar Receive(void) //接收一个字节数据
{
uchar dat;
while(RI==0);//RI没有被置1,就一直等待单片机数据接收完毕(RI=1)
RI=0;
dat=SBUF;
return dat;
}
void mian()
{
TMOD=0x20;//定时器T1工作方式2;
SCON=0x50; //串口工作方式1,允许接收(REN=1)
PCON=0x00; //波特率9600
TH1=0xfd;
TL1=0xfd;
TR1=1;
REN=1;//允许接收
while(1)
{
P1=Receive();
}
}
#include
#define uchar unsigned char
uchar code Tab[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
void Send(uchar dat)
{
SBUF=dat;
while(TI==0);
TI=0;
}
void delay()
{
uchar m,n;
for(m=0;m<200;m++)
for(n=0;n<250;n++);
}
void main()
{
uchar i;
TMOD=0x20;
SCON=0x40; //串口工作方式1
PCON=0x00; //设置波特率9600
TH1=0xfd;
TL1=0xfd;
TR1=1;
while(1)
{
for(i=0;i<8;i++)
{
Send(Tab[i]);
delay();
}
}
}