stm32之软件I2C读写MPU6050陀螺仪、加速度传感器应用案例

news2024/11/17 9:36:38

系列文章目录

1. stm32之I2C通信协议


文章目录

  • 系列文章目录
  • 前言
  • 一、电路接线图
  • 二、应用案例代码
  • 三、应用案例分析
    • 3.1 I2C通信模块
    • 3.2 MPU6050模块


前言

提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者

本案例实现了一个stm32使用软件I2C通信读写MPU6050陀螺仪、加速度传感器的功能,最终我们将MPU6050的实时数据显示在了OLED上。OLED最上面显示的是设备ID号,左下角三个是加速度传感器的输出数据,分别为X轴、Y轴和Z轴的加速度。右边三个是陀螺仪传感器的输出数据,分别为X轴、Y轴和Z轴的角速度。当我们改变MPU6050传感器的姿态这六个数据就会相应地变化。


一、电路接线图

在这里插入图片描述
在这里,stm32作为主机,MPU6050作为从机,是一主一从的模型。

j接线方面,MPU6050模块的VCC和GND分别接到电源的正负极进行供电,SCL接到stm32的PB10口,SDA接到stm32的PB11口。XCL和XDA用于扩展的接口这里暂时用不到就先不接。AD0引脚可用于修改从机地址的最低位,如果有需要可以接上,这里由于模块内置了下拉电阻,所以引脚悬空的话相当于接地。最后INT,中断信号输出脚,我们暂时用不到先不接。

由于本次通信使用的是软件模拟I2C,就是用普通的GPIO口手动翻转电平实现的协议,它并不需要stm32内部的外设资源支持,所以这里的端口其实可以任意指定,我们只需要在程序中指定好即可。这算是软件I2C相比于硬件I2C的一大优势,就是端口不受限,可以任意指定。

注意:根据I2C协议的硬件电路规定SCL和SDA都应该外挂一个上拉电阻的,但是由于MPU6050模块本身内部就已经集成了上拉电阻了,所以这里就不需要再外挂上拉电阻了。

二、应用案例代码

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

MyI2C.c:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

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);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);
	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);
		MyI2C_W_SCL(0);
	}
}

uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;
	MyI2C_W_SDA(1);
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SCL(1);
		if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}
		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);
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA();
	MyI2C_W_SCL(0);
	return AckBit;
}

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

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.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);
	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);
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);
}

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;
	
	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;
}

主程序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);
	}
}

完整工程:stm32之软件I2C读写MPU6050陀螺仪、加速度传感器

三、应用案例分析

这里大概讲一下思路,细节部分我就不讲了,如果你仔细看过上一篇关于软件I2C通信的文章,那其实实现I2C通信大部分内容只不过就是一个代码逻辑思维的过程而已。

从整体架构来看主要分为I2C通信模块、MPU6050模块以及OLED模块,接下来重点分析一下I2C通信模块以及MPU6050模块。

3.1 I2C通信模块

I2C通信模块主要封装了一个模块初始化函数和六个时序单元模块。

I2C模块初始化函数:

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);
}

初始化函数里主要做了两件事,第一件事就是把SCL和SDA都初始化为开漏输出模式,第二件事就是把SCL和SDA置为高电平。

注意:这里SCL和SDA都配置为开漏输出的模式,这个原因在介绍I2C硬件电路规定时已经讲过了。

六个时序单元模块:

六个时序单元分别是起始信号、终止信号、发送一个字节、接收一个字节、发送应答以及接收应答。

发起通信、结束通信:

起始信号:SCL高电平期间,SDA从高电平切换到低电平
终止信号:SCL高电平期间,SDA从低电平切换到高电平

void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}

这里要注意的是,为了保证时序的统一性,通信开始和通信结束都要把SCL跟SDA释放保持高电平状态,因为你不能保证SCL跟SDA在上一个时序的状态是怎样的。

