【江协STM32】10-4/5 I2C通信外设、硬件I2C读写MPU6050

news2025/1/14 4:59:03

1. I2C外设简介

  • STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担
  • 支持多主机模型
  • 支持7位/10位地址模式
  • 支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)
  • 支持DMA
  • 兼容SMBus协议
  • STM32F103C8T6 硬件I2C资源:I2C1、I2C2
I2C框图
I2C基本结构
I2C主机发送操作流程
I2C主机接收操作流程

硬件I2C的波形会更加规整,每个时钟的周期、占空比都非常一致。软件I2C由于操作引脚之后加了延时,这个延时有时加的多、有时加的少,所以软件时序的时钟周期、占空比可能不规整,不过由于I2C是同步时序,这些不规整也没有影响。

SCL低电平写,高电平读,虽然整个电平的任意时刻都可以读写,但一般要求保证尽早的原则,所以可以直接认为是SCL下降沿写,上升沿读。软件I2C在下降沿之后,因为操作端口之后有一些延时,所以等了一会才进行写入操作。但在硬件I2C中,数据写入都紧贴下降沿的(SCL下降沿,SDA立马切换数据),读的时刻也是紧贴上升沿进行的。还是由于I2C是同步时序,这些不标准的波形也不影响通信。

软件I2C时序波形
硬件I2C时序波形

2. 硬件I2C读写MPU6050

2.1 接线图

I2C1_SCL和I2C1_SDA引脚分别为PB6和PB7,其重映射为PB8和PB9,但由于这几个引脚都在板子右下角,被OLED占用,不方便接线,所以使用I2C2。I2C2_SCL和I2C2_SDA引脚分别为PB10和PB11。

2.2 代码

主函数和程序现象与软件I2C相同,区别在于通信的底层(MyI2C.c),所以本工程不需要MyI2C模块。

基于软件I2C代码修改,主要任务为:第一步,配置I2C外设,对I2C2外设进行初始化,替换MyI2C_Init();第二步,控制外设电路,实现指定地址写的时序,替换MPU6050_WriteReg();第三步,控制外设电路,实现指定地址读的时序,替换MPU6050_ReadReg()。

配置I2C外设:

  1. 开启I2C外设和对应GPIO口的时钟
  2. 把I2C外设对应的GPIO口初始化为复用开漏模式
  3. 使用结构体,对整个I2C进行配置
  4. 使能I2C

MPU6050.c

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

#define MPU6050_ADDRESS 0xD0

//  封装I2C_CheckEvent和超时退出循环
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
    uint32_t Timeout;
    Timeout = 10000;
    while(I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)//   监测EVx事件是否发生
    { // 循环超时退出机制,防止意外卡死
        Timeout--;
        if(Timeout == 0)
        {
            break;//    跳出循环
        }
    }
}

// 指定地址写
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
//    MyI2C_Start();
//    MyI2C_SendByte(MPU6050_ADDRESS);// 发送从机地址+读写位
//    MyI2C_ReceiveAck();//   接收应答
//    MyI2C_SendByte(RegAddress);//   指定寄存器地址
//    MyI2C_ReceiveAck();
//    MyI2C_SendByte(Data);//   发送写入指定寄存器地址下的数据
//    MyI2C_ReceiveAck();
//    MyI2C_Stop();
    
    I2C_GenerateSTART(I2C2, ENABLE);//  生成起始条件
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//   监测EV5事件是否发生
    
    I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);//   发送从机地址,第3个参数是读写位
    //  库函数中发送数据都自带了接收应答的过程。同样,接收数据也自带了发送应答的过程。
    //  如果应答错误,硬件会通过标志位和中断提示。所以发送地址后应答位就不需要处理了
    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_2事件
    
    I2C_GenerateSTOP(I2C2, ENABLE);//   终止时序
}

//  指定地址读
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
    uint8_t Data;
    
