求解51单片机I2C对24c02读写汇编程序

电路如图所示,图中高亮SDL实际为SCL时钟,SCL接51单片机p2.1口,SDA姐51单片机p2.0口,VCC为5V
求一个能实现对24c02任意一个地址写入一个数据,再把这个数据从24c02里面读出来并把读出来的数据传送给P1口,因为P1口节的是led灯,要求用汇编写
如下是我自己写的汇编程序,发现怎么也成功不了,求高人指点迷津,给一个能够通过测试的读写24c02程序
org 0000h
ljmp main
start:
setb sda
setb scl
lcall delay
clr sda
lcall delay
clr scl
ret
waitack:
clr scl
setb sda
lcall delay
setb scl
lcall delay
mov c,sda
jc waitack
clr sda
clr scl
ret
stop:
clr sda
setb scl
lcall delay
setb sda
lcall delay
clr scl
clr sda
ret
write_data:
mov r0,#00h
mov b,a
lcall write_byte
ret
write_byte:
lcall start
mov a,#0a0h
lcall sendbyte
lcall waitack
mov a,r0
lcall sendbyte
lcall waitack
mov a,b
lcall sendbyte
lcall waitack
lcall stop
ret
sendbyte:
mov r7,#08h
s_byte:
rlc a
mov sda,c
setb scl
lcall delay
clr scl
djnz r7,s_byte
ret
read_data:
mov r0,#00h
lcall read_byte
call stop
mov temp,a
ret
read_byte:
call start
mov a,#0a0h
lcall sendbyte
lcall waitack
mov a,r0
lcall sendbyte
lcall waitack
lcall start
mov a,#0a1h
lcall sendbyte
lcall waitack
lcall rcvbyte
ret
rcvbyte:
mov r7,#08h
clr a
setb sda
r_byte:
clr scl
lcall delay
setb scl
lcall delay
mov c,sda
rlc a
setb sda
djnz r7,r_byte
ret
delay:
nop
nop
ret
main:
scl bit p2.1
sda bit p2.0
temp equ 30h
mov sp,#60h
mov p1,#0ffh
mov b,0aah;
lcall write_data
lcall read_data
mov p1,a
end

