【江协STM32】10-2/3 MPU6050简介、软件I2C读写MPU6050

news2025/1/9 6:18:38

1. MPU6050简介

  • MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景
  • 3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度
  • 3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度

           

1.1 主要参数

  • 16位ADC采集传感器的模拟信号,量化范围:-32768~32767
  • 加速度计满量程选择:±2、±4、±8、±16(g)
  • 陀螺仪满量程选择: ±250、±500、±1000、±2000(°/sec)
  • 可配置的数字低通滤波器
  • 可配置的时钟源
  • 可配置的采样分频

I2C从机地址:1101000(AD0=0)、1101001(AD0=1)
如果在程序中用十六进制表示,一般有两种表示方式,以1101000地址为例。第一种,直接把7位二进制数转换为十六进制,110 1000就是0x68,所以可以说MPU6050的从机地址是0x68。在I2C通信时序中,第一个字节的高7位是从机地址,最低位是读写位,所以如果认为0x68是从机地址,在发送第一个字节时,需要先把0x68左移1位,再按位或上读写位。第二种,把0x68左移1位后的数据当做从机地址。0x68左移1位后是0xD0,则MPU6050的从机地址就是0xD0,这时在实际发送第一个字节时,如果需要写,就直接把0xD0当作第一个字节,如果需要读,就或上0x01,即0xD1当作第一个字节。

1.2 硬件电路

MPU6050模块原理图

XCL、XDA:MPU6050的6轴传感器不够用,需要进行扩展时使用,通常用于外接磁力计或者气压计,MPU6050的主机接口可以直接访问这些扩展芯片的数据,在MPU6050内有DMP单元进行数据融合和姿态解算。

1.3 模块框图

2. 软件I2C读写MPU6050

2.1 接线图

SCL接PB10、SDA接PB11。由于本节代码使用的是软件I2C,即用普通的GPIO口手动翻转电平,不需要STM32内部的外设资源支持,所以这里端口可以任意指定,也可以SCL接PA8,SDA接PA9等。

根据I2C协议的硬件规定,SCL和SDA均应外挂上拉电阻,由于模块内部自带上拉电阻,所以不需要外接。XCL和XDA用于扩展的接口,不使用。AD0引脚修改从机地址的最低位,由于模块内接了下拉电阻,所以引脚悬空相当于接地。INT中断信号输出脚,不使用。

2.2 代码

程序的整体架构:

  1. 首先,建立I2C通信层的.c和.h模块。在通信层中,写好I2C底层的GPIO初始化和6个时序基本单元(起始、终止、发送一个字节、接收一个字节、发送应答、接收应答)。
  2. 再建立MPU6050的.c的.h模块。在此层,基于I2C通信模块实现指定地址读、指定地址写,再实现写寄存器对芯片进行配置、读寄存器得到传感器数据。
  3. 最后在main.c中调用MPU6050模块,初始化,获取数据,显示数据。

由于使用的是软件I2C,所以stm32的I2C库函数就不需要看了,只需要使用GPIO的读写函数。 

MyI2C.c(防止和库函数的函数重名)

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

void MyI2C_Init(void)
{
    //  需要完成2个任务:
    //  ① 把SCL和SDA均初始化为开漏输出模式;
    //  ② 把SCL和SDA置高电平。
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//	开漏输出(虽然名字是"输出",但开漏输出模式仍然可以输入)
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;//  SCL是PB10,SDA是PB11
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);//   置SCL和SDA高电平
}

//  对操作端口的库函数进行封装
void MyI2C_W_SCL(uint8_t BitValue)//    写SCL。参数给1或0,就可以释放或拉低SCL
{
    GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
    Delay_us(10);// 如果单片机主频较快,可以加延时,防止从机跟不上
}

void MyI2C_W_SDA(uint8_t BitValue)//    写SDA。参数给1或0,就可以释放或拉低SDA
{
    GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
    Delay_us(10);
}

uint8_t MyI2C_R_SDA(void)// 读SDA
{
    uint8_t BitValue;
    BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
    Delay_us(10);
    return BitValue;
}

