425 lines
8.3 KiB
C
425 lines
8.3 KiB
C
#include "eeprom.h"
|
||
|
||
uint8_t ee_warray[20] = {0};//eeprom写入数组
|
||
uint8_t ee_rarray[20] = {0};//eeprom读取数组
|
||
|
||
void ee_io_init(void)
|
||
{
|
||
|
||
GPIO_InitTypeDef GPIO_InitStruct;
|
||
|
||
/* GPIO Ports Clock Enable */
|
||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||
/*Configure GPIO pin Output Level */
|
||
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
|
||
|
||
/*Configure GPIO pins : PB6 PB9 */
|
||
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7; //PB6 PB7
|
||
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; //开漏输出
|
||
GPIO_InitStruct.Pull = GPIO_NOPULL; //上下拉模式配置为既不上拉也不下拉
|
||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//IO口速度配置
|
||
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); //初始化
|
||
}
|
||
|
||
static void i2c_delay(void)
|
||
{
|
||
uint8_t i;
|
||
for (i = 0; i < 40; i++);
|
||
}
|
||
void i2c_start(void)
|
||
{
|
||
// 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号
|
||
|
||
I2C_SDA_1();
|
||
I2C_SCL_1();
|
||
i2c_delay();
|
||
I2C_SDA_0();
|
||
i2c_delay();
|
||
|
||
I2C_SCL_0();
|
||
i2c_delay();
|
||
}
|
||
|
||
void i2c_stop(void)
|
||
{
|
||
// 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号
|
||
I2C_SDA_0();
|
||
I2C_SCL_1();
|
||
i2c_delay();
|
||
I2C_SDA_1();
|
||
i2c_delay();
|
||
}
|
||
void i2c_sendbyte(uint8_t _ucByte)
|
||
{
|
||
uint8_t i;
|
||
|
||
// 先发送字节的高位bit7
|
||
for (i = 0; i < 8; i++)
|
||
{
|
||
if (_ucByte & 0x80)
|
||
{
|
||
I2C_SDA_1();
|
||
}
|
||
else
|
||
{
|
||
I2C_SDA_0();
|
||
}
|
||
i2c_delay();
|
||
I2C_SCL_1();
|
||
i2c_delay();
|
||
I2C_SCL_0();
|
||
if (i == 7)
|
||
{
|
||
I2C_SDA_1(); // 释放总线
|
||
}
|
||
_ucByte <<= 1; // 左移一个bit
|
||
i2c_delay();
|
||
}
|
||
}
|
||
uint8_t i2c_readbyte(void)
|
||
{
|
||
uint8_t i;
|
||
uint8_t value;
|
||
|
||
/* 读到第1个bit为数据的bit7 */
|
||
value = 0;
|
||
for (i = 0; i < 8; i++)
|
||
{
|
||
value <<= 1;
|
||
I2C_SCL_1();
|
||
i2c_delay();
|
||
if (I2C_SDA_READ())
|
||
{
|
||
value++;
|
||
}
|
||
I2C_SCL_0();
|
||
i2c_delay();
|
||
}
|
||
return value;
|
||
}
|
||
|
||
|
||
uint8_t i2c_waitack(void)
|
||
{
|
||
uint8_t re;
|
||
|
||
I2C_SDA_1(); /* CPU释放SDA总线 */
|
||
// i2c_Delay();
|
||
I2C_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
|
||
i2c_delay();
|
||
|
||
if (I2C_SDA_READ()) /* CPU读取SDA口线状态 */
|
||
{
|
||
re = 1;
|
||
}
|
||
else
|
||
{
|
||
re = 0;
|
||
}
|
||
|
||
I2C_SCL_0();
|
||
i2c_delay();
|
||
return re;
|
||
}
|
||
|
||
void i2c_ack(void)
|
||
{
|
||
I2C_SDA_0(); /* CPU驱动SDA = 0 */
|
||
i2c_delay();
|
||
I2C_SCL_1(); /* CPU产生1个时钟 */
|
||
i2c_delay();
|
||
I2C_SCL_0();
|
||
i2c_delay();
|
||
I2C_SDA_1(); /* CPU释放SDA总线 */
|
||
}
|
||
|
||
void i2c_nack(void)
|
||
{
|
||
I2C_SDA_1(); /* CPU驱动SDA = 1 */
|
||
i2c_delay();
|
||
I2C_SCL_1(); /* CPU产生1个时钟 */
|
||
i2c_delay();
|
||
I2C_SCL_0();
|
||
i2c_delay();
|
||
}
|
||
|
||
uint8_t i2c_checkdevice(uint8_t _Address)
|
||
{
|
||
uint8_t ucAck;
|
||
|
||
if (I2C_SDA_READ() && I2C_SCL_READ())
|
||
{
|
||
i2c_start(); /* 发送启动信号 */
|
||
|
||
/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
|
||
i2c_sendbyte(_Address | I2C_WR);
|
||
ucAck = i2c_waitack(); /* 检测设备的ACK应答 */
|
||
|
||
i2c_stop(); /* 发送停止信号 */
|
||
|
||
return ucAck;
|
||
}
|
||
return 1; /* I2C总线异常 */
|
||
}
|
||
|
||
uint8_t ee_checkok(void)
|
||
{
|
||
if (i2c_checkdevice(EE_DEV_ADDR) == 0)
|
||
{
|
||
return 1;
|
||
}
|
||
else
|
||
{
|
||
/* 失败后,切记发送I2C总线停止信号 */
|
||
i2c_stop();
|
||
return 0;
|
||
}
|
||
}
|
||
uint8_t ee_writebytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize)
|
||
{
|
||
uint16_t i,m;
|
||
uint16_t usAddr;
|
||
usAddr = _usAddress;
|
||
for (i = 0; i < _usSize; i++)
|
||
{
|
||
/* 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 */
|
||
if ((i == 0) || (usAddr & (EE_PAGE_SIZE - 1)) == 0)
|
||
{
|
||
/* 第0步:发停止信号,启动内部写操作 */
|
||
i2c_stop();
|
||
|
||
/* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms
|
||
CLK频率为200KHz时,查询次数为30次左右
|
||
*/
|
||
for (m = 0; m < 1000; m++)
|
||
{
|
||
/* 第1步:发起I2C总线启动信号 */
|
||
i2c_start();
|
||
|
||
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
|
||
|
||
// #if EE_ADDR_A8 == 1
|
||
// i2c_SendByte(EE_DEV_ADDR | I2C_WR | ((_usAddress >> 7) & 0x0E)); /* 此处是写指令 */
|
||
// #else
|
||
i2c_sendbyte(EE_DEV_ADDR | I2C_WR);
|
||
// #endif
|
||
|
||
/* 第3步:发送一个时钟,判断器件是否正确应答 */
|
||
if (i2c_waitack() == 0)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
if (m == 1000)
|
||
{
|
||
goto cmd_fail; /* EEPROM器件写超时 */
|
||
}
|
||
/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
|
||
if (EE_ADDR_BYTES == 1)
|
||
{
|
||
i2c_sendbyte((uint8_t)usAddr);
|
||
if (i2c_waitack() != 0)
|
||
{
|
||
goto cmd_fail; /* EEPROM器件无应答 */
|
||
}
|
||
}
|
||
else
|
||
{
|
||
i2c_sendbyte(usAddr >> 8);
|
||
if (i2c_waitack()!= 0)
|
||
{
|
||
goto cmd_fail; /* EEPROM器件无应答 */
|
||
}
|
||
|
||
i2c_sendbyte(usAddr);
|
||
if (i2c_waitack() != 0)
|
||
{
|
||
goto cmd_fail; /* EEPROM器件无应答 */
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 第6步:开始写入数据 */
|
||
i2c_sendbyte(_pWriteBuf[i]);
|
||
|
||
/* 第7步:发送ACK */
|
||
if (i2c_waitack() != 0)
|
||
{
|
||
goto cmd_fail; /* EEPROM器件无应答 */
|
||
}
|
||
usAddr++; /* 地址增1 */
|
||
}
|
||
|
||
/* 命令执行成功,发送I2C总线停止信号 */
|
||
i2c_stop();
|
||
|
||
/* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms
|
||
CLK频率为200KHz时,查询次数为30次左右
|
||
*/
|
||
for (m = 0; m < 1000; m++)
|
||
{
|
||
/* 第1步:发起I2C总线启动信号 */
|
||
i2c_start();
|
||
|
||
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
|
||
#if EE_ADDR_A8 == 1
|
||
i2c_sendbyte(EE_DEV_ADDR | I2C_WR | ((_usAddress >> 7) & 0x0E)); /* 此处是写指令 */
|
||
#else
|
||
i2c_sendbyte(EE_DEV_ADDR | I2C_WR); /* 此处是写指令 */
|
||
#endif
|
||
|
||
/* 第3步:发送一个时钟,判断器件是否正确应答 */
|
||
if (i2c_waitack() == 0)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
if (m == 1000)
|
||
{
|
||
goto cmd_fail; /* EEPROM器件写超时 */
|
||
}
|
||
|
||
/* 命令执行成功,发送I2C总线停止信号 */
|
||
i2c_stop();
|
||
|
||
return 1;
|
||
|
||
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
|
||
/* 发送I2C总线停止信号 */
|
||
i2c_stop();
|
||
return 0;
|
||
}
|
||
uint8_t ee_readbytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
|
||
{
|
||
uint16_t i;
|
||
|
||
/* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
|
||
|
||
/* 第1步:发起I2C总线启动信号 */
|
||
i2c_start();
|
||
|
||
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
|
||
|
||
i2c_sendbyte(EE_DEV_ADDR | I2C_WR); /* 此处是写指令 */
|
||
|
||
/* 第3步:发送ACK */
|
||
if (i2c_waitack() != 0)
|
||
{
|
||
goto cmd_fail; /* EEPROM器件无应答 */
|
||
}
|
||
/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
|
||
if (EE_ADDR_BYTES == 1)
|
||
{
|
||
i2c_sendbyte((uint8_t)_usAddress);
|
||
if (i2c_waitack() != 0)
|
||
{
|
||
goto cmd_fail; /* EEPROM器件无应答 */
|
||
}
|
||
}
|
||
else
|
||
{
|
||
i2c_sendbyte(_usAddress >> 8);
|
||
if (i2c_waitack() != 0)
|
||
{
|
||
goto cmd_fail; /* EEPROM器件无应答 */
|
||
}
|
||
|
||
i2c_sendbyte(_usAddress);
|
||
if (i2c_waitack() != 0)
|
||
{
|
||
goto cmd_fail; /* EEPROM器件无应答 */
|
||
}
|
||
}
|
||
|
||
/* 第6步:重新启动I2C总线。下面开始读取数据 */
|
||
i2c_start();
|
||
|
||
/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
|
||
|
||
i2c_sendbyte(EE_DEV_ADDR | I2C_RD); /* 此处是写指令 */
|
||
|
||
/* 第8步:发送ACK */
|
||
if (i2c_waitack() != 0)
|
||
{
|
||
goto cmd_fail; /* EEPROM器件无应答 */
|
||
}
|
||
|
||
/* 第9步:循环读取数据 */
|
||
for (i = 0; i < _usSize; i++)
|
||
{
|
||
_pReadBuf[i] = i2c_readbyte(); /* 读1个字节 */
|
||
|
||
/* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
|
||
if (i != _usSize - 1)
|
||
{
|
||
i2c_ack(); /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
|
||
}
|
||
else
|
||
{
|
||
i2c_nack(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
|
||
}
|
||
}
|
||
/* 发送I2C总线停止信号 */
|
||
i2c_stop();
|
||
|
||
return 1; /* 执行成功 */
|
||
|
||
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
|
||
/* 发送I2C总线停止信号 */
|
||
i2c_stop();
|
||
return 0;
|
||
}
|
||
|
||
void ee_read_data()
|
||
{
|
||
ee_readbytes(ee_rarray,0,20); //读取保存的校准值
|
||
//输出电流补偿
|
||
ao1_offsets = (float)((ee_rarray[0] << 8) | ee_rarray[1]);
|
||
if(ao1_offsets > 0xF000)
|
||
ao1_offsets = ao1_offsets-1-0xFFFF;
|
||
else
|
||
ao1_offsets = ao1_offsets;
|
||
|
||
ao2_offsets = (float)((ee_rarray[2] << 8) | ee_rarray[3]);
|
||
if(ao2_offsets > 0xF000)
|
||
ao2_offsets = ao2_offsets-1-0xFFFF;
|
||
else
|
||
ao2_offsets = ao2_offsets;
|
||
//输入电流补偿
|
||
for(int c = 0;c < 4;c++)
|
||
{
|
||
current_offsets[c] = ((ee_rarray[2*(c+2)] << 8) | ee_rarray[2*(c+2)+1]);
|
||
}
|
||
//气体压力补偿
|
||
for(int p = 0;p < 4;p++)
|
||
{
|
||
pressure_offsets[p] = ((ee_rarray[2*(p+6)] << 8) | ee_rarray[2*(p+6)+1]);
|
||
}
|
||
}
|
||
|
||
void ee_write_data()
|
||
{
|
||
//输出电流补偿
|
||
ee_warray[0] = ((int)(ao1_offsets) >> 8) & 0xff;
|
||
ee_warray[1] = (int)(ao1_offsets) & 0xff;
|
||
ee_warray[2] = ((int)(ao2_offsets) >> 8) & 0xff;
|
||
ee_warray[3] = (int)(ao2_offsets) & 0xff;
|
||
//输入电流补偿
|
||
for(int c = 0;c < 4;c++)
|
||
{
|
||
ee_warray[2*(c+2)] = (current_offsets[c] >> 8) & 0xff;
|
||
ee_warray[2*(c+2)+1] = current_offsets[c] & 0xff;
|
||
}
|
||
//气体压力补偿
|
||
for(int p = 0;p < 4;p++)
|
||
{
|
||
ee_warray[2*(p+6)] = (pressure_offsets[p] >> 8) & 0xff;
|
||
ee_warray[2*(p+6)+1] = pressure_offsets[p] & 0xff;
|
||
}
|
||
//写入eeprom
|
||
ee_writebytes(ee_warray,0,20);
|
||
}
|
||
|
||
|
||
|