资讯详情

AVR TWI读写范例程序(AT24C02)

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

#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的外部上拉电阻到。

#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 A_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 ; //TWI_操作状态 unsigned char SLA; //从设备的器件地址 unsigned int ADDR; //从设备的数据地址 unsigned char *pBUF; //数据缓冲区指针 unsigned int DATALEN; //数据长度 unsigned char ST; //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: //错误状态 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) { unsigned char 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_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_WRITE,0x10,&ORGDATA[0],8); //从0x10地址开始写入8个字节数据 while(strTWI.STATUS==TW_BUSY); //等待操作完成 if (strTWI.STATUS==TW_FAIL) { //操作失败? } _delay_ms(10); //延时等待编程完成 while(1) { 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) { //操作失败? } //读取成功,

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台