AVR单片机I/O口模拟I2C总线操作AT24CXX的通用程序
来源: 芯艺工作室
本站广告
  ENC28J60+uIP DHCP动态获取IP及UDP广播服务端示例
  ENC28J60+uIP 基于TCP的MODBUS IO示例
  C720H5上实现MODBUS远程IO
  ENC28J60+uIP HTTP GET方式将温度数据上传到web服务器
  FM1702读写S50(也叫M1)卡相关资料

以下为I/O模拟I2C接口函数实现:
twi.h:

//twi.h
#ifndef TWI_H
#define TWI_H

void TwiInit(void);
uint8_t TwiStart(void);
void TwiStop(void);
uint8_t TwiWriteByte(uint8_t c);
//读一字节 ack: 1时发TW_ACK,0时发TW_NOACK
uint8_t TwiReadByte(uint8_t *c, uint8_t ack);

//当I/O口模拟时此值为1
//当硬件 TWI接口时此值为0x18,即发送SLA+W后接收到ACK状态
#define NO_BUSY 0x1

#endif

#endif

twi.c:

/********************************
  AVR单片机I/O口模拟I2C操作程序 
  文件名:twi.c
  编译:WinAVR-20070122

  硬件:CA-M8X  
      配置:外部4MHz
      打开:S7(1,2,3) - EEPROM连接
            S6(1,2)   - 4MHz晶振连接
            S5(5,6)   - UART连接
      注:PC5模拟SCL,PC4模拟SDA,PC3连接写保护引脚
  
  芯艺设计室 2004-2007  版权所有 
  转载请保留本注释在内的全部内容
  WEB: http://www.chipart.cn
  Email: changfutong@sina.com
*******************************/

#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>

/*注:
AVR单片机I/O口模拟I2C总线时建议在外部连接上拉电阻
这样可通过改变I/O口输入输出方向的方式来设置高低
输出口保持不变(0)
此时如DDRX寄存器为1则变成输出0
若DDRX为0,则I/O口程高阻,但因外部的上拉电阻,总线相当于设置高
即通过设置DDRX的方式控制总线的高低
*/
#define SET_SCL DDRC&=~_BV(PC5)
#define CLR_SCL DDRC|=_BV(PC5)
#define SET_SDA DDRC&=~_BV(PC4)
#define CLR_SDA DDRC|=_BV(PC4)

#define SDA_PIN  (PINC&_BV(PC4))

static void twi_delay_bus(void)
{
  _delay_loop_2(10);
}

static void twi_ack(uint8_t ack)
{
  if(!ack)  //非应答
    SET_SDA;            
  else    //应答
    CLR_SDA;
  twi_delay_bus();
  SET_SCL;
  twi_delay_bus();                  
  CLR_SCL;                     
  twi_delay_bus();
}

/*********以下为外部可调用的接口函数***********/
//初始化本模块
void TwiInit(void)
{
  PORTC&=~(_BV(PC5)|_BV(PC4));
  DDRC&=~(_BV(PC5)|_BV(PC4));
}
//产生启动信号
uint8_t TwiStart(void)
{
  twi_delay_bus();
  SET_SDA;       
  twi_delay_bus();  
  SET_SCL;
  twi_delay_bus();
  CLR_SDA;           
  twi_delay_bus();
  CLR_SCL;         
  twi_delay_bus();  
  return 1;
}
//产生停止信号
void TwiStop(void)
{
  twi_delay_bus();
  CLR_SDA;    
  twi_delay_bus();  
  SET_SCL;
  twi_delay_bus();
  SET_SDA;    
  twi_delay_bus();  
}

//向总线写一字节,并返回有无应答
uint8_t TwiWriteByte(uint8_t c)
{
  uint8_t i,ack;

  for(i=0;i<8;i++)  
  {
    if(c&0x80)
      SET_SDA;   
    else  
      CLR_SDA;                
    SET_SCL;       
    twi_delay_bus();        
    CLR_SCL; 
    c<<=1;
    twi_delay_bus();
  }
  twi_delay_bus();

  SET_SDA;                
  twi_delay_bus();
  SET_SCL;
  twi_delay_bus();
  if(SDA_PIN)
    ack=0;     //失败
  else 
    ack=1;        
  CLR_SCL;
  twi_delay_bus();
  return ack;  
}

//读一字节 ack: 1时应答,0时不应答
uint8_t TwiReadByte(uint8_t *c, uint8_t ack)
{
  uint8_t i,ret;

  ret=0; 
  SET_SDA;
  for(i=0;i<8;i++)
  {
    twi_delay_bus();          
    CLR_SCL;                  
    twi_delay_bus();
    SET_SCL;                
    twi_delay_bus();
    ret<<=1;
    if(SDA_PIN)
      ret++;  
  }
  CLR_SCL;    
  twi_delay_bus();
  twi_ack(ack);
  *c=ret;
  return(ret);  
}


以下为AT24CXX的操作函数实现:
at24cxx.h:

//AT24CXX.H
#ifndef AT24CXX_H
#define AT24CXX-H