一开始,空闲状态下SCL和SDA都处于高电平状态,没有任何设备占据总线。

当主机需要与从机通信时。首先,在SCL高电平期间主机拉低SDA产生一个起始信号S(Start),紧跟着,在起始信号之后主机把SCL拉低(为等会释放SCL产生高电平作准备)。

当主机不再需要跟主机通信时。首先,在产生终止信号之前主机先拉低SDA为后续SDA的上升沿作准备,然后释放SCL,再释放SDA,这样就产生了SCL高电平期间SDA的上升沿了,也就是终止信号了。

最后SCL和SDA无论是在起始还是结束的时候都是处于高电平的状态。

发送、接收一个字节:

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);
		MyI2C_W_SCL(0);
	}
}

uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;
	MyI2C_W_SDA(1);
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SCL(1);
		if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}
		MyI2C_W_SCL(0);
	}
	return Byte;
}

发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节。

这里关于如何依次提取字节中位的操作请参考我的另一篇文章,我在这篇文章里已经详细地分析过了,这里就不再累述了,文章传送门:计算机常见运算之左移操作、右移操作以及按位与、按位或

接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)

这里讲一下接收的逻辑。

首先,声明并初始化一个 uint8_t 类型的变量 Byte用于存储接收到的8位数据,初始值为 0x00,即全零。

MyI2C_R_SDA() 用于读取 SDA 线的电平状态。如果 SDA 线为高电平,则表示当前接收到的数据位是 1。如果为低电平,则是 0。

if (MyI2C_R_SDA() == 1) 检查 SDA 线的状态。如果为高电平(即接收到的数据位为 1),则将该位写入 Byte 中对应的位置。

Byte |= (0x80 >> i) 通过按位或运算将接收到的 1 设置在 Byte 中的正确位置。0x80 是 1000 0000,通过 >> i 操作依次右移,将第 i 位接收到的 1 写入 Byte 中。

当 i = 0 时,0x80 >> 0 仍然是 1000 0000,因此该位被写入 Byte 的最高位(MSB)。
当 i = 1 时,0x80 >> 1 是 0100 0000,因此该位被写入 Byte 的次高位。
依此类推,直到 i = 7 时,0x80 >> 7 是 0000 0001,该位被写入 Byte 的最低位(LSB)。

发送应答以及接收应答:

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);
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA();
	MyI2C_W_SCL(0);
	return AckBit;
}

发送应答: 主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答

接收应答: 主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)

这里要注意的是在发送或接收完应答位之后将SCL拉低的目的是为了更好地衔接时序单元。

3.2 MPU6050模块

MPU6050模块主要封装了一个模块初始化函数以及一个指定地址写函数和一个指定地址读函数。

指定地址写:

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();
}

指定地址写逻辑:发起起始信号 -> 从机寻址+指定读写位(0表示为写入数据) -> 接收从机应答位ACK -> 指定寄存器地址 -> 接收从机应答位ACK -> 写入一个字节数据 -> 接收从机应答位ACK -> 发起终止信号

指定地址读:

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);
	MyI2C_ReceiveAck();
	Data = MyI2C_ReceiveByte();
	MyI2C_SendAck(1);
	MyI2C_Stop();
	
	return Data;
}

指定地址读逻辑:发起起始信号 -> 从机寻址+指定读写位(0表示为写入数据) -> 接收从机应答位ACK -> 指定寄存器地址 -> 接收从机应答位ACK -> 重新发起起始信号 -> 从机寻址+指定读写位(1表示为读取数据) -> 接收从机应答位ACK -> 读取一个字节数据 -> 主机发送应答位(1表示为非应答) -> 发起终止信号

模块初始化:

MPU6050配置参数解析如下:

void MPU6050_Init(void)
{
	MyI2C_Init();
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//接触睡眠,选择陀螺仪时钟
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);//六个轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//采用分频为10
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);//滤波参数给最大
	//陀螺仪和加速度计都选择最大量程
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);
}

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

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

