[Lanqiao Cup] [Embedded Category] Section 8: EEPROM


IIC protocol:
PCF8591: Integrated AD/DA function. This chip communicates with the microcontroller through the IIC protocol.
AT24C02: EEPROM chip also communicates with the microcontroller through the IIC protocol.

Introduction to IIC protocol:

The I2C bus is a standard bidirectional interface where the controller/processor acts as a master and communicates with slave devices. A slave device cannot actively transmit data unless it has been addressed by the master device. Each device on the I2C bus has a specific device address to distinguish multiple devices on the same I2C bus. Many slave devices will require configuration at boot time to set the behavior of the device. This is typically done when the master accesses the slave's internal register map with a unique register address. A device can have one or more registers, which are used to store, write, or read data.
The physical I2C interface consists of serial clock (SCL) and serial data (SDA) lines. Both the SDA and SCL lines must be connected to VCC through pull-up resistors. The size of the pull-up resistor is determined by the capacitance on the I2C bus (for more details, please refer to the article on I2C pull-up resistor calculation ([SLVA689]). Data transfer can only be initiated when the bus is idle. If after a STOP condition When both the SDA and SCL lines are high, the bus is considered idle.

The general process of master device accessing slave device

Assume that the master device wants to send data to the slave device:

  1. The master transmitter sends a START condition and addresses the slave receiver
  2. Master transmitter sends data to slave receiver
  3. The master transmitter terminates the transmission with a STOP condition

If the master wants to receive/read data from the slave:

  1. The master transmitter sends a START condition and addresses the slave receiver
  2. The master receiver sends a request to read the register
  3. The master receiver receives data from the slave transmitter
  4. The master transmitter terminates the transmission with a STOP condition

START and STOP conditions

I2C communication with the master device is initiated by the master device sending a START condition and terminated by the master device sending a STOP condition. A high-to-low transition on the SDA line when SCL is high defines a START condition. A low-to-high transition on the SDA line while SCL is high defines a STOP condition.

The repeated START condition is similar to the START condition and is used to restart transmission without making the bus idle. It looks the same as the START condition, but is different from the START condition because it occurs before the STOP condition (when the bus is not idle). This is useful when the master wants to start new communication but does not want the bus to be idle during a STOP condition, which could otherwise cause the master to lose control of another device (in a multi-master environment).

Data validity and byte format

One data bit is transferred during each clock pulse of SCL. A byte consists of 8 bits on the SDA line . Bytes can be device addresses, register addresses, or data written to or read from a slave device. Data is transferred most significant bit (MSB) first . Any number of data bytes can be transferred from master to slave between START and STOP conditions. The data on the SDA line must remain stable during the high period of the clock cycle because changes on the data line when SCL is high are interpreted as control commands (START or STOP).

Confirmation (ACK) and non-confirmation (NACK)

Each data byte (including the address byte) is followed by an ACK bit from the receiver. The ACK bit indicates that the receiver communicated to the transmitter that the byte was successfully received and another byte can be sent.
The sender must release the SDA line before the receiver sends an ACK. To send the ACK bit, the receiver should pull down the SDA line during the low phase of the ACK/NACK related clock cycle (cycle 9) so that the SDA line is stable low during the high phase of ACK.
When the SDA line remains high during the ACK/NACK related clock cycle, this is interpreted as a NACK. There are several conditions that can cause a NACK to be generated:

  1. The communicating party cannot receive or send because it is performing some real-time function and is not ready to start communicating with the master yet.
  2. During the transmission, the receiver gets data or commands that it does not understand.
  3. During the transmission, the receiver cannot receive any more data bytes.
  4. The master receiver finishes reading the data and indicates it to the slave device via NACK.

IIC data transfer

Data must be sent to or received from the slave device, but the way to do this is by reading or writing from a register in or from the slave device.
A register is a location in the slave's memory that contains information, whether it's configuration information or some sampled data to be sent back to the master. The master device must write information to these registers to instruct the slave device to perform tasks.
Although there are usually registers in I2C slave devices, please note that not all slave devices have registers. Some devices are simple and contain only 1 register that can be written directly by sending the register data immediately after the slave address, rather than addressing the register.
An example of a single-register device is an 8-bit I2C switch, which is controlled via I2C commands.
Since it has 1 bit to enable or disable the channel, only 1 register is required and the master only writes the register data after the slave address, skipping the register number.

Write data to slave device

To write on the I2C bus, the master will send a START condition on the bus with the slave address and the last bit set to 0 (R/W bit), which indicates a write. After the slave sends the acknowledge bit, the master sends the register address of the register it wishes to write to. The slave will confirm again to let the master know it is ready. After this, the master will start sending data to the slave's registers, until the master has sent all the data it needs (sometimes this is just a byte), the master will terminate the transfer with a STOP condition.

Read data from slave

Reading from a slave is very similar to writing, but requires a few extra steps. In order to read from a slave, the master must first indicate to the slave which register it wishes to read from. This is initiated by the master in a similar manner to a write, by sending the address with the R/W bit equal to 0 (indicating a write), followed by the address of the register from which it wishes to read. Once the slave acknowledges this register address, the master will again send the START condition, followed by the slave address, with the R/W bit set to 1 (indicating a read). This time, the slave will acknowledge the read request and the master releases the SDA bus, but will continue to clock the slave. During this part of the communication, the master device will become the master receiver and the slave device will become the slave transmitter.
The master will continue to send clock pulses, but will release the SDA line so that the slave can transmit data. At the end of each data byte, the master will send an ACK to the slave to let the slave know that it is ready to receive more data. Once the master has received the expected number of bytes, it sends a NACK, signaling the slave to stop communication and release the bus. The host will then end the communication with a STOP condition.

AT24C02 chip (EEPROM)

The main feature is that it will not be lost when power is turned off, and the internal storage structure is a MOS tube. (Flash is also a storage structure that is not lost when power is lost.)
The circuit diagram is as follows:
Insert image description here
Pin 8 is VCC, and pin 4 is GND, which is the pin for power supply. After connecting, the chip can work.
Then there are the communication pins of the IIC. SCL and SDA are connected to the P20 port and P21 port of the microcontroller respectively. Pull up to VCC through two pull-up resistors, because the IIC slave device and the host communicate through open-drain mode. If it is open-drain, the VCC of the pull-up resistor must be used to output a high level.

So what are the uses of the E1, E2, and E3 pins respectively?
Insert image description here
IIC devices have a device address. For 24C02, just look at the first line of the picture above, because its capacity is 1K/2K.
It can be seen that 4 of the first 7 digits are already fixed, and the remaining three digits are A0, A1, and A2 in the circuit schematic diagram. Then in the circuit schematic diagram, A0, A1, and A2 are all connected to GND, and then the address is determined. The first seven digits are 1010000.
The last digit determines whether to write or read. If it is 0, it means writing to the chip, if it is 1, it means reading.
So the device address of AT24C02: 1010000 (R/W (write is active low))

  1. 0xA0 represents writing data to AT24C02
  2. 0xA1 represents reading the data of AT24C02

The WC pin is a write protection pin. When WP is high, AT24C02 cannot write data.

AT24C02芯片特点
• Low-voltage and Standard-voltage Operation
– 2.7 (VCC = 2.7V to 5.5V)
– 1.8 (VCC = 1.8V to 5.5V)
• Internally Organized 128 x 8 (1K), 256 x 8 (2K), 512 x 8 (4K),
1024 x 8 (8K) or 2048 x 8 (16K)
• Two-wire Serial Interface
• Schmitt Trigger, Filtered Inputs for Noise Suppression
• Bidirectional Data Transfer Protocol
• 100 kHz (1.8V) and 400 kHz (2.7V, 5V) Compatibility
• Write Protect Pin for Hardware Data Protection
• 8-byte Page (1K, 2K), 16-byte Page (4K, 8K, 16K) Write Modes
• Partial Page Writes Allowed
• Self-timed Write Cycle (5 ms max)
• High-reliability
– Endurance: 1 Million Write Cycles
– Data Retention: 100 Years
• Automotive Devices Available
• 8-lead JEDEC PDIP, 8-lead JEDEC SOIC, 8-lead Ultra Thin Mini-MAP (MLP 2x3), 5-lead
SOT23, 8-lead TSSOP and 8-ball dBGA2 Packages
• Die Sales: Wafer Form, Waffle Pack and Bumped Wafers

Translated version:

[AT24002 Features]
1. Low voltage and standard voltage versions;
2. AT24002: 256*8 (2K)
3. IIC protocol;
4. Communication noise reduction:
5. Bidirectional transmission protocol;
6. 400KHz communication rate;
7. Hardware write protection;
8. Writing cycle (5ms)
9. High reliability: 1 million writes, data can be stored for 100 years!

programming:

  1. Copy the i2c-halc and h files in [Resource Data Package] to [Programming Project]
  2. The IO initialization code of the IIC part called in main.c; PB6, PB7)
  3. Programming in the i2c.c file: read and write EEPROM functions;
  4. Call the EEPROM Write and EEPROM Read functions in main.c to complete the EEPROM read and write program.

