当前位置: 首页 > news >正文

stm32入门学习10-I2C和陀螺仪模块

(一)I2C通信

(1)通信方式

I2C是一种同步半双工的通信方式,同步指的是通信双方时钟为一个时钟,半双工指的是在同一时间只能进行接收数据或发送数据,其有一条时钟线(SCL)和一条数据线(SDA),使用I2C通信要遵守一定的规定

其收发数据有几种模式

(1)开启传输:在时钟线(SCL)为1时将数据线(SDA)由1转为0;

(2)结束传输:在时钟线(SCL)为1时将数据线(SDA)由0转为1;

(3)传输数据:在时钟线(SCL)为0时在数据线(SDA)中写入数据,将时钟线(SCL)由0变为1发送;

(4)接收数据:先释放数据线(SDA置1),后将时钟线(SCL)由0变为1后读取数据线(SDA);

(5)接收响应:I2C在主机传输8位数据时会给主机响应,为1则接收失败,为0则接收成功,主机需要接收响应,接收方式和接收数据相同;

(6)发送响应:I2C在主机接收8位数据时要给从机响应,给1则从机停止继续向主机发送,给0则继续向主机发送数据

这里可以注意到,I2C通信中只有开始和结束时在时钟线(SCL)为1时操作的数据线(SDA),其余都是在时钟线为0时操作数据线;

通信过程中其有一定的协议

(1)发送数据:(1)开启传输;(2)写入外设地址写模式;(3)接收从机响应;(4)写入外设寄存器的地址;(5)接收响应;(6)写入数据;(7)接收响应;(8)结束传输

(2)接收数据:(1)开启传输;(2)写入外设地址写模式;(3)接收从机响应;(4)写入外设寄存器的地址;(5)接收响应;(6)重新开启传输;(7)写入外设地址读模式;(8)接收响应;(9)读取数据;(10)发送响应;(11)结束传输

(2)软件模拟

这样我们就可以用代码来模拟I2C通信,我们选择时钟线(SCL)接在PA0口上,数据线(SDA)接在PA1口上

#define SCL GPIO_Pin_0
#define SDA GPIO_Pin_1

(1)时钟打开和初始化

这里要注意的是I2C是采用开漏输出的,即默认为高电平,我们这里端口输出模式也要选择开漏输出

void i2c_init()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef gpio_init;gpio_init.GPIO_Mode = GPIO_Mode_Out_OD;gpio_init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;gpio_init.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &gpio_init);GPIO_SetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_1);
}

(2)置高低电平和读取数据

为了方便给SCL和SDA高电平或低电平,封装几个函数方便后面调用,分别为置某个端口高电平、置某个端口低电平、读取某个端口的值

void set(uint16_t io)
{GPIO_SetBits(GPIOA, io);Delay_us(10);
}void reset(uint16_t io)
{GPIO_ResetBits(GPIOA, io);Delay_us(10);
}unsigned char read(uint16_t io)
{unsigned char result;result = GPIO_ReadInputDataBit(GPIOA, io);return result;
}

(3)开启传输

和前面讲的一样,我们要在SCL高电平的情况下把SDA由高电平拉到低电平,然后拉低SCL,为后面的传输数据做准备

void i2c_start()
{set(SDA);set(SCL);reset(SDA);reset(SCL);
}

由于我们不知道在开始前数据线和时钟线是否一定为高电平,因此我们先把两者置高电平后再拉低

(4)结束传输

我们要在SCL为高电平的情况下把SDA由低电平上拉为高电平

void i2c_end()
{reset(SDA);set(SCL);set(SDA);
}

我们不知道在要结束的时候SDA是否为低电平,因此我们先拉低SDA,为后面的上拉做准备,至于我们的时钟线SCL,我们确保其在除了结束传输这一步外每一步结束时都为低电平,可以注意一下其他步骤的代码

(5)传输一个字节

我们在时钟线SCL为低电平的时候把数据放在数据线SDA上,然后把SCL拉高为高电平即完成传输,循环8次,传输一个字节

void i2c_send_byte(unsigned char message)
{unsigned char i;for(i = 0; i < 8; i++){if ((message & (0x80>>i)) == 0)reset(SDA);elseset(SDA);set(SCL);reset(SCL);}
}

这里的数据传输为高位先行,先传输高位,我们使用“与”的方法依次提取从高位到低位的八位bit,将数据message和1000 0000 的右移i位相与;

