STM32F429 Getting Started: IIC Communication Protocol (Hardware)

I. I2C Agreement

The I2C communication protocol (Inter-Integrated Circuit) was developed by Phiilps. Because of its small pins, simple hardware implementation and strong scalability, it does not require external transceiver devices such as USART, CAN and so on. It is now widely used for communication among multiple integrated circuits (IC) in the system.

(1) The characteristics of the I2C physical layer:

 

The resistance is generally 4.7k.

Its physical layer has the following characteristics:

(1) It is a bus supporting multiple devices. "Bus" refers to the signal line shared by multiple devices. In an I2C communication bus, multiple I2C communication devices can be connected, supporting multiple communication hosts and multiple communication slaves.

(2) An I2C bus only uses two bus lines, a two-way serial data line (SDA) and a serial clock line (SCL). The data line is used to represent the data, and the clock line is used to synchronize the sending and receiving of the data.

(3) Each device connected to the bus has a separate address that the host can use to access between different devices.

(4) Bus is connected to power supply by pull-up resistance. When I2C device is idle, it will output high resistance state, while when all devices are idle and output high resistance state, pull bus to high level by pull-up resistance. (Line and characteristics) If one device outputs low level at this time, it is impossible for other devices to output high level, which can take the form of high resistance state (i.e., disconnection).

(5) When multiple hosts use the bus at the same time, in order to prevent data conflicts, arbitration will be used to decide which device will occupy the bus. (0V)

(6) There are three transmission modes: standard mode with a transmission rate of 100 kbit/s, fast mode with a transmission rate of 400 kbit/s and high speed mode with a transmission rate of 3.4 Mbit/s, but most I 2C devices currently do not support high speed mode.

(7) The number of IC s connected to the same bus is limited by the maximum bus capacitance of 400pF. (Prevents interference)

(2) Protocol Layer of IIC

The IIC protocol defines the starting and stopping signals for communication, data validity, response, arbitration, clock synchronization, and address broadcasting.

  1. Basic IIC reading and writing process:

    Host writes data to slave:

The shadow part is: data is transferred from host to slave.

The non-shaded part is: data is transferred from the computer to the host. (Direction is controlled by read-write bits)

After the start signal is generated, all slave computers begin to wait for the slave address signal (SLAVE_ADDRESS) that the host broadcasts next.On the I2C bus, the address of each device is unique. When the host broadcasts the same address as a device, the device is selected. Devices that are not selected will ignore the subsequent data signals. According to the I2C protocol, this slave address can be 7 or 10 digits. After the address address, it is the chosen location for the direction of transmission and the bit is 0.When the host receives a matching address, the host or returns an ACK or NACK signal from the opportunity. The host can continue to send or receive data only after receiving an answer signal.

After broadcasting the address and receiving the answer signal, the host begins to formally transmit data (DATA) to the slave computer. The size of the data package is 8 bits. For each byte of data sent by the host, it waits for the slave response signal (ACK).Repeat this process to transfer N data to the slave machine, which has no size limit. When the data transfer ends, the host sends a stop signal (P) to the slave machine to indicate that the data is no longer being transmitted.

S: Transmission start signal.

SLAVE_ADDRESS: Slave address. (equivalent to point name)

A/A: Answer (ACK) or Non-Answer (NACK) Signal

The host reads data from the slave:

After broadcasting the address and receiving the answer signal, data (DATA) is returned to the host from the computer, and the packet size is 8 bits. Every time one data is sent from the computer, it waits for the host's answer signal (ACK).Repeat this process to return N data, which has no size limit. When the host wants to stop receiving data, it returns a non-answering signal (NACK) to the slave and automatically stops data transmission from the slave.

Communication Composite Mode:

 

In addition to basic read and write, I2C communication is more commonly in a composite format, where there are two start signals (S) in the transmission process. In the first transmission, the host looks for a "data" from the device through SLAVE_ADDRESS, which is usually used to represent a register or memory address inside the device (note the difference between SLAVE_ADDRESS and SLAVE_ADDRESS)In the second transmission, the content of the address is read or written. That is, the first communication tells the reader to write the address from the machine, and the second communication tells the reader to write the actual content. The SLAVE_ADDRESS mentioned earlier is the device-specific address. This address is the memory address written to the device after the device has been distinguished.

2.Start and stop signals for communication

 

  • When the SCL line is high level SDA line switches from high level to low level - the beginning of communication.

  • When the SCL line is at high level, the SDA line switches from low level to high level - the communication stops.

  • Start and stop signals are generally generated by the host.

