这不,看了OURAVR网站的范例,稍微修改了一下,帖在这里当做个笔记.
//
usart.h
#include < avr / io.h >
// 管脚定义
#define PIN_RXD 0 // PD0 RXD
#define PIN_TXD 1 // PD1 TXD
// 常量定义
#define BAUDRATE 9600 // 波特率
void put_c(unsigned char c);
void put_s(unsigned char * ptr);
void init_USART( void );
#include < avr / io.h >
// 管脚定义
#define PIN_RXD 0 // PD0 RXD
#define PIN_TXD 1 // PD1 TXD
// 常量定义
#define BAUDRATE 9600 // 波特率
void put_c(unsigned char c);
void put_s(unsigned char * ptr);
void init_USART( void );
//
usart.c
#include " usart.h "
void put_c(unsigned char c) // 发送采用查询方式
... {
while( !(UCSRA & (1<<UDRE)) );
UDR=c;
}
void put_s(unsigned char * ptr)
... {
while (*ptr)
...{
put_c(*ptr++);
}
put_c(0x0D);
put_c(0x0A); //结尾发送回车换行
}
void init_USART( void ) // USART 初始化
... {
//USART 9600 8, n,1 PC上位机软件(超级终端等)也要设成同样的设置才能通讯
UCSRC = (1<<URSEL) | 0x06;
UBRRL= (F_CPU/BAUDRATE/16-1)%256;
UBRRH= (F_CPU/BAUDRATE/16-1)/256;
UCSRA = 0x00;
UCSRB = (1<<TXEN);
//使能接收中断,使能接收,使能发送
}
#include " usart.h "
void put_c(unsigned char c) // 发送采用查询方式
... {
while( !(UCSRA & (1<<UDRE)) );
UDR=c;
}
void put_s(unsigned char * ptr)
... {
while (*ptr)
...{
put_c(*ptr++);
}
put_c(0x0D);
put_c(0x0A); //结尾发送回车换行
}
void init_USART( void ) // USART 初始化
... {
//USART 9600 8, n,1 PC上位机软件(超级终端等)也要设成同样的设置才能通讯
UCSRC = (1<<URSEL) | 0x06;
UBRRL= (F_CPU/BAUDRATE/16-1)%256;
UBRRH= (F_CPU/BAUDRATE/16-1)/256;
UCSRA = 0x00;
UCSRB = (1<<TXEN);
//使能接收中断,使能接收,使能发送
}
//
TWI_main.c
/**/ /***********************************************
**** 名 称:AVR TWI使用范例_AT24C02
****
**** 作 者:zhiyu
**** 编译器:WINAVR20070525
****
**** 参 考:http://www.ouravr.com/guide_index.html(TWI范例(AT24C02))
**** 日 期:2007.07.26
****
**** 芯 片:M16L
**** 时钟源:外部4M晶振
****
**** 结 果:测试成功
**** 问 题:中断里面的state部分不理解,如自加操作,其他基本没什么问题
***********************************************/
/**/ /*
本程序简单的示范了如何使用ATMEGA16的TWI 读写AT24C02 IIC EEPROM
TWI协议
(即IIC协议,请认真参考IIC协议的内容,否则根本就不能掌握)
一主多从的应用,M16作主机
(M16做从机和多主多从的应用不多,请自行参考相关文档)
中断模式
(因为AVR的速度很高,而IIC的速度相对较低,
采用查询模式会长时间独占CPU,令CPU的利用率明显下降。
特别是IIC速度受环境影响只能低速通讯时,对系统的实时性产生严重的影响。
查询模式可以参考其它文档和软件模拟IIC的文档)
AT24C02/04/08的操作特点
出于简化程序考虑,各种数据没有对外输出,学习时建议使用JTAG ICE硬件仿真器
*/
#include < avr / io.h >
#include < avr / interrupt.h >
#include < util / delay.h >
#include " usart.h "
#include < util / twi.h >
// 定义了各种模式下的状态码列表(TWSR已屏蔽预分频位),本文后面附上中文描述
// 管脚定义
#define pinSCL 0 // PC0 SCL
#define pinSDA 1 // PC1 SDA
// 为保险起见,最好在SCL/SDA接上1~10K的外部上拉电阻到VCC。
#define FREQ 4 // FREQ为系统振荡周期(以MHZ为单位,因为现在用的是外部4M晶振,所以FREQ=4)
#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 ] =
... {65,66,67,68,69,70,71,72} ; // 原始数据:64:A; 66:B; 67:c……
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)
// 返回值 是否能执行当前操作
// 此函数参考《AVR单片机GCC程序设计》P72和P120
void DelayMs(unsigned int t)
... {
unsigned int i;
for(i=0;i<t;i++)
_delay_loop_2(FREQ*4-1);
}
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)
... {//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|TW_READ; //发器件地址读SLAR(原来的程序只是TWDR=strTWI.SLA,不得)
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;
unsigned int m,n,k,t;
//上电默认DDRx=0x00,PORTx=0x00 输入,无上拉电阻
PORTA=0xFF; //不用的管脚使能内部上拉电阻。
PORTB=0xFF;
PORTC=0xFF; //SCL,SDA使能了内部的10K上拉电阻
//串口初始化
DDRD =(1<<PIN_TXD); //TXD为输出
PORTD =0xFF;
init_USART();
//TWI初始化
TWSR=0x00; //预分频=4^0=1
TWBR=TWBR_SET;
TWAR=0x00; //主机模式,该地址无效
TWCR=0x00; //关闭TWI模块
sei(); //使能全局中断
put_s("Hello!");
put_s("这是一个简单TWI程序");
put_s("请你仔细对照数据");
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)
...{
put_s("TWI写操作FAIL!");
}
DelayMs(10); //延时等待编程完成
put_s("TWI写操作succeed!");
while(1)
...{
//从0x10地址开始读出8个字节数据放入CMPDATA数组中
i=TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_READ,0x10,&CMPDATA[0],8);
while(strTWI.STATUS==TW_BUSY); //等待操作完成
// 如果不加等待,则需要检测返回值i才能知道当前操作是否执行了
// 0 OP_BUSY 之前的操作没完成,没执行当前操作
// 1 OP_RUN 当前操作执行中
if (strTWI.STATUS==TW_FAIL)
...{
put_s("TWI读操作FAIL!");
}
//读取成功,对比ORGDATA和CMPDATA的数据
put_s("原来ORGDATA的数据:");
for(m=0;m<8;m++)
...{
put_c(ORGDATA[m]);
put_c(' ');
}
put_c(0x0D);
put_c(0x0A);
put_s("新的CMPDATA的数据:");
for(n=0;n<8;n++)
...{
put_c(CMPDATA[n]);
put_c(' ');
}
put_c(0x0D);
put_c(0x0A);
//读取整个AC24C02的数据
i=TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_READ,0x00,&BUFFER[0],256);
//从0x00地址开始读出256个字节数据(整个ATC24C02)
while(strTWI.STATUS==TW_BUSY); //等待操作完成
put_s("整个AC24C02的数据:");
for(k=0,t=1;k<256;k++,t++)
...{
if(BUFFER[k]>=65 && BUFFER[k]<=72)
put_c(BUFFER[k]);
else
put_c(48);
put_c(' ');
if(t==16)
...{
put_c(0x0D);
put_c(0x0A);
t=0;
}
}
put_c(0x0D);
put_c(0x0A);
DelayMs(50000); //延时,好看数据
};
}
/**/ /***********************************************
**** 名 称:AVR TWI使用范例_AT24C02
****
**** 作 者:zhiyu
**** 编译器:WINAVR20070525
****
**** 参 考:http://www.ouravr.com/guide_index.html(TWI范例(AT24C02))
**** 日 期:2007.07.26
****
**** 芯 片:M16L
**** 时钟源:外部4M晶振
****
**** 结 果:测试成功
**** 问 题:中断里面的state部分不理解,如自加操作,其他基本没什么问题
***********************************************/
/**/ /*
本程序简单的示范了如何使用ATMEGA16的TWI 读写AT24C02 IIC EEPROM
TWI协议
(即IIC协议,请认真参考IIC协议的内容,否则根本就不能掌握)
一主多从的应用,M16作主机
(M16做从机和多主多从的应用不多,请自行参考相关文档)
中断模式
(因为AVR的速度很高,而IIC的速度相对较低,
采用查询模式会长时间独占CPU,令CPU的利用率明显下降。
特别是IIC速度受环境影响只能低速通讯时,对系统的实时性产生严重的影响。
查询模式可以参考其它文档和软件模拟IIC的文档)
AT24C02/04/08的操作特点
出于简化程序考虑,各种数据没有对外输出,学习时建议使用JTAG ICE硬件仿真器
*/
#include < avr / io.h >
#include < avr / interrupt.h >
#include < util / delay.h >
#include " usart.h "
#include < util / twi.h >
// 定义了各种模式下的状态码列表(TWSR已屏蔽预分频位),本文后面附上中文描述
// 管脚定义
#define pinSCL 0 // PC0 SCL
#define pinSDA 1 // PC1 SDA
// 为保险起见,最好在SCL/SDA接上1~10K的外部上拉电阻到VCC。
#define FREQ 4 // FREQ为系统振荡周期(以MHZ为单位,因为现在用的是外部4M晶振,所以FREQ=4)
#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 ] =
... {65,66,67,68,69,70,71,72} ; // 原始数据:64:A; 66:B; 67:c……
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)
// 返回值 是否能执行当前操作
// 此函数参考《AVR单片机GCC程序设计》P72和P120
void DelayMs(unsigned int t)
... {
unsigned int i;
for(i=0;i<t;i++)
_delay_loop_2(FREQ*4-1);
}
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)
... {//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|TW_READ; //发器件地址读SLAR(原来的程序只是TWDR=strTWI.SLA,不得)
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;
unsigned int m,n,k,t;
//上电默认DDRx=0x00,PORTx=0x00 输入,无上拉电阻
PORTA=0xFF; //不用的管脚使能内部上拉电阻。
PORTB=0xFF;
PORTC=0xFF; //SCL,SDA使能了内部的10K上拉电阻
//串口初始化
DDRD =(1<<PIN_TXD); //TXD为输出
PORTD =0xFF;
init_USART();
//TWI初始化
TWSR=0x00; //预分频=4^0=1
TWBR=TWBR_SET;
TWAR=0x00; //主机模式,该地址无效
TWCR=0x00; //关闭TWI模块
sei(); //使能全局中断
put_s("Hello!");
put_s("这是一个简单TWI程序");
put_s("请你仔细对照数据");
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)
...{
put_s("TWI写操作FAIL!");
}
DelayMs(10); //延时等待编程完成
put_s("TWI写操作succeed!");
while(1)
...{
//从0x10地址开始读出8个字节数据放入CMPDATA数组中
i=TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_READ,0x10,&CMPDATA[0],8);
while(strTWI.STATUS==TW_BUSY); //等待操作完成
// 如果不加等待,则需要检测返回值i才能知道当前操作是否执行了
// 0 OP_BUSY 之前的操作没完成,没执行当前操作
// 1 OP_RUN 当前操作执行中
if (strTWI.STATUS==TW_FAIL)
...{
put_s("TWI读操作FAIL!");
}
//读取成功,对比ORGDATA和CMPDATA的数据
put_s("原来ORGDATA的数据:");
for(m=0;m<8;m++)
...{
put_c(ORGDATA[m]);
put_c(' ');
}
put_c(0x0D);
put_c(0x0A);
put_s("新的CMPDATA的数据:");
for(n=0;n<8;n++)
...{
put_c(CMPDATA[n]);
put_c(' ');
}
put_c(0x0D);
put_c(0x0A);
//读取整个AC24C02的数据
i=TWI_RW(SLA_24CXX+(ADDR_24C02<<1)+TW_READ,0x00,&BUFFER[0],256);
//从0x00地址开始读出256个字节数据(整个ATC24C02)
while(strTWI.STATUS==TW_BUSY); //等待操作完成
put_s("整个AC24C02的数据:");
for(k=0,t=1;k<256;k++,t++)
...{
if(BUFFER[k]>=65 && BUFFER[k]<=72)
put_c(BUFFER[k]);
else
put_c(48);
put_c(' ');
if(t==16)
...{
put_c(0x0D);
put_c(0x0A);
t=0;
}
}
put_c(0x0D);
put_c(0x0A);
DelayMs(50000); //延时,好看数据
};
}
//
Makefile,主要的几项,只是针对我这里的程序,要灵活运用哦
MCU = atmega16
F_CPU = 4000000
TARGET = main
SRC = TWI_main.c usart.c // 多文件编译才会用到这一项,可以参考这个帖子:
http: // www.mcublog.com/blog/user1/4266/archives/2006/6145.html
MCU = atmega16
F_CPU = 4000000
TARGET = main
SRC = TWI_main.c usart.c // 多文件编译才会用到这一项,可以参考这个帖子:
http: // www.mcublog.com/blog/user1/4266/archives/2006/6145.html
当然了,看一下协议本身是很重要的,也是一定的.这个例子不错,值得一看.