xtaens 发表于 2012-4-27 14:35:42

最近调试了AVR88的硬件TWI,写FM24C64,但还是没出来,请高人...


EEPROM:FM24C64
编译器:IAR AVR 5.51
CPU:Atmega88
外部晶振:7.3728MHz

现在的现象是:读出来的全是0(不知对不对),无法写入

下面是高人的代码:

//------------------------------------------------------------------------------
// 文件名:TWI_driver.h
// 说明:TWI总线主机驱动
// 功能:实现AVR作为TWI主机,读写从机的功能(使用中断进行处理,有一定容错性)
// 使用方法:   1.TWI读写函数(包括随机读,连续读,字节写,页写)
//unsigned char TWI_RW(unsigned char sla,unsigned int addr,unsigned char *ptr,\
//                                          unsigned int len);
//            根据sla的最低位决定(由中断程序中判断)
//            bit0=1 TW_READ读
//            bit0=0 TW_WRITE 写
//            sla                        器件地址(不能搞错)(实现可以先定义好每个器件的读写地址)
//                    addr                EEPROM地址(0~1023)
//                    *ptr                读写数据缓冲区
//                    len                        读数据长度(1~1024),写数据长度(1 or 8 or 16)
//            返回值                是否能执行当前操作
//
// 运行平台:   AVR 8bit (M128已验证)
// 依存关系:   TWI_driver.c
// 移植人:   刘大川
// 最后修改:   2008-5-28
//------------------------------------------------------------------------------
#ifndef _TWI_DRIVER_H_
#define _TWI_DRIVER_H_

//TWI总线速度定义
#define fSCL                                100000                                //TWI时钟为100KHz
//预分频系数=1(TWPS=0)
#if F_CPU < fSCL*36
#define TWBR_SET                         10;                                        //TWBR必须大于等于10
#else
#define TWBR_SET                (F_CPU/fSCL-16)/2;        //计算TWBR值
#endif

//主机返回ACT时的TWCR值
#define TW_ACT                                (1<<TWINT)|(1<<TWEN)|(1<<TWIE)


//在这里设置从机地址(分读、写,区别为最低位状态)
#define ADDR_24CXX                        0xA0        //24Cxx系列的厂商器件地址(高四位)
#define ADDR_24CXX_READ                ADDR_24CXX+TW_READ
#define ADDR_24CXX_WRITE        ADDR_24CXX+TW_WRITE                                     
                                     
//TWI_操作状态                         
#define TW_BUSY                                0          
#define TW_OK                                1          
#define TW_FAIL                                2          
//TWI_读写命令状态                   
#define OP_BUSY                                0          
#define OP_RUN                                1          
                                     
                                     
//TWI读写操作公共步骤                
#define ST_FAIL                                0                //出错状态
#define ST_START                        1                //START状态检查
#define ST_SLAW                                2                //SLAW状态检查
#define ST_WADDR                        3                //ADDR状态检查
//TWI读操作步骤                      
#define ST_RESTART                        4                //RESTART状态检查
#define ST_SLAR                                5                //SLAR状态检查
#define ST_RDATA                        6                //读取数据状态检查,循环n字节
//TWI写操作步骤                      
#define ST_WDATA                        7                //写数据状态检查,循环n字节

#define FAIL_MAX                        20                //重试次数最大值

//------------------------------------------------------------------------------
//数据类型声明
//------------------------------------------------------------------------------
struct str_TWI                                                                        //TWI数据结构
{
    volatile unsigned char        STATUS;                                //TWI_操作状态
    unsigned char        SLA;                                                //从设备的器件地址
    unsigned int        ADDR;                                                //从设备的数据地址
    unsigned char        *pBUF;                                                //数据缓冲区指针
    unsigned int        DATALEN;                                        //数据长度
    unsigned char        STATE;                                                //TWI读写操作步骤
    unsigned char        FAILCNT;                                        //失败重试次数
};
struct str_TWI strTWI;                                                        //TWI的数据结构变量
//------------------------------------------------------------------------------
// 函数原型声明
//------------------------------------------------------------------------------
//void TWI_Init(void);
unsigned char TWI_RW(unsigned char sla,unsigned int addr,unsigned char *ptr,\
                                        unsigned int len);

#endif




