论坛风格切换切换到宽版
  • 3365阅读
  • 1回复

汇编和C51模拟I2C的程序 [复制链接]

上一主题 下一主题
离线BA3CE
 
发帖
1652
只看楼主 倒序阅读 0楼 发表于: 2004-06-07
前几天我转载了个c51模拟i2c的程序,在实际使用中发现偶尔通讯失败,经用逻辑分析仪分析,发现主要出现在ack和启动/停止i2c总线函数上,我将这部分改为asm的,实际效果很好。

sda bit p1.1
scl bit p1.2
public   start_i2c,stop_i2c,_ack_i2c
i2c_dt   segment data
i2c_pr   segment code
rseg     i2c_dt
stacksave:ds     1
rseg     i2c_pr  

start_i2c:
      setb sda   ;发送起始条件的数据信号
      nop
      setb scl
      nop     ;起始条件建立时间大于4.7us,延时
      nop
      nop
      nop
      nop
      clr sda   ;发送起始信号
      nop     ;起始条件锁定时间大于4μs
      nop
      nop
      nop
      nop
      clr scl   ;钳住i2c总线,准备发送或接收数据
      nop
      nop
      ret
stop_i2c:
      clr sda   ;发送结束条件的数据信号
      nop     ;发送结束条件的时钟信号
      setb scl   ;结束条件建立时间大于4μs
      nop
      nop
      nop
      nop
      nop
      setb sda   ;发送i2c总线结束信号
      nop
      nop
      nop
      nop
      ret      
_ack_i2c:
      mov a,r7
      jz   ack
      setb sda
      sjmp no_ack
ack:     clr sda      
no_ack:   nop
      nop
      nop
      setb scl
      nop
      nop
      nop
      nop
      nop
      clr scl
      nop
      nop
      ret
      end


#include <reg51.h>       /*头文件的包含*/
#include <intrins.h>
#define uchar unsigned char /*宏定义*/
#define uint unsigned int

#define _nop() _nop_()     /*定义空指令*/

/* 常,变量定义区 */

extern void start_i2c(void);
extern void stop_i2c(void);
extern void ack_i2c(uchar idata);

sbit bell=p2^6;                                 /*端口位定义*/
sbit sda=p1^1;         /*模拟i2c数据传送位*/
sbit scl=p1^2;         /*模拟i2c时钟控制位*/

                                /*状态标志*/
bit ack;             /*应答标志位*/

/*******************************************************************
          字节数据传送函数          
函数原型: void sendbyte(uchar c);
功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
  此状态位进行操作.(不应答或非应答都使ack=0 假)  
  发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
********************************************************************/
void sendbyte(uchar c)
{
uchar bitcnt;

for(bitcnt=0;bitcnt<8;bitcnt++) /*要传送的数据长度为8位*/
  {
  if((c<<bitcnt)&0x80)sda=1;   /*判断发送位*/
    else sda=0;          
  _nop();
  scl=1;           /*置时钟线为高,通知被控器开始接收数据位*/
    _nop();
    _nop();           /*保证时钟高电平周期大于4μs*/
    _nop();
    _nop();
    _nop();      
  scl=0;
  }
 
  _nop();
  _nop();
  sda=1;           /*8位发送完后释放数据线,准备接收应答位*/
  _nop();
  _nop();  
  scl=1;
  _nop();
  _nop();
  _nop();
  if(sda==1)ack=0;  
    else ack=1;     /*判断是否接收到应答信号*/
  scl=0;
  _nop();
  _nop();
}

/*******************************************************************
          字节数据传送函数          
函数原型: uchar rcvbyte();
功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号),
  发完后请用应答函数。
********************************************************************/      
uchar rcvbyte()
{
uchar retc;
uchar bitcnt;

retc=0;
sda=1;         /*置数据线为输入方式*/
for(bitcnt=0;bitcnt<8;bitcnt++)
    {
    _nop();      
    scl=0;     /*置时钟线为低,准备接收数据位*/
    _nop();
    _nop();       /*时钟低电平周期大于4.7μs*/
    _nop();
    _nop();
    _nop();
    scl=1;     /*置时钟线为高使数据线上数据有效*/
    _nop();
    _nop();
    retc=retc<<1;
    if(sda==1)retc=retc+1; /*读数据位,接收的数据位放入retc中 */
    _nop();
    _nop();
    }
scl=0;  
_nop();
_nop();
return(retc);
}


