STM32——I2C通信

news2024/11/16 19:33:36

文章目录

  • I2C通信
  • 使用I2C通信的硬件设备
  • 硬件电路
  • I2C时序基本单元
    • 起始与终止
    • 发送
    • 接收
    • 发送应答与接收应答
  • I2C时序
    • 指定地址写
    • 当前地址读
    • 指定地址读
    • 连续读与写
  • MPU6050简介
  • MPU6050参数
  • 硬件电路
  • MPU6050框图
  • 系统时钟
  • MPU6050的中断源
  • 寄存器映像
  • 软件I2C读写MPU6050
    • 电路设计
    • 关键代码

I2C通信

  • I2C总线(Inter IC BUS)是由Philips公司开发的一种通用数据总线
  • 两根通信线:SCL(Serial Clock)、SDA(Serial Data)
  • 同步【多一条时钟线,控制从机读写】,半双工【一根通信线】
  • 带数据应答
  • 支持总线挂载多设备(一主多从、多主多从)
  • 控制原理和单片机cpu操作外设原理一样,单片机通过数据总线对寄存器进行读取和写入,而单片机需要通过协议来读写外部模块的寄存器,配置mpu6050的寄存器进而控制硬件电路。
  • 异步通信对传输速率严格,且不能处理进入中断,必须完整发送一个字节,非常依赖硬件外设的支持,必须USART电路才能使用,同步时序比异步时序稳定性更加高,可以暂停传输(同时暂停时钟线)
  • 软件模拟I2C通信:通过软件手动翻转电平实现通信,对硬件的依赖程度低

使用I2C通信的硬件设备

在这里插入图片描述

硬件电路

在这里插入图片描述

  • 所有I2C设备的SCL连在一起,SDA连在一起

  • 主机对SCL线是完全控制,对SDA线不是,在从机发送数据和从机应答的时候,主机才会转交SDA控制器给从机,从机的SCL线只能被动读取。

  • 为了防止总线没有协调好导致电源短路问题,I2C禁止所有设备输出强上拉高电平【不能是推挽输出,会输出高电平】,采用外置弱上拉电阻+开漏输出的电路结构,可以同时兼具输入和输出功能。输入的时候先输出1,再读取输入数据寄存器

    • 即设备的SCL和SDA均要配置成开漏输出模式【只有低电平和浮空两种状态】
    • SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右【为了避免高电平造成的引脚浮空,加弱上拉电阻,类似于弹簧,配合开漏输出就是下拉的低电平或不拉的高电平
    • CPU和外设内部的SCL和SDA设计如下图所示
      在这里插入图片描述
    • 有线与现象,一个从机是低电平,则SDA线就是低电平,只有所有设备输出高电平,SDA线才是高电平,利用这个特性可以实现多主机模式下的时钟同步和总线仲裁,这就是SCL也采用上拉电阻的原因。

I2C时序基本单元

总结:
在读写时,SCL低电平时,SDA写,SCL高电平时,SDA不变
在起始和结束时,SCL高电平,SDA变化

起始与终止

空闲状态,SCL和SDA由于外挂的上拉电阻拉高致高电平
起始和终止都是由主机产生的,从机始终不会去碰SCL和SDA线

  • 起始条件:SCL高电平期间,SDA从高电平切换到低电平
    在这里插入图片描述

  • 终止条件:SCL高电平期间,SDA从低电平切换到高电平
    在这里插入图片描述

发送

串口通信是低位先行,i2c是高位先行

  • 发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节【在发送时,SCL和SDA都由主机进行控制】
    在这里插入图片描述
    由于有时钟线的控制,主机可以进入中断,此时传输会暂停,时序不会发送变化

接收

  • 接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)此时从机获得SDA控制权,释放SDA就是主机切换成输入模式,“释放”可以理解为主机原本就是输入模式,在下拉为低电平时是输出模式
    在这里插入图片描述

发送应答与接收应答

发送应答和接收应答就是发送数据的一位和接收数据的一位

  • 发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
    在这里插入图片描述

    • 发送应答逻辑:主机接收完一个字节数据后,主机会给从机发送一个发送应答,决定是否继续接收,不继续接收就会收回对SDA的控制权
  • 接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
    在这里插入图片描述

    • 接收应答逻辑:主机发送完一个字节数据后,发送一个接收应答,释放SDA控制权,若有从机拿到控制权并拉低电平,表示某个从机接收完毕。

