E2PROM STM32使用I2C协议驱动

发布于:2024-12-19 ⋅ 阅读:(103) ⋅ 点赞:(0)

关于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;
	}