STM32的I2C外设:
可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发,数据收发等功能。
支持多主机模型,支持7/10位地址,支持DMA
STM32F103C8T6的I2C资源:I2C1,I2C2。
引脚分配:
PB10:I2C2_SCL PB11:I2C2_SDA
PB6:I2C1_SCL PB7:I2C2_SDA
操作顺序:
1. 在I2C_CR2寄存器中设定该模块的输入时钟以产生正确的时序
2.配置时钟控制寄存器
3. 配置上升时间寄存器
4.编程I2C_CR1寄存器启动外设
5. 置I2C_CR1寄存器中的START位为1,产生起始条件
代码实现:
#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);//开始时序
//等待EV5事件(可参考上文图245);等不到(SUCCESS)就一直空循环,等到就跳出循环,执行下一步;
I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter);//发送从机地址(点名从机)
//I2C2,从机地址,读写位置0(发送);
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//等待EV6事件;
I2C_SendData(I2C2,RegAddress);//发送寄存器的地址(点名从机的寄存器)
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING);//等待EV8事件;
I2C_SendData(I2C2,Data);
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);
//连续发送字节时,等待EV8事件,当发送最后一个字节后,应等待EV8_2事件;
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);
//等待EV5事件(可参考上文图245);等不到(SUCCESS)就一直空循环,等到就跳出循环,执行下一步;
I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter);//发送从机地址(点名从机)
//I2C2,从机地址,读写位置0(发送);
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//等待EV6事件;
I2C_SendData(I2C2,RegAddress);//发送寄存器的地址(点名从机的寄存器)
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待EV8_2事件(等待发送完成再产生重复起始条件);
I2C_GenerateSTART(I2C2,ENABLE);//开始时序
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//等待EV5事件
I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Receiver);//接收,读写位置1(接收)
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);//等待EV6事件;
I2C_AcknowledgeConfig(I2C2,DISABLE);//不给应答,ACK置0;
I2C_GenerateSTOP(I2C2,ENABLE);//停止时序
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED);//等待EV7事件;
Data = I2C_ReceiveData(I2C2);
I2C_AcknowledgeConfig(I2C2,ENABLE);//ACK置1,方便产生下一个开始时序;
return Data;
}
void MPU6050_Init(void)//
{
// MyI2C_Init();初始化
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);
//I2C2是APB1的外设,所以用APB1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//GPIO是APB2的外设,所以用APB2
GPIO_InitTypeDef GPIO_InitStructure;//初始化GPIO
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;//初始化I2C2
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//是否给应答
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//响应7位地址
I2C_InitStructure.I2C_ClockSpeed = 50000;//时钟频率:50KHZ
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
//占空比 低:高 = 2:1;
//因为I2C总线采用上拉电阻,是弱上拉,弹回高电平速度比拉低电平速度慢,所以要给低电平更多资源
//让数据有足够的时间放到SDA总线上
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;//STM32作从机时的地址,暂时不用
I2C_Init(I2C2,&I2C_InitStructure);
I2C_Cmd(I2C2,ENABLE);//使能I2C2;
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_ReadID(void)//获取ID号
{
return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}
void MPU6050_ReadData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{//获取数据(6个int16_t的数据)
*AccX = (MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H) << 8)
| MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
*AccY = (MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H) << 8)
| MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
*AccZ = (MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H) << 8)
| MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
*GyroX = (MPU6050_ReadReg(MPU6050_GYRO_XOUT_H) << 8)
| MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
*GyroY = (MPU6050_ReadReg(MPU6050_GYRO_YOUT_H) << 8)
| MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
*GyroZ = (MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H) << 8)
| MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
}