3. Data validity

IIC uses SDA signal lines to transmit data, SCL signal lines to synchronize data, and SDA data is written to transmit one bit of data in each clock cycle of the SCL.

 

  • SDA data is valid when SCL is high, that is, SDA data 1 at high level and 0 at low level.

  • SCL is low level, SDA data is not valid, generally at this time SDA switches the level to prepare for the next data representation.

  • Each data transfer is in bytes, and the number of bytes transferred is unlimited.

4. Address and data direction

  • Each device on the IIC bus has its own independent address. When the host initiates communication, it sends the device address (SLAVE_ADDRESS) through the SDA signal line to find slaves. The device address can be 7 or 10 bits.

  • A data bit R/W immediately following the device address is used to indicate the direction of data transmission. A data direction bit of 1 indicates that the host reads data from the slave, and a bit of 0 indicates that the host writes data from the slave.

  • When reading the direction of data, the host releases control over the SDA signal line, which is controlled by the slave computer. The host receives the signal. When writing the direction of data, the SDA is controlled by the host and receives the signal from the machine. (The sender has control over the SDA line.)

Usually we use 7 bits to store addresses. Some IIC s use 10 bits. When we use 7 bits, we incorporate the last directional bit into it to form either an 8-bit read address or an 8-bit write address.

5. Response

 

  • I2C's data and address transmissions are responsive. Responses include "Answer (ACK)" and "Non-Answer (NACK)" signals. As a data receiver, when a device (both master and slave) receives a byte of data or address transmitted by I2C, it needs to send "Answer (ACK)" to the other party if it wants the other party to continue sending data.Signal, the sender will continue to send the next data; if the receiver wants to end data transmission, it will send the other party a "non-responding (NACK)" signal, after receiving the signal, the sender will produce a stop signal to end the signal transmission.

  • The host generates a clock during transmission. At the ninth clock, the data sender releases control of the SDA, and the data receiver controls the SDA. If the SDA is at a high level, it represents a non-responding signal (NACK) and a low level indicates an responding signal (ACK).

(3) IIC features and architecture of STM32

  • Software IIC: Use the CPU to directly control the level of the communication pin to generate logic that meets the communication protocol standards.

  • Hardware IIC: The IIC on-chip peripheral of STM32 is responsible for the implementation of IIC communication protocol. As long as it is configured properly, it will automatically generate communication signals according to the requirements of the protocol, send and receive data, and cache it. The CPU can complete the data sending and receiving as long as it detects the state of the peripheral and accesses the data registers. This way of processing IIC protocol by the hardware peripheral reduces the work of the CPU, andMake software design easier.

  • The IIC peripheral of STM32 can be used as the host and slave for communication, supports 100Kbit/s and 400Kbit/s rates, supports 7-bit and 10-bit device addresses, supports DMA data transfer, and has data verification function.

Architecture Analysis:

1 is the communication pin; 2 is the clock control logic; 3 is the data control logic; 4 is the overall control logic.

(1) Communication pin

(2) Clock control logic

The clock signal of the SCL line is controlled by the IIC interface according to the clock controller (CCR), and the parameters controlled are mainly the clock frequency.

  • Select the Standard/Fast mode for IIC communication, which corresponds to 100Kbit/s or 400Kbit/s communication rate for IIC.

  • The duty cycle of SCL clock can be selected in fast mode, Tlow/Thigh=2 or Tlow/Thigh=16/9 mode. Tlow is the cycle of low level and Thigh is the cycle of high level.

  • The 12-bit configuration factor CCR in the CCR register works with the input clock source of the IIC peripheral to generate the SCL clock. The IIC peripheral input clock source of STM32 is PCLK1.

The formula for calculating the clock frequency is as follows:

Standard mode: Thigh=CCR * TPCKL1 Tlow = CCR*TPCLK1

When Tlow/Thigh=2 in Quick Mode: Thigh = CCR * TPCKL1 Tlow = 2 * CCR * TPCKL1

When Tlow/Thigh=16/9 in Quick Mode: Thigh = 9 * CCR * TPCKL1 Tlow = 16* CCR*TPCKL1

For example, PCLK1=45MHz, to configure a rate of 400Kbit/s, calculate as follows:

PCLK clock cycle: TPCLK1 = 1/45000000

Target SCL clock cycle: TSCL = 1/400000