//    MyI2C_Start();
//    MyI2C_SendByte(MPU6050_ADDRESS);// 发送从机地址+读写位
//    MyI2C_ReceiveAck();//   接收应答
//    MyI2C_SendByte(RegAddress);//   指定寄存器地址
//    MyI2C_ReceiveAck();
//    
//    MyI2C_Start();//    重复起始条件
//    MyI2C_SendByte(MPU6050_ADDRESS | 0xD1);//   读写位1,读出数据
//    MyI2C_ReceiveAck();
//    Data = MyI2C_ReceiveByte();//   从机发送数据,主机接收数据
//    MyI2C_SendAck(1);//  这里只想要1个字节,所以不给从机应答
//    MyI2C_Stop();
    
    I2C_GenerateSTART(I2C2, ENABLE);//  生成起始条件
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//   监测EV5事件是否发生
    
    I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);//   发送从机地址,第3个参数是读写位
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);// 等待EV6事件
    
    I2C_SendData(I2C2, RegAddress);//   发送寄存器地址
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);//   检查EV8事件
    
    I2C_GenerateSTART(I2C2, ENABLE);//  重复起始条件
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);// 等待EV5事件
    
    I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);//   发送从机地址,第3个参数是读写位
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);// 等待EV6事件
    
    //  这里需要在接收完成前提前设置Ack非应答和终止时序
    I2C_AcknowledgeConfig(I2C2, DISABLE);// 配置ACK位,临时给非应答
    I2C_GenerateSTOP(I2C2, ENABLE);//   终止时序
    
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);//   等待EV7事件产生后,一个字节的数据就已经在DR内了
    Data = I2C_ReceiveData(I2C2);//    读取DR
    
    I2C_AcknowledgeConfig(I2C2, ENABLE);// 配置ACK位,给应答,为了方便指定地址收多个字节
    //  虽然现在程序中只有收一个字节,但写一下方便之后改进

    return Data;
}

void MPU6050_Init(void)
{
//    MyI2C_Init();
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;//	复用开漏模式。开漏是I2C协议的设计要求,复用是GPIO的控制权要交给硬件外设
	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_Ack = I2C_Ack_Enable;// 应答位
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;// 指定STM32作为从机可以响应几位的地址,STM32作为从机模式才会用到
    I2C_InitStructure.I2C_ClockSpeed = 50000;//  时钟频率
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//   时钟占空比。此参数只有在时钟频率>100kHz(进入快速状态)时才生效,在<=100kHz(标准速度)下为固定的1:1
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;// 自身地址1。STM32作为从机使用,用于指定STM32的自身地址。如果I2C_AcknowledgedAddress选择了响应7位地址,这里就要指定一个自身的7位地址
    I2C_Init(I2C2, &I2C_InitStructure);
    
    I2C_Cmd(I2C2, ENABLE);
    
    MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//   配置PWR_MGMT_1寄存器,解除睡眠,选择X轴陀螺仪时钟
    MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);
    MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//   采样率分频,决定了数据输出的快慢。10分频
    MPU6050_WriteReg(MPU6050_CONFIG, 0x06);//   数字低通滤波器给110
    MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);//  陀螺仪配置,选择最大量程11
    MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);// 加速度计配置,选择最大量程11
    
}

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

//  由于需要返回6个变量,使用指针的地址传递
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
                        int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
    uint8_t Data_H, Data_L;
    
    Data_H = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);//    加速度寄存器X轴高8位
    Data_L = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
    *AccX = (Data_H << 8) | Data_L;//   加速度计X轴的16位数据
    
    Data_H = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
    Data_L = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
    *AccY = (Data_H << 8) | Data_L;
    
    Data_H = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
    Data_L = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
    *AccZ = (Data_H << 8) | Data_L;
    
    Data_H = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);//    陀螺仪寄存器X轴高8位
    Data_L = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
    *GyroX = (Data_H << 8) | Data_L;//   陀螺仪X轴的16位数据
    
    Data_H = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
    Data_L = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
    *GyroY = (Data_H << 8) | Data_L;
    
    Data_H = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
    Data_L = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
    *GyroZ = (Data_H << 8) | Data_L;
}

