STM32软件I2C驱动MPU6050

这篇具有很好参考价值的文章主要介绍了STM32软件I2C驱动MPU6050。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

STM32软件I2C驱动MPU6050

STM32F103C8T6基于Keil MDK标准库

硬件接线

这里没有什么复杂的地方,采用MPU6050的现成模块.模块的SCL接B10,SDA接B11,这里连接了一个OLED显示屏,用于显示获取到的数据.
注意:这里使用的模块自带上拉电阻

STM32软件I2C驱动MPU6050

软件实现

首先在工程目录里创建:

"MyI2C.h"和"MyI2C.c"文件,用于软件驱动I2C.

"MPU6050.h","MPU6050.c"和"MPU6050Reg.h"文件,用于MPU6050的驱动.

  • 在MyI2C.h文件中设置软件I2C的GPIO号,这里采用宏定义的方式:
//设置I2C引脚端口,注意如端口号修改,时钟使能也要修改
#define SCL_PORT    GPIOB
#define SCL_LINE    GPIO_Pin_10
#define SDA_PORT    GPIOB
#define SDA_LINE    GPIO_Pin_11
  • 软件I2C的延迟宏定义
//设置I2C操作延迟(速度)
#define I2C_DELAY    do{Delay_us(10);}while(0);
  • 由于使用库函数的GPIO_WriteBit()函数操作GPIO口电平不够优雅简洁,这里使用了带参宏和函数来对GPIO进行操作,注意:这里的操作使用了延时宏.
//I2C引脚电平写
#define SCL_SET(x)    do{GPIO_WriteBit(SCL_PORT,SCL_LINE,(BitAction)(x)); I2C_DELAY;} \
                      while(0);
#define SDA_SET(x)    do{GPIO_WriteBit(SDA_PORT,SDA_LINE,(BitAction)(x)); I2C_DELAY;} \
                      while(0);
//I2C引脚电平读
uint8_t READ_SDA(void){
    uint8_t val;
    val = GPIO_ReadInputDataBit(SDA_PORT,SDA_LINE);
    I2C_DELAY;
    return val;
}
  • 接下来写软件I2C的初始化代码,这里就是配置GPIO,不像硬件I2C那样麻烦,注意这里GPIO模式要配置成开漏输出(I2C的定义).
void MyI2C_Init(){
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Pin  = SCL_LINE | SDA_LINE;
    GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&GPIO_InitStructure);
}
  • 然后就是I2C的六块拼图:起始,终止,接收一个字节,发送一个字节,接收应答,发送应答

起始和终止信号

STM32软件I2C驱动MPU6050

注意:SDA_SET(1)不是设置SDA或SCL为高电平,而是释放总线(开漏输出特性),然后利用上拉电阻把总线拉高

void MyI2C_Start(){
    //为保证兼容重复开始条件,先释放SDA再释放SCL
    SDA_SET(1);
    SCL_SET(1);
    SDA_SET(0);
    SCL_SET(0);
}
void MyI2C_Stop(){
    SDA_SET(0);
    SCL_SET(1);
    SDA_SET(1);
}

发送一个字节和接收一个字节

STM32软件I2C驱动MPU6050

STM32软件I2C驱动MPU6050

void MyI2C_SendByte(uint8_t byte){
    for(uint8_t i = 0;i < 8;i++){
        SDA_SET(byte & (0x80 >> i));    //SDA写数据,I2C是高位先行
        SCL_SET(1);    SCL_SET(0);            //给SCL一个脉冲,让从机把SDA的数据读走
    }
}

uint8_t MyI2C_ReceiveByte(){
    uint8_t byte = 0x00;
    SDA_SET(1);            //先释放SDA
    for(uint8_t i = 0; i < 8;i++){
        SCL_SET(1);        //设置SCL为高,此时从机把数据放在SDA上
        if(READ_SDA() == 1){byte |= (0x80 >> i);}    //由高到低位读SDA
        SCL_SET(0);        //设置SCL为低,一个时钟结束
    }
    return byte;
}

