单片机常用的总线如i2c、spi、uart等,将其硬件抽象层分离,相关外设在其抽象层上的应用函数接口调用。这样的好处是 :
1、外设的驱动方便移植到任何单片机;
2、外设总线方便移植到其他单片机,只需更改部分寄存器配置;
3、总线调试OK后,调试新的外设程序时,不用重重复复调试总线的程序。
比如,楼上的程序分层就不够好,i2c总线和eeprom交叉,假如我这个i2c总线要挂其他i2c设备(传感器、ADC等),那又得重新写一次总线程序?又重新配置?又调试时序对不对?做重复的工作就是浪费青春啊!假设换了一家公司,用了不同单片机,这个eeprom程序移植到其他单片机时又是重新写一遍,多吃力啊。
附上24xx系列EEPROM程序,模拟i2c,i2c作了封装,看接口函数即可,
/*
* File : 24clxx.c
*
* This file is part of eeprom driver for 24clxx,such as at24c02/at24c16.
* COPYRIGHT (C) 2014 - 2016,
*
* Change Logs:
* Date Author Notes
*
*
* Depend on:
* i2c_bitops.c
*
* Note:
* 24c04 24c08 24c16 地址为8位,超过0xff地址后,注意页的选择读写
* 24c32 及以上地址为16位
* EEPROM 页读写需要作应答查询或作短暂延时以等待数据写入完成;FM24CLXX则不需要
*/
#define USE_24CLXX_EN 1
#define USE_24CLXX_DEBUG 0
#if USE_24CLXX_EN
#include "main.h"
//eeprom/fram 参数
#define EEPROM_TYPE 0 //0->EEPROM 1->FRAM
#define EEPROM_MODEL 16 //EEPROM 类型 24c16
#define EE24CLXX_SLAVE_ADDR 0x50 //注意读写位,实际地址为0x50
#define EE24CLXX_PageSize 16 //AT24C16每页有16个字节 24C02->8 24c64->32
static void I2C_24CLXXWaitStandby(u8 slave_addr);
//页写延时,FRAM不用
static void I2C_24CLXXWaitStandby(u8 slave_addr)
{
#if EEPROM_TYPE
#else
u16 i;
i = 0xFFFF;
while (i--);
#endif
}
//写多字节,需确保当前写地址+写入数据长度不能超过EEPROM的一页
static void i2c_24clxx_write( u16 WriteAddr, char* pBuffer,u16 NumByteToWrite)
{
struct st_i2c_msg ee24_msg[2];
u8 buf[2];
u8 slave_addr;
//if(WriteAddr+NumByteToWrite > EE24CLXX_PageSize)
// return;
if(EEPROM_MODEL > 16)
{//大于2k字节时,地址为16位
slave_addr = EE24CLXX_SLAVE_ADDR;
buf[0] = (WriteAddr >>8)& 0xff;
buf[1] = WriteAddr & 0xff;
ee24_msg[0].size = 2;
}
else
{
slave_addr = EE24CLXX_SLAVE_ADDR | (WriteAddr>>8);
buf[0] = WriteAddr & 0xff;
ee24_msg[0].size = 1;
}
ee24_msg[0].addr = slave_addr;
ee24_msg[0].flags = ST_I2C_WR;
ee24_msg[0].buff = buf;
ee24_msg[1].addr = slave_addr;
ee24_msg[1].flags = ST_I2C_WR | ST_I2C_NO_START;
ee24_msg[1].buff = (u8*)pBuffer;
ee24_msg[1].size = NumByteToWrite;
i2c_bitops_bus_xfer(&i2c1_dev,ee24_msg,2);
}
/*******************************************************
**
** 外部函数
**
*******************************************************/
//写一字节
char ee_24clxx_writebyte(u16 addr,u8 data)
{
struct st_i2c_msg ee24_msg[2];
u8 buf[3];
u8 slave_addr;
if(EEPROM_MODEL > 16)
{//大于2k字节时,地址为16位
slave_addr = EE24CLXX_SLAVE_ADDR;
buf[0] = (addr >>8)& 0xff; //高位地址在前
buf[1] = addr & 0xff;
buf[2] = data;
ee24_msg[0].size = 3;
}
else
{
slave_addr = EE24CLXX_SLAVE_ADDR | (addr>>8);
buf[0] = addr & 0xff;
buf[1] = data;
ee24_msg[0].size = 2;
}
ee24_msg[0].addr = slave_addr;
ee24_msg[0].flags = ST_I2C_WR;
ee24_msg[0].buff = buf;
i2c_bitops_bus_xfer(&i2c1_dev,ee24_msg,1);
return 0;
}
//写多字节
//新的页写
char ee_24clxx_writebytes(u16 write_addr, char* pwrite_buff, u16 writebytes)
{
u8 write_len,page_offset;
u16 write_remain,write_current_addr;
char error = 0,*pbuff;
pbuff = pwrite_buff; //不要直接使用形参计算,特别是指针操作
write_remain = writebytes;
write_current_addr = write_addr;
while(write_remain > 0)
{
page_offset = write_current_addr & 0x0f; //write_current_addr%EE24CLXX_PageSize
write_len = write_remain > (EE24CLXX_PageSize - page_offset) ? (EE24CLXX_PageSize - page_offset) : write_remain;
i2c_24clxx_write(write_current_addr,pbuff, write_len);
write_remain = write_remain - write_len;
if(write_remain > 0)
{
pbuff = pbuff + EE24CLXX_PageSize - page_offset;
write_current_addr = write_current_addr + EE24CLXX_PageSize - page_offset;
I2C_24CLXXWaitStandby(0);
}
}
I2C_24CLXXWaitStandby(0);
{//校验数据
int i;
char checkdata;
for(i = 0;i < writebytes;i++)
{
ee_24clxx_readbytes(write_addr+i,&checkdata,1);
if(checkdata != pwrite_buff[i])
{
error = 1;
break;
}
}
}
return error;
}
//读多字节,连续读
void ee_24clxx_readbytes(u16 ReadAddr, char* pBuffer, u16 NumByteToRead)
{
struct st_i2c_msg ee24_msg[2];
u8 buf[2];
u8 slave_addr;
if(EEPROM_MODEL > 16)
{//大于2k字节时,地址为16位
slave_addr = EE24CLXX_SLAVE_ADDR;
buf[0] = (ReadAddr >>8)& 0xff;
buf[1] = ReadAddr & 0xff;
ee24_msg[0].size = 2;
}
else
{
slave_addr = EE24CLXX_SLAVE_ADDR | (ReadAddr>>8);
buf[0] = ReadAddr & 0xff;
ee24_msg[0].size = 1;
}
ee24_msg[0].buff = buf;
ee24_msg[0].addr = slave_addr;
ee24_msg[0].flags = ST_I2C_WR;
ee24_msg[1].addr = slave_addr;
ee24_msg[1].flags = ST_I2C_RD;
ee24_msg[1].buff = (u8*)pBuffer;
ee24_msg[1].size = NumByteToRead;
i2c_bitops_bus_xfer(&i2c1_dev,ee24_msg,2);
}
//擦除EEPROM
char ee_24clxx_erasebytes(u16 WriteAddr, char Erasedata, u16 NumByteToErase)
{
char error = 0;
u16 i;
//char *buff;
char buff[2048]; //测试用,实际使用需动态分配内存
//buff = (char*)malloc(2048);
for(i = 0;i < NumByteToErase;i++)
{
buff[i] = Erasedata;
}
error = ee_24clxx_writebytes(WriteAddr,buff,NumByteToErase);
//free(buff);
return error;
}
//eeprom test
#if USE_24CLXX_DEBUG
#define WRSIZE 2048
#define EEP_Firstpage 0
char I2c_Buf_Write[WRSIZE];
char I2c_Buf_Read[WRSIZE];
void EE24CLXX_Test(void)
{
u16 i,error = 0;
for ( i=0; i<WRSIZE; i++ ) //填充缓冲
{
I2c_Buf_Write[i] = i;
}
ee_24clxx_readbytes(EEP_Firstpage, I2c_Buf_Read, WRSIZE);
ee_24clxx_readbytes(EEP_Firstpage, I2c_Buf_Read, WRSIZE);
error = ee_24clxx_writebytes(EEP_Firstpage, I2c_Buf_Write, WRSIZE);
Delayms(5);
ee_24clxx_readbytes(EEP_Firstpage, I2c_Buf_Read, WRSIZE);
for ( i=0; i<WRSIZE; i++ ) //填充缓冲
I2c_Buf_Write[i] = 0;
error = ee_24clxx_writebytes(EEP_Firstpage, I2c_Buf_Write, WRSIZE);
Delayms(5);
ee_24clxx_readbytes(EEP_Firstpage, I2c_Buf_Read,WRSIZE);
//while(error);
}
#endif
#endif
|