谁愿意告诉我i2c怎么通信的

xirenshen 2004-03-17 02:23:13
请详细一点或提供资料来源
...全文
248 30 打赏 收藏 转发到动态 举报
写回复
用AI写文章
30 条回复
切换为时间正序
请发表友善的回复…
发表回复
eric9811 2004-03-23
  • 打赏
  • 举报
回复
public SEND_DATA,RCV_DATA
extrn bit(NO_ACK,BUS_FAULT,I2C_BUSY)
;====================================
;define I/O
SCL_PIN bit P1.1 ;P0.6
SDA_PIN bit P1.0 ;P0.7
;====================================
;SLV_ADDR = A
;BUFF_POINT = R0
;BYTE_CNT = R1
;BIT_CNT = R2
;====================================
IIC_CODE SEGMENT CODE
RSEG IIC_CODE
SEND_DATA:
mov A,#0A0H
acall MASTER_CONTROLLER
jb NO_ACK,SD_EX
SD_LOOP:
mov A,@R0
acall SEND_BYTE
inc R0
jb NO_ACK,SD_EX
djnz R1,SD_LOOP
SD_EX:
acall SEND_STOP
ret
;--------------------------------------
RCV_DATA:
mov A,#0A1H
acall MASTER_CONTROLLER
jb NO_ACK,RD_EX
RD_LOOP:
acall RECV_BYTE
mov @R0,A
inc R0
djnz R1,RD_LOOP
RD_EX:
acall SEND_STOP
ret

;--------------------------------------
MASTER_CONTROLLER:
setb I2C_BUSY
clr NO_ACK
clr BUS_FAULT
jnb SCL_PIN,FAULT
jnb SDA_PIN,FAULT
clr SDA_PIN
acall DELAY_3_CYCLES
clr SCL_PIN
acall DELAY_3_CYCLES ;send START
acall SEND_BYTE ;send SLV+W/R
ret
FAULT:
setb BUS_FAULT
ret

;-------------------------------------- SEND_BYTE:
mov R2,#8
SB_LOOP:
rlc A
mov SDA_PIN,C
acall RELEASE_SCL_HIGH
acall DELAY_3_CYCLES
clr SCL_PIN
acall DELAY_3_CYCLES
djnz R2,SB_LOOP
setb SDA_PIN
acall RELEASE_SCL_HIGH
acall DELAY_4_CYCLES
jnb SDA_PIN,SB_EX
setb NO_ACK
SB_EX:
clr SCL_PIN
acall DELAY_3_CYCLES
ret
;--------------------------------------
RECV_BYTE:
mov R2,#8
RB_LOOP:
acall RELEASE_SCL_HIGH
acall DELAY_3_CYCLES
mov C,SDA_PIN
rlc A
clr SCL_PIN
acall DELAY_3_CYCLES
djnz R2,RB_LOOP
push ACC
mov A,R1
cjne A,#1,RB_ACK
setb SDA_PIN
sjmp RB_ACLK
RB_ACK:
clr SDA_PIN
RB_ACLK:
acall RELEASE_SCL_HIGH
pop ACC
acall DELAY_3_CYCLES
clr SCL_PIN
setb SDA_PIN
acall DELAY_4_CYCLES
ret
;--------------------------------------
SEND_STOP:
clr SDA_PIN
acall RELEASE_SCL_HIGH
acall DELAY_3_CYCLES
setb SDA_PIN
clr I2C_BUSY
ret
DELAY_4_CYCLES:
nop
DELAY_3_CYCLES:
nop
ret
RELEASE_SCL_HIGH:
setb SCL_PIN
jnb SCL_PIN,$
ret
end

这是用51模拟I2C的,关于I2C程序,把I2C协议看懂了就行!
xirenshen 2004-03-23
  • 打赏
  • 举报
回复
惭愧,惭愧,我看的太粗了,没注意细节,谢谢各位
W32API 2004-03-23
  • 打赏
  • 举报
回复
呵呵,谁的又会有错?
我贴的代码可是直接从 RISC 处理器项目中直接拿出来的东西。。。处理器运行频率是 100MHz+ ,延时不该用 nop(); 去做的。
xirenshen 2004-03-22
  • 打赏
  • 举报
回复
cwjof@163.com
  • 打赏
  • 举报
回复
---------------------------------------------------------------
;**************** X24C02 WRITING AND READING SUBROUTINE *************************************

