最近调试了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; //保存状态
}
没有人知道吗 如果只是单独读写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信号
}
jingwaner 发表于 2012-4-27 16:31 static/image/common/back.gif
如果只是单独读写FM24C64,可以不用理会TWI 状态
/*****************TWI参数初始化********************/
我刚用M16调试完,需要的话给你 jingwaner 发表于 2012-4-27 16:31 static/image/common/back.gif
如果只是单独读写FM24C64,可以不用理会TWI 状态
/*****************TWI参数初始化********************/
我试了你的程序,把写的函数改了一下我就可以正确的写了。
读函数很好用,再次感谢大侠的无私分享 wgxf333 发表于 2012-4-28 10:49 static/image/common/back.gif
我刚用M16调试完,需要的话给你
嗯,谢谢哈,我弄出来了,就用2楼的程序 有FM24C04的吗?
为什么都不用TWI硬件中断来实现呢?? foxpro2005 发表于 2012-12-11 18:46 static/image/common/back.gif
为什么都不用TWI硬件中断来实现呢??
喜欢模拟的
对协议会理解的更好
页:
[1]