52单片机iic读写c语言,51单片机—再论IIC—软件通用包(汇编、c语言)—03

感觉心有不甘,呵呵

a4c26d1e5885305701be709a3d33442f.png,小小整理一下。

我们知道51单片机中具有IIC总线接口的毕竟是少数(其实我就不知道那款~~)如果是是不带IIC总线的单片机,并不必扩展IIC总线接口,只是需要通过软件模拟,这无疑会给IIC总线的应用提供更广泛的空间。通常大多数的单片机应用系统中只有一个CPU,这种单主系统如果采用IIC总线技术,则总线上只有单片机对IIC总线从器件的访问,没有总线的竞争问题。这是后只需要模拟主发送和主接收时序。基于上述考虑,(这才是重点,呵呵

a4c26d1e5885305701be709a3d33442f.png),提供在这种使用情况下的时序模拟软件,使IIC总线的使用不受单片机必须带有IIC总线接口的限制。

下面的9个例子可以作为一个软件包,这个软件放入单片机系统的程序程序存储器中,以便用来调用。

1、IIC总线典型信号时序及模拟子程序(其实上一篇已经介绍过了,不过我这里有点补充,顺便小小介绍一下吧)

IIC总线上数据传送时,有起始位,停止位,应答位,非应答位等信号。按照典型IIC总线传送速率的要求,如图:

a4c26d1e5885305701be709a3d33442f.png

对于IIC总线的典型信号,可以用指令操作来模拟其时序过程。

若89C51单片机的系统时钟为12MHz,相信的单周期指令为1us,则起始位(START)、停止位(STOP)、发送应答位(MACK)、发送非应答位(MNACK)的5个模拟子程序如下:

1)启动IIC总线子程序START(先是汇编,然后是c语言的)

;--------------------------------------------------------

;发开始信号子程序

START: SETB SDA

SETB SCL ;起始条件建立大于4.7us

NOP

NOP

NOP

NOP

NOP

CLR SDA ;起始条件锁定大于4us

NOP

NOP

NOP

NOP

NOP

CLR SCL ;钳住总线,准备好发送数据

NOP

RET

//----------------------------------------------------------

//开始信号函数

void IIC_start()

{

sda=1;

_nop_();

scl=1; //开始信号建立时间大于4.7us _nop_();

_nop_();

_nop_();

_nop_();

_nop_();

sda=0;

//开始信号锁定大于4us

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

scl=0; //钳住IIC总线,准备发送或者接收数据

_nop_();

_nop_();

}

2)停止IIC总线程序STOP(先是汇编,后是c语言)

;--------------------------------------------------------

;发结束信号子程序

STOP: CLR SDA

NOP

SETB SCL ;结束总时间大于4us

NOP

NOP

NOP

NOP

NOP

SETB SDA

NOP ;保证一个终止信号和其实信号的空闲时间大于4.7us

NOP

NOP

NOP

NOP

RET

//---------------------------------------------------------------

//结束信号函数

void IIC_stop()

{

sda=0;

_nop_();

scl=1; //结束时间大于4us

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

sda=1; //保证一个终止信号和其实信号的空闲时间大于4.7us

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

}

3)主控器向被控器发送应答位信号子程序MACK(由于这个信号和发送非应答信号在c语言里写在了一起,所以最后写c语言的程序)

;--------------------------------------------------------------

;发送应答信号子程序

MACK: CLR SDA

NOP

NOP

SETB SCL NOP ;保持数据时间,既SCL为高,时间大于4.7us

NOP

NOP

NOP

NOP

CLR SCL

NOP

NOP

RET

4)主控器向被控器发送非应答新号子程序MNACK

;------------------------------------------------------------

;发送非应答信号子程序

MNACK: SETB SDA

NOP

NOP

SETB SCL

NOP ;保持数据时间,既SCL为高,时间大于4.7us

NOP

NOP

NOP

NOP

CLR SCL

NOP

NOP

RET

a4c26d1e5885305701be709a3d33442f.png然后就是上两个的c的程序:

//---------------------------------------------------------------------

//主控器件向被控器件发送应答函数

void IIC_ack_send(bit a)

{ if(a==0) //发应答信号

sda=0;

else

sda=1;

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

scl=1;

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

scl=0;

_nop_();

_nop_(); }

5)主控器件检查被控器件发送来的应答位

;---------------------------------------------------------------

;检测应答位子程序

;返回值ACK=1时表示应达

CACK: SETB SDA

NOP

NOP

SETB SCL

CLR ACK

NOP

NOP

MOV C,SDA

JC CEND

SETB ACK ;应答位置1,既返回应答位为1

CEND: NOP

CLR SCL

NOP

//-----------------------------------------------------------------

//主控器件接收应答函数

void IIC_ack_acc()

{

_nop_();

_nop_();

sda=1; //8位数据发送完后,准备接收数据

_nop_();

_nop_();

scl=1;

_nop_();

_nop_();

_nop_();

if(sda==1)

ack=0;

else

ack=1; //接收到应答位,ack为1,否则置0

scl=0;

_nop_();

_nop_(); }

