江协科技STM32学习- P35 硬件I2C读写MPU6050

        🚀write in front🚀  
🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​ 

💬本系列哔哩哔哩江科大STM32的视频为主以及自己的总结梳理📚 

🚀Projeet source code🚀   

💾工程代码放在了本人的Gitee仓库:iPickCan (iPickCan) - Gitee.com

引用:

STM32入门教程-2023版 细致讲解 中文字幕_哔哩哔哩_bilibili

Keil5 MDK版 下载与安装教程(STM32单片机编程软件)_mdk528-CSDN博客

STM32之Keil5 MDK的安装与下载_keil5下载程序到单片机stm32-CSDN博客

0. 江协科技/江科大-STM32入门教程-各章节详细笔记-查阅传送门-STM32标准库开发_江协科技stm32笔记-CSDN博客

【STM32】江科大STM32学习笔记汇总(已完结)_stm32江科大笔记-CSDN博客

江科大STM32学习笔记(上)_stm32博客-CSDN博客

STM32学习笔记一(基于标准库学习)_电平输出推免-CSDN博客

STM32 MCU学习资源-CSDN博客

stm32学习笔记-作者: Vera工程师养成记

stem32江科大自学笔记-CSDN博客

术语:

英文缩写描述
GPIO:General Purpose Input Onuput通用输入输出
AFIO:Alternate Function Input Output复用输入输出
AO:Analog Output模拟输出
DO:Digital Output数字输出
内部时钟源 CK_INT:Clock Internal内部时钟源
外部时钟源 ETR:External Trigger 时钟源 External 触发
外部时钟源 ETR:External Trigger mode 1外部时钟源 External 触发 时钟模式1
外部时钟源 ETR:External Trigger mode 2外部时钟源 External 触发 时钟模式2
外部时钟源 ITRx:Internal Trigger inputs外部时钟源,ITRx (Internal trigger inputs)内部触发输入
外部时钟源 TIx:exTernal Input pin 外部时钟源 TIx (external input pin)外部输入引脚
CCR:Capture/Comapre Register捕获/比较寄存器
OC:Output Compare输出比较
IC:Input Capture输入捕获
TI1FP1:TI1 Filter Polarity 1Extern Input 1 Filter Polarity 1,外部输入1滤波极性1
TI1FP2:TI1 Filter Polarity 2Extern Input 1 Filter Polarity 2,外部输入1滤波极性2
DMA:Direct Memory Access直接存储器存取

正文:

0. 概述

从 2024/06/12 定下计划开始学习下江协科技STM32课程,接下来将会按照哔站上江协科技STM32的教学视频来学习入门STM32 开发,本文是视频教程 P2 STM32简介一讲的笔记。

1.🚚I2C硬件库函数

stm32中I2C库函数介绍(stm32f10x_i2c.h) 

下面这些是关于I2C的库函数,这里我会挑选最常见的以及本期要用到的来去进行详细讲解。

1.初始化

在学过前面这么多结构体初始化的函数,对于结构体的初始化我们已经再属性不过了,下面是I2C外设的结构体初始化的函数。

void I2C_DeInit(I2C_TypeDef* I2Cx);
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);
void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);

一般情况我们都是自己去定义配置结构体再初始化的,下面我们看一个定义好结构体初始化的示例:

I2C_InitTypeDef I2C_initstruct;
    I2C_initstruct.I2C_Mode = I2C_Mode_I2C; //选择模式,这里选定使用I2C模式
    I2C_initstruct.I2C_ClockSpeed = 100000; //选择时钟的频率,不得大于400kHz
    I2C_initstruct.I2C_DutyCycle = I2C_DutyCycle_2;//占空比选择,低电平:高电平,如果当前是标准速度的话,这个是没用的的,如果在快速时钟的时候,这个才会起作用
    I2C_initstruct.I2C_Ack = I2C_Ack_Enable; //选择是否给应答
    I2C_initstruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//stm32作为从机的时候的响应地址位数
    I2C_initstruct.I2C_OwnAddress1 = 0x01;   //stm32作为从机的时候地址,我们这里是主机,所以这里随便设一个就行了
    I2C_Init(I2C2, &I2C_initstruct);
