❶ 这是一个单片机I2C的程序,麻烦高手帮忙指点下;为什么有时显示不行呢,虽然概率很低,要隔很长
1、可能延时时间不够,器件读写速度慢,跟不上。
void delay()
{ ;; } //延迟5毫秒左右;
你用多少的晶振?12M的晶振标准51核,只能延时2个微秒。
2、write_byte(0xaa);
respons();
像这种写入数据的操作需要时间比较长,你的等待时间可能不够。查看你器件的datasheet,看它的写入时间需要多久。
3、拿示波器看一下你IIC总线的波形的上升时间和下降时间能不能满足IIC规范的要求。
4、嵌入式系统从稳定性考虑,最好对每个函数的操作成功与否进行判断,系统要对错误的情况进行处理。比如楼主的respons();函数,比较好的一种处理方式是:如果等待超时,那么返回错误码,由上层函数决定如何处理错误。重发or放弃,等等。 楼主刚刚开始学习不必要深究,可以思考下这方面的问题。
❷ 请问I2C总线如何实现与单片机的应用
IIC只是一个通信协议,只要两个终端符合这个协议就可以实现数据的交换,有的单片机已经集成了IIC的外设,用起来比较简单,还有一部分并没有这样的外设,就要用IO口来模拟出IIC的两条线,一样可以实现通信,祝成功~
❸ 单片机iic程序不懂给分析下啊
贴个程序给你看看,,,单片机没有I2C总线接口,只能靠软件模拟
/*程序的I2C从器件地址为1010,片选地址为000*/
#include <reg52.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit SDA=P1^7;
sbit SCL=P1^6;
void Delay(uint us)
{
for(us;us>0;us--);
}
void start_iic() // 启动I2C总线
{
SDA=1; // 发送起始条件数据信号,启动时,
SCL=1; // 必需使数据线、时钟信号线处于高电平(空闲态)
Delay(10); // 使用I2C总线必需考虑保持时间和建立时间,故延时
SDA=0; //产生下降沿,发送起始信号
Delay(10);
SCL=0;
}
void stop_iic()
{
SDA=0; //为产生上跳沿做准备
SCL=1; //打开时钟线
Delay(10);
SDA=1; //产生停止信号(上跳沿有效)
Delay(10);
SCL=0; //时钟线恢复无效态//
}
void ack_iic()
{
SDA=0; // 接受器件发送应答信号
SCL=1;
Delay(10);
SCL=0;
SDA=1; //应答信号低电平有效,故需将其重新置高电平
}
void nack_iic()
{
SDA=1; //主器件发非应答信号,通知AT24C08不再发送数据
SCL=1;
Delay(10);
SCL=0;
SDA=0; //非应答信号高电平有效,故需将其重新置低电平
}
Write_byte(uchar c)
{
uchar i;
for(i=0;i<8;i++)
{
if(c&0x80)SDA=1;
else SDA=0;
SCL=1;
Delay(10);
SCL=0; //因为当时钟线有效是,数据线必须保持稳定的电平,
c=c<<1; //要改变SDA电平,应先将SCL拉低
}
SDA=1; //释放I2C总线,准备接受应答信号
SCL=1;
Delay(10);
if(SDA==1)F0=0; //没有接到应答位
else F0=1;
SCL=0;
}
uchar Read_byte()
{
uchar i;
uchar r=0;
SDA=1; //置数据线为输入方式
for(i=0;i<8;i++)
{
r=r<<1;
SCL=1;
Delay(10); //保证一定的电平保持时间
if(SDA==1)r++; //从高位开始,一位一位的读
SCL=0;
}
return r;
}
main()
{
uchar slave=0xa0; //I2C总线从器件地址(注意:硬件电路的接法要是片选地址为0,否则不能工作)
uchar Rslave=slave+1; //主器件发送读控制字字节
uchar addre=0x20; // 指定的写数据地址
uchar wbuf=0x23; //将要写进addre的数据
uchar rbuf; //存放读出的数据的临时变量
start_iic(); //产生起始信号
Write_byte(slave); //发送从器件地址
if(F0==0)return 0; //检查应答位
Write_byte(addre); //发送目的地址
if(F0==0)return 0;
Write_byte(wbuf); //发送8为数据
if(F0==0)return 0;
stop_iic(); //停止信号
/*8位的数据发送完毕*/
Delay(1000);
start_iic();
Write_byte(slave);
if(F0==0)return 0;
Write_byte(addre);
if(F0==0)return 0;
start_iic(); //再次产生起始信号,不能少
Write_byte(Rslave); //送读控制字
if(F0==0)return 0;
rbuf=Read_byte(); //读出指定单元的内容
nack_iic(); //非应答信号
stop_iic();
/*8位的数据读取完毕*/
TMOD=0x20; //串口调试
TL1=0xfd;
TH1=0xfd;
SCON=0x40;
PCON=0x00;
TR1=1;
while(1)
{
SBUF=rbuf; //放入缓冲
while(TI==0);
TI=0;
Delay(10000);
}
}
❹ 单片机i2c总线编程,
return 一旦执行,就退出函数了。
也就是说 如果flag==0 那么就return0 后面的就不执行了,退出函数
❺ 求:51单片机模拟i2c总线程序
//给你一个简单的可断电保存的计时程序,用的是24C08
#include <reg52.h> // 包含51单片机寄存器定义的头文件
#include <intrins.h> //包含_nop_()函数定义的头文件
#define OP_READ 0xa1 // 器件地址以及读取操作,0xa1即为1010 0001B
#define OP_WRITE 0xa0 // 器件地址以及写入操作,0xa1即为1010 0000B
sbit SCL=P3^4; //将串行时钟总线SCL位定义在为P3.4引脚
sbit SDA=P3^5; //将串行数据总线SDA位定义在为P3.5引脚
unsigned char code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//定义共阳数码管显示字型码
unsigned char sec=0; //定义计数值,每过1秒,sec加1
unsigned int count; //定时中断次数
bit write=0; //写24C08的标志;
sbit shiwei=P2^6; //十位选通定义
sbit gewei=P2^7; //个位选通定义
sbit K5=P3^2; //清0按键
/*****************************************************
函数功能:延时1ms
***************************************************/
void delay1ms()
{
unsigned char i,j;
for(i=0;i<10;i++)
for(j=0;j<33;j++)
;
}
/*****************************************************
函数功能:延时若干毫秒
入口参数:n
***************************************************/
void delaynms(unsigned char n)
{
unsigned char i;
for(i=0;i<n;i++)
delay1ms();
}
/***************************************************
函数功能:开始数据传送
***************************************************/
void start()
// 开始位
{
SDA = 1; //SDA初始化为高电平“1”
SCL = 1; //开始数据传送时,要求SCL为高电平“1”
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
SDA = 0; //SDA的下降沿被认为是开始信号
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
SCL = 0; //SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递)
}
/***************************************************
函数功能:结束数据传送
***************************************************/
void stop()// 停止位
{
SDA = 0; //SDA初始化为低电平“0” _n
SCL = 1; //结束数据传送时,要求SCL为高电平“1”
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
SDA = 1; //SDA的上升沿被认为是结束信号
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
SDA=0;
SCL=0;
}
/***************************************************
函数功能:检测应答位
***************************************************/
bit Ask() //检测应答
{
bit ack_bit; //储存应答位
SDA = 1; // 发送设备(主机)应在时钟脉冲的高电平期间(SCL=1)释放SDA线,
//以让SDA线转由接收设备(AT24Cxx)控制
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
SCL = 1; //根据上述规定,SCL应为高电平
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
ack_bit = SDA; //接受设备(AT24Cxx)向SDA送低电平,表示已经接收到一个字节
//若送高电平,表示没有接收到,传送异常 结束发送
SCL = 0; //SCL为低电平时,SDA上数据才允许变化(即允许以后的数据传递)
return ack_bit; // 返回AT24Cxx应答位
}
/***************************************************
函数功能:从AT24Cxx读取数据
出口参数:x
***************************************************/
unsigned char ReadData()
// 从AT24Cxx移入数据到MCU
{
unsigned char i;
unsigned char x; //储存从AT24Cxx中读出的数据
for(i = 0; i < 8; i++)
{
SCL = 1; //SCL置为高电平
x<<=1; //将x中的各二进位向左移一位
x|=(unsigned char)SDA; //将SDA上的数据通过按位“或“运算存入x中
SCL = 0; //在SCL的下降沿读出数据
}
return(x); //将读取的数据返回
}
/***************************************************
函数功能:向AT24Cxx的当前地址写入数据
入口参数:y (储存待写入的数据)
***************************************************/
//在调用此数据写入函数前需首先调用开始函数start(),所以SCL=0
void WriteCurrent(unsigned char y)
{
unsigned char i;
for(i = 0; i < 8; i++) // 循环移入8个位
{
SDA = (bit)(y&0x80); //通过按位“与”运算将最高位数据送到S
//因为传送时高位在前,低位在后
_nop_(); //等待一个机器周期
SCL = 1; //在SCL的上升沿将数据写入AT24Cxx
_nop_(); //等待一个机器周期
_nop_(); //等待一个机器周期
SCL = 0; //将SCL重新置为低电平,以在SCL线形成传送数据所需的8个脉冲
y <<= 1; //将y中的各二进位向左移一位
}
}
/***************************************************
函数功能:向AT24Cxx中的指定地址写入数据
入口参数:add (储存指定的地址);dat(储存待写入的数据)
***************************************************/
void WriteSet(unsigned char add, unsigned char dat)
// 在指定地址addr处写入数据WriteCurrent
{
start(); //开始数据传递
WriteCurrent(OP_WRITE); //选择要操作的AT24Cxx芯片,并告知要对其写入数据
Ask();
WriteCurrent(add); //写入指定地址
Ask();
WriteCurrent(dat); //向当前地址(上面指定的地址)写入数据
Ask();
stop(); //停止数据传递
delaynms(4); //1个字节的写入周期为1ms, 最好延时1ms以上
}
/***************************************************
函数功能:从AT24Cxx中的当前地址读取数据
出口参数:x (储存读出的数据)
***************************************************/
unsigned char ReadCurrent()
{
unsigned char x;
start(); //开始数据传递
WriteCurrent(OP_READ); //选择要操作的AT24Cxx芯片,并告知要读其数据
Ask();
x=ReadData(); //将读取的数据存入x
stop(); //停止数据传递
return x; //返回读取的数据
}
/***************************************************
函数功能:从AT24Cxx中的指定地址读取数据
入口参数:set_addr
出口参数:x
***************************************************/
unsigned char ReadSet(unsigned char set_addr)
// 在指定地址读取
{
start(); //开始数据传递
WriteCurrent(OP_WRITE); //选择要操作的AT24Cxx芯片,并告知要对其写入数据
Ask();
WriteCurrent(set_addr); //写入指定地址
Ask();
return(ReadCurrent()); //从指定地址读出数据并返回
}
/***********************************************************/
void LEDshow() //LED显示函数
{
P0=table[sec/10];
shiwei=0;
delaynms(2);
shiwei=1;
P0=table[sec%10];
gewei=0;
delaynms(2);
gewei=1;
}
/***********************************************************/
/***************************************************
函数功能:主函数
***************************************************/
void main(void)
{
TMOD=0x01; //定时器0工作在方式1
ET0=1;
EA=1;
TH0=(65536-50000)/256; //对TH0 TL0赋值
TL0=(65536-50000)%256; //使定时器0.05秒中断一次
SDA = 1; // SDA=1,SCL=1,使主从设备处于空闲状态
SCL = 1;
sec=ReadSet(2);//读出保存的数据赋于sec
TR0=1; //开始计时
while(1)
{
LEDshow();
if(write==1) //判断计时器是否计时一秒
{
write=0; //清零
WriteSet(2,sec); //在24c08的地址2中写入数据sec
}
if(K5==0){
delaynms(10);
if(K5==0){
sec=0;
}
}
}
}
/**************************************************************/
void t0(void) interrupt 1 using 0 //定时中断服务函数
{
TH0=(65536-50000)/256; //对TH0 TL0赋值
TL0=(65536-50000)%256; //重装计数初值
count++; //每过50ms tcnt加一
if(count==20) //计满20次(1秒)时
{
count=0; //重新再计
sec++;
write=1; //1秒写一次24C08
if(sec==100) //定时100秒,在从零开始计时
{sec=0;}
}
}
❻ 51单片机如何模拟I2C总线中从机接收ID,发送数据的程序
#include /*头文件的包含*/
#include
#define uchar unsigned char /*宏定义*/
#define uint unsigned int
/*端口位定义*/
sbit BELL_OUT=P3^5;
sbit SCL="P1"^3;/*模拟I2C数据传送位*/
sbit SDA="P1"^4;/*模拟I2C时钟控制位*/
bit ack; /*应答标志位*/
/*********************************************************************
起动总线函数
函数原型: void Start_I2c();
功能:启动I2C总线,即发送I2C起始条件
********************************************************************/
void Start_I2c()
{
SDA="1"; /*发送起始条件的数据信号*/
_nop_();
SCL="1"; /*起始条件建立时间大于4.7us,延时*/
_nop_();
SDA="0"; /*发送起始信号*/
_nop_(); /* 起始条件锁定时间大于4μs*/
SCL="0"; /*钳住I2C总线,准备发送或接收数据 */
_nop_();
}
/***********************************************
结束总线函数
函数原型: void Stop_I2c();
功能:结束I2C总线,即发送I2C结束条件
***********************************************/
void Stop_I2c()
{
SDA="0"; /*发送结束条件的数据信号*/
_nop_(); /*发送结束条件的时钟信号*/
SCL="1"; /*结束条件建立时间大于4μs*/
_nop_();
SDA="1"; /*发送I2C总线结束信号*/
_nop_();
}
/*******************************************************************
字节数据传送函数
函数原型: void SendByte(uchar c);
功能:将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对此状
态位进行操作(不应答或非应答都使ack=0 假) 。发送数据正常,ack=1;
ack=0表示被控器无应答或损坏。
********************************************************************/
void SendByte(uchar c)
{
uchar BitCnt;
for(BitCnt=0;BitCnt<8;BitCnt++) /*要传送的数据长度为8位*/
{
SCL="0";
if((c<
else SDA="0";
SCL="1"; /*置时钟线为高,通知被控器开始接收数据位*/
_nop_(); /*保证时钟高电平周期大于4μs*/
}
//从机应答,可以用应答和非应答信号代替
_nop_();
SCL="0";
_nop_();
SDA="1"; //
_nop_();
SCL="1";
_nop_();
if(SDA==1){ack=0;} /*判断是否接收到应答信号*/
else ack="1";
SCL="0";
_nop_();
}
/*******************************************************************
字节数据传送函数
函数原型: uchar RcvByte();
功能:用来接收从器件传来的数据,并判断总线错误(不发应答信号),
发完后请用应答函数。
********************************************************************/
uchar RcvByte()
{
uchar retc;
uchar BitCnt;
retc="0";
for(BitCnt=0;BitCnt<8;BitCnt++)
{
SCL="1"; /*置时钟线为高使数据线上数据有效*/
_nop_();
retc="retc"<<1;
if(SDA==1) retc="retc"+1; /*读数据位,接收的数据位放入retc中 */
SCL="0";
}
return(retc);
}
/********************************************************************
应答子函数
原型: void Ack_I2c();
功能:主控器进行应答信号
********************************************************************/
void Ack_I2c()
{
SDA="0"; /*在此发出应答信号 */
_nop_();
SCL="0";
_nop_();
SCL="1";
_nop_();
SCL="0"; /*清时钟线,钳住I2C总线以便继续接收*/
_nop_();
SDA="1";
_nop_();
}
/********************************************************************
非应答子函数
原型: void NoAck_I2c();
功能:主控器进行非应答信号
********************************************************************/
void NoAck_I2c()
{
SDA="1"; /*在此发出非应答信号 */
_nop_();
SCL="1";
_nop_();
SCL="0"; /*清时钟线,钳住I2C总线以便继续接收*/
}
/*******************************************************************
向无子地址器件发送字节数据函数
函数原型: bit ISendByte(uchar sla,ucahr c);
功能:从启动总线到发送地址,数据,结束总线的全过程,从器件地址sla。如果
返回1表示操作成功,否则操作有误。
********************************************************************/
bit ISendByte(uchar sla,uchar c)
{
Start_I2c(); /*启动总线*/
SendByte(sla); /*发送器件地址*/
if(ack==0)return(0);
SendByte(c); /*发送数据*/
if(ack==0)return(0);
Stop_I2c(); /*结束总线*/
return(1);
}
/*******************************************************************
向有子地址器件发送多字节数据函数
函数原型: bit ISendStr(uchar sla,uchar suba,ucahr *s,uchar no);
功能:从启动总线到发送地址,子地址,数据,结束总线的全过程,从器件地址sla,
子地址suba,发送内容是s指向的内容,发送no个字节。如果返回1表示
操作成功,否则操作有误。
********************************************************************/
bit ISendStr(uchar sla,uchar suba,uchar *s,uchar no)
{
uchar i;
Start_I2c(); /*启动总线*/
SendByte(sla); /*发送器件地址*/
if(ack==0)return(0);
SendByte(suba); /*发送器件子地址*/
if(ack==0)return(0);
for(i=0;i
{
SendByte(*s); /*发送数据*/
if(ack==0)return(0);
s++;
}
Stop_I2c(); /*结束总线*/
//delayMs(1); //
return(1);
}
/*******************************************************************
向无子地址器件读字节数据函数
函数原型: bit IRcvByte(uchar sla,ucahr *c);
功能:从启动总线到发送地址,读数据,结束总线的全过程,从器件地址sla,返
回值在c。如果返回1表示操作成功,否则操作有误。
********************************************************************/
bit IRcvByte(uchar sla,uchar *c)
{
Start_I2c(); /*启动总线*/
SendByte(sla+1); /*发送器件地址*/
if(ack==0)return(0);
*c=RcvByte(); /*读取数据*/
NoAck_I2c(); /*发送非就答位*/
Stop_I2c(); /*结束总线*/
return(1);
}
/**********************************************************************
向有子地址器件读取多字节数据函数
函数原型: bit ISendStr(uchar sla,uchar suba,ucahr *s,uchar no);
功能:从启动总线到发送地址,子地址,读数据,结束总线的全过程,从器件地址sla,
子地址suba,读出的内容放入s指向的存储区,读no个字节。如果返回1
表示操作成功,否则操作有误。
**********************************************************************/
bit IRcvStr(uchar sla,uchar suba,uchar *s,uchar no)
{
Start_I2c(); /*启动总线*/
SendByte(sla); /*发送器件地址*/
if(ack==0)return(0);
SendByte(suba); /*发送器件子地址*/
if(ack==0)return(0);
Start_I2c();
SendByte(sla+1);
if(ack==0)return(0);
while(no!=1)
{
*s=RcvByte();/*发送数据*/
Ack_I2c(); /*发送就答位*/
s++;
no--;
}
*s=RcvByte();
NoAck_I2c(); /*发送非应位*/
Stop_I2c(); /*结束总线*/
return(1);
}