请教各位I2C对24C02写完后无法立即读的问题
最近在学I2C读写24C02的东西,弄了几天了,搞不定。下面是代码截图,就是对24C02的读写操作,如果是先写后立即读,在Proteus里仿真是读出0xFF,在开发板里是读出0x00,总之读不出。但是,按照先写后立即读操作完,把写的那部分代码注释掉再去跑,Proteus和开发板里都会读出0xAF,请问各位是什么原因。感觉I2C比USART稀奇古怪多了,USART我看几小时就搞定了,I2C几天都搞不定,I2C协议早先我也没接触过什么。代码图
先写后立即读,读不出
只读,能读出
本帖最后由 hellohello22 于 2021-2-16 18:24 编辑
i2c问题不是芯片坏(小概率)就是时序不对 没有例外我自己写的驱动就一直正常 hellohello22 发表于 2021-2-16 18:22
i2c问题不是芯片坏(小概率)就是时序不对 没有例外我自己写的驱动就一直正常 ...
Proteus 仿真也是不正常的。按先写后立即读先跑一遍,是读不出来的,看到网上解释是要给24C02写内存的一点时间,建议等待6ms,但是我都等待2000ms了,仍然读不出。但是然后按只读方式再跑一遍,原来写进去的就能读出来了。仿真和实际的开发板都是这样,应该不是芯片问题。先写后读,按照I2C的解释是两个不同模式的切换,即由写切换到读模式,说是RS(重启)方法切换,但是我不知道啥叫RS方法,我只能理解为先STOP,再START,不知道对不对。看24C02手册读取数据有不同模式,默认是按顺序读,也就是当前操作(无论读写)第N个地址,如果接下来发送读命令,它会去读取第N+1地址的内容,这显然不符合我的要求,不过我是按照手册的“任意地址读”的方式操作的,对于只读,还是能读出来的。
{:5_639:}
我写完马上读都可以 有示波器看下波形就知道 我用的开发平台是ATMEL STUDIO 7,ATMEGA16似乎有些古老了。{:5_604:}网上的代码似乎比我的更底层,还要根据接收和发送来设置什么SDA的输入输出方式,也就是I/O模式,但是AV Studio7似乎不鸟这些,也就是按照M16手册说明,语句里只要开了TWI功能,SDA,SCL这两个脚好像就不在是什么I/O口了,任凭你怎么设置PC0,PC1的输入输出方式,全然不鸟,语句也比网上的简单多了,网上也根本找不到相似的原因。 hellohello22 发表于 2021-2-16 18:56
我写完马上读都可以 有示波器看下波形就知道
上开发板用示波器看了,先写的过程完整被抓下来了,数据全正确都有,到了STOP,也执行了,但是后续的START之类的波形全没了,也就是没有执行读过程。虽然代码在执行,而且状态也符合,但是实际没有读的波形产生,很是奇怪。不知道I2C协议里的读写状态切换的RS信号是啥信号,不知道STOP之后,为什么延迟一段时间用START,实际不会开启。
上拉电阻那里,接了电压没?我只看到一个探针 本帖最后由 hellohello22 于 2021-2-16 21:58 编辑
IO模拟,自己写一遍时序就搞定了,读写随便切换,闻所未闻乱七八糟的, 24c02前阵子刚读写过,没有任何问题, i2c是最简单的了
最近把spi翻了个遍, TF卡挂载FAT32也成功了,随便读写文件和电脑交换, 完全自己写的代码,包括FAT表项,目录项, 簇各种操作,简单高效 拓荒牛 发表于 2021-2-16 20:34
上拉电阻那里,接了电压没?我只看到一个探针
那个探针是激励源符号,是一个DC 5V电源,表示接一个5V电压。Proteus大概就是这么用电源的。你仔细看下M16,是没有VCC和GND引脚的,估计默认就是接到5V那去了,根本不让接电源。
hellohello22 发表于 2021-2-16 20:37
IO模拟,自己写一遍时序就搞定了,读写随便切换,闻所未闻乱七八糟的, 24c02前阵子刚读写过,没有任何问题 ...
I/O口模拟的话要写的就太多了,我也是刚学,很多C语言和指令都不熟。{:5_604:}M16本身就有TWI硬件相关指令,读写数据是按一个字节8位来操作的,写代码相对一位一位来操作要省事。据说用I/O口模拟要用一堆的delay延时,不好把握,用TWI硬件指令,访问TWI的中断标志就可以知道设备是否有ACK回应,根据不同回应代码就知道是什么类型的回应,相对方便些,也更高效些。I2C协议描述里,有S,P信号,也就是START和STOP信号,不过也是奇怪,发送P信号就是结束了,怎么下一次的START无效了,而且是代码正常执行,实际却没有波形。
本帖最后由 hellohello22 于 2021-2-16 22:35 编辑
cyradg 发表于 2021-2-16 20:58
I/O口模拟的话要写的就太多了,我也是刚学,很多C语言和指令都不熟。M16本身就有TWI硬件相关指 ...
想学习原理,就得从底层开始,囫囵吞枣学不深,出了问题定位更难,我一直坚持自己写底层,收获甚大,细节一清二楚,为了读写tf和串行flash, 把中断+DMA摸熟,砍柴不误磨刀工{:5_604:}
我都是用汇编写,不喜欢用C,汇编写更有安全感 给楼主一个建议:用示波器看波形,时序要严格,误差不能太大; 汇编-MS51:
rdee:
call star_ee
;*************************
mov a,dph
;**************************2001.7.17
rl a
orl a,#slaw
;**************************2001.7.17
call w1bt
call cack
jb f0,rdee_end
mov a,dpl
call w1bt
call cack
jb f0,rdee_end
call star_ee
;*************************
mov a,dph
;**************************2001.7.17
rl a
orl a,#slar
;**************************2001.7.17
call w1bt
call cack
jb f0,rdee_end
call r1bt
;call mack
call mnack
call stop_ee
;***********************2001.7.22
clr ee_f
ret
rdee_end:
setb ee_f
;sjmp rdee
ret
star_ee:
setb sda
setb scl
nop
nop
clrsda
nop
nop
clrscl
ret
stop_ee:
clrsda
setb scl
nop
nop
setb sda
ret
mack:
clrsda
setb scl
nop
nop
clrscl
setb sda
ret
mnack:
setb sda
setb scl
nop
nop
clrscl
clrsda
ret
cack:
setb sda
setb scl
nop
nop
clr f0
jnb sda,cend
setb f0
cend:
clr scl
ret
w1bt:
mov r7,#08d
wlp:
rlc a
jc wr1
wr0:
clr sda
setb scl
nop
nop
clr scl;********
jmp wlp1
wr1:
setb sda
setb scl
nop
nop
clr scl
clr sda
wlp1:
nop
djnz r7,wlp
ret
r1bt:
mov r7,#08d
rlp:
setb sda
setb scl
nop
nop
jnb sda ,rd0
rd1:
setb c
rlc a
clr scl
jmp rlp1
rd0:
clr c
rlc a
clr scl
rlp1:
nop
djnz r7,rlp
ret
wree:
call star_ee
;*************************
mov a,dph
;**************************2001.7.17
rl a
orl a,#slaw
;**************************2001.7.17
call w1bt
call cack
jb f0,wree_end
mov a,dpl
call w1bt
call cack
jb f0,wree_end
mov a,r4
call w1bt
call cack
jb f0,wree_end
call stop_ee
;call led
push dph
push dpl
push 00h
push 01h
push a
push b
call led
pop b
pop a
pop 01h
pop 00h
pop dpl
pop dph
clr ee_f
ret
wree_end:
setb ee_f
ret
24C16:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Nop_:
nop
nop
nop
nop
ret
StarEe:
setb sda
call Nop_
setb scl
call Nop_
clr sda
call Nop_
clr scl
ret
StopEe:
clr sda
call Nop_
setb Scl
call Nop_
setb sda
call Nop_
clr scl
ret
Ack:
Call Nop_
setb scl
call Nop_
mov c,sda
call Nop_
clr scl
ret
McuAck:
clr sda
call Ack
setb Sda
ret
McuNAck:
setb sda
setb Scl
call NOp_
clr Scl
clr Sda
ret
WrByte:
mov b,#8
Wr11:
rlc a
mov sda,c
call Ack
djnz b,Wr11
setb Sda
call Ack
ret
RdByte:
mov b,#08
Rd11:
call Ack
rlc a
djnz b,Rd11
ret
MakeAdr:
mov a,dph
rl a
orl a,#0a0h
ret
;*********************************
;a=0 =>24c16;=>24c32...
;r1=Long;R0=Adr;Dptr=EeAdr
;*********************************
EeAll:
call StarEe
mov a,#0
jz Ee24c16
;*************
;Ee24c32
mov a,#0a0h
call WrByte
jc EeErr
mov a,dph
call WrByte
jc EeErr
sjmp EeDo
Ee24c16:
mov a,#dph
call MakeAdr
call WrByte
EeDo:
mov a,Dpl
call WrByte
jc EeErr
ret
EeErr:
setb ee24
ret
;)))))))))))))))))))))))))))))))))
WrEe:
call EeAll
jc eeerr
;***** Ok
we1:
mov a,r4
call WrByte
jc EeErr
;********** Delay
call StopEe
clr c
mov ee24,c
ret
RdEe:
call EeAll
jc EeErr
;********* Ok
call StarEe
mov a,#0a1H
call WrByte
jc Eeerr
;********** Ok
call RdByte
call McuNAck
call StopEe
clr c
mov ee24,c
ret
C51:
//定义EE24XX的管脚********************************
sbit SCL =P1^2; //
sbit SDA =P1^3;
/************************************************
延时程序
************************************************/
void nop_(void)
{
unsigned char i=5;
while (i--);
}
/***********************************************
开始位
************************************************/
void Start_ee (void)
{
SDA=HIGH;
nop_();
SCL=HIGH;
nop_();
SDA=LOW;
nop_();
SCL=LOW;
nop_();
}
/********************************
停止位
**********************************/
void Stop_ee (void)
{
SDA=LOW;
nop_();
SCL=HIGH;
nop_();
SDA=HIGH;
nop_();
SCL=LOW;
nop_();
}
/*********************************
应答位,返回SDA信号
**********************************/
bit Ack(void)
{
bit cy;
SCL=HIGH;
nop_();
cy=SDA;
SCL=LOW;
nop_();
return(cy);
}
/***********************************
主应答 返回SDA
************************************/
bit McuAck(void)
{
bit cy;
SDA=LOW;
cy=Ack();
SDA=HIGH;
return(cy);
}
/************************************
不应答
*************************************/
void McuNAck(void)
{
SDA=HIGH;
SCL=HIGH;
nop_();
SCL=LOW;
SDA=LOW;
}
/************************************
写入一个字节 ,返回成功标志
*************************************/
bit Wr_Byte(unsigned char Dat)
{
unsigned char i;
bit cy;
for (i=0;i<8;i++)
{
if ((Dat & 0x80)==0x80) SDA=HIGH;
else SDA=LOW;
cy=Ack();
Dat<<=1;
}
SDA=HIGH;
return(Ack());
}
/**********************************
读取一个字节
***********************************/
unsigned char Rd_Byte(void)
{
unsigned char i,j=0;
for (i=0;i<8;i++)
{
j<<=1;
if (Ack()) j++;
}
return j;
}
/*******************************************
EE24类型检测,返回标志位
********************************************/
unsigned char EeAll (unsigned int EeAdr)
{
Start_ee();
#if Ee_Type //1 EE24c32__
if (Wr_Byte(Wr_ee)) return 1;
if (Wr_Byte((unsigned char)EeAdr>>8)) return 1;
if (Wr_Byte((unsigned char)EeAdr)) return 1;
#else
if (Wr_Byte((unsigned char)(EeAdr>>7 |Wr_ee))) return 1;
if (Wr_Byte((unsigned char)EeAdr )) return 1;
#endif
return 0;
}
/****************************************************
连续写入字节,返回UINT8标志
*****************************************************/
unsigned char WriteEe24(unsigned int EeAdr,unsigned char *RamAdr,unsigned char Number)
{
bit cy;
if (EeAll(EeAdr)) return (0);
while (Number--)
{
cy=Wr_Byte(*RamAdr++);
}
Stop_ee();
Delay (10);
return (1);
}
/****************************************************
连续读取,返回UINT8标志
*****************************************************/
unsigned char ReadEe24(unsigned int EeAdr,unsigned char *RamAdr,unsigned char Number)
{
if (EeAll(EeAdr)) return (0);
Start_ee();
if (Wr_Byte(Rd_ee)) return (0);
while (--Number)
{
*RamAdr++=Rd_Byte();
McuAck();
}
*RamAdr=Rd_Byte();
McuNAck();
Stop_ee();
return (1);
}
/**************************************************
每过10秒写入EE24
**************************************************/
void SaveEe24c512(void)
{
unsigned char Buf,i;
static unsigned char Flag=0;
if ((UseData.Tm % WrSec) ==0)
{
if(Flag==0) //2004-7-9改FLAG
{
Flag=1; //写入标志
Buf=WorkData.Nu;
Buf=WorkData.Nu>>8;
i=WriteEe24(HeadAdr+2*Menu.Piont++,Buf,2);
if (Menu.Piont >= WrNumber)
{
EndCount(); //溢出
UseData.ToBak=0;
UseData.To=0;
UseData.Tm=0; //2004-4-18
Menu.Piont=0;
UseData.LedPower=Msg_Up; //暂停放电
}
}
}
else
{
Flag=0;
}
}
24C512:
sda bit p3.6
scl bit p3.7
mov Sp,#50h
;*********************************************************
type equ 0
;TYPE =0用EE24C01-EE24C16
;TYPE =1用EE24C32-EE24C512
;*********************************************************
;测试程序
;*********************************************************
tEST:
mov a,#0eh
call INput
mov a,#type
mov r0,#21h
mov r1,#4
call Wree
call Delay
call Delay
mov a,#0h
call INput
mov a,#type
mov r0,#21h
mov r1,#8
call Rdee
nop
sjmp TEST
InPut:
mov r7,#8
mov r0,#21h
lok1:
mov @r0,a
inc r0
djnz r7,lok1
ret
Delay:
mov R7,#20
de11:
djnz b,$
djnz R7,De11
ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;子程序
;入口程序条件:
;A=0 用EE24C01-EE24C16
;A=1 用EE24C32-EE24C512
;DPTR =EE24XX的地址
;R0=MCU内部RAM的地址
;R1=读出(写入)数据的长度BYTE
;C=1操作失败
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Nop_:
nop
nop
nop
nop
ret
StarEe:
setb sda
call Nop_
setb scl
call Nop_
clr sda
call Nop_
clr scl
ret
StopEe:
clr sda
call Nop_
setb Scl
call Nop_
setb sda
call Nop_
clr scl
ret
Ack:
Call Nop_
setb scl
call Nop_
mov c,sda
call Nop_
clr scl
ret
McuAck:
clr sda
call Ack
setb Sda
ret
McuNAck:
setb sda
setb Scl
call NOp_
clr Scl
clr Sda
ret
WrByte:
mov b,#8
Wr11:
rlc a
mov sda,c
call Ack
djnz b,Wr11
setb Sda
call Ack
ret
RdByte:
mov b,#08
Rd11:
call Ack
rlc a
djnz b,Rd11
ret
MakeAdr:
mov a,dph
rl a
orl a,#0a0h
ret
;*********************************
;a=0 =>24c16;=>24c32...
;r1=Long;R0=Adr;Dptr=EeAdr
;*********************************
EeAll:
call StarEe
jz Ee24c16
;*************
;Ee24c32
mov a,#0a0h
call WrByte
jc EeErr
mov a,dph
call WrByte
jc EeErr
sjmp EeDo
Ee24c16:
mov a,#dph
call MakeAdr
call WrByte
EeDo:
mov a,Dpl
call WrByte
jc EeErr
ret
EeErr:
nop
ret
;)))))))))))))))))))))))))))))))))
WrEe: ;写子程序
call EeAll
jc eeerr
;***** Ok
we1:
mov a,@r0
call WrByte
jc EeErr
;********** Delay
inc r0
djnz r1,We1
call StopEe
clr c
ret
RdEe:;读子程序
call EeAll
jc EeErr
;********* Ok
call StarEe
mov a,#0a1H
call WrByte
jc Eeerr
;********** Ok
DEC R1
mov a,r1
jz Red11
Red1:
call RdByte
call McuAck
mov @r0,a
inc r0
djnz r1,Red1
Red11:
call RdByte
MOV @R0,A
call McuNAck
call StopEe
clr c
ret
extern void Delay (unsigned int mS);
//void Wr_Ee24_Byte (unsigned int EeAdr,unsigned char Dat);
unsigned char ReadEe24(unsigned int EeAdr,unsigned char *RamAdr,unsigned char Number);
//定义EE24XX的管脚********************************
sbit SCL =P3^7; // P3.7
sbit SDA =P3^6; //P3.6
/************************************************
延时程序
************************************************/
void nop_(void)
{
unsigned char i=1;
while (i--);
}
/***********************************************
开始位
************************************************/
void Start_ee (void)
{
SDA=HIGH;
nop_();
SCL=HIGH;
nop_();
SDA=LOW;
nop_();
SCL=LOW;
nop_();
}
/********************************
停止位
**********************************/
void Stop_ee (void)
{
SDA=LOW;
nop_();
SCL=HIGH;
nop_();
SDA=HIGH;
nop_();
SCL=LOW;
nop_();
}
/*********************************
应答位,返回SDA信号
**********************************/
bit Ack(void)
{
bit cy;
SCL=HIGH;
nop_();
cy=SDA;
SCL=LOW;
nop_();
return(cy);
}
/***********************************
主应答 返回SDA
************************************/
bit McuAck(void)
{
bit cy;
SDA=LOW;
cy=Ack();
SDA=HIGH;
return(cy);
}
/************************************
不应答
*************************************/
void McuNAck(void)
{
SDA=HIGH;
SCL=HIGH;
nop_();
SCL=LOW;
SDA=LOW;
}
/************************************
写入一个字节 ,返回成功标志
*************************************/
bit Wr_Byte(unsigned char Dat)
{
unsigned char i;
bit cy;
for (i=0;i<8;i++)
{
if ((Dat & 0x80)==0x80) SDA=HIGH;
else SDA=LOW;
cy=Ack();
Dat<<=1;
}
SDA=HIGH;
return(Ack());
}
/**********************************
读取一个字节
***********************************/
unsigned char Rd_Byte(void)
{
unsigned char i,j=0;
for (i=0;i<8;i++)
{
j<<=1;
if (Ack()) j++;
}
return j;
}
/*******************************************
EE24类型检测,返回标志位
********************************************/
unsigned char EeAll (unsigned int EeAdr)
{
Start_ee();
#if Ee_Type //1 EE24c32__
if (Wr_Byte(Wr_ee)) return 1;
if (Wr_Byte((unsigned char)EeAdr>>8)) return 1;
if (Wr_Byte((unsigned char)EeAdr)) return 1;
#else
if (Wr_Byte(((unsigned char)EeAdr>>7) |Wr_ee )) return 1;
if (Wr_Byte((unsigned char)EeAdr )) return 1;
#endif
return 0;
}
/****************************************************
连续写入字节,返回UINT8标志
*****************************************************/
unsigned char WriteEe24(unsigned int EeAdr,unsigned char *RamAdr,unsigned char Number){
unsigned char i;
ReadEe24(EeAdr,&i,Number);
if(i==*RamAdr) return 0;
if (EeAll(EeAdr)) return 0;
while (Number--) {
Wr_Byte(*RamAdr++);
}
Stop_ee();
Delay (3500);
return 1;
}
/****************************************************
连续读取,返回UINT8标志
*****************************************************/
unsigned char ReadEe24(unsigned int EeAdr,unsigned char *RamAdr,unsigned char Number){
if (EeAll(EeAdr)) return 0;
Start_ee();
if (Wr_Byte(Rd_ee)) return 0;
while (--Number) {
*RamAdr++=Rd_Byte();
McuAck();
}
*RamAdr=Rd_Byte();
McuNAck();
Stop_ee();
return 1;
}
//***********************************************************************************
//2006-11-28添加 比较写入数据是否相同!
/*
void Wr_Ee24_Byte (unsigned int EeAdr,unsigned char Dat){
if (EeAll(EeAdr)) return;
Wr_Byte(Dat);
Stop_ee();
Delay (6000);
}
*/
dqp05 发表于 2021-2-16 22:07
给楼主一个建议:用示波器看波形,时序要严格,误差不能太大;
不会汇编。{:5_604:}已经上示波器了,写进去看波形好像没问题,但是读被我修改的面目全非,看示波器,发出起始信号、片选信号以后,后续的内存地址,内存数据都发不出去,也不能STOP,原先都可以的,现在都不行了,好像也没改多少呀,哎,I2C真TMD啰嗦。
页:
[1]
2