[其他ST产品] STM32入门笔记12_04_硬件I2C读写MPU6050

[复制链接]
 楼主| 大鹏2365 发表于 2023-11-20 23:07 | 显示全部楼层 |阅读模式
I2C通信外设
I2C外设简介
STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担
支持多主机模型
支持7位/10位地址模式
支持不同的通讯速度,标准速度(100kHz),快速(400kHz)
支持DMA
兼容SMBus协议
STM32F103C8T6硬件I2C资源: I2C1、I2C2
I2C框图
84284655b761f5a989.png
SDA线控制 88785655b762b6da51.png

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



 楼主| 大鹏2365 发表于 2023-11-20 23:08 | 显示全部楼层
单片机从模式控制(了解)
36578655b765a46f3c.png
报错误校验(PEC) 用于提高通信的可靠性,使用CRC-8算法
当stm32主机属于从模式时,可以通过自身地址寄存器和双地址寄存器为stm32自定义地址
 楼主| 大鹏2365 发表于 2023-11-20 23:08 | 显示全部楼层
SCL线控制
75702655b766d308b2.png
控制寄存器用于配置硬件电路

CR1
26992655b7679c8f16.png
 楼主| 大鹏2365 发表于 2023-11-20 23:09 | 显示全部楼层
CR2
29128655b76b1dd053.png 状态寄存器可以读取电路的信息

SR1 82171655b76bd595e8.png
 楼主| 大鹏2365 发表于 2023-11-20 23:10 | 显示全部楼层
SR2

19441655b76cd4c67b.png

时钟控制寄存器用于配置时钟信号
 楼主| 大鹏2365 发表于 2023-11-20 23:10 | 显示全部楼层
CCR

22781655b76d9d9502.png

具体翻阅STM32F10xxx参考手册
 楼主| 大鹏2365 发表于 2023-11-20 23:10 | 显示全部楼层
I2C基本结构
42460655b76ebd46e2.png
 楼主| 大鹏2365 发表于 2023-11-20 23:10 | 显示全部楼层
时序图
主机发送

11054655b76fbe4b51.png
 楼主| 大鹏2365 发表于 2023-11-20 23:11 | 显示全部楼层
主机接收
7563655b7734cb2c5.png
 楼主| 大鹏2365 发表于 2023-11-20 23:12 | 显示全部楼层
软件硬件波形对比(了解)

1902655b77430835f.png
 楼主| 大鹏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);

状态检测函数,用于获取状态寄存器中的标志位
 楼主| 大鹏2365 发表于 2023-11-20 23:12 | 显示全部楼层
main.c
  1. #include "stm32f10x.h"
  2. #include "delay.h"
  3. #include "OLED.h"
  4. #include "MPU6050.h"
  5. uint8_t ID;
  6. int main(void)
  7. {
  8.         OLED_Init();
  9.         MPU6050_Init();
  10.         OLED_ShowString(1, 1, "ID:");
  11.         ID = MPU6050_GetID();
  12.         OLED_ShowHexNum(1, 4, ID, 2);
  13.         MPU6050_DataTypeDef MPU6050_DataStructure;
  14.         while(1)
  15.         {
  16.                 // 读取数据
  17.                 MPU6050_GetData(&MPU6050_DataStructure);
  18.                 // 显示数据
  19.                 OLED_ShowSignedNum(2, 1, MPU6050_DataStructure.AccX, 5);
  20.                 OLED_ShowSignedNum(3, 1, MPU6050_DataStructure.AccY, 5);
  21.                 OLED_ShowSignedNum(4, 1, MPU6050_DataStructure.AccZ, 5);
  22.                 OLED_ShowSignedNum(2, 8, MPU6050_DataStructure.GyroX, 5);
  23.                 OLED_ShowSignedNum(3, 8, MPU6050_DataStructure.GyroY, 5);
  24.                 OLED_ShowSignedNum(4, 8, MPU6050_DataStructure.GyroZ, 5);
  25.         }
  26. }
 楼主| 大鹏2365 发表于 2023-11-20 23:12 | 显示全部楼层
