现在的位置: 首页 > 综合 > 正文

也发一个TWI多机通讯的简单例子.并经过抗干扰测试 IIC I2C

2013年09月14日 ⁄ 综合 ⁄ 共 6649字 ⁄ 字号 评论关闭

这个简单例子是这样的:
总线上只设一个主机,主机使用查询读写
总线上允许有多个从机,从机使用硬件中断功能。
*****************
我开始是把主机和从机都使用中断功能的,但是,我发现主机很容易受到线路的干扰而“TWI死机”(使用导线一端接地,另外一端不断的摩擦短接TWI的通讯线路),死机时,TWI的TWINT不再置位中断标记,致使中断程序无法运行,即使把通讯线路物理截断(单保留主机和上拉电阻),TWINT也无法置位。这时候,你的中断代码再完善都没有用了,也谈不上中断后判断仲裁成败的问题了,也就是说,这个AVR(ATMEGA48)硬件TWI可能有点问题,当然,也不排除我菜的缘故。
为了抗干扰,我使用这个办法,主机每次读写完毕后,都退出TWI功能一次。从机暂时没有发现死机现象,但是,为了保险,建议从机不断的检测总线是否被占用,一旦占用超时,也退出TWI一次,然后再进入待机状态


/*****************************************************

CodeWizardAVR V1.25.9 Professional
Chip type           : ATmega48
Clock frequency     : 8.000000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 128
*****************************************************/ 
// TWCR     寄存器说明
// Bit 7 – TWINT: TWI 中断标志
// Bit 6 – TWEA: 使能TWI 应答
// Bit 5 – TWSTA: TWI START 状态位
// Bit 4 – TWSTO: TWI STOP 状态位
// Bit 3 – TWWC: TWI 写冲突标志
// Bit 2 – TWEN: TWI 使能
// Bit 1 – Res: 保留
// Bit 0 – TWIE: TWI 中断使能

#include <mega48.h>
#include <delay.h>
#include <nokia3310.h>  

#define iic_time             65535             //主机在单位时间内判断运行是否正常
#define iic_init             TWCR=0b11000101;  //待机
#define iic_start_run        TWCR=0b10100100;  //主机发出start信号  
#define iic_byte_run         TWCR=0b10000100;  //清除中断.启动.停止的标记/启动主机数据收或发
#define iic_byte_run_ack     TWCR=0b11000100;  //清除中断.启动.停止的标记/启动主机数据收或发/应答使能
#define iic_stop_run         TWCR=0b10010100;  //主机停止/释放总线/TWI就绪 
#define iic_off              TWCR=0b10000000;  //强制退出TWI,适用于总线受干扰时主机使用
#define iic_stop_no          TWCR&16           //发送STOP是否结束
#define iic_int_ok           TWCR>127          //中断标记置位
#define iic_status           TWSR              //TWI运行状态 
#define iic_datas            TWDR    

unsigned char iic_news[10];                    //数据量
unsigned int  iic_n;                           //通讯数据量计数
                                              