2.使能操作

我们本期不需要用DMA到使能,所以这里就不过多讲解。

void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
3.生成起始位和结束位标志
void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);

手册解释如下:

4.发送I2C从机地址

下面这个函数其实就是前面学习软件I2C的“点名”操作,硬件也是一样的,同样都是需要进行点名指定的I2C从机来进行通讯。如果有多个从机,主机会执行下面这个函数后会给每一个从机发送Address的数据,然后从机接收到了后就对比自己的IP数据,如果一样的话那么就开始跟主机通讯,其他从机保持沉默。

void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);

5.发送数据和接收数据

下面这个是发送数据:

void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);

下面可以看到把数据放入到数据寄存器DR的操作后,然后剩下就是进入到发送数据的流程。 

下面这个是接受数据:

uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);

我们可以看到这个函数的返回值是DR寄存器里面的数据。 

6.发送应答位

下面这个函数是用来发送应答位的,当主机接收到了数据之后会向从机发送应答情况,如果应答位是ENABLE表示接收完成返回一个应答反之就是无应答返回 ,手册解释如下

void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);

7.状态检测

下面这个函数是用来检测每次操作一个步骤之后的状态的,比如发送了一个字节后的数据,然后就会进入一个EVx的状态,这里就需要去执行这个状态是否完成,然后再执行下一步,这是一种缓冲机制以保证数据收发的准确性。在上一期我们也是详细讲解过了这个过程了的。

ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);

下面是这个函数的相关参数: 

2.🚚硬件I2C读取MPU6050

实验现象如下,其实跟前面软件I2C读取MPU6050现象是一样的,只是方法不同。(软件I2C读取MPU6050链接:stm32入门-----软件I2C读写MPU6050-CSDN博客)

1.电路连线图

查STM32F102T8C6引脚定义表,看到I2C1 PB6, PB7引脚已经给 OLED液晶显示屏使用,本节我们使用I2C2的PB10,PB11引脚。

2.主要工程文件 
主要工程文件如下,这里我们就没有像软件那部分一样需要建立一个MyI2C的文件来去执行收发数据的底层,这里我们是使用硬件外设配置库函数来去实现I2C通讯的,底层是由硬件来自动执行的,所以就不需要MyI2C这个文件了。

其中MPU6050.c文件是用来配置I2C外设以及MPU6050相关模块的文件,MPU6050_reg.h文件是存放MPU6050相关寄存器数据的文件

 MPU6050_reg.h文件数据如下:

#ifndef __MP6050_REG_H__
#define __MP6050_REG_H__

#define MP6050_SMPLRT_DIV		0x19
#define MP6050_CONFIG			0x1A
#define MP6050_GYRO_CONFIG		0x1B
#define MP6050_ACCEL_CONFIG		0x1C

#define MP6050_ACCEL_XOUT_H		0x3B
#define MP6050_ACCEL_XOUT_L		0x3C
#define MP6050_ACCEL_YOUT_H		0x3D	
#define MP6050_ACCEL_YOUT_L		0x3E
#define MP6050_ACCEL_ZOUT_H		0x3F
#define MP6050_ACCEL_ZOUT_L		0x40
#define MP6050_TEMP_OUT_H		0x41
#define MP6050_TEMP_OUT_L		0x42
#define MP6050_GYRO_XOUT_H		0x43
#define MP6050_GYRO_XOUT_L		0x44
#define MP6050_GYRO_YOUT_H		0x45
#define MP6050_GYRO_YOUT_L		0x46
#define MP6050_GYRO_ZOUT_H		0x47
#define MP6050_GYRO_ZOUT_L		0x48

#define MP6050_PWM_MGMT_1		0x6B
#define MP6050_PWM_MGMT_2		0x6C
#define MP6050_WHO_AM_I			0x75
#endif

这里我们主要去讲解MPU6050.c这个文件里面的内容,这个是配置I2C外设的主要文件。

