一.I2C通讯
1.I2C通讯简介
I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线;
两根通信线:SCL(Serial Clock)、SDA(Serial Data);
同步,半双工,带数据应答;
支持总线挂载多设备(一主多从、多主多从)
2.硬件电路
所有I2C设备的SCL连在一起,SDA连在一起;
设备的SCL和SDA均要配置成开漏输出模式;
SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右
3.I2C时序基本单元
I2C总线处于空闲状态时,默认情况下SCL和SDA上拉电阻拉高高电平,SCL和SDA先均处于高电平状态;
起始条件:SCL高电平期间,SDA从高电平切换到低电平;
终止条件:SCL高电平期间,SDA从低电平切换到高电平
起始条件和终止条件都是由主机产生的;
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节;
主机如果发送0,就拉低SDA到低电平;
主机如果发送1,就有上拉电阻拉高到高电平;
SCL高电平期间(如下图B7位置),从机读取SDA;
SCL低电平期间,主机把数据放在SDA线上;数据放完之后,主机再次放手SCL拉高电电平,从机读取SDA线上的数据,一次运行八次,读到一个字节的数据;
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
接收与发送的区别:
发送:SCL低电平,主机将数据放在SDA线上,SCL高电平,从机读取SDA线上的数据;
接收:SCL低电平,从机将数据放在SDA线上,SCL高电平,主机读取SDA线上的数据;
发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
4.I2C外设简介
STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担
支持多主机模型
支持7位/10位地址模式
支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)
支持DMA
STM32F103C8T6 硬件I2C资源:I2C1、I2C2
5.I2C框图
二.I2C程序标准库(此程序以MPU6065模块演示)
1.软件I2C程序
MyI2C.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
void MyI2C_W_SCL(uint8_t BitValue) //写SCL引脚反转 直接调用这个函数,传参给1或0就可释放或拉低SCL端口
{
GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue); //bitactiion是一个枚举类型的变量,表示这个位是高电平还是低电平, 将uint8_t 强转BitValue(0或1)
Delay_us(10); //防止一些高性能单片机端口反转速度快,丛机速度跟不上 ,引脚延时10us
}
void MyI2C_W_SDA(uint8_t BitValue) //写SDA引脚反转 直接调用这个函数,传参给1或0就可释放或拉低SDA端口
{
GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);
Delay_us(10); //防止一些高性能单片机端口反转速度快,丛机速度跟不上 ,引脚延时10us
}
uint8_t MyI2C_R_SDA(void) //读SDA引脚
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11); //读PB11引脚的状态
Delay_us(10);
return BitValue; //将读到的参数返回 SDA线的电平
}
void MyI2C_Init(void) //软件I2C不需要库函数,
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //软件I2C需要将端口配置开漏输出模式,开漏输出模式仍可以输入,输入时,端口输出1,再读取数据寄存器
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11); //将PA12和PA13置位高电平,SCL和SDA高电平空闲状态
}
//配置I2C 6个时序基本单元
//1.起始条件
void MyI2C_Start(void) //起始条件函数:先拉低SDA,再拉低SCL,就能触发起始条件
{
MyI2C_W_SDA(1); //先释放SDA引脚,输出1 ,高电平空闲
MyI2C_W_SCL(1); //释放SCL引脚,输出1 ,高电平空闲
MyI2C_W_SDA(0); //先拉低SDA引脚,输出0 ,低电平有效
MyI2C_W_SCL(0); //再拉低SCL引脚,输出0 ,低电平有效
}
//2.终止条件
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0); //先拉低SDA引脚,输出0 ,低电平有效
MyI2C_W_SCL(1); //然后释放SCL引脚,输出1 ,高电平空闲
MyI2C_W_SDA(1); //再释放SDA引脚,输出1 ,高电平空闲
}
//3.发送一个字节
void MyI2C_SendByte(uint8_t Byte)
{
uint8_t i;
for(i = 0; i < 8; i++) //循环八次, (0x80 >> i)),没循环一次,右移一次,从字节的最高位一直到最后一位
{
MyI2C_W_SDA(Byte & (0x80 >> i)); //用发送的字节Byte &(与)0x80(1000 0000),看字节高位,如果是1,SDA高电平,如果是0 SDA低电平
MyI2C_W_SCL(1); //然后释放SCL引脚,输出1 ,高电平空闲 释放SCL后,丛机立刻把放在SDA的数据读走
MyI2C_W_SCL(0); //再拉低SCL引脚,输出0 ,低电平有效 可以继续放下一个数据
}
}
//4.接收一个字节
uint8_t MyI2C_ReceiveByte(void)
{
uint8_t Byte = 0x00;
uint8_t i ;
MyI2C_W_SDA(1); //主机释放SDA,丛机把数据放在SDA上;
for(i = 0; i < 8; i++) //循环8次,读八次
{
MyI2C_W_SCL(1); //主机释放SCL,主机读取数据;
if (MyI2C_R_SDA() == 1)
{
Byte |= (0x80 >> i); //如果读到SDA为1,把Byte的最高位 置1 右移八次
}
MyI2C_W_SCL(0);
}
return Byte;
}
//5.发送应答
void MyI2C_SendAck(uint8_t AckByte)
{
MyI2C_W_SDA(AckByte ); //主机把 AckByte放到SDA上
MyI2C_W_SCL(1); //然后释放SCL引脚,输出1 ,高电平空闲 释放SCL后,丛机立刻把放在SDA的数据读
MyI2C_W_SCL(0); //再拉低SCL引脚,输出0 ,低电平有效 可以继续放下一个数据
}
//6.接收应答
uint8_t MyI2C_ReceiveAck(void)
{
uint8_t AckByte ;
MyI2C_W_SDA(1); //主机释放SDA,丛机把数据放在SDA上;
MyI2C_W_SCL(1); //主机释放SCL,主机读取数据;
AckByte = MyI2C_R_SDA();
MyI2C_W_SCL(0);
return AckByte;
}
MPU6050.C
#include "stm32f10x.h" // Device header
#include "MyI2C.h"
#include "MPU6050_Reg.h"
#define MPU6050_ADDRESS 0XD0 //替换丛机地址0XD0
void MPU6050_WriteReg(uint8_t RegAddress , uint8_t Data) //指定地址写寄存器 参数1:8位寄存器地址 参数2:8位数据
{
MyI2C_Start(); //起始时序
MyI2C_SendByte(MPU6050_ADDRESS); //发送字节:丛机的地址+读写位
MyI2C_ReceiveAck() ; //接收应答 :返回一个应答位
MyI2C_SendByte(RegAddress); //发送字节 :指定寄存器地址
MyI2C_ReceiveAck() ; //接收应答 :返回一个应答位
MyI2C_SendByte(Data); //发送字节 :指定寄存器下的数据
MyI2C_ReceiveAck() ; //接收应答 :返回一个应答位
MyI2C_Stop();
}
uint8_t MPU6050_ReadReg(uint8_t RegAddress) //读丛机寄存器;参数1;指定读的地址
{
uint8_t Data;
MyI2C_Start(); //起始时序
MyI2C_SendByte(MPU6050_ADDRESS); //发送字节:丛机的地址+读写位
MyI2C_ReceiveAck() ; //接收应答 :返回一个应答位
MyI2C_SendByte(RegAddress); //发送字节 :指定寄存器地址
MyI2C_ReceiveAck() ; //接收应答 :返回一个应答位
MyI2C_Start(); //起始时序
MyI2C_SendByte(MPU6050_ADDRESS | 0X01); //发送字节:丛机的地址+读写位
MyI2C_ReceiveAck() ; //接收应答 :返回一个应答位
Data = MyI2C_ReceiveByte(); //接收一个字节 存放在Data
MyI2C_SendAck(1); //0给丛机应答,1不给应答
MyI2C_Stop();
return Data;
}
void MPU6050_Init(void)
{
MyI2C_Init();
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) //读取设备ID号码
{
return MPU6050_ReadReg(0x75);
}
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;
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
int16_t AX,AY,AZ,GX,GY,GZ;
uint8_t ID ;
int main()
{
OLED_Init();
MPU6050_Init();
uint8_t ID = MPU6050_GetID();
OLED_ShowString(1,1,"ID:");
while(1)
{
MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
OLED_ShowSignedNum(2,1,AX,5);
OLED_ShowSignedNum(3,1,AY,5);
OLED_ShowSignedNum(4,1,AZ,5);
OLED_ShowSignedNum(2,8,GX,5);
OLED_ShowSignedNum(3,8,GY,5);
OLED_ShowSignedNum(4,8,GZ,5);
OLED_ShowHexNum(1,4,ID,2);
}
}
2.硬件I2C代码
MPU6050.c
#include "stm32f10x.h" // Device header
#include "MPU6050_Reg.h"
#define MPU6050_ADDRESS 0XD0 //替换丛机地址0XD0
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t Timeout;
Timeout = 10000;
while (I2C_CheckEvent(I2C2, I2C_EVENT) != SUCCESS)//起始条件发送完成后,会产生一个标志位: EV5S事件
{
Timeout--;
if(Timeout == 0)
{
break;
}
}
}
void MPU6050_WriteReg(uint8_t RegAddress , uint8_t Data) //指定地址写寄存器
{
//1.起始条件
I2C_GenerateSTART(I2C2,ENABLE); //开启起始条件
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//起始条件发送完成后,会产生一个标志位: EV5S事件
// 返回值SUCCESS: Last event is equal to the I2C_EVENT
//表示最后一次事件等于我们指定的事件
//while循环,等待事件发生,完成后跳出循环
//2.发送从机地址
I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter); //向DR寄存器写一个七位字节:即从机地址
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED );//当地址发送出去后,接收到应答位后,会产生 EV6事件:
//3.发送数据
I2C_SendData(I2C2,RegAddress);//直接写入DR,发送一个字节的数据(函数传参)
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING );//等待事件完成, EV8 :
//EV8时间完成后,继续写入下一个数据
I2C_SendData(I2C2,Data);//直接写入DR,发送一个字节的数据(函数传参)
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED );//等待事件完成, EV8-2:结束发送 :最后一个字节
//4.终止条件
I2C_GenerateSTOP(I2C2,ENABLE); //开启终止时序
}
uint8_t MPU6050_ReadReg(uint8_t RegAddress) //读丛机寄存器;参数1;指定读的地址
{
uint8_t Data;
//1.起始条件
I2C_GenerateSTART(I2C2,ENABLE); //开启起始条件
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) ;//起始条件发送完成后,会产生一个标志位: EV5S事件
// 返回值SUCCESS: Last event is equal to the I2C_EVENT
//表示最后一次事件等于我们指定的事件
//while循环,等待事件发生,完成后跳出循环
//2.发送从机地址
I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter); //向DR寄存器写一个七位字节:即从机地址
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) ;//当地址发送出去后,接收到应答位后,会产生 EV6事件:
//3.发送数据
I2C_SendData(I2C2,RegAddress);//直接写入DR,发送一个字节的数据(函数传参)
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED ) ;//等待事件完成, EV8 :
//4.生成重复起始条件
I2C_GenerateSTART(I2C2,ENABLE); //重复开启起始条件
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//重复起始条件发送完成后,会产生一个标志位: EV5S事件
//5.发送从机地址
I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Receiver); //向DR寄存器读一个七位字节:即从机地址
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ) ;//当地址接收到后,接收到应答位后,会产生 EV6事件
//需要接收多个字节将下列代码用FOR循环
//6.配置ACK位
I2C_AcknowledgeConfig(I2C2,DISABLE); //不给应答
//7.配置停止位
I2C_GenerateSTOP(I2C2,ENABLE);
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED);//EV7时间产生后,一个字节的数据就在DR中
//8.读取DR寄存器
Data = I2C_ReceiveData(I2C2); //将返回的DR内的值存放Data
//9.把ACK再置一
I2C_AcknowledgeConfig(I2C2,ENABLE); //默认恢复ACK应答
return Data;
}
void MPU6050_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE); //开启RCC I2C时钟;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启RCC GPIOB时钟;
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体,变量名: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; //选择速度50MHZ;
GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化结构体
//初始化I2C外设
I2C_InitTypeDef I2C_InitStruct; //定义I2C结构体
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; //应答位配置,是否应答
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //STM32做从机相应几位地址
I2C_InitStruct.I2C_ClockSpeed = 50000; //配置SCL的时钟频率 最大400Khz;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; //时钟占空比,只有在时钟频率大于100khz,此参数才有效
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C ; //模式:I2C
I2C_InitStruct.I2C_OwnAddress1 = 0x00; //STM32做从机的地址
I2C_Init(I2C2, &I2C_InitStruct); //初始化结构体
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) //读取设备ID号码
{
return MPU6050_ReadReg(0x75);
}
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;
}
MPU6050.h
#ifndef __MPU6050_H
#define __MPU6050_H
void MPU6050_WriteReg(uint8_t RegAddress , uint8_t Data) ;
uint8_t MPU6050_ReadReg(uint8_t RegAddress) ;
void MPU6050_Init(void);
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY,int16_t *GyroZ);
uint8_t MPU6050_GetID(void) ;
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
int16_t AX,AY,AZ,GX,GY,GZ;
uint8_t ID ;
int main()
{
OLED_Init();
MPU6050_Init();
uint8_t ID = MPU6050_GetID();
OLED_ShowString(1,1,"ID:");
while(1)
{
MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
OLED_ShowSignedNum(2,1,AX,5);
OLED_ShowSignedNum(3,1,AY,5);
OLED_ShowSignedNum(4,1,AZ,5);
OLED_ShowSignedNum(2,8,GX,5);
OLED_ShowSignedNum(3,8,GY,5);
OLED_ShowSignedNum(4,8,GZ,5);
OLED_ShowHexNum(1,4,ID,2);
}
}
三.HAL库+CubeMX代码
1.设置RCC时钟
2.设置Debug
3.设置时钟频率
4.配置I2C(此工程中,仅使用主模式)
Master features 主模式特性
I2C Speed Mode: IIC模式设置 快速模式和标准模式。实际上也就是速率的选择。
I2C Clock Speed:I2C传输速率,默认为100KHz
Slave features 从模式特性
Clock No Stretch Mode: 时钟没有扩展模式
IIC时钟拉伸(Clock stretching) - -clock stretching通过将SCL线拉低来暂停一个传输.直到释放SCL线为高电平,传输才继续进行.clock stretching是可选的,实际上大多数从设备不包括SCL驱动,所以它们不能stretch时钟.
Primary Address Length selection: 从设备地址长度 设置从设备的地址是7bit还是10bit 大部分为7bit
Dual Address Acknowledged: 双地址确认
Primary slave address: 从设备初始地址
5.配置USART串口,将读到数据打印出来
串口使用默认配置
6.生成文件
设置好路径后直接生成文件
7.代码部分
OLED显示驱动部分和Printf部分代码不在展示,有需要可留言联系我;
MPU6050.h
#ifndef __MPU6050SD_H
#define __MPU6050SD_H
#include "main.h"
//============= MPU6050 内部寄存器地址 =============//
#define SlaveAddress 0x68 //从机设备地址,AD0接地
#define SMPLRT_DIV 0x19 //陀螺仪采样频率,典型值:0x07(125HZ)
#define CONFIG 0x1A //低通滤波器,典型值0x06(5HZ)
#define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值 0x18 (不自检,2000 deg/s)
#define ACCEL_CONFIG 0x1C //加速计自检,测量范围及高通滤波频率,典型值 0x01(不自检 2g 5HZ)
#define FIFO_EN 0x23 //各功能的 FIFO 使能或失能
#define INT_PIN_CFG 0x37 //中断,旁路设置
#define INT_ENABLE 0x38 //中断使能寄存器
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define USER_CTRL 0x6A //用户控制寄存器
#define PWR_MGMT1 0x6B //电源管理1,典型值:0x01(正常启动)
#define PWR_MGMT2 0x6C //电源管理2,设置是正常工作模式还是唤醒模式
#define WHO_AM_I 0x75 //MPU6050 的 IIC 地址寄存器,只读
extern short Gyrox,Gyroy,Gyroz;
extern short Accelx,Accely,Accelz;
void MPU6050_Config(void);
uint8_t Get_WHOAMI(void);
float MPU6050_Temperature(void);
void MPU6050_Gyro(void);
void MPU6050_Accel(void);
void I2C_WriteByteToSlave(uint8_t mpu_add,uint8_t reg_add,uint8_t reg_data);
#endif
MPU6050.c
#include "main.h"
#include "MPU6050.h"
#include "i2c.h"
short Gyrox,Gyroy,Gyroz;
short Accelx,Accely,Accelz;
void I2C_WriteByteToSlave(uint8_t mpu_add,uint8_t reg_add,uint8_t reg_data)
{
uint8_t sendbuf[2];
sendbuf[0]=reg_add;
sendbuf[1]=reg_data;
HAL_I2C_Master_Transmit(&hi2c2,mpu_add,sendbuf,2,10);
}
//=============初始化 MPU6050,解除睡眠模式 NO1=============//
void MPU6050_Config(void)
{
I2C_WriteByteToSlave(SlaveAddress,PWR_MGMT1,0x80); //启动 MPU6050
HAL_Delay(100); //等待 MPU6050复位完毕
I2C_WriteByteToSlave(SlaveAddress,PWR_MGMT1,0x00); //唤醒 MPU6050
I2C_WriteByteToSlave(SlaveAddress,GYRO_CONFIG,0x18); //设置陀螺仪量程为 2000 deg/s
I2C_WriteByteToSlave(SlaveAddress,ACCEL_CONFIG,0x00); //设置加速度传感器量程为 2g
I2C_WriteByteToSlave(SlaveAddress,SMPLRT_DIV,0x13); //设置采样率的分频值为19,此时采样率为15HZ
I2C_WriteByteToSlave(SlaveAddress,CONFIG,0x04); //设置数字低通滤波器,滤波带宽为20HZ
I2C_WriteByteToSlave(SlaveAddress,USER_CTRL,0x00); //关闭所有FIFO,并且关闭 I2C 主模式
I2C_WriteByteToSlave(SlaveAddress,FIFO_EN,0x00); //关闭所有FIFO
I2C_WriteByteToSlave(SlaveAddress,INT_PIN_CFG,0x80); //设置INT引脚(低电平有效,推挽输出)
I2C_WriteByteToSlave(SlaveAddress,INT_ENABLE,0x00); //关闭所有中断
I2C_WriteByteToSlave(SlaveAddress,PWR_MGMT1,0x01); //使用X轴的PLL时钟作为工作时钟
I2C_WriteByteToSlave(SlaveAddress,PWR_MGMT2,0x00); //设置陀螺仪和加速度计处于正常工作模式
}
//=============== 获取IIC地址 ================//
uint8_t Get_WHOAMI(void)
{
uint8_t buf[1];
HAL_I2C_Mem_Read(&hi2c2,SlaveAddress,WHO_AM_I,I2C_MEMADD_SIZE_8BIT,buf,1,50);
return *buf;
}
//=========获取 MPU6050 芯片温度 =========//
float MPU6050_Temperature(void)
{
uint8_t temp[2];
short value;
HAL_I2C_Mem_Read(&hi2c2,SlaveAddress,TEMP_OUT_H,I2C_MEMADD_SIZE_8BIT,temp,2,50);
value = (short) ((temp[0]<<8)|temp[1]);
return (36.53 + (value/340.0));
}
//=========获取 MPU6050 陀螺仪数据 =========//
void MPU6050_Gyro(void)
{
uint8_t temp[6];
HAL_I2C_Mem_Read(&hi2c2,SlaveAddress,GYRO_XOUT_H,I2C_MEMADD_SIZE_8BIT,temp,6,50);
Gyrox = (short) ((temp[0]<<8)|temp[1]);
Gyroy = (short) ((temp[2]<<8)|temp[3]);
Gyroz = (short) ((temp[4]<<8)|temp[5]);
}
//=========获取 MPU6050 加速计数据 =========//
void MPU6050_Accel(void)
{
uint8_t temp[6];
HAL_I2C_Mem_Read(&hi2c2,SlaveAddress,ACCEL_XOUT_H,I2C_MEMADD_SIZE_8BIT,temp,6,50);
Accelx = (short) ((temp[0]<<8)|temp[1]);
Accely = (short) ((temp[2]<<8)|temp[3]);
Accelz = (short) ((temp[4]<<8)|temp[5]);
}
main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "OLED.h"
#include "MPU6050.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
float temp;
uint8_t ID;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C2_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
MPU6050_Config();
OLED_Init();
OLED_ShowChinese(0,0,"你好,世界。");
OLED_ShowChinese(0,17,"你好,世界。");
OLED_Update();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
temp = MPU6050_Temperature();
MPU6050_Gyro();
MPU6050_Accel();
ID = Get_WHOAMI();
printf("MPU6050实时数据\r\n");
//printf("\r\n");
//HAL_Delay(100);
printf("MPU6050's temp is %f \r\n",temp);
//printf("\r\n");
//HAL_Delay(100);
printf("MPU6050's ID is %x \r\n",ID);
// printf("\r\n");
printf("MPU6050's Gyrox is %d \r\n",Gyrox);
printf("MPU6050's Gyroy is %d \r\n",Gyroy);
printf("MPU6050's Gyroz is %d \r\n",Gyroz);
printf("MPU6050's Accelx is %d \r\n",Accelx);
printf("MPU6050's Accely is %d \r\n",Accely);
printf("MPU6050's Accelz is %d \r\n",Accelz);
printf("--------------------------------\r\n");
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
演示效果文章来源:https://www.toymoban.com/news/detail-812880.html
文章来源地址https://www.toymoban.com/news/detail-812880.html
到了这里,关于STM32 I2C通讯+MPU6050通讯演示的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!