COUNTER EQU 25H ;COUNTER UNIT USED TO SAVE THE CIRCLE_TIMES
BYTEADD EQU 24H ;WRITING&READ BYTE_ADDRESS OF 24C02
SENDDATA EQU R1
SDA BIT P3.4
SCL BIT P3.5
;===============================WRITE SUBROUTINE==========================================
;========= SEND WRITING_ADDRESS TO 24H UNIT AND SENDDATA TO R1 ==========
;=============================================================================================
WRITE: LCALL START
MOV A,#0A0H ;SENDING THE SLAVE ADDRESS AND WRITE SIGNAL TO 24C02(A0/A1/A2=0)
NOP
LCALL SENDBYTE
NOP
LCALL ACK ;SENDING ACK SIGNAL
NOP
MOV A, BYTEADD
LCALL SENDBYTE
NOP
LCALL ACK
MOV A,SENDDATA ;SENDING THE DATA WANTED TO BE WRITEN
LCALL SENDBYTE
LCALL ACK
NOP
LCALL STOP ;SROP THE OPREATION
NOP
RET
NOP
;==================================START PROCESS==========================================
;==SENDING A DOWN_EDGE DURING SCL HOLDING HIGH LEVEL,USE TO START A WRITE OR READ OPREATION===
;=============================================================================================
START: SETB SDA
SETB SCL
NOP
NOP
NOP
NOP
CLR SDA
NOP
NOP
NOP
NOP
CLR SCL
NOP
NOP
RET
NOP
NOP

STOP: CLR SDA
SETB SCL
NOP
NOP
NOP
NOP
SETB SDA
NOP
NOP
NOP
NOP
CLR SCL
NOP
NOP
RET
NOP
NOP

SENDBYTE: MOV COUNTER,#08H
CYCLE1: RLC A
MOV SDA, C
LCALL CLOCK
DJNZ COUNTER,CYCLE1
NOP
NOP
RET
NOP
NOP

CLOCK: NOP
SETB SCL
NOP
NOP
NOP
MOV C, SDA
CLR SCL
NOP
NOP
RET
NOP
NOP

ACK: SETB SDA
LCALL CLOCK
NOP
NOP
RET
NOP
NOP
;====================================READ SUBROUTINE=====================================
;====== SEND THE READ_ADDRESS TO THE 24H UNIT AND THE RETURN DATA IS LOCATED IN ACC ==========
;=============================================================================================
READ: SETB SDA ;Make SDA as an imput pin
LCALL START ;
MOV A,#0A0H ;SENDING THE SLAVE ADDRESS AND READ SIGNAL TO 24C02(A0/A1/A2=0)
NOP
LCALL SENDBYTE
NOP
LCALL ACK ;SENDING ACK SIGNAL
NOP
MOV A, BYTEADD
LCALL SENDBYTE
NOP
LCALL ACK
LCALL START
MOV A,#0A1H ;SENDING THE SLAVE ADDRESS AND READ SIGNAL TO 24C02(A0/A1/A2=0)
NOP
LCALL SENDBYTE
NOP
LCALL ACK ;SENDING ACK SIGNAL
NOP
MOV COUNTER,#08H
CYCLE2: LCALL CLOCK
RLC A
DJNZ COUNTER,CYCLE2
NOP
NOP
RET
END

这是汇编的
说明:25h为循环次数,即写或读多少个字节
24h存放的是24c02内部rom读或写的地址


这两个都是我亲自调试了的,没错!
  • 打赏
  • 举报
回复
#include<reg51.h>
#include<absacc.h>
#define WriteDeviceAddress 0xa0
#define ReadDviceAddress 0xa1
//extern void DelayMs(unsigned int);
//extern void Read24c02(unsigned char *RamAddress,unsigned char RomAddress,unsigned char bytes);
//extern void Write24c02(unsigned char *RamAddress,unsigned char RomAddress,unsigned char bytes);
sbit SCL=P1^1;
sbit SDA=P1^0;


void DelayMs(unsigned int number)
{
unsigned char temp;
for(;number!=0;number--)
{
for(temp=112;temp!=0;temp--)
{}
}
}


void Start()
{
SDA=1;
SCL=1;
SDA=0;
SCL=0;
}


void Stop()
{
SCL=0;
SDA=0;
SCL=1;
SDA=1;
}


void Ack()
{
SDA=0;
SCL=1;
SCL=0;
SDA=1;
}


void NoAck()
{
SDA=1;
SCL=1;
SCL=0;
}

bit TestAck()
{
bit ErrorBit;
SDA=1;
SCL=1;
ErrorBit=SDA;
SCL=0;
return(ErrorBit);
}

bit Write8Bit(unsigned char input)
{
unsigned char temp;
for(temp=8;temp!=0;temp--)
{
SDA=(bit)(input&0x80);
SCL=1;
SCL=0;
input=input<<1;
}
}


void Write24c02(unsigned char *Wdata,unsigned char RomAddress,unsigned char number)
{
Start();
Write8Bit(WriteDeviceAddress);
TestAck();
Write8Bit(RomAddress);
TestAck();
for(;number!=0;number--)
{
Write8Bit(*Wdata);
TestAck();
Wdata++;
}
Stop();
DelayMs(10);
}


