关于E2PROM
使用E2PROM可以保存数据,特点就是掉电不丢失。
在我们CT107D开发板上所使用的的器件是AT24C02,一个容量大小是2Kb/s,也就是256字节的E2PROM。
24C02是一个基于I2C通信协议的器件。
24C01/02/04/08/16 是低工作电压的1K/2K/4K/8K/16K 位串行电可擦除只读存储器,内部组织为128/256/512/1024/2048 个字节,每个字节8 位,该芯片被广泛应用于低电压及低功耗的工商业领域。
1、串行时钟信号引脚(SCL):在SCL 输入时钟信号的上升沿将数据送入EEPROM器件,并在时钟的下降沿将数据读出。
2、串行数据输入/输出引脚(SDA):SDA 引脚可实现双向串行数据传输。该引脚为开漏输出,可与其它多个开漏输出器件或开集电极器件线或连接。
3、器件/页地址脚(A2,A1,A0):A2、A1 和A0 引脚为24C01与24C02 的硬件连接的器件地址输入引脚。24C01在一个总线上最多可寻址八个1K 器件,24C02 在一个总线上最多可寻址八个2K 器件,A2、A1 和A0 内部必须连接。
4、24C04 仅使用A2、A1 作为硬件连接的器件地址输入引脚,在一个总线上最多可寻址四个4K 器件。A0 引脚内部未连接。
5、24C08 仅使用A2 作为硬件连接的器件地址输入引脚,在一个总线上最多可寻址两个8K 器件。A0和A1 引脚内部未连接。
6、24C16 未使用作为硬件连接的器件地址输入引脚,在一个总线上最多可连接一个16K器件。A0、A1 和A2 引脚内部未连接。
7、写保护(WP)引脚:24C01/02/04/08/16 具有用于硬件数据写保护功能的引脚。当该引脚接GND时,允许正常的读/写操作。当该引脚接VCC 时,芯片启动写保护功能。
单片机开发步骤
可以发现上述的通信接口类型,都是针对串行通信设计的,所以重点是对通信接口的配置使用,即遵循既定的通信协议标准进行芯片配置。这里以IIC的通信为例,需要完成四种信号的发送(起始位、终止位、数据0/应答、数据1/非应答)。由于采用的是串行的数据传送方式,所以对于数据帧的传送,是一位一位的传送,在接收时也是逐位接收。另外对于EE2PROM而言,存储器配备了写保护,WK引脚,可以对存储器的数据进行一定程度的加密操作。
IIC总线上会外挂很多的从机,所以为了建立主从之间的通信,首先需要传送从机的地址,确认完毕后,才可以发送数据,按照实际的应用场景,可以分为三种不同的数据传送方式,格式分别如下所示,其中灰色部分由主机发出,而白色由从机发出,另外从机地址后的0表示主机向从机发送数据,而1表示从机向主机发送数据;A表示应答,A非表示非应答。
主机只发送数据:
主机只接受数据:
主机发送和接收数据:
程序设计
I2C.C
#include "i2c_hal.h"
#define DELAY_TIME 20
/**
* @brief SDA线输入模式配置
* @param None
* @retval None
*/
void SDA_Input_Mode()
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.Pin = GPIO_PIN_7;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/**
* @brief SDA线输出模式配置
* @param None
* @retval None
*/
void SDA_Output_Mode()
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.Pin = GPIO_PIN_7;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/**
* @brief SDA线输出一个位
* @param val 输出的数据
* @retval None
*/
void SDA_Output( uint16_t val )
{
if ( val )
{
GPIOB->BSRR |= GPIO_PIN_7;
}
else
{
GPIOB->BRR |= GPIO_PIN_7;
}
}
/**
* @brief SCL线输出一个位
* @param val 输出的数据
* @retval None
*/
void SCL_Output( uint16_t val )
{
if ( val )
{
GPIOB->BSRR |= GPIO_PIN_6;
}
else
{
GPIOB->BRR |= GPIO_PIN_6;
}
}
/**
* @brief SDA输入一位
* @param None
* @retval GPIO读入一位
*/
uint8_t SDA_Input(void)
{
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET){
return 1;
}else{
return 0;
}
}
/**
* @brief I2C的短暂延时
* @param None
* @retval None
*/
static void delay1(unsigned int n)
{
uint32_t i;
for ( i = 0; i < n; ++i);
}
/**
* @brief I2C起始信号
* @param None
* @retval None
*/
void I2CStart(void)
{
SDA_Output(1);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
SDA_Output(0);
delay1(DELAY_TIME);
SCL_Output(0);
delay1(DELAY_TIME);
}
/**
* @brief I2C结束信号
* @param None
* @retval None
*/
void I2CStop(void)
{
SCL_Output(0);
delay1(DELAY_TIME);
SDA_Output(0);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
SDA_Output(1);
delay1(DELAY_TIME);
}
/**
* @brief I2C等待确认信号
* @param None
* @retval None
*/
unsigned char I2CWaitAck(void)
{
unsigned short cErrTime = 5;
SDA_Input_Mode();
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
while(SDA_Input())
{
cErrTime--;
delay1(DELAY_TIME);
if (0 == cErrTime)
{
SDA_Output_Mode();
I2CStop();
return ERROR;
}
}
SDA_Output_Mode();
SCL_Output(0);
delay1(DELAY_TIME);
return SUCCESS;
}
/**
* @brief I2C发送确认信号
* @param None
* @retval None
*/
void I2CSendAck(void)
{
SDA_Output(0);
delay1(DELAY_TIME);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
SCL_Output(0);
delay1(DELAY_TIME);
}
/**
* @brief I2C发送非确认信号
* @param None
* @retval None
*/
void I2CSendNotAck(void)
{
SDA_Output(1);
delay1(DELAY_TIME);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
SCL_Output(0);
delay1(DELAY_TIME);
}
/**
* @brief I2C发送一个字节
* @param cSendByte 需要发送的字节
* @retval None
*/
void I2CSendByte(unsigned char cSendByte)
{
unsigned char i = 8;
while (i--)
{
SCL_Output(0);
delay1(DELAY_TIME);
SDA_Output(cSendByte & 0x80);
delay1(DELAY_TIME);
cSendByte += cSendByte;
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
}
SCL_Output(0);
delay1(DELAY_TIME);
}
/**
* @brief I2C接收一个字节
* @param None
* @retval 接收到的字节
*/
unsigned char I2CReceiveByte(void)
{
unsigned char i = 8;
unsigned char cR_Byte = 0;
SDA_Input_Mode();
while (i--)
{
cR_Byte += cR_Byte;
SCL_Output(0);
delay1(DELAY_TIME);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
cR_Byte |= SDA_Input();
}
SCL_Output(0);
delay1(DELAY_TIME);
SDA_Output_Mode();
return cR_Byte;
}
//
void I2CInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.Pin = GPIO_PIN_7 | GPIO_PIN_6;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void eeprom_write(uint8_t addr,uint8_t dat)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
uint8_t eeprom_read(uint8_t addr)
{
uint8_t rec;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStop();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
rec=I2CReceiveByte();
I2CSendNotAck();
I2CStop();
return rec;
}
void eeprom_write16(uint8_t addr,uint16_t dat)
{
uint8_t dat_h=dat>>8;
uint8_t dat_l=dat;
eeprom_write(addr,dat_h);
HAL_Delay(10);
eeprom_write(addr+1,dat_l);
HAL_Delay(10);
}
uint16_t eeprom_read16(uint8_t addr)
{
uint8_t dat_h=eeprom_read(addr);
uint8_t dat_l=eeprom_read(addr+1);
return (dat_h<<8)+dat_l;
}
void eeprom_write_f(uint8_t addr,float dat)
{
uint16_t dat_f=dat*100;
uint8_t dat_h=dat_f>>8;
uint8_t dat_l=dat_f;
eeprom_write(addr,dat_h);
HAL_Delay(10);
eeprom_write(addr+1,dat_l);
HAL_Delay(10);
}
float eeprom_read_f(uint8_t addr)
{
uint8_t dat_h=eeprom_read(addr);
uint8_t dat_l=eeprom_read(addr+1);
return ((dat_h<<8)+dat_l)/100.0f;
}
void mcp_write(uint8_t dat) //0-127 对应0-100k的阻值
{
I2CStart();
I2CSendByte(0x5e);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
I2C.H
#ifndef __I2C_HAL_H
#define __I2C_HAL_H
#include "stm32g4xx_hal.h"
void I2CStart(void);
void I2CStop(void);
unsigned char I2CWaitAck(void);
void I2CSendAck(void);
void I2CSendNotAck(void);
void I2CSendByte(unsigned char cSendByte);
unsigned char I2CReceiveByte(void);
void I2CInit(void);
void eeprom_write(uint8_t addr,uint8_t dat);
uint8_t eeprom_read(uint8_t addr);
void eeprom_write16(uint8_t addr,uint16_t dat);
uint16_t eeprom_read16(uint8_t addr);
void eeprom_write_f(uint8_t addr,float dat);
float eeprom_read_f(uint8_t addr);
void mcp_write(uint8_t dat);
#endif
main.c
#include "i2c_hal.h"
I2CInit(); //初始化I2C
char eep_num=0; //定义存放的数据
eep_num=eeprom_read(8); //放在初始化位置读取
if(bkey[1].short_flag==1)
{
eep_num++;
eeprom_write(8,eep_num); //存入数据的地址和存入的数据
sprintf(text," KEY_1 down! ");
LCD_DisplayStringLine(Line8,(uint8_t *)text);//局部刷新
bkey[1].short_flag=0;
}
uint8_t eep_num; // 0-255
uint16_t eep_frq; //0-65535
float eep_adc; //浮点数
eep_num=eeprom_read(8); //初始化的位置读取数据,上电后只读一次
eep_frq=eeprom_read16(10);
eep_adc=eeprom_read_f(12);
sprintf(text,"EEP:%d,%d,%.2f ",eep_num,eep_frq,eep_adc);
LCD_DisplayStringLine(Line6,(uint8_t *)text);//局部刷新
if(bkey[2].short_flag==1)
{
eep_num+=1;
eeprom_write(8,eep_num);
HAL_Delay(10);
eeprom_write16(10,(uint16_t)frq1);
eeprom_write_f(12,adc_data_PB15);
sprintf(text," KEY_2 down! ");
LCD_DisplayStringLine(Line8,(uint8_t *)text);//局部刷新
bkey[2].short_flag=0;
}