High level time in SCL clock cycle: THIGH = TSCL/3

Low level time in SCL clock cycle: TLOW = 2*TSCL/3

Calculate CCR value: CCR = THIGH/TPCLK1 = 37.5

The result is a decimal, and CCR registers cannot configure decimal parameters, so we can only take CCR to 38, so the actual SCL frequency of I2C cannot reach 400 KHz (about 394736Hz).

(3) Data control logic

SDA signals from IIC are mainly connected to data shift registers, which are data sources and targets of data registers (DR), address registers (OAR), PEC registers (checks), and SDA data lines.

  • When sending data outward, the Data Shift Register uses the Data Register as the data source to send data bit by bit over the SDA signal line.

  • When receiving data from outside, the data shift register stores the data sampled by the SDA signal line bit by bit in the Data Register.

  • If data validation is enabled, the received data is processed by a PCE calculator, and the results are stored in a PEC register.

  • When the I2C of STM32 works in slave mode and receives the device address signal, the data shift register compares the received address with the value of STM32's own I2C Address Register in order to respond to the host's address.

  • STM32's own I2C address can be modified by modifying its own Address Register to support the simultaneous use of two I2C device addresses stored in OAR1 and OAR2, respectively.

(4) Overall logical control

The overall control logic is responsible for coordinating the entire IIC peripheral. The mode of operation of the control logic changes according to the parameters of the Control Register (CR1/CR2) that we configure. When the peripheral is working, the control logic modifies the status register (SR1/SR2) according to the working state of the peripheral.Control logic is also responsible for controlling the generation of IIC interrupt signals, DMA requests and various IIC communication signals (start, stop, response signals, etc.) as required.

 

(4) IIC communication process of STM32

When communicating with IIC peripherals, it writes parameters to different data bits of Status Registers (SR1 and SR2) at different stages of the communication, and reads these register flags to understand the communication status.

  1. Host Sender

Mr. Start:

The EV5 event is then generated, as seen in register I2C_SR1, to detect whether the SB bit is 1:

After that, you start sending the address, which is the 8-bit write address we mentioned earlier. Then you send the device address and wait for an answer signal. If there is a slave response, an event "EV6" and "EV8" will be generated. Then the "ADDR" bit and "TXE" bit of the SR1 register are set to 1, ADDR 1 means the address has been sent, and TXE 1 means the register is empty.

 

After the above steps are executed properly and the ADDR bit is cleared, we write the data to the IIC's "data register DR" and the TXE bit will be reset to 0, indicating that the data register is not empty. After the IIC peripheral sends the data through one bit of the SDA signal line, EV8 will be generated again."Events, where the TXE bit is set to 1, repeat this process to send multiple bytes of data; a stop signal P is generated, at which point an EV2 event is generated, and the TXE bit and the BTF bit of SR 1 are set to 1 to indicate the end of the communication.

2. Main Receiver

The main receiver receive process and events are described below:

(1) As with the main sending process, the start signal (S) is generated by the host side. After controlling the start signal, it generates the event "EV5", which indicates that the start signal has been sent to the "SB" position 1 of the SR1 register.

(2) Send the device address immediately and wait for an answer signal. If there is a slave response, the event "EV6" is generated when the "ADDR" bit of the SR1 register is set to 1, indicating that the address has been sent.

(3) Start sending data to the host after receiving the address from the host. When the host receives the data, an "EV7" event will occur, and the RXNE of the SR1 register is set to 1, indicating that the receiving data register is not empty. After we read the register, we can empty the data register to receive the next data. At this time, we can control I2C to send an answer signal (ACK).Or Non-Answer Signal (NACK), if answered, repeat the above steps to receive data, if not answered, stop transmission;

(4) After sending a non-response signal, a stop signal (P) is generated to end the transmission.

(5) IIC Initialization Structure

