Learning I2C Protocol – Hardware I2C Implementation

Initial Implementation of Hardware I2C

Based on Standard Library

1. Introduction to Basic Library Functions of Hardware I2C

void I2C_DeInit(I2C_TypeDef* I2Cx);

void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);

void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);

void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);

void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);

void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);

void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);

uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);

void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);

ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);

1. void I2C_DeInit(I2C_TypeDef* I2Cx);

(1) Function Description: Resets the I2Cx peripheral registers to their default values.

(2) I2Cx: x can be 1 or 2, used to select the I2C peripheral.

In the STM32F103C/R/VX series, there are two I2C peripherals on the chip, I2C1 and I2C2, both located on the APB1 peripheral bus.

(3) Usage: Usually called before reinitializing the I2C peripheral to ensure all registers are in their initial state, avoiding residual configurations affecting the new initialization process.

2. void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);

(1) Function Description: Initializes the I2Cx peripheral according to the parameters specified in I2C_InitStruct.

(2) I2Cx: x can be 1 or 2, used to select the I2C peripheral.

(3) I2C_InitStruct: Pointer to the I2C_InitTypeDef structure, which contains the configuration information for the specified I2C peripheral.

Structure parameters of I2C_InitStruct:

  • I2C_ClockSpeed: Clock frequency, this parameter must be set to a value below 400kHz.
  • I2C_Mode: I2C mode, this parameter can be @ref I2C_mode

I2C_mode: I2C_Mode_I2C, I2C_Mode_SMBusDevice, I2C_Mode_SMBusHost

  • I2C_DutyCycle: Duty cycle for I2C fast mode, this parameter can be @ref I2C_duty_cycle_in_fast_mode
  • I2C_OwnAddress1: Specifies the first device’s own address, supports 7-bit or 10-bit addresses.
  • I2C_Ack: Enable or disable acknowledgment, this parameter can be @ref I2C_acknowledgement.

I2C_acknowledgement: I2C_Ack_Enable, I2C_Ack_Disable

  • I2C_AcknowledgedAddress: Specifies acknowledgment for 7-bit or 10-bit addresses, this parameter can be @ref i2c_cogndged_address.

3. void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);

(1) Function Description: Fills each member of I2C_InitStruct with default values.

(2) I2C_InitStruct: Pointer to the I2C_InitTypeDef structure that will be initialized.

(3) Usage: Initializes the members of the I2C_InitTypeDef structure to default values. This step is usually performed before calling the I2C_Init function to ensure all configuration parameters are in a known initial state, avoiding undefined behavior caused by uninitialized values.

4. void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

(1) Function Description: Enables or disables the specified I2C peripheral.

(2) I2Cx: x can be 1 or 2, indicating I2C1 or I2C2.

(3) NewState: The new state of the I2Cx peripheral, can be: ENABLE or DISABLE, indicating enable or disable.

5. void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState)

(1) Function Description: Generates a START condition for I2Cx communication.

(2) I2Cx: x is 1 or 2, indicating I2C1 or I2C2.

(3) NewState: ENABLE or DISABLE.

6. void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState)

(1) Function Description: Generates a STOP condition for I2Cx communication.

(2) I2Cx: x can be 1 or 2, indicating I2C1 or I2C2.

(3) NewState: ENABLE or DISABLE.

7. void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState)

(1) Function Description: Enables or disables acknowledgment feature for the specified I2C reception.

(2) I2Cx: x can be 1 or 2, indicating I2C1 or I2C2.

(3) NewState: ENABLE or DISABLE, DISABLE means setting ACK to 0, indicating no acknowledgment.

8. void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data)

(1) Function Description: Sends a data byte via the I2Cx peripheral.

(2) I2Cx: x can be 1 or 2, indicating I2C1 or I2C2.

(3) Data: The byte to be transmitted.

9. uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx)

(1) Function Description: Returns the most recently received data from the I2Cx peripheral.

(2) I2Cx: x can be 1 or 2, indicating I2C1 or I2C2.

(3) Return Value: The received data value.

10. void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction)

(1) Function Description: Sends the address byte to select the slave device.

(2) I2Cx: x can be 1 or 2, indicating I2C1 or I2C2.

(3) Address: Specifies the slave address to be transmitted.

(4) I2C_Direction: Specifies whether the I2C device is a transmitter or receiver. Values can be: I2C_Direction_Transmitter (transmit mode), I2C_Direction_Receiver (receive mode).

11. ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)

(1) Function Description: Checks if the last I2Cx event is equal to the event passed as a parameter.

(2) I2Cx: x can be 1 or 2, indicating I2C1 or I2C2.

(3) I2C_EVENT: Specifies the event to check, which can help developers synchronize the state of I2C communication, ensuring each step proceeds as expected.

(4) Return Value: SUCCESS: The last event is equal to I2C_EVENT; ERROR: The last event differs from I2C_EVENT.

2. Implementing Hardware I2C Communication

For the code implementation of hardware I2C, simply utilize the functions encapsulated in the library to piece together the I2C protocol timing diagram.

(1) void MPU6050_Init()

  • Function: Initialize Hardware I2C
void MPU6050_Init() { RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // Open-drain output GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); I2C_InitTypeDef I2C_InitStructure; I2C_InitStructure.I2C_Ack=I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed=50000; I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_16_9; I2C_InitStructure.I2C_Mode=I2C_Mode_I2C; I2C_InitStructure.I2C_OwnAddress1=0x00; I2C_Init(I2C2,&I2C_InitStructure); I2C_Cmd(I2C2,ENABLE); }

(2) void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)

  • Function: Wait for flag processing (simple)
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT) { uint32_t TimeOut; TimeOut=10000; while(I2C_CheckEvent(I2Cx,I2C_EVENT)!=SUCCESS) { TimeOut--; if(TimeOut==0) break; } }

(3) void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data)

  • Function: Write one byte
// Write one byte to the specified address void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data) { I2C_GenerateSTART(I2C2,ENABLE); // MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS); I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter); // Send slave address /* The library functions automatically handle acknowledgment for sent bytes, and the reception of bytes also automatically handles acknowledgment. If an acknowledgment error occurs, the hardware will alert via interrupts and status flags, so no further acknowledgment reception is needed, just wait for the event */ // MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS); I2C_SendData(I2C2,RegAddress); // MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING); while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING)!=SUCCESS); I2C_SendData(I2C2,Data); // MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED); while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCCESS); I2C_GenerateSTOP(I2C2,ENABLE); }

(4) uint8_t MPU6050_ReadReg(uint8_t RegAddress)

  • Function: Read one byte
// Read one byte from the specified address uint8_t MPU6050_ReadReg(uint8_t RegAddress) { uint8_t Data; /* Specify address part */ I2C_GenerateSTART(I2C2,ENABLE); // MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS); I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter); // Send slave address (transmit direction) /* The library functions automatically handle acknowledgment for sent bytes, and the reception of bytes also automatically handles acknowledgment. If an acknowledgment error occurs, the hardware will alert via interrupts and status flags, so no further acknowledgment reception is needed, just wait for the event */ // MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS); I2C_SendData(I2C2,RegAddress); // MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED); while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCCESS); /* At this point */ I2C_GenerateSTART(I2C2,ENABLE); // MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS); I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Receiver); // Send slave address (receive direction) // MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)!=SUCCESS); I2C_AcknowledgeConfig(I2C2, DISABLE); I2C_GenerateSTOP(I2C2,ENABLE); MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED); Data=I2C_ReceiveData(I2C2); I2C_AcknowledgeConfig(I2C2, ENABLE); return Data; }

Leave a Comment