【STM32】I2C接口(一主多从)

本篇博客重点在于标准库函数的理解与使用,搭建一个框架便于快速开发

目录

前言

I2C外设简介

IO口初始化 

I2C接口配置

I2C时钟配置

I2C初始化

I2C接口使能 

I2C外设配置框架

主机的发送与接收

主机发送

主机接收 

 I2C例程


前言

I2C协议介绍:【通信协议】I2C总线(一主多从)-CSDN博客

本篇博客学习使用STM32的I2C硬件收发电路生成I2C时序。

I2C外设简介

STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担。

  • 支持多主机功能
  • 支持7位/10位地址模式
  • 支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)
  • 支持DMA
  • 兼容SMBus协议

STM32F103C8T6 硬件I2C资源:I2C1、I2C2

I2C默认工作于从模式。接口在生成起始条件后自动地从从模式切换到主模式;当产生停止信号时,则从主模式切换到从模式。 

IO口初始化 

GPIO的其它参数的理解可以阅读下方博客,这里不再赘述。

【STM32】GPIO和AFIO标准库使用框架_gpio afio-CSDN博客

 根据引脚定义表GPIOB的10和11可分别复用在单片机内的I2C2接口的SCL和SDA线。再由I2C协议,则可配置这两个引脚的GPIO模式为复用开漏输出模式。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);

I2C接口配置

I2C时钟配置

I2C1、I2C2均在APB1总线上

再由RCC时钟树,需要使能I2C外设对应的时钟

RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);

I2C初始化

I2C模式

I2C_Mode_I2C  I2C模式
I2C_Mode_SMBusDeviceSMBus设备
I2C_Mode_SMBusHostSMBus主机
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;

时钟速度 

I2C_ClockSpeed 该参数用来设置时钟频率,这个值不能高于 400KHz

时钟频率(KHz)速度模式
0~100标准
101~400快速
I2C_InitStructure.I2C_ClockSpeed = 50000;//标准速度

 占空比

I2C_DutyCycle描述
I2C_DutyCycle_16_9I2C 快速模式 T_low/T_high = 16/9
I2C_DutyCycle_2I2C 快速模式 T_low/T_high = 2

注意:该参数只有在I2C 工作在快速模式(时钟工作频率高于 100KHz)下才有意义

小于等于100KHz的标准速度下,占空比为固定的1:1

I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//标准速度配置该参数无意义

 单片机地址

I2C_AcknowledgedAddres 定义了应答 7 位地址还是 10 位地址

I2C_OwnAddress1 该参数用来设置单片机第一个自身地址,它可以是一个 7 位地址或者一个 10 位地址 

单片机作为从机模式下才需配置,这两个参数,不在本博客学习范围内,就随便配置了

I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;

 应答位使能

发送数据自带接收应答的过程,接收数据自带发送应答的过程,不需要接收与发送应答位就失能

I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;

I2C接口使能 

I2C接口配置的最后调用即可

I2C_Cmd(I2C2, ENABLE);

I2C外设配置框架

RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);

I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_ClockSpeed = 50000;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_Init(I2C2, &I2C_InitStructure);

I2C_Cmd(I2C2, ENABLE);

主机的发送与接收

代码来自江协科技:[10-5] 硬件I2C读写MPU6050_哔哩哔哩_bilibili


/*while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)

while循环等待硬件置事件发送标志位,但硬件I2C有许多事件,就有许多while循环,容易造成堵塞
*/


//超时退出,防止阻塞主循环其他程序
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;
		}
	}
}

主机发送

一定要对着参考手册的寄存器描述看

	I2C_GenerateSTART(I2C2, ENABLE); //使能起始条件生成电路
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//EV5:等待起始条件发送完成


	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);//告诉MPU6050从机,主机要发送数据,自动接收应答位,可使能应答位有关的中断
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//EV6:等待地址发送结束

	//
	I2C_SendData(I2C2, RegAddress);//发送指定数据指定要写的寄存器地址
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);//EV8:移位寄存器数据未发送完,就可以将数据放在数据寄存器等着了,这里等待EV8事件发生即可
   //可用for循环这两句发多个字节
	
	I2C_SendData(I2C2, Data);//发送数据
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);//EV8_2:等待移位寄存器和数据寄存器都为空,数据完成后再发送起始条件
	
    I2C_GenerateSTOP(I2C2, ENABLE);

主机接收 

uint8_t Data;   //存放接收的一个字节

I2C_GenerateSTART(I2C2, ENABLE);
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);

I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);

I2C_SendData(I2C2, RegAddress);
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待数据发送完,再生成起始条件,等待I2C_EVENT_MASTER_BYTE_TRANSMITTING事件也可行

I2C_GenerateSTART(I2C2, ENABLE);        ///使能重复起始条件生成电路,改变数据传输方向
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//EV5:等待重复起始条件发送完成

I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);//告诉MPU6050从机,主机要接收数据
MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);//EV6:等待地址发送结束

//
I2C_AcknowledgeConfig(I2C2, DISABLE);//在从机发完一个字节之后的应答位时序前,失能ACK应答位,只接受一个字节的数据
I2C_GenerateSTOP(I2C2, ENABLE);//根据序列图建议,这里设置STOP请求

MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);//EV7:接收数据寄存器非空
Data = I2C_ReceiveData(I2C2);//接收1个字节
//for循环调用,接收多个字节,只需在最后接收最后一个字节之前,失能应答和使能停止生成电路

I2C_AcknowledgeConfig(I2C2, ENABLE);//使能ACK应答位,恢复初始化设置状态

return Data;

注: 当TxE或BTF位置位时,停止条件应安排在出现EV8_2事件时 

 I2C例程

mpu6050的c文件

#include "stm32f10x.h"                  // Device header
#include "MPU6050_Reg.h"

#define MPU6050_ADDRESS		0xD0

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;
		}
	}
}

void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	I2C_GenerateSTART(I2C2, ENABLE);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
	
	I2C_SendData(I2C2, RegAddress);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
	
	I2C_SendData(I2C2, Data);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
	
	I2C_GenerateSTOP(I2C2, ENABLE);
}

uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	I2C_GenerateSTART(I2C2, ENABLE);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
	
	I2C_SendData(I2C2, RegAddress);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
	
	I2C_GenerateSTART(I2C2, ENABLE);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);
	
	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;
}

void MPU6050_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	I2C_InitTypeDef I2C_InitStructure;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStructure.I2C_ClockSpeed = 50000;
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_InitStructure.I2C_OwnAddress1 = 0x00;
	I2C_Init(I2C2, &I2C_InitStructure);
	
	I2C_Cmd(I2C2, ENABLE);
	
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);
}

uint8_t MPU6050_GetID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}

void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t DataH, DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
	*AccX = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
	*AccY = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	*AccZ = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*GyroX = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	*GyroY = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	*GyroZ = (DataH << 8) | DataL;
}
  • 9
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值