typedef struct {
	 uint32_t I2C_ClockSpeed; 	//Set SCL clock frequency below 40y0000
	 uint16_t I2C_Mode; 		//Specify working mode, optional I2C mode and SMBUS mode 
	 uint16_t I2C_DutyCycle;	//Specify clock duty cycle, optional low/high = 2:1 and 16:9 modes
	 uint16_t I2C_OwnAddress1;  //Specify your own I2C device address 
	 uint16_t I2C_Ack; 			//Enables or turns off the response (generally) 
	 uint16_t I2C_AcknowledgedAddress; //Specifies the length of the address, which can be 7 and 10 bits 
} I2C_InitTypeDef;
  • I2C_ClockSpeed: Sets the transfer rate of IIC. The function writes the clock factor to the clock control register CCR of IIC after calculation based on the input value.

  • I2C_Mode: Choose how I 2C is used, there are I2C mode (I2C_Mode_I2C) and SMMBus master and slave mode (I2C_Mode_SMBusHost, I2C_Mode_SMBusDevice). I2C does not need to distinguish between master and slave modes here, just set I2C_Mode_I2C directly.

  • I2C_DutyCycle: Sets the SCL line clock duty cycle of IIC.

  • I2C_OwnADDress1: The device's own address is configured. Each device connected to the I2C bus must have its own address, including as a host. The address can be set to 7 or 10 bits (determined by the I2C_AcknowledgeAddress member below)As long as the address is unique on the I2C bus, the I2C peripheral of STM32 can use two addresses simultaneously, that is, to respond to both addresses simultaneously. This structure member I2C_OwnAddress1 is configured with the default address stored by the OAR1 register. If you need to set the second address register OAR2, you can use the I2C_OwnAddress2Config function to configure it. OAR2 does not support 10Bit address.

  • I2C_Ack_Enable: Set to enable a response signal to be sent, and changing to non-response and non-response often results in communication errors.

  • I2C_AcknowledgeAddress: This member chooses whether I2C's addressing mode is 7-bit or 10-bit. This depends on the address that actually connects to the device on the I2C bus. The configuration of this member also affects the I2C_OwnAddress1 member. I2C_OwnAddress1 only supports 10-bit addresses when it is set here in 10-bit mode.

(6) Reading and Writing EEPROM Experiments

(1) First, let's familiarize ourselves with the EEPROM on the board:

 

As you can see from the diagram, the chip is AT24C02, its device address is fixed, its four high bits are fixed at 1010, and the last three are A2\A1\A0. Because the three pins are grounded, 1010000 is the device address of the device. The above is known from the AT24C02 specification. We can also differentiate multiple EEPROM s by modifying the value of A2/A1/A0.

 

EEPROM has some differences in reading and writing from STM32. It must resend the address once after each data write before it can continue to write:

So we use the following method, we just need to determine the starting address and the starting data, and then the data will be filled in sequentially:

The next step is to read, write, read, locate the data, and then transfer it, which takes a lot of time to send three bytes at a time. The figure below saves time, depending on whether the user continues to respond.

There is also a WP pin in the EEPROM chip, which has write protection function. When the pin level is high, write data is prohibited. When the pin level is low, write data can be written. We grounded directly without write protection function.

(2) Software design

  1. Set Pin

#define EEPROM_I2C                              I2C1
#define EEPROM_I2C_CLK                         RCC_APB1Periph_I2C1

#define EEPROM_SCL_GPIO_PORT                    GPIOB
#define EEPROM_SCL_GPIO_CLK                     RCC_AHB1Periph_GPIOB
#define EEPROM_SCL_PIN                          GPIO_Pin_6
#define EEPROM_SCL_AF                           GPIO_AF_I2C1
#define EEPROM_SCL_SOURCE                       GPIO_PinSource6

#define EEPROM_SDA_GPIO_PORT                GPIOB
#define EEPROM_SDA_GPIO_CLK                 RCC_AHB1Periph_GPIOB
#define EEPROM_SDA_PIN                       GPIO_Pin_7
#define EEPROM_SDA_AF                        GPIO_AF_I2C1
#define EEPROM_SDA_SOURCE                   GPIO_PinSource7

2.Initialize GPIO

void EEPROM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
		
RCC_AHB1PeriphClockCmd(EEPROM_SCL_GPIO_CLK|EEPROM_SDA_GPIO_CLK,ENABLE);
  
  /* Connect SCL*/
GPIO_PinAFConfig(EEPROM_SCL_GPIO_PORT,EEPROM_SCL_SOURCE,EEPROM_SCL_AF);
  /*  Connect SDA*/
GPIO_PinAFConfig(EEPROM_SDA_GPIO_PORT,EEPROM_SDA_SOURCE,EEPROM_SDA_AF);

  /* GPIO Initialization */
  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  
  /* Configure SCL pins for multiplexing  */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Pin = EEPROM_SCL_PIN  ;  
  GPIO_Init(EEPROM_SCL_GPIO_PORT, &GPIO_InitStructure);

  /* Configure SDA pins for multiplexing */
  GPIO_InitStructure.GPIO_Pin = EEPROM_SDA_PIN;
  GPIO_Init(EEPROM_SDA_GPIO_PORT, &GPIO_InitStructure);

}

