STM32基础教程——软件I2C
目录
前言
I2C
MPU6050
技术实现
原理图
连线图
代码实现
技术要点
I2C初始化
SCL输出和SDA输入输出控制
起始信号
停止信号
发送一个字节
读取一个字节
发送应答位
接收应答位
MPU6050初始化
指定地址写
指定地址读
读取数据寄存器
问题记录
前言
I2C
I2C(Inter-Integrated Circuit)是一种由飞利浦公司在1980年代开发的串行通信协议,主要用于在同一电路板上的短距离集成电路之间的通信。它以两线式接口著称,这两条线分别是SDA(数据线)和SCL(时钟线),允许通过这些线路在支持I2C协议的各种设备之间进行双向通信。I2C协议支持多主多从架构,使得多个设备可以通过共享的数据线和时钟线实现有效的通信。其特点是简单、成本低,并且能够支持多种速度标准,如标准模式(100 kbit/s)、快速模式(400 kbit/s)以及高速模式(3.4 Mbit/s),适用于各种应用场景,包括但不限于传感器数据交换、EEPROM编程等。
I2C主要特点 (部分)
- 并行总线/I2C总线协议转换器
- 多主机功能,该模块既可做主设备也可做从设备
- I2C主设备功能
——产生时钟
——产生起始和终止信号
- I2C从设备功能
——可编程的I2C地址检测
——可响应两个从地址的双地址功能
——停止检测位
- 支持不同的通讯速度
——标准速度(高达100kHz)
——快速(高达400kHz)
模式选择
接口可以下列4种模式中的一种运行:
- 从发送器模式
- 从接收器模式
- 主发送器模式
- 主接收器模式
该模块默认地工作于从模式接口在生成起始条件后自动地从从模式切换到主模式: 当仲裁丢失或产生停止信号时,则从主模式切换到从模式。允许多主机功能。
通信流
主模式时,I2C接口启动数据传输并产生时钟信号。串行数据传输总是以起始条件开始并以停止条件结束。起始条件和停止条件都是在主模式下由软件控制产生。
从模式时,I2C接口能识别它自己的地址(7位或10位)和广播呼叫地址。软件能够控制开启或禁止广播呼叫地址的识别。
数据和地址按8位/字节传输,高位在前。跟在起始条件之后的1或2个字节是地址(7位模式为1个字节,10位模式为2个字节)。地址只在主模式发送。
在一个字节传输的8个时钟后的第9个时钟期间,接收器必须回送一个应答位(ACK)给发送器。
软件可以开启或禁止应答(ACK),并可以设置I2C接口的地址(7位、10位地址或广播呼叫地址)
I2C主模式
在主模式时,I2C接口启动数据传输并产生时钟信号。串行数据传输总是以起始条件开始并以停止条件结束。当通过START位在总线上产生了起始条件,设备就进入了主模式。
起始条件
当BUSY=0时,设置START=1,I2C接口将产生一个开始条件并切换至主模式。
一旦发出开始条件:SB位被硬件置位,然后主设备等待读SR1寄存器,紧跟着将从地址写入DR寄存器(EV5)。
从地址的发送
从地址通过内部移位寄存器被送到SDA线上。
在7位地址模式时,只需送出一个地址字节。
——一旦改地址字节被送出,ADDR位被硬件置位,随后主设备等待一次读SR1寄存器,跟着读SR2寄存器。根据送出从地址的最低位,主设备决定进入发送器模式还是接收器模式。
在 7位地址模式时,
——要进入发送器模式,主设备发送从地址时置最低位为‘0’。
——要进入接收器模式,主设备发送从地址时置最低为‘1’。
主发送器
在发送了地址和清除了ADDR位后,主设备通过内部移位寄存器将字节从DR寄存器发送到SDA线上。
主设备等待,直到TxE被清除。(EV8)
当收到应答脉冲时:
TxE位被硬件置位,如果TxE被置位并且在上一次数据发送结束之前没有写入新的数据字节到DR寄存器,则BTF位被硬件置位,在清除BTF之前I2C接口将保持SCL为低电平;读出I2C_SR1之后再写入I2C_DR寄存器将清除BTF位。
关闭通信
在DR寄存器中写入最后一个字节后,通过设置STOP位产生一个停止条件(EV8_2)然后I2C接口将自动回到从模式。
主接收器
在发送地址和清除ADDR之后,I2C接口进入主接收器模式。在此模式下,I2C接口从SDA线接收数据字节,并通过内部移位寄存器送至DR寄存器。在每个字节后,I2C接口依次执行以下操作:
- 如果ACK位被置位,发出一个应答脉冲。
- 硬件设置RxNE=1(EV7)
如果RxNE位被置位,并且在接收新数据结束之前,DR寄存器中的数据没有被读走,硬件将设置BTF=1,在清除BTF之前I2C接口将保持SCL为低电平读出I2C_SR1之后再读出I2C_DR寄存器将清除BTF位。
关闭通信
主设备在从从设备接收到最后一个字节后发送一个NACK。接收到NACK后,从设备释放对SCL和SDA线的控制:主设备就可以发送一个停止/重起始条件。
- 为了在收到最后一个字节后发送一个NACK,在读倒数第二个数据字节之后(在倒数第二个RxNE事件之后)必须清除ACK位。
- 为了产生一个停止/重起始条件软件必须在读倒数第二个数据字节之后(在倒数第二个RxNE事件之后)设置STOP/START位。
- 只接收一个字节时,刚好在EV6之后(EV6_1时,清除ADDR之后)要关闭应答和停止条件的产生位。
在产生了停止条件后,I2C接口自动回到从模式。
MPU6050
MPU6050是一款集成的运动处理传感器,它结合了一个3轴陀螺仪和一个3轴加速度计,能够提供精确的角速度和加速度数据,常用于姿态检测、运动跟踪及导航等应用。该器件支持I2C和SPI通信接口,便于与微控制器(如Arduino、STM32等)连接,并且内置数字运动处理器(DMP),可以分担主控芯片的数据处理负担,提高系统效率。MPU6050广泛应用于无人机、机器人、可穿戴设备等领域,因其高性价比和易用性而受到开发者青睐。
技术实现
原理图
连线图
代码实现
main.c
/**********************************************************
1.实验名称:I2C读取MPU6050
2.实验环境:STM32F103C8T6最小系统板
3.实验内容:使用I2C协议与MPU6050通信,读取寄存器获取加速度计和陀螺仪数据,显示加速度和角速度
4.作者;abai
5.实验时间:2025-4-28
**********************************************************/
#include "stm32f10x.h" // Device header
#include "Delay.h" //延时函数
#include "OLED.h"
#include "MPU6050.h"int main(void)
{int16_t Acce_x,Acce_y,Acce_z,Gyro_x,Gyro_y,Gyro_z;/*OLED初始化*/OLED_Init();MPU6050_Init();while(1){MPU6050_GetValue(&Acce_x,&Acce_y,&Acce_z,&Gyro_x,&Gyro_y,&Gyro_z);OLED_ShowString(1,1,"Acce:");OLED_ShowString(1,9,"Gyro:");OLED_ShowSignedNum(2,1,Acce_x,5);OLED_ShowSignedNum(3,1,Acce_y,5);OLED_ShowSignedNum(4,1,Acce_z,5);OLED_ShowSignedNum(2,9,Gyro_x,5);OLED_ShowSignedNum(3,9,Gyro_y,5);OLED_ShowSignedNum(4,9,Gyro_z,5);}
}
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.h
#ifndef MPU6050_H
#define MPU6050_H#include "stm32f10x.h" // Device headervoid MPU6050_Init(void);
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_RedReg(uint8_t RegAddress);
void MPU6050_GetValue(int16_t* Acce_x, int16_t* Acce_y, int16_t* Acce_z,int16_t* Gyro_x, int16_t* Gyro_y, int16_t* Gyro_z);
void MPU6050_RedTemp(int16_t* Temp);#endif
MPU6050.c
#include "MPU6050.h"
#include "MPU6050_Reg.h"#define MPU6050_ADDRESS 0xD0void MPU6050_Init(void)
{MyI2C_Init();//解除睡眠MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0X01); //电源管理寄存器1//6个轴均不待机MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0X00); //电源管理寄存器2MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0X09); //采样率分频寄存器MPU6050_WriteReg(MPU6050_CONFIG,0x06); //不使用帧同步,低通滤波采用最平滑滤波即110BMPU6050_WriteReg(MPU6050_GYRO_CONFIG,0X18); //陀螺仪配置寄存器sMPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0X18); //加速度计寄存器}//指定地址写
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();
}
//指定地址读
uint8_t MPU6050_RedReg(uint8_t RegAddress)
{uint8_t ByteValue;MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS);MyI2C_ReceiveACK();MyI2C_SendByte(RegAddress);MyI2C_ReceiveACK();MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS | 0x01);MyI2C_ReceiveACK();ByteValue = MyI2C_ReceiveByte();MyI2C_SendACK(1);MyI2C_Stop();return ByteValue;
}//读取数据寄存器
void MPU6050_GetValue(int16_t* Acce_x, int16_t* Acce_y, int16_t* Acce_z,int16_t* Gyro_x, int16_t* Gyro_y, int16_t* Gyro_z)
{uint8_t Data_H,Data_L;//加速度计X轴Data_H = MPU6050_RedReg(MPU6050_ACCEL_XOUT_H);Data_L = MPU6050_RedReg(MPU6050_ACCEL_XOUT_L);*Acce_x = (Data_H << 8) | Data_L;//加速度计Y轴Data_H = MPU6050_RedReg(MPU6050_ACCEL_YOUT_H);Data_L = MPU6050_RedReg(MPU6050_ACCEL_YOUT_L);*Acce_y = (Data_H << 8) | Data_L;//加速度计Z轴Data_H = MPU6050_RedReg(MPU6050_ACCEL_ZOUT_H);Data_L = MPU6050_RedReg(MPU6050_ACCEL_ZOUT_L);*Acce_z = (Data_H << 8) | Data_L;//陀螺仪x轴Data_H = MPU6050_RedReg(MPU6050_GYRO_XOUT_H);Data_L = MPU6050_RedReg(MPU6050_GYRO_XOUT_L);*Gyro_x = (Data_H << 8) | Data_L;//陀螺仪y轴Data_H = MPU6050_RedReg(MPU6050_GYRO_YOUT_H);Data_L = MPU6050_RedReg(MPU6050_GYRO_YOUT_L);*Gyro_y = (Data_H << 8) | Data_L;//陀螺仪Z轴Data_H = MPU6050_RedReg(MPU6050_GYRO_ZOUT_H);Data_L = MPU6050_RedReg(MPU6050_GYRO_ZOUT_L);*Gyro_z = (Data_H << 8) | Data_L;
}void MPU6050_RedTemp(int16_t* Temp)
{int16_t Data_H,Data_L;Data_H = MPU6050_RedReg(MPU6050_TEMP_OUT_H);Data_L = MPU6050_RedReg(MPU6050_TEMP_OUT_L);*Temp = (Data_H << 8) | Data_L;
}
MyI2C.h
#ifndef MYI2C_H
#define MYI2C_H#include "stm32f10x.h" // Device header
#include "Delay.h"void MyI2C_Init(void);
void MyI2C_SCL_W(uint8_t BitValue);
void MyI2C_SDA_W(uint8_t BitValue);
uint8_t MyI2C_SDA_R(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
void MyI2C_SendACK(uint8_t ACKBit);
uint8_t MyI2C_ReceiveByte(void);
uint8_t MyI2C_ReceiveACK(void);#endif
MyI2C.c
#include "MyI2C.h"void MyI2C_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);
}void MyI2C_SCL_W(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);Delay_us(10);
}void MyI2C_SDA_W(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);Delay_us(10);
}uint8_t MyI2C_SDA_R(void)
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);Delay_us(10);return BitValue;
}void MyI2C_Start(void)
{MyI2C_SDA_W(1);MyI2C_SCL_W(1);MyI2C_SDA_W(0);MyI2C_SCL_W(0);
}void MyI2C_Stop(void)
{MyI2C_SDA_W(0);MyI2C_SCL_W(1);MyI2C_SDA_W(1);
}void MyI2C_SendByte(uint8_t Byte)
{uint8_t i;MyI2C_SCL_W(0);for(i=0;i<8;i++){MyI2C_SDA_W((Byte<<i)&0x80);MyI2C_SCL_W(1);MyI2C_SCL_W(0);}//释放SDAMyI2C_SDA_W(1);
}uint8_t MyI2C_ReceiveByte(void)
{uint8_t ByteValue = 0x00;uint8_t i;MyI2C_SDA_W(1);MyI2C_SCL_W(0);for(i=0;i<8;i++){MyI2C_SCL_W(1);if(MyI2C_SDA_R() == 1)ByteValue |= (0x80>>i);MyI2C_SCL_W(0);} return ByteValue;
}void MyI2C_SendACK(uint8_t ACKBit)
{MyI2C_SDA_W(ACKBit);MyI2C_SCL_W(1);MyI2C_SCL_W(0);
}uint8_t MyI2C_ReceiveACK(void)
{uint8_t ACKBit;//主机释放SDAMyI2C_SDA_W(1);MyI2C_SCL_W(1);ACKBit = MyI2C_SDA_R();MyI2C_SCL_W(0);return ACKBit;
}
OLED部分代码参照文章《STM32基础教程——OLED显示》http://【STM32基础教程 ——OLED显示 - CSDN App】https://blog.csdn.net/2301_80319641/article/details/145837521?sharetype=blog&shareId=145837521&sharerefer=APP&sharesource=2301_80319641&sharefrom=link
技术要点
I2C初始化
void MyI2C_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);
}
I2C初始化,目的将PB10和PB11分别作为SCL,SDA。初始化GPIO将它们均配置为开漏输出,因为从设备MPU6050的SDA和SCL引脚均配置了外部弱上拉。输出模式下依旧能够读取输入,当主机接收从机发送的数据时,SDA连接的引脚的数据依旧会在每个APB2时钟被采样到输入数据寄存器。在开漏模式下,对输入数据寄存器的访问可以得到I/O状态。但是推挽输出模式下不可以。这与它们的电气特性有关。
SCL输出和SDA输入输出控制
void MyI2C_SCL_W(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);Delay_us(10);
}void MyI2C_SDA_W(uint8_t BitValue)
{GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);Delay_us(10);
}uint8_t MyI2C_SDA_R(void)
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);Delay_us(10);return BitValue;
}
对模拟SDA和SCL的读写进行封装
起始信号
void MyI2C_Start(void)
{MyI2C_SDA_W(1);MyI2C_SCL_W(1);MyI2C_SDA_W(0);MyI2C_SCL_W(0);
}
I2C起始信号,在SCL高电平期间,SDA产生一个下降沿。
起始信号模拟过程:
- 将SDA拉高
- 将SCL拉高
- 将SDA拉低产生下降沿
- 将SCL拉低产生下降沿
停止信号
void MyI2C_Stop(void)
{MyI2C_SDA_W(0);MyI2C_SCL_W(1);MyI2C_SDA_W(1);
}
I2C停止信号:在SCL高电平期间,SDA产生一个上升沿。
终止信号模拟过程:
- 将SDA拉低
- 将SCL拉高
- 将SDA拉高
发送一个字节
void MyI2C_SendByte(uint8_t Byte)
{uint8_t i;MyI2C_SCL_W(0);for(i=0;i<8;i++){MyI2C_SDA_W((Byte<<i)&0x80);MyI2C_SCL_W(1);MyI2C_SCL_W(0);}//释放SDAMyI2C_SDA_W(1);
}
现将SCL拉低,允许SDA数据发生变化,然后使用for循环循环8次发送一个完整的数据字节,每次对数据字节先移位,然后将最高位发送(通过位设置/清除寄存器)。 将SCL拉高再拉低完成一位的发送。数据发送完毕后要将SDA拉高释放SDA。
读取一个字节
uint8_t MyI2C_ReceiveByte(void)
{uint8_t ByteValue = 0x00;uint8_t i;MyI2C_SDA_W(1);MyI2C_SCL_W(0);for(i=0;i<8;i++){MyI2C_SCL_W(1);if(MyI2C_SDA_R() == 1)ByteValue |= (0x80>>i);MyI2C_SCL_W(0);} return ByteValue;
}
读取字节数据之前,先释放SDA,然后将SCL拉低允许数据变化。使用for循环完成一个字节的读取,进入循环先将SCL拉高,保证数据稳定,经对应位的数据赋给临时变量,然后将SCL拉低,允许数据变化。读取完成后将数据字节返回。
发送应答位
void MyI2C_SendACK(uint8_t ACKBit)
{MyI2C_SDA_W(ACKBit);MyI2C_SCL_W(1);MyI2C_SCL_W(0);
}
先发送应答信号,然后将时钟信号拉高再拉低完成一个时钟周期,同时防止影响后续操作。
接收应答位
uint8_t MyI2C_ReceiveACK(void)
{uint8_t ACKBit;//主机释放SDAMyI2C_SDA_W(1);MyI2C_SCL_W(1);ACKBit = MyI2C_SDA_R();MyI2C_SCL_W(0);return ACKBit;
}
主机先释放SDA, 将SCL拉高,保证SDA数据稳定。读取ACK信号,最后将SCL拉低,防止影响后续操作。
MPU6050初始化
void MPU6050_Init(void)
{MyI2C_Init();//解除睡眠MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0X01); //电源管理寄存器1//6个轴均不待机MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0X00); //电源管理寄存器2MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0X09); //采样率分频寄存器MPU6050_WriteReg(MPU6050_CONFIG,0x06); //不使用帧同步,低通滤波采用最平滑滤波即110BMPU6050_WriteReg(MPU6050_GYRO_CONFIG,0X18); //陀螺仪配置寄存器MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0X18); //加速度计寄存器
}
MPU6050初始化,先初始化I2C,然后指定电源管理寄存器1写入命令0x01,解除MPU6050睡眠,再指定电源管理寄存器2,写入命令0x00,设置6个轴均不待机。然后指定采样分频率寄存器,写入命令0x09,采用10分频。制定MPOU6050 配置寄存器,写入命令0x06,不采用帧同步,采用最平滑的滤波对应位写入110B。对陀螺仪和加速度计寄存器均配置为不自检模式,量程选择最大范围。
指定地址写
//指定地址写
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起始信号,然后发从从机7位地址 加一位读写控制位,这里读写控制位清零,写入从机地址。然后接收应答位。发送要写入的寄存器的地址,接收应答位,再将数据写入,接收应答位,然后发送停止信号。
指定地址读
//指定地址读
uint8_t MPU6050_RedReg(uint8_t RegAddress)
{uint8_t ByteValue;MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS);MyI2C_ReceiveACK();MyI2C_SendByte(RegAddress);MyI2C_ReceiveACK();MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS | 0x01);MyI2C_ReceiveACK();ByteValue = MyI2C_ReceiveByte();MyI2C_SendACK(1);MyI2C_Stop();return ByteValue;
}
- 发送I2C起始信号
- 发送从机地址加一位读写控制位,读写控制位清零,写入数据。
- 接收应答
- 发送指定寄存器的地址
- 接收应答
- 发送I2C起始信号
- 发送从机地址及一位读写控制位,读写控制位置1,读取数据
- 接收应答标志位
- 接收数据
- 发送非应答信号
- 发送停止信号
读取数据寄存器
//读取数据寄存器
void MPU6050_GetValue(int16_t* Acce_x, int16_t* Acce_y, int16_t* Acce_z,int16_t* Gyro_x, int16_t* Gyro_y, int16_t* Gyro_z)
{uint8_t Data_H,Data_L;//加速度计X轴Data_H = MPU6050_RedReg(MPU6050_ACCEL_XOUT_H);Data_L = MPU6050_RedReg(MPU6050_ACCEL_XOUT_L);*Acce_x = (Data_H << 8) | Data_L;//加速度计Y轴Data_H = MPU6050_RedReg(MPU6050_ACCEL_YOUT_H);Data_L = MPU6050_RedReg(MPU6050_ACCEL_YOUT_L);*Acce_y = (Data_H << 8) | Data_L;//加速度计Z轴Data_H = MPU6050_RedReg(MPU6050_ACCEL_ZOUT_H);Data_L = MPU6050_RedReg(MPU6050_ACCEL_ZOUT_L);*Acce_z = (Data_H << 8) | Data_L;//陀螺仪x轴Data_H = MPU6050_RedReg(MPU6050_GYRO_XOUT_H);Data_L = MPU6050_RedReg(MPU6050_GYRO_XOUT_L);*Gyro_x = (Data_H << 8) | Data_L;//陀螺仪y轴Data_H = MPU6050_RedReg(MPU6050_GYRO_YOUT_H);Data_L = MPU6050_RedReg(MPU6050_GYRO_YOUT_L);*Gyro_y = (Data_H << 8) | Data_L;//陀螺仪Z轴Data_H = MPU6050_RedReg(MPU6050_GYRO_ZOUT_H);Data_L = MPU6050_RedReg(MPU6050_GYRO_ZOUT_L);*Gyro_z = (Data_H << 8) | Data_L;
}
读取加速度计和陀螺仪的数据寄存器,分别读取加速度计和陀螺仪的X,Y,Z轴高8位数据寄存器和低8位数据寄存器。传递指针参数存储相应的数据。
问题记录
暂无
相关文章:
STM32基础教程——软件I2C
目录 前言 I2C MPU6050 技术实现 原理图 连线图 代码实现 技术要点 I2C初始化 SCL输出和SDA输入输出控制 起始信号 停止信号 发送一个字节 读取一个字节 发送应答位 接收应答位 MPU6050初始化 指定地址写 指定地址读 读取数据寄存器 问题记录 前言 I2C …...
Xilinx FPGA | 管脚约束 / 时序约束 / 问题解析
注:本文为 “Xilinx FPGA | 管脚约束 / 时序约束 / 问题解析” 相关文章合辑。 略作重排,未整理去重。 如有内容异常,请看原文。 Xilinx FPGA 管脚 XDC 约束之:物理约束 FPGA技术实战 于 2020-02-04 17:14:53 发布 说明&#x…...
应用层自定义协议序列与反序列化
目录 一、网络版计算器 二、网络版本计算器实现 2.1源代码 2.2测试结果 一、网络版计算器 应用层定义的协议: 应用层进行网络通信能否使用如下的协议进行通信呢? 在操作系统内核中是以这种协议进行通信的,但是在应用层禁止以这种协议进行…...
大数据:数字时代的驱动力
📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 随着互联网和技术的迅猛发展,数据已经成为现代社会最宝贵的资源之一。大数据(Big Data)作为一种全新的信息资源,正以前所未有的方式改变着各个行业的运作模式,推动着社会的进步与创新。无论是金…...
java学习之数据结构:二、链表
本节介绍链表 目录 1.什么是链表 1.1链表定义 1.2链表分类 2.链表实现 2.1创建链表 1)手动创建 2)创建链表类进行管理链表的相关操作 2.2添加元素 1)头插法 2)尾插法 3)任意位置插入 2.3删除 2.4查找 1&…...
评估动态化烹饪工艺与营养实训室建设要点
在全民健康意识显著提升、健康饮食理念深度普及的时代背景下,烹饪工艺与营养实训室建设要点已不再局限于传统单一的技能训练模式。需以行业需求为导向,通过功能集成化设计推动革新 —— 将食品安全防控、营养科学分析、智能烹饪技术、餐饮运营管理等多元…...
Java学习手册:关系型数据库基础
一、关系型数据库概述 关系型数据库是一种基于关系模型的数据库,它将数据组织成一个或多个表(或称为关系),每个表由行和列组成。每一列都有一个唯一的名字,称为属性,表中的每一行是一个元组,代…...
吾爱出品 [Windows] EndNote 21.5.18513 汉化补丁
[Windows] EndNote 链接:https://pan.xunlei.com/s/VOPLLs6DqKNz-EoBSWVRTSmGA1?pwd9isc# Thomson Scientific公司推出了2025,本次的endnote21大概率是最后一个版本啦,现已决定进行更新。 本次采用的环境为python3.12,win11&am…...
Sentinel学习
sentinel是阿里巴巴研发的一款微服务组件,主要为用户提供服务保护,包括限流熔断等措施 (一)主要功能 流量控制(限流):比如限制1s内有多少请求能到达服务器,防止大量请求打崩服务器…...
【中间件】brpc_基础_execution_queue
execution_queue 源码 1 简介 execution_queue.h 是 Apache BRPC 中实现 高性能异步任务执行队列 的核心组件,主要用于在用户态线程(bthread)中实现任务的 异步提交、有序执行和高效调度。 该模块通过解耦任务提交与执行过程,提…...
Servlet(二)
软件架构 1. C/S 客户端/服务器端 2. B/S 浏览器/服务器端: 客户端零维护,开发快 资源分类 1. 静态资源 所有用户看到相同的部分,如:html,css,js 2. 动态资源 用户访问相同资源后得到的结果可能不一致,如:s…...
如何提升个人的思维能力?
提升个人的逻辑思维能力是一个系统性工程,需要长期训练和科学方法。以下是分阶段、可操作的详细建议,涵盖理论基础、日常训练和实战应用: 一、构建逻辑基础认知 1. 学习逻辑学核心理论 入门读物:《简单的逻辑学》麦克伦尼&am…...
[UVM]UVM中reg_map的作用及多个rem_map的使用案例
UVM中reg_map的作用及多个rem_map的使用案例 摘要:在 UVM (Universal Verification Methodology) 中,寄存器模型是用于验证 DUT (Design Under Test) 寄存器行为的重要工具。UVM 寄存器模型中的 uvm_reg_map(简称 reg_map)是寄存器模型的核心组成部分之一,用于定义…...
重新构想E-E-A-T:提升销售与搜索可见性的SEO策略
在2025年的数字营销环境中,谷歌的E-E-A-T(经验、专业性、权威性、可信度)已成为SEO和内容营销的核心支柱。传统的E-E-A-T优化方法通常聚焦于展示作者资质或获取反向链接,但这些策略可能不足以应对AI驱动的搜索和日益挑剔的用户需求…...
AI 采用金字塔(Sohn‘s AI Adoption Pyramid)
这张图是 Sohn 的 AI 采用金字塔(Sohn’s AI Adoption Pyramid) ,用于描述不同程度的 AI 应用层次,各层次意义如下: 金字塔层级 Level 1:业务角色由人类主导,AI 起辅助作用,如 AI …...
影刀RPA中新增自己的自定义指令
入门到实战明细 1. 影刀RPA自定义指令概述 1.1 定义与作用 影刀RPA的自定义指令是一种强大的功能,旨在提高流程复用率,让用户能够个性化定制指令,实现流程在不同应用之间的相互调用。通过自定义指令,用户可以将常用的、具有独立…...
驱动总裁v2.19(含离线版)驱动工具软件下载及安装教程
1.软件名称:驱动总裁 2.软件版本:2.19 3.软件大小:602 MB 4.安装环境:win7/win10/win11 5.下载地址: https://www.kdocs.cn/l/cdZMwizD2ZL1?RL1MvMTM%3D 提示:先转存后下载,防止资源丢失&am…...
SQL经典实例
第1章 检索记录 1.1 检索所有行和列 知识点:使用SELECT *快速检索表中所有列;显式列出列名(如SELECT col1, col2)提高可读性和可控性,尤其在编程场景中更清晰。 1.2 筛选行 知识点:通过WHERE子句过滤符合条…...
2025深圳杯(东三省)数学建模竞赛D题完整分析论文(共36页)(含模型、可运行代码、数据结果)
2025深圳杯数学建模竞赛D题完整分析论文 目录 摘 要 一、问题重述 二、问题分析 三、模型假设 四、符号定义 五、问题一模型的建立与求解 5.1 问题一模型的建立 5.1.1 问题建模背景 5.1.2 特征工程设计 5.1.3 分类模型结构与数学表达 5.2 问题一模型的求…...
大数据技术:从趋势到变革的全景探索
📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 在数字化时代的浪潮下,大数据已经不再是一个陌生的概念。从日常生活中的社交媒体,到企业决策支持系统,再到公共管理的大数据应用,它正在改变着我们的工作和生活方式。随着技术的进步,传统的数据…...
C++【内存管理】
C语言中的动态内存管理 int main() { int* p2(int*)calloc(4,sizeof(int)); int* p3(int*)realloc(p2,sizeof(int)*10); free(p3); }这里因为扩容数据小,所以是原地扩容,p2p3地址一样,不用free(p2) 如果扩容空间大就不是原地扩容,而是新找一块空间,然后拷贝 C内存管理方式 n…...
【Go类库分享】mcp-go Go搭建MCP服务
【Go类库分享】mcp-go Go搭建MCP服务 介绍 目前Go 生态圈有两个知名的开发 MCP 的库,一个是mark3labs/mcp-go,另一个是metoro-io/mcp-golang。 在介绍常用库之前,先来简单介绍一下mcp协议: MCP全称Model Context Protocol 模型上下文协议&a…...
人工智能发展史 — 物理学诺奖之 Hopfield 联想和记忆神经网络模型
目录 文章目录 目录1982 年,Hopfield 联想和记忆神经网络模型背景知识历史:霍普菲尔德简介神经学:大脑的联想记忆机制物理学:磁性材料的自旋玻璃理论和能量最小值函数 Hopfield 神经网络基本原理记忆存储(训练…...
Docker —— 技术架构的演进
Docker —— 技术架构的演进 技术架构演进总结单机架构优点缺点总结 应用数据分离架构优点缺点总结 应用服务集群架构1. Nginx2. HAProxy3. LVS(Linux Virtual Server)4. F5 BIG-IP对比总结选型建议 读写分离/主从分离架构1. MyCat简介 2. TDDLÿ…...
Docker与WSL2如何清理
文章目录 Docker与WSL2如何清理一、docker占据磁盘空间核心原因分析1. WSL2 虚拟磁盘的动态扩展特性2. Docker 镜像分层缓存与未清理资源 二、解决方案步骤 1:清理 Docker 未使用的资源步骤 2:手动压缩 WSL2 虚拟磁盘1. 关闭 WSL2 和 Docker Desktop2. 定…...
单片机嵌入式按键库
kw_btn库说明 本库主要满足嵌入式按键需求,集成了常用的按键响应事件:高电平、低电平、上升沿、下降沿、单击、双击、长按键事件。可以裸机运行,也可以配合实时操作系统运行。 本库开源连接地址:gitee连接 实现思路 本库采用C语…...
多多铃声 7.4| 拥有丰富的铃声曲库,满足不同用户的个性化需求,支持一键设置手机铃声
多多铃声是一款提供丰富铃声资源的应用程序,它拥有广泛的铃声曲库,涵盖各种风格和类型,能够满足不同用户的个性化需求。该应用程序支持分类浏览和热门榜单功能,让用户可以轻松找到当前最流行或自己感兴趣的铃声。此次分享的版本为…...
基于stm32的四旋翼飞行器:MPU6050讲解 · 上(参数读取)
大伙早上好,不知道大伙有没有飞行器情结,就是学习嵌入式就想做一个能飞的东西。小白兔不才,小白兔有啊,所以最近准备做一个简单的飞行器出来,如果失败了,那么这个系列就只能烂尾了,如果成功了&a…...
使用xlwings将excel表中将无规律的文本型数字批量转化成真正的数字
之前我写了一篇文章excel表中将无规律的文本型数字批量转化成真正的数字-CSDN博客 是使用excel自带的操作,相对繁琐。 今天使用xlwings操作,表格如下(有真正的数字,也有文本型数字,混在在一起)࿱…...
linux netlink实现用户态和内核态数据交互
1,内核态代码 #include <linux/module.h> #include <linux/netlink.h> #include <net/sock.h> #define NETLINK_TEST 31 struct sock *nl_sk NULL; static void nl_recv_msg(struct sk_buff *skb) { struct nlmsghdr *nlh; int pid; …...
学习黑客安全基础理论入门
准备安全课程内容 你已安装Kali和相关工具,并希望从基础开始学习安全。为了使课程更加互动,我会提供有趣的文本,并结合可视化内容,可能还会提供一些参考链接。内容方面,我会根据最新的中国网络安全法律作出更新&#…...
探索内容智能化的关键解决方案
北京先智先行科技有限公司拥有三款旗舰产品,分别是“先知大模型”、“先行AI商学院”以及“先知AIGC超级工场”。这三款产品在企业发展过程中扮演着重要角色。 北京先智先行科技有限公司围绕先知大模型等核心要素,构建了完备的业务体系。先知大模型私…...
学习黑客色即是空
二、Day 3 学习目标(保真版) 一句话目标: 学会用 Asset-Threat-Vulnerability-Risk (ATVR) 四件套给任何系统快速画“风险画像”,并能把它映射到黑客常说的 5 阶段攻击生命周期。 1. 30 分钟理论——ATVR 四件套 概念核心定义参考…...
【Java学习】关于springBoot的自动配置和起步依赖
关于springBoot的起步依赖:解决了spring框架中开发者配置依赖难的问题,各种依赖及版本的不同,可能引发不同的问题,使得开发者的精力大部分可能耗费在非业务代码中。所以springBoot起步依赖解决了各种依赖难的配置问题。 起步依赖…...
【LLaMA-Factory实战】1.3命令行深度操作:YAML配置与多GPU训练全解析
一、引言 在大模型微调场景中,命令行操作是实现自动化、规模化训练的核心手段。LLaMA-Factory通过YAML配置文件和多GPU分布式训练技术,支持开发者高效管理复杂训练参数,突破单机算力限制。本文将结合结构图、实战代码和生产级部署经验&#…...
【Mytais系列】介绍、核心概念
MyBatis 是一款优秀的 持久层框架,它通过简化 JDBC 操作、提供灵活的 SQL 映射能力,成为 Java 开发中处理数据库交互的核心工具之一。以下是 MyBatis 的核心框架和概念解析: 一、MyBatis 框架概述 1. 核心定位 作用:将 Java 对象…...
Vivado FPGA 开发 | 创建工程 / 仿真 / 烧录
注:本文为 “Vivado FPGA 开发 | 创建工程 / 仿真 / 烧录” 相关文章合辑。 略作重排,未整理去重。 如有内容异常,请看原文。 Vivado 开发流程(手把手教学实例)(FPGA) 不完美先生 于 2018-04-…...
PowerShell从5.1升级到7.X
文章目录 环境背景安装PowerShell 7.X其它启动PowerShell 5.1和7.X$PSVersionTable.PSVersion启动PowerShell 5.1时强制启动7.X 参考 环境 Windows 11 专业版 背景 PowerShell 5.1是Windows内置的,发布时间是2016 年。现在PowerShell版本已经到了7.5.1࿰…...
域名与官网的迷思:数字身份认证的全球困境与实践解方-优雅草卓伊凡
域名与官网的迷思:数字身份认证的全球困境与实践解方-优雅草卓伊凡 一、官网概念的法律与技术界定 1.1 官网的实质定义 当卓伊凡被问及”公司域名就是官网吗”这一问题时,他首先指出:”这相当于问’印着某公司logo的建筑就是该公司总部吗’…...
Vue实现成绩增删案例
Vue实现成绩增删案例 案例功能需求案例实现实现思路完整代码功能演示 案例小结 案例功能需求 1.通过vue渲染数据,将成绩的相关信息显示出来(学号,学科,成绩) 2.能够增加相关的成绩信息 3.能够删除相关的成绩信息 4.能…...
开源项目实战学习之YOLO11:ultralytics-cfg-models-rtdetr(十一)
👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 1. __init__.py2. model.py3. predict.py4. train.py5. val.py ultralytics-cfg-models-rtdetr 主要与 Ultralytics 库中 RTDETR(实时目标检测模型,R…...
【Bootstrap V4系列】学习入门教程之 组件-按钮(Buttons)
Bootstrap V4系列 学习入门教程之 组件-按钮(Buttons) 按钮(Buttons)一、示例二、可用作按钮的 HTML 标签三、带轮廓线的按钮四、按钮的尺寸五、活动状态六、禁用状态七、按钮插件切换状态Checkbox and radio buttons (…...
【java八股文】深入浅出synchronized优化原理
🔍 开发者资源导航 🔍🏷️ 博客主页: 个人主页📚 专栏订阅: JavaEE全栈专栏 synchronized优化原理 synchronized即使悲观锁也是乐观锁,拥有自适应性。 jvm内部会统计每个锁的竞争激烈程度&…...
裴蜀定理及其证明
裴蜀定理 对于所有整数 a a a和 b b b,存在: g c d ( a , b ) a x b y gcd(a,b)axby gcd(a,b)axby 并且 a x b y axby axby一定是 g c d ( a , b ) gcd(a,b) gcd(a,b)的倍数。 证明 定义一个集合: { a x b y | a x b y &…...
单片机嵌入式CAN库
kw_can库说明 本库是针对CAN类型数据的收发设计: 主要应用于大数据量(数据处理速度高于缓存CAN_RTX_FIFO_SIZE大小)接收不丢包可快速进出接收中断可跨线程调用发送接口。 本库开源连接地址:gitee连接 实现思路 本库采用C语言…...
基于 JSP 和 Servlet 的数字信息分析小应用
Java Web 实验:基于 JSP 和 Servlet 的数字信息分析小应用 一、实验目的 实现一个简单的 Java Web 应用,通过 JSP 表单收集用户输入的文本信息,提交至 Servlet 分析其中是否包含数字,并返回结果。掌握 JSP 与 Servlet 的协同工作…...
从零认识阿里云OSS:云原生对象存储的核心价值
引言 在云计算时代,海量数据的存储与管理成为企业数字化转型的关键命题。阿里云对象存储OSS(Object Storage Service)作为云原生的分布式存储服务,凭借其独特的架构设计和丰富的功能矩阵,正在成为企业构建数据湖、管理…...
2025年深圳杯数学建模(东三省)B题【颜色转换】原论文讲解
大家好呀,从发布赛题一直到现在,总算完成了2025年深圳杯数学建模(东三省)B题【颜色转换】完整的成品论文。 给大家看一下目录吧: 目录 摘 要: 一、问题重述 二.问题分析 2.1问题一 2.2问…...
开源语音合成和转换项目
开源语音合成和转换项目 大模型出来以后,语音合成和转换方面也有了很大的变化。在语音转换文字方面有Whisper、SeamlessM4T等;在语音合成方面有ChatTTS(中英文)、Orpheus TTS(仅仅支持英文)、Amphion&…...
考研408《计算机组成原理》复习笔记,第二章计算机性能
一、计算机各项性能指标 1、计算机系统整体的性能指标: 从宏观上看,整个计算机是由软件硬件共同性能决定的,但是【最主要的决定性的影响】还是来自于【硬件】 因为计算机组成原理主要讲【硬件】,那么我们也仅考虑【硬件性能】 2…...