I2C时序

从机设备地址分为7位和10位,一般相同型号的芯片地址是一样的
当相同型号的芯片挂载在同一总线上时,可以通过最后几位区分,例如mpu6050的最后一位可以由AD0引脚控制,接低电平时地址是1101000,接高电平时地址是1101001

指定地址写

对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)
在这里插入图片描述

  • 起始后,第一个字节内容必须是从机地址+读写位(0写1读)
  • 接收应答:由于主机释放SDA之后立即就被从机拉低表示接收到该字节,根据线与的特性,此处就没有拉高的波形出现
  • 在写操作时,第二个字节可以是指令控制字(如AD转换器)或寄存器地址(如MPU6050)

在这里插入图片描述

当前地址读

对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)
在这里插入图片描述

  • 由于读写标志位为1时,主机还来不及发送要读寄存器的地址
  • 从机哪个寄存器的数据呢?取决于之前写入的地址+1,该时序不常用

指定地址读

指定地址写+当前地址读(第三个字节不写入内容就直接开始读)

对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)
在这里插入图片描述

  • sr表示重复起始条件,在后面可以加停止时序,地址指针不会消失

在这里插入图片描述

连续读与写

  • 可以连续写入多个字节或读取多个字节,只需要在最后的时序写入多个字节,因为地址指针会在读写后自动加一,所以可以连续操作。读取时,还打算继续读就需要给应答,在结束的字节后给非应答,从机交出SDA控制权,也就是说从机的控制权从读写操作位1到发送应答位为1
  • 连续地址写
    在这里插入图片描述
  • 连续地址读
    在这里插入图片描述

MPU6050简介

  • MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角(欧拉角),常应用于平衡车、飞行器等需要检测自身姿态的场景
  • 3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度
  • 3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度
  • 9轴是多了3轴磁场强度,10轴是多了气压强度
  • 常见的数据融合算法有卡尔曼滤波,互补滤波
    在这里插入图片描述
    加速度计(可以看做测力计,根据牛顿第二定律F=ma可以计算加速度),加速度计具有静态稳定性,不具有动态稳定性
    在这里插入图片描述
    MPU6050的陀螺仪测量的是角速度,角速度积分就可以得到角度,具有动态稳定性,不具有静态稳定性
    在这里插入图片描述

MPU6050参数

  • 16位ADC采集传感器的模拟信号,量化范围:-32768~32767

  • 加速度计满量程选择:±2、±4、±8、±16(g)【量化范围对应加速度计的量程范围,变化大则选大量程,测量范围越广,变化小则选小量程,精度更高】

  • 陀螺仪满量程选择: ±250、±500、±1000、±2000(°/sec)

  • 可配置的数字低通滤波器(平缓信号抖动)

  • 可配置的时钟源

  • 可配置的采样分频

  • I2C从机地址:

    • 1101000(AD0=0)
      • 关于从机地址的十六进制,有两种表示方法
        • 将二进制转十六进制是0x68当做从机地址,在I2C通信时需要左移一位,按位或上读写位,读1写0
        • 将左移1位的数0xD0作为从机地址,此时在I2C通信时0xD0是写地址,0xD1是读地址
    • 1101001(AD0=1)

硬件电路

在这里插入图片描述

  • SCL和SDA在硬件电路中已经接上拉电阻
  • XCL和XDA是为了扩展芯片功能,通常是用于外接磁力计(偏航角)和气压计(高度),MPU6050的主机接口可以直接访问扩展芯片的数据,在芯片内部的DMP单元进行数据融合和姿态解算
  • INT引脚可以设置电平跳变,用于输出芯片内部的中断事件
  • 引脚功能图
    在这里插入图片描述

MPU6050框图

在这里插入图片描述

  • 灰色部分为传感器,本质上是可变电阻,通过串联分压后输出模拟电压,经过ADC转换后输出数据保存在寄存器中,不存在数据覆盖,每个传感器都有2个字节的寄存器,整个过程是硬件自动化完成
  • 每个传感器都有自测单元self test,通过比较使能和失能后该寄存器的数据差是否在合理范围,进而判断传感器是否有问题
  • Charge Pump电荷泵,用于升压,电源与电容并联时充电,串联时放电,放电时电压就是放大2倍,由于电容的电压会放完,所以不断的充电放电就能升压,在MPU6050中陀螺仪需要高电压支持所以设计该升压电路
  • DMP:数字运动处理器是芯片内部自带的姿态解算的硬件算法
  • 通信接口部分,上半部分显示MPU6050作为从机时,可以采用SPI通信,此时引脚的功能发送改变,也可以采用I2C通信。下半部分显示MPU6050作为主机时通过I2C协议与从机进行通信。Bypass Mux为开关,用于控制两路I2C是否为同一个通路,如果为同一个通路,则stm32既可以控制MPU6050又可以控制MPU6050的从设备。