MPU6050.c
  1. #include "stm32f10x.h"
  2. #include "MPU6050_SEG.h"

  3. #define MPU_ADDRESS 0xD0

  4. /*
  5. *  等待事件 带超时退出机制
  6. *  将while与I2C_CheckEvent封装在一起
  7. */
  8. void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
  9. {
  10.         uint32_t Timeout=50000;
  11.         while(Timeout && (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS))
  12.         {
  13.                 Timeout --;
  14.         }
  15. }

  16. /*
  17. *  往指定地址写入指定数据
  18. */
  19. void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
  20. {
  21.         // 起始条件
  22.         I2C_GenerateSTART(I2C2, ENABLE);
  23.         // 等待 EV5
  24.         MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
  25.         // 呼叫设备(MPU6050)
  26.         I2C_Send7bitAddress(I2C2, MPU_ADDRESS, I2C_Direction_Transmitter);
  27.         // 等待 EV6
  28.         MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
  29.         // 寻找写入地址
  30.         I2C_SendData(I2C2, RegAddress);
  31.         // 等待EV8
  32.         MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
  33.         // 写入数据
  34.         I2C_SendData(I2C2, Data);
  35.         // 等待EV8_2
  36.         MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
  37.         // 终止条件
  38.         I2C_GenerateSTOP(I2C2, ENABLE);
  39. }

  40. /*
  41. *  从指定地址读取数据
  42. */
  43. uint8_t MPU6050_ReadReg(uint8_t RegAddress)
  44. {
  45.         uint8_t Data;
  46.         // 起始条件
  47.         I2C_GenerateSTART(I2C2, ENABLE);        // 起始条件
  48.         MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);        // 等待 EV5
  49.        
  50.         I2C_Send7bitAddress(I2C2, MPU_ADDRESS, I2C_Direction_Transmitter); //呼叫设备 写
  51.         MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); // 等待 EV6
  52.        
  53.         I2C_SendData(I2C2, RegAddress); // 寻找读取地址
  54.         MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); // 等待EV8_2
  55.        
  56.         I2C_GenerateSTART(I2C2, ENABLE); // 再次产生起始条件
  57.         MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); // 等待 EV5
  58.        
  59.         I2C_Send7bitAddress(I2C2, MPU_ADDRESS, I2C_Direction_Receiver); // 呼叫设备 读
  60.         MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); // 等待 EV6
  61.        
  62.         I2C_AcknowledgeConfig(I2C2, DISABLE);  // 关闭应答
  63.         I2C_GenerateSTOP(I2C2, ENABLE);                // 终止条件
  64.        
  65.         MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);  // 等待EV7
  66.         Data = I2C_ReceiveData(I2C2); // 接收数据

  67.         I2C_AcknowledgeConfig(I2C2, ENABLE);  // 开启应答
  68.         return Data;
  69. }

  70. /*
  71. *  配置MPU6050
  72. */
  73. void MPU6050_Init(void)
  74. {
  75.         // I2C初始化
  76.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
  77.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  78.        
  79.         // GPIO
  80.         GPIO_InitTypeDef GPIO_InitStructure;
  81.         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_OD;  // 复用开漏
  82.         GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10|GPIO_Pin_11;
  83.         GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  84.         GPIO_Init(GPIOB, &GPIO_InitStructure);
  85.         // I2C
  86.         I2C_InitTypeDef I2C_InitStructure;
  87.         I2C_InitStructure.I2C_Ack=I2C_Ack_Enable;  // 有应答
  88.         I2C_InitStructure.I2C_ClockSpeed=50000;  // 400 kHz以下  100 kHz以上为快速通讯
  89.         I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2;  // 低电平比高电平2:1 仅在快速通讯生效  
  90.         I2C_InitStructure.I2C_Mode=I2C_Mode_I2C;
  91.         I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
  92.     // 从模式下 stm32作为从机的地址所占位数       
  93.         I2C_InitStructure.I2C_OwnAddress1=0x00;  // 从模式下stm32作为从机的地址
  94.         I2C_Init(I2C2, &I2C_InitStructure);
  95.         // 开启I2C2
  96.         I2C_Cmd(I2C2, ENABLE);
  97.        
  98.         MPU6050_WriteReg(PWR_MGMT_1, 0x01);
  99.         MPU6050_WriteReg(PWR_MGMT_2, 0x00);
  100.         MPU6050_WriteReg(SMPLRT_DIV, 0x09);
  101.         MPU6050_WriteReg(CONFIG, 0x06);
  102.         MPU6050_WriteReg(GYRO_CONFIG, 0x18);
  103.         MPU6050_WriteReg(ACCEL_CONFIG, 0x18);
  104. }
  105. /*
  106. * 获取ID号
  107. */
  108. uint8_t MPU6050_GetID(void)
  109. {
  110.         return MPU6050_ReadReg(WHO_AM_I);
  111. }
  112. /*
  113. *  获取MPU6050数据
  114. */
  115. void MPU6050_GetData(MPU6050_DataTypeDef * MPU_DataStructure)
  116. {
  117.         uint16_t Data_H, Data_L;  // 高位数据和低位数据
  118.         Data_H = MPU6050_ReadReg(ACCEL_XOUT_H);
  119.         Data_L = MPU6050_ReadReg(ACCEL_XOUT_L);
  120.         MPU_DataStructure->AccX=(Data_H<<8) | Data_L;
  121.        
  122.         Data_H = MPU6050_ReadReg(ACCEL_YOUT_H);
  123.         Data_L = MPU6050_ReadReg(ACCEL_YOUT_L);
  124.         MPU_DataStructure->AccY=(Data_H<<8) | Data_L;
  125.        
  126.         Data_H = MPU6050_ReadReg(ACCEL_ZOUT_H);
  127.         Data_L = MPU6050_ReadReg(ACCEL_ZOUT_L);
  128.         MPU_DataStructure->AccZ=(Data_H<<8) | Data_L;
  129.        
  130.         Data_H = MPU6050_ReadReg(GYRO_XOUT_H);
  131.         Data_L = MPU6050_ReadReg(GYRO_XOUT_L);
  132.         MPU_DataStructure->GyroX=(Data_H<<8) | Data_L;
  133.        
  134.         Data_H = MPU6050_ReadReg(GYRO_YOUT_H);
  135.         Data_L = MPU6050_ReadReg(GYRO_YOUT_L);
  136.         MPU_DataStructure->GyroY=(Data_H<<8) | Data_L;
  137.        
  138.         Data_H = MPU6050_ReadReg(GYRO_ZOUT_H);
  139.         Data_L = MPU6050_ReadReg(GYRO_ZOUT_L);
  140.         MPU_DataStructure->GyroZ=(Data_H<<8) | Data_L;
  141. }
 楼主| 大鹏2365 发表于 2023-11-20 23:13 | 显示全部楼层
