I²C(Inter-Integrated Circuit,集成电路间通信)是一种广泛应用于微控制器和各种外部设备之间的串行通信协议。它由 Philips(现 NXP)公司开发,具有多种优点,如简化的硬件连接、支持多个设备(最多 127 个设备共享同一总线)等。I²C 是一种双线制协议,通常使用两根信号线:SCL(时钟线) 和 SDA(数据线)。支持一主多从和多主多从的模式,主机与从机配合实现数据的相互交互.
STM32 微控制器提供了 I²C 外设,通过它可以方便地与支持 I²C 协议的外部设备(如 EEPROM、传感器、LCD 显示模块等)进行通信。STM32 的 I²C 外设具有以下特点:
I²C 通信需要以下硬件连接:
假设使用 STM32F4 系列,常见的引脚连接为:
在 STM32CubeMX 中配置 I²C 外设:
main.c
#include "main.h"
#include "stm32f4xx_hal.h"
// I²C 句柄声明
I2C_HandleTypeDef hi2c1;
// I²C 初始化函数
void I2C1_Init(void) {
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000; // 设置 I²C 波特率 100kHz
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; // 设置占空比
hi2c1.Init.OwnAddress1 = 0; // 作为主机时地址为 0
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 7 位地址模式
hi2c1.Init.Ack = I2C_ACK_ENABLE; // 启用应答功能
hi2c1.Init.PeripheralMode = I2C_MODE_MASTER; // 主机模式
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; // 禁止时钟拉伸
if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
// 初始化失败,错误处理
Error_Handler();
}
}
// 向 I²C 从设备写入数据
void I2C_Write(uint8_t devAddr, uint8_t regAddr, uint8_t data) {
HAL_I2C_Mem_Write(&hi2c1, devAddr, regAddr, I2C_MEMADD_SIZE_8BIT, &data, 1, HAL_MAX_DELAY);
}
// 从 I²C 从设备读取数据
uint8_t I2C_Read(uint8_t devAddr, uint8_t regAddr) {
uint8_t receivedData = 0;
HAL_I2C_Mem_Read(&hi2c1, devAddr, regAddr, I2C_MEMADD_SIZE_8BIT, &receivedData, 1, HAL_MAX_DELAY);
return receivedData;
}
// 主函数
int main(void) {
HAL_Init();
SystemClock_Config();
// 初始化 I²C1
I2C1_Init();
// 向 I²C 从设备写入数据
uint8_t deviceAddress = 0xA0; // 假设从设备地址为 0xA0
uint8_t registerAddress = 0x01; // 假设寄存器地址为 0x01
uint8_t dataToSend = 0x55; // 要发送的数据
I2C_Write(deviceAddress, registerAddress, dataToSend);
// 从 I²C 从设备读取数据
uint8_t receivedData = I2C_Read(deviceAddress, registerAddress);
// 无限循环
while (1) {
}
}
// 错误处理函数
void Error_Handler(void) {
while (1) {
}
}
// 系统时钟配置函数(STM32CubeMX 自动生成)
void SystemClock_Config(void) {
// 省略时钟配置代码,STM32CubeMX 自动生成
}
HAL_I2C_Mem_Write
函数通过指定的寄存器地址发送一个字节的数据。HAL_I2C_Mem_Read
函数从指定的寄存器地址读取一个字节的数据。项目:利用i²c协议读取或写入MPU6050芯片内寄存器数据
1初始化关于IC协议的GPIO解结构体,结构体成员配置开漏输出,引脚分别对应i²c的SCL和SDA引脚,输出速度任意
2配置开始信号,停止信号,发送数据应答,接受数据应答,发送数据,接受数据六个时序单元函数
3在主函数里面操作时序单元写入MPU6050内部地址,写入数据,或者从内部寄存器中读取数据显示在OLED屏上
iIC通信程序代码
#include "stm32f10x.h" // Device header
#include "IIC.h"
#include "Delay.h"
#define SCL_PIN GPIO_Pin_10
#define SDA_PIN GPIO_Pin_11
#define IIC_PORT GPIOB
void Myiic_W_SCL(uint8_t BitVAlue) //控制SCL函数
{
GPIO_WriteBit(GPIOB, SCL_PIN, (BitAction)BitVAlue);
delay_us(10);
}
void Myiic_W_SDA(uint8_t BitVAlue)//写入SDA函数
{
GPIO_WriteBit(GPIOB, SDA_PIN , (BitAction)BitVAlue);
delay_us(10);
}
uint8_t Myiic_R_SDA(void )//读取SDA函数
{
uint8_t DataValue=GPIO_ReadInputDataBit( IIC_PORT , SDA_PIN );
delay_us(10);
return DataValue;
}
void iic_Init()//初始化关于iIC的GPIO引脚
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE );
GPIO_InitTypeDef iicGPIO_Initsturt;
iicGPIO_Initsturt.GPIO_Mode = GPIO_Mode_Out_OD ;
iicGPIO_Initsturt.GPIO_Speed = GPIO_Speed_50MHz;
iicGPIO_Initsturt.GPIO_Pin = SCL_PIN| SDA_PIN ;
GPIO_Init(IIC_PORT , &iicGPIO_Initsturt);
GPIO_SetBits(IIC_PORT , SCL_PIN | SDA_PIN );
}
void Myiic_Start(void)//开始时序单元
{
Myiic_W_SCL(1);
Myiic_W_SDA(1);
Myiic_W_SDA(0);
Myiic_W_SCL(0);
}
void Myiic_Stop(void)//结束时序单元
{
Myiic_W_SDA(0);
Myiic_W_SCL(1);
Myiic_W_SDA(1);
}
void Myiic_SendData(uint8_t Byte)//发送数据单元
{
uint8_t i;
for(i=0;i<8;i++){
Myiic_W_SDA(Byte&(0x80>>i));
Myiic_W_SCL(1);
Myiic_W_SCL(0);
}
}
uint8_t Myiic_ReceiveData(void)//接受数据单元
{
uint8_t DATA=0x00;
uint8_t i;
Myiic_W_SDA(1);
for(i=0;i<8;i++){
Myiic_W_SCL(1);
if(Myiic_R_SDA()==1)
{
DATA|=(0x80>>i);
}
Myiic_W_SCL(0);
}
return DATA;
}
void SendACk(uint8_t DataValue)//发送数据时序应答
{
Myiic_W_SDA(DataValue);
Myiic_W_SCL(1);
Myiic_W_SCL(0);
}
uint8_t ReceiveAck(void)//接受数据时序应答
{
uint8_t DATA;
Myiic_W_SDA(1);
Myiic_W_SCL(1);
DATA= Myiic_R_SDA();
Myiic_W_SCL(0);
return DATA;
}
//mpu6050代码通信嵌套iIC程序
#include "mpu6050.h"
#include "stm32f10x.h" // Device header
#include "IIC.h"
#define MPU6050_ADDRESS 0xD0
uint8_t mpu6050_WriteReg(uint8_t Regadress,uint8_t Data)//对mpu6050传感器地址寄存器数据写入
{
Myiic_Start();
Myiic_SendData(MPU6050_ADDRESS );
ReceiveAck();
Myiic_SendData(Regadress );
ReceiveAck();
Myiic_SendData(Data);
ReceiveAck();
Myiic_Stop();
}
uint8_t mpu6050_redReg(uint8_t RegADress)//对mpu6050传感器地址寄存器的数据读取
{
uint8_t data;
Myiic_Start();
Myiic_SendData(MPU6050_ADDRESS );
ReceiveAck();
Myiic_SendData(RegADress);
ReceiveAck();
Myiic_Start();
Myiic_SendData(MPU6050_ADDRESS |0x01);
ReceiveAck();
data=Myiic_ReceiveData();
SendACk(1);
Myiic_Stop();
return data;
}
//主函数实现单片机对mpu6050芯片的数据读写
void mpu6050_Init()
{
iic_Init();
}#include "stm32f10x.h"
#ifndef _MPU6050_H_
#define _MPU6050_H_
uint8_t mpu6050_WriteReg(uint8_t Regadress,uint8_t Data);
uint8_t mpu6050_redReg(uint8_t RegADress);
void mpu6050_Init(void);
void mpu6050_Init(void);
#endif
#include "oled.h"
#include "adc.h"
#include "dc.h"
#include "usart.h"
#include "mpu6050.h"
#include "IIC.h"
int main(void){
mpu6050_Init();
OLED_Init();
mpu6050_WriteReg(0x6B,0x00);
mpu6050_WriteReg(0x19,0xAA);
uint8_t IDnumber=mpu6050_redReg(0x19);
OLED_ShowNum (1,1,IDnumber,3);//在OLED屏上显示寄存器的应答信号
while(1){
}
}