系统时钟

在这里插入图片描述

  • 可以使用内部晶振
  • 内部陀螺仪的晶振
  • 外部时钟

MPU6050的中断源

在这里插入图片描述

  • 分别是自由落体,运动,零运动等
  • 会通过INT引脚输出电平跳变,若该引脚接入stm32可以通过外部中断来接受这个中断信号,通过CPU进行处理

寄存器映像

在这里插入图片描述

  • DMPLRT:采样频率分频器
    • 分频越小,内部AD转换越快,数据寄存器刷新越快
  • CONFIG:配置寄存器
    • 主要配置低通滤波,配置参数越大,输出数据抖动越小
  • GYRO:陀螺仪配置寄存器
    • 配置自测和满量程
  • ACCEL:加速度计配置寄存器
    • 配置自测、满量程、高通滤波(运动检查)

在这里插入图片描述

  • ACCEL:加速度计数据寄存器,分为高八位和低八位
  • TEMP:温度寄存器,分为高八位和低八位
  • GYRO:陀螺仪数据寄存器,分为高八位和低八位

十六位的有符号数以二进制补码的形式存储,读出高八位左移八位,或上低八位
在这里插入图片描述

  • PWR:电源管理寄存器1和2
    • 电源管理寄存器1:设备复位、睡眠模式(上电时默认是睡眠模式,需要关闭)、循环模式、温度传感器使能与失能、选择系统时钟来源
    • 电源管理寄存器1:后六位可以控制六个轴进入待机模式
  • WHO:器件ID号
    • 中间六位是ID号,最高位是0,上电默认是0x68

软件I2C读写MPU6050

电路设计

在这里插入图片描述

关键代码

MyI2C.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
/*
封装三个函数操作相应的IO口便于移植
MyI2C_W_SCL
MyI2C_W_SDA
MyI2C_R_SDA
*/
void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
	Delay_us(10);
}

void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
	Delay_us(10);
}

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

void MyI2C_Init(void)
{
	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;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}

void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);//为了兼容sr重复起始条件,先释放sda后释放scl
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);//为了确保释放sda是高电平,先拉低sda
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}

void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));//高位先行,按位与取出相应位数,右移得到相应的位
		MyI2C_W_SCL(1);//高位读取sda数据
		MyI2C_W_SCL(0);
	}
}

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则在该字节的相应位置写入1
		}
		MyI2C_W_SCL(0);
	}
	return Byte;
}
//发送用应答
void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}
//接收应答
uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;
	MyI2C_W_SDA(1);//释放sda,而不是输出1
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA();//读到0则表示从机应答
	MyI2C_W_SCL(0);
	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 | 0x01);//指定地址写,修改最低位为1
	MyI2C_ReceiveAck();
	Data = MyI2C_ReceiveByte();
	MyI2C_SendAck(1);//主机收回控制权
	MyI2C_Stop();
	
	return Data;
}
//指定地址写之前需要取消睡眠模式
void MPU6050_Init(void)
{
	MyI2C_Init();
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//最后三位选择时钟,0x01选择陀螺仪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);//中间2位是满量程,给11是最大量程
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);//中间2位是满量程,给11是最大量程
}

uint8_t MPU6050_GetID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);//指定地址读
}
//获取数据寄存器函数,参数为指针,指向各个数据寄存器的地址
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t DataH, DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
	*AccX = (DataH << 8) | DataL;//该变量为16位,有指针指向该地址
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
	*AccY = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	*AccZ = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*GyroX = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	*GyroY = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	*GyroZ = (DataH << 8) | DataL;
}
/*
多返回值设计:
1. 在该文件中定义全局变量,extern暴露出去,在main文件中使用
2. 用指针
3. 使用结构体打包


*/



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);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);

#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 header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"

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

