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