代码其他部分见(【江协STM32】10-2/3 MPU6050简介、软件I2C读写MPU6050,第2.2节)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2276295.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

初识 Git——《Pro Git》

Why Git&#xff1f; 1. 本地版本控制系统 Why&#xff1a; 许多人习惯用复制整个项目目录的方式来保存不同的版本&#xff0c;或许还会改名加上备份时间以示区别。 这么做唯一的好处就是简单&#xff0c;但是特别容易犯错。 有时候会混淆所在的工作目录&#xff0c;一不小心…

记一次学习skynet中的C/Lua接口编程解析protobuf过程

1.引言 最近在学习skynet过程中发现在网络收发数据的过程中数据都是裸奔&#xff0c;就想加入一种数据序列化方式&#xff0c;json、xml简单好用&#xff0c;但我就是不想用&#xff0c;于是就想到了protobuf&#xff0c;对于protobuf C/C的使用个人感觉有点重&#xff0c;正好…

使用RSyslog将Nginx Access Log写入Kafka

个人博客地址&#xff1a;使用RSyslog将Nginx Access Log写入Kafka | 一张假钞的真实世界 环境说明 CentOS Linux release 7.3.1611kafka_2.12-0.10.2.2nginx/1.12.2rsyslog-8.24.0-34.el7.x86_64.rpm 创建测试Topic $ ./kafka-topics.sh --zookeeper 192.168.72.25:2181/k…

MySQL(行结构)

后面也会持续更新&#xff0c;学到新东西会在其中补充。 建议按顺序食用&#xff0c;欢迎批评或者交流&#xff01; 缺什么东西欢迎评论&#xff01;我都会及时修改的&#xff01; MySQL 一行记录是怎么存储的&#xff1f; | 小林coding MySQL原理 - InnoDB引擎 - 行记录存…

ros2笔记-6.2 使用urdf创建机器人模型

本节主要跟着小鱼老师的视频操作&#xff0c;不同的仿真平台有不同的建模语言&#xff0c;但是几乎都支持URDF。 本节使用URDF创建一个机器人模型。 6.2.1 帮机器人创建一个身体 URDF使用XML来描述机器人的结构和传感器、执行器等信息。 在chapt6/chap6_ws/src创建功能包:r…

基于mybatis-plus历史背景下的多租户平台改造

前言 别误会&#xff0c;本篇【并不是】 要用mybatis-plus自身的多租户方案&#xff1a;在表中加一个tenant_id字段来区分不同的租户数据。并不是的&#xff01; 而是在假设业务系统已经使用mybatis-plus多数据源的前提下&#xff0c;如何实现业务数据库隔开的多租户系统。 这…

【JAVA基础】Collections方法的具体使用方法