发送应答和接收应答

STM32软件I2C驱动MPU6050

void MyI2C_SendACK(uint8_t ackbit){
    SDA_SET(ackbit);        //把应答位放在SDA上
    SCL_SET(1);    SCL_SET(0);    //给SCL一个脉冲,让从机读取应答位
}

uint8_t MyI2C_ReceiveACK(){
    uint8_t ackbit;
    SDA_SET(1);        //释放SDA
    SCL_SET(1);        //给SCL一个脉冲,让从机把应答位写到SDA上
    ackbit = READ_SDA();
    SCL_SET(0);
    return ackbit;
}
  • 不要忘记在MyI2C.h头文件中声明
void MyI2C_Init(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendACK(uint8_t ackbit);
uint8_t MyI2C_ReceiveACK(void);
  • 至此,I2C六块拼图全部完成,可以开始写应用层代码了

  • 首先在MPU6050_Reg.h中添加如下宏定义,这样就不用查寄存器表了

#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H

#define    MPU6050_SMPLRT_DIV        0x19
#define    MPU6050_CONFIG            0x1A
#define    MPU6050_GYRO_CONFIG        0x1B
#define    MPU6050_ACCEL_CONFIG    0x1C

#define    MPU6050_ACCEL_XOUT_H    0x3B
#define    MPU6050_ACCEL_XOUT_L    0x3C
#define    MPU6050_ACCEL_YOUT_H    0x3D
#define    MPU6050_ACCEL_YOUT_L    0x3E
#define    MPU6050_ACCEL_ZOUT_H    0x3F
#define    MPU6050_ACCEL_ZOUT_L    0x40
#define    MPU6050_TEMP_OUT_H        0x41
#define    MPU6050_TEMP_OUT_L        0x42
#define    MPU6050_GYRO_XOUT_H        0x43
#define    MPU6050_GYRO_XOUT_L        0x44
#define    MPU6050_GYRO_YOUT_H        0x45
#define    MPU6050_GYRO_YOUT_L        0x46
#define    MPU6050_GYRO_ZOUT_H        0x47
#define    MPU6050_GYRO_ZOUT_L        0x48

#define    MPU6050_PWR_MGMT_1        0x6B
#define    MPU6050_PWR_MGMT_2        0x6C
#define    MPU6050_WHO_AM_I        0x75

#endif

  • 首先在MPU6050.c文件中,添加芯片的I2C的地址号,同样采用宏定义方式.0x68为MPU6050的固有I2C地址,实际发送时,要把I2C的地址左移1位,再在最低位写0表示写时序,写1表示读时序.
#define MPU6050_I2C_ADDR    (0x68)
#define MPU6050_WRITE_ADDR    (((MPU6050_I2C_ADDR) << 1) | 0x00)
#define MPU6050_READ_ADDR    (((MPU6050_I2C_ADDR) << 1) | 0x01)

这是芯片的读寄存器函数

  • 我们要通过这个函数对芯片的寄存器进行读出,具体为什么要这样做,可以参考芯片的读写时序图

STM32软件I2C驱动MPU6050

  • 上图部分解释:
  1. Master/Slave 主机/从机
  2. S开始 ,AD+W地址+写 ,ACK应答
  3. RA寄存器地址 ,AD+R地址+读 ,DATA数据 ,NACK非应答 ,停止P .
  • 我们使用的是单字节读时序,参考上图和下表,时序结构就很清晰了.
uint8_t MPU6050_ReadReg(uint8_t reg_addr){
    uint8_t data;
    MyI2C_Start();  //1
    MyI2C_SendByte(MPU6050_WRITE_ADDR);  //2
    MyI2C_ReceiveACK();  //3
    MyI2C_SendByte(reg_addr);  //4
    MyI2C_ReceiveACK();  //5
    
    MyI2C_Start();  //6
    MyI2C_SendByte(MPU6050_READ_ADDR);  //7
    MyI2C_ReceiveACK();  //8
    data = MyI2C_ReceiveByte();  //9
    MyI2C_SendACK(1);        //NACK 10
    MyI2C_Stop();  //11
    
    return data;
}

这里是芯片的写寄存器函数

STM32软件I2C驱动MPU6050

void MPU6050_WriteReg(uint8_t reg_addr,uint8_t data){
    MyI2C_Start();  //1
    MyI2C_SendByte(MPU6050_WRITE_ADDR);  //2
    MyI2C_ReceiveACK();  //3
    MyI2C_SendByte(reg_addr);  //4
    MyI2C_ReceiveACK();  //5
    MyI2C_SendByte(data);  //6
    MyI2C_ReceiveACK();  //7
    MyI2C_Stop();  //8
}
  • 好了,我们现在有芯片的读/写函数了,众所周知,如果我们能把一个屏幕的任意一个像素点点亮,那么,我们就能对这块屏幕胡作非为了,哈哈.屏幕如此,芯片亦如此.

下面是MPU6050的初始化函数

首先初始化I2C(GPIO初始化),然后配置必要的寄存器,最后动态分配一块内存区域,用于下面的函数返回结构体变量,注意使用malloc函数要在.h文件内包含#include <stdlib.h>这个库文件

void MPU6050_Init(){
    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,0x00);
    MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x00);
    //动态分配一个内存区域
    p = (MPU6050_DATA*)malloc(sizeof(MPU6050_DATA));
}
  • 对上述初始化函数配置的寄存器的解释:

  • MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);