unsigned char Read8Bit()
{
unsigned char temp,rbyte=0;
for(temp=8;temp!=0;temp--)
{
SCL=1;
rbyte=rbyte<<1;
rbyte=rbyte ¦((unsigned char)(SDA));
SCL=0;
}
return(rbyte);
}


Read24c02(unsigned char *RamAddress,unsigned char RomAddress,unsigned char bytes)
{
unsigned char temp,rbyte;
Start();
Write8Bit(WriteDeviceAddress);
TestAck();
Write8Bit(RomAddress);
TestAck();
Start();
Write8Bit(ReadDviceAddress);
TestAck();
while(bytes!=1)
{
*RamAddress=Read8Bit();
Ack();
RamAddress++;
bytes--;
}
*RamAddress=Read8Bit();
NoAck();
Stop();
}


main()
{
Write24c02(0x30,0x00,0x05)
Read24c02(0x30,0x00,0x05);
while(1);
}
说明:0x30是内部RAM存放地址,也就是从30H开始存放的数据,你可以改
0x00是24c02的内部地址
0x05是指一次连续写(或读)多少个字节
比如:我有5个数据,放在40h--44h
想写进24c02从05h开始的地址
即Write24c02(0x40,0x05,0x05)
读的道理一样

W32API 2004-03-22
  • 打赏
  • 举报
回复
注意时序,i2c 有几种速率,器件支持的话你多快都可以。
所以延时使用 i2c 的标准描述会更容易转移到任何器件的访问代码中。
转移后的传输时序问题只需要改变 #define 的定义,如果使用一套代码来访问多中速率的器件,可以同时把表述延时的数组传送给底层函数。
这种代码可以延展到 100M 时钟的 RISC 处理器中也正常使用。
硬件设计时同时还应注意上拉部分,这会影响电平跳变的延时。
fengyuenong 2004-03-22
  • 打赏
  • 举报
回复
这倒不会,因为产生stop或start条件,必须使之维持不变>4.0us,具体的你可以看看我发给你文档中的时序图。
对与你说的第三种:因为电脑执行一条指令(如scl=1这条指令)最多1us吧<4.0us.所以你紧接着把sda置1的话,并不会产生stop条件的。其实主要看时序,建议你好好看看时序图,你就会明白了。
xirenshen 2004-03-22
  • 打赏
  • 举报
回复
第二种我不知道是对是错,但我觉得第三种不合适,因为先把scl置1,那么可以假设之前或此时的sda一直为0,所以当你把sda再置1的时候,现在的状况可以看作是产生了一个stop条件,然后再将sda清0,可以看作是产生了一个start条件 ,也就是说产生了两个状态,当然好像也没有什么错误。
fengyuenong 2004-03-22
  • 打赏
  • 举报
回复
你说的第一种void Start() { SDA=1; SCL=1; SDA=0; SCL=0; }
和第三种 void Start() { SCL=1; SDA=1; SDA=0; SCL=0; }
这两种不是一样吗?不都是先将sda和scl置1,然后再使sda置0,最后使scl置0嘛,都是一样的。
至于你说的第二种void Start() { SCL=0; SDA=1; SCL=1; SDA=0;} 不知道它把scl置0有什么意义,我看了这么多资料没有一个是这样定义的,应该是错的。
其实你把问题想复杂了,说明了
也就是:sda,scl置1(保证其是空闲状态)————>sda=0---->scl=0
(延时>4.7us) (延时>4.0us)
就这么一个顺序而已。没有什么其它的表达方式了。
不知道你听懂没,我最近也在看i2c,基本上看懂了,互相交流吧,哈哈
xirenshen 2004-03-22
  • 打赏
  • 举报
回复
fengyuenong(风月浓) :
都是从FAQ里找出来的,前两种方法都保证在scl高电平的时候sdl上或下沿触发,只是表达的时候描述的顺序不一样,可是第三种方法是从你发给我的文件里找出来的,请看i2c.pdf文档的第六页。
其实我的问题就是:不同的表达方式是否有问题,如果有,那么,为什么?
fengyuenong 2004-03-22
  • 打赏
  • 举报
回复
我不是给你发了几个文档了吗。看看下边的程序:因为在i2c没有启动之前,scl和sda都应该是空闲状态,所以应该把sda和scl都设置为1,如果要启动,应该使sda保持高电平>4.7us,然后再让sda变为低电平0,而且还要保持低电平>4.0us,这样才算一个完整的启动信号。然后再把scl置0,准备发送或接收数据。
void Start_I2C()
{
SDA=1; //发送起始条件的数据信号
_Nop();
SCL=1;
_Nop(); //起始条件建立时间大于4.7us,延时
_Nop();
_Nop();
_Nop();
_Nop();
SDA=0; //发送起始信号
_Nop(); //起始条件锁定时间大于4us
_Nop();
_Nop();
_Nop();
_Nop();
SCL=0; //钳住I2C总线,准备发送或接收数据
_Nop();
_Nop();
}
所以start函数就这么定义的
你的第二种定义不知道你是从那里弄来的
W32API 2004-03-22
  • 打赏
  • 举报