(6)接收一个字节

先释放数据线SDA(将其置1),后在SCL置1后读取SDA,循环八次,读取一个字节

unsigned char i2c_receive_byte()
{unsigned char i;unsigned char message = 0x00;set(SDA);for (i = 0; i < 8; i++){set(SCL);if (read(SDA) == 1)message |= (0x80>>i);reset(SCL);}return message;
}

这里使用“或”的方式来接收数据,如果接收到i位数据为1,则将message与1000 0000 右移i位相或,第i位置1,其余位保持不变;

(7)发送应答

发送应答和发送单个bit做法相同,需要在SCL低电平期间将应答置于SDA中,再SCL上拉发送

void i2c_send_ack(unsigned char ack)
{if (ack == 0)reset(SDA);else set(SDA);set(SCL);reset(SCL);
}

(8)接收应答

接收应答和接收单个bit做法相同,需要先释放数据线SDA(置1),在SCL置高电平时读取数据线的值

unsigned char i2c_receive_ack()
{unsigned char ack;set(SDA);set(SCL);ack = read(SDA);reset(SCL);return ack;
}

(3)封装

这样I2C的几种基本通信方式就写好了,我们只需要把其封装,再按照发送接收的规定,就可以读取改写外设寄存器,最后.c和.h文件可以这样

#include "stm32f10x.h"                  // Device header
#include "Delay.h"#define SCL GPIO_Pin_0
#define SDA GPIO_Pin_1void i2c_init()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef gpio_init;gpio_init.GPIO_Mode = GPIO_Mode_Out_OD;gpio_init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;gpio_init.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &gpio_init);GPIO_SetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_1);
}void set(uint16_t io)
{GPIO_SetBits(GPIOA, io);Delay_us(10);
}void reset(uint16_t io)
{GPIO_ResetBits(GPIOA, io);Delay_us(10);
}unsigned char read(uint16_t io)
{unsigned char result;result = GPIO_ReadInputDataBit(GPIOA, io);return result;
}void i2c_start()
{set(SDA);set(SCL);reset(SDA);reset(SCL);
}void i2c_end()
{reset(SDA);set(SCL);set(SDA);
}void i2c_send_byte(unsigned char message)
{unsigned char i;for(i = 0; i < 8; i++){if ((message & (0x80>>i)) == 0)reset(SDA);elseset(SDA);set(SCL);reset(SCL);}
}unsigned char i2c_receive_byte()
{unsigned char i;unsigned char message = 0x00;set(SDA);for (i = 0; i < 8; i++){set(SCL);if (read(SDA) == 1)message |= (0x80>>i);reset(SCL);}return message;
}void i2c_send_ack(unsigned char ack)
{if (ack == 0)reset(SDA);else set(SDA);set(SCL);reset(SCL);
}unsigned char i2c_receive_ack()
{unsigned char ack;set(SDA);set(SCL);ack = read(SDA);reset(SCL);return ack;
}
#ifndef __I2C_H__
#define __I2C_H__void i2c_init(void);
void i2c_start(void);
void i2c_end(void);
void i2c_send_byte(unsigned char message);
unsigned char i2c_receive_byte(void);
void i2c_send_ack(unsigned char ack);
unsigned char i2c_receive_ack(void);#endif

(二)MPU-6050

MPU-6050是一个可以测量加速度和角速度的陀螺仪加速度计,其外设写地址为0xD0,外设的读地址为0xD1,其测量的加速度和角速度存在其内部寄存器中,我们通过I2C访问其内部寄存器来读取测量值

经过我们前面的程序,我们已经有了这几个函数:(1)开启传输函数;(2)结束传输函数;(3)发送一个字节函数;(4)接收一个字节函数;(5)发送应答函数;(6)接收应答函数;通过这些函数我们就可以操作寄存器了

(1)写某个位置的一个字节

按照我们之前的说法,我们要写某个寄存器,我们先要开启传输,发送外设写地址,接收应答,发送寄存器地址,接收应答,写入数据,接收应答,结束传输,对应下面的每一行代码

void mpu_write(unsigned char address, unsigned char message)
{i2c_start();i2c_send_byte(mpu6050_address);ack = i2c_receive_ack();i2c_send_byte(address);ack = i2c_receive_ack();i2c_send_byte(message);ack = i2c_receive_ack();i2c_end();Delay_us(10);
}