在使用上述的子程序时,如果单片机不是12MHz,自己调整啊~~哈哈

2、IIC总线数据传送的模拟子程序

从IIC总线的数据操作中可以看出,出了起始位、停止位、发送应答函数、发送非应答函数、应答检测函数(这里书上把它看作了数据传送的那部分,管他呢~~~呵呵

a4c26d1e5885305701be709a3d33442f.png),还有发送一字节数据,接受以字节数据,发送n字节数据,接收n字节数据这几个子程序。

1)发送一字节数据WR_BYTE

该子程序是向虚拟的IIC总显得数据线SDA上发送一字节数据的操作。调用该子程序前,将要发送的数据送入A中。

还是先汇编,然后c代码:

;-----------------------------------------------------------------

;发送字节子程序

;字节数据放入ACC

;每发送一个字节筪掉用一次CACK(检测应答子程序),取应答位

WR_BYTE: MOV R0,#08H ;8位数据长度送R0

W_LP:

RLC A ;发送数据左移,使发送位入c

JC W_R1 ;判读发送1,还是发送0,发送1,转入W_R1

SJMP W_R0 ;发送0转入W_R0

W_LP1: DJNZ R0,W_LP

NOP

RET

W_R1: ;发送1

SETB SDA

NOP

SETB SCL

NOP

NOP

NOP

NOP

NOP

CLR SCL

SJMP W_LP1

W_R0: ;发送0

CLR SDA

NOP

SETB SCL

NOP

NOP

NOP

NOP

NOP

CLR SCL

SJMP W_LP1

//----------------------------------------------------------------------

//写一个字节函数

void send_byte(uchar c)

{

uchar bit_cnt;

for(bit_cnt=0;bit_cnt<8;bit_cnt++) //循环传送8位

{

if((c<

sda=1;

else

sda=0;

_nop_(); scl=1; //发送到数据线上

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

scl=0;

} }

2)接收一字节数据RD_BYTE(这个的汇编感觉并不肯定,有可能有错~~

a4c26d1e5885305701be709a3d33442f.png,当然c语言还是没问题的,呵呵)

;----------------------------------------------------------------------

;读取字节子程序

;读出的值再ACC中

;每取一个字节要发送一个应达或者非应答信号(要知道你读取就成了从机,就要发送一个信号告诉主机)

RD_BYTE: MOV R0,#08H ;8位数据长度

R_LP: SETB SDA ;置SDA为输入方式

NOP

CLR SCL ;置时钟线为低电平,准备接收数据

NOP

NOP

NOP

NOP

NOP

SETB SCL

NOP

MOV C,SDA ;读取数据位

RLC A ;进行数据位的处理

NOP

DJNZ R0,R_LP ;没有到8位,再来一次

RET

//------------------------------------------------------------------------

//接收一个字节函数

//返回接收的8位数据

uchar rec_byte()

{

uchar temp;

uchar bit_cnt;

temp=0;

sda=1; //置数据线为输入方式

for(bit_cnt=0;bit_cnt<8;bit_cnt++)

{

_nop_();

scl=0; //置时钟线为低电平,准备接收数据

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

scl=1;

_nop_();

_nop_();

temp<<=1;

if(sda==1) //接收当前数据位,接收内容放入temp中

temp+=1;

_nop_();

_nop_(); }

scl=0;

_nop_();

_nop_();

return(temp);

}

3)向被控器件发送n个字节数据子程序MCU_WRN_BYTE

;-----------------------------------------------------------------------

;向器件指定地址写入N个数据

;入口参数:器件从地质SLA,器件子地址SUBA、发送数据缓冲区MTD、发送字节数NUMBYTE

;占用:A,R0,R1,R3,CY

MCU_WRN_BYTE:

MOV A,NUM_BYTE

MOV R3,A

LCALL START ;启动总线

MOV A,SLA

LCALL WR_BYTE ;发送器件地址

LCALL CACK

JNB ACK,RET_WRN

MOV A,SUBA ;指定子地址,并发送

LCALL WR_BYTE

LCALL CACK

MOV R1,#MTD

WRN_DA: MOV A,@R1

LCALL WR_BYTE ;开始写入数据

LCALL CACK

JNB ACK,MCU_WRN_BYTE

INC R1

DJNZ R3,WRN_DA ;判断写完没有

RET_WRN: LCALL STOP

RET

//---------------------------------------------------------------

//向器件指定地址按页写函数(这里虽然是说按页,其实原理是一样的~~,要是不明白可以看上一篇我整理的)

//入口参数有4个:器件地址码、器件单元地址、写入的数据串的指针、写入的字节个数

//写入成功,返回1;不成功,返回0,使用后必须结束总线

bit mcu_send_string(uchar add,uchar rom_add,uchar *s,uchar

num)

