打印
[其他ST产品]

STM32入门笔记12_04_硬件I2C读写MPU6050

[复制链接]
655|17
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
I2C通信外设
I2C外设简介
STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担
支持多主机模型
支持7位/10位地址模式
支持不同的通讯速度,标准速度(100kHz),快速(400kHz)
支持DMA
兼容SMBus协议
STM32F103C8T6硬件I2C资源: I2C1、I2C2
I2C框图

SDA线控制

发送模式: 当发送寄存器空时(TXE), 单片机将数据写入数据寄存器(DATA REGISTER), 当数据移位寄存器为空时, 数据从数据寄存器转入数据移位寄存器, 再通过数据移位寄存器一位位输出
接收模式: 数据一位位被送到数据移位寄存器中, 移位寄存器再把数据一位位送到数据寄存器中(DATA REGISTER), 当接收寄存器非空时(RXNE), 单片机就可以将数据寄存器中的数据读出



使用特权

评论回复
沙发
大鹏2365|  楼主 | 2023-11-20 23:08 | 只看该作者
单片机从模式控制(了解)

报错误校验(PEC) 用于提高通信的可靠性,使用CRC-8算法
当stm32主机属于从模式时,可以通过自身地址寄存器和双地址寄存器为stm32自定义地址

使用特权

评论回复
板凳
大鹏2365|  楼主 | 2023-11-20 23:08 | 只看该作者
SCL线控制

控制寄存器用于配置硬件电路

CR1

使用特权

评论回复
地板
大鹏2365|  楼主 | 2023-11-20 23:09 | 只看该作者
CR2
状态寄存器可以读取电路的信息

SR1

使用特权

评论回复
5
大鹏2365|  楼主 | 2023-11-20 23:10 | 只看该作者
SR2



时钟控制寄存器用于配置时钟信号

使用特权

评论回复
6
大鹏2365|  楼主 | 2023-11-20 23:10 | 只看该作者
CCR



具体翻阅STM32F10xxx参考手册

使用特权

评论回复
7
大鹏2365|  楼主 | 2023-11-20 23:10 | 只看该作者
I2C基本结构

使用特权

评论回复
8
大鹏2365|  楼主 | 2023-11-20 23:10 | 只看该作者
时序图
主机发送

使用特权

评论回复
9
大鹏2365|  楼主 | 2023-11-20 23:11 | 只看该作者
主机接收

使用特权

评论回复
10
大鹏2365|  楼主 | 2023-11-20 23:12 | 只看该作者
软件硬件波形对比(了解)

使用特权

评论回复
11
大鹏2365|  楼主 | 2023-11-20 23:12 | 只看该作者
程序设计
根据主机发送和主机接收的时序图重写 MPU6050_WriteReg()和MPU6050_ReadReg()

I2C的配置结构体意义见注释

I2C常用函数介绍
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);

用于配置I2C
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

开启对应的I2C外设
void I2C_GenerateSTART(I2C_TypeDef I2Cx, FunctionalState NewState);*

产生起始条件
void I2C_GenerateSTOP(I2C_TypeDef I2Cx, FunctionalState NewState);*

产生终止条件
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);

控制是否有应答
void I2C_SendData(I2C_TypeDef I2Cx, uint8_t Data);*

发送数据
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);

用于寻找从机地址
ErrorStatus I2C_CheckEvent(I2C_TypeDef I2Cx, uint32_t I2C_EVENT)*

状态检测函数,用于检查I2C通讯过程中产生的事件
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);

状态检测函数,用于获取状态寄存器中的标志位

使用特权