i2c.c:

#include "i2c.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);
}

i2c.h:

#ifndef __I2C_H
#define __I2C_H

#include "main.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);

#endif

i2c.cDuring the competition, you need to write the functions for reading and writing EEPROM in the file according to the routine given above and the IIC protocol mentioned earlier (remember i2c.hto declare it in after writing):

//写24C02
void EEPROM_Write(u8 add,u8 dat)
{
    
    
	I2CStart(); 
	I2CSendByte(0xa0); 
	I2CWaitAck(); 
	
	I2CSendByte(add);	
	I2CWaitAck(); 
	I2CSendByte(dat); 
	I2CWaitAck(); 
	I2CStop();
	HAL_Delay(5);
}
//读24C02
u8 EEPROM_Read(u8 add)
{
    
    
	u8 dat;
	
	I2CStart(); 
	I2CSendByte(0xa0);
	I2CWaitAck(); 	
	I2CSendByte(add);
	I2CWaitAck(); 
	
	I2CStart();
	I2CSendByte(0xa1); 
	I2CWaitAck();
	dat = I2CReceiveByte(); 
	I2CSendNotAck();
	I2CStop();
	
	return(dat);
}

Then we can main.ccall the read and write functions in the file to test the EEPROM:

Note that the address range of EEPROM is 0~0xff, which is 0-255. The value range written is also 0-255. (Because the maximum number of digits written is 8 digits, the maximum number is 0xff, which is 255)