//------------------------------------------------------------------------------
// 文件名:TWI_driver.c
// 说明:TWI总线主机驱动
// 功能:实现AVR作为TWI主机,读写从机的功能(使用中断进行处理,有一定容错性)
// 使用方法:   1.TWI读写函数(包括随机读,连续读,字节写,页写)
//unsigned char TWI_RW(unsigned char sla,unsigned int addr,unsigned char *ptr,\
//                                          unsigned int len);
//            根据sla的最低位决定(由中断程序中判断)
//            bit0=1 TW_READ读
//            bit0=0 TW_WRITE 写
//            sla                        器件地址(不能搞错)(实现可以先定义好每个器件的读写地址)
//                    addr                EEPROM地址(0~1023)
//                    *ptr                读写数据缓冲区
//                    len                        读数据长度(1~1024),写数据长度(1 or 8 or 16)
//            返回值                是否能执行当前操作
//
// 运行平台:   AVR 8bit (M128已验证)
// 依存关系:   TWI_driver.h
// 移植人:   刘大川
// 最后修改:   2008-5-28
//------------------------------------------------------------------------------

#include <avr/interrupt.h>
#include <util/twi.h>
#include "twi_driver.h"

//AT24C02的读写函数(包括随机读,连续读,字节写,页写)
//根据sla的最低位决定(由中断程序中判断)
//bit0=1 TW_READ读
//bit0=0 TW_WRITE 写
//sla                        器件地址(不能搞错)
//        addr                EEPROM地址(0~1023)
//        *ptr                读写数据缓冲区
//        len                        读数据长度(1~1024),写数据长度(1 or 8 or 16)
//返回值                是否能执行当前操作
unsigned char TWI_RW(unsigned char sla,unsigned int addr,unsigned char *ptr,unsigned int len)
{
    unsigned char i;
    if (strTWI.STATUS==TW_BUSY)
    {//TWI忙,不能进行操作
      return OP_BUSY;
    }
    strTWI.STATUS=TW_BUSY;
    i=(addr>>8)<<1;
    i&=0x06;                                                                        //考虑了24C04/08的EEPROM地址高位放在SLA里面
    strTWI.SLA=sla+i;
    strTWI.ADDR=addr;
    strTWI.pBUF=ptr;
    strTWI.DATALEN=len;
    strTWI.STATE=ST_START;
    strTWI.FAILCNT=0;
    TWCR=(1<<TWSTA)|TW_ACT;                                                //启动start信号
    return OP_RUN;
}