MPU6050.h
  1. #ifndef __MPU6050_H
  2. #define __MPU6050_H
  3. #include "MPU6050_REG.h"
  4. void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
  5. uint8_t MPU6050_ReadReg(uint8_t RegAddress);
  6. void MPU6050_Init(void);
  7. uint8_t MPU6050_GetID(void);
  8. void MPU6050_GetData(MPU6050_DataTypeDef * MPU_DataStructure);
  9. #endif

 楼主| 大鹏2365 发表于 2023-11-20 23:13 | 显示全部楼层
MPU6050_REG.h
  1. #ifndef __MPU6050_REG_H
  2. #define __MPU6050_REG_H
  3. // 寄存器地址
  4. #define SMPLRT_DIV                                 0x19
  5. #define CONFIG                                        0x1A
  6. #define GYRO_CONFIG                         0x1B
  7. #define ACCEL_CONFIG                         0x1C
  8. #define ACCEL_XOUT_H                         0x3B
  9. #define ACCEL_XOUT_L                         0x3C
  10. #define ACCEL_YOUT_H                         0x3D
  11. #define ACCEL_YOUT_L                         0x3E
  12. #define ACCEL_ZOUT_H                         0x3F
  13. #define ACCEL_ZOUT_L                         0x40
  14. #define TEMP_OUT_H                                 0x41
  15. #define        TEMP_OUT_L                                0x42
  16. #define        GYRO_XOUT_H                                0x43
  17. #define        GYRO_XOUT_L                                0x44
  18. #define        GYRO_YOUT_H                                0x45
  19. #define        GYRO_YOUT_L                                0x46
  20. #define        GYRO_ZOUT_H                                0x47
  21. #define        GYRO_ZOUT_L                                0x48

  22. #define        PWR_MGMT_1                                0x6B
  23. #define        PWR_MGMT_2                                0x6C
  24. #define        WHO_AM_I                                0x75

  25. /*
  26. *  MPU6050数据结构体
  27. */
  28. typedef struct
  29. {
  30.         int16_t AccX;
  31.         int16_t AccY;
  32.         int16_t AccZ;
  33.         int16_t GyroX;
  34.         int16_t GyroY;
  35.         int16_t GyroZ;
  36. } MPU6050_DataTypeDef;

  37. #endif

LEDyyds 发表于 2024-4-7 17:00 | 显示全部楼层
实际上就是I2C通信
万图 发表于 2024-7-11 07:28 | 显示全部楼层

电流若是偏大就会直接导致器件烧毁
Uriah 发表于 2024-7-11 08:31 | 显示全部楼层

人体具有300PF的等效电容
帛灿灿 发表于 2024-7-11 10:27 | 显示全部楼层

从而达到对电子设备进行静电保护
周半梅 发表于 2024-7-11 13:26 | 显示全部楼层

就是因为它的作用就是在电子产品设备受到雷击浪涌与ESD静电放电或者其他瞬态电压时
您需要登录后才可以回帖 登录 | 注册

本版积分规则

61

主题

692

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部