//  接下来完成I2C的6个时序基本单元
//
//  1、起始条件
//  首先确保SCL和SDA释放,然后先拉低SDA,再拉低SCL
void MyI2C_Start(void)
{
    MyI2C_W_SDA(1);//   释放SDA
    MyI2C_W_SCL(1);//   释放SCL
    
    MyI2C_W_SDA(0);//   先拉低SDA
    MyI2C_W_SCL(0);//   再拉低SCL
}

//  2、终止条件
//  SCL高电平期间,SDA从低电平切换到高电平
void MyI2C_Stop(void)
{
    MyI2C_W_SDA(0);//   先拉低SDA,避免SDA之前为高电平时释放无法产生上升沿
    MyI2C_W_SCL(1);//   再释放SCL
    MyI2C_W_SDA(1);//   再释放SDA
}

//  3、发送一个字节
//  SCL低电平,SDA变换数据;SCL高电平,SDA保持数据稳定。放完1位后,释放SCL,拉低SCL,驱动时钟运转
void MyI2C_SendByte(uint8_t Byte)
{
    uint8_t i;
    for(i = 0; i < 8; i++)
    {
        MyI2C_W_SDA(Byte & (0x80 >> i));//  首先趁SCL低电平,把Byte的最高位放在SDA线上。下一轮就是把次高位放在SDA线上
        //  取Byte最高位为Byte&0x80,取次高位为Byte&0x40...在循环中,使用右移i位即可实现按位与数值的变换
        MyI2C_W_SCL(1);//   再释放SCL。释放后从机会立刻把SDA的数据读走
        MyI2C_W_SCL(0);//   拉低SCL
    }
}

//  4、接收一个字节
//  SCL低电平,从机把数据放到SDA上,为了防止主机干扰从机写入数据,主机需要先释放SDA,释放SDA也相当于切换为输入模式
//  然后主机在SCL高电平期间读取SDA,再拉低SCL。这样重复8次,主机就能读到一个字节了。
uint8_t MyI2C_ReceiveByte(void)
{
    uint8_t i, Byte = 0x00;
    MyI2C_W_SDA(1);//   主机释放SDA
    for(i = 0; i < 8; i++)
    {
        MyI2C_W_SCL(1);//   主机释放SCL
        if(MyI2C_R_SDA() == 1)//    主机读取数据
        {
            Byte |= (0x80 >> i);// 如果读取到1,就从高到低把Byte对应位置1
        }
        MyI2C_W_SCL(0);//   读取1位后拉低SCL,这时从机会把下一位数据放到SDA上
    }
    return Byte;
}

//  5、发送应答
//  其实就是发送一个字节的简化
void MyI2C_SendAck(uint8_t AckBit)
{
    MyI2C_W_SDA(AckBit);//  函数进入时,SCL低电平,主机把AckBit放到SDA上
    MyI2C_W_SCL(1);//   再释放SCL。从机读取应答
    MyI2C_W_SCL(0);//   拉低SCL,进入下一个时序单元
}

//  6、接收应答
//  其实就是接收一个字节的简化
uint8_t MyI2C_ReceiveAck(void)
{
    uint8_t AckBit;
    MyI2C_W_SDA(1);//   函数进入时SCL低电平,主机释放SDA防止干扰从机。同时,从机把应答位放在SDA上
    MyI2C_W_SCL(1);//   主机释放SCL,读取应答位
    AckBit = MyI2C_R_SDA();
    MyI2C_W_SCL(0);//   读取后拉低SCL,进入下一个时序单元
    return AckBit;
}

MyI2C.h

#ifndef __MYI2C_H
#define __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);

#endif

MPU6050.c

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

#define MPU6050_ADDRESS 0xD0

// 指定地址写
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_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();
    
    return Data;
}

void MPU6050_Init(void)
{
    MyI2C_Init();
    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;
}

MPU6050.h

#ifndef __MPU6050_H
#define __MPU6050_H

void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);

void MPU6050_Init(void);
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
                        int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);
uint8_t MPU6050_GetID(void);

#endif

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

main.c

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

int16_t AX, AY, AZ, GX, GY, GZ;