3.MPU6050.c代码剖析
(1)检测步骤超时操作

在进入到检测状态的函数的时候,难免会出现意外超时的情况,这里我们就需要去对这个bug进行处理,比如如果超时的情况就放弃这个字节读取或写入的操作。

//检测事件后的超时操作
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT) {
    uint32_t time_out = 100000;
    while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS) {
        time_out--;
        if (!time_out) {
            break;
        } 
    }
}
(2)指定地址写

代码是对应下面这个序列图进行写的,序列图跟代码的步骤是一样的,我们这里使用的是7位。

void MP6050_WriteReg(uint8_t RegAddr, uint8_t data)
{
	//起始条件
	I2C_GenerateSTART(I2C2, ENABLE);	//产生Start起始位
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);	//等待EV5事件
	
	//发送I2C从机地址
	I2C_Send7bitAddress(I2C2, MP6050_I2CADDR, I2C_Direction_Transmitter);
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS); //等待EV6事件

	
	//发送寄存器地址
	I2C_SendData(I2C2, RegAddr);
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS); //等待EV8事件
	
	//发送数据
	I2C_SendData(I2C2, data);
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS); //等待EV8事件
	
	//发送停止位
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS); //等待EV8_2事件
	I2C_GenerateSTOP(I2C2, ENABLE);
}
(3)指定地址读

uint8_t MP6050_ReadReg(uint8_t RegAddr)
{
	uint8_t data = 0;
	
	//起始条件
	I2C_GenerateSTART(I2C2, ENABLE);	//产生Start起始位
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);	//等待EV5事件
	
	//发送I2C从机地址
	I2C_Send7bitAddress(I2C2, MP6050_I2CADDR, I2C_Direction_Transmitter);
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS); //等待EV6事件
	
	//发送寄存器地址
    //这里需要等待EV8_2事件等待数据传输完成,TRANSMITTED 而不是 TRANSMITTING 正在传输
	I2C_SendData(I2C2, RegAddr);
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS); //等待EV8_2事件
	
	//重复起始条件
	I2C_GenerateSTART(I2C2, ENABLE);	//产生Start起始位
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);	//等待EV5事件
	
	//发送I2C从机地址
	I2C_Send7bitAddress(I2C2, MP6050_I2CADDR, I2C_Direction_Receiver);
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS); //等待EV6事件
	
	//只接收一个字节必须在EV6事件之后清除ACK位和应答位
	I2C_AcknowledgeConfig(I2C2, DISABLE);	//设置NACK非应答位
	I2C_GenerateSTOP(I2C2, ENABLE);			//停止条件
	
	//接收数据
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS); //等待EV7事件
	data = I2C_ReceiveData(I2C2);

	//回复默认的ACK=1,方便连续接收
	I2C_AcknowledgeConfig(I2C2, ENABLE);
	
	return data;
}


(4)初始化配置
(5)获取MPU6050寄存器数据
4.主函数代码

这部分其实跟软件I2C读写MPU6050的是一样的。

MP6050.c

#include "stm32f10x.h"                  // Device header
#include "MP6050.h"
#include "MyI2C.h"
#include "Delay.h"
#include "MP6050_Reg.h"
#include "OLED.h"

#define MP6050_I2CADDR 		(0x68 << 1)
#define MP6050_I2CRead_DIR	0x01
#define MP6050_I2CWrit_DIR	0x00