评论回复
12
大鹏2365|  楼主 | 2023-11-20 23:12 | 只看该作者
main.c
#include "stm32f10x.h" 
#include "delay.h"
#include "OLED.h"
#include "MPU6050.h"
uint8_t ID;
int main(void)
{
        OLED_Init();
        MPU6050_Init();
        OLED_ShowString(1, 1, "ID:");
        ID = MPU6050_GetID();
        OLED_ShowHexNum(1, 4, ID, 2);
        MPU6050_DataTypeDef MPU6050_DataStructure;
        while(1)
        {
                // 读取数据
                MPU6050_GetData(&MPU6050_DataStructure);
                // 显示数据
                OLED_ShowSignedNum(2, 1, MPU6050_DataStructure.AccX, 5);
                OLED_ShowSignedNum(3, 1, MPU6050_DataStructure.AccY, 5);
                OLED_ShowSignedNum(4, 1, MPU6050_DataStructure.AccZ, 5);
                OLED_ShowSignedNum(2, 8, MPU6050_DataStructure.GyroX, 5);
                OLED_ShowSignedNum(3, 8, MPU6050_DataStructure.GyroY, 5);
                OLED_ShowSignedNum(4, 8, MPU6050_DataStructure.GyroZ, 5);
        }
}

使用特权

评论回复
13
大鹏2365|  楼主 | 2023-11-20 23:12 | 只看该作者
MPU6050.c
#include "stm32f10x.h"
#include "MPU6050_SEG.h"

#define MPU_ADDRESS 0xD0

/*
*  等待事件 带超时退出机制
*  将while与I2C_CheckEvent封装在一起
*/
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
        uint32_t Timeout=50000;
        while(Timeout && (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS))
        {
                Timeout --;
        }
}

/*
*  往指定地址写入指定数据
*/
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
        // 起始条件
        I2C_GenerateSTART(I2C2, ENABLE);
        // 等待 EV5
        MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
        // 呼叫设备(MPU6050)
        I2C_Send7bitAddress(I2C2, MPU_ADDRESS, I2C_Direction_Transmitter);
        // 等待 EV6
        MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
        // 寻找写入地址
        I2C_SendData(I2C2, RegAddress);
        // 等待EV8
        MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
        // 写入数据
        I2C_SendData(I2C2, Data);
        // 等待EV8_2
        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);        // 等待 EV5
       
        I2C_Send7bitAddress(I2C2, MPU_ADDRESS, I2C_Direction_Transmitter); //呼叫设备 写
        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, MPU_ADDRESS, I2C_Direction_Receiver); // 呼叫设备 读
        MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); // 等待 EV6
       
        I2C_AcknowledgeConfig(I2C2, DISABLE);  // 关闭应答
        I2C_GenerateSTOP(I2C2, ENABLE);                // 终止条件
       
        MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);  // 等待EV7
        Data = I2C_ReceiveData(I2C2); // 接收数据

        I2C_AcknowledgeConfig(I2C2, ENABLE);  // 开启应答
        return Data;
}