STM32软件I2C驱动MPU6050

我们配置为0b0000 0001,即设备不复位,不睡眠,关闭循环模式,温度传感器使能,时钟选择为内部X轴陀螺仪晶振.

  • 以下为CLKSEL位对应的时钟源选择:

STM32软件I2C驱动MPU6050

  • MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00);

STM32软件I2C驱动MPU6050

这里配置为0b0000 0000,唤醒频率我们不配置(默认1.25Hz),6轴的传感器也不待机.

  • 以下为LP_WAKE_CTRL位对应的唤醒频率:

STM32软件I2C驱动MPU6050

  • MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);

STM32软件I2C驱动MPU6050

采样率分频配置为0x09,低通滤波配置为最大,此时陀螺仪输出速率为1kHz(下文会提及),根据手册公式可得采样率100Hz.

  • 手册给出的采样率计算公式

STM32软件I2C驱动MPU6050

  • MPU6050_WriteReg(MPU6050_CONFIG,0x06);

STM32软件I2C驱动MPU6050

此处配置为低通滤波最大(0b0000 0110),对应输出速率为5Hz

  • 下图为低通滤波器配置表,可见当没有启用DLPF时,陀螺仪输出频率为8kHz,启用DLPF时,输出频率为1kHz.

STM32软件I2C驱动MPU6050

  • MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x00); MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x00);

STM32软件I2C驱动MPU6050

  • 陀螺仪量程配置位对应的量程

STM32软件I2C驱动MPU6050

STM32软件I2C驱动MPU6050

  • 加速度计量程配置为对应的量程

STM32软件I2C驱动MPU6050

我们这里没有配置自检和加速度计高通滤波,我们配置了陀螺仪的量程为最低:±250°/s,加速度计量程也为最低:±2g,以获得最大的测量精度.

  • 至此,MPU6050的初始化完成.

下面是陀螺仪获取数据的函数

这里在MPU6050.h文件里声明了一个结构体变量,用于储存MPU6050的数据信息

typedef struct{
    int16_t AccX,AccY,AccZ;
    int16_t GyroX,GyroY,GyroZ;
    int16_t Temp;
}MPU6050_DATA;

这里获取数据的函数采用返回结构体指针的方式返回多个变量,这样做可以减少模块之间的耦合性,提高程序可移植性.