/*******************************************************************
            向无子地址器件发送字节数据函数          
函数原型: bit isendbyte(uchar sla,ucahr c);
功能:   从启动总线到发送地址,数据,结束总线的全过程,从器件地址sla.
      如果返回1表示操作成功,否则操作有误。
注意:   使用前必须已结束总线。
********************************************************************/
bit isendbyte(uchar sla,uchar c)
{
  start_i2c();           /*启动总线*/
  sendbyte(sla);         /*发送器件地址*/
  if(ack==0)return(0);
  sendbyte(c);           /*发送数据*/
  if(ack==0)return(0);
stop_i2c();           /*结束总线*/
return(1);
}




/*******************************************************************
            向有子地址器件发送多字节数据函数          
函数原型: bit isendstr(uchar sla,uchar suba,ucahr *s,uchar no);
功能:   从启动总线到发送地址,子地址,数据,结束总线的全过程,从器件
      地址sla,子地址suba,发送内容是s指向的内容,发送no个字节。
      如果返回1表示操作成功,否则操作有误。
注意:   使用前必须已结束总线。
********************************************************************/
bit isendstr(uchar sla,uchar suba,uchar *s,uchar no)
{
  uchar i;

  start_i2c();           /*启动总线*/
  sendbyte(sla);         /*发送器件地址*/
  if(ack==0)return(0);
  sendbyte(suba);         /*发送器件子地址*/
  if(ack==0)return(0);

  for(i=0;i<no;i++)
  {  
  sendbyte(*s);           /*发送数据*/
    if(ack==0)return(0);
  s++;
  }
stop_i2c();           /*结束总线*/
return(1);
}






/*******************************************************************
            向无子地址器件读字节数据函数          
函数原型: bit ircvbyte(uchar sla,ucahr *c);
功能:   从启动总线到发送地址,读数据,结束总线的全过程,从器件地
      址sla,返回值在c.
      如果返回1表示操作成功,否则操作有误。
注意:   使用前必须已结束总线。
********************************************************************/
bit ircvbyte(uchar sla,uchar *c)
{
  start_i2c();           /*启动总线*/
  sendbyte(sla+1);       /*发送器件地址*/
  if(ack==0)return(0);
  *c=rcvbyte();           /*读取数据*/
  ack_i2c(1);           /*发送非就答位*/
stop_i2c();             /*结束总线*/
return(1);
}



/*******************************************************************
            向有子地址器件读取多字节数据函数          
函数原型: bit isendstr(uchar sla,uchar suba,ucahr *s,uchar no);
功能:   从启动总线到发送地址,子地址,读数据,结束总线的全过程,从器件
      地址sla,子地址suba,读出的内容放入s指向的存储区,读no个字节。
      如果返回1表示操作成功,否则操作有误。
注意:   使用前必须已结束总线。
********************************************************************/
bit ircvstr(uchar sla,uchar suba,uchar *s,uchar no)
{
  uchar i;

  start_i2c();           /*启动总线*/
  sendbyte(sla);         /*发送器件地址*/
  if(ack==0)return(0);
  sendbyte(suba);         /*发送器件子地址*/
  if(ack==0)return(0);

  start_i2c();
  sendbyte(sla+1);
    if(ack==0)return(0);

  for(i=0;i<no-1;i++)
  {  
  *s=rcvbyte();           /*发送数据*/
    ack_i2c(0);           /*发送就答位*/
  s++;
  }
  *s=rcvbyte();
  ack_i2c(1);           /*发送非应位*/
stop_i2c();             /*结束总线*/
return(1);
}






                /*   完毕     */
离线雨夜听香
发帖
566
只看该作者 1楼 发表于: 2004-06-10
好程序         !