📄 i2c.inc
字号:
;*************************************************************************
; I2C.INC
; 标准80C51单片机模拟I2C总线的主机程序头文件
; Copyright (c) 2006,广州周立功单片机发展有限公司
; All rights reserved.
; 本程序仅供学习参考,不提供任何可靠性方面的担保;请勿用于商业目的
;*************************************************************************
;*************************************************************************
;模拟I2C总线的引脚定义
;*************************************************************************
I2C_SCL BIT P3.4
I2C_SDA BIT P3.5
;*************************************************************************
;定义I2C总线时钟的延时值,要根据实际情况修改,取值1~255
;SCL信号周期约为(I2C_DELAY_VALUE*4+12)个机器周期
;*************************************************************************
I2C_DELAY_VALUE EQU 5
;*************************************************************************
;定义I2C总线停止后在下一次开始之前的等待时间,取值1~65535
;等待时间约为(I2C_STOP_WAIT_VALUE*8)个机器周期
;对于多数器件取值为1即可;但对于某些器件来说,较长的延时是必须的
;*************************************************************************
I2C_STOP_WAIT_VALUE EQU 1
;*************************************************************************
;定义延时变量,用于宏I2C_Delay()
;*************************************************************************
;unsigned char data I2C_Delay_t;
I2C_Delay_t DATA GLOBAL_IRAM ;在主文件里要用SET定义GLOBAL_IRAM的初值
GLOBAL_IRAM SET GLOBAL_IRAM+1
;*************************************************************************
;宏定义:I2C_Delay
;功能:延时,模拟I2C总线专用
;*************************************************************************
I2C_Delay MACRO
MOV I2C_Delay_t, #I2C_DELAY_VALUE
DJNZ I2C_Delay_t, $
ENDM
;*************************************************************************
;函数:I2C_Init()
;功能:I2C总线初始化,使总线处于空闲状态
;说明:在main()函数的开始处,通常应当要执行一次本函数
;*************************************************************************
;void I2C_Init()
I2C_Init:
;{
; I2C_SCL = 1;
; I2C_Delay();
; I2C_SDA = 1;
; I2C_Delay();
SETB I2C_SCL
I2C_Delay
SETB I2C_SDA
I2C_Delay
;}
RET
;*************************************************************************
;函数:I2C_Start()
;功能:产生I2C总线的起始状态
;说明:
; SCL处于高电平期间,当SDA出现下降沿时启动I2C总线
; 不论SDA和SCL处于什么电平状态,本函数总能正确产生起始状态
; 本函数也可以用来产生重复起始状态
; 本函数执行后,I2C总线处于忙状态
;*************************************************************************
;void I2C_Start()
;{
I2C_Start:
; I2C_SDA = 1;
; I2C_Delay();
; I2C_SCL = 1;
; I2C_Delay();
; I2C_SDA = 0;
; I2C_Delay();
; I2C_SCL = 0;
; I2C_Delay();
SETB I2C_SDA
I2C_Delay
SETB I2C_SCL
I2C_Delay
CLR I2C_SDA
I2C_Delay
CLR I2C_SCL
I2C_Delay
;}
RET
;*************************************************************************
;函数:I2C_Write()
;功能:向I2C总线写1个字节的数据
;参数:
; dat:要写到总线上的数据
;*************************************************************************
;void I2C_Write(unsigned char dat)
;dat = R7
I2C_Write:
;{
; unsigned char t = 8;
; 分配:t = R1
MOV R1, #8
MOV A, R7
; do
I2C_Write_do:
; {
; I2C_SDA = (bit)(dat & 0x80);
; dat <<= 1;
MOV C, ACC.7
MOV I2C_SDA, C
ADD A, ACC
; I2C_SCL = 1;
; I2C_Delay();
; I2C_SCL = 0;
; I2C_Delay();
SETB I2C_SCL
I2C_Delay
CLR I2C_SCL
I2C_Delay
; } while ( --t != 0 );
DJNZ R1, I2C_Write_do
;}
RET
;*************************************************************************
;函数:I2C_Read()
;功能:从从机读取1个字节的数据
;返回:读取的一个字节数据
;*************************************************************************
;unsigned char I2C_Read()
;返回值 = ACC
I2C_Read:
;{
; unsigned char dat;
; unsigned char t = 8;
; 分配:dat = ACC
; 分配:t = R1
MOV R1, #8
; I2C_SDA = 1; //在读取数据之前,要把SDA拉高
SETB I2C_SDA
; do
I2C_Read_do:
; {
; I2C_SCL = 1;
; I2C_Delay();
; dat <<= 1;
; if ( I2C_SDA ) dat |= 0x01;
SETB I2C_SCL
I2C_Delay
ADD A, ACC
JNB I2C_SDA, I2C_Read_endif
INC A
I2C_Read_endif:
; I2C_SCL = 0;
; I2C_Delay();
CLR I2C_SCL
I2C_Delay
; } while ( --t != 0 );
DJNZ R1, I2C_Read_do
; return dat;
;}
RET
;*************************************************************************
;函数:I2C_GetAck()
;功能:读取从机应答位
;返回:
; 0:从机应答
; 1:从机非应答
;说明:
; 从机在收到每个字节的数据后,要产生应答位
; 从机在收到最后1个字节的数据后,一般要产生非应答位
;*************************************************************************
;bit I2C_GetAck()
;返回值 = CY
I2C_GetAck:
;{
; bit ack;
; 分配:ack = CY
; I2C_SDA = 1;
; I2C_Delay();
; I2C_SCL = 1;
; I2C_Delay();
; ack = I2C_SDA;
; I2C_SCL = 0;
; I2C_Delay();
SETB I2C_SDA
I2C_Delay
SETB I2C_SCL
I2C_Delay
MOV C, I2C_SDA
CLR I2C_SCL
I2C_Delay
; return ack;
;}
RET
;*************************************************************************
;函数:I2C_PutAck()
;功能:主机产生应答位或非应答位
;参数:
; ack=0:主机产生应答位
; ack=1:主机产生非应答位
;说明:
; 主机在接收完每一个字节的数据后,都应当产生应答位
; 主机在接收完最后一个字节的数据后,应当产生非应答位
;*************************************************************************
;void I2C_PutAck(bit ack)
;ack = 0x20.0
I2C_PutAck:
;{
; I2C_SDA = ack;
; I2C_Delay();
; I2C_SCL = 1;
; I2C_Delay();
; I2C_SCL = 0;
; I2C_Delay();
MOV C, 0x20.0
MOV I2C_SDA, C
I2C_Delay
SETB I2C_SCL
I2C_Delay
CLR I2C_SCL
I2C_Delay
;}
RET
;*************************************************************************
;函数:I2C_Stop()
;功能:产生I2C总线的停止状态
;说明:
; SCL处于高电平期间,当SDA出现上升沿时停止I2C总线
; 不论SDA和SCL处于什么电平状态,本函数总能正确产生停止状态
; 本函数执行后,I2C总线处于空闲状态
;*************************************************************************
;void I2C_Stop()
I2C_Stop:
;{
; unsigned int t;
; 分配:t = (R0,R1)
; I2C_SDA = 0;
; I2C_Delay();
; I2C_SCL = 1;
; I2C_Delay();
; I2C_SDA = 1;
; I2C_Delay();
CLR I2C_SDA
I2C_Delay
SETB I2C_SCL
I2C_Delay
SETB I2C_SDA
I2C_Delay
; t = I2C_STOP_WAIT_VALUE;
MOV R0, #HIGH(I2C_STOP_WAIT_VALUE)
MOV R1, #LOW(I2C_STOP_WAIT_VALUE)
; while ( --t != 0 ); //在下一次产生Start之前,要加一定的延时
I2C_Stop_while:
DJNZ_W R0,R1, I2C_Stop_while ;宏DJNZ_W的定义见Branch.INC
;}
RET
;*************************************************************************
;函数:I2C_Puts()
;功能:主机通过I2C总线向从机发送多个字节的数据
;参数:
; SlaveAddr:从机地址(高7位是从机地址,最低位是写标志0)
; SubAddr:从机的子地址
; Size:数据的字节数
; *dat:要发送的数据
;返回:
; 0:发送成功
; 1:在发送过程中出现异常
;*************************************************************************
;bit I2C_Puts(unsigned char SlaveAddr, unsigned char SubAddr, unsigned char Size, char *dat)
;SlaveAddr = R7
;SubAddr = R6
;Size = R5
;*dat = (0x08,0x09,0x0A)
;返回值 = CY
I2C_Puts:
;{
;//检查长度
; if ( Size == 0 ) return 0;
CJNE R5, #0, I2C_Puts_endif1
CLR C
RET
I2C_Puts_endif1:
;//确保从机地址最低位是0
; SlaveAddr &= 0xFE;
ANL 0x07, #0xFE
;//启动I2C总线
; I2C_Start();
LCALL I2C_Start
;//发送从机地址
; I2C_Write(SlaveAddr);
LCALL I2C_Write
; if ( I2C_GetAck() )
LCALL I2C_GetAck
JNC I2C_Puts_endif2
; {
; I2C_Stop();
LCALL I2C_Stop
; return 1;
SETB C
RET
; }
I2C_Puts_endif2:
;//发送子地址
; I2C_Write(SubAddr);
MOV A, R6
MOV R7, A
LCALL I2C_Write
; if ( I2C_GetAck() )
LCALL I2C_GetAck
JNC I2C_Puts_endif3
; {
; I2C_Stop();
LCALL I2C_Stop
; return 1;
SETB C
RET
; }
I2C_Puts_endif3:
;//发送数据
; do
I2C_Puts_do:
; {
; I2C_Write(*dat++);
MOV R1, 0x08
MOV DPH, 0x09
MOV DPL, 0x0A
LCALL BytePtrLD ;函数BytePtrLD()的定义见Pointer.INC
MOV R7, A
LCALL I2C_Write
INC_W 0x09,0x0A ;宏INC_W的定义见Calc.INC
; if ( I2C_GetAck() )
LCALL I2C_GetAck
JNC I2C_Puts_endif4
; {
; I2C_Stop();
LCALL I2C_Stop
; return 1;
SETB C
RET
; }
I2C_Puts_endif4:
; } while ( --Size != 0 );
DJNZ R5, I2C_Puts_do
;//发送完毕,停止I2C总线,并返回结果
; I2C_Stop();
LCALL I2C_Stop
; return 0;
CLR C
;}
RET
;*************************************************************************
;函数:I2C_Put()
;功能:主机通过I2C总线向从机发送1个字节的数据
;参数:
; SlaveAddr:从机地址(高7位是从机地址,最低位是写标志0)
; SubAddr:从机的子地址
; dat:要发送的数据
;返回:
; 0:发送成功
; 1:在发送过程中出现异常
;*************************************************************************
;bit I2C_Put(unsigned char SlaveAddr, unsigned char SubAddr, char dat)
;SlaveAddr = R7
;SubAddr = R6
;dat = R5
;返回值 = CY
I2C_Put:
;{
PUSH 0x08
PUSH 0x09
PUSH 0x0A
; return I2C_Puts(SlaveAddr,SubAddr,1,&dat);
MOV 0x08, #PtrTypeIDATA ;PtrTypeIDATA的定义见Pointer.INC
MOV 0x09, #0
PUSH 0x05
MOV 0x0A, SP
MOV R5, #1
LCALL I2C_Puts
DEC SP
POP 0x0A
POP 0x09
POP 0x08
;}
RET
;*************************************************************************
;函数:I2C_Gets()
;功能:主机通过I2C总线从从机接收多个字节的数据
;参数:
; SlaveAddr:从机地址(高7位是从机地址,最低位是读标志1)
; SubAddr:从机的子地址
; Size:数据的字节数
; *dat:保存接收到的数据
;返回:
; 0:接收成功
; 1:在接收过程中出现异常
;*************************************************************************
;bit I2C_Gets(unsigned char SlaveAddr, unsigned char SubAddr, unsigned char Size, char *dat)
;SlaveAddr = R7
;SubAddr = R6
;Size = R5
;*dat = (0x08,0x09,0x0A)
;返回值 = CY
I2C_Gets:
;{
;//检查长度
; if ( Size == 0 ) return 0;
CJNE R5, #0, I2C_Gets_endif1
;注:CJNE执行后,CY会自动清0
RET
I2C_Gets_endif1:
;//确保从机地址最低位是0
; SlaveAddr &= 0xFE;
ANL 0x07, #0xFE
;//启动I2C总线
; I2C_Start();
LCALL I2C_Start
;//发送从机地址
; I2C_Write(SlaveAddr);
LCALL I2C_Write
; if ( I2C_GetAck() )
LCALL I2C_GetAck
JNC I2C_Gets_endif2
; {
; I2C_Stop();
LCALL I2C_Stop
; return 1;
SETB C
RET
; }
I2C_Gets_endif2:
;//发送子地址
; I2C_Write(SubAddr);
PUSH 0x07
MOV A, R6
MOV R7, A
LCALL I2C_Write
POP 0x07
; if ( I2C_GetAck() )
LCALL I2C_GetAck
JNC I2C_Gets_endif3
; {
; I2C_Stop();
LCALL I2C_Stop
; return 1;
SETB C
RET
; }
I2C_Gets_endif3:
;//发送重复起始条件
; I2C_Start();
LCALL I2C_Start
;//发送从机地址
; SlaveAddr |= 0x01;
INC R7
; I2C_Write(SlaveAddr);
LCALL I2C_Write
; if ( I2C_GetAck() )
LCALL I2C_GetAck
JNC I2C_Gets_endif4
; {
; I2C_Stop();
LCALL I2C_Stop
; return 1;
SETB C
RET
; }
I2C_Gets_endif4:
;//接收数据
; for (;;)
I2C_Gets_for:
; {
; *dat++ = I2C_Read();
LCALL I2C_Read
MOV R1, 0x08
MOV DPH, 0x09
MOV DPL, 0x0A
LCALL BytePtrST ;函数BytePtrST()的定义见Pointer.INC
INC_W 0x09,0x0A ;宏INC_W的定义见Calc.INC
; if ( --Size == 0 )
DJNZ R5, I2C_Gets_endif5
; {
; I2C_PutAck(1);
PUSH 0x20
SETB 0x20.0
LCALL I2C_PutAck
POP 0x20
; break;
SJMP I2C_Gets_endfor
; }
I2C_Gets_endif5:
; I2C_PutAck(0);
PUSH 0x20
CLR 0x20.0
LCALL I2C_PutAck
POP 0x20
; }
SJMP I2C_Gets_for
I2C_Gets_endfor:
;//接收完毕,停止I2C总线,并返回结果
; I2C_Stop();
LCALL I2C_Stop
; return 0;
CLR C
;}
RET
;*************************************************************************
;函数:I2C_Get()
;功能:主机通过I2C总线从从机接收1个字节的数据
;参数:
; SlaveAddr:从机地址(高7位是从机地址,最低位是读标志1)
; SubAddr:从机的子地址
; *dat:保存接收到的数据
;返回:
; 0:接收成功
; 1:在接收过程中出现异常
;*************************************************************************
;bit I2C_Get(unsigned char SlaveAddr, unsigned char SubAddr, char *dat)
;SlaveAddr = R7
;SubAddr = R6
;*dat = (0x08,0x09,0x0A)
;返回值 = CY
I2C_Get:
;{
PUSH 0x05
; return I2C_Gets(SlaveAddr,SubAddr,1,dat);
MOV R5, #1
LCALL I2C_Gets
POP 0x05
;}
RET
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -