一、软件I2C过程
I2C_WriteData 函数
-
发送起始条件(Start): 调用
I2C_Start()
函数发送起始条件,即先拉低SDA线,然后拉低SCL线,以准备开始通信。 -
发送设备地址和写标志位: 通过调用
I2C_WriteByte()
函数发送目标设备的地址和写标志位(写操作时,该标志位为 0),以告知设备将要进行写入操作。 -
发送寄存器地址: 再次调用
I2C_WriteByte()
函数,发送要写入数据的寄存器地址,告诉设备接下来要写入的数据将存储在哪个寄存器中。 -
写入数据: 通过循环调用
I2C_WriteByte()
函数,依次向设备寄存器中写入数据。每次写入一个字节的数据,直到所有数据写入完毕。 -
发送停止条件(Stop): 调用
I2C_Stop()
函数发送停止条件,即先拉高SDA线,然后拉高SCL线,表示数据传输结束。
I2C_ReadData 函数
-
发送起始条件(Start): 调用
I2C_Start()
函数发送起始条件,同样是先拉低SDA线,然后拉低SCL线。 -
发送设备地址和写标志位,并发送寄存器地址: 与写入数据流程类似,首先发送设备地址和写标志位,然后发送要读取数据的寄存器地址。
-
再次发送起始条件(Repeated Start): 调用
I2C_Start()
函数,发送重复的起始条件,即先拉低SDA线,然后拉低SCL线,以准备切换到读取模式。 -
发送设备地址和读标志位: 通过调用
I2C_WriteByte()
函数,发送目标设备的地址和读标志位(读操作时,该标志位为 1),以告知设备将要进行读取操作。 -
读取数据: 通过循环调用
I2C_ReadByte()
函数,依次从设备中读取数据,并存储到指定的数据缓冲区中。在最后一次读取时,不发送应答信号。 -
发送停止条件(Stop): 调用
I2C_Stop()
函数发送停止条件,表示读取数据的操作结束。
二、完整代码
BspI2C.c
/*
BspI2C.c
Implementation File for STM32 I2C Module
*/
/*
Modification History
--------------------
01a 27Feb24 Jasper Created
*/
/* Includes */
#include "include.h"
/* Define the start and stop conditions of I2C */
void I2C_Start(void) {
/* __________
*SCL \________
* ______
*SDA \_____________
*/
I2C_SDA_HIGH;
I2C_SCL_HIGH;
osDelay(I2C_DELAY);
I2C_SDA_LOW;
osDelay(I2C_DELAY);
I2C_SCL_LOW;
osDelay(I2C_DELAY);
}
void I2C_Stop(void) {
/* ____________
*SCL _____/
* _______
*SDA __________/
*/
I2C_SCL_LOW;
I2C_SDA_LOW;
osDelay(I2C_DELAY);
I2C_SCL_HIGH;
osDelay(I2C_DELAY);
I2C_SDA_HIGH;
osDelay(I2C_DELAY);
}
/* Define data write and read for I2C */
uint8_t I2C_WriteByte(uint8_t byte) {
uint8_t i;
for (i = 0; i < 8; i++) {
if (byte & 0x80) {
I2C_SDA_HIGH;
} else {
I2C_SDA_LOW;
}
osDelay(I2C_DELAY);
I2C_SCL_HIGH;
osDelay(I2C_DELAY);
I2C_SCL_LOW;
byte <<= 1;
}
/* Read the acknowledge bit */
I2C_SDA_HIGH;
osDelay(I2C_DELAY);
I2C_SCL_HIGH;
osDelay(I2C_DELAY);
uint8_t usAck = I2C_SDA_READ; /* Read the acknowledge bit, 0 indicates successful data reception by the device */
I2C_SCL_LOW;
return usAck;
}
uint8_t I2C_ReadByte(uint8_t ack) {
uint8_t i, byte = 0;
for (i = 0; i < 8; i++) {
byte <<= 1;
I2C_SCL_HIGH;
osDelay(I2C_DELAY);
if (I2C_SDA_READ) {
byte |= 0x01;
}
I2C_SCL_LOW;
osDelay(I2C_DELAY);
}
/* Send the acknowledge bit */
if (ack) {
I2C_SDA_LOW;
} else {
I2C_SDA_HIGH;
}
osDelay(I2C_DELAY);
I2C_SCL_HIGH;
osDelay(I2C_DELAY);
I2C_SCL_LOW;
return byte;
}
/* Initialize the I2C bus */
void I2C_Init(void) {
/* Initialize GPIO pins */
GPIO_InitTypeDef GPIO_InitStruct;
__HAL_RCC_GPIOD_CLK_ENABLE();
GPIO_InitStruct.Pin = I2C_SCL_PIN | I2C_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; /* Open-drain output mode */
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStruct);
/* Initialize the I2C bus */
I2C_Stop(); /* Send stop condition */
}
uint8_t I2C_WriteData(uint8_t address, uint8_t reg, uint8_t* data, uint16_t size) {
I2C_Start(); /* Send start condition */
uint8_t usAck = I2C_WriteByte(address << 1); /* Send device address and write flag */
if (usAck) {
I2C_Stop(); /* Send stop condition */
return usAck; /* Return error */
}
/* Send register address */
usAck = I2C_WriteByte(reg);
if (usAck) {
I2C_Stop(); /* Send stop condition */
return usAck; /* Return error */
}
for (uint16_t i = 0; i < size; i++) { /* Loop to send data */
usAck = I2C_WriteByte(data[i]); /* Send data */
if (usAck) {
I2C_Stop(); /* Send stop condition */
return usAck; /* Return error */
}
}
I2C_Stop(); /* Send stop condition */
return 0; /* Return no error */
}
uint8_t I2C_ReadData(uint8_t address, uint8_t reg, uint8_t* data, uint16_t size) {
I2C_Start(); /* Send start condition */
uint8_t usAck = I2C_WriteByte(address << 1); /* Send device address and write flag */
if (usAck) {
I2C_Stop(); /* Send stop condition */
return usAck; /* Return error */
}
/* Send register address */
usAck = I2C_WriteByte(reg);
if (usAck) {
I2C_Stop(); /* Send stop condition */
return usAck; /* Return error */
}
I2C_Start(); /* Send start condition again */
/* Send device address and read flag again */
usAck = I2C_WriteByte((address << 1) | 0x01);
if (usAck) {
I2C_Stop(); /* Send stop condition */
return usAck; /* Return error */
}
for (uint16_t i = 0; i < size; i++) { /* Loop to read data */
data[i] = I2C_ReadByte(i < size - 1); /* Read data */
/* Check if it's the last byte, if so, exit the loop */
if (i == size - 1) {
break;
}
}
I2C_Stop(); /* Send stop condition */
return 0; /* Return no error */
}
BspI2C.h
/*
BspI2C.h
Implementation File for STM32 I2C Module
*/
/*
Modification History
--------------------
01a 27Feb24 Jasper Created
*/
#ifndef __BSP_I2C_H__
#define __BSP_I2C_H__
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <stm32f1xx_hal.h>
#define I2C_DELAY 1
#define I2C_GPIO_PORT GPIOD
#define I2C_SCL_PIN GPIO_PIN_12
#define I2C_SDA_PIN GPIO_PIN_13
// 定义I2C的时钟和数据线控制宏
#define I2C_SCL_HIGH HAL_GPIO_WritePin(I2C_GPIO_PORT, I2C_SCL_PIN, GPIO_PIN_SET)
#define I2C_SCL_LOW HAL_GPIO_WritePin(I2C_GPIO_PORT, I2C_SCL_PIN, GPIO_PIN_RESET)
#define I2C_SDA_HIGH HAL_GPIO_WritePin(I2C_GPIO_PORT, I2C_SDA_PIN, GPIO_PIN_SET)
#define I2C_SDA_LOW HAL_GPIO_WritePin(I2C_GPIO_PORT, I2C_SDA_PIN, GPIO_PIN_RESET)
#define I2C_SDA_READ HAL_GPIO_ReadPin(I2C_GPIO_PORT, I2C_SDA_PIN)
#if 0
#define HIGH (uint32_t)1
#define LOW (uint32_t)0
#define SCL_OUTPUT(x) if(x == 0) HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SCL_PIN,GPIO_PIN_RESET); else HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SCL_PIN,GPIO_PIN_SET);
#define SDA_OUTPUT(x) if(x == 0) HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SDA_PIN,GPIO_PIN_RESET); else HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SDA_PIN,GPIO_PIN_SET);
#define SDA_READ() HAL_GPIO_ReadPin(I2C_GPIO_PORT,I2C_SDA_PIN)
#endif
void I2C_Init(void);
// 写入数据到设备
// 写入数据到设备
uint8_t I2C_WriteData(uint8_t address, uint8_t reg, uint8_t* data, uint16_t size);
// 从设备读取数据
uint8_t I2C_ReadData(uint8_t address, uint8_t reg, uint8_t* data, uint16_t size);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __BSP_SPI_H__ */