void At24cxxWaitBusy(void);
void At24cxxConfig(uint8_t device_addr,uint8_t page_size);
void At24cxxWriteByte(uint16_t addr,uint8_t dat);
uint8_t At24cxxReadByte(uint16_t addr);
void At24cxxWritePage(uint16_t page_index,uint8_t *buf);
void At24cxxReadPage(uint16_t page_index,uint8_t *buf);

#endif

at24cxx.c:

/********************************
  通用AT24CXX操作接口程序 
  本程序适合于AT24C32/64,AT24C128/256等器件
  文件名:at.c
  编译:WinAVR-20070122

  硬件:CA-M8X  
  注:本程序需要I/O模拟或硬件实现的I2C总线接口函数
  
  芯艺设计室 2004-2007  版权所有 
  转载请保留本注释在内的全部内容
  WEB: http://www.chipart.cn
  Email: changfutong@sina.com
*******************************/
#include <avr/io.h>
#include <stdint.h>

#include "twi.h" //i2c接口函数声明处

#define TW_WRITE 0
#define TW_READ  1

#define TW_ACK 1
#define TW_NOACK 0

/*以下两个宏控制AT24CXX的WP引脚,如未连接可定义为空:
  #define EEPROM_WRITE_ENABLE  
  #define EEPROM_WRITE_DISABLE 
  AT24CXX中WP引脚接地时写允许,接电源(高)时写保护,
  如不接,器件内部有接地电阻,即写允许.  */
//在CA-M8X板上该引脚通过S7(3)连接MEGA8的PC3
#define EEPROM_WRITE_ENABLE  PORTC&=~_BV(PC3),DDRC|=_BV(PC3)
#define EEPROM_WRITE_DISABLE PORTC|=_BV(PC3),DDRC|=_BV(PC3)

static uint8_t g_PageSize=0;//页大小(按字节)
static uint8_t g_DeviceAddr=0;//器件地址
static uint8_t g_PageBitCount;//一页所占用的位数(如一页为64字节,则6)

/*器件忙检测,原理:器件忙时不会对主机的写操作应答*/
//忙检测接口函数,只有一种情况才需要调用这个函数
//即:当刚写完成,要读数据时
//而连续的读或者写操作之间不需要调用这个函数
void At24cxxWaitBusy(void)
{
  uint8_t i;
  //检测EEPROM是否忙
  while(1)
  {
    TwiStart();
    i=TwiWriteByte(g_DeviceAddr);
    TwiStop();
    if(i==NO_BUSY)
      break;
  }  
  return ;
}

/* 设置当前操作器件的地址和页大小
device_addr最低位必须为0
只有在使用页访问器件时page_size有用
不使用页访问时可指定page_size为0 */
void At24cxxConfig(uint8_t device_addr,uint8_t page_size)
{
  uint8_t i;
  
  g_DeviceAddr=device_addr;
  g_PageSize=page_size;
  g_PageBitCount=0;
  
  if(page_size==0)
    return ;
  //计算一页所占用位数
  for(i=1;i<10;i++)//不能大于9次
  {
    if(page_size==(1<<i))
    {
      g_PageBitCount=i;
      break;
    }//if
  }//for
}

//AT24CXX通用随机写一字节函数
void At24cxxWriteByte(uint16_t addr,uint8_t dat)
{
  At24cxxWaitBusy();
  EEPROM_WRITE_ENABLE;
  TwiStart();
  TwiWriteByte(g_DeviceAddr );//= |TW_WRITE
  TwiWriteByte(addr>>8);//写地址高字节
  TwiWriteByte(addr);//写地址低字节  
  TwiWriteByte(dat);//写数据字节
  TwiStop();
  EEPROM_WRITE_DISABLE;
}

//AT24CXX通用随机读一字节函数
uint8_t At24cxxReadByte(uint16_t addr)
{
  uint8_t ret;
  
  TwiStart();
  TwiWriteByte(g_DeviceAddr);//写地址
  TwiWriteByte(addr>>8);
  TwiWriteByte(addr);
  
  TwiStart();
  TwiWriteByte(g_DeviceAddr | TW_READ);
  TwiReadByte(&ret,TW_NOACK);//NO ACK
  TwiStop();
  
  return ret;
}

//AT24CXX通用写页函数,page_index为页地址,即表示第几页
void At24cxxWritePage(uint16_t page_index,uint8_t *buf)
{
  uint8_t i;  
  
  //页索引调整到绝对地址
  page_index<<=g_PageBitCount;
  
  //检测EEPROM是否忙
  At24cxxWaitBusy();
  
  //写一页
  EEPROM_WRITE_ENABLE;
  TwiStart();
  TwiWriteByte(g_DeviceAddr );//= |TW_WRITE
  TwiWriteByte(page_index>>8);//写地址高字节
  TwiWriteByte(page_index);//写地址低字节
  
  for(i=0;i<g_PageSize;i++)
    TwiWriteByte(buf[i]);
    
  TwiStop();
  EEPROM_WRITE_DISABLE;
}

