1、I2C驱动程序
/*****************************************************************************
FileName : i2c.c
Function : I2C协议驱动
Author : mike
Email : [email protected]
Version : V1.0
Date : 2019-10-19
Note :
*****************************************************************************/
#include "include.h"
#include "i2c.h"
void iic_delay(void)
{
_nop_();
_nop_();
}
void iic_init(void) //iic初始化---释放总线
{
IIC_DATA_HIGH();
IIC_CLK_HIGH();
}
void iic_start(void) //IIC开始---在SCL高电平时SDA有一个下降沿
{
IIC_DATA_HIGH();
iic_delay();
IIC_CLK_HIGH();
iic_delay();
IIC_DATA_LOW();
iic_delay();
}
void iic_stop(void) //IIC停止---在SCL高电平时SDA有一个上升沿
{
IIC_DATA_LOW();
iic_delay();
IIC_CLK_HIGH();
iic_delay();
IIC_DATA_HIGH();
iic_delay();
}
void iic_response(void) //响应---等待返回一个sda=0或者i>=250,默认为响应
{
u8 retry = 0;
IIC_CLK_HIGH();
iic_delay();
IIC_DATA_OUT();
while(IIC_DATA_IS_HIGH() && retry++ < 250)
{
iic_delay();
}
IIC_CLK_LOW();
iic_delay();
}
void iic_write_byte(u8 dat) //iic写一字节
{
u8 i;
IIC_CLK_LOW();
iic_delay();
IIC_DATA_OUT();
for(i=8;i!=0;i--)
{
if (dat & BIT(7))
IIC_DATA_HIGH();
else
IIC_DATA_LOW();
dat <<= 1; //此句必须放在后面
iic_delay();
IIC_CLK_HIGH();
iic_delay();
IIC_CLK_LOW();
iic_delay();
}
IIC_DATA_HIGH();
iic_delay();
}
u8 iic_read_byte(void) //iic读一字节
{
u8 i;
u8 temp;
IIC_CLK_LOW();
iic_delay();
IIC_DATA_HIGH();
iic_delay();
IIC_DATA_IN();
for(i=8;i!=0;i--)
{
IIC_CLK_HIGH();
iic_delay();
temp <<= 1; //此句必须放前面
if(IIC_DATA_IS_HIGH())
temp |= BIT(0);
IIC_CLK_LOW();
iic_delay();
}
IIC_DATA_HIGH();
iic_delay();
return temp;
}
/*****************************************************************************
FileName : i2c.h
Function : I2C协议驱动
Author : mike
Email : [email protected]
Version : V1.0
Date : 2019-10-19
Note :
*****************************************************************************/
#ifndef I2C_H
#define I2C_H
#define BIT(n) (1<<(n))
//sbit IIC_SCL = P2^0;
//sbit IIC_SDA = P2^1;
#define IIC_DATA 1 //P21
#define IIC_CLK 0 //P20
#define IIC_DATA_CLK_OUT() //DATA和CLK设为输出
#define IIC_DATA_IN() //DATA设为输入
#define IIC_DATA_OUT() //DATA设为输出
#define IIC_DATA_CLK_HIGH() //DATA和CLK输出高
#define IIC_DATA_HIGH() P2 |= BIT(IIC_DATA) //DATA输出高
#define IIC_DATA_LOW() P2 &= ~BIT(IIC_DATA)//DATA输出低
#define IIC_CLK_HIGH() P2 |= BIT(IIC_CLK) //CLK输出高
#define IIC_CLK_LOW() P2 &= ~BIT(IIC_CLK) //CLK输出低
#define IIC_DATA_IS_HIGH() (P2 & BIT(IIC_DATA))//判断DATA是否为高
void iic_init(void);
void iic_start(void);
void iic_stop();
void iic_response(void);
void iic_write_byte(u8 dat);
u8 iic_read_byte(void);
#endif
2、24CXX驱动程序
/*****************************************************************************
FileName : eeprom_24cxx.c
Function : 24CXX eeprom驱动程序
Author : mike
Email : [email protected]
Version : V1.0
Date : 2019-10-19
Note :
器件 总容量(bit) 总页数 byte/页 字地址长度
24C02 2K 32 8 8位
24C04 4K 32 16 9位
24C08 8K 64 16 10位
24C16 16K 128 16 11位
24C32 32K 128 32 12位
24C64 64K 256 32 13位
1byte = 8bit
注意:24C16以下空间的大于8位后的寻址高位地址在片选地址中选择,详细看芯片手册.
另外要注意的就是字节页,一次连续写入的数据量不能超过一页的数据量.有些老款的
芯片甚至不支持跨页写入.为了适用也参照不跨页写入的方法写这个程序.而读取数据
没有这个限制,只要单片机开辟的缓存够大,可以一直连续读下去.
eeprom有写入寿命,一般寿命10万次,避免频繁擦写
*****************************************************************************/
#include "include.h"
#include "eeprom_24cxx.h"
void eeprom_write_byte(u16 addr,u8 dat) //eeprom写一字节---写如果写超出范围,会从地址0开始覆盖
{
iic_write_byte(EEPROM_24CXX_SLAVE_ADDR,addr,dat);
delay_ms(5); //24cxx写一字节后必须延时
}
u8 eeprom_read_byte(u16 addr) //eeprom读一字节
{
u8 temp;
temp = iic_read_byte(EEPROM_24CXX_SLAVE_ADDR,addr);
return temp;
}
void eeprom_write_nbyte(u16 addr,u8 *pbuf,u8 len) //eeprom写N字节
{
#if 1
while(len--)
{
iic_write_byte(EEPROM_24CXX_SLAVE_ADDR,addr++,*pbuf++);
delay_ms(5); //24cxx写一字节后必须延时
}
#else
while(len)
{
iic_write_byte(EEPROM_24CXX_SLAVE_ADDR,addr,*pbuf);
addr ++;
pbuf ++;
len --;
}
#endif
}
void eeprom_read_nbyte(u16 addr,u8 *pbuf,u8 len) //eeprom读N字节
{
#if 1
while(len--)
{
*pbuf++ = iic_read_byte(EEPROM_24CXX_SLAVE_ADDR,addr ++);
}
#else
while(len)
{
*pbuf = iic_read_byte(EEPROM_24CXX_SLAVE_ADDR,addr);
pbuf ++;
addr ++;
len --;
}
#endif
}
void eeprom_write_multi(u16 addr,u8 *pbuf,u8 len) //eeprom写N字节---支持跨页
{
u8 page, cnt;
/*
if(num > EE_SIZE) //如果超过eeprom最大容量地址
{
return 0;
}
*/
cnt = (addr%PAGE_SIZE >len)? len : addr%PAGE_SIZE;
if(cnt)
{
eeprom_write_nbyte(addr, pbuf, cnt);
delay_ms(2); //跨页时要延时2ms
addr += cnt;
pbuf += cnt;
len -= cnt;
}
page = len/PAGE_SIZE;
cnt = len%PAGE_SIZE;
while(page--)
{
eeprom_write_nbyte(addr, pbuf, PAGE_SIZE);
delay_ms(2); //跨页时要延时2ms
addr += PAGE_SIZE;
pbuf += PAGE_SIZE;
}
if(cnt)
{
eeprom_write_nbyte(addr, pbuf, cnt);
delay_ms(2); //写完要延时2ms
}
}
void eeprom_read_multi(u16 addr,u8 *value,u8 len) //eeprom读N字节---
{
u8 i;
for(i = 0; i < len; i++)
{
*(value + i) = eeprom_read_byte(addr + i);
}
}
void eeprom_24cxx_init(void) //返回0表示eeprom初始化成功
{
u8 temp = 0;
u8 det_flag = 0;
iic_init(EEPROM_24CXX_SLAVE_ADDR,EEPROM_24CXX_SPEED); //初始化IIC
temp = eeprom_read_byte(EE_ADDR_MAX); //读取是否曾经写入0xAA---开机时不用每次都写入
//printf("temp = 0x%02X\n",temp);
if(temp != 0xAB) //失败---或者第一次开机
{
eeprom_write_byte(EE_ADDR_MAX,0xAB); //再次写入固定值0xAA
temp = eeprom_read_byte(EE_ADDR_MAX); //再检测写入0xAA是否成功
if(temp != 0xAB) //读取失败---器件有问题
{
det_flag = 1; //返回1
}
}
#if DEBUG_LOG_EN
if(!det_flag)
{
printf("24cxx eeprom init ok\n");//检测成功
}
else
{
printf("24cxx eeprom init error\n");//检测失败
}
#endif
}
/*****************************************************************************
FileName : eeprom_24cxx.h
Function : 24CXX eeprom驱动程序
Author : mike
Email : [email protected]
Version : V1.0
Date : 2019-10-19
Note :
*****************************************************************************/
#ifndef __EEPROM_24CXX_H
#define __EEPROM_24CXX_H
/************************************************************
01 -> 24C01; 02 -> 24C02; 04 -> 24C04; 08 -> 24C08;
16 -> 24C16; 32 -> 24C32; 64 -> 24C64; 128 -> 24C128;
256-> 24C256; 512 -> 24C512;
************************************************************/
#define EE_24C01_SIZE 0x007F//127
#define EE_24C02_SIZE 0x00FF//255 //32页,每页8byte
#define EE_24C04_SIZE 0x01FF//511
#define EE_24C08_SIZE 0x03FF//1023 //64页,每页16byte
#define EE_24C16_SIZE 0x07FF//2047 //128页,每页16byte
#define EE_24C32_SIZE 0x0FFF//4095
#define EE_24C64_SIZE 0x1FFF//8191
#define EE_24C128_SIZE 0x3FFF//16383
#define EE_24C256_SIZE 0x7FFF//32767
#define EE_24C512_SIZE 0xFFFF//65535
#define EE_24C01 1 //127
#define EE_24C02 2 //255(32页,每页8byte)
#define EE_24C04 4
#define EE_24C08 8 //1023(64页,每页16byte)
#define EE_24C16 16 //2047
#define EE_24C32 32 //4095
#define EE_24C64 64 //8191
#define EE_24C128 128 //16383
#define EE_24C256 256 //32767
#define EE_24C512 512 //65535
#define EEPROM_TYPE EE_24C02
#define EE_ADDR_MAX EE_24C02
#define EEPROM_24CXX_ID_FLAG 0x55 //24CXX第一次开机标志记忆地址
#if EEPROM_TYPE == EE_24C01
#define PAGE_SIZE 8 //字节/页
#define EE_SIZE 0x007F //eeprom的总容量
#elif EEPROM_TYPE == EE_24C02
#define PAGE_SIZE 16
#define EE_SIZE 0x00FF
#elif EEPROM_TYPE == EE_24C04
#define PAGE_SIZE 16
#define EE_SIZE 0x01FF
#elif EEPROM_TYPE == EE_24C08
#define PAGE_SIZE 16
#define EE_SIZE 0x03FF
#elif EEPROM_TYPE == EE_24C16
#define PAGE_SIZE 16
#define EE_SIZE 0x07FF
#elif EEPROM_TYPE == EE_24C32
#define PAGE_SIZE 32
#define EE_SIZE 0x0FFF
#elif EEPROM_TYPE == EE_24C64
#define PAGE_SIZE 32
#define EE_SIZE 0x1FFF
#elif EEPROM_TYPE == EE_24C128
#define PAGE_SIZE 64
#define EE_SIZE 0x3FFF
#elif EEPROM_TYPE == EE_24C256
#define PAGE_SIZE 64
#define EE_SIZE 0x7FFF
#elif EEPROM_TYPE == EE_24C512
#define PAGE_SIZE 128
#define EE_SIZE 0xFFFF
#endif
#define EEPROM_24CXX_SLAVE_ADDR 0xA0
#define EEPROM_24CXX_SPEED 100000
void eeprom_write_byte(u16 addr,u8 dat);
u8 eeprom_read_byte(u16 addr);
void eeprom_write_nbyte(u16 addr,u8 *pbuf,u8 len);
void eeprom_read_nbyte(u16 addr,u8 *pbuf,u8 len);
void eeprom_write_multi(u16 addr,u8 *pbuf,u8 len);
void eeprom_read_multi(u16 addr,u8 *value,u8 len);
void eeprom_24cxx_init(void);
#endif