Write and Read an I2C EEPROM with STM32

EEPROMs (Electrically Erasable Programmable Read-Only Memories) allow the non-volatile storage of application data or the storage of small amounts of data in the event of a power failure. Using external memories that allow you to add storage capacity for all those applications that require data recording. We can choose many types of memories depending on the type of interface and their capacity.

EEPROMs are generally classified and identified based on the type of serial bus they use. The first two digits of the code identify the serial bus used:

  • Parallel: 28 (for example 28C512) much used in the past but now too large due to having many dedicated pins for parallel transmission
  • Serial I2C: 24 (for example 24LC256)
  • Serial SPI: 25 (for example 25AA080A)
  • Serial - Microwire: 93 (for example 93C56C-E/SN)
  • Serial – UN I/O: 11 (for example 11LC040-I/SN)

Now we will see how to write or read data on an I2C EEPROM like 24C256C. This serial EEPROM is organized as 32,768 words of 8 bits each. The device’s cascading feature allows up to eight devices to share a common 2-wire bus. It is available in various 8-pin packages

The device can be used in applications consuming low power. The device is available in all standard 8-pin packages. The operating voltage is comprised of between 1.7V and 5.5V.

  • Serial Clock (SCL) is an input pin used to control data flow. On the positive-edge clock, the data is inserted into the EEPROM device, while on the negative edge clock, the data is processed out of the EEPROM module.
  • Serial Data (SDA) is a bidirectional input-output for serial data transfer. It is an open-drain pin.
  • Device Addresses (A2, A1, A0) are input pins to set the device address. These pins allow you to customize the address of the device within the I2C bus. They must connect directly to GND or to VCC (hard wired). If these pins are left floating, the A2, A1, and A0 pins will be internally pulled down to GND. When using a pull-up resistor, it recommends using 10kOhm or less.
  • Write Protect (WP) is an input pin. We can perform normal writing operations, by connecting it to GND; When connected directly to VCC, all write operations to the memory are restricted. If this pin is left open/floating, it will be pulled down to the GND(internally). When using a pull-up resistor, it recommends using 10kOhm or less.
  • Device Power Supply (VCC)
  • Ground (GND)

In our example, we connect A0, A1, A2 directly to VCC in this way the device address is 1010111 (in general A0, A1, A2 identify the last three significant bits of the device address 1 0 1 0 A2 A1 A0) is 0x57 in Hexadecimal. The 4 most significant bits are preset (Control Code), the A0, A1, A2 are Chip Select Bits.

Now we start with our project using STNucleoL053R8 and STCube to generate the initialization code. Below is shown the connection

  • A0, A1, A2 are connected directly to VCC in this way the device address is 1010111 (in general A0, A1, A2 identify the last three significant bits of the device address 1 0 1 0 A2 A1 A0) is 0x57 in Hexadecimal.
  • WP is connected to the ground to allow the normal write operation
  • SCL and SDA are connected to PA8 and PA9 respectively of STM32L053R8

So, we configure the I2C1 using STCube and leave all configuration as it is and we will operate in polling mode.

In GPIO setting select PA9 (SDA) and PA8 (SCL).

Now we create a new STM32CubeMX project with the following steps:
  • Select File > New project from the main menu bar. This opens the New Project window.
  • Go to the Board selector tab and filter on STM32L0 Series.
  • Select NUCLEO-L053R8 and click OK to load the board within the STM32CubeMX user interface
      Then the tool will open the pinout view.
  • Select Debug Serial Wire under SYS, for do it click on System Core (on the topo right) and then select SYS and finally flag on “Debug Serial Wire”.
 
  • Select Internal Clock as clock source under TIM2 peripheral. To do this click on Timers and then select TIM2. Now go to clock source and select through the drop-down menu “internal clock”.
  • Select and enable in “Connectivity” the I2C1 and left all configuration as it is and we will operate in polling mode.
  • Configure in GPIO setting PA9 (SDA) and PA8 (SCL) to manage the I2C communication.
  • Check that the signals are properly assigned on pins:
    • SYS_SWDIO on PA13
    • TCK on PA14
    • SDA I2C1on PA9
    • SCL I2C1on PA8
  • Go to the Clock Configuration tab and no change the configuration in order to use the MSI as input clock and an HCLK of 2.097 MHz.
  • Select Timers -> TIM2 and change the Prescaler to 16000 and the Counter Period to 1000.
  • In the Project Manager tab, configure the code to be generated and click OK to generate the code.

Our project has been initialized by STCubeMX. In the /Core/Src/main.c we will find our main where we will write the main body of our program.

Now let’s see what the code generator did:

