单总线通讯学习的困扰,感觉比较难尤其是I2C总线。视频看了多遍,自己还是做不好

炼气士 2011-06-29 11:09:54
单总线学习对时间的要求太多了,学起来也比较费劲。视频反复的看,原理也反复的读。写出来的代码就是出不来结果。
sbit scl=P3^4;
sbit sda=P3^5;
sbit E=P2^2;
sbit RS=P2^0;
sbit RW=P2^1;
uchar devAddr=0x0a;
// I2C BUS AT24C08 地址为0xa0+0x00/0x01,写/读
uchar writeData=1;
uchar readData=0;

void delay8us()
{
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
}

// startbit
void start_bit()
{
scl = 1;
sda = 1;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
sda = 0;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
scl=0;
}

// stopbit
void stop_bit()
{
scl=1;
sda=0;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
sda=1;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
scl=0;
sda=0;

}

void ack_bit()
{
uint i;


scl=1;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
while(sda&&(i<200))
{
i++;
}
scl=0;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();


}

void noAck_bit()
{
// uint i;
sda =1;
delay8us();
scl=1;
delay8us();
scl=0;
//while(!sda);
//接收停止
}


void write_byte_24c08(uchar dat)
{
uchar i;
scl=0;
_nop_();
for(i=0;i<8;i++)
{
if(dat&0x80)
sda=1;
else
sda=0;
dat<<=1;
delay8us();
scl=1;
delay8us();
scl=0;
delay8us();
}
sda=1;
delay8us();
}

void write_data_24c08(uchar romAddr,uchar dat)
{

start_bit();
write_byte_24c08(0xa0); //slave address
ack_bit(); // 等待从机发出应答信号
write_byte_24c08(romAddr); // 需要写入的地址
ack_bit();
write_byte_24c08(dat); // 写入数据
ack_bit();
stop_bit(); // 如果 需要对其他从机写,则不发停止,发起始信号


}

uchar read_byte_24c08()
{
uchar i,value;
scl=0;
delay8us();
sda=1;
delay8us();
for(i=0;i<8;i++)
{
scl=1;
delay8us();

value<<=1;
if(sda)
{
value++;
}
scl=0;
delay8us();
}

sda = 1;
scl = 0;
return value;
}

void read_24c08(uchar romAdd)
{
start_bit();
write_byte_24c08(0xa0); //写设备地址
ack_bit();
write_byte_24c08(romAdd); // 写要读取的寄存器地址
ack_bit();
start_bit();
write_byte_24c08(0xa1); // 功能为读
readData=read_byte_24c08();
noAck_bit();
stop_bit();
}


//--1602
void delay_50us(uint t)
{
uchar j;
for(;t>0;t--)
for(j=19;j>0;j--);
}
void write_com(uchar com)
{
RS = 0;
RW = 0;
P0 = com;
E = 0;
delay_50us(10);
E = 1;
delay_50us(20);
E = 0;
}
void init1602(void)
{
delay_50us(300);
write_com(0x38);
delay_50us(100);
write_com(0x38);
delay_50us(100);
write_com(0x38);
delay_50us(100);
write_com(0x38);
write_com(0x08);
write_com(0x01);
write_com(0x06);
write_com(0x0c);
}

void write_data(uchar dat)
{
RS = 1;
RW = 0;
P0 = dat;
E = 0;
delay_50us(10);
E = 1;
delay_50us(20);
E = 0;
}
void display()
{
//read = 0x31;
write_com(0x80);
//write_data('0');
write_data(readData+0x30);
}

void main()
{
scl=1;
sda=1; //初始化
init1602();
write_data_24c08(0x08,writeData);
delay_50us(20);
read_24c08(0x08);
while(1)
display();
}
1602的显示是好的,单总线的学习有什么经验可以借鉴不?
...全文
296 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
炼气士 2011-07-02
  • 打赏
  • 举报
回复
通过别人的例子,一步步排查,终于找到2个问题问题
1是出在停止信号上面,
void stop_bit()
scl=1;
sda=0;
....
这样处理会有问题,如果在条用stop_bit函数的上一个处理过程中sda=1,那么这是进入
stop_bit后scl=1,而sda又置为0,所以就无意间产生了起始信号,导致操作失败。

2是出在主函数中写入操作执行后,延时太短,读出操作失败。

总结了一下,要模拟I2C总线只要是执行时序操作,如果要对sda赋值操作,为了保险起见先将scl置0,操作完毕后scl也要置0,以便下次使用或者其他主机用,sda也释放掉。

虽然后续i2c是mcu自带了,但还是在调试中还是学到了,至少下次会注意程序也是有“上下文的”,只有理解了才不会出错。

再次感谢楼上给位朋友。

AnYidan 2011-07-01
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 codesnail 的回复:]

目前一般的状况是,i2c控制器是mcu自带的,只需要设置好参数就可以了,不需要用GPIO口去模拟。
[/Quote]

++
bjtea 2011-06-30
  • 打赏
  • 举报
回复
1)在子程序 read_byte_24c08()中,需要赋初值value=0,这个错误是致命的;
2)在stop_bit()中不需要最后又置SDA=0,这个仅是建议;

各个子程序的功能安排的不太合理,所以不能满足闭门造车,应该多学习观察别人成功的代码;
codesnail 2011-06-30
  • 打赏
  • 举报
回复
目前一般的状况是,i2c控制器是mcu自带的,只需要设置好参数就可以了,不需要用GPIO口去模拟。
shen_guang_wu 2011-06-30
  • 打赏
  • 举报
回复
void start_bit()
{
scl = 1;
sda = 1;
_nop_();

这里应该是先scl=0;sda=1;scl=1;还有就是硬件IC的PIN1、2、3是否接地?

27,377

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 单片机/工控
社区管理员
  • 单片机/工控社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