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

📄 twi_main.doc

📁 iic for avr very good
💻 DOC
📖 第 1 页 / 共 2 页
字号:
/***********************************************
****      AVR TWI读写读写范例                 ***
****  	                                     ***
**** 作者:  HJJourAVR                        ***
**** 编译器:WINAVR20050214                   ***
****                                         ***
****  	www.OurAVR.com	 2005.10.18          ***
***********************************************/
/*
本程序简单的示范了如何使用ATMEGA16的TWI 读写AT24C02 IIC EEPROM
    TWI协议
     	(即IIC协议,请认真参考IIC协议的内容,否则根本就不能掌握)
    一主多从的应用,M16作主机
     	(M16做从机和多主多从的应用不多,请自行参考相关文档)
	中断模式
    	(因为AVR的速度很高,而IIC的速度相对较低,
     	采用查询模式会长时间独占CPU,令CPU的利用率明显下降。
     	特别是IIC速度受环境影响只能低速通讯时,对系统的实时性产生严重的影响。
     	查询模式可以参考其它文档和软件模拟IIC的文档)
     AT24C02/04/08的操作特点
 
出于简化程序考虑,各种数据没有对外输出,学习时建议使用JTAG ICE硬件仿真器
*/

/**********************ATEEPROM读写过程*********************************
	字节写:Start→器件地址&W→应答→选ROM地址→应答→写数据→应答→Stop
中断过程	START→MT_SLA_ACK→MT_DATA_ACK(地址)→MT_DATA_ACK(数据)(STOP中止)

	页面写:Start→器件地址&W→应答→选ROM地址→应答→写数据→应答→写NEXT地址数据→应答→。。。写NEXT地址数据→应答→Stop
中断过程	START→MT_SLA_ACK→MT_DATA_ACK(地址)→MT_DATA_ACK(数据)→MT_DATA_ACK(数据)→。。。MT_DATA_ACK(数据)(STOP中止)
						 
	字节读:Start→器件地址&W→应答→选ROM地址→应答→Restart→器件地址&R→应答→读数据→无应答→Stop
中断过程	START→MT_SLA_ACK→MT_DATA_ACK(地址)→RE_START→MR_SLA_ACK→MR_DATA_NOACK(STOP中止)
				
	页面读:Start→器件地址&W→应答→选ROM地址→应答→Restart→器件地址&R→应答→读数据→应答→读NEXT地址数据→应答→。。。读NEXT地址数据→无应答→Stop
中断过程	START→MT_SLA_ACK→MT_DATA_ACK(地址)→RE_START→MR_SLA_ACK→MR_DATA_ACK→MR_DATA_ACK→。。。MR_DATA_NOACK(STOP中止)		
*************************************************************************/

#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <avr/delay.h>
//时钟定为外部晶振7.3728MHz,F_CPU=7372800
#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;
}

/*
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,所以耗时并不长。
*/
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:
        //错误状态

⌨️ 快捷键说明

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