搜索
bottom↓
回复: 10

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

[复制链接]

出0入0汤圆

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

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

 楼主| 发表于 2011-8-20 13:14:41 | 显示全部楼层
/*****************************************************
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;
    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=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,目的是防止线路受干扰而无法置位中断标记

  }
}

出0入0汤圆

 楼主| 发表于 2011-8-20 13:16:36 | 显示全部楼层
点击此处下载 ourdev_669431SP7BZK.rar(文件大小:48K) (原文件名:M48主机查询多从机中断通讯I2C.rar)

出0入0汤圆

发表于 2011-8-20 14:25:25 | 显示全部楼层
谢谢楼主贡献!正想搞这个!make!!!

出0入0汤圆

发表于 2011-8-21 09:08:25 | 显示全部楼层
mark~iic

出0入0汤圆

发表于 2011-8-23 10:55:14 | 显示全部楼层
正在学习

出0入0汤圆

发表于 2011-11-3 23:36:45 | 显示全部楼层
学习

出0入0汤圆

发表于 2011-11-6 12:15:31 | 显示全部楼层
……楼主,我用avr studio编程的……里面说sei和cli没有声明……是怎么回事呢?请教下~~!

出0入0汤圆

 楼主| 发表于 2011-11-27 13:26:40 | 显示全部楼层
回复【7楼】 demon75


(原文件名:QQ截图20111127132747.png)

出0入0汤圆

发表于 2012-1-11 20:13:33 | 显示全部楼层
LZ,请教下从机怎么发数据给主机呀。

出0入4汤圆

发表于 2012-12-13 21:42:25 | 显示全部楼层
谢谢,近几天一直关注IIC
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-24 05:19

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表