java基础中Collections及collect(toList,toSet,toMap)的用法 package com.gaofeng;import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream;public class demo01 {public static void main(String[] …

HDFS 的API的操作

3.1 客户端环境准备(windows) 1&#xff09;拷贝hadoop-3.1.X到非中文路径&#xff08;比如d:\&#xff09;。 2&#xff09;配置HADOOP_HOME环境变量 3&#xff09;配置Path环境变量。 注意&#xff1a;如果环境变量不起作用&#xff0c;可以重启电脑试试。 也可以直接添加…

【数据库】二、关系数据库

文章目录 二、关系数据库1 关系2 关系数据库3 完整性约束4 关系运算 二、关系数据库 1 关系 域&#xff1a;一组具有相同数据类型的值的集合。 笛卡尔积&#xff1a;所有域&#xff08;域可相同&#xff09;中所有取值的组合 例如&#xff1a;D1{1,2,3}&#xff0c;D2{A,b}&…

[笔记] 使用 Jenkins 实现 CI/CD :从 GitLab 拉取 Java 项目并部署至 Windows Server

随着软件开发节奏的加快&#xff0c;持续集成&#xff08;CI&#xff09;和持续部署&#xff08;CD&#xff09;已经成为确保软件质量和加速产品发布的不可或缺的部分。Jenkins作为一款广泛使用的开源自动化服务器&#xff0c;为开发者提供了一个强大的平台来实施这些实践。然而…

playwright 模拟登录

一、流程如下 C#代码&#xff1a; using Microsoft.Playwright; using Newtonsoft.Json; using System; using System.IO; using System.Net.Http; using System.Text; using System.Xml.Linq;namespace TestProject3 {[TestClass]public class UnitTest1 : PageTest{[TestMet…

使用大数据分析提升电子商务的转化率

&#x1f496; 欢迎来到我的博客&#xff01; 非常高兴能在这里与您相遇。在这里&#xff0c;您不仅能获得有趣的技术分享&#xff0c;还能感受到轻松愉快的氛围。无论您是编程新手&#xff0c;还是资深开发者&#xff0c;都能在这里找到属于您的知识宝藏&#xff0c;学习和成长…

HTB:Bastion[WriteUP]

目录 连接至HTB服务器并启动靶机 信息收集 使用rustscan对靶机TCP端口进行开放扫描 将靶机TCP开放端口号提取并保存 使用nmap对靶机TCP开放端口进行脚本、服务扫描 使用nmap对靶机TCP开放端口进行漏洞、系统扫描 使用nmap对靶机常用UDP端口进行开放扫描 使用enum4linux…

HAMi + prometheus-k8s + grafana实现vgpu虚拟化监控

最近长沙跑了半个多月&#xff0c;跟甲方客户对了下项目指标&#xff0c;许久没更新 回来后继续研究如何实现 grafana实现HAMi vgpu虚拟化监控&#xff0c;毕竟合同里写了需要体现gpu资源限制和算力共享以及体现算力卡资源共享监控 先说下为啥要用HAMi吧&#xff0c; 一个重要原…

springboot使用Easy Excel导出列表数据为Excel

springboot使用Easy Excel导出列表数据为Excel Easy Excel官网&#xff1a;https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write 主要记录一下引入时候的pom&#xff0c;直接引入会依赖冲突 解决方法&#xff1a; <!-- 引入Easy Excel的依赖 -->&l…

泛目录和泛站有什么差别

啥是 SEO 泛目录&#xff1f; 咱先来说说 SEO 泛目录是啥。想象一下&#xff0c;你有一个巨大的图书馆&#xff0c;里面的书架上摆满了各种各样的书&#xff0c;每一本书都代表着一个网页。而 SEO 泛目录呢&#xff0c;就像是一个超级图书管理员&#xff0c;它的任务就是把这些…

黑马天机学堂学习计划模块

核心功能 系统设计思路 ​​​​​​​ 代码分析 1. 学习记录管理 • 存储学习记录到 Redis&#xff1a; 利用 Redis 缓存学习记录&#xff0c;减少频繁的数据库访问。 public void writeRecordCache(LearningRecord record) {String key String.format("LEARNING:R…

初学stm32 --- DAC输出三角波和正弦波

输出三角波实验简要&#xff1a; 1&#xff0c;功能描述 通过DAC1通道1(PA4)输出三角波&#xff0c;然后通过DS100示波器查看波形 2&#xff0c;关闭通道1触发(即自动) TEN1位置0 3&#xff0c;关闭输出缓冲 BOFF1位置1 4&#xff0c;使用12位右对齐模式 将数字量写入DAC_…

专题 - STM32

基础 基础知识 STM所有产品线&#xff08;列举型号&#xff09;&#xff1a; STM产品的3内核架构&#xff08;列举ARM芯片架构&#xff09;&#xff1a; STM32的3开发方式&#xff1a; STM32的5开发工具和套件&#xff1a; 若要在电脑上直接硬件级调试STM32设备&#xff0c;则…

25年无人机行业资讯 | 1.1 - 1.5

25年无人机行业资讯 | 1.1 - 1.5 中央党报《经济日报》刊文&#xff1a;低空经济蓄势待发&#xff0c;高质量发展需的平衡三大关系 据新华网消息&#xff0c;2025年1月3日&#xff0c;中央党报《经济日报》发表文章指出&#xff0c;随着国家发展改革委低空经济发展司的成立&a…