(2)读某个位置的一个字节

和前面说的一样,要读取某个外设的寄存器,我们需要开启传输,发送外设写地址,接收应答,发送寄存器地址,接收应答,重新开启传输,发送外设读地址,接收应答,读取数据,发送应答,结束传输,对应代码为

uint8_t mpu_read(unsigned char address)
{uint8_t message = 0x00;i2c_start();i2c_send_byte(mpu6050_address);ack = i2c_receive_ack();i2c_send_byte(address);ack = i2c_receive_ack();i2c_start();i2c_send_byte(mpu6050_address | 0x01);ack = i2c_receive_ack();message = i2c_receive_byte();i2c_send_ack(1);i2c_end();return message;
}

(3)初始化MPU-6050

MPU-6050默认为睡眠模式,如果不初始化不会进行数据转换,这里直接操作寄存器转换如下,主要为停止睡眠模式、选择时钟等,顺便把I2C也在此初始化

void mpu_init()
{i2c_init();mpu_write(0x6B, 0x01);	//PWR_MGMT_1 -> 0000 0001mpu_write(0x6C, 0x00);	//PWR_MGMT_2 -> 0000 0000mpu_write(0x19, 0x09);	//SMPLRT_DIV -> 0000 1001mpu_write(0x1A, 0x06);	//CONFIG -> 0000 0110mpu_write(0x1B, 0x18);	//GYRO_CONFIG -> 0001 1000mpu_write(0x1C, 0x18);	//ACCEL_CONFIG -> 0001 1000
}

(4)传输数据

MPU-6050中有六个数据,分别为x、y、z轴加速度,x、y、z轴的角速度,我们需要一次返回六个变量,可以用数组,但这里用一个结构体来返回六个变量

typedef struct inf
{int x_acceleration;int y_acceleration;int z_acceleration;int x_angular_velocity;int y_angular_velocity;int z_angular_velocity;
} information;

这六个变量都是16位数据,其高八位和低八位存在不同的寄存器中

我们可以通过把高位左移8位“或”低位的方式来读取其16位寄存器

这里记录下常见的错误:如果你在读取某些有符号数据时其逼近但不超过最大值,但是却从来没有为负数,这可能是因为在位数小的数据强行转换为位数大的数据中出现的错误,其会在位数小数据的前面自动补0,而众所周知我们负数的补码是要在前面补1的,比如8位有符号数据1111 1101为-3,若将其强行转化为16位有符号数据则默认为0000 0000 1111 1101,这就是一个很大的正数了,我们要的16位-3应该为1111 1111 1111 1101

information mpu_get_inf()
{uint8_t inf_L;uint8_t inf_H;information infor;inf_H = mpu_read(0x3B);inf_L = mpu_read(0x3C);infor.x_acceleration = (inf_H<<8) | inf_L;inf_H = mpu_read(0x3D);inf_L = mpu_read(0x3E);infor.y_acceleration = (inf_H<<8) | inf_L;inf_H = mpu_read(0x3F);inf_L = mpu_read(0x40);infor.z_acceleration = (inf_H<<8) | inf_L;inf_H = mpu_read(0x43);inf_L = mpu_read(0x44);infor.x_angular_velocity = (inf_H<<8) | inf_L;inf_H = mpu_read(0x45);               inf_L = mpu_read(0x46);               infor.y_angular_velocity = (inf_H<<8) | inf_L;inf_H = mpu_read(0x47);               inf_L = mpu_read(0x48);               infor.z_angular_velocity = (inf_H<<8) | inf_L;if (infor.x_acceleration & 0x8000){infor.x_acceleration |= 0xFFFF0000;}if (infor.y_acceleration & 0x8000){infor.y_acceleration |= 0xFFFF0000;}if (infor.z_acceleration & 0x8000){infor.z_acceleration |= 0xFFFF0000;}if (infor.x_angular_velocity & 0x8000){infor.x_angular_velocity |= 0xFFFF0000;}if (infor.y_angular_velocity & 0x8000){infor.y_angular_velocity |= 0xFFFF0000;}if (infor.z_angular_velocity & 0x8000){infor.z_angular_velocity |= 0xFFFF0000;}return infor;
}

最后的一连串if就是来解决类型转化间的错误的

这样我们就成功读到了寄存器内的数据并返回一个包含所有数据的结构体