3. Initialize IIC

#define I2C_OWN_ADDR 0x77//This address only needs to be different from the IIC peripheral address on STM32

#define I2C_SPEED 400000

#Define EEPROM_ADDR (0x50< < 1) //0xA0 is also possible

Why does the EEPROM_ADDR above need to be moved one bit to the left because the directional position is added, as it is handled in Library functions:

Next, initialize:

void EEPROM_I2C_ModeConfig(void)
{
  I2C_InitTypeDef I2C_InitStruct;
    
  //Enable I2C clock 
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
  //Enable response
  I2C_InitStruct.I2C_Ack =  I2C_Ack_Enable;
  //IIC Addressing Mode
  I2C_InitStruct.I2C_AcknowledgedAddress =  I2C_AcknowledgedAddress_7bit;
  //Communication rate
  I2C_InitStruct.I2C_ClockSpeed =  I2C_SPEED; 
  //Duty Cycle
  I2C_InitStruct.I2C_DutyCycle =  I2C_DutyCycle_2;
  //IIC mode
  I2C_InitStruct.I2C_Mode =  I2C_Mode_I2C ;
  //IIC Self Address
  I2C_InitStruct.I2C_OwnAddress1 = I2C_OWN_ADDR;
  //IIC Write Configuration
  I2C_Init(EEPROM_I2C,&I2C_InitStruct);
  //Enabling IIC
  I2C_Cmd(EEPROM_I2C,ENABLE);

}

4.byte write function

The order of this function is derived from the order in which the EEPROM above writes data. Events such as EV5 that it generates are declared in Library functions:

The final implementation is as follows:

void EEPROM_Byte_Write(uint8_t* pData,uint8_t addr)
{
  I2C_GenerateSTART(EEPROM_I2C,ENABLE); //Generate start signal
  
  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)!= SUCCESS )
  {
     
  }
    
  I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);
   					      while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!= SUCCESS)
  {
  
  }
  
  I2C_SendData(EEPROM_I2C,addr);
  
  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED)!= SUCCESS)
  {
  
  }
  
  I2C_SendData(EEPROM_I2C,*pData);
  
  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED)!= SUCCESS)
  {
  
  }
  
  I2C_GenerateSTOP(EEPROM_I2C,ENABLE);

}

5. Testing

Write a function that reads a byte to test. We use serial port to test. In the process of testing, we need to note that we need to wait for EEPROM to finish writing data before we can take the next step. Check if the ACK in ADDR bit is set to 1, and if not, resend. This can be caused because STM32 runs too fast. STM32 acknowledges when EEPROM has not been written yet.When the transfer is complete, it causes the card to be stuck in a waiting function.

void Wait_for_EEPROM(void)
{
  do
  {
    I2C_GenerateSTART(EEPROM_I2C,ENABLE);
      
    I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);  
  
  }while(I2C_GetFlagStatus(EEPROM_I2C,I2C_FLAG_ADDR) == RESET);
  
  //Wait for ADDR = 1 to execute the following statement
  I2C_ClearFlag(EEPROM_I2C,I2C_FLAG_AF);
  
  I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
    
  while(I2C_GetFlagStatus(EEPROM_I2C,I2C_FLAG_BUSY) == SET);
  //Bus idle, execute the following statements, to prevent the process of execution, just end and start again

}  

Once you have solved these problems, you can write and read functions:

uint8_t EEPROM_Byte_Read(uint8_t addr)
{
  uint8_t readTemp;

  Wait_for_EEPROM();
  
  
  I2C_GenerateSTART(EEPROM_I2C,ENABLE);
  
  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS )
  {
    
  }
    
  I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);
  
  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS )
  {
  
  }
  
  I2C_SendData(EEPROM_I2C,addr);
  
  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS )
  {
  
  }
  
  I2C_GenerateSTART(EEPROM_I2C,ENABLE);
  
  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS )
  {

  }

  I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Receiver);
  
  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS )
  {
  
  }
  
  I2C_AcknowledgeConfig(EEPROM_I2C,DISABLE); //Turn on non-response when a data is received
  
  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS )
  {
  
  }
  
  readTemp = I2C_ReceiveData(EEPROM_I2C);
   
  I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
  
  return readTemp;

}

6. Principal Functions