;**************************************
;AT24C04测试程序 供参考
;工作频率: 12.000MHz
;**************************************
SCL BIT P2.0 ;AT24C04的时钟线
SDA BIT P2.1 ;AT24C04的数据线
BUF EQU 30H ;数据缓存区
;**************************************
ORG 0
JMP Reset
ORG 100H
Reset:
CALL AT24C04_WritePage ;写一页数据
CALL Delay5ms ;写一页数据需延时5ms
CALL AT24C04_ReadPage ;读一页数据
JMP $
;**************************************
;向AT24C04写1页(16字节)数据
;将TESTDATA开始的16个测试数据写如设备的00~0F地址中
;入口参数:无
;出口参数:无
;**************************************
AT24C04_WritePage:
CALL AT24C04_Start ;起始信号
MOV A,#0A0H ;发送设备地址+写信号
CALL AT24C04_SendByte ;发送
MOV A,#00H ;发送存储单元地址
CALL AT24C04_SendByte ;发送
MOV R0,#16 ;16字节计数器
MOV DPTR,#TESTDATA ;测试数据首地址
WriteNext:
CLR A ;读取测试数据
MOVC A,@A+DPTR
CALL AT24C04_SendByte ;写入设备
INC DPTR ;准备下一个数据的地址
DJNZ R0,WriteNext ;判断16字节是否完成
CALL AT24C04_Stop ;停止信号
RET
TESTDATA:
DB 000H,011H,022H,033H,044H,055H,066H,077H
DB 088H,099H,0AAH,0BBH,0CCH,0DDH,0EEH,0FFH
;**************************************
;从AT24C04读取1页(16字节)数据
;将设备的00~0F地址中的数据读出存放在DATA区的BUF中
;入口参数:无
;出口参数:无
;**************************************
AT24C04_ReadPage:
CALL AT24C04_Start ;起始信号
MOV A,#0A0H ;发送设备地址+写信号
CALL AT24C04_SendByte ;发送
MOV A,#00H ;发送存储单元地址
CALL AT24C04_SendByte ;发送
CALL AT24C04_Start ;起始信号
MOV A,#0A1H ;发送设备地址+读信号
CALL AT24C04_SendByte ;发送
MOV R0,#16 ;16字节计数器
MOV R1,#BUF ;数据缓冲区首地址
ReadNext:
CALL AT24C04_RecvByte ;读取数据
MOV @R1,A ;保存数据
CJNE R0,#2,$+3 ;判断回应ACK还是NAK
CALL AT24C04_SendACK ;发送应答信号
INC R1 ;缓冲区地址加1
DJNZ R0,ReadNext ;判断16字节是否完成
CALL AT24C04_Stop ;停止信号
RET
;**************************************
;延时5微秒
;不同的工作环境,需要调整此函数
;入口参数:无
;出口参数:无
;**************************************
Delay5us: ;2 当改用1T的MCU时,请调整此延时函数
NOP ;1
RET ;2
;**************************************
;延时5毫秒
;不同的工作环境,需要调整此函数
;入口参数:无
;出口参数:无
;**************************************
Delay5ms: ;2 当改用1T的MCU时,请调整此延时函数
PUSH ACC ;2
PUSH DPL ;2
PUSH DPH ;2
MOV DPTR,#-500 ;2
Delay5ms1:
NOP ;1
NOP ;1
NOP ;1
NOP ;1
INC DPTR ;2
MOV A,DPL ;1
ORL A,DPH ;1
JNZ Delay5ms1 ;2
POP DPH ;2
POP DPL ;2
POP ACC ;2
RET ;2
;**************************************
;起始信号
;入口参数:无
;出口参数:无
;**************************************
AT24C04_Start:
SETB SDA
SETB SCL ;拉高时钟线
CALL Delay5us ;延时
CLR SDA ;产生下降沿
CALL Delay5us ;延时
CLR SCL ;拉低时钟线
RET
;**************************************
;停止信号
;入口参数:无
;出口参数:无
;**************************************
AT24C04_Stop:
CLR SDA
SETB SCL ;拉高时钟线
CALL Delay5us ;延时
SETB SDA ;产生上升沿
CALL Delay5us ;延时
RET
;**************************************
;发送应答信号
;入口参数:C (0:ACK 1:NAK)
;出口参数:无
;**************************************
AT24C04_SendACK:
MOV SDA,C ;写应答信号
SETB SCL ;拉高时钟线
CALL Delay5us ;延时
CLR SCL ;拉低时钟线
CALL Delay5us ;延时
RET
;**************************************
;接收应答信号
;入口参数:无
;出口参数:C
;**************************************
AT24C04_RecvACK:
SETB SCL ;拉高时钟线
CALL Delay5us ;延时
MOV C,SDA ;读应答信号
CLR SCL ;拉低时钟线
CALL Delay5us ;延时
RET
;**************************************
;向IIC总线发送一个字节数据
;入口参数:ACC
;出口参数:无
;**************************************
AT24C04_SendByte:
PUSH 0
MOV 0,#8 ;8位计数器
SendNext:
RLC A ;移出数据的最高位
MOV SDA,C ;送数据口
SETB SCL ;拉高时钟线
CALL Delay5us ;延时
CLR SCL ;拉低时钟线
CALL Delay5us ;延时
DJNZ 0,SendNext ;判断8位数据是否发送完成
POP 0
JMP AT24C04_RecvACK ;接收应答信号
; RET
;**************************************
;从IIC总线接收一个字节数据
;入口参数:无
;出口参数:ACC
;**************************************
AT24C04_RecvByte:
SETB SDA ;使能内部上拉,准备读取数据
PUSH 0
MOV 0,#8 ;8位计数器
RecvNext:
SETB SCL ;拉高时钟线
CALL Delay5us ;延时
MOV C,SDA ;读数据口
RLC A ;保存数据
CLR SCL ;拉低时钟线
CALL Delay5us ;延时
DJNZ 0,RecvNext ;判断8位数据是否接收完成
POP 0
RET
;**************************************
END
温馨提示:答案为网友推荐,仅供参考
第1个回答  推荐于2018-04-10
ORG 0000H
LJMP MAIN