void MP6050_Init(void)
{
	//使能RCC外设时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	//配置GPIO
	GPIO_InitTypeDef gpioInitStructure;
	gpioInitStructure.GPIO_Mode = GPIO_Mode_AF_OD;			//复用开漏输出模式
	gpioInitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;	//使用GPIOB_Pin10, Pin11
	gpioInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &gpioInitStructure);
	
	//配置I2C外设
	I2C_InitTypeDef I2C_InitStruct;
	I2C_StructInit(&I2C_InitStruct);
	
	I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;				//I2C工作方式
	I2C_InitStruct.I2C_ClockSpeed = 100*1000;			//I2C时钟频率,不超过400KHz
	I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_16_9;	//I2C时钟占空比
	I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;			//I2C应答
	I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;	//I2C从机7位模式
	I2C_InitStruct.I2C_OwnAddress1 = 0x01;				//I2C从机模式使用,自身地址寄存器
	I2C_Init(I2C2, &I2C_InitStruct);
	
	//使能I2C外设
	I2C_Cmd(I2C2, ENABLE);
	
	
	MP6050_WriteReg(MP6050_PWM_MGMT_1, 0x01);	//关闭MP6050睡眠模式,采样时钟选择内部GyroX轴时钟
	MP6050_WriteReg(MP6050_PWM_MGMT_2, 0x00);	//MP6050 6轴都使能,不使用Wakeup模式
	MP6050_WriteReg(MP6050_SMPLRT_DIV, 0x09);	//MP6050采样分频值10分频,实际采样频率=时钟频率/(分频值+1)
	MP6050_WriteReg(MP6050_CONFIG, 0x06);		//MP6050不使用FSYNC,DLPF数字低通滤波器选择6最平滑滤波
	MP6050_WriteReg(MP6050_GYRO_CONFIG, 0x18);	//陀螺仪量程选择最大量程
	MP6050_WriteReg(MP6050_ACCEL_CONFIG, 0x18);	//加速度计量程选择最大量程,不使用高通滤波

	MP6050_ReadReg(MP6050_WHO_AM_I);
	
	
}

void MP6050_WriteReg(uint8_t RegAddr, uint8_t data)
{
	//起始条件
	I2C_GenerateSTART(I2C2, ENABLE);	//产生Start起始位
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);	//等待EV5事件
	
	//发送I2C从机地址
	I2C_Send7bitAddress(I2C2, MP6050_I2CADDR, I2C_Direction_Transmitter);
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS); //等待EV6事件

	
	//发送寄存器地址
	I2C_SendData(I2C2, RegAddr);
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS); //等待EV8事件
	
	//发送数据
	I2C_SendData(I2C2, data);
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS); //等待EV8事件
	
	//发送停止位
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS); //等待EV8_2事件
	I2C_GenerateSTOP(I2C2, ENABLE);
}

uint8_t MP6050_ReadReg(uint8_t RegAddr)
{
	uint8_t data = 0;
	
	//起始条件
	I2C_GenerateSTART(I2C2, ENABLE);	//产生Start起始位
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);	//等待EV5事件
	
	//发送I2C从机地址
	I2C_Send7bitAddress(I2C2, MP6050_I2CADDR, I2C_Direction_Transmitter);
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS); //等待EV6事件
	
	//发送寄存器地址
    //这里需要等待EV8_2事件等待数据传输完成,TRANSMITTED 而不是 TRANSMITTING 正在传输
	I2C_SendData(I2C2, RegAddr);
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS); //等待EV8_2事件
	
	//重复起始条件
	I2C_GenerateSTART(I2C2, ENABLE);	//产生Start起始位
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);	//等待EV5事件
	
	//发送I2C从机地址
	I2C_Send7bitAddress(I2C2, MP6050_I2CADDR, I2C_Direction_Receiver);
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS); //等待EV6事件
	
	//只接收一个字节必须在EV6事件之后清除ACK位和应答位
	I2C_AcknowledgeConfig(I2C2, DISABLE);	//设置NACK非应答位
	I2C_GenerateSTOP(I2C2, ENABLE);			//停止条件
	
	//接收数据
	while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS); //等待EV7事件
	data = I2C_ReceiveData(I2C2);

	//回复默认的ACK=1,方便连续接收
	I2C_AcknowledgeConfig(I2C2, ENABLE);
	
	return data;
}




