TWI(IIC)使用范例AT24C02

 

    这不,看了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 );

 

// 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);
    
//使能接收中断,使能接收,使能发送
}

 

// 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);    //延时,好看数据
    }
;
}

 

// 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

当然了,看一下协议本身是很重要的,也是一定的.这个例子不错,值得一看.

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值