SCL BIT P2.1
SDA BIT P2.0
TEMP EQU 30H
BYTE_ADDR EQU 31H
;---------------I2C通讯程序----------------
;------------------------------------------
;---------------读取I2C数据----------------
;读取一个字节
;输入:BYTE_ADDR,读取字节地址
;输出:A
;------------------------------------------
I2CREAD:
LCALL I2C_START ;起始
MOV A,#0A0H
LCALL I2C_TXBYTE ;发送芯片地址数据+写信号
LCALL I2C_RXACK ;接收ACK
MOV A,BYTE_ADDR
LCALL I2C_TXBYTE ;发送RAM地址数据
LCALL I2C_RXACK ;接收ACK
LCALL I2C_START ;起始
MOV A,#0A1H
LCALL I2C_TXBYTE ;发送地址数据+读信号
LCALL I2C_RXACK ;接收ACK
LCALL I2C_RXBYTE ;接收数据
SETB C ;读一个字节C=1
LCALL I2C_TXACK ;发送NAK
LCALL I2C_STOP ;读取完成
RET
;----------------------------
;R4=读出字节数
;BYTE_ADDR=读出初始地址
;R1=数据指针
;----------------------------
I2C_PAGE_RD:
LCALL I2C_START ;起始
MOV A,#0A0H
LCALL I2C_TXBYTE ;发送芯片地址数据+写信号
LCALL I2C_RXACK ;接收ACK
MOV A,BYTE_ADDR
LCALL I2C_TXBYTE ;发送RAM地址数据
LCALL I2C_RXACK ;接收ACK
LCALL I2C_START ;起始
MOV A,#0A1H
LCALL I2C_TXBYTE ;发送地址数据+读信号
LCALL I2C_RXACK ;接收ACK
NXT_BYTE:
LCALL I2C_RXBYTE ;接收数据
MOV @R1,A
INC R1
CLR C ;连续读C=0
LCALL I2C_TXACK ;发送NAK
LCALL I2C_DELAY
DJNZ R4,NXT_BYTE
LCALL I2C_NOACK ;NOACK
LCALL I2C_STOP ;读取完成
RET
;----------------写一字节I2C数据------------
;写入一个字节
;输入:BYTE_ADDR,写入字节地址;A=写入数据
;输出:无
;------------------------------------------
I2CWRITE:
PUSH ACC
LCALL I2C_START ;开始写
MOV A,#0A0H
LCALL I2C_TXBYTE ;发送地址数据+写信号
LCALL I2C_RXACK ;接收ACK
MOV A,BYTE_ADDR
LCALL I2C_TXBYTE ;发送RAM地址数据
LCALL I2C_RXACK ;接收ACK
POP ACC
LCALL I2C_TXBYTE ;写数据
LCALL I2C_RXACK ;接收ACK
LCALL I2C_STOP ;写完成
LCALL I2C_DLYMS ;延时1ms
RET
;----------------------------
;R4=写入字节数
;BYTE_ADDR=写入初始地址
;R1=数据指针
;----------------------------
I2C_PAGE_WR:
LCALL I2C_START ;开始写
MOV A,#0A0H
LCALL I2C_TXBYTE ;发送地址数据+写信号
LCALL I2C_RXACK ;接收ACK
MOV A,BYTE_ADDR
LCALL I2C_TXBYTE ;发送RAM地址数据
LCALL I2C_RXACK ;接收ACK
NEXT_DATA: ;WRITE 16 BYTES TO
MOV A,@R1 ;EEPROM
LCALL I2C_TXBYTE ;写数据
LCALL I2C_RXACK ;接收ACK
INC R1
DJNZ R4,NEXT_DATA
LCALL I2C_STOP ;写完成
LCALL I2C_DLYMS ;延时1ms
RET
;----------------------------
;发送I2C起始信号
;----------------------------
I2C_START:
SETB SCL ;时钟高电平时数据下降沿为启动信号
LCALL I2C_DELAY
SETB SDA
LCALL I2C_DELAY
CLR SDA ;数据线下降沿
LCALL I2C_DELAY ;延时
CLR SCL ;时钟->低
LCALL I2C_DELAY ;延时
RET
;----------------------------
;发送I2C停止信号
;----------------------------
I2C_STOP:
CLR SDA
LCALL I2C_DELAY ;延时
SETB SCL ;时钟->高
LCALL I2C_DELAY ;延时
SETB SDA ;数据线上升沿
LCALL I2C_DELAY ;延时
CLR SCL
RET
;----------------------------
;发送ACK/NAK信号
;----------------------------
I2C_TXACK:
MOV SDA,C ;送ACK数据
LCALL I2C_DELAY ;延时
SETB SCL ;时钟->高
LCALL I2C_DELAY ;延时
CLR SCL ;时钟->低
LCALL I2C_DELAY ;延时
SETB SDA ;发送完成
RET
;----------------------------
;接收ACK/NAK信号
;----------------------------
I2C_RXACK:
SETB SDA ;准备读数据
LCALL I2C_DELAY ;延时
SETB SCL ;时钟->高
LCALL I2C_DELAY ;延时
MOV C,SDA ;读取ACK信号
CLR SCL ;时钟->低
LCALL I2C_DELAY ;延时
RET
;----------------------------
I2C_NOACK:
SETB SDA ;NO ACKNOWLEDGE
LCALL I2C_DELAY
SETB SCL
LCALL I2C_DELAY
CLR SCL
LCALL I2C_DELAY
RET
;----------------------------
;发送一字节数据
;----------------------------
I2C_TXBYTE:
MOV R7,#8 ;8位计数
TXNEXT:
RLC A ;移出数据位
MOV SDA,C ;数据送数据口
SETB SCL ;时钟->高
LCALL I2C_DELAY ;延时
CLR SCL ;时钟->低
LCALL I2C_DELAY ;延时
DJNZ R7,TXNEXT ;送下一位
RET
;----------------------------
;接收一字节数据
;----------------------------
I2C_RXBYTE:
MOV R7,#8 ;8位计数
RXNEXT:
SETB SCL ;时钟->高
LCALL I2C_DELAY ;延时
MOV C,SDA
RLC A
CLR SCL ;时钟->低
LCALL I2C_DELAY ;延时
DJNZ R7,RXNEXT ;收下一位
RET
;----------------------------
I2C_DELAY: ;
NOP
NOP
RET ;
;----------------------------
I2C_DLYMS:
PUSH 0
PUSH 1
MOV R1,#2
I2CDL:
MOV R0,#248
DJNZ R0,$
DJNZ R1,I2CDL
POP 1
POP 0
RET
;----------------------------
MAIN:
MOV SP,#60H
MOV A,#0AAH
MOV BYTE_ADDR,#0
LCALL I2CWRITE
LCALL I2CREAD
MOV P1,A
SJMP $
END
;----------------------------本回答被提问者采纳
第2个回答  2012-07-27
我们也用这个2402,但如果我发我们的这个软件给你,估计你也要看好久,
我大概看了你的软件,看起来好像没有什么问题,但有几个地方,我想你看一下,
DELAY 子程式,你用两个NOP,NOP ,这个DELAY 到底是多长时间?不知道你用的单片机是什么型号,但我想两个NOP ,估计也就是1US 的时间,可能还没有到1US ,DATASHEET 要求CLK ,low pulse 估计要2US 以上的吧,所以你先把这个DELAY 加长,起码DATASHEET 要求2US ,你加到5US 去看看如何,然后还有一个问题就是WAITACK 那,如果一直读不到ACK ,那你也要有个时间跳出去,是10MS ,还是多少,DATASHEET 好像写的最大时间是5MS的了吧!
如果还不行,你用示波器来对你发的东西的波形到底和你写的是不是一样。