After that, the serial port is needed in the main function to receive the data from EEPROM. When the write function is blocked out after power failure, the serial port can still get the data, which also indicates that the data will not be lost after power failure of EEPROM:

int main(void)
{	
  
  uint8_t test_addr = 0x02;
  uint8_t test_writeData = 0x27;
  uint8_t test_readData =0;
  
  /*Initialize USART configuration mode 115200 8-N-1, interrupt reception*/
  Debug_USART_Config();
	
	/* Send a string */
  Usart_SendString( DEBUG_USART,"This is a EEPROM Test experiment\n");
  printf("This is a EEPROM Test experiment\n");
  
  
  printf("\r\n Initialization I2C\r\n");
  
  EEPROM_GPIO_Config();
  EEPROM_I2C_ModeConfig();
  
  printf("\r\nI2C Initialization complete\r\n");  
  
  EEPROM_Byte_Write(&test_writeData,test_addr);

  test_readData = EEPROM_Byte_Read(test_addr);  

  printf("test_readData =0x%x",test_readData);
	
  while(1)
	{	
		
	}	
}

(7) Read and write EEPROM program upgrade version

When we really use IIC, we don't use the above code for writing and reading because it's not rigorous and bug-prone, so I learned IIC from the routine:

Difference 1: The address of the hardware EEPROM can be modified.

void I2C_EE_Init(void)
{
  I2C_GPIO_Config(); 
 
  I2C_Mode_Configu();

/* Select the address EEPROM will write to based on the definition in header file i2c_ee.h */
#ifdef EEPROM_Block0_ADDRESS
  /* Select EEPROM Block0 to write */
  EEPROM_ADDRESS = EEPROM_Block0_ADDRESS;
#endif

#ifdef EEPROM_Block1_ADDRESS  
	/* Select EEPROM Block1 to write to */
  EEPROM_ADDRESS = EEPROM_Block1_ADDRESS;
#endif

#ifdef EEPROM_Block2_ADDRESS  
	/* Select EEPROM Block2 to write to */
  EEPROM_ADDRESS = EEPROM_Block2_ADDRESS;
#endif

#ifdef EEPROM_Block3_ADDRESS  
	/* Select EEPROM Block3 to write to */
  EEPROM_ADDRESS = EEPROM_Block3_ADDRESS;
#endif
}

Difference 2: Define a number to subtract itself so that it completes the event's flag position within the set time. If not, print out an event with an error:

uint32_t I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr)
{
  //IIC Start Signal
  I2C_GenerateSTART(EEPROM_I2C, ENABLE);
  //Set timeout wait time
  I2CTimeout = I2CT_FLAG_TIMEOUT;

  //Detect EV5 events and clear flags
  while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0);
  }    

  //Send EEPROM device address
  I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDRESS,I2C_Direction_Transmitter);
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;
  //Detect EV6 events and clear flags
  while(!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1);
  }    
      
  //Send an internal address to write to EEPROM
  I2C_SendData(EEPROM_I2C, WriteAddr);
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;

  //Detect EV8 events and clear flags
  while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))  
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
  } 
  //Send a byte of data to write
  I2C_SendData(EEPROM_I2C, *pBuffer); 
   
  I2CTimeout = I2CT_FLAG_TIMEOUT;

  //Detect EV8 events and clear flags
  while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
  } 
  
  //Send stop signal
  I2C_GenerateSTOP(EEPROM_I2C, ENABLE);
  
  return 1;
}

//Return function
static  uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode)
{
  EEPROM_ERROR("I2C Wait timeout!errorCode = %d",errorCode); //Equivalent to printf
  return 0;
}

In the library function, you also learned that you can print error, error-reporting macros in real time, which can point directly to the wrong place:

#define EEPROM_DEBUG(fmt,arg...)          do{\
                                          if(EEPROM_DEBUG_ON)\
                                          printf("<<-EEPROM-DEBUG->> 											  [%d]"fmt"\n",__LINE__, ##arg);\
                                          }while(0)

There are also macros for finding errors as follows:

#define EEPROM_INFO(fmt,arg...)           printf("<<-EEPROM-INFO->> 													"fmt"\n",##arg)
#define EEPROM_ERROR(fmt,arg...)          printf("<<-EEPROM-ERROR->> 													"fmt"\n",##arg)