相关文章

空间计量 | 空间杜宾误差模型SDEM

空间计量研究中&#xff0c;空间杜宾误差模型&#xff0c;其考虑两项&#xff0c;分别是自变量X的空间滞后作用&#xff0c;以及误差扰动项的空间滞后作用&#xff0c;其数学模型公式如下&#xff1a; y βk * x θk * Wx u, u λ * Wu &#xff08;其中βk表示X的回归系…

AI学习记录 - 线性代数(3Blue1Brown)

一天更新一点点&#xff0c;只更新重点内容&#xff0c;一句话定义&#xff0c;简单的定义&#xff0c;避免脑子及记太多 向量的加法就是一种趋势运动 向量的延长缩短&#xff0c;就是分量的延长缩短 基向量就是在平面或者任意维度空间随便定义的一个向量 多个基向量的组合可…

每天分享一个FPGA开源代码(1)- spi

1、SPI总线进行通信的结构 SPI总线主要包括四根关键信号线&#xff1a; &#xff08;1&#xff09;SCK (Serial Clock) 串行时钟线&#xff0c;由主设备产生&#xff0c;控制数据传输的速率和时机。 &#xff08;2&#xff09;MOSI (Master Out Slave In) 主设备数据输出线…

指针的一些细节补充———C语言

野指针&#xff1a; 1.未初始化的指针&#xff1a; eg&#xff1a; int *p; // 未初始化的指针 *p 5; // 未定义行为&#xff0c;p 是野指针 ————————————————————————————————————————————————————————…

记Codes 开源研发项目管理平台——管理系统颠覆性创新实现之事件驱动+信息流

引言 市面上所有管理系统&#xff0c;数据都不是以推流的方式展现到前端&#xff0c;有新数据产生需主动刷新页面才能看到&#xff0c;也就是“人找事”&#xff1b;而不是主动推送的“事找人”&#xff0c;Codes 敢为人先&#xff0c;采用事件驱动信息流实现“事找人”。 1、…

一起学习LeetCode热题100道(64/100)

64.搜索二维矩阵(学习) 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&am…

Java Excel转PDF(免费)

目前市面上 Excel 转 PDF 的组件较多&#xff1a; 收费&#xff1a;aspose、GcExcel、spire开源&#xff1a;jacob、itextpdf 其中收费的组件封装得比较好&#xff0c;代码简洁&#xff0c;转换的效果也很好&#xff0c;但收费也高得离谱&#xff1a; 为了成本考虑&#xff…

共享单车|基于SprinBoot+vue的共享单车数据储存系统(源码+数据库+文档)

共享单车数据储存系统 基于SprinBootvue的共享单车数据储存系统 一、前言 二、系统设计 三、系统功能设计 系统登录注册实现 管理员模块实现 用户模块实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介…

STM32和TMSF28335定时器的系统时钟问题

用户可通过多个预分频器配置AHB、高速APB(APB2)和低速APB(APB1)域的频率。AHB和APB2域的最大频率是72MHz。APB1域的最大允许频率是36MHz。 定时器时钟频率分配由硬件按以下2种情况自动设置&#xff1a; 如果相应的APB预分频系数是1&#xff0c;定时器的时钟频率与所在APB总线…

俄罗斯OZON真的适合小白做吗,有哪些坑需要知道

OZON 对于小白来说有一定的可行性&#xff0c;但也存在一些需要注意的 “坑”&#xff1a; OZON 平台适合小白的原因&#xff1a; 入驻门槛较低&#xff1a;目前中国卖家入驻 OZON 是免费的&#xff0c;具备企业营业执照&#xff08;暂不支持个体工商户注册&#xff09;&#…

Part4-DOM学习笔记-获取元素属性及节点操作