main.c is as follows:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
#include "led.h"
#include "key.h"
#include "i2c.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//Led执行程序
__IO uint32_t ledTick =0,keyTick=0;
u8 led_ctrl=0xff;
void LED_Process(void)
{
    
    
	if(uwTick-ledTick<500)return;
	ledTick=uwTick;
	LED_Control(led_ctrl);
	led_ctrl=~led_ctrl;
}
void KEY_Process(void)
{
    
    
	if(uwTick-keyTick<10)return;
	keyTick=uwTick;
	Key_Read();
//	if(Trg&0x01)
//	{
    
    
//	LED_Control(0x01);
//	}
	if(Trg)
	{
    
    
		LED_Control(Trg);
	}
}

void LCD_Process(void)
{
    
    
	u8 display_buf[20];
	//[问题]长数据对端数据的覆盖问题
	sprintf((char*)display_buf,"%d",4000);
	LCD_DisplayStringLine(Line0,display_buf);
	sprintf((char*)display_buf,"%d",10);
	LCD_DisplayStringLine(Line0,display_buf);
	//解决方案:加空格,针对字符串
	LCD_DisplayStringLine(Line2,"hello");
	LCD_DisplayStringLine(Line2,"h     ");
	//解决方案:格式化输出,针对数据
	sprintf((char*)display_buf,"%5d",5000);//默认5位,显示右对齐
	LCD_DisplayStringLine(Line3,display_buf);
	sprintf((char*)display_buf,"%5d",10);
	LCD_DisplayStringLine(Line3,display_buf);
	
	sprintf((char*)display_buf,"%-5d",10);//左对齐
	LCD_DisplayStringLine(Line4,display_buf);
	
	sprintf((char*)display_buf,"%05d",500);//前面补0
	LCD_DisplayStringLine(Line5,display_buf);
	
	sprintf((char*)display_buf,"%5.2f",3.1415926);//显示小鼠,总长是5位,小数点算一位
	LCD_DisplayStringLine(Line6,display_buf);
	
	sprintf((char*)display_buf,"%x",15);
	LCD_DisplayStringLine(Line7,display_buf);//%x显示16进制,%o显示8进制
	
  sprintf((char*)display_buf,"%c",'a');
	LCD_DisplayStringLine(Line8,display_buf);//%s字符串,%c是字符
	
	sprintf((char*)display_buf,"%d %%",10);
	LCD_DisplayStringLine(Line9,display_buf);//输出百分号:%
}
u8 val_24c02=0;

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
    
    
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
	
	LCD_Init();
	LED_Control(0x00);
	//LCD_Process();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	LCD_Process();
  I2CInit();
	EEPROM_Write(0x10,0x55);
	val_24c02=EEPROM_Read(0x10);
	u8 display_buf[20];
  while (1)
  {
    
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    //LED_Process();
		sprintf((char*)display_buf,"EEPROM:%d",val_24c02);
  	LCD_DisplayStringLine(Line1,display_buf);//输出百分号:%
		KEY_Process();
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
    
    
  RCC_OscInitTypeDef RCC_OscInitStruct = {
    
    0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {
    
    0};

  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2;
  RCC_OscInitStruct.PLL.PLLN = 20;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
  {
    
    
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
    
    
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
    
    
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

Guess you like

Origin blog.csdn.net/Gorege__Hu/article/details/129911933