回复
/**************************************************************************
** I2C Function
**************************************************************************/
static void i2c_100k_start(void)
{
set_sda(1);
set_scl(1);
wait_timer(T_BUF);
set_sda(0);
wait_timer(T_HD_DAT);
set_scl(0);

return;
}

static void i2c_100k_stop(void)
{
set_sda(0);
set_scl(0);
wait_timer(T_F);
set_scl(1);
wait_timer(T_R + T_SU_STO);
set_sda(1);
wait_timer(T_BUF);

return;
}

static int i2c_GetACK(void)
{
int j = 0;
int ret = -1;

set_scl(0);
wait_timer(T_LOW / 2);
set_sda(1);
wait_timer(T_LOW / 2);
set_scl(1);
wait_timer(T_R);
while (j < T_HIGH)
{
if (!sense_sda()) ret = 0;
wait_timer(T_TEST);
j += T_TEST;
}
set_scl(0);
set_sda(1);
wait_timer(T_F);

return ret;
}

static void i2c_SendACK()
{
wait_timer(T_LOW/2);
set_scl(0);
set_sda(0);
wait_timer(T_LOW/2);
set_scl(1);
wait_timer(T_R + T_HIGH);
set_scl(0);
set_sda(1);
wait_timer(T_R);

return;
}

static void i2c_SendNoACK()
{
wait_timer(T_LOW/2);
set_scl(0);
set_sda(1);
wait_timer(T_LOW/2);
set_scl(1);
wait_timer(T_R + T_HIGH);
set_scl(0);
wait_timer(T_R);

return;
}

static int i2c_100k_transmit(int byte)
{
int i = 0x80;

while (i)
{
wait_timer(T_LOW / 2);

if ((byte & i))
{
set_sda(1);
}
else if (!(byte & i))
{
set_sda(0);
}

wait_timer(T_LOW / 2);
set_scl(1);
wait_timer(T_HIGH);
set_scl(0);
i /= 2;
}

return i2c_GetACK();
}

static int i2c_100k_RXD(void)
{
int ret = 0;
int j = 0;

while(j < 8)
{
ret <<= 1;
wait_timer(T_AA);
set_scl(1);
wait_timer(T_R);
if (sense_sda())
ret |= 0x01;
else
ret |= 0x00;
wait_timer(T_HIGH);
set_scl(0);
wait_timer(T_AA + T_HD_DAT + T_LOW);

j++;
}

return ret;
}

static int i2c_100k_SendByte(int addr, int cmdbyte)
{
int ret = 0;

i2c_100k_start();

if (!(ret = i2c_100k_transmit(addr)))
ret = i2c_100k_transmit(cmdbyte);

i2c_100k_stop();

return ret;
}

static int i2c_100k_SendArray(int addr, int n, int * pcmd)
{
int ret = 0;
int i = 0;

i2c_100k_start();

ret = i2c_100k_transmit(addr);

while (!ret && i < n)
ret = i2c_100k_transmit(pcmd[i++]);

i2c_100k_stop();

return ret;
}

static int i2c_100k_ReadByte(void)
{
int ret = 0;

ret = i2c_100k_RXD();

i2c_SendACK();

return ret;
}

static int i2c_100k_ReadEndByte(void)
{
int ret = 0;

ret = i2c_100k_RXD();

i2c_SendNoACK();

return ret;
}

xirenshen 2004-03-22
  • 打赏
  • 举报
回复
居然还有这种定义
void Start()
{
SCL=1;
SDA=1;

SDA=0;
SCL=0;
}
到目前为止在官方资料上看到三种定义了,是不是说都可以,不过个人认为最后这种不是很严谨。
xirenshen 2004-03-22
  • 打赏
  • 举报
回复
没人看懂我要问的问题,快哭了
fengyuenong 2004-03-22
  • 打赏
  • 举报
回复
已经发出去了

Icestone 2004-03-20
  • 打赏
  • 举报
回复
gz
flowercity 2004-03-20
  • 打赏
  • 举报
回复
第一种是对的
你应该看I2C的时序图啊
很清楚的啊
曲阿子期 2004-03-19
  • 打赏
  • 举报
回复
也给我发一个可以吗? fzq333@sina.com
fengyuenong 2004-03-19
  • 打赏
  • 举报
回复
你给个邮箱,我发给你个文档,你看一下就明白了
加载更多回复(10)

27,375

社区成员

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

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