(5)封装与声明

最后的.c 和 .h代码如下

#include "stm32f10x.h"                  // Device header
#include "i2c.h"
#include "Delay.h"#define mpu6050_address 0xD0
unsigned char ack;void mpu_write(unsigned char address, unsigned char message)
{i2c_start();i2c_send_byte(mpu6050_address);ack = i2c_receive_ack();i2c_send_byte(address);ack = i2c_receive_ack();i2c_send_byte(message);ack = i2c_receive_ack();i2c_end();Delay_us(10);
}uint8_t mpu_read(unsigned char address)
{uint8_t message = 0x00;i2c_start();i2c_send_byte(mpu6050_address);ack = i2c_receive_ack();i2c_send_byte(address);ack = i2c_receive_ack();i2c_start();i2c_send_byte(mpu6050_address | 0x01);ack = i2c_receive_ack();message = i2c_receive_byte();i2c_send_ack(1);i2c_end();return message;
}void mpu_init()
{i2c_init();mpu_write(0x6B, 0x01);	//PWR_MGMT_1 -> 0000 0001mpu_write(0x6C, 0x00);	//PWR_MGMT_2 -> 0000 0000mpu_write(0x19, 0x09);	//SMPLRT_DIV -> 0000 1001mpu_write(0x1A, 0x06);	//CONFIG -> 0000 0110mpu_write(0x1B, 0x18);	//GYRO_CONFIG -> 0001 1000mpu_write(0x1C, 0x18);	//ACCEL_CONFIG -> 0001 1000
}typedef struct inf
{int x_acceleration;int y_acceleration;int z_acceleration;int x_angular_velocity;int y_angular_velocity;int z_angular_velocity;
} information;information mpu_get_inf()
{uint8_t inf_L;uint8_t inf_H;information infor;inf_H = mpu_read(0x3B);inf_L = mpu_read(0x3C);infor.x_acceleration = (inf_H<<8) | inf_L;inf_H = mpu_read(0x3D);inf_L = mpu_read(0x3E);infor.y_acceleration = (inf_H<<8) | inf_L;inf_H = mpu_read(0x3F);inf_L = mpu_read(0x40);infor.z_acceleration = (inf_H<<8) | inf_L;inf_H = mpu_read(0x43);inf_L = mpu_read(0x44);infor.x_angular_velocity = (inf_H<<8) | inf_L;inf_H = mpu_read(0x45);               inf_L = mpu_read(0x46);               infor.y_angular_velocity = (inf_H<<8) | inf_L;inf_H = mpu_read(0x47);               inf_L = mpu_read(0x48);               infor.z_angular_velocity = (inf_H<<8) | inf_L;if (infor.x_acceleration & 0x8000){infor.x_acceleration |= 0xFFFF0000;}if (infor.y_acceleration & 0x8000){infor.y_acceleration |= 0xFFFF0000;}if (infor.z_acceleration & 0x8000){infor.z_acceleration |= 0xFFFF0000;}if (infor.x_angular_velocity & 0x8000){infor.x_angular_velocity |= 0xFFFF0000;}if (infor.y_angular_velocity & 0x8000){infor.y_angular_velocity |= 0xFFFF0000;}if (infor.z_angular_velocity & 0x8000){infor.z_angular_velocity |= 0xFFFF0000;}return infor;
}
#ifndef __MPU_H__
#define __MPU_H__typedef struct inf
{int x_acceleration;int y_acceleration;int z_acceleration;int x_angular_velocity;int y_angular_velocity;int z_angular_velocity;
} information;//extern struct information;
void mpu_init(void);
information mpu_get_inf(void);#endif

(三)主函数调用

由于我们的MPU-6050初始化中已经包含了I2C的初始化,我们只要引用MPU头文件即可,我们在第1列显示加速度,在第9列显示角速度

#include "stm32f10x.h"                  // Device header
#include "mpu6050.h"
#include "OLED.h"
#include "Delay.h"int main()
{information num;mpu_init();OLED_Init();while(1){num = mpu_get_inf();OLED_ShowSignedNum(1, 1, num.x_acceleration, 5);OLED_ShowSignedNum(2, 1, num.y_acceleration, 5);OLED_ShowSignedNum(3, 1, num.z_acceleration, 5);OLED_ShowSignedNum(1, 9, num.x_angular_velocity, 5);OLED_ShowSignedNum(2, 9, num.y_angular_velocity, 5);OLED_ShowSignedNum(3, 9, num.z_angular_velocity, 5);Delay_ms(500);}return 0;
}