Difference 3: Read multiple data, data blocks.

 /*
  * @brief   Read a piece of data from EEPROM 
  * @param   
  *		@arg pBuffer:Buffer pointer holding data read from EEPROM
  *		@arg WriteAddr:Address of EEPROM receiving data
  *     @arg NumByteToWrite:Number of bytes to read from EEPROM
  * @retval  nothing
  */
uint32_t I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)
{  
    I2CTimeout = I2CT_LONG_TIMEOUT;
	//Bus is not idle
    while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))   
    {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9);
    }
  //Generate start signal 
  I2C_GenerateSTART(EEPROM_I2C, ENABLE);
  //*((u8 *)0x4001080c) &=~0x80;
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;

  //Detect EV5 events and clear flags
  while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);
   }

  //Send EEPROM device address
  I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESSI2C_Direction_Transmitter);

  I2CTimeout = I2CT_FLAG_TIMEOUT;
 
  //Detect EV6 events and clear flags
  while(!I2C_CheckEvent(EEPROM_I2C,	I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) 
    {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(11);
   }
  //Clear EV6 events by resetting the PE bit
  I2C_Cmd(EEPROM_I2C, ENABLE);

  //Send the internal address of the EEPROM to read (that is, the address of the EEPROM internal memory)
  I2C_SendData(EEPROM_I2C, ReadAddr);  

  I2CTimeout = I2CT_FLAG_TIMEOUT;

  //Detect EV8 events and clear flags
  while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(12);
   }
  //Generate second IIC start signal
  I2C_GenerateSTART(EEPROM_I2C, ENABLE);
  
     I2CTimeout = I2CT_FLAG_TIMEOUT;

  //Detect EV5 events and clear
  while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT))
    {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(13);
   }
  //Send EEPROM device address
  I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Receiver);
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;

  //Detect EV6 events and clear flags
  while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
    {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(14);
   }
  //How much data to read from the settings
  while(NumByteToRead)  
  {
    //If it equals 1, then it's the last data, send a non-answer signal, and end the transmission
    if(NumByteToRead == 1)
    {
      //Send Non-Answer Signal
      I2C_AcknowledgeConfig(EEPROM_I2C, DISABLE);
      
      //Send stop signal
      I2C_GenerateSTOP(EEPROM_I2C, ENABLE);
    }

		I2CTimeout = I2CT_LONG_TIMEOUT;
		while(I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)  
		{
			if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
		} 	
		{
	  //Read a byte of data from the device via IIC
      *pBuffer = I2C_ReceiveData(EEPROM_I2C);

      //Point to Next Address
      pBuffer++; 
      
      //Received Data Decrease
      NumByteToRead--;
		}			
  }

  //Enable answering for next transfer
  I2C_AcknowledgeConfig(EEPROM_I2C, ENABLE);
  
  return 1;
}

Writing in this way is just mentioned above. EEPROM reading data is a composite IIC time series, which actually includes a writing process and a reading process.

During the first communication of the read sequence, I2C is used to send the device address addressing (write direction), followed by the "memory address" to read; during the second communication, I2C is used again to send the device address addressing (read direction), after which EEPROM returns to the host from the "memory address"Starting data is transmitted byte by byte, and it will continue to transmit as long as the host's response is "Answer Signal". When the host wants to end the transmission, it sends a "Non-Answer Signal" and ends the communication with a "Stop Signal", which also stops the transmission as a slave EEPROM.

Difference 4: Writing data on one page (multiple data) does not require waiting for EEPROM to write a byte, because it temporarily stores the written data in one place

In the self-written example above, we only transfer one data, and each time we write, we need to wait for it to be cached before we write the second one. However, the disadvantage of this function is that it can only write eight data, which is not flexible:

 /*
  * @brief   The number of bytes that can be written in a write loop in EEPROM, but written at one time
  *          Cannot exceed EEPROM page size, AT24C02 has 8 bytes per page
  * @param   
  *		@arg pBuffer:Buffer Pointer
  *		@arg WriteAddr:Write Address
  *     @arg NumByteToWrite:Bytes Written
  * @retval  nothing
  */