这里还使用了移位操作把高8位和低8位的数据结合,应该很好理解,这里不再阐述.

MPU6050_DATA* MPU6050_GetData(){
    
    uint16_t dataH,dataL;
    //AccX
    dataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
    dataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
    p->AccX = (dataH << 8)    | dataL;
    //AccY
    dataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
    dataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
    p->AccY = (dataH << 8)    | dataL;
    //AccZ
    dataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
    dataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
    p->AccZ = (dataH << 8)    | dataL;
    //GyroX
    dataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
    dataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
    p->GyroX = (dataH << 8)    | dataL;
    //GyroY
    dataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
    dataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
    p->GyroY = (dataH << 8)    | dataL;
    //GyroZ
    dataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
    dataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
    p->GyroZ = (dataH << 8)    | dataL;
    //TEMP
    dataH = MPU6050_ReadReg(MPU6050_TEMP_OUT_H);
    dataL = MPU6050_ReadReg(MPU6050_TEMP_OUT_L);
    p->Temp = (dataH << 8)    | dataL;
    
    return p;
}

最后在main.c文件内调用功能函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"

int main(void){
    OLED_Init();
    //MPU6050初始化
    MPU6050_Init();
    //创建一个MPU6050_DATA类型的指针变量ptr
    MPU6050_DATA* ptr = NULL;
    //用于保存加速度计的中间值
    float AccX,AccY,AccZ;
    
    while (1){
        //获取MPU6050的数据,返回一个结构体指针给ptr
        ptr = MPU6050_GetData();
        //OLED显示陀螺仪数据,这里没有把原始数据换算成°/s的单位了
        OLED_ShowSignedNum(1,1,ptr->GyroX,5);
        OLED_ShowSignedNum(2,1,ptr->GyroY,5);
        OLED_ShowSignedNum(3,1,ptr->GyroZ,5);
        //把加速度计的数据转换成m/s^2的单位
        AccX = (float)(ptr->AccX) * (float)((float)2 / (float)32767);
        AccY = (float)(ptr->AccY) * (float)((float)2 / (float)32767);
        AccZ = (float)(ptr->AccZ) * (float)((float)2 / (float)32767);
        //在OLED上显示数据,这里的单位是cm/s^2了
        OLED_ShowSignedNum(1,8,(int16_t)(AccX * 9.8 * 100),5);
        OLED_ShowSignedNum(2,8,(int16_t)(AccY * 9.8 * 100),5);
        OLED_ShowSignedNum(3,8,(int16_t)(AccZ * 9.8 * 100),5);
        //显示读取到的温度数据,单位℃
        OLED_ShowSignedNum(4,8,ptr->Temp,5);
    }
}

使用逻辑分析仪抓取的部分波形如图:

STM32软件I2C驱动MPU6050

试验现象如图:

STM32软件I2C驱动MPU6050

此文章是一篇学习笔记,由笔者在学习B站江协科技UP主的STM32入门教程时写下,部分资料和代码来自江协科技.感谢前辈制作这门教程,致敬!

至此,软件I2C读取MPU6050的例程结束,感谢阅读.如果帮助到了你,还请动动手指点个赞,笔者将十分感谢!

如有错误,欢迎指正! 有些地方不太明白,欢迎与我讨论.共同学习,一起进步.

QQ:1583031618

By Sightseer 2023/07/13文章来源地址https://www.toymoban.com/news/detail-554630.html