//AT24CXX通用读页函数,page_index为页地址,即表示第几页
void At24cxxReadPage(uint16_t page_index,uint8_t *buf)
{
  uint8_t i;
  
  //页索引调整到绝对地址
  page_index<<=g_PageBitCount;
  
  TwiStart();
  TwiWriteByte(g_DeviceAddr);//写地址
  TwiWriteByte(page_index>>8);
  TwiWriteByte(page_index);
  
  TwiStart();
  TwiWriteByte(g_DeviceAddr | TW_READ);
  for(i=0;i<g_PageSize-1;i++)
    TwiReadByte(&buf[i],TW_ACK);

  TwiReadByte(&buf[i],TW_NOACK);//最后一字节不应答
  TwiStop();
}


测试部分:
uart.c:

/****************************************
文件名:uart.c
****************************************/
#include <avr/io.h>
#include <stdio.h>

static int uart_putchar(char c, FILE *stream);

static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

static int uart_putchar(char c, FILE *stream)
{
  if (c == '\n')
    uart_putchar('\r', stream);
  loop_until_bit_is_set(UCSRA, UDRE);
  UDR = c;
  return 0;
}

void StdIoInit(void)
{
  UCSRB=0;
  UBRRH=0;
  UBRRL=25;      //9600   

   UCSRB=_BV(TXEN);  
  stdout = &mystdout;
  stderr = &mystdout;
  printf("Uart初始化完成!\n");
}

test.c:

/********************************
  AVR单片机I/O口模拟I2C总线操作AT24CXX的通用程序
  文件名:test.c
  编译:WinAVR-20070122

  硬件:CA-M8X  
      配置:外部4MHz
      打开:S7(1,2,3) - EEPROM连接
            S6(1,2)   - 4MHz晶振连接
            S5(5,6)   - UART连接
      注:PC5模拟SCL,PC4模拟SDA,PC3连接写保护引脚
  
  芯艺设计室 2004-2007  版权所有 
  转载请保留本注释在内的全部内容
  WEB: http://www.chipart.cn
  Email: changfutong@sina.com
*******************************/
#include<avr/io.h>
#include<stdint.h>
#include<stdio.h>

#include "twi.h"
#include "at24cxx.h"

#define AT24C256_PAGE_SIZE 64    //AT24C256页大小
#define AT24C32_PAGE_SIZE 32    //AT24C32页大小
#define MAX_PAGE_SIZE  64      //最大可能用到的缓冲,在定义缓冲时使用

#define AT24C256A_ADDR 0xA0  //CAM8X第一片AT24C256芯片地址
#define AT24C256B_ADDR 0xA2  //CAM8X第二片AT24C256芯片地址(CAM8X标准配置没有焊接这块存储器芯片)
#define AT24C32_ADDR    0xA4 //AT24C32芯片地址

static uint8_t g_PageBuffer[MAX_PAGE_SIZE];//页数据的缓冲

void StdIoInit(void);//uart.c中实现,调试用

//测试AT24C256
void test256(void)
{
  uint8_t i;

  printf("test at24c256:\n");
  At24cxxConfig(AT24C256A_ADDR,AT24C256_PAGE_SIZE);
  
  //测试随机读/写字节
  At24cxxWriteByte(333,33);
  At24cxxWaitBusy();
  i=At24cxxReadByte(333);
  printf("ReadByte:%d\n",i);
  
  //测试随机读/写页
  for(i=0;i<AT24C256_PAGE_SIZE;i++)
    g_PageBuffer[i]=i+2;
  At24cxxWritePage(3,g_PageBuffer);

  At24cxxWaitBusy();

  At24cxxReadPage(3,g_PageBuffer);
  printf("Page:\n");
  for(i=0;i<AT24C256_PAGE_SIZE;i++)
  {
    if((i+1)%10==0)
      printf("%d\n",g_PageBuffer[i]);
    else
      printf("%d ",g_PageBuffer[i]);
  }
  printf("\n");
}

//测试at24c32
void test32(void)
{
  uint8_t i;
  printf("test at24c32:\n");
  At24cxxConfig(AT24C32_ADDR,AT24C32_PAGE_SIZE);
  
  //测试随机读/写字节
  At24cxxWriteByte(200,170);
  At24cxxWaitBusy();
  i=At24cxxReadByte(200);
  printf("ReadByte:%d\n",i);
  
  //测试随机读/写页
  for(i=0;i<AT24C32_PAGE_SIZE;i++)
    g_PageBuffer[i]=i+33;
  At24cxxWritePage(6,g_PageBuffer);

  At24cxxWaitBusy();

  At24cxxReadPage(6,g_PageBuffer);
  printf("Page:\n");
  for(i=0;i<AT24C32_PAGE_SIZE;i++)
  {
    if((i+1)%10==0)
      printf("%d\n",g_PageBuffer[i]);
    else
      printf("%d ",g_PageBuffer[i]);
  }
  printf("\n");  
}


int main(void)
{
  StdIoInit();//uart打印输出初始化
  TwiInit();  //TWI口初始化
  
  test256();
  test32();
  
  while(1);
}


测试结果:




声明:芯艺工作室(http://www.chipart.cn)保留所有权力,转载请注明出处!

芯艺工作室    蒙ICP备06005492号

Copyright© 2004-2020 ChipArt Studio All Rights Reserved