void MP6050_GetData(struct MP6050_Data_ParaOut *MP6050ParaOut)
{
	uint8_t Data_H = 0;
	uint8_t Data_L = 0;
	
	Data_H = MP6050_ReadReg(MP6050_ACCEL_XOUT_H);
	Data_L = MP6050_ReadReg(MP6050_ACCEL_XOUT_L);
	MP6050ParaOut->AccelX = (Data_H << 8) | Data_L;		//MP6050加速度计X轴
	
	
	Data_H = MP6050_ReadReg(MP6050_ACCEL_YOUT_H);
	Data_L = MP6050_ReadReg(MP6050_ACCEL_YOUT_L);
	MP6050ParaOut->AccelY = (Data_H << 8) | Data_L;		//MP6050加速度计Y轴
	
	Data_H = MP6050_ReadReg(MP6050_ACCEL_ZOUT_H);
	Data_L = MP6050_ReadReg(MP6050_ACCEL_ZOUT_L);
	MP6050ParaOut->AccelZ = (Data_H << 8) | Data_L;		//MP6050加速度计Z轴
	
	
	Data_H = MP6050_ReadReg(MP6050_GYRO_XOUT_H);
	Data_L = MP6050_ReadReg(MP6050_GYRO_XOUT_L);
	MP6050ParaOut->GyroX = (Data_H << 8) | Data_L;		//MP6050角速度计X轴
	
	Data_H = MP6050_ReadReg(MP6050_GYRO_YOUT_H);
	Data_L = MP6050_ReadReg(MP6050_GYRO_YOUT_L);
	MP6050ParaOut->GyroY = (Data_H << 8) | Data_L;		//MP6050角速度计X轴
	
	Data_H = MP6050_ReadReg(MP6050_GYRO_ZOUT_H);
	Data_L = MP6050_ReadReg(MP6050_GYRO_ZOUT_L);
	MP6050ParaOut->GyroZ = (Data_H << 8) | Data_L;		//MP6050角速度计X轴
}

Main.c

#include "stm32f10x.h"                  // Device header
#include "oled.h"
#include "Countersensor.h"
#include "Encoder.h"
#include "Timer.h"
#include "AD.h"
#include "Delay.h"
#include "MyDMA.h"
#include "UART.h"
#include <stdio.h>
#include "Key.h"
#include "String.h"
#include "LED.h"
#include "MyI2C.h"
#include "MP6050.h"
#include "MP6050_Reg.h"

#define MP6050_REG_ID		0x75
#define MP6050_REG_PWRMNG2	0x6B
#define MP6050_REG_SIMPDIV	0x19

int main(int argc, char *argv[])
{
	uint8_t AckBit = 0;
	uint8_t MP6050Id;
	uint8_t RegVal = 0;
	struct MP6050_Data_ParaOut MP6050Data = {0};
	
	OLED_Init();
	MP6050_Init();

	while(1)                                                                                 
	{
		MP6050_GetData(&MP6050Data);
		OLED_ShowSignedNum(1, 1, MP6050Data.AccelX ,5);
		OLED_ShowSignedNum(1, 8, MP6050Data.AccelY ,5);
		OLED_ShowSignedNum(2, 1, MP6050Data.AccelZ ,5);
		OLED_ShowSignedNum(3, 1, MP6050Data.GyroX ,5);
		OLED_ShowSignedNum(3, 8, MP6050Data.GyroY ,5);
		OLED_ShowSignedNum(4, 1, MP6050Data.GyroZ ,5);
		Delay_ms(100);
	}
	
	return 1;
}

实验结果:

使用淘宝购买的19块钱的24MHz 8通过逻辑分析仪抓取下硬件I2C读取时的通信时序。

可以看到硬件I2C的时序,SCLK时钟特别规整,SDA的变化也紧跟着SCK的下降沿。

3🚚实验问题记录和经验分享

本节硬件I2C实验遇到了一个问题,就是I2C_ReceiveData()读取出来的数据总是固定的值 '0xD',反复排查才发现是因为在读取I2C DR接收寄存器之前没有等待EV7事件。如果不等待EV7事件就直接读取I2C DR寄存器,根据STM32的主接收时序此时DR寄存器的值还是上一次的值,I2C数据还没有开始接收,所以此时立即读I2C DR寄存器就总是读取到的上一次值。

正确的做法是,接收时先等待EV7事件确认I2C 已经接收到数据,再读取I2C DR寄存器,此时读取到的才是正确的接收数据。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学习虚幻C++开发日志——定时器

