C语言程序>>IIC

来源:互联网 发布:双端面磨床 知乎 编辑:程序博客网 时间:2024/06/05 09:30
 #include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <avr/delay.h>
#define F_CPU 16000000//
#include <compat/twi.h>
//定义了各种模式下的状态码列表(TWSR已屏蔽预分频位),本文后面附上中文描述

//管脚定义
#define  pinSCL    0     //PC0 SCL
#define  pinSDA    1     //PC1 SDA
//为保险起见,最好在SCL/SDA接上1~10K的外部上拉电阻到VCC。

#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

#define TW_ACT    (1<<TWINT)|(1<<TWEN)|(1<<TWIE)
//TWCR只能IN/OUT,直接赋值比逻辑运算(|= &=)更节省空间

#define SLA_24CXX   0xA0    //24Cxx系列的厂商器件地址(高四位)
#define ADDR_24C02   0x00
// AT24C02的地址线A2/1/0全部接地,SLAW=0xA0+0x00<<1+0x00,SLAR=0xA0+0x00<<1+0x01

//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 //重试次数最大值


//定义全局变量
unsigned char ORGDATA[8]=
    {0xAA,0xA5,0x55,0x5A,0x01,0x02,0x03,0x04}; //原始数据
unsigned char CMPDATA[8];      //比较数据
unsigned char BUFFER[256];      //缓冲区,可以装载整个AC24C02的数据

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的数据结构变量

//仿真时在watch窗口,监控这些全局变量。


//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;
}

SIGNAL(SIG_2WIRE_SERIAL)
{//IIC中断
    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;       //保存状态
}

void init_IIC(void)
{
    unsigned char i;
 char j = 0;

    PORTC|=0x03; //SCL,SDA使能了内部的10K上拉电阻

    //TWI初始化
    TWSR=0x00;         //预分频=0^4=1
    TWBR=TWBR_SET;
    TWAR=0x00;         //主机模式,该地址无效
    TWCR=0x00;         //关闭TWI模块

    strTWI.STATUS=TW_OK;
}

char test_24c02(void)
{
    unsigned char i;
 char j = 0;

    init_IIC();

    strTWI.STATUS=TW_OK;

    TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_WRITE,0x10,&ORGDATA[0],8);
    //从0x10地址开始写入8个字节数据
    while(strTWI.STATUS==TW_BUSY);    //等待操作完成
    if (strTWI.STATUS==TW_FAIL)
    {
        return(0);//操作失败?
    }
    _delay_ms(100);        //延时等待编程完成

    i=TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_READ,0x10,&CMPDATA[0],8);
    //从0x10地址开始读出8个字节数据
    while(strTWI.STATUS==TW_BUSY);   //等待操作完成
    //如果不加等待,则需要检测返回值i才能知道当前操作是否执行了
    // 0 OP_BUSY 之前的操作没完成,没执行当前操作
    // 1 OP_RUN  当前操作执行中
    if (strTWI.STATUS==TW_FAIL)
    {
        return(0);//操作失败?
    }
    //读取成功,对比ORGDATA和CMPDATA的数据

    i=TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_READ,0x00,&BUFFER[0],256);
    //从0x00地址开始读出256个字节数据(整个ATC24C02)
    while(strTWI.STATUS==TW_BUSY);   //等待操作完成

 for(i = 0;i < 8;i ++)
 {
  if(ORGDATA[i] != CMPDATA[i])
  {
   return(0);
  }
 }
 return(0xff);
}
#ifndef _IIC_
#define _IIC_

void init_IIC(void);
char test_24c02(void);

#endif

原创粉丝点击