uint32_t I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite)
{
  I2CTimeout = I2CT_LONG_TIMEOUT;

  while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))  
   {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4);
  } 
  
  //Generate IIC start signal
  I2C_GenerateSTART(EEPROM_I2C, ENABLE);
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;

  //Detect EV5 events and clear flags
  while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(5);
  } 
  
  //Send EEPROM device address
  I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;

  //Detect EV6 events and clear flags
  while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) 
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(6);
  } 
  //Send the internal address of the EEPROM to be written (that is, the address of the EEPROM internal memory)
  I2C_SendData(EEPROM_I2C, WriteAddr);  

  I2CTimeout = I2CT_FLAG_TIMEOUT;

  //Detect EV8 events and clear flags
  while(! I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) 
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(7);
  } 
  //Loop through data for a set number
  while(NumByteToWrite--)  
  {
    //Send Buffer Data
    I2C_SendData(EEPROM_I2C, *pBuffer); 

    //Point to the next data in the buffer
    pBuffer++; 
  
    I2CTimeout = I2CT_FLAG_TIMEOUT;

    //EV8 events detected and flags cleared
    while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(8);
    } 
  }

  //Send stop signal
  I2C_GenerateSTOP(EEPROM_I2C, ENABLE);
  
  return 1;
}

In order to solve the above problem of writing only 8 data, the following improvements have been made, that is, to control how many times the above function is called, in which there is also a distinction between which address to start writing from, that is, alignment or misalignment:

/* AT24C01/02 8 bytes per page */
#define I2C_PageSize 8
 /*
  * @brief   Write data from buffer to I2C EEPROM
  * @param   
  *		@arg pBuffer:Buffer Pointer
  *		@arg WriteAddr:Write Address
  *     @arg NumByteToWrite:Bytes Written
  * @retval  nothing
  */
void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite)
{
  u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;
  //Address operation complement, calculates whether WriteAddr is an integer multiple of IIC_PageSize, that is, alignment
  Addr = WriteAddr % I2C_PageSize;
  //How many different data can be aligned
  count = I2C_PageSize - Addr;
  //Calculate how many full pages to write
  NumOfPage =  NumByteToWrite / I2C_PageSize;
  //Find the number of bytes on the page you are not satisfied with
  NumOfSingle = NumByteToWrite % I2C_PageSize;
 
  //Addr=0 means exactly aligned
  if(Addr == 0) 
  {
    //If the number to be written is less than eight
    if(NumOfPage == 0) 
    {
      I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
      I2C_EE_WaitEepromStandbyState();
    }
    //Conversely, greater than
    else  
    {
      //Full page first
      while(NumOfPage--)
      {
        I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize); 
        I2C_EE_WaitEepromStandbyState();
        WriteAddr +=  I2C_PageSize;
        pBuffer += I2C_PageSize;
      }
      //Finish the rest
      if(NumOfSingle!=0)
      {
        I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
        I2C_EE_WaitEepromStandbyState();
      }
    }
  }
  //If not aligned
  else 
  {
    //If less than eight
    if(NumOfPage== 0) 
    {
      I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
      I2C_EE_WaitEepromStandbyState();
    }
    //If greater than eight
    else
    {
      //Do not add this operation because the addresses are not aligned and the extra count s are processed separately
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / I2C_PageSize;
      NumOfSingle = NumByteToWrite % I2C_PageSize;	
      //Write the remaining bytes on all pages first
      if(count != 0)
      {  
        I2C_EE_PageWrite(pBuffer, WriteAddr, count);
        I2C_EE_WaitEepromStandbyState();
          
        //WteAddr with count aligns the address to the page long enough to fill the next page
        WriteAddr += count;
        pBuffer += count;
      } 
      //Write the whole page
      while(NumOfPage--)
      {
        I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);
        I2C_EE_WaitEepromStandbyState();
        WriteAddr +=  I2C_PageSize;
        pBuffer += I2C_PageSize;  
      }
      //If there is more than one unsatisfactory page of data, finish writing
      if(NumOfSingle != 0)
      {
        I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); 
        I2C_EE_WaitEepromStandbyState();
      }
    }
  }  
}

Page writing supported by EEPROM is only an accelerated I2C transmission sequence and does not actually require that pages be read and written at a time. EEPROM supports random access (read and write directly to any address)In some memory, such as NAND FLASH, it must be written as a block, such as 512 or 4096 bytes per block, the smallest unit of data writing is a block, and the entire block needs to be erased before writing; NOR FLASH must be erased as a Sector/Block before writing, before it can be written as a byte.The minimum unit for EEPROM data write and erase is bytes, not pages, and there is no need to erase the entire page before data is written.

The efficiency of National Day is really good or low, five days to complete a thing T, next time you have to supervise yourself!!!

Tags: IoT stm32

Posted on Tue, 05 Oct 2021 12:47:38 -0400 by VertLime