int main(void)
{
	OLED_Init();
	MPU6050_Init();
	
	OLED_ShowString(1, 1, "ID:");
	ID = MPU6050_GetID();
	OLED_ShowHexNum(1, 4, ID, 2);
	
	while (1)
	{
		MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);//参数指针,指向各个寄存器地址
		OLED_ShowSignedNum(2, 1, AX, 5);
		OLED_ShowSignedNum(3, 1, AY, 5);
		OLED_ShowSignedNum(4, 1, AZ, 5);
		OLED_ShowSignedNum(2, 8, GX, 5);
		OLED_ShowSignedNum(3, 8, GY, 5);
		OLED_ShowSignedNum(4, 8, GZ, 5);
	}
}

参考视频:江科大自化协

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

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

相关文章

C语言-扫雷

文章目录完整扫雷1. 说明2. 思路3. 各个功能实现3.1 雷盘初始化与打印1&#xff09;雷盘定义2&#xff09; 随机布置雷3.2 玩家排查雷1&#xff09; 获取坐标周围雷数2&#xff09; 递归展开3&#xff09;胜负判断3&#xff09; 显示雷位置4. 游戏试玩5. 游戏完整代码game.htes…

【定时任务】---- xxl-job、@Scheduled

一、Scheduled注解实现的定时任务 要实现计划任务&#xff0c;首先通过在配置类注解EnableScheduling来开启对计划任务的支持&#xff0c;然后在要执行计划任务的方法上注解Scheduled&#xff0c;声明这是一个计划任务。 在Spring Boot 的入口类 XXXApplication 中,必然会有S…

东南大学洪伟教授评述:毫米波与太赫兹技术

今日推荐文章作者为东南大学毫米波国家重点实验室主任、IEEE Fellow 著名毫米波专家洪伟教授&#xff0c;本文选自《毫米波与太赫兹技术》&#xff0c;发表于《中国科学: 信息科学》2016 年第46卷第8 期——《信息科学与技术若干前沿问题评述专刊》。 本文概要介绍了毫米波与太…

CSS知识点精学6-精灵图、背景图片大小、文字阴影、盒子阴影、过渡

目录 一.精灵图 1.精灵图的介绍 2.精灵图的使用步骤 二.背景图片大小 三.文字阴影 四.盒子阴影 五.过渡 一.精灵图 1.精灵图的介绍 场景&#xff1a;项目中将多张小图片&#xff0c;合成一张大图片&#xff0c;这张图片称之为精灵图 优点&#xff1a;减少服务器发送次…

clickhouse入门学习以及数据迁移

本文主要介绍如何入门clickhouse&#xff0c;以及将mariadb数据迁移过来&#xff0c;最后介绍当前几种的训练的示例数据库集。1、中文教程&#xff1a;中文教程&#xff1a;中文教程有了教程&#xff0c;需要有数据可以训练&#xff0c;教程提供示例数据集&#xff0c;但是数据…

Java基础之《netty(22)—Protobuf》

一、Protobuf基本介绍 1、Protobuf是Google发布的开源项目&#xff0c;全称Google Protobuf Buffers&#xff0c;是一种轻便高效的结构化数据存储格式&#xff0c;可以用于结构化数据串行化&#xff0c;或者说序列化。它很适合做数据存储或RPC数据交换格式。 2、参考文档 htt…

粒子系统-主模块参数

目录 Duration Looping Prewarm Start Lifttime Start Speed Start Size 3D Start Rotation Start Rotation Start color Simulation Space Max Particles Duration 粒子系统的工作时长&#xff0c;如果不勾选Looping的话&#xff0c;在5秒后就再也没有粒子发射 L…

HTTPS头部的Referer字段

目录 Referrer-policy 如何设置referer 盗链 防盗链的工作原理 绕过图片防盗链 利用https网站盗链http资源网站&#xff0c;refer不会发送 利用iframe伪造请求referer 利用XMLHttpRequest Referer请求头包含了当前请求页面的来源页面的地址&#xff0c;即表示当前页面是…

【Proteus仿真】【STM32单片机】智能窗帘控制系统设计

文章目录一、功能简介二、软件设计三、实验现象联系作者一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使用LCD1602显示模块、按键模块、HC05蓝牙、DHT11温湿度、PCF8591 ADC模块、光线传感器、28BYJ48步进电机等。 主要功能&#xff1a; 系统运行后&…

链表算法-回文结构、两个链表公共节点