/*
*  配置MPU6050
*/
void MPU6050_Init(void)
{
        // I2C初始化
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
       
        // GPIO
        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_InitTypeDef I2C_InitStructure;
        I2C_InitStructure.I2C_Ack=I2C_Ack_Enable;  // 有应答
        I2C_InitStructure.I2C_ClockSpeed=50000;  // 400 kHz以下  100 kHz以上为快速通讯
        I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2;  // 低电平比高电平2:1 仅在快速通讯生效  
        I2C_InitStructure.I2C_Mode=I2C_Mode_I2C;
        I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
    // 从模式下 stm32作为从机的地址所占位数       
        I2C_InitStructure.I2C_OwnAddress1=0x00;  // 从模式下stm32作为从机的地址
        I2C_Init(I2C2, &I2C_InitStructure);
        // 开启I2C2
        I2C_Cmd(I2C2, ENABLE);
       
        MPU6050_WriteReg(PWR_MGMT_1, 0x01);
        MPU6050_WriteReg(PWR_MGMT_2, 0x00);
        MPU6050_WriteReg(SMPLRT_DIV, 0x09);
        MPU6050_WriteReg(CONFIG, 0x06);
        MPU6050_WriteReg(GYRO_CONFIG, 0x18);
        MPU6050_WriteReg(ACCEL_CONFIG, 0x18);
}
/*
* 获取ID号
*/
uint8_t MPU6050_GetID(void)
{
        return MPU6050_ReadReg(WHO_AM_I);
}
/*
*  获取MPU6050数据
*/
void MPU6050_GetData(MPU6050_DataTypeDef * MPU_DataStructure)
{
        uint16_t Data_H, Data_L;  // 高位数据和低位数据
        Data_H = MPU6050_ReadReg(ACCEL_XOUT_H);
        Data_L = MPU6050_ReadReg(ACCEL_XOUT_L);
        MPU_DataStructure->AccX=(Data_H<<8) | Data_L;
       
        Data_H = MPU6050_ReadReg(ACCEL_YOUT_H);
        Data_L = MPU6050_ReadReg(ACCEL_YOUT_L);
        MPU_DataStructure->AccY=(Data_H<<8) | Data_L;
       
        Data_H = MPU6050_ReadReg(ACCEL_ZOUT_H);
        Data_L = MPU6050_ReadReg(ACCEL_ZOUT_L);
        MPU_DataStructure->AccZ=(Data_H<<8) | Data_L;
       
        Data_H = MPU6050_ReadReg(GYRO_XOUT_H);
        Data_L = MPU6050_ReadReg(GYRO_XOUT_L);
        MPU_DataStructure->GyroX=(Data_H<<8) | Data_L;
       
        Data_H = MPU6050_ReadReg(GYRO_YOUT_H);
        Data_L = MPU6050_ReadReg(GYRO_YOUT_L);
        MPU_DataStructure->GyroY=(Data_H<<8) | Data_L;
       
        Data_H = MPU6050_ReadReg(GYRO_ZOUT_H);
        Data_L = MPU6050_ReadReg(GYRO_ZOUT_L);
        MPU_DataStructure->GyroZ=(Data_H<<8) | Data_L;
}

使用特权

评论回复
14
大鹏2365|  楼主 | 2023-11-20 23:13 | 只看该作者
MPU6050.h
#ifndef __MPU6050_H
#define __MPU6050_H
#include "MPU6050_REG.h"
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);
void MPU6050_Init(void);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(MPU6050_DataTypeDef * MPU_DataStructure);
#endif

使用特权

评论回复
15
大鹏2365|  楼主 | 2023-11-20 23:13 | 只看该作者
MPU6050_REG.h
#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H
// 寄存器地址
#define SMPLRT_DIV                                 0x19
#define CONFIG                                        0x1A
#define GYRO_CONFIG                         0x1B
#define ACCEL_CONFIG                         0x1C
#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        PWR_MGMT_1                                0x6B
#define        PWR_MGMT_2                                0x6C
#define        WHO_AM_I                                0x75

/*
*  MPU6050数据结构体
*/
typedef struct
{
        int16_t AccX;
        int16_t AccY;
        int16_t AccZ;
        int16_t GyroX;
        int16_t GyroY;
        int16_t GyroZ;
} MPU6050_DataTypeDef;

#endif

使用特权

评论回复
16
LEDyyds| | 2024-4-7 17:00 | 只看该作者
实际上就是I2C通信

使用特权

评论回复
17
万图| | 2024-7-11 07:28 | 只看该作者

电流若是偏大就会直接导致器件烧毁

使用特权

评论回复
18
Uriah| | 2024-7-11 08:31 | 只看该作者

人体具有300PF的等效电容

使用特权

评论回复
19
帛灿灿| | 2024-7-11 10:27 | 只看该作者

从而达到对电子设备进行静电保护

使用特权

评论回复
20
周半梅| | 2024-7-11 13:26 | 只看该作者

就是因为它的作用就是在电子产品设备受到雷击浪涌与ESD静电放电或者其他瞬态电压时

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

47

主题

656

帖子

0

粉丝