int main(void)
{
    OLED_Init();
    MPU6050_Init();
    
    OLED_ShowString(1, 1, "ID:");
    OLED_ShowHexNum(1, 4, MPU6050_GetID(), 2);
    
    OLED_ShowString(1, 7, "Acc|Gyro");
    OLED_ShowString(2, 1, "X       |");
    OLED_ShowString(3, 1, "Y       |");
    OLED_ShowString(4, 1, "Z       |");
    
    while(1)
    {
        MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
        OLED_ShowSignedNum(2, 3, AX, 5);
        OLED_ShowSignedNum(3, 3, AY, 5);
        OLED_ShowSignedNum(4, 3, AZ, 5);
        OLED_ShowSignedNum(2, 10, GX, 5);
        OLED_ShowSignedNum(3, 10, GY, 5);
        OLED_ShowSignedNum(4, 10, GZ, 5);
    }
}

其他引用的头文件和c代码可在此处查阅:Delay.h(【江协STM32】3-2 LED闪烁&LED流水灯&蜂鸣器,第1.3节) 

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

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

相关文章

裸机器搭建k8s部署 1.28.10版本

问了搭建k8s集群踩了很多坑&#xff0c;问题主要出现在网络插件处&#xff0c;因此主要是master节点操作问题。重新走一下流程整理一下笔记。 目录 虚拟机准备 虚拟机 系统版本信息 修改镜像地址 配置静态ip 关闭防火前和交换分区 转发 IPv4 并让 iptables 看到桥接流量…

HCIE-day10-ISIS

ISIS ISIS&#xff08;Intermediate System-to-Intermediate System&#xff09;中间系统到中间系统&#xff0c;属于IGP&#xff08;内部网关协议&#xff09;&#xff1b;是一种链路状态协议&#xff0c;使用最短路径优先SPF算法进行路由计算&#xff0c;与ospf协议有很多相…

70.爬楼梯 python

爬楼梯 题目题目描述示例 1&#xff1a;示例 2&#xff1a;提示&#xff1a; 题解思路分析Python 实现代码空间优化代码解释提交结果 题目 题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff…

优质内容在个人IP运营中的重要性:以开源AI智能名片商城小程序为应用实例的深度探讨

摘要&#xff1a;在数字化时代&#xff0c;个人品牌&#xff08;IP&#xff09;的塑造与传播已成为各行各业提升影响力、吸引用户关注、促进商业转化的关键策略。优质内容作为连接个人IP与目标受众的桥梁&#xff0c;其在个人IP运营中的重要性不言而喻。本文旨在深入探讨优质内…

有限元分析学习——Anasys Workbanch第一阶段笔记(8)水杯案例的对称与轴对称处理

目录 1 序言 2 对称处理 2.1 模型处理 2.2 网格划分、约束载荷及接触设置 2.3 计算结果 3 轴对称处理 3.1 对称与轴对称概念 3.2 轴对称问题的应用 3.2.1 创建分析案例 3.2.2 导入并处理模型 3.2.3 网格划分、约束载荷及接触设置 3.2.4 后处理计算结果 1 序言 本章…

网络安全-web渗透环境搭建-BWAPP(基础篇)

01--所需系统环境&#xff1a; 虚拟主机系统部署&#xff08;vmware&#xff0c;虚拟主机创建、虚拟主机网络配置&#xff08;桥接&#xff0c;便于网络中多个主机都能访问虚拟主机&#xff09;、虚拟软件功能&#xff0c;快照、克隆、镜像文件加载&#xff0c;ova文件制作&am…

Java 实现 Elasticsearch 查询当前索引全部数据

Java 实现 Elasticsearch 查询当前索引全部数据 需求背景通常情况Java 实现查询 Elasticsearch 全部数据写在最后 需求背景 通常情况下&#xff0c;Elasticsearch 为了提高查询效率&#xff0c;对于不指定分页查询条数的查询语句&#xff0c;默认会返回10条数据。那么这就会有…

算能AI计算服务器SE5设备树的二次修改实操

目录 1.大纲 2.实操 2.下载对应文件包 3.解包启动文件 4.修改对应的设备树 5.重启后 教程链接&#xff1a;https://github.com/sophgo/sophon-tools/tree/main/source/pmemory_edit 1.大纲 2.实操 2.1 选择串口&#xff0c;波特率115200&#xff0c;重启设备&#xff0…

Python的Matplotlib库应用(超详细教程)