/*
TWI中断函数
        这个函数流程只是考虑了器件地址后有一个字节数据(命令)地址的IIC器件
        (大部分IIC接口器件都是这种类型,常见的例如AT24C01/02/04/08/16,DS1307,DS1721等)
        对于有两个字节数据地址的IIC器件(例如AT24C32/64/128/256等大容量EEPROM),请稍作改动

//根据strTWI.SLA的最低位决定
//bit0=1 TW_READ读
//bit0=0 TW_WRITE 写

        虽然中断服务程序很长,但每次只执行一个 case,所以耗时并不长。
*/
ISR(TWI_vect)
{//TWI中断
    unsigned char action,state,status;
    action=strTWI.SLA&TW_READ;                                        //取操作模式
    state=strTWI.STATE;
    status=TWSR&0xF8;                                                        //屏蔽预分频位
    if ((status>=0x60)||(status==0x00))
    {//总线错误或从机模式引发的中断,不予处理
      return;
    }
    switch(state)
    {
    case ST_START:        //START状态检查
      if(status==TW_START)
      {//发送start信号成功
            TWDR=strTWI.SLA&0xFE;                                //发送器件地址写SLAW
            TWCR=TW_ACT;                                         //触发下一步动作,同时清start发送标志
      }
      else
      {//发送start信号出错
            state=ST_FAIL;
      }
      break;
    case ST_SLAW:        //SLAW状态检查
      if(status==TW_MT_SLA_ACK)
      {//发送器件地址成功
            TWDR=strTWI.ADDR;                                        //发送eeprom地址
            TWCR=TW_ACT;                                         //触发下一步动作
      }
      else
      {//发送器件地址出错
            state=ST_FAIL;
      }
      break;
    case ST_WADDR:        //ADDR状态检查
      if(status==TW_MT_DATA_ACK)
      {//发送eeprom地址成功
            if (action==TW_READ)
            {//读操作模式
                TWCR=(1<<TWSTA)|TW_ACT;                        //发送restart信号,下一步将跳到RESTART分支
            }
            else
            {//写操作模式
                TWDR=*strTWI.pBUF++;                 //写第一个字节
                strTWI.DATALEN--;
                state=ST_WDATA-1;                                //下一步将跳到WDATA分支
                TWCR=TW_ACT;                                 //触发下一步动作
            }
      }
      else
      {//发送eeprom地址出错
            state=ST_FAIL;
      }
      break;
    case ST_RESTART:        //RESTART状态检查,只有读操作模式才能跳到这里
      if(status==TW_REP_START)
      {//发送restart信号成功
            TWDR=strTWI.SLA;                                        //发器件地址读SLAR
            TWCR=TW_ACT;                                         //触发下一步动作,同时清start发送标志
      }
      else
      {//重发start信号出错
            state=ST_FAIL;
      }
      break;
    case ST_SLAR:        //SLAR状态检查,只有读操作模式才能跳到这里
      if(status==TW_MR_SLA_ACK)
      {//发送器件地址成功
            if (strTWI.DATALEN--)
            {//多个数据
                TWCR=(1<<TWEA)|TW_ACT;                        //设定ACK,触发下一步动作
            }
            else
            {//只有一个数据
                TWCR=TW_ACT;                                        //设定NAK,触发下一步动作
            }
      }
      else
      {//发送器件地址出错
            state=ST_FAIL;
      }
      break;
    case ST_RDATA:        //读取数据状态检查,只有读操作模式才能跳到这里
      state--;                                                                //循环,直到读完指定长度数据
      if(status==TW_MR_DATA_ACK)
      {//读取数据成功,但不是最后一个数据
            *strTWI.pBUF++=TWDR;
            if (strTWI.DATALEN--)
            {//还有多个数据
                TWCR=(1<<TWEA)|TW_ACT;                        //设定ACK,触发下一步动作
            }
            else
            {//准备读最后一个数据
                TWCR=TW_ACT;                                        //设定NAK,触发下一步动作
            }
      }
      else if(status==TW_MR_DATA_NACK)
      {//已经读完最后一个数据
            *strTWI.pBUF++=TWDR;
            TWCR=(1<<TWSTO)|TW_ACT;                                //发送停止信号,不会再产生中断了
            strTWI.STATUS=TW_OK;
      }
      else
      {//读取数据出错
            state=ST_FAIL;
      }
      break;
    case ST_WDATA:        //写数据状态检查,只有写操作模式才能跳到这里
      state--;                                                                //循环,直到写完指定长度数据
      if(status==TW_MT_DATA_ACK)
      {//写数据成功
            if (strTWI.DATALEN)
            {//还要写
                TWDR=*strTWI.pBUF++;
                strTWI.DATALEN--;
                TWCR=TW_ACT;                                 //触发下一步动作
            }
            else
            {//写够了
                TWCR=(1<<TWSTO)|TW_ACT;                        //发送停止信号,不会再产生中断了
                strTWI.STATUS=TW_OK;
                //启动写命令后需要10ms(最大)的编程时间才能真正的把数据记录下来
                //编程期间器件不响应任何命令
            }
      }
      else
      {//写数据失败
            state=ST_FAIL;
      }
      break;
    default:
      //错误状态
      state=ST_FAIL;
      break;
    }

    if (state==ST_FAIL)
    {//错误处理
      strTWI.FAILCNT++;
      if (strTWI.FAILCNT<FAIL_MAX)
      {//重试次数未超出最大值,
            TWCR=(1<<TWSTA)|TW_ACT;                                //发生错误,启动start信号
      }
      else
      {//否则停止
            TWCR=(1<<TWSTO)|TW_ACT;                                //发送停止信号,不会再产生中断了
            strTWI.STATUS=TW_FAIL;
      }
    }
    state++;
    strTWI.STATE=state;                                                        //保存状态
}

xtaens 发表于 2012-4-27 16:19:36

没有人知道吗

jingwaner 发表于 2012-4-27 16:31:46

如果只是单独读写FM24C64,可以不用理会TWI 状态

