STM32模拟IIC读写24CXX
2018-04-15 来源:eefocus
文件(iic.h):
#define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=0X80000000;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=0X30000000;}
#define IIC_SCL PBout(6)
#define IIC_SDA PBout(7)
#define READ_SDA PBin(7)
void bsp_iic_init(void);
void bsp_iic_start(void);
void bsp_iic_stop(void);
void bsp_iic_sendByte(u8 txd);
u8 bsp_iic_readByte(unsigned char ack);
u8 bsp_iic_waitAck(void);
void bsp_iic_ack(void);
void bsp_iic_nAck(void);
文件(iic.c):
/*
****************************************************************
** brief : IIC 初始化,IO模拟IIC
** note : SDA-PB7,SCL-PB6
****************************************************************
*/
void bsp_iic_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);
}
/*
***************************************************************
** brief : IIC开始信号
** note : SCL为高电平时,SDA由高电平向低电平跳变
***************************************************************
*/
void bsp_iic_Start(void)
{
delay_5us();
SDA_OUT();
IIC_SDA=1;
IIC_SCL=1;
delay_5us();
IIC_SDA=0;
delay_5us();
IIC_SCL=0;
}
/*
***************************************************************
** brief : IIC停止信号
** note : SCL为高电平时,SDA由低电平向高电平跳变
***************************************************************
*/
void bsp_iic_stop(void)
{
delay_5us();
SDA_OUT();
IIC_SCL=0;
IIC_SDA=0;
delay_5us();
IIC_SCL=1;
delay_5us();
IIC_SDA=1;
delay_5us();
}
/*
**********************************************************
** brief : 等待应答信号到来
** ret : 1.接收应答失败
** 0.接收应答成功
*********************************************************
*/
u8 bsp_iic_waitAck(void)
{
u8 ucErrTime=0;
delay_5us();
SDA_IN();
IIC_SDA=1;
delay_5us();
IIC_SCL=1;
delay_5us();
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>100)
{
bsp_iic_stop();
return 1;
}
}
delay_5us();
IIC_SCL=0;
return 0;
}
/* brief : 产生应答信号*/
void bsp_iic_ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_5us();
IIC_SCL=1;
delay_5us();
IIC_SCL=0;
}
/* brief : 产生非应答信号*/
void bsp_iic_nAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_5us();
IIC_SCL=1;
delay_5us();
IIC_SCL=0;
}
/*
*************************************************
** breif : IIC发送一个字节
*************************************************
*/
void bsp_iic_sendByte( u8 txd )
{
u8 t;
delay_5us();
SDA_OUT();
IIC_SCL=0;
for(t=0;t<8;t++)
{
if((txd&0x80)>>7)
IIC_SDA=1;
else
IIC_SDA=0;
txd<<=1;
delay_5us();
IIC_SCL=1;
delay_5us();
IIC_SCL=0;
delay_5us();
}
}
/*
**************************************************************************************
** brief : 读1个字节
** ack :1.发送ACK,0.发送NACK
** note : 主机作为接收方,发送一个非应答信号表示数据传输结束
** 发送一个应答信号表示数据接收成功
** 从机作为接收方,发送一个非应答信号表示数据接受失败
** 发送一个应答信号表示数据接收成功
**************************************************************************************
*/
u8 bsp_iic_readByte(unsigned char ack)
{
unsigned char i,receive=0;
delay_5us();
SDA_IN();
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_5us();
IIC_SCL=1;
receive<<=1;
delay_5us();
if(READ_SDA)receive++;
delay_5us();
}
if (!ack)
bsp_iic_nAck();
else
bsp_iic_ack();
return receive;
}
文件(24cxx.h)
u8 bsp_eeprom_readOneByte(u16 ReadAddr);
void bsp_eeprom_writeOneByte(u16 WriteAddr,u8 DataToWrite);
void bsp_eeprom_writeLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);
u32 bsp_eeprom_readLenByte(u16 ReadAddr,u8 Len);
void bsp_eeprom_write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite);
void bsp_eeprom_read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead);
u8 bsp_eeprom_writePage_zero(u16 WriteAddr, u8 data, u16 NumToWrite);
void bsp_eeprom_init(void);
void bsp_eeprom_reset(void);
文件(24cxx.c)
#define AT24C01 127
#define AT24C02 255
#define AT24C04 511
#define AT24C08 1023
#define AT24C16 2047
#define AT24C32 4095
#define AT24C64 8191
#define AT24C128 16383
#define AT24C256 32767
#define EE_TYPE AT24C256
#define EE_PAGE_SIZE 64
/*
**************************************************************************
** note : 1. EEPROM高四位地址固定为1010
** 2. 主设备在从从设备接收到最后一个字节后发送
** 一个NACK 。接收到NACK 后,从设备释放对SCL和SDA
** 线的控制;主设备就可以发送一个停止/ 重起始
** 条件
** 3. 在EEPROM的一次写循环中可以写多个字节,但一次
** 写入的字节数不能超过EEPROM的页大小
*************************************************************************
*/
void bsp_eeprom_init(void)
{
bsp_eeprom_reset();
delay_ms(1);
}
/* brief : EEPROM复位*/
void bsp_eeprom_reset(void)
{
u32 i;
//OSSchedLock();
bsp_iic_Start();
SDA_OUT();
delay_us(1);
IIC_SDA = 1;
for(i=0; i<9; ++i) {
delay_us(1);
IIC_SCL = 1;
delay_us(1);
IIC_SCL = 0;
}
SDA_IN();
bsp_iic_Start();
IIC_Stop();
//OSSchedUnlock();
}
/*
**********************************************************
** brief : 读数据
** ReadAddr : 待读数据地址
** ret : 读出的数据
**********************************************************
*/
u8 bsp_eeprom_readOneByte(u16 ReadAddr)
{
u8 temp=0;
//OSSchedLock();
bsp_iic_Start();
if(EE_TYPE>AT24C16)
{
bsp_iic_sendByte(0XA0); /*发送写命令*/
bsp_iic_waitAck();
bsp_iic_sendByte(ReadAddr>>8);/*发送高地址*/
} else {
bsp_iic_sendByte(0XA0+((ReadAddr/256)<<1));/*发送器件地址0XA0,写数据*/
}
bsp_iic_waitAck();
bsp_iic_sendByte(ReadAddr%256); /*发送低地址*/
bsp_iic_waitAck();
bsp_iic_Start();
bsp_iic_sendByte(0XA1); /*进入接收模式 */
bsp_iic_waitAck();
temp=bsp_iic_readByte(0);
bsp_iic_stop();
//OSSchedUnlock();
return temp;
}
/*
*******************************************************************
** brief : 指定地址写入数据
** WriteAddr : 待写入地址
** DataToWrite : 待写入数据
*******************************************************************
*/
void bsp_eeprom_writeOneByte(u16 WriteAddr,u8 DataToWrite)
{
//OSSchedLock();
bsp_iic_Start();
if(EE_TYPE>AT24C16)
{
bsp_iic_sendByte(0XA0);
bsp_iic_waitAck();
bsp_iic_sendByte(WriteAddr>>8);
}else
{
bsp_iic_sendByte(0XA0+((WriteAddr/256)<<1));
}
bsp_iic_waitAck();
bsp_iic_sendByte(WriteAddr%256);
bsp_iic_waitAck();
bsp_iic_sendByte(DataToWrite);
bsp_iic_waitAck();
bsp_iic_stop();
//OSSchedUnlock();
/*AT24C256的WriteCycle 最大5ms,所以这个世界绝对不能小于5ms*/
delay_ms(10);
}
/*
***********************************************************************************************
** brief : 指定地址写入指定长度的数据
** WriteAddr : 待写入地址
** datas : 待写入数据缓冲指针
** NumToWrite : 待写入数据长度,调用方需要确保不超过页大小
** ret : 0成功,否者失败
** note : 在用Page 的方式进行离线数据写入时,会出现写入失败的情况.
***********************************************************************************************
*/
static u8 bsp_eeprom_writePage(u16 WriteAddr, u8 *datas, u16 NumToWrite)
{
u8 result;
u8 ret = 0;
u32 i;
//OSSchedLock();
do {
bsp_iic_Start();
if(EE_TYPE>AT24C16)
{
bsp_iic_sendByte(0XA0);
result = bsp_iic_waitAck();
if(0 != result) {
ret = 1;
break;
}
bsp_iic_sendByte(WriteAddr>>8);
} else {
bsp_iic_sendByte(0XA0+((WriteAddr/256)<<1));
}
result = bsp_iic_waitAck();
if(0 != result) {
ret = 1;
break;
}
bsp_iic_sendByte(WriteAddr%256);
result = bsp_iic_waitAck();
if(0 != result) {
ret = 1;
break;
}
i=0;
while(NumToWrite--) { /* 循环发送数据*/
bsp_iic_sendByte(datas[i]);
++i;
result = bsp_iic_waitAck();
if(0 != result) {
ret = 1;
break;
}
}
bsp_iic_stop();
}while(0);
//OSSchedUnlock();
delay_ms(10);
return ret;
}
/*
***********************************************************************************
** brief : 指定地址写入指定长度的数据
** WriteAddr : 待写入地址
** data : 待写入数据
** NumToWrite : 待写入数据长度,调用方需要确保不超过页大小
** ret : 0成功,否者失败
***********************************************************************************
*/
u8 bsp_eeprom_writePage_zero(u16 WriteAddr, u8 data, u16 NumToWrite)
{
u8 result;
u8 ret = 0;
//OSSchedLock();
do {
bsp_iic_Start();
if(EE_TYPE>AT24C16)
{
bsp_iic_sendByte(0XA0);
result = bsp_iic_waitAck();
if(0 != result) {
ret = 1;
break;
}
bsp_iic_sendByte(WriteAddr>>8);
} else {
bsp_iic_sendByte(0XA0+((WriteAddr/256)<<1));
}
result = bsp_iic_waitAck();
if(0 != result) {
ret = 1;
break;
}
bsp_iic_sendByte(WriteAddr%256);
result = bsp_iic_waitAck();
if(0 != result) {
ret = 1;
break;
}
while(NumToWrite--) {
bsp_iic_sendByte(data);
result = bsp_iic_waitAck();
if(0 != result) {
ret = 1;
break;
}
}
bsp_iic_stop();
}while(0);
//OSSchedUnlock();
delay_ms(10);
return ret;
}
/*
************************************************************************
** breif : 指定地址写入指定长度数据
** WriteAddr : 待写入地址
** DataToWrite : 待写入数据
** Len : 待写入数据长度1-4
************************************************************************
*/
void bsp_eeprom_writeLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
{
u8 b[4];
b[0] = (u8)DataToWrite;
b[1] = (u8)(DataToWrite>>8);
b[2] = (u8)(DataToWrite>>16);
b[3] = (u8)(DataToWrite>>24);
bsp_eeprom_write(WriteAddr, b, Len);
}
/*
************************************************************************
** breif : 指定地址开始读出长度为Len的数据
** 该函数用于读出16bit或者32bit的数据.
** ReadAddr : 开始读出的地址
** Len : 要读出数据的长度2,4
** Ret : 读出的16bit或32bit数据
************************************************************************
*/
u32 bsp_eeprom_readLenByte(u16 ReadAddr,u8 Len)
{
u8 buf[4];
u32 temp=0;
u32 v;
if(Len == 0 || Len == 1 || Len == 3|| (Len > 4)) {
return 0;
}
bsp_eeprom_read(ReadAddr, buf, Len);
if(Len == 2) {
v = buf[1];
temp |= (v << 8);
v = buf[0];
temp |= v;
}
if(Len == 4) {
v = buf[3];
temp |= (v << 24);
v = buf[2];
temp |= (v << 16);
v = buf[1];
temp |= (v << 8);
v = buf[0];
temp |= v;
}
return temp;
}
/*
*****************************************************************************
** breif : 指定地址读出指定长度的数据
** ReadAddr : 待读取地址
** buf : 待读出数据的缓冲指针
** numToRead : 待读出的数据长度
*****************************************************************************
*/
void bsp_eeprom_read(u16 ReadAddr,u8 *buf,u16 numToRead)
{
u8 temp=0;
if(numToRead == 0) {
return ;
}
//OSSchedLock();
bsp_iic_Start();
if(EE_TYPE>AT24C16)
{
bsp_iic_sendByte(0XA0);
bsp_iic_waitAck();
bsp_iic_sendByte(ReadAddr>>8);
} else {
bsp_iic_sendByte(0XA0+((ReadAddr/256)<<1));
}
bsp_iic_waitAck();
bsp_iic_sendByte(ReadAddr%256);
bsp_iic_waitAck();
bsp_iic_Start();
bsp_iic_sendByte(0XA1);
bsp_iic_waitAck();
while(numToRead-1) {
temp = bsp_iic_readByte(1);
*buf = temp;
++buf;
--numToRead;
}
temp = bsp_iic_readByte(0);
*buf = temp;
bsp_iic_stop();
//OSSchedUnlock();
}
/*
*****************************************************************************
** brief : 将缓冲区的数据写到EEPROM 中
** WriteAddr : 待写入地址
** pBuffer : 数据缓冲区指针
** NumToWrite : 待写入字节数
** note : http://wenku.baidu.com/view/4ffb2eb21a37f111f1855ba7.html
*****************************************************************************
*/
void bsp_eeprom_write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
u16 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;
Addr = WriteAddr % EE_PAGE_SIZE; /* 写入地址是不是64的整数倍*/
count = EE_PAGE_SIZE - Addr; /* 距离下一页页首的距离*/
NumOfPage = NumToWrite / EE_PAGE_SIZE; /* 一共多少页*/
NumOfSingle = NumToWrite % EE_PAGE_SIZE; /* 不够一页的数据余量*/
if(Addr == 0) /* 首页地址*/
{
if(NumOfPage == 0) /* 不足一页数据*/
{
bsp_eeprom_writePage(WriteAddr, pBuffer, NumOfSingle);
}else /* 数据超过一页*/
{
while(NumOfPage -- )
{
bsp_eeprom_writePage(WriteAddr, pBuffer, EE_PAGE_SIZE);
pBuffer += EE_PAGE_SIZE;
WriteAddr += EE_PAGE_SIZE;
}
if(NumOfSingle > 0)
{
bsp_eeprom_writePage(WriteAddr, pBuffer, NumOfSingle);
}
}
} else /* 非首页地址*/
{
if(NumOfPage == 0) /* 不足一页数据*/
{
bsp_eeprom_writePage(WriteAddr, pBuffer, NumOfSingle);
} else /* 数据超过一页*/
{
NumToWrite -= count;
NumOfPage = NumToWrite / EE_PAGE_SIZE; /* 剩余的页数*/
NumOfSingle = NumToWrite % EE_PAGE_SIZE; /* 不足一页的数据*/
if(count > 0)
{
bsp_eeprom_writePage(WriteAddr, pBuffer, count);
pBuffer += count;
WriteAddr += count;
}
while(NumOfPage -- )
{
bsp_eeprom_writePage(WriteAddr, pBuffer, EE_PAGE_SIZE);
pBuffer += EE_PAGE_SIZE;
WriteAddr += EE_PAGE_SIZE;
}
if(NumOfSingle > 0)
{
bsp_eeprom_writePage(WriteAddr, pBuffer, NumOfSingle);
}
}
}
}