interrupt [TWI] void twi_isr(void)             //从机twi中断程序/请在相关位置处理被读写的数据
{                                                
  lcd_puthex(TWSR);                            //LCD监控 
  
  switch(iic_status)
  {     
    case 0x60:                                 //自己的SLA+W已经被接收ACK已返回
    {
      iic_n=0;
      iic_init;
      break;
    }
    
    case 0x70:                                 //接收到广播地址ACK已返回
    {
      iic_n=0;
      iic_init;
      break;
    }
    
    case 0x80:                                 //以前以自己的SLA+W被寻址,数据已经被接收,ACK已返回
    {
      iic_news[iic_n]=iic_datas; 
      iic_n++;
      iic_init; 
      break;
    } 
    
    case 0xA0:                                 //在以从机工作时接收到STOP或重复START
    { 
      iic_init;
      break;
    }
    
    case 0xA8:                                 //自己的SLA+R已经被接收ACK已返回
    { 
      iic_n=0;
      iic_datas=iic_news[iic_n];
      iic_init;
      break;
    } 
    
    case 0xB8:                                 //数据已经发送接收到ACK
    {
      iic_n++;
      iic_datas=iic_news[iic_n];
      iic_init;
      break;
    }
    
    case 0xC0:                                 //数据已经发送接收到NOT_ACK
    {
      iic_init;
      break;
    }
    
    case 0x90:                                 //以前以广播方式被寻址,数据已经被接收ACK已返回
    {
      iic_news[iic_n]=iic_datas;
      iic_init;               
      iic_n++;
      break;
    }
    
    default:    
    { 
      iic_off;    
      iic_init;
      break; 
    }           
  } 

/*/从机*********************************************************************

void main(void)

  unsigned int n; 
  lcd_init();
  lcd_cls();   
  TWBR=32;                                              //8MHz/(16+2*32)=100KHz 
  TWAR=2+1;                                             //设定自己的地址,广播接收使能 
  iic_init;                                 
  #asm("sei")                                           //打开全局中断  
  while (1)
  { 
    if(PINC.5==0 || PINC.4==0) if(n<65535) n++;         //检测线路是否被长久占用/防止TWI死机
    if(PINC.5==1 && PINC.4==1) n=0; 
    if(n==65535) {iic_off;iic_init;}  
  }
}
                             
//*************************************************************************/

//主机写(不含STOP信号)/查询方式/参数:从机地址.数量.数组

unsigned char iic_write(unsigned char address, unsigned char n, unsigned char *p)

  unsigned char i;
  unsigned int  a;
  if(address%2 || n<1) return(4);                       //参数不对返回4
  iic_start_run;                                        //发送START
  for(a=0; a<iic_time; a++) if(iic_int_ok) break;       //简单的限时等待
  if(iic_status!=0x08 && iic_status!=0x10) return(1);   //如果(重复)START失败就返回1
  iic_datas=address; iic_byte_run;                      //发送地址SLA+W
  for(a=0; a<iic_time; a++) if(iic_int_ok) break;
  if(iic_status!=0x18) return(2);                       //SLA+W发送失败就返回2
  for(i=0;i<n;i++)                                      //发送数据
  {  
    iic_datas=p[i];
    iic_byte_run;
    for(a=0; a<iic_time; a++) if(iic_int_ok) break;
    if(iic_status!=0x28) return(3);                     //数据发送失败就返回3
  }
  return(0);                                            //成功就返回0

//主机读(不含STOP信号)/查询方式/参数:从机地址.数量.数组

unsigned char iic_read(unsigned char address, unsigned char n, unsigned char *p)

  unsigned char i;
  unsigned int  a;
  if(address%2 || n<1) return(4);                       //参数不对返回4
  iic_start_run;                                        //发送START
  for(a=0; a<iic_time; a++) if(iic_int_ok) break;       //简单的限时等待
  if(iic_status!=0x08 && iic_status!=0x10) return(1);   //如果(重复)START失败就返回1
  iic_datas=address+1; iic_byte_run;                    //发送地址SLA+R
  for(a=0; a<iic_time; a++) if(iic_int_ok) break;
  if(iic_status!=0x40) return(2);                       //SLA+R发送失败就返回2
  for(i=0;i<n;i++)                                      //读取数据
  {                
    if(i+1<n) iic_byte_run_ack; else iic_byte_run;
    for(a=0; a<iic_time; a++) if(iic_int_ok) break;
    if(iic_status!=0x50 && iic_status!=0x58) return(3); //数据读取失败就返回3
    p[i]=iic_datas;
  }
  return(0);                                            //成功就返回0
}

void iic_stop(void)                                     //主机发送STOP
{
  unsigned int a;
  iic_stop_run;
  for(a=0; a<iic_time; a++) if(iic_stop_no); else break; 
}

//主机*********************************************************************

void main(void)
{  
  unsigned char iic_ok;
  iic_news[0]=0xAA;iic_news[1]=0xBB;iic_news[2]=0xCC;iic_news[3]=0xDD;
  lcd_init();
  lcd_cls();   
  TWBR=32;                                              //8MHz/(16+2*32)=100KHz  
  while (1)
  { 
    lcd_gotoxy(0,0);
    //请更新需要写的数据
    iic_ok=iic_write(0,4,iic_news);                     //成功返回0/(地址,数据量,数组)   
    lcd_puthex(iic_ok); 
    //请更新需要写的数据
    iic_ok=iic_write(2,4,iic_news); 
    lcd_puthex(iic_ok); 
    
    iic_ok=iic_read(2,4,iic_news);
    lcd_puthex(iic_ok);
    //请保存读取的数据 
    DDRD=255;PORTD=PORTD^255;                           //LED观测/正常时LED闪很快而看不见/不正常时闪很慢

    iic_stop();
    iic_off;                                            //关闭.复位TWI,目的是防止线路受干扰而无法置位中断标记

  }
}

【上篇】
【下篇】

抱歉!评论已关闭.