/*****************TWI参数初始化********************/
void TWI_Init()
{
   TWCR= 0x00;                         //清除控制寄存器
   TWBR= 0x28;                         //780HZ
   TWSR= 0x01;                         //4分频
   TWCR= (1<<TWEA)|(1<<TWEN);          //使能TWI
}
/*****************TWI读取一个字节数据(单子地址器件)********************/
uchar TWI_Read_OneByte_OneADD(uchar Device_ADD,uchar Slave_ADD)
{
   uchar Temp;
   TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);      //发出Start信号
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位

   TWDR=(Device_ADD);                           //写入器件地址(W)
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位

   TWDR=(Slave_ADD);                            //写入子地址
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位

   TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);      //发出Restart信号   
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位
   
   TWDR=(Device_ADD|0x01);                      //写入器件地址(R)
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位

   TWCR=(1<<TWINT)|(1<<TWEN);                   //读出数据
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位
   Temp=TWDR;
   TWCR=(1<<TWINT)|(1<<TWSTO)|(1<<TWEN);      //Stop信号
   return Temp;
}
/*****************TWI读取一个字节数据(双子地址器件)********************/
uchar TWI_Read_OneByte_TwoADD(uchar Device_ADD,uint Slave_ADD)
{
   uchar Temp;
   uchar ADD_H,ADD_L;
   ADD_L=(uchar)Slave_ADD;
   Slave_ADD>>=8;
   ADD_H=(uchar)Slave_ADD;
   TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);      //发出Start信号
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位

   TWDR=(Device_ADD);                           //写入器件地址(W)
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位

   TWDR=(ADD_H);                              //写入高位子地址
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位
   
   TWDR=(ADD_L);                              //写入低位子地址
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位   

   TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);      //发出Restart信号   
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位
   
   TWDR=(Device_ADD|0x01);                      //写入器件地址(R)
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位

   TWCR=(1<<TWINT)|(1<<TWEN);                   //读出数据
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位
   Temp=TWDR;
   
   TWCR=(1<<TWINT)|(1<<TWSTO)|(1<<TWEN);      //Stop信号
   return Temp;
}
/*****************TWI读取多个字节数据(双子地址器件)********************/
void TWI_Read_MultiByte_TwoADD(uchar Device_ADD,uint Slave_ADD,char *Output,uchar Number)
{
   uchar i;
   uchar ADD_H,ADD_L;
   ADD_L=(uchar)Slave_ADD;
   Slave_ADD>>=8;
   ADD_H=(uchar)Slave_ADD;
   TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);      //发出Start信号
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位

   TWDR=(Device_ADD);                           //写入器件地址(W)
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位

   TWDR=(ADD_H);                              //写入高位子地址
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位
   
   TWDR=(ADD_L);                              //写入低位子地址
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位   

   TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);      //发出Restart信号   
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位
   
   TWDR=(Device_ADD|0x01);                      //写入器件地址(R)
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位

   TWCR=(1<<TWINT)|(1<<TWEN);                   //读出数据
   for(i=0;i<Number-1;i++)
      {
         TWCR|=(1<<TWEA);                     //每接收一次,发送一个ACK
         while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位
         Output=TWDR;
      }
   TWCR&=~(1<<TWEA);                            //最后一次,发送NACK
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位
   Output=TWDR;
   Output='\0';                //最后一位不上'\0'变成字符串
   
   TWCR=(1<<TWINT)|(1<<TWSTO)|(1<<TWEN);      //Stop信号
}
/*****************TWI写入多个字节数据(双子地址器件)********************/
void TWI_Write_MultiByte_TwoADD(uchar Device_ADD,uint Slave_ADD,char* Input,uchar Number)
{
   uchar i;
   uchar ADD_H,ADD_L;
   ADD_L=(uchar)Slave_ADD;
   Slave_ADD>>=8;
   ADD_H=(uchar)Slave_ADD;
   TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);      //发出Start信号
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位

   TWDR=(Device_ADD);                           //写入器件地址(W)
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位

   TWDR=(ADD_H);                              //写入高位子地址
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位
   
   TWDR=(ADD_L);                              //写入低位子地址
   TWCR=(1<<TWINT)|(1<<TWEN);                  
   while(!(TWCR&(1<<TWINT)));                   //等待TWINT置位
   
   for(i=7-Number;i<7;i++)                      //TODO:******写入的是序列的后几位****
      {
         TWDR=Input;   
         TWCR=(1<<TWINT)|(1<<TWEN);             //写入数据
         while(!(TWCR&(1<<TWINT)));             //等待TWINT置位
      }
   
   TWCR=(1<<TWINT)|(1<<TWSTO)|(1<<TWEN);      //Stop信号
}

wgxf333 发表于 2012-4-28 10:49:12

jingwaner 发表于 2012-4-27 16:31 static/image/common/back.gif
如果只是单独读写FM24C64,可以不用理会TWI 状态

/*****************TWI参数初始化********************/


我刚用M16调试完,需要的话给你

xtaens 发表于 2012-4-28 13:53:21

jingwaner 发表于 2012-4-27 16:31 static/image/common/back.gif
如果只是单独读写FM24C64,可以不用理会TWI 状态

/*****************TWI参数初始化********************/


我试了你的程序,把写的函数改了一下我就可以正确的写了。

读函数很好用,再次感谢大侠的无私分享

xtaens 发表于 2012-4-28 14:00:45

wgxf333 发表于 2012-4-28 10:49 static/image/common/back.gif
我刚用M16调试完,需要的话给你

嗯,谢谢哈,我弄出来了,就用2楼的程序

wzhenhua 发表于 2012-12-11 09:04:54

有FM24C04的吗?

foxpro2005 发表于 2012-12-11 18:46:18

为什么都不用TWI硬件中断来实现呢??

xtaens 发表于 2012-12-17 08:30:40

foxpro2005 发表于 2012-12-11 18:46 static/image/common/back.gif
为什么都不用TWI硬件中断来实现呢??

喜欢模拟的

对协议会理解的更好
页: [1]
查看完整版本: 最近调试了AVR88的硬件TWI,写FM24C64,但还是没出来,请高人...