官方文档&#xff1a;虚幻引擎中的Gameplay定时器 | 虚幻引擎 5.5 文档 | Epic Developer Community | Epic Developer Community 定时器 安排在经过一定延迟或一段时间结束后要执行的操作。例如&#xff0c;您可能希望玩家在获取某个能力提升道具后变得无懈可击&#xff0c;…

【简道云 -注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

【表格解决问题】EXCEL行数过多,WPS如何按逐行分别打印多个纸张中

1 问题描述 如图&#xff1a;我的表格行数太多了。打印在一张纸上有点不太好看 2 解决方式 Step01&#xff1a;先选中你需要打印的部分&#xff0c;找到【页面】->【打印区域】->【设置打印区域】 Step02&#xff1a;先选中一行&#xff0c;找到【插入分页符】 Step0…

提高交换网络可靠性之链路聚合

转载请注明出处 该实验为链路聚合的配置实验。 1.改名&#xff0c;分别将交换机1和交换机2改名为S1&#xff0c;S2&#xff0c;然后查看S1&#xff0c;S2的STP信息。以交换机1为例&#x1f447;。 2.交换机S1&#xff0c;S2上创建聚合端口&#xff0c;将端口加入聚合端口。以S…

SpringMVC笔记 一万字

此笔记来自于B站尚硅谷 文章目录 一、SpringMVC 简介1、什么是MVC2、什么是SpringMVC3、SpringMVC的特点 二、HelloWorld1、开发环境2、创建maven工程a>添加web模块b>打包方式&#xff1a;warc>引入依赖 3、配置web.xmla>默认配置方式b>扩展配置方式 4、创建请求…

【Hive sql面试题】找出连续活跃3天及以上的用户

表数据如下&#xff1a; 要求&#xff1a;求出连续活跃三天及以上的用户 建表语句和插入数据如下&#xff1a; create table t_useractive(uid string,dt string );insert into t_useractive values(A,2023-10-01 10:10:20),(A,2023-10-02 10:10:20),(A,2023-10-03 10:16…

livp是什么格式文件?这几款软件可以轻松处理!

今天&#xff0c;我们要探讨的是一种可能相对陌生但又颇具特色的文件格式——LIVP。它通常与某些特定的软件或设备相关联&#xff0c;比如某些品牌的相机或视频编辑软件。LIVP文件往往包含了丰富的图像或视频信息&#xff0c;以及与之相关的元数据&#xff08;如拍摄时间、地点…

贪心算法---java---黑马

贪心算法 1)Greedy algorithm 称之为贪心算法或者贪婪算法&#xff0c;核心思想是 将寻找最优解的问题分为若干个步骤每一步骤都采用贪心原则&#xff0c;选取当前最优解因为未考虑所有可能&#xff0c;局部最优的堆叠不一定得到最终解最优 贪心算法例子 Dijkstra while …