(三)总结

通过读取加速度和角速度,我们通过I2C通信协议读写MPU-6050,了解了I2C的工作原理和手动模拟I2C的工作流程,解决了一些遇到的错误,积累了错误处理的经验

相关文章:

stm32入门学习10-I2C和陀螺仪模块

&#xff08;一&#xff09;I2C通信 &#xff08;1&#xff09;通信方式 I2C是一种同步半双工的通信方式&#xff0c;同步指的是通信双方时钟为一个时钟&#xff0c;半双工指的是在同一时间只能进行接收数据或发送数据&#xff0c;其有一条时钟线&#xff08;SCL&#xff09;…...

GDB常用指令

GDB调试&#xff1a;GDB调试的是可执行文件&#xff0c;在gcc编译时加入-g参数&#xff0c;告诉gcc在编译时加入调试信息&#xff0c;这样gdb才能调试这个被编译的文件。此外还会加上-Wall参数尽量显示所有警告信息。 GDB命令格式&#xff1a; 1、start&#xff1a;程序在第一…...

Nginx 高级 扩容与高效

Nginx高级 第一部分&#xff1a;扩容 通过扩容提升整体吞吐量 1.单机垂直扩容&#xff1a;硬件资源增加 云服务资源增加 整机&#xff1a;IBM、浪潮、DELL、HP等 CPU/主板&#xff1a;更新到主流 网卡&#xff1a;10G/40G网卡 磁盘&#xff1a;SAS(SCSI) HDD&#xff08;机械…...

pythonflaskMYSQL自驾游搜索系统32127-计算机毕业设计项目选题推荐(附源码)

目 录 摘要 1 绪论 1.1研究背景 1.2爬虫技术 1.3flask框架介绍 2 1.4论文结构与章节安排 3 2 自驾游搜索系统分析 4 2.1 可行性分析 4 2.2 系统流程分析 4 2.2.1数据增加流程 5 2.3.2数据修改流程 5 2.3.3数据删除流程 5 2.3 系统功能分析 5 2.3.1 功能性分析 6 2.3.2 非功…...

C++ vector的基本使用(待补全)

std::vector 是C标准模板库(STL)中的一个非常重要的容器类&#xff0c;它提供了一种动态数组的功能。能够存储相同类型的元素序列&#xff0c;并且可以自动管理存储空间的大小&#xff0c;以适应序列大小变化&#xff0c;处理元素集合的时候很灵活 1. vector的定义 构造函数声…...

Java 属性拷贝 三种实现方式

