当前位置: 首页 > 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; 没啥好说的定义了两个变量&…...

智能闹钟如何判断用户已经醒了?

智能闹钟判断用户是否已经醒来的方式主要依赖于其内置的传感器和算法系统。以下是一些常见的判断方法&#xff1a; 一、传感器监测 体动传感器&#xff1a;智能闹钟通常配备有体动传感器&#xff0c;用于监测用户的身体运动。当用户从睡眠状态转变为清醒状态&#xff0c;并开始…...

【算法】动态规划解决背包问题

应用场景——01背包问题 有一个背包&#xff0c;背包的容量为 4&#xff0c;现有如下物品 要求 1.目标为装入背包的总价值最大&#xff0c;并且重量不超出 2.要求装入的物品不能重复 动态规划算法介绍 1.动态规划算法的核心是&#xff1a;将大问题划分为小问题进行解决&…...

day09 工作日报表

日期 30日07月2024年 任务安排 今天主要就是讲了security类工作的原理&#xff0c;然后就是让我们自己做项目 工作中的问题 今天做项目的时候发现有时候用postman测试返回20001&#xff0c;说错误见控制台&#xff0c;但是控制台一片祥和&#xff0c;于是就尝试用tr…...

C++学习之路(1)— 第一个HelloWorld程序

C学习之路&#xff08;1&#xff09;— 第一个HelloWorld程序 一、前言 C在C语言的基础上添加了对面向对象编程和泛型编程的支持&#xff0c;在 20世纪90年代便是最重要的编程语言之一&#xff0c;并在21世纪仍保持强劲势头。C继承了C语言高效、简洁、快速和可移植性的传统。 …...

python3 pyside6图形库学习笔记及实践(三)

目录 前言菜单栏相关控件使用QtDesigner快速构建菜单栏结构语法 上下文菜单概念为窗体添加上下文菜单为控件添加上下文菜单 折叠菜单资源的加载内置图标Rcc的使用创建资源文件加载资源文件 前言 本系列文章为b站PySide6教程以及官方文档的学习笔记 原视频传送门&#xff1a;【…...

03 库的操作

目录 创建查看修改删除备份和恢复查看连接情况 1. 创建 语法 CRATE DATABASE [IF NOT EXISTS] db_name [create_specification [, create_specification] …] create_specification:  CHARACTER SET charset_name  CPLLATE collation_name 说明&#xff1a; 大写的标识关键…...

嵌入式人工智能(44-基于树莓派4B的扩展板-LED按键数码管TM1638)

树莓派性能非常强悍&#xff0c;但是对于某些复杂的项目来说&#xff0c;会出现心有余而口不足的情况&#xff0c;为了解决这类问题&#xff0c;可以在树莓派上使用扩展板&#xff0c;我们介绍几款常见的扩展板&#xff0c;不仅可以扩展到树莓派&#xff0c;其他单片机或嵌入式…...

linux通过抓包工具tcpdump查看80端口访问量情况

方法&#xff1a; tcpdump -i ens32 -tn dst port 80 -c 10 | awk -F"." {print $1"."$2"."$3"."$4} | sort | uniq -c | sort -nr |head -n 10 #-i&#xff1a;指定端口 #-t&#xff1a;在输出的每一行不打印时间戳 #-n&#xff…...

Mac 上安装和卸载 SDKMAN 及管理多个 JDK

前言 当电脑上有多个 JDK 环境的时候&#xff0c;切换管理比较麻烦&#xff0c;这时候可以使用 SDKMAN 来安装、管理 JDK。 一、安装 SDKMAN! 1. 安装前置条件 首先&#xff0c;确保已经安装了 curl 。如果没有&#xff0c;可以通过 Homebrew 来安装&#xff1a; brew inst…...

字节测开一面面经

1 . 自我介绍 2 . 讲一下常见的数据结构 : 讲了数组,set,list,map,树&#xff0c;图&#xff0c;队列 &#xff0c; 栈等 ; 3 . 讲一下java反射场景和作用 ; 4 . 讲一下你了解的机器学习算法 ; 5 . 我讲完ML之后 &#xff0c; 问了knn和贝叶斯的使用的场景区别(没答上来) ; 6 .…...