First of all, we find the “Include” section we can add the library needed.

 
/* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h"
In our case we can add also “stm32l0xx_hal.h” library to be able to use HAL library (I2C HAL library included)
#include "stm32l0xx_hal.h " #include "Var.h " #include "Funct.h "
In “Private variables” has been defined two privates variable htim2 and hi2c1;
  • - htim2 as first parameter an instance of the C struct TIM_HandleTypeDef;
  • - hi2c1 as first parameter an instance of the C struct UART_HandleTypeDef.
/* Private variables ---------------------------------------------------------*/ TIM_HandleTypeDef htim2; UART_HandleTypeDef hi2c1; unsigned short int address; // eeprom address unsigned char EEP_pag = 0x00 // EEPROM page unsigned char EEP_pos = 0x00 // EEPROM position unsigned char rdata = 0x00 // to store the data read from EEPROM
In “Private function prototypes” we find the protype of function to initialize the System Clock, GPIO, timer and peripheral:
/* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM2_Init(void); static void MX_I2C1_Init(void);

This function has been generated automatically by STCubeMx with the parameter selected.

The main contains the initialization of the peripherals and variables, before the while loop the code call the function Write_EEPROM() and Read_EEPROM() to write and read a data in a specific address of the EEPROM. these functions were written in EEPROM.c, a C file added to our project in the src folder.

  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(); MX_TIM2_Init(); MX_I2C1_Init();   /* USER CODE BEGIN 2 */ address = 0x00 << 8 | 0x00 // eeprom page 0 , position 0 // Now we want to store 10 in page 0x00 and position 0x00 of EEPROM Write_EEPROM(address, 10, 0) // Now we want store in rdata variable the content of cell memory 0x0000 rdata = Read_EEPROM(address, 0)   /* USER CODE END 2 */   /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ } /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
Furthermore, we have added two header files and one c file:
  • Var.h contains the declaration of global variables:
/** Var.h * Created on: 27 ott 2021 * Author: utente */ #ifndef INC_VAR_H_ #define INC_VAR_H_ extern unsigned char buf[20]; extern int i; #endif /* INC_VAR_H_ */
  • Funct.h contains the prototype of the user function
/** Funct.h * Created on: 28 ott 2021 * Author: utente */ #ifndef INC_FUNCT_H_ #define INC_FUNCT_H_ extern unsigned char Read_EEPROM(unsigned int, unsigned char); extern void Write_EEPROM(unsigned int, unsigned char, unsigned char); #endif /* INC_FUNCT_H_ */
  • EEPROM.c contains the function written by the user to handle the writing and reading operation with EEPROM:
  • unsigned char Read_EEPROM(addr, device) reads from cell memory address (addr)and store the content in dato.
  • void Write_EEPROM(addr, dato, device) writes data (dato) to memory address (addr).
/** Serial.c * Created on: Oct 29, 2021 * Author: utente */ #include "stm32l0xx.h" // Device header #include "stm32l0xx_hal_conf.h" #include "stm32l0xx_hal.h " #include "Var.h " #include "Funct.h " extern I2C_HandleTypeDef hi2c1; unsigned char Read_EEPROM(unsigned int addr, unsigned char device) { unsigned char page; uint8_t dato; page=0xAF; // due to chip select bits setting HAL_I2C_Mem_Read(&hi2c1,page, addr, I2C_MEMADD_SIZE_16BIT, &dato,1,5); return dato; } void Write_EEPROM(unsigned int addr, unsigned char dato, unsigned char device) { unsigned char page; page=0xAF; //due to chip select bits setting HAL_I2C_Mem_Write(&hi2c1,page, addr, I2C_MEMADD_SIZE_16BIT, &dato,1,5 ); while(HAL_I2C_IsDeviceReady(&hi2c1, 0xA0, 1, HAL_MAX_DELAY) != HAL_OK); HAL_Delay(10); }
Now we are ready to compile and run the project:
  1. Compile the project within IDE.
  1. Download it to the board.
  2. Run the program.
So, that was all for today. I hope you have enjoyed today's lecture. In the next tutorial, we will have a look at How to perform SPI Communication with STM32. Till then take care and have fun !!! :)
Syed Zain Nasir

I am Syed Zain Nasir, the founder of <a href=https://www.TheEngineeringProjects.com/>The Engineering Projects</a> (TEP). I am a programmer since 2009 before that I just search things, make small projects and now I am sharing my knowledge through this platform.I also work as a freelancer and did many projects related to programming and electrical circuitry. <a href=https://plus.google.com/+SyedZainNasir/>My Google Profile+</a>

Share
Published by
Syed Zain Nasir