6.获取元素属性 6.1 获取元素属性 获取元素的属性有两种方式&#xff1a; element.属性&#xff1a; 获取内置属性值&#xff0c;元素本身自带的属性 不能获取自定义属性 代码示例如 console.log(div.id)element.getAttribute(‘属性’)&#xff1a; 可以获取内置属性值 可…

高带宽云服务器有什么用处?这几种用处要知道

高带宽云服务器的主要用途包括提供更快的网络连接速度、支持高并发访问和大规模数据传输、提高应用程序的性能和用户体验等。在当今数字化时代&#xff0c;随着大数据、云计算、流媒体等技术的飞速发展&#xff0c;高带宽云服务器已成为许多企业和个人用户的首选。以下将详细分…

C++ PCL求解法向量及可视化

【版权声明】本文为博主原创文章&#xff0c;未经博主允许严禁转载&#xff0c;我们会定期进行侵权检索。 参考书籍&#xff1a;《人工智能点云处理及深度学习算法》 本文为专栏《Python三维点云实战宝典》系列文章&#xff0c;专栏介绍地址“【python三维深度学习】python…

C语言sprintf函数使用

1 其函数原型为&#xff1a;int sprintf(char *str, const char *format,...)。 具体用法如下&#xff1a; 基本语法&#xff1a; str&#xff1a;目标字符串的指针&#xff0c;用于存储格式化后的结果。format&#xff1a;格式化字符串&#xff0c;用于指定输出的格式。后续是…

IP地址SSL证书要怎么申请?

申请IP地址SSL证书的过程可以简化为以下四个步骤&#xff1a; 1. 选择证书提供商 确定需求&#xff1a;首先&#xff0c;明确你的需求&#xff0c;包括所需的证书类型&#xff08;如IP地址证书&#xff09;和加密算法等。选择CA机构&#xff1a;选择一个受信任的证书颁发机构…

有了这款低代码工具,从此让你告别烦人的996报表制作模式

一、低代码平台兴起的背景 近年来&#xff0c;低代码与零代码平台的兴起&#xff0c;无疑是IT领域的一股强劲风潮&#xff0c;它们依托互联网技术的飞速进步&#xff0c;致力于简化软件开发流程&#xff0c;推动工具向更加易用、高效的方向演进。在这一浪潮中&#xff0c;尽管…

重塑工程机械设备市场:创新网络推广方案引领潮流

在重塑工程机械设备领域&#xff0c;网络营销推广已日益凸显为企业拓展市场版图、提升品牌知名度不可或缺的关键手段。鉴于互联网时代的海量信息与激烈竞争&#xff0c;关键词搜索排名优化更是成为了衡量网络推广成效的重要标尺。为了在这场数字战役中脱颖而出&#xff0c;我们…

二、测试的基本概念

文章目录 一、需求&#xff08;一&#xff09;需求的概念&#xff08;二&#xff09;用户需求&#xff08;三&#xff09;软件需求 二、从软件测试人员角度看需求&#xff08;一&#xff09;为什么需求对软件测试人员如此重要&#xff08;二&#xff09;如何才可以深入理解被测…

【论文速读】| ARVO: 开源软件可重现漏洞的全景图

本次分享论文&#xff1a;ARVO: Atlas of Reproducible Vulnerabilities for Open Source Software 基本信息 原文作者&#xff1a;Xiang Mei, Pulkit Singh Singaria, Jordi Del Castillo, Haoran Xi, Abdelouahab (Habs) Benchikh, Tiffany Bao, Ruoyu Wang, Yan Shoshitai…

Java Email发送:如何配置SMTP服务器发信?

Java Email发送性能如何优化&#xff1f;怎么实现Java发信功能&#xff1f; Java Email发送功能使得开发者能够轻松集成邮件服务到他们的应用程序中。通过配置SMTP服务器&#xff0c;Java Email发送可以实现高效、可靠的邮件传递。AokSend将详细介绍如何配置SMTP服务器以进行J…