stm32入门学习10-I2C和陀螺仪模块

news2024/9/29 21:22:59

(一)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);
		else
			set(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_1

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

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);
		else
			set(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 0001
	mpu_write(0x6C, 0x00);	//PWR_MGMT_2 -> 0000 0000
	mpu_write(0x19, 0x09);	//SMPLRT_DIV -> 0000 1001
	mpu_write(0x1A, 0x06);	//CONFIG -> 0000 0110
	mpu_write(0x1B, 0x18);	//GYRO_CONFIG -> 0001 1000
	mpu_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 0001
	mpu_write(0x6C, 0x00);	//PWR_MGMT_2 -> 0000 0000
	mpu_write(0x19, 0x09);	//SMPLRT_DIV -> 0000 1001
	mpu_write(0x1A, 0x06);	//CONFIG -> 0000 0110
	mpu_write(0x1B, 0x18);	//GYRO_CONFIG -> 0001 1000
	mpu_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的工作流程,解决了一些遇到的错误,积累了错误处理的经验

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

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

相关文章

代码随想录——买卖股票的最佳时机 IV(Leetcode 188)

题目链接 动态规划 class Solution {public int maxProfit(int k, int[] prices) {int[][] dp new int[prices.length][2 * k 1];// 初始化for(int i 1; i < 2 * k 1; i i 2){dp[0][i] -prices[0];}// dp更新寻找最大利润for(int i 1; i < prices.length; i){…

使用Halcon变换与校正图像

使用Halcon变换与校正图像 文章目录 使用Halcon变换与校正图像1. 二维图像的平移、旋转和缩放1.图像的平移2.图像的旋转3.图像的缩放2. 图像的仿射变换3. 投影变换4 实例&#xff1a;透视形变图像校正 由于相机拍摄的时候可能存在角度偏差&#xff0c;因此实际获得的画面可能会…

(C23/C++23) 语句末尾的标签

文章目录 &#x1f516;前言&#x1f3f7;️ref&#x1f3f7;️标号 &#x1f516;兼容&#x1f3f7;️23标准前&#x1f3f7;️23标准后&#x1f3f7;️原因 &#x1f516;未兼容&#x1f516;END&#x1f31f;关注我 &#x1f516;前言 &#x1f3f7;️ref C23提案复合语句末…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 两数之和绝对值最小(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题…

基于机器学习和深度学习的时间序列分析和预测(Python)

时间序列数据与其它数据不同主要是因为时间序列数据在时间维度上存在依赖关系&#xff0c;这说明在时间序列数据当中过去的历史数据当中隐藏着一些时间序列数据固有的特性&#xff0c;例如&#xff0c;周期性、趋势性、不规则性等。时间序列预测便是通过不同的方法来捕捉这种规…

【抽象工厂模式】从理论到实战:构建可扩展的软件家族(设计模式系列)

文章目录 Java设计模式系列&#xff1a;抽象工厂模式详解1. 引言抽象工厂模式概述为何选择抽象工厂模式 2. 基础知识回顾Java基础概念复习面向对象编程原则设计模式的原则和目的 3. 抽象工厂模式的定义定义与解释模式的目的与其他工厂模式的区别 4. 抽象工厂模式的结构抽象产品…

【Android】数据持久化——数据存储

持久化技术简介 在你打开完成了一份PPT之后关闭程序&#xff0c;再次打开肯定是希望之前的内容还存在在电脑上&#xff0c;一打开PPT&#xff0c;之前的内容就自动出现了。数据持久化就是将那些内存中的瞬时数据保存到存储设备中&#xff0c;保证即使在手机或电脑关机的情况下…

React 学习——别名路径配置(可以使用@代表src),引用文件时使用;联想路径提示

一.别名路径配置 1、安装craco &#xff08;npm i -D craco/craco&#xff09;&#xff1b;安装成功的截图如下&#xff1a; 2、在项目的根目录下创建一个 名为 craco.config.js 文件&#xff1b;&#xff08;必须是根目录下&#xff0c;名称必须和我这个一样&#xff09;&…

C语言调试宏全面总结(六大板块)

C语言调试宏进阶篇&#xff1a;实用指南与案例解析C语言调试宏高级技巧与最佳实践C语言调试宏的深度探索与性能考量C语言调试宏在嵌入式系统中的应用与挑战C语言调试宏在多线程环境中的应用与策略C语言调试宏在并发编程中的高级应用 C语言调试宏进阶篇&#xff1a;实用指南与案…

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

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

Vue3 列表自动滚动播放(表头固定、列表内容自动滚动播放)+ vue3-seamless-scroll - 附完整示例

vue3-seamless-scroll&#xff1a;Vue3.0 无缝滚动组件&#xff0c;支持Vite2.0&#xff0c;支持服务端打包 目前组件支持上下左右无缝滚动&#xff0c;单步滚动&#xff0c;并且支持复杂图标的无缝滚动&#xff0c;目前组件支持平台与Vue3.0支持平台一致。 目录 效果 一、介绍…

安装vscode -- linux

前言 相信很多人在刚开始使用linux时&#xff0c;不知道怎么安装vscode来辅助我们编程&#xff0c;那么我将在此记录我所用的安装vscode的方法。 安装方法 方法一&#xff1a;snap 第一步&#xff1a;检查软件更新状况 sudo apt update在终端输入上述命令&#xff0c;会提…

大模型学习笔记 - LLM 之RLHF人类对齐的简单总结

LLM - RLHF人类对齐的简单总结 LLM-人类对齐 1. RLHF(Reinforcement Learning from Human Feedback, RLHF),基于人类反馈的强化学习2 奖励模型训练3 强化学习训练 3.1 PPO介绍3.2 进阶的RLHF的介绍 3.2.1. 过程监督奖励模型3.2.2. 基于AI反馈的强化学习3.2.3. 非强化学习的对齐…

卷积神经网络 - 基本卷积函数的变体篇

序言 在深度学习和卷积神经网络&#xff08; CNN \text{CNN} CNN&#xff09;的广阔领域中&#xff0c;基本卷积函数是构建网络结构的基础&#xff0c;它们通过滑动窗口的方式对输入数据进行特征提取。然而&#xff0c;随着应用场景和数据复杂性的增加&#xff0c;单一的卷积方…

苹果Vision Pro生态发展:现状、挑战与未来展望

苹果公司以其创新技术和强大的生态系统闻名于世。在最近的财报会议上,CEO蒂姆库克分享了Vision Pro平台的最新进展,引发了业界的广泛关注。本文将深入探讨Vision Pro生态的现状、面临的挑战以及与其他XR平台的对比分析。 一、Vision Pro生态现状 据库克介绍,Vision Pro平台…

爬1688商品---(测试版)

半成品. from DrissionPage import ChromiumPage import time from selenium import webdriver urlhttps://p4psearch.1688.com/hamlet.html?scene6&cositebaidujj_pz&locationre&trackid885662561117990122602pageChromiumPage()page.get(url)def key_wof():inde…

C++ QT开发 学习笔记(3)

C QT开发 学习笔记(3) - WPS项目 标准对话框 对话框类说明静态函数函数说明QFileDialog文件对话框getOpenFileName()选择打开一个文件getOpenFileNames()选择打开多个文件getSaveFileName()选择保存一个文件getExistingDirectory()选择一个己有的目录getOpenFileUrl()选择打幵…

荒原之梦考研:考研二战会很难吗?

考研二战是不是很难&#xff0c;其实很大程度上取决于我们自己&#xff0c;我们能否认清自己的优势&#xff0c;能否指定和执行合理的计划&#xff0c;有没有强大的心理支撑等&#xff0c;都是决定考研二战能否成功&#xff0c;或者能否比较轻松的成功的关键。 在本文中&#…

HCIP重修总笔记(中)

第八节 BGP基础 一、BGP产生背景 BGPBorder Gateway Protocol&#xff0c;边界网关协议)是一种用于自治系统间的动态路出协议&#xff0c;是一种外部网关协议。 自治系统AS:一组同一个管理机构进行管理&#xff0c;对外呈现统一选路策略的路由器的集合。 自治系统编号: …

浅谈基础的图算法——强联通分量算法(c++)

文章目录 强联通分量SCC概念例子有向图的DFS树代码例题讲解[POI2008] BLO-Blockade题面翻译题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 思路AC代码 【模板】割点&#xff08;割顶&#xff09;题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示…