⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 twi.c

📁 avr单片机利用Twi总线读写eeprom24lc04
💻 C
字号:
/*********************************************
*	程序名称:TWI读写EEPROM试验程序
*	设计者:    cht
*	版本:    V1.0
*	设计日期: 08/04/07
*	修改日期: 08/04/07
*	说明:    由于I2c设备速度较慢,采用中断方式,适用于24AAXX,24LCXX,AT24Cxx
*
*
*********************************************/

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


#define uchar unsigned char
#define uint  unsigned int
#define fSCL 100000

#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<<TWIE)|(1<<TWEN)

#define SLA_24LCXX  0XA0;
//#define ADDR_24LCXX 0X00;

#define TW_WRITE 0
#define TW_READ  1

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

uchar ORGDATA[8]=
    {0xAA,0xA5,0x55,0x5A,0x01,0x02,0x03,0x04};	//原始要写入的数据
uchar CMPDATA[8];						//比较数据
uchar BUFFER[256];						//缓冲区

struct str_TWI									//TWI数据结构
{
    volatile uchar	STATUS;				//TWI_操作状态
    uchar	SLA;						//从设备的器件地址
    uint	ADDR;						//从设备的数据地址
    uchar	*pBUF;						//数据缓冲区指针
    uint	DATALEN;					//数据长度
    uchar	STATE;						//TWI读写操作步骤
    uchar	FAILCNT;					//失败重试次数
};

struct str_TWI strTWI;	
/****************************************************
* 函数名称:TWI_cmd(uchar sla,uint addr,uchar *ptr,uint len)
* 功能:    向I2C设备发送命令
* 说明:    sla=I2C设备地址+可编程地址+R/W位
			addr为I2C设备内部地址
			*ptr为数据缓冲区
			len为数据读写长度

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

uchar TWI_cmd(uchar sla,uint addr,uchar *ptr,uint len)
{
    uchar 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;
}

ISR(TWI_vect)
{
	uchar 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;							//保存状态

}


int main(void)
{
    uchar i;
   /* //上电默认DDRx=0x00,PORTx=0x00 输入,无上拉电阻
    PORTA=0xFF;									//不用的管脚使能内部上拉电阻。
    PORTB=0xFF;
    PORTC=0xFF;									//SCL,SDA使能了内部的10K上拉电阻
    PORTD=0xFF;*/

    //TWI初始化
    TWSR=0x00;									//预分频=0^4=1
    TWBR=TWBR_SET;
    TWAR=0x00;									//主机模式,该地址无效
    TWCR=0x00;									//关闭TWI模块
    sei();										//使能全局中断
    strTWI.STATUS=TW_OK;
	
    TWI_cmd(0xA0,0x10,&ORGDATA[0],8);
	//TWI_RW(0XA0,0x10,&ORGDATA[0],8);
    //从0x10地址开始写入8个字节数据
    while(strTWI.STATUS==TW_BUSY);				//等待操作完成
    if (strTWI.STATUS==TW_FAIL)
    {
        //操作失败?
    }
    _delay_ms(10);								//延时等待编程完成
    while(1)
    {
        i=TWI_cmd(0XA1,0x10,&CMPDATA[0],8);
        //从0x10地址开始读出8个字节数据
        while(strTWI.STATUS==TW_BUSY);			//等待操作完成
        //如果不加等待,则需要检测返回值i才能知道当前操作是否执行了
        // 0 OP_BUSY 之前的操作没完成,没执行当前操作
        // 1 OP_RUN  当前操作执行中
        if (strTWI.STATUS==TW_FAIL)
        {
            //操作失败?
        }
        //读取成功,对比ORGDATA和CMPDATA的数据

        i=TWI_cmd(0XA1,0x00,&BUFFER[0],256);
        //从0x00地址开始读出256个字节数据(整个ATC24C02)
        while(strTWI.STATUS==TW_BUSY);			//等待操作完成
    };
}




⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -