stm32入门学习11-硬件I2C和MPU
(一)I2C硬件电路
stm32内部有I2C的硬件电路,我们可以使用stm32的标准库函数来实现I2C,这可以为我们减少对软件资源的占用
I2C硬件电路常用的标准库函数
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);
//通过结构体初始化I2Cvoid I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);
//给初始化结构体赋默认值void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
//使能I2C,初始化之后要使能其才开始工作void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);
//开始I2C传输void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);
//结束I2C传输void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);
//发送一个字节数据uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);
//接收一个字节数据void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);
//发送7位地址,在最开始I2C寻址调用这个函数ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);
//接收事件,在使用硬件开始传输或发送数据等操作之后都会有对应的事件需要接收判断后再执行下一个操作void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);
//设置响应为,相当于软件实现I2C中的ack
我们只要使用这些库函数,按照其规定的流程进行数据的发送和接收即可,不需要我们手动在软件中模拟I2C的高低电平跳变
(二)使用库函数实现I2C数据的发送与接收
(1)发送
I2C发送流程如图,在开始传输完成后,其会产生EV5事件,我们在事件产生后即可发送地址位,成功发送后会产生EV6事件,接着发送寄存器地址,因为这里使用的是一个寄存器和一个移位寄存器来发送数据,所以我们在寄存器的数据写入到移位寄存器中正在发送(EV8事件)即可将新的数据写入寄存器,在最后一个数据位我们没有数据写入寄存器,且移位寄存器发送完毕,会产生EV8_2事件,表示发送完成,然后即可停止传输
这里我们发送一个字节后的接收响应由硬件自动接收,不需要我们手动接收响应
由于我们会频繁接收各种事件,我们可以把接收事件的函数封装成一个简单的函数,且让其超时退出,不让程序卡死
接收事件函数
void mpu_wait_flag(int event)
{unsigned int time = 10000;while (I2C_CheckEvent(I2C2, event) != SUCCESS){time--;if (time == 0){break;}}
}
按照流程,我们可以这样编写发送函数
发送函数
void mpu_write(unsigned char address, unsigned char inf)
{I2C_GenerateSTART(I2C2, ENABLE);mpu_wait_flag(I2C_EVENT_MASTER_MODE_SELECT); //EV5I2C_Send7bitAddress(I2C2, mpu_address, I2C_Direction_Transmitter);mpu_wait_flag(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //EV6I2C_SendData(I2C2, address);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTING); //EV8I2C_SendData(I2C2, inf);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTED); //EV8_2I2C_GenerateSTOP(I2C2, ENABLE);
}
第一行我们先开始传输,第二行我们接收EV5事件,第三行我们发送7位地址,并且第三个参数为读模式还是写模式,我们选择写模式,第四行我们等待EV6事件,紧接着第五行发送要读取的寄存器地址,第六行等待寄存器地址移动到移位寄存器正在发送事件(EV8),最后第七行写要发送的数据,等待发送完成事件,最后停止传输;
(2)接收
接收数据的流程如图所示
这里一样,我们在开始之后会产生EV5事件,在传递MPU地址后会产生EV6事件,接着读取数据,这里由于我们在读取事件之后硬件电路会立刻把我们之前预设的ack响应发给从机,因此我们需要在读取最后一个数据之前把ack置0,我们这里只读取一个字节的数据,也就是要在发送MPU地址之后,读取数据之前先把ack置0,对应代码如下
unsigned char mpu_read(unsigned char address)
{unsigned char inf;I2C_GenerateSTART(I2C2, ENABLE);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTED);I2C_Send7bitAddress(I2C2, mpu_address, I2C_Direction_Transmitter);mpu_wait_flag(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);I2C_SendData(I2C2, address);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTED);I2C_GenerateSTART(I2C2, ENABLE);mpu_wait_flag(I2C_EVENT_MASTER_MODE_SELECT);I2C_Send7bitAddress(I2C2, mpu_address, I2C_Direction_Receiver);mpu_wait_flag(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);I2C_AcknowledgeConfig(I2C2, DISABLE); //ack=0mpu_wait_flag(I2C_EVENT_MASTER_BYTE_RECEIVED);inf = I2C_ReceiveData(I2C2);I2C_GenerateSTOP(I2C2, ENABLE);I2C_AcknowledgeConfig(I2C2, ENABLE);return inf;
}
第一段对应我们软件I2C的给寄存器地址操作,我们仍需要先写入寄存器地址,然后在第二段我们重新开始传输,接收EV5事件后发送MPU地址,这里第三个参数选择读模式,等待地址发送完成,之后值得注意的是我们先执行的不是读取数据,而是先把应答位置非应答,再去读取数据,读取完成后我们即可停止传输
(三)MPU-6050
经过上面两个函数的编写,我们已经可以调用上面两个函数来实现在指定外设寄存器上写数据和读数据的操作,我们即可进行MPU的初始化和读取转换数据所在的寄存器,这里就和之前使用软件模拟I2C的代码没有什么区别,因为软件我们只是模拟了I2C的波形,最终也封装成了写字节和读字节的函数
(1)初始化
(1)打开时钟和初始化GPIO
这里因为我们使用内部的I2C硬件电路,因此我们打开时钟我们要把I2C的时钟打开,stm32F03C8有两个I2C,这里使用的是I2C2,对应的SCL、SDA引脚为PB10、PB11
void mpu_rcc_init()
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
}
初始化GPIO,这里要用复用开漏输出
void mpu_gpio_init()
{GPIO_InitTypeDef gpio_init;gpio_init.GPIO_Pin = SCL | SDA;gpio_init.GPIO_Mode = GPIO_Mode_AF_OD;gpio_init.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &gpio_init);
}
(2)I2C初始化
使用I2C硬件电路还要对I2C进行初始化,可以看下初始化结构体
(1)第一个参数是选择时钟速度,数值越大电平翻转频率越高,低于100KHz的是正常模式,高于100KHz的是高速模式,这里选择100KHz;
(2)第二个参数是选择模式,可以选泽I2C模式、SMBus设备模式或SMBus主控模式,这里选择I2C模式;
(3)第三个参数是选择高低电平的占空比,这里只有在高速模式下有效,有16:9和2:1两种可选,这里不是高速模式,可以随便选择;
(4)第四个参数是选择自身地址,自身可以作为从机,设置自身地址让其他主机呼叫,随便选择,只要不和MPU或其他I2C上的地址冲突即可;
(5)第五个参数是应答位设置,可以选择应答和非应答,给应答;
(6)第六个参数是选择7位地址模式还是10位地址模式,这里选择7为地址模式;
这样我们就可以初始化I2C了
void mpu_i2c_init()
{I2C_InitTypeDef i2c_init;i2c_init.I2C_ClockSpeed = 100000;i2c_init.I2C_Mode = I2C_Mode_I2C;i2c_init.I2C_DutyCycle = I2C_DutyCycle_2;i2c_init.I2C_OwnAddress1 = 0x00;i2c_init.I2C_Ack = I2C_Ack_Enable;i2c_init.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;I2C_Init(I2C2, &i2c_init);I2C_Cmd(I2C2, ENABLE);
}
最后不要忘了使能I2C,否则其不工作
(3)MPU初始化
我们把时钟、gpio、I2C的初始化都集成于此,我们还要像软件那样配置寄存器来关闭MPU睡眠模式和选择时钟等功能,具体代码如下
void mpu_init()
{mpu_rcc_init();mpu_gpio_init();mpu_i2c_init();mpu_write(0x6B, 0x01); //PWR_MGMT_1 -> 0000 0001mpu_write(0x6C, 0x00); //PWR_MGMT_2 -> 0000 0000}mpu_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
}
(2)读取参数
最后我们选择将x轴、y轴和z轴的加速度和角速度放在一个结构体中返回,我们还要注意16位整数转为32位整数中的符号问题
结构体定义
typedef struct infs
{int x_acceleration;int y_acceleration;int z_acceleration;int x_angular_velocity;int y_angular_velocity;int z_angular_velocity;
} information;
16位整数转32为整数
int mpu_16_to_32(int original)
{int final;if (original & 0x8000){final = original | 0xFFFF0000;}return final;
}
我们只要读取MPU对应数据的寄存器即可,这里和软件模拟的操作一样
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 = mpu_16_to_32((inf_H<<8) | inf_L);inf_H = mpu_read(0x3D);inf_L = mpu_read(0x3E);infor.y_acceleration = mpu_16_to_32((inf_H<<8) | inf_L);inf_H = mpu_read(0x3F);inf_L = mpu_read(0x40);infor.z_acceleration = mpu_16_to_32((inf_H<<8) | inf_L);inf_H = mpu_read(0x43);inf_L = mpu_read(0x44);infor.x_angular_velocity = mpu_16_to_32((inf_H<<8) | inf_L);inf_H = mpu_read(0x45); inf_L = mpu_read(0x46); infor.y_angular_velocity = mpu_16_to_32((inf_H<<8) | inf_L);inf_H = mpu_read(0x47); inf_L = mpu_read(0x48); infor.z_angular_velocity = mpu_16_to_32((inf_H<<8) | inf_L);return infor;
}
(3)封装与声明
最后的.c 和 .h文件如下
#include "stm32f10x.h" // Device header#define SCL GPIO_Pin_10
#define SDA GPIO_Pin_11#define mpu_address 0xD0void mpu_wait_flag(int event)
{unsigned int time = 10000;while (I2C_CheckEvent(I2C2, event) != SUCCESS){time--;if (time == 0){break;}}
}void mpu_write(unsigned char address, unsigned char inf)
{I2C_GenerateSTART(I2C2, ENABLE);mpu_wait_flag(I2C_EVENT_MASTER_MODE_SELECT); //EV5I2C_Send7bitAddress(I2C2, mpu_address, I2C_Direction_Transmitter);mpu_wait_flag(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //EV6I2C_SendData(I2C2, address);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTING); //EV8I2C_SendData(I2C2, inf);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTED); //EV8_2I2C_GenerateSTOP(I2C2, ENABLE);
}unsigned char mpu_read(unsigned char address)
{unsigned char inf;I2C_GenerateSTART(I2C2, ENABLE);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTED);I2C_Send7bitAddress(I2C2, mpu_address, I2C_Direction_Transmitter);mpu_wait_flag(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);I2C_SendData(I2C2, address);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_TRANSMITTED);I2C_GenerateSTART(I2C2, ENABLE);mpu_wait_flag(I2C_EVENT_MASTER_MODE_SELECT);I2C_Send7bitAddress(I2C2, mpu_address, I2C_Direction_Receiver);mpu_wait_flag(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);I2C_AcknowledgeConfig(I2C2, DISABLE);mpu_wait_flag(I2C_EVENT_MASTER_BYTE_RECEIVED);inf = I2C_ReceiveData(I2C2);I2C_GenerateSTOP(I2C2, ENABLE);I2C_AcknowledgeConfig(I2C2, ENABLE);return inf;
}void mpu_rcc_init()
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
}void mpu_gpio_init()
{GPIO_InitTypeDef gpio_init;gpio_init.GPIO_Pin = SCL | SDA;gpio_init.GPIO_Mode = GPIO_Mode_AF_OD;gpio_init.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &gpio_init);
}void mpu_i2c_init()
{I2C_InitTypeDef i2c_init;i2c_init.I2C_ClockSpeed = 100000;i2c_init.I2C_Mode = I2C_Mode_I2C;i2c_init.I2C_DutyCycle = I2C_DutyCycle_2;i2c_init.I2C_OwnAddress1 = 0x00;i2c_init.I2C_Ack = I2C_Ack_Enable;i2c_init.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;I2C_Init(I2C2, &i2c_init);I2C_Cmd(I2C2, ENABLE);
}void mpu_init()
{mpu_rcc_init();mpu_gpio_init();mpu_i2c_init();mpu_write(0x6B, 0x01); //PWR_MGMT_1 -> 0000 0001mpu_write(0x6C, 0x00); //PWR_MGMT_2 -> 0000 0000}mpu_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 infs
{int x_acceleration;int y_acceleration;int z_acceleration;int x_angular_velocity;int y_angular_velocity;int z_angular_velocity;
} information;int mpu_16_to_32(int original)
{int final;if (original & 0x8000){final = original | 0xFFFF0000;}return final;
}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 = mpu_16_to_32((inf_H<<8) | inf_L);inf_H = mpu_read(0x3D);inf_L = mpu_read(0x3E);infor.y_acceleration = mpu_16_to_32((inf_H<<8) | inf_L);inf_H = mpu_read(0x3F);inf_L = mpu_read(0x40);infor.z_acceleration = mpu_16_to_32((inf_H<<8) | inf_L);inf_H = mpu_read(0x43);inf_L = mpu_read(0x44);infor.x_angular_velocity = mpu_16_to_32((inf_H<<8) | inf_L);inf_H = mpu_read(0x45); inf_L = mpu_read(0x46); infor.y_angular_velocity = mpu_16_to_32((inf_H<<8) | inf_L);inf_H = mpu_read(0x47); inf_L = mpu_read(0x48); infor.z_angular_velocity = mpu_16_to_32((inf_H<<8) | inf_L);return infor;
}
#ifndef __MPU_H__
#define __MPU_H__typedef struct infs
{int x_acceleration;int y_acceleration;int z_acceleration;int x_angular_velocity;int y_angular_velocity;int z_angular_velocity;
} information;void mpu_init(void);
information mpu_get_inf(void);#endif
(四)主函数调用
我们只要在主函数开始时对MPU初始化,然后在循环中调用读取寄存器值的函数即可,和软件模拟实现的程序一样
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "MPU.h"
#include "Delay.h"
int main()
{mpu_init();OLED_Init();information inf;while(1){inf = mpu_get_inf();OLED_ShowSignedNum(1, 1, inf.x_acceleration, 5);OLED_ShowSignedNum(2, 1, inf.y_acceleration, 5);OLED_ShowSignedNum(3, 1, inf.z_acceleration, 5);OLED_ShowSignedNum(1, 8, inf.x_acceleration, 5);OLED_ShowSignedNum(2, 8, inf.y_acceleration, 5);OLED_ShowSignedNum(3, 8, inf.z_acceleration, 5);Delay_ms(500);}return 0;
}
(五)总结
在学习软件模拟I2C后,这里学习的是通过硬件I2C,通过库函数来实现I2C通信,读取MPU寄存器中的数据,我们对I2C的理解更加深入,对stm32的了解也更加具体
相关文章:

stm32入门学习11-硬件I2C和MPU
(一)I2C硬件电路 stm32内部有I2C的硬件电路,我们可以使用stm32的标准库函数来实现I2C,这可以为我们减少对软件资源的占用 I2C硬件电路常用的标准库函数 void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct); /…...
如何在C++、PHP、GO中使用AI生成PPT API接口
在当今快节奏的商业环境中,演示文稿的制作不仅需要快速,还需要具有吸引力和专业性。AI生成PPT API 服务提供了一种创新的解决方案,能够根据用户提供的内容自动生成演示文稿,极大地提高了效率和质量。本文将详细介绍AI生成PPT的优势…...

力扣面试150 逆波兰表达式求值 栈 模拟栈
Problem: 150. 逆波兰表达式求值 👨🏫 参考题解 class Solution {//纯数组模拟栈实现(推荐) 3 ms 36 MBpublic static int evalRPN(String[] tokens) {int[] numStack new int[tokens.length / 2 1];int index 0;for (String s : tokens) {swit…...

动手学深度学习V2每日笔记(深度卷积神经网络AlexNet)
本文主要参考沐神的视频教程 https://www.bilibili.com/video/BV1h54y1L7oe/spm_id_from333.788.recommend_more_video.0&vd_sourcec7bfc6ce0ea0cbe43aa288ba2713e56d 文档教程 https://zh-v2.d2l.ai/ 本文的主要内容对沐神提供的代码中个人不太理解的内容进行笔记记录&…...
室内定位:紧耦合的学习惯性里程 (TLIO)
a### TLIO论文解读:紧耦合的学习惯性测程 (TLIO) 在惯性测量单元 (IMU) 领域,如何在短时间内精确地估计位置和姿态一直是一个挑战。最近,论文《TLIO: Tight Learned Inertial Odometry》提出了一种创新的方法,通过将深度学习与扩展卡尔曼滤波器 (EKF) 紧密结合,来解决这一…...
【面试之算法篇】寻找二叉树中两个节点的最低公共祖先
题目 给定一个树的根节点root和两个子节点a,b,返回二叉树中两个节点的最低公共祖先。二叉树每个节点的值都是不同的整数 10060 12040 null 4 74和7的最低公共祖先是120,60和40的最低公共祖先是60 思路 两个节点的祖先会有多个,只有是祖先的节点才有可能会是最低公共…...
使用Unity开发编辑系统时复制物体的一些细节问题
首先是复制一个GameObject时组件中的变量内容的复制问题,这个在Unity复制对象时让私有变量也被复制的简单方法这篇博客里面做了说明,但是其实还有一个问题,就是有些时候需要被复制的物体在刚创建出来的时候需要自动执行一些操作,这…...

【C++】模版初阶+STL简介
🚀个人主页:奋斗的小羊 🚀所属专栏:C 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~ 目录 前言💥1、函数模版💥1.1 函数模板概念💥1.2 函数模板格式💥1…...
Vue3中的toRef和toRefs的区别和用法
刚做了Ref和Reactive区别及使用方法笔记,再来总结一下,toRef 和 toRefs 的作用、用法、区别 1、作用和区别 toRef 和 toRefs 可以用来复制 reactive 里面的属性然后转成 ref,而且它既保留了响应式,也保留了引用,也就…...
【docker快捷部署系列一】docker快速入门,安装docker,解决运行Docker Quickstart Terminal出错
1、docker快速入门 视频链接 知识点概述 docker是轻量级虚拟机image是镜像 相当于虚拟机快照container是容器,相当于运行起来的虚拟机程序Dockerfile 是创建docker镜像的自动化脚本docker-compose 是一个定义和运行多个容器命令的工具,包括运行Docker…...

vulnhub靶机实战_DC-8
一、靶机下载 靶机下载链接汇总:https://download.vulnhub.com/使用搜索功能,搜索dc类型的靶机即可。本次实战使用的靶机是:DC-8系统:Debian下载链接:https://download.vulnhub.com/dc/DC-8.zip 二、靶机启动 下载完…...

如何做到项目真实性优化?保姆级写简历指南第五弹!
大家好,我是程序员鱼皮。做知识分享这些年来,我看过太多简历、也帮忙修改过很多的简历,发现很多同学是完全不会写简历的、会犯很多常见的问题,不能把自己的优势充分展示出来,导致措施了很多面试机会,实在是…...
Python Beautiful Soup介绍
在Web数据抓取和网页解析的世界里,Python以其简洁的语法和丰富的库资源成为了许多开发者的首选语言。而Beautiful Soup,作为Python中一个强大的HTML和XML解析库,更是以其易用性和灵活性赢得了广泛的赞誉。本文将带你走进Beautiful Soup的世界…...

NDI Tools汉化版的安装
目录 一、安装包下载 二、安装英文版 三、安装汉化版 NDI(Network Device Interface)即网络设备接口,是由美国 NewTek 公司开发的免费标准,它可使兼容的视频产品以高质量、低延迟、精确到帧的方式通过网络进行通讯、传输和接收广播级质量的视频,非常适合在现场直播制作…...

【JAVA多线程】AQS,JAVA并发包的核心
目录 1.概述 1.1.什么是AQS 1.2.AQS和BlockQueue的区别 1.3.AQS的结构 2.源码分析 2.1.CLH队列 2.2.模板方法的实现 2.2.1.独占模式 1.获取资源 2.释放资源 2.2.2.共享模式 1.概述 1.1.什么是AQS AQS非常非常重要,可以说是JAVA并发包(java.…...

springcloud loadbalancer nacos无损发布
前言 故事背景 jenkins部署时总是会有几秒钟接口调用报错,观察日志是因为流量被下发到已下线的服务,重启脚本在停止应用之前先调用nacos注销实例api后再重启依然会短暂出现此问题。项目架构是springcloud alibaba,通过openfeign进行微服务之间调用&…...
React原理
函数式编程 一种编程范式,概念比较多纯函数不可变值vdom和diff Vue2.x Vue3.x React 三者实现vdom细节都不同核心概念和实现思路,都一样h函数 用来生成vnode的函数 vnode数据结构 {tag: div,props: {className: div-class},children: [{tag: p,children: 测试}, ...] }pat…...
React-Native优质开源项目
React Native是由Facebook开发的一种开源框架,它允许开发者使用JavaScript和React编写原生应用,提供了一套跨平台的UI组件,可以在iOS和Android上实现一致的用户体验。在React Native的生态系统中,有许多优质的开源项目,…...

Ajax-02
一.form-serialize插件 作用:快速收集表单元素的值 const form document.querySelector(.example-form) const data serialize(form,{hash:true,empty:true}) *参数1:要获取哪个表单的数据 表单元素设置name属性,值会作为对象的属性名 建议…...

供应商较多的汽车制造业如何选择供应商协同平台?
汽车制造业的供应商种类繁多,根据供应链的不同环节和产品特性,可以大致分为以下几类。 按供应链等级分包括: 一级供应商通常具有较高的技术水平和生产能力,能够满足汽车厂商对零部件的高品质、高性能和高可靠性的要求。 二级供应…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...