uchar read_byte()
{
uchar i,k;
scl=0; //-IIC规定在SCL底电平的时候,允许SDA发生变化。所以,这里必须自定义为低
delay(); //-单片机端口变化需要一点时间,从机收到变化也需要一点时间,所以必须延时
sda=1; //-这肯定是用51单片机写的程序,因为51单片机在读取IO口时,必须把该口先写为1
delay();
for(i=0;i<8;i++)
{
scl=1; //-把SCL变为高电平时,SDA就不会变化了。数据也就稳定了。
delay();
k=(k<<1)|sda; //-这样写你会理解。K = K<<1; 先把缓冲左移
// K = K | SDA; 再把缓冲的最低位或 *** 作一下,得到数据位
scl=0; //允许IIC发送下一个位
delay(); //-保证IIC能把数据送出
}
return k; 返回读取到的数据。
}
整个核心就在移位、或 *** 作。
先移位后,则缓冲的最低位就为0了。该位与SDA或一下以后,如果数据为1则缓冲的最低位为1否则最低位仍然为0由于FOR循环只有8次。所以,也就读取8次数据就完成了。
不是的,他无法判断你从哪读取,
读数据,首先为起始信号-》从地址(最后一位为W写)-》重复起始信号
-》从地址(最后一位为R读)-》存放的寄存器地址或读的数据地址->
读到的数据。
这么个过程。不懂的可以接着问。
建议好好看下IIC时序图就明白了
写程序的时候,多注意一下芯片的资料。24C01的资料里面写的很清楚。读写必须按要求的波形来处理。我发一段我用在ST芯片的程序给你。由于IIC接口采用模拟的方式。所以,与什么芯片没有关系。你把这段程序看一下。相信你可以看懂。使用这段程序的产品已经出货几十万套产品。不会有问题。
ST芯片的端口输入输出设定比较麻烦。所以有单独的子程序来处理。
单片机IIC的SDA端口在向EEPROM(24C01)发数据时,设为输出
在读取EEPROM数据的时候则设为输入。
//-------------------------------------------
//-程序名称: Eestart
//-入口参数:无
//-出口参数:无
//-功能说明: IIC的START信号生成
//-------------------------------------------
void Eestart(void) //启动I2C总线//
{
SETSDA;
DelayNOP(STARTNOP);
SETSCL;
DelayNOP(STARTNOP);
CLRSDA;
DelayNOP(STARTNOP);
CLRSCL; ;--注意此处,很重要。总线只有SCL为低时,才允许SDA变化。
DelayNOP(STARTNOP);
}
//-------------------------------------------
//-程序名称: Eestop
//-入口参数:无
//-出口参数:无
//-功能说明: IIC的STOP信号生成
//-------------------------------------------
void Eestop(void )/停止I2C总线/
{
i2csdaoutput();
CLRSCL;
DelayNOP(STARTNOP);
CLRSDA;
DelayNOP(STARTNOP);
SETSCL;
DelayNOP(STARTNOP);
SETSDA;
}
//-------------------------------------------
//-程序名称: read_byte_eeprom
//-入口参数:ackstate
//-出口参数:返回值(读取到的数据)
//-功能说明: 读取1byte的EEPROM数据
//-------------------------------------------
UI08 read_byte_eeprom(UI08 ackstate) //-read
{
UI08 j,data;
i2csdainput(); //-SDA端口设为输入状态
data = 0; //-将接收缓冲清空
//DelayNOP(NOP1US); //-2011-7-21 新增延时
for(j=0;j<8;j++)
{
//-
data <<= 1; //-将接收缓冲左移,保证高位先发
SETSCL;
DelayNOP(NOP2US); //-延时
if(p_eepsdain != 0) //-此处的p_eepsdain 就是SDA端口,ST芯片输入输出端口不是同一名字
{
//-检测到数据为1时,将接收缓冲0BIT置1
data++;
}
CLRSCL;
DelayNOP(NOP1P5US);
}
if(ackstate == ACKREAD)
{
//-有要求发送ACK信号的则发送ACK信号
i2csdaoutput(); //-将SDA端口设为输出
CLRSDA;
DelayNOP(NOP2US);
SETSCL;
DelayNOP(NOP2US);
CLRSCL;
}
return(data);
}
//-------------------------------------------
//-程序名称: write_byte_eeprom
//-入口参数:wrdata
//-出口参数:返回值,FALSE(写入失败),TRUE(写入成功)
//-功能说明: 将wrdata写入EEPROM
//-------------------------------------------
bool write_byte_eeprom(UI08 wrdata)
{
bool j=FALSE; //-返回值,默认为失败
UI08 i;
//DelayNOP(NOP1US); //-2011-7-21 新增延时
for(i=0; i<8; i++)
{
//-总共移位8次
if((0x80 & wrdata) != 0)
{
//-最高位为1时,SDA置1
SETSDA;
}
else
{
//-最高位为0时,SDA置0
CLRSDA;
}
wrdata <<= 1; //-将发送数据左移,实现高位先发
//DelayNOP(NOP1US); //-2011-7-21 新增延时
SETSCL;
DelayNOP(NOP2US);
CLRSCL;
DelayNOP(NOP1P5US);
}
//--read ACK signal--{
i2csdainput(); //-将SDA设为输入状态
DelayNOP(NOP1US);
SETSCL;
if(p_eepsdain == 0)
{
//-读取到正确的ACK信号,则返回写入成功
j = TRUE;
}
DelayNOP(NOP1P5US);
CLRSCL;
i2csdaoutput(); //-将SDA端口设为输出
//--read ACK signal--}
return(j);
}
//-------------------------------------------
//-程序名称: DelayNOP
//-入口参数:cntnop
//-出口参数:无
//-功能说明: 进行cntnop个延时
//-------------------------------------------
void DelayNOP(UI08 cntnop)
{
UI08 i;
for(i=0; i<cntnop; i++);
}
//-------------------------------------------
//-程序名称: i2csdainput
//-入口参数:无
//-出口参数:无
//-功能说明: 将SDA端口设为输入
//-------------------------------------------
void i2csdainput(void)
{
/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructureGPIO_Pin = GPIO_Pin_9; //SDA
GPIO_InitStructureGPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructureGPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/
}
//-------------------------------------------
//-程序名称: i2csdaoutput
//-入口参数:无
//-出口参数:无
//-功能说明: 将SDA端口设为输出
//-------------------------------------------
void i2csdaoutput(void)
{
/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructureGPIO_Pin = GPIO_Pin_9; //SDA
GPIO_InitStructureGPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructureGPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/
}
我的理解是,读数据的时候,是从机控制总线,因此必须提前把sda释放(也就是拉高)。否则从机将不能改变总线。sda为1后,主机控制scl=1,然后,从机根据自己要发送的数据选择将sda置为1或0(即在scl=1时,从机改变sda)。
而在发送数据时,主机控制总线,在scl=0时,sda被主机改变;scl=1时,sda不变,并被从机读取。(scl=0时,主机改变sda)
应答时,相当于读取一位数据,同样是在scl=1时,由从机改变sda。
而起始、结束时,是由 主机 控制sda。在scl为1时,sda的任何改变都将被视为起始、终止信号。
个人理解,如有错误请赐教!
ps 当然也可以理解为 在读取时仍然是在scl=0时,从机改变sda,不过也不影响逻辑,怎样容易怎样理解呗
i2c有定义 字节格式
发送到SDA 线上的每个字节必须为8 位,每次传输可以发送的字节数量不受限制。每个字节后必须跟一个响应位。首先传输的是数据的最高位(MSB),如果从机要完成一些其他功能后(例如一个内部中断服务程序)才能接收或发送下一个完整的数据字节,可以使时钟线SCL 保持低电平,迫使主机进入等待状态,当从机准备好接收下一个数据字节并释放时钟线SCL 后数据传输继续。
意思是如果你发送 0x01 那么前面很多个0一定是第一个发送出去的(msb),1是最后发送出去的(lsb)
一般的话,如果你的 ad 转换器是有 I2c接口,那么就直接连接到单片机的i2c好了,具体通讯细节无需考虑
单片机一般先发一个命令给二总线模块(比如e2prom)。
然后得到总线模块的回应。
再发地址码,随后接受数据。
i2c_SCL只是同步信号,在两个脉冲之间得到的i2c_sda数据视为一个完整数据。
IIC协议有讲的,每一次传输都有
起始
应答
错误
总线忙
非应答
等信号格式
NoACK就是非应答,他告诉iic传输的是最后一个数据,TestAck()
是发送一个字后等待从机应答
以上就是关于关于单片机模拟I2C的总线通信,读一个字节的问题。。全部的内容,包括:关于单片机模拟I2C的总线通信,读一个字节的问题。。、IIC数据读取问题、51单片机 I2C 24c02的单个数据读写等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)