STM32-i²c

STM32 I²C 通信协议介绍及项目操作

1. I²C 协议简介

I²C(Inter-Integrated Circuit,集成电路间通信)是一种广泛应用于微控制器和各种外部设备之间的串行通信协议。它由 Philips(现 NXP)公司开发,具有多种优点,如简化的硬件连接、支持多个设备(最多 127 个设备共享同一总线)等。I²C 是一种双线制协议,通常使用两根信号线:SCL(时钟线)SDA(数据线)。支持一主多从和多主多从的模式,主机与从机配合实现数据的相互交互.

STM32-i²c_第1张图片

通信原理:SCL时钟控制线,高电平,SDA数据有效,低电平SDA数据无效可进行电平切换,其次主机从从机读取数据时,主机将SDA控制权转交给从机,在产生起始信号时产生EV5事件,SR1寄存器中SB位置1表示起始信号已经发送,SDA在SCL高电平期间寻转变成低电平,主机向从机发送寻址地址同时发送传输数据方向时产生EV6以及EV8事件,SR1寄存器中ADDR位及TXE位被置1表示地址已经发送,等待从机应答,从机再向数据写入一个字节八位的数据,然后可以多次发送相同类型的数据,当主机不想读取数据时,发送非应答信号产生EV8_2事件,SR1寄存器TxE位以及BTF位被置1,进入停止位,主机读取数据结束,当主机向从机写入数据时,从机将SDA控制权转交给主机,主机发送起始信号时产生EV5事件SR1寄存器中的SB位置1,表示起始信号已经发送,,SDA线在SCL高电平期间从高电平切换低电平,同时主机发送从机寻址基地后EV6事件SR1寄存器ADDR位被置1,传输发行位写入数据,等待从机应答,从机应答后,主机向从机发送数据,SCL高低电平切换采样数据,当发送完一个字节数据八位后是否继续发送等待从机应答也就是SDA在SCL转向高低电平时的切换状态产生EV7事件SR1寄存器中的RxNE位被置1表示数据接受寄存器非空,,如果不需要在接收数据,就产生非应答信号,然后进入停止位结束数据传输..
1.1 I²C 协议特点
  • 双线通信:I²C 仅使用两根线:时钟线(SCL)和数据线(SDA)。
  • 支持多主机和从机:I²C 支持多主机设备和多个从设备在同一总线上进行通信。
  • 多速率支持:I²C 支持不同的通信速率,如标准模式(100kbps)、快速模式(400kbps)和高速模式(3.4Mbps)。
  • 地址模式:每个 I²C 从设备有一个唯一的 7 位或 10 位地址。
1.2 I²C 通信的基本操作
  • 启动信号(START):由主设备发出,用于开始通信。
  • 停止信号(STOP):由主设备发出,表示通信结束。
  • 应答信号(ACK):每接收到一个字节,接收方发送一个 ACK 信号,表示接收到数据。如果没有接收到数据,则发送 NACK(非应答信号)。
1.3 I²C 数据帧结构
  • 起始信号(Start Condition):主设备发送一个特定信号标志着通信开始。
  • 设备地址(Device Address):7 位或 10 位的设备地址,主设备通过地址来选择特定的从设备。
  • 数据字节(Data Byte):每个字节的数据在 SDA 线上传输。
  • 应答位(ACK/NACK):每接收一个字节后,接收方发送一个 ACK 位,表示确认接收。

2. STM32 I²C 外设介绍

STM32 微控制器提供了 I²C 外设,通过它可以方便地与支持 I²C 协议的外部设备(如 EEPROM、传感器、LCD 显示模块等)进行通信。STM32 的 I²C 外设具有以下特点:

  • 支持多个主机和从设备:允许多个主设备和从设备在同一总线上进行数据交换。
  • 低功耗:适合嵌入式设备和传感器应用。
  • 灵活性强:支持标准模式、快速模式和高速模式的通信。

3. STM32 I²C 通信配置

3.1 硬件连接

I²C 通信需要以下硬件连接:

  • SDA(数据线):数据线,通过这根线进行数据交换。
  • SCL(时钟线):时钟线,由主设备生成时钟信号。
  • VCC 和 GND:为设备提供电源和地线。

假设使用 STM32F4 系列,常见的引脚连接为:

  • SCL(PB6):STM32 的时钟信号线
  • SDA(PB7):STM32 的数据线
3.2 CubeMX 配置

在 STM32CubeMX 中配置 I²C 外设:

  1. 启用 I²C 外设:选择要使用的 I²C 通道(如 I²C1 或 I²C2)。
  2. 引脚配置:将 SDA 和 SCL 引脚配置为 I²C 模式。
  3. 配置 I²C 参数:选择波特率、工作模式(主机或从机)和其他相关设置(如设备地址、时钟频率等)。
  4. 生成代码:生成 STM32 HAL 库代码。
3.3 代码实现

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 自动生成
}

4. 代码解释

  • I2C1_Init:该函数初始化 I²C1 外设,并配置主机模式、时钟频率(100kHz)和其他相关参数。
  • I2C_Write:向 I²C 从设备写入数据。使用 HAL_I2C_Mem_Write 函数通过指定的寄存器地址发送一个字节的数据。
  • I2C_Read:从 I²C 从设备读取数据。使用 HAL_I2C_Mem_Read 函数从指定的寄存器地址读取一个字节的数据。
  • main:在主函数中,初始化 I²C,向从设备写入数据并读取数据。

5. 调试和测试

  1. 使用 I²C 兼容设备(如 EEPROM、传感器等)与 STM32 进行连接。
  2. 通过串口输出调试信息,确认 I²C 数据传输是否成功。
  3. 使用示波器或逻辑分析仪查看 I²C 总线信号,检查时钟线(SCL)和数据线(SDA)是否正常。

6. 常见问题和解决方案

  • 设备地址不正确:确认从设备的地址是否正确,I²C 地址通常需要左移一位。
  • 数据传输失败:检查时钟频率、引脚配置以及硬件连接是否正确。
  • I²C 总线冲突:如果使用多个主设备,可能会出现总线冲突。确保主设备和从设备的电气连接正确。

7. 扩展应用

  • 温度传感器:使用 I²C 与温度传感器(如 LM75 或 DS18B20)进行通信。
  • EEPROM 存储:使用 I²C 通信与 EEPROM 存

项目:利用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){
		
		}
}



你可能感兴趣的:(stm32,c语言,嵌入式硬件)