到了这里,关于STM32软件I2C驱动MPU6050的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32--MPU6050与I2C外设

    在51单片机专栏中,用过I2C通信来进行实现AT24C02的数据存储; 里面介绍的是 利用程序的编程来实现I2C的时序 ,进而实现AT24C02与单片机之间的关系连接; 本章将介绍使用I2C的硬件外设来实现I2C通信,和介绍MPU6050,利用I2C通信实现STM32对MPU6050的控制. I2C通信软件实现程序链接

    2024年02月11日
    浏览(55)
  • STM32 I2C通讯+MPU6050通讯演示

    1.I2C通讯简介 I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线; 两根通信线:SCL(Serial Clock)、SDA(Serial Data); 同步,半双工,带数据应答; 支持总线挂载多设备(一主多从、多主多从) 2.硬件电路 所有I2C设备的SCL连在一起,SDA连在一起; 设备的SCL和SDA均要配置

    2024年01月21日
    浏览(68)
  • STM32学习笔记(十)丨I2C通信(使用I2C实现MPU6050和STM32之间通信)

    ​  本次课程采用单片机型号为STM32F103C8T6。(鉴于笔者实验时身边只有STM32F103ZET6,故本次实验使基于ZET6进行的) ​  课程链接:江协科技 STM32入门教程   往期笔记链接:   STM32学习笔记(一)丨建立工程丨GPIO 通用输入输出   STM32学习笔记(二)丨STM32程序调试

    2024年01月19日
    浏览(57)
  • 【STM32】STM32学习笔记-硬件I2C读写MPU6050(35)

    I2C(Inter-Integrated Circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。 串行的 8 位双向数据传输位速率在标准模式下可

    2024年01月25日
    浏览(57)
  • 【STM32】I2C练习,HAL库读取MPU6050角度陀螺仪

    MPU-6000(6050)为全球首例整合性6轴运动处理组件,相较于多组件方案,免除了组合陀螺仪与加速器时间轴之差的问题,减少了大量的封装空间。当连接到三轴磁强计时,MPU-60X0提供完整的9轴运动融合输出到其主I2C或SPI端口(SPI仅在MPU-6000上可用)。 寄存器地址 寄存器内容 0X3B

    2024年02月16日
    浏览(50)
  • STM32F407硬件I2C实现MPU6050通讯(CUBEIDE)

    工程代码 https://download.csdn.net/download/weixin_52849254/87886714 I2C1通道可选择三种不同的通讯协议:I2C、SMBus-Alert-mode、SMBus-two-wire-Interface。 SMBus (System Management Bus,系统管理总线), 为系统和电源管理这样的任务提供了一条控制总线,SMBus与I2C总线之间在时序特性上存在一些差别 修改

    2024年02月09日
    浏览(51)
  • 【STM32学习】——STM32-I2C外设&硬件读写MPU6050&软硬件读写波形对比

    目录 前言 一、I2C外设 二、硬件I2C操作流程 1.主机发送时序 3.其他时序

    2024年02月10日
    浏览(45)
  • 【STM32】软件I2C控制频率

    在上一篇文章中,我们已经介绍了整个软件I2C的实现原理,但是也遗留了一个问题,那就是I2C速率的控制,其实就是控制SCL信号的频率。 微秒级延时 在上篇文章中,我们使用了SysTick进行延时,具体如下: 关于SysTick延时的原理,可以参考这篇文章 HAL库下的systick 底层配置

    2024年02月11日
    浏览(44)
  • STM32完成软件I2C通讯

    今天的重点是利用STM32的软件方案和MPU60506轴姿态传感器建立通讯,今天只完成了简单的发送地址和接收应答的部分,特此记录一下过程,以后忘记可以随时翻出来看看。 先介绍最基本的I2C通讯的最基本的6个时序: 一:起始条件:SCL高电平期间,SDA从高电平切换到低电平 时

    2024年04月13日
    浏览(37)
  • STM32 SHT40驱动源码(使用硬件I2C)

    目录 简介: SHT40.c: SHT40.h 测试结果:         SHT40是瑞士Sensirion公司推出的第四代温湿度传感器,内部集成加热器用于去除表面微小液滴。集成I2C接口,典型的相对湿度精度1.8%RH,典型温度精度0.2℃,运行在0-100%RH和-40-125℃的环境中。 主控:STM32H7B0VBT6 平台:STM32CubeIDE SHT4

    2024年03月19日
    浏览(72)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包