{

uchar i;

IIC_start(); send_byte(add); //发送器件地址码

IIC_ack_acc();

if(ack==0)

return(0); //有应答,发送ROM的单元地址

send_byte(rom_add);

IIC_ack_acc();

if(ack==0)

return(0);

for(i=0;i

{

send_byte(*s);

IIC_ack_acc();

if(ack==0)

return(0);

s++;

}

IIC_stop();

return(1);

}

4)从被控器件读取n个字节数据子程序MCU_RDN_BYTE

;--------------------------------------------------------------------------------

;从器件地址读取N个数据

;入口参数:器件从地址,器件子地址SUBA,接收字节数NUM_BYTE

;出口参数:接收数据缓冲区

;占用:A,R0,R1,R2,R3,CY

MCU_RDN_BYTE:

MOV R3,NUM_BYTE

LCALL START

MOV A,SLA

LCALL WR_BYTE ;发送器件地址

LCALL CACK JNB ACK,RET_RDN

MOV A,SUBA ;指定子地址

LCALL WR_BYTE

LCALL CACK

LCALL START

MOV A,SLA

INC A ;准备进行读操作

LCALL WR_BYTE

LCALL CACK

JNB ACK,MCU_RDN_BYTE

MOV R1,#MRD

RDN1: LCALL RD_BYTE ;读操作开始

MOV @R1,A

DJNZ R3,SACK

LCALL MNACK ;最后一个字节发非应答位

RET_RDN: LCALL STOP

RET

SACK: LCALL MACK ;发送应答位

INC R1

SJMP RDN1

//-------------------------------------------------------------------------------

//从器件指定地址读多个字节

//入口参数有4个:器件地址码、器件单元地址、读出的数据串、读出的字节个数,写入成功返回1,不成功返回0

bit mcu_rec_string(uchar add,uchar rom_add,uchar *s,uchar

num)

{

uchar i;

IIC_start();

send_byte(add); //发送器件的地址码,这里最低位为0,写

IIC_ack_acc();

if(ack==0)

return(0);

send_byte(rom_add); //发送器件的单元地址

IIC_ack_acc();

if(ack==0)

return(0);

IIC_start(); //重新发送开始信号,开始读取数据

send_byte(add+1); //发送器件的地址码,这里最后一位为1,读

IIC_ack_acc();

if(ack==0)

return(0);

for(i=0;i

{

*s=rec_byte();

IIC_ack_send(0); //送应答信号

s++;

}

*s=rec_byte();

IIC_ack_send(1); //送非应答信号

IIC_stop();

return(1);

}

a4c26d1e5885305701be709a3d33442f.png总算是好了~~呵呵,哦了,整理好了~~要是哪里不对~~我就shit了

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是51单片机完整的IIC读取和发送C语言程序的示例代码: ```c #include <reg52.h> sbit SDA = P2^0; //定义SDA数据线引脚 sbit SCL = P2^1; //定义SCL时钟线引脚 void delay() { int i = 1000; while(i--); } void IIC_Start() { SDA = 1; SCL = 1; delay(); SDA = 0; delay(); SCL = 0; } void IIC_Stop() { SDA = 0; SCL = 1; delay(); SDA = 1; delay(); } void IIC_Ack() { SDA = 0; delay(); SCL = 1; delay(); SCL = 0; delay(); SDA = 1; } void IIC_NAck() { SDA = 1; delay(); SCL = 1; delay(); SCL = 0; delay(); } unsigned char IIC_WriteByte(unsigned char dat) { unsigned char i; for(i=0; i<8; i++) { SDA = dat & 0x80; delay(); SCL = 1; delay(); SCL = 0; dat <<= 1; } delay(); SDA = 1; delay(); SCL = 1; delay(); i = SDA; SCL = 0; return i; } unsigned char IIC_ReadByte() { unsigned char i; unsigned char dat = 0; SDA = 1; for(i=0; i<8; i++) { SCL = 1; delay(); dat <<= 1; dat |= SDA; SCL = 0; delay(); } return dat; } void IIC_Init() { SDA = 1; SCL = 1; delay(); } void main() { unsigned char dat; IIC_Init(); IIC_Start(); IIC_WriteByte(0xA0); IIC_Ack(); IIC_WriteByte(0x00); IIC_Ack(); IIC_Stop(); IIC_Start(); IIC_WriteByte(0xA1); IIC_Ack(); dat = IIC_ReadByte(); IIC_NAck(); IIC_Stop(); while(1); } ``` 这段代码实现了51单片机通过IIC总线读取和发送数据的功能。其中,IIC_Start()函数用于产生起始信号,IIC_Stop()函数用于产生停止信号,IIC_Ack()和IIC_NAck()函数分别用于产生应答和非应答信号,IIC_WriteByte()和IIC_ReadByte()函数分别用于发送和接收一个字节的数据。在main函数中,我们首先初始化IIC总线,然后发送一个写命令,接着发送一个要写入的地址,再停止总线,接着再发送一个读命令,最后接收一个字节的数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值