第一种 List<OrederPayCustomer> orederPayCustomerList this.list(queryWrapper); List<CustomerResp>customerRespListnew ArrayList<>();for (OrederPayCustomer orederPayCustomer : orederPayCustomerList) {CustomerResp customerResp new Custome…...

Java-变量,运算符,输入与输出

目录 一&#xff0c;语法基础 1.基本Java程序 2.语法基础 2.1 变量 2.2 常量限制(fiinal)类比C中的const 2.3 类型转化 2.4 运算符 2.5 表达式 2.5 输入与输出 2.5.1 输入 2.5.2 输出 一&#xff0c;语法基础 1.基本Java程序 public class Main{public static void…...

五、一个quad同时支持pcie和sfp两种高速接口的ref时钟配置

项目描述 上位机将截图数据通过 XDMA 写入到 FPGA 侧的 DDR 内存区域 1 中通过 axi_lite 接口给 axi_read_start 信号&#xff0c;通知 AXI_read 模块启动读取数据&#xff0c;然后通过 GTP TX 模块发送出去。经过光纤回环&#xff0c;GTP RX 端接收到数据&#xff0c;送给 AX…...

AI辅助教育:九章大模型的数学辅导功能解析

1.简介 九章大模型是学而思为学习研发的模型&#xff0c;该模型对于数学做了很多专门的训练&#xff0c;在题目推荐方面做得比较好。 同时&#xff0c;这个模型也能支持上传图片&#xff0c;对图片内容进行分析&#xff0c;然后针对内容进行校对&#xff0c;推荐相识题目。 支…...

力扣刷题之3128.直角三角形

题干描述 给你一个二维 boolean 矩阵 grid 。 请你返回使用 grid 中的 3 个元素可以构建的 直角三角形 数目&#xff0c;且满足 3 个元素值 都 为 1 。 注意&#xff1a; 如果 grid 中 3 个元素满足&#xff1a;一个元素与另一个元素在 同一行&#xff0c;同时与第三个元素…...

OD C卷 - 机场航班调度

机场航班调度&#xff08;100&#xff09; 航班组成&#xff1a;前两个大写字母代表航空公司缩写&#xff0c;后面4个数字代表航班信息&#xff1b;对输入的航班排序 首先按照航空公司缩写升序排序&#xff1b;同一航空公司的按照航班信息升序排序&#xff1b; 输入描述&…...

uni-app中使用支付宝扫码插件并且在真机调试时使用(详细教程)

前言&#xff1a;uni-app自带的扫码api 识别不灵敏&#xff0c;每次都得扫很长时间且不断调整才能扫出来码&#xff0c;所以决定使用支付宝扫码插件&#xff0c;官方插件地址&#xff1a;https://ext.dcloud.net.cn/plugin?id2636#detail 使用步骤: 1、下载插件到项目中 2、…...

每日学术速递8.5—1

1.SV4D: Dynamic 3D Content Generation with Multi-Frame and Multi-View Consistency 标题&#xff1a; SV4D&#xff1a;具有多帧和多视图一致性的动态 3D 内容生成 作者&#xff1a;Yiming Xie, Chun-Han Yao, Vikram Voleti, Huaizu Jiang, Varun Jampani 文章链接&…...

1、操作系统相关概念

1、操作系统是计算机上的第一层软件&#xff0c;用于管理计算机硬件设备&#xff0c;提高他们的利用率和通吐量&#xff0c;并为用户和应用程序提供一个接口。不同操作系统目标不同&#xff0c;查询设备的操作系统&#xff0c;侧重人机交互性&#xff1b;武器控制操作系统&…...

【ModelSim】仿真问题记录

1、波形出不全&#xff1a; 1、甚至连clk波形都出不来 2、个别波形只有到仿真结束的时候才出现 解决办法&#xff1a; 1、添加波形需要是实例中的net 2、排查是否存在声明与示例的位宽不一致的信号 3、观察是否存在未初始化的变量寄存器 4、缩短整个仿真的步长 2、Instance列…...

如何提高深度学习中数据运行的稳定性

在深度学习中&#xff0c;模型的训练通常会受到随机性因素的影响&#xff0c;如参数初始化、数据加载顺序等。这会导致每次训练得到的结果有所不同。要减少这种不稳定性&#xff0c;可以采取以下措施&#xff1a; 1.固定随机种子 通过设置随机种子&#xff0c;可以使得每次训…...

【连续数组】python刷题记录

R3-前缀和专题 绝对要用字典记录 ben神&#xff0c;前缀和字典 class Solution:def findMaxLength(self, nums: List[int]) -> int:#前缀和字典,key为差值&#xff0c;value为坐标dict{0:-1}#当前1和0的差值counter0ret0for i,num in enumerate(nums):#多1&#xff0b;1if…...

JavaScript青少年简明教程:DOM和CSS简介

JavaScript青少年简明教程&#xff1a;DOM和CSS简介 DOM简介 DOM&#xff08;Document Object Model&#xff09;将文档表示为一个树形结构&#xff0c;其中每个节点都是一个对象&#xff0c;每个对象都有其自身的属性和方法。 通过对DOM的操作&#xff0c;开发者可以使用编…...

架构师知识梳理(一):计算机硬件

目录 计算机硬件组成 CPU CPU的组成 CPU的功能 校验码 奇偶校验 CRC CRC计算案例 指令 指令指行过程 指令系统 指令系统分类 指令流水线技术 流水线技术相关计算公式 存储 计算机存储系统设计 高速缓存Cache 缓存的局部性原理 地址映射 替换算法 关于命中…...

从根儿上学习spring 四 之run方法启动第一段

图1 由上图我们可以看到&#xff0c;我把run方法分成了5个小段&#xff0c;每小段使用红框圈了起来&#xff0c;这一篇我们先开始讲第一段。大家需要关注下行号&#xff0c;我讲的时候可能会使用行号对应具体某行代码。 图1-289-290行&#xff1a; 没啥好说的定义了两个变量&…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...