最近一直在刷算法&#xff0c;以前没有重视这块&#xff0c;偶然巧合下&#xff0c;想到了某几次的面试&#xff0c;虽然没有以这个为主&#xff0c;但是也都有问过算法的题&#xff0c;因为没有这方面的积累&#xff0c;所以心底里一直抗拒&#xff0c;最近也有时间&#xff0…

git第n次学习笔记

git工作流程git四个工作区域Workspace&#xff1a;工作区&#xff0c;就是你平时存放项目代码的地方Index/Stage&#xff1a;暂存区&#xff0c;用于临时存放你的改动&#xff0c;事实上它只是一个文件&#xff0c;保存即将提交到文件列表信息Repository&#xff1a;仓库区&…

CDGA|想做好数据安全,数据治理是核心

在数字化转型渐进成熟下&#xff0c;企业加强数据治理&#xff0c;保障数据安全&#xff0c;为数字经济持续发展筑牢安全屏障&#xff0c;是时代发展的客观需要。 首先&#xff0c;整个安全能力是在应用内部的&#xff0c;我们对数据流的精确感知和管控&#xff0c;能做到和应用…

【七】Netty JBoss Marshallin 编解码

Netty JBoss Marshallin 编解码介绍Marshallin 开发环境maven 依赖业务场景模拟流程图代码展示订购采购消息 POJO 类订购应答消息 POJO 类SubscribeReqServer 服务端启动类MarshallingCodeCFactory服务端业务处理类 SubscribeServerHandler客户端启动类 SubscribeClient客户端 …

leetcode.2471 逐层排序二叉树所需的最少操作数目 - bfs + 置换环算法 + 并查集

2471. 逐层排序二叉树所需的最少操作数目 目录 1、循环标记置换环 2、并查集置换环 思路&#xff1a; 总操作数目 每一层最小操作数之和 每一层元素个数 - 置换环数 先用bfs对树进行层序遍历&#xff0c;一层一层地计算 置换环&#xff1a;对每个节点&#xff0c;将其指向…

全国首例:新一代仿生型人工心脏在福建成功植入

此时此刻&#xff0c;福建福清吴先生的体内正搏动着一颗新款的“人工心脏”。心脏是生命的中枢&#xff0c;一旦衰竭生命也将终止&#xff0c;人工心脏为这些心衰患者带来了新的希望。福建医科大学附属协和医院心外科团队&#xff0c;将科幻电影里装着人工心脏的“钢铁侠”变成…

六派巨量转移技术概述

1. 巨量转移技术概述 与OLED显示技术不同&#xff0c;无机LED无法在玻璃或其他大尺寸衬底进行大面积的制作&#xff0c;因此需要在半导体衬底上进行制作&#xff0c;然后再转移到驱动背板上。当前LED所采用的衬底一般为蓝宝石&#xff0c;但蓝宝石与外延层之间的晶格和热膨胀系…

国产技术迎来突破,光量子芯片横空出世,中文编程也有好消息

国外光刻机不再牛&#xff0c;随着这项技术问世&#xff0c;我们摆脱芯片卡脖子困境成为可能&#xff01; 欧美技术如此领先&#xff0c;我们凭什么实现弯道超车&#xff1f;就凭国内领先全球的量子技术&#xff0c;还有惊艳问世的光量子芯片&#xff0c;让欧美震惊不已&#x…

Fossid简介及API接口调用开发

FOSSID简介 FOSSID 是由瑞典FOSSID 公司开发的一款开源代码检测和管理工具&#xff0c;能够全面、准确、高效的发现用户代码库中的开源代码及其风险。 FOSSID 是一个软件解决方案&#xff0c;能够单独部署使用&#xff0c;也可以与现有的开发流程进行无缝集成&#xff0c;能够…

Apache Shiro教程(4)

shiro授权 授权 授权&#xff0c;即访问控制&#xff0c;控制谁能访问哪些资源&#xff0c;主体进行身份认证后需要分配权限方可访问系统的资源&#xff0c;对于某些资源没有权限是无法访问的。 关键对象 授权可简单理解为 who 对 what 进行how操作 授权流程 授权方式 基于角…

如何优化Blender内存不足问题

众所周知&#xff0c; Blender是一款免费软件&#xff0c;在动画、3D 电影等所有领域都易于使用。您是否曾经在 Blender 场景中工作&#xff0c;突然无缘无故崩溃&#xff1f;或者简单地说&#xff0c;您在渲染过程中突然移动缓慢甚至导致卡顿。当你有最后期限时&#xff0c;这…