目录 一、环境搭建 1.1 配置matplotlib库 1.2 配置seaborn库 1.3 配置Skimage库 二、二维图像 2.1 曲线&#xff08;直线&#xff09;可视化 2.2 曲线&#xff08;虚线&#xff09;可视化 2.3 直方图 2.4 阶梯图 三、三维图像 3.1 3D曲面图 3.2 3D散点图 3.3 3D散…

Flutter:封装一个自用的bottom_picker选择器

效果图&#xff1a;单列选择器 使用bottom_picker: ^2.9.0实现&#xff0c;单列选择器&#xff0c;官方文档 pubspec.yaml # 底部选择 bottom_picker: ^2.9.0picker_utils.dart AppTheme&#xff1a;自定义的颜色 TextWidget.body Text() <Widget>[].toRow Row()下边代…

牛客网刷题 ——C语言初阶(6指针)——BC106 上三角矩阵判定

1. 题目描述——BC106 上三角矩阵判定 牛客网OJ题链接 描述 KiKi想知道一个n阶方矩是否为上三角矩阵&#xff0c;请帮他编程判定。上三角矩阵即主对角线以下的元素都为0的矩阵&#xff0c;主对角线为从矩阵的左上角至右下角的连线。 示例 输入&#xff1a; 3 1 2 3 0 4 5 0 0…

力扣刷题:数组OJ篇(下)

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 目录 1.轮转数组&#xff08;1&#xff09;题目描述…

《(限)战斗天赋VR》V02122024官方中文学习版

《(限)战斗天赋VR》官方中文版https://pan.xunlei.com/s/VODaeHDXSxw4BNDNl39dxJXnA1?pwdusm5# 一款具有挑战性的基于物理的roguelite剑术格斗游戏&#xff0c;你可以在一个超级无缝的程序地牢中创造自己的战斗风格&#xff0c;体验无与伦比的游戏体验。有80多种敌人变种、10…

《Spring Framework实战》3:概览

欢迎观看《Spring Framework实战》视频教程 Spring Framework 为基于现代 Java 的企业应用程序提供了全面的编程和配置模型 - 在任何类型的部署平台上。 Spring 的一个关键要素是应用程序级别的基础设施支持&#xff1a;Spring 专注于企业应用程序的 “管道”&#xff0c;以便…

基于SpringBoot+Vue动漫交流系统平台设计和实现

系统介绍&#xff1a; 免费的源码&#xff0c;我刚找到的&#xff0c;给大家推荐一下源码下载 动漫交流系统平台是一个专为动漫爱好者设计的在线社区平台&#xff0c;旨在为用户提供一个便捷的环境来分享动漫。这个系统包含了多种功能&#xff0c;如动漫分类、动漫视频、动漫…

微信小程序获取图片使用session(上篇)

概述&#xff1a; 我们开发微信小程序&#xff0c;从后台获取图片现实的时候&#xff0c;通常采用http get的方式&#xff0c;例如以下代码 <image class"user_logo" src"{{logoUrl}}"></image>变量logoUrl为ur图片l的请求地址 但是对于很多…

HTML5实现好看的中秋节网页源码

HTML5实现好看的中秋节网页源码 前言一、设计来源1.1 网站首页界面1.2 登录注册界面1.3 节日由来界面1.4 节日习俗界面1.5 节日文化界面1.6 节日美食界面1.7 节日故事界面1.8 节日民谣界面1.9 联系我们界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载结束语 HTML5实现好看…

Redis 数据库源码分析

Redis 数据库源码分析 我们都知道Redis是一个 <key,value> 的键值数据库&#xff0c;其实也就是一个 Map。如果让我来实现这样一个 Map&#xff0c;我肯定是用数组&#xff0c;当一个 key 来的时候&#xff0c;首先进行 hash 运算&#xff0c;接着对数据的 length 取余&…

【海南省】乡镇界arcgis格式shp数据乡镇名称和编码gis矢量数据

《海南省乡镇界GIS矢量数据详解》 GIS&#xff08;Geographic Information System&#xff09;是一种强大的地理信息系统&#xff0c;它结合了地图、数据库和计算机技术&#xff0c;用于管理和分析空间数据。在本篇中&#xff0c;我们将聚焦于一个特定的GIS数据集——“最新海…

JVM 核心知识点总结

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…