基于vue框架的的留守儿童帮扶管理系统c2691(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;留守儿童,帮扶活动,申请记录,帮扶机构,帮扶进度,帮扶人,申请加入记录,参与帮扶记录,地区信息 开题报告内容 基于Vue框架的留守儿童帮扶管理系统开题报告 一、研究背景与意义 在现代化进程中&#xff0c;随着城乡经济差异的不断扩大&a…

MySQL数据库迁移到DM8数据库

1. 达梦新建zsaqks库 2. 打开DM数据迁移工具 3. 新建工程 4. 迁移 - 右击 - 新建迁移 下一步 5. 选择迁移方式 6. MySQL数据源 请输入MySQL数据库信息 7. DM数据库目的 请输入达梦数据库信息 8. 迁移选项 保持对象名大小写(勾选) 9. 指定模式 指定是从数据源复制对象。 10.…

关于电脑蓝屏的那些解决方案--总有一款适合你

目录 背景内存检测硬盘检测拆机除尘上硅脂查看蓝屏日志--计算机管理1796事件进入bios启用安全启动状态创建转储期间出错失败蓝屏crystaldiskinfo查找BitLocker 恢复密钥关闭cpu-c步骤一&#xff1a;进入BIOS设置步骤二&#xff1a;找到CPU C-state设置步骤三&#xff1a;关闭CP…

HTML 语法规范——代码注释、缩进与格式、标签与属性、字符编码等

文章目录 一、代码注释1.1 使用注释的主要目的1.2 使用建议二、标签的使用2.1 开始标签和结束标签2.2 自闭合标签2.3 标签的嵌套2.4 标签的有效性三、属性四、缩进与格式4.1 一致的缩进4.2 元素单独占用一行4.3 嵌套元素的缩进4.4 避免冗长的行五、字符编码六、小结在开发 HTML…

项目一:使用 Spring + SpringMVC + Mybatis + lombok 实现网络五子棋

一&#xff1a;系统展示: 二&#xff1a;约定前后端接口 2.1 登陆 登陆请求&#xff1a; GET /login HTTP/1.1 Content-Type: application/x-www-form-urlencodedusernamezhangsan&password123登陆响应&#xff1a; 正常对象&#xff1a;正常对象会在数据库中存储&…

从 vue 源码看问题 — vue 初始化都做了什么事?

前言 最近想要对 Vue2 源码进行学习&#xff0c;主要目的就是为了后面在学习 Vue3 源码时&#xff0c;可以有一个更好的对比和理解&#xff0c;所以这个系列暂时不会涉及到 Vue3 的内容&#xff0c;但是 Vue3 的核心模块和 Vue2 是一致的&#xff0c;只是在实现上改变了方式、…

如何在BSV区块链上实现可验证AI

​​发表时间&#xff1a;2024年10月2日 nChain的顶尖专家们已经找到并成功测试了一种方法&#xff1a;通过区块链技术来验证AI&#xff08;人工智能&#xff09;系统的输出结果。这种方法可以确保AI模型既按照规范运行&#xff0c;避免严重错误&#xff0c;遵守诸如公平、透明…

MATLAB——矩阵操作

内容源于b站清风数学建模 数学建模清风老师《MATLAB教程新手入门篇》https://www.bilibili.com/video/BV1dN4y1Q7Kt/ 目录 1.MATLAB中的向量 1.1向量创建方法 1.2向量元素的引用 1.3向量元素修改和删除 2.MATLAB矩阵操作 2.1矩阵创建方法 2.2矩阵元素的引用 2.3矩阵…

SQL基础—2

1.左外连接查询&#xff08;left join on&#xff09; A - A∩B 左外连接查询两张表条件都满足的数据&#xff0c;以及左边表(A表)存在的数据(以左边表为主查询表)。 A - A∩B (A和A交B)。 示例&#xff1a;使用左外连接将dept表作为主查询表&#xff0c;查询员工编号、员工姓…

【Java并发】乐观锁、悲观锁、CAS、版本号机制

前言 在现代计算机系统中&#xff0c;处理并发操作时&#xff0c;锁机制是至关重要的。本文将介绍乐观锁、悲观锁以及CAS&#xff08;Compare and Swap&#xff09;这三种常见的并发控制技术&#xff0c;帮助理解它们的原理和应用场景。 1.悲观锁 1.1 定义 悲观锁是一种在访…

【优选算法】——二分查找!

目录 1、二分查找 2、在排序数组中查找元素的第一个和最后一个位置 3、搜索插入位置 4、x的平方根 5、山脉数组的封顶索引 6、寻找峰值 7、寻找旋转排序数组中的最小值 8、点名 9、完结散花 1、二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组…

Fooocus图像生成软件本地部署教程:在Windows上快速上手AI创作

文章目录 前言1. 本地部署Fooocus图像生成软件1.1 安装方式1.2 功能介绍 2. 公网远程访问Fooocus3. 固定Fooocus公网地址 前言 本篇文章将介绍如何在本地Windows11电脑部署开源AI生图软件Fooocus&#xff0c;并结合Cpolar内网穿透工具轻松实现公网环境远程访问与使用。 Foooc…