ESP32官方MPU6050组件介绍

news2024/11/25 0:30:00

前言

(1)因为我需要使用MPU6050的组件,但是又需要在这条I2C总线上挂载多个设备,所以我本人打算自己对官方的MPU6050的组件进行微调。建立一个I2C总线,设备依赖于这个总线挂载。
(2)既然要做移植工作,所以就需要弄明白这个组件应当如何使用。

MPU6050组件函数介绍

mpu6050_create()

使用方法

(1)此函数用于创建一个指向MPU6050初始化信息的mpu6050_handle_t无符号类型指针。我们可以通过这个指针访问到mpu6050.c这个组件中的mpu6050_dev_t结构体。
(2)这样做能够做到常说的高内聚,低耦合的作用。如果不明白也没事,记住他返回的这个数据是和MPU6050的信息有关,我们能用就行
(3)如何传入数据:
<1>当你的MPU6050是挂载在ESP32的I2C0时候,传入I2C_NUM_0。如果是I2C1,传入I2C_NUM_1。如果是挂载在低功耗I2C中,传入LP_I2C_NUM_0。需要注意的是,不是所有的ESP32都具有I2C1和低功耗I2C,这个需要自行查阅芯片手册。
<2>关于MPU6050的地址,有两种。当MPU6050的第9号引脚AD0为低电平的时候地址为0x68,即MPU6050_I2C_ADDRESS。当这个引脚为高电平时候地址为0x69,即MPU6050_I2C_ADDRESS_1

/**
 * @brief  初始化MPU6050相关信息
 * 
 * @param   port       要挂载在哪个I2C总线上
 *         -dev_addr   MPU6050的地址
 * 
 * @return  NULL       初始化MPU6050的信息失败
 *         -非NULL指针 初始化MPU6050的信息成功
*/
mpu6050_handle_t mpu6050_create(i2c_port_t port, const uint16_t dev_addr)

简单概述底层实现

(1)mpu6050_handle_t存放在头文件中,用于暴露接口。
(2)mpu6050_dev_t这个结构体具体作用讲实话我也没有搞太明白,只能说我知道的认为重要的两个部分。
<1>bus,存放mpu6050是挂载在哪个I2C下。
<2>dev_addr,MPU6050的地址。
(3)函数实现部分介绍,注意,我也只能讲我懂的部分:
<1>使用calloc()函数分配1个mpu6050_dev_t类型的空间,并且将这块空间全部初始化为0。
<2>对申请到的sensor变量进行初始化,将MPU6050挂载的I2C信息,MPU6050地址信息存入这个变量。可能有些人会有疑惑,为什么地址信息dev_addr << 1需要进行一次右移操作。这个和I2C的时序逻辑有关,一般I2C设备的地址为7bit,最后1bit负责存放是对设备读还是写的信息。因此这里需要进行右移一位。
<3>最后返回的数据进行强制类型转换为mpu6050_handle_t这样就能够实现我上述所说的高内聚,低耦合的功能。

/*--- mpu6050.h ---*/
typedef void *mpu6050_handle_t;

/*--- mpu6050.c ---*/
typedef struct {
    i2c_port_t bus;
    gpio_num_t int_pin;
    uint16_t dev_addr;
    uint32_t counter;
    float dt;  /*!< delay time between two measurements, dt should be small (ms level) */
    struct timeval *timer;
} mpu6050_dev_t;

mpu6050_handle_t mpu6050_create(i2c_port_t port, const uint16_t dev_addr)
{
    mpu6050_dev_t *sensor = (mpu6050_dev_t *) calloc(1, sizeof(mpu6050_dev_t));
    sensor->bus = port;
    sensor->dev_addr = dev_addr << 1;
    sensor->counter = 0;
    sensor->dt = 0;
    sensor->timer = (struct timeval *) calloc(1, sizeof(struct timeval));
    return (mpu6050_handle_t) sensor;
}

mpu6050_config()

使用介绍

(1)用于设置MPU6050的加速度计满量程和陀螺仪满量程。
(2)如何传入数据:
<1>传入mpu6050_create()函数创建的mpu6050_handle_t指针。
<2>ACCE_FS_2G,加速度计满量程为+/-2g。ACCE_FS_4G,为+/-4g。ACCE_FS_8G,为+/-8g。ACCE_FS_16G,为+/-16g。
<3>GYRO_FS_250DPS,陀螺仪满量程是+/- 250度每秒。GYRO_FS_500DPS,为500度每秒。GYRO_FS_1000DPS,为1000度每秒。GYRO_FS_2000DPS,为2000度每秒。
(3)关于这个设置由你自己看情况决定。如果值太高将会降低分辨率,值太低就无法测量过高的数据。

/**
 * @brief  设置MPU6050的加速度计满量程和陀螺仪满量程
 * 
 * @param   sensor   mpu6050_create()函数创建的mpu6050_handle_t指针
 *         -acce_fs  设置加速度计满量程
 *         -gyro_fs  设置陀螺仪满量程
 * 
 * @return  ESP_OK   MPU6050配置成功
 *         -ESP_FAIL MPU6050配置失败
*/
esp_err_t mpu6050_config(mpu6050_handle_t sensor, 
						 const mpu6050_acce_fs_t acce_fs,
					     const mpu6050_gyro_fs_t gyro_fs)

简单概述底层实现

(1)进入mpu6050_config()函数,我们会看到他先建立了一个8bit的无符号数组,将陀螺仪配置放在前面,加速度计配置放在后面。这是因为MPU6050的陀螺仪配置寄存器GYRO_CONFIG在加速度计寄存器ACCEL_CONFIG的前面,而MPU6050的每个寄存器为8bit。

esp_err_t mpu6050_config(mpu6050_handle_t sensor, 
						 const mpu6050_acce_fs_t acce_fs, 
						 const mpu6050_gyro_fs_t gyro_fs)
{
    uint8_t config_regs[2] = {gyro_fs << 3,  acce_fs << 3};
    return mpu6050_write(sensor, MPU6050_GYRO_CONFIG, config_regs, sizeof(config_regs));
}

mpu6050_write()

MPU6050写数据I2C格式

(1)前面我们知道了mpu6050_config()配置如何实现的,但是又有一个问题mpu6050_write()里面做了什么。
(2)我们讲解mpu6050_write()函数前,需要知道主机和MPU6050的通讯格式。
(3)原图在MPU-6000 and MPU-6050 Product Specification Revision 3.4手册的35页,也就是9.3 I2C Communications Protocol章节。直接看图,我不想过多讲解,图依旧很清晰了。

在这里插入图片描述
在这里插入图片描述

函数解析

(1)前面了解了MPU6050的写数据的格式之后,就可以开始解析函数了。
<1>首先对sensor这个mpu6050_handle_t类型指针强制类型转换,前面我们说了,这个mpu6050_handle_t本质就是一个无符号类型指针,利用他能够做到高内聚低耦合。这里你只需传入创建的指针数据,然后强制类型转换即可对这个指针访问。
<2>i2c_cmd_link_create()创建一个I2C 连接的句柄。这个听起来是不是很术语,看不懂。讲实话,我看到这个我也是懵逼的。我查看了一下这个函数的源码,个人理解就是,一个双向链表,里面存储了一些I2C数据传输的信息。之后的I2C数据传输,需要利用这个链表来进行设置。至于为什么使用链表,原因很简单,你不知道I2C通讯会传输多少个数据,而且I2C数据传输肯定是从头往下走,所以采用的链表是很好的决定。
其实和上面对sensor这个mpu6050_handle_t类型指针强制类型转换是一个道理。I2C相关的函数都在i2c.c里面,用于实现高内聚低耦合。
<3>i2c_master_start(),由于I2C协议规定,我们需要先发送一个起始信号。所以这个函数就是将起始信号写入缓存区。
<4>i2c_master_write_byte(),由于I2C协议规定,你发送起始信号之后,I2C上的从机都被激活。这个时候主机需要告诉从机我是在和谁通讯,因此需要传入从机地址信息,也就是MPU6050地址信息。当从机知道主机是在和谁交互时候,I2C总线上其他没有被选中的从机将会进入休眠。只有主机和从机开始通讯。
<5>主机和从机通讯建立完成之后,主机将会告诉从机,我要对那个寄存器进行操作。
<6>i2c_master_write(),现在主机和从机联系和对那个寄存器操作都有一个清晰的认识,于是可以开始传输数据了。
<7>i2c_master_stop(),数据传输完成之后主机需要告诉从机,我数据写完了,你可以休息了。
<8>i2c_master_cmd_begin(),上述操作进行完之后,起始ESP32的I2C并没有真正的工作。上述就是在缓冲区写入数据,调用这个函数,才是真正的将缓冲区的数据输出。
<9>i2c_cmd_link_delete(),通讯结束之后,我们需要调用这个函数删除与I2C的连接。

/**
 * @brief  设置MPU6050的加速度计满量程和陀螺仪满量程
 * 
 * @param   sensor         mpu6050_create()函数创建的mpu6050_handle_t指针
 *         -reg_start_addr 要进行写入数据的寄存器
 *         -data_buf       写入寄存器中的数据
 *         -data_len       写入寄存器中的数据长度
 * 
 * @return  ESP_OK         MPU6050寄存器数据写入成功
 *         -ESP_FAIL       MPU6050寄存器数据写入失败
*/
static esp_err_t mpu6050_write(mpu6050_handle_t sensor, 
						       const uint8_t reg_start_addr, 
						       const uint8_t *const data_buf, 
						       const uint8_t data_len)
{
    mpu6050_dev_t *sens = (mpu6050_dev_t *) sensor;
    esp_err_t  ret;

    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    ret = i2c_master_start(cmd);
    assert(ESP_OK == ret);
    ret = i2c_master_write_byte(cmd, sens->dev_addr | I2C_MASTER_WRITE, true);
    assert(ESP_OK == ret);
    ret = i2c_master_write_byte(cmd, reg_start_addr, true);
    assert(ESP_OK == ret);
    ret = i2c_master_write(cmd, data_buf, data_len, true);
    assert(ESP_OK == ret);
    ret = i2c_master_stop(cmd);
    assert(ESP_OK == ret);
    ret = i2c_master_cmd_begin(sens->bus, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);

    return ret;
}

mpu6050_wake_up()

使用介绍

(1)这里只需要传入MPU6050的句柄即可唤醒MPU6050。

/**
 * @brief  唤醒MPU6050
 * 
 * @param   sensor   mpu6050_create()函数创建的mpu6050_handle_t指针
 * @return  RT_EOK   MPU6050唤醒成功
 *         -RT_ERROR MPU6050唤醒失败
*/
esp_err_t mpu6050_wake_up(mpu6050_handle_t sensor)

简单概述底层实现

(1)这里其实也不难,就是对MPU6050的PWR_MGMT_1寄存器bit6清零即可。因为PWR_MGMT_1寄存器的bit6如果为1,MPU6050将会进入低功耗休眠状态。
(2)至于为什么需要先读取MPU6050的数据,很简单,如果直接写入一个数据,可能导致其他数据位数据被破坏。

esp_err_t mpu6050_wake_up(mpu6050_handle_t sensor)
{
    esp_err_t ret;
    uint8_t tmp;
    ret = mpu6050_read(sensor, MPU6050_PWR_MGMT_1, &tmp, 1);
    if (ESP_OK != ret) {
        return ret;
    }
    tmp &= (~BIT6);
    ret = mpu6050_write(sensor, MPU6050_PWR_MGMT_1, &tmp, 1);
    return ret;
}

mpu6050_read()

MPU6050读数据I2C格式

(1)mpu6050_wake_up()函数里面有一个mpu6050_read()没有进行介绍,这里介绍一下。
(2)MPU6050的读数据有两次起始信号,而写数据只有一次起始信号。第二次起始信号开始之后,就可以读数据了。
(3)原图在MPU-6000 and MPU-6050 Product Specification Revision 3.4手册的36页,也就是9.3 I2C Communications Protocol章节。

在这里插入图片描述

函数解析

(1)这里要根据上图一起理解。与mpu6050_write()函数相同部分我就不再赘述了。
<1>依旧是强制类型转换,建立I2C连接,发送起始信号。
<2>两个i2c_master_write_byte(),这里注意,虽然我们主机ESP32是要读取数据,但是第一次还是写数据,因为从机需要知道,主机接下来是要读取那个寄存器的数据。
<3>第二次i2c_master_start()i2c_master_write_byte(),这个是告诉从机,主机已经可以开始读取你的数据了。
<4>i2c_master_read(),读取数据,这个函数最后一个参数为I2C_MASTER_LAST_NACK表示主机每次收到数据返回一个ACK,不过主机最后一次收到数据返回NACK,告诉从机要停止发数据了。
<5>最后依旧是发送停止信息,使用i2c_master_cmd_begin()函数将缓冲区数据输出。删除与I2C的联系。

/**
 * @brief  设置MPU6050的加速度计满量程和陀螺仪满量程
 * 
 * @param   sensor         mpu6050_create()函数创建的mpu6050_handle_t指针
 *         -reg_start_addr 要进行读取数据的寄存器
 *         -data_buf       读取到的数据存入空间
 *         -data_len       要读取数据的长度
 * 
 * @return  ESP_OK         MPU6050寄存器数据读取成功
 *         -ESP_FAIL       MPU6050寄存器数据读取失败
*/
static esp_err_t mpu6050_read(mpu6050_handle_t sensor, 
                              const uint8_t reg_start_addr, 
                              uint8_t *const data_buf, 
                              const uint8_t data_len)
{
    mpu6050_dev_t *sens = (mpu6050_dev_t *) sensor;
    esp_err_t  ret;

    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    ret = i2c_master_start(cmd);
    assert(ESP_OK == ret);
    ret = i2c_master_write_byte(cmd, sens->dev_addr | I2C_MASTER_WRITE, true);
    assert(ESP_OK == ret);
    ret = i2c_master_write_byte(cmd, reg_start_addr, true);
    assert(ESP_OK == ret);
    ret = i2c_master_start(cmd);
    assert(ESP_OK == ret);
    ret = i2c_master_write_byte(cmd, sens->dev_addr | I2C_MASTER_READ, true);
    assert(ESP_OK == ret);
    ret = i2c_master_read(cmd, data_buf, data_len, I2C_MASTER_LAST_NACK);
    assert(ESP_OK == ret);
    ret = i2c_master_stop(cmd);
    assert(ESP_OK == ret);
    ret = i2c_master_cmd_begin(sens->bus, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);

    return ret;
}

mpu6050_get_xxx简单介绍

(1)因为本人精力有限,不可能每个函数都完完全全介绍一边。经过上面的了解,重要的部分读者应该都了解了。其他的一些函数,对底层实现感兴趣的可以自行查看MPU-6050_Register_Map手册,对照着代码进行理解。
(2)上面的几个函数了解了之后,就只有如下这三个函数需要了解了。
<1>mpu6050_get_acce()函数,获取MPU6050的加速度值。
<2>mpu6050_get_gyro()函数,获取MPU6050的陀螺仪值。
<2>mpu6050_get_temp()函数,获取MPU6050的温度值。
(4)这三个函数,第一个都是传入的I2C句柄。(mpu6050_create()函数创建的mpu6050_handle_t指针)第二个参数略有不同:
<1>mpu6050_get_acce()函数,他需要传入一个mpu6050_acce_value_t类型结构体指针,最终对数据进行处理是采用acce.acce_x方法。

typedef struct {
    float acce_x;    //x轴加速度
    float acce_y;    //y轴加速度
    float acce_z;    //z轴加速度
} mpu6050_acce_value_t;

typedef struct {
    float gyro_x;    //x轴的角速度
    float gyro_y;    //y轴的角速度
    float gyro_z;    //z轴的角速度
} mpu6050_gyro_value_t;

typedef struct {
    float temp;      //MPU6050温度
} mpu6050_temp_value_t;

MPU6050组件使用

单元测试函数简单介绍

(1)在官方MPU6050的组件中,你会看到很多TEST_ASSERT开头的函数,这个是Unity测试单元。作用类似于assert的断言,当测试结果失败,将会终止程序进行复位操作。
(2)GitHub链接:https://github.com/ThrowTheSwitch/Unity
(3)函数简单介绍:
<1>TEST_ASSERT_NOT_NULL_MESSAGE(),用于测试传入的第一个参数是不是空指针,如果是空指针控制台将会输出第二个参数数据,并且终止程序。
<2>TEST_ASSERT_EQUAL(),判断第二个参数是否和第一个参数相等。如果不相等将会终止程序,并且输出信息。
<3>TEST_ASSERT_EQUAL_MESSAGE(),判断第二个参数是否和第一个参数相等。如果不相等将会输出第三个参数数据。

官方例程

(1)如下为官方测试例程。

/*
 * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <stdio.h>
#include "unity.h"
#include "driver/i2c.h"
#include "mpu6050.h"
#include "esp_system.h"
#include "esp_log.h"

#define I2C_MASTER_SCL_IO 26      /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO 25      /*!< gpio number for I2C master data  */
#define I2C_MASTER_NUM I2C_NUM_0  /*!< I2C port number for master dev */
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */

static const char *TAG = "mpu6050 test";
static mpu6050_handle_t mpu6050 = NULL;

/**
 * @brief i2c master initialization
 */
static void i2c_bus_init(void)
{
    i2c_config_t conf;
    conf.mode = I2C_MODE_MASTER;
    conf.sda_io_num = (gpio_num_t)I2C_MASTER_SDA_IO;
    conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
    conf.scl_io_num = (gpio_num_t)I2C_MASTER_SCL_IO;
    conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
    conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
    conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL;

    esp_err_t ret = i2c_param_config(I2C_MASTER_NUM, &conf);
    TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C config returned error");

    ret = i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0);
    TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C install returned error");
}

/**
 * @brief i2c master initialization
 */
static void i2c_sensor_mpu6050_init(void)
{
    esp_err_t ret;

    i2c_bus_init();
    mpu6050 = mpu6050_create(I2C_MASTER_NUM, MPU6050_I2C_ADDRESS);
    TEST_ASSERT_NOT_NULL_MESSAGE(mpu6050, "MPU6050 create returned NULL");

    ret = mpu6050_config(mpu6050, ACCE_FS_4G, GYRO_FS_500DPS);
    TEST_ASSERT_EQUAL(ESP_OK, ret);

    ret = mpu6050_wake_up(mpu6050);
    TEST_ASSERT_EQUAL(ESP_OK, ret);
}

TEST_CASE("Sensor mpu6050 test", "[mpu6050][iot][sensor]")
{
    esp_err_t ret;
    uint8_t mpu6050_deviceid;
    mpu6050_acce_value_t acce;
    mpu6050_gyro_value_t gyro;
    mpu6050_temp_value_t temp;

    i2c_sensor_mpu6050_init();

    ret = mpu6050_get_deviceid(mpu6050, &mpu6050_deviceid);
    TEST_ASSERT_EQUAL(ESP_OK, ret);
    TEST_ASSERT_EQUAL_UINT8_MESSAGE(MPU6050_WHO_AM_I_VAL, mpu6050_deviceid, "Who Am I register does not contain expected data");

    ret = mpu6050_get_acce(mpu6050, &acce);
    TEST_ASSERT_EQUAL(ESP_OK, ret);
    ESP_LOGI(TAG, "acce_x:%.2f, acce_y:%.2f, acce_z:%.2f\n", acce.acce_x, acce.acce_y, acce.acce_z);

    ret = mpu6050_get_gyro(mpu6050, &gyro);
    TEST_ASSERT_EQUAL(ESP_OK, ret);
    ESP_LOGI(TAG, "gyro_x:%.2f, gyro_y:%.2f, gyro_z:%.2f\n", gyro.gyro_x, gyro.gyro_y, gyro.gyro_z);

    ret = mpu6050_get_temp(mpu6050, &temp);
    TEST_ASSERT_EQUAL(ESP_OK, ret);
    ESP_LOGI(TAG, "t:%.2f \n", temp.temp);

    mpu6050_delete(mpu6050);
    ret = i2c_driver_delete(I2C_MASTER_NUM);
    TEST_ASSERT_EQUAL(ESP_OK, ret);
}

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

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

相关文章

list(链表)

文章目录 功能迭代器的分类sort函数&#xff08;排序&#xff09;merage&#xff08;归并&#xff09;unique(去重&#xff09;removesplice&#xff08;转移&#xff09; 功能 这里没有“[]"的实现&#xff1b;原因&#xff1a;实现较麻烦&#xff1b;这里使用迭代器来实…

vue3基础语法

2020年9月18日发布 2022年2月7日称为默认版本&#xff0c;意味vue3是现在也是未来 Ant Design Pc端组件库 Element Plus Pc端组件库 Vant 移动端 VueUse 基于composition 组合式api的常用函数集合 vue3中文文档&#xff1a;https://cn.vuejs.org/guide/introduction.html…

pandas_datareader读取yahoo金融数据超时问题timeout解决方案

在《Python金融数据挖掘》一书中&#xff0c;学习到网络数据源这一章节&#xff0c;利用书中的方法安装了pandas_datareader包&#xff0c;但在获取雅虎数据&#xff08;get_data_yahoo&#xff09;时会出现以下问题&#xff1a; 经过仔细分析和尝试&#xff0c;排除了yahoo受中…

2023年中国智能电视柜产量、需求量、市场规模及行业价格走势[图]

电视柜是随着电视机的发展和普及而演变出的家具种类&#xff0c;其主要作用是承载电视机&#xff0c;又称视听柜&#xff0c;随着生活水平的提高&#xff0c;与电视机相配套的电器设备也成为电视柜的收纳对象。 随着智能家具的发展&#xff0c;智能电视机柜的造型和风格都是有了…

2023/10/1 -- ARM

今日任务&#xff1a;select实现服务器并发 ser.c&#xff1a; #include <myhead.h>#define ERR_MSG(msg) do{\printf("%d\n",__LINE__);\perror(msg);\ }while(0)#define PORT 8888#define IP "192.168.1.5"int main(int argc, const char *argv[…

Java中DateTimeFormatter的使用方法和案例

&#x1f514;简介 在Java中,DateTimeFormatter类用于格式化和解析日期时间对象。它是日期时间格式化的强大而灵活的工具。 &#x1f514;作用 &#x1f335;1.本地化时间 本地化时间指根据指定的语言环境显示时间 1.1.创建DateTimeFormatter时指定Locale DateTimeFormatt…

ORA-01034: ORACLE not available?一文解决

1.情况描述 oracle用户sqlplus登陆数据库&#xff08;11gR2 单机asm&#xff09;&#xff0c;进去查询一些基本的视图发现报错 ORA-01034: ORACLE not available&#xff0c;详细如下 [oracleoomcserver db_1]$ sqlplus / as sysdba SQL*Plus: Release 11.2.0.4.0 Production…

ClassNotFoundException与NoClassDefFoundError

如果这springboot服务启动时两个报错同时出现&#xff0c;那大概率是依赖间冲突导致的 查资料发现是springcloud的依赖版本和springboot的依赖版本不兼容&#xff0c;顺藤摸瓜找到springcloud jar包中调用org.springframework.boot.context.properties.ConfigurationProperties…

酷开科技OTT大屏营销,做好价值塑造

洞察2023&#xff0c;随着技术与数据入局OTT领域&#xff0c;程序化投放、数据追踪、人群定位等等能力正逐步深入&#xff0c;围绕OTT大屏营销&#xff0c;新营销的价值也正在被重构。随着国内5G、人工智能、云计算等技术不断普及&#xff0c;大屏营销服务成为OTT行业发展的主流…

笔试强训Day9

T1&#xff1a;走方格的方案数 链接&#xff1a;走方格的方案数_牛客题霸_牛客网 (nowcoder.com) 描述 请计算n*m的棋盘格子&#xff08;n为横向的格子数&#xff0c;m为竖向的格子数&#xff09;从棋盘左上角出发沿着边缘线从左上角走到右下角&#xff0c;总共有多少种走法…

SpringCloud Alibaba - Sentinel 微服务保护解决雪崩问题、Hystrix 区别、安装及使用

目录 一、Sentinel 1.1、背景&#xff1a;雪崩问题 1.2、雪崩问题的解决办法 1.2.1、超时处理 缺陷&#xff1a;为什么这里只是 “缓解” 雪崩问题&#xff0c;而不是百分之百解决了雪问题呢&#xff1f; 1.2.2、舱壁模式 缺陷&#xff1a;资源浪费 1.2.3、熔断降级 1.…

【汇编的救赎】汇编语言入门必看基本知识点

汇编语言是一种低级语言&#xff0c;用于编写计算机程序。它直接操作计算机硬件&#xff0c;提供了对处理器寄存器、内存和指令等底层资源的直接访问。汇编语言的语法简单明了&#xff0c;每条指令都对应着一条机器指令&#xff0c;具有高度的灵活性和效率。通过汇编语言&#…

MySQL SQL性能分析(SQL优化 一)

在开发和维护数据库应用程序时&#xff0c;优化SQL查询的性能是至关重要的。MySQL提供了一些强大的工具和技术&#xff0c;帮助我们进行SQL性能分析&#xff0c;找出潜在的瓶颈并进行相应的优化。 查看SQL的执行频率 show [ session| global ] status 命令查看服务器状态信息…

web:[RoarCTF 2019]Easy Calc

题目 进入页面是一个计算器的页面 随便试了一下 查看源代码看看有什么有用的信息 访问一下这个calc.php 进行代码审计 <?php error_reporting(0); if(!isset($_GET[num])){show_source(__FILE__); }else{$str $_GET[num];$blacklist [ , \t, \r, \n,\, ", , \[, \]…

Python语言:函数的使用

按我的理解&#xff0c;编程世界中的函数就是一个模块&#xff1a;提前写好一个特动功能&#xff0c;方便以后直接调用且实现其功能&#xff0c;可以大大提高工作效率。 今天我们通过一个python语言的函数使用小案例来进一步加深对函数的理解。案例名字为S的银行之行。S是一个吝…

1.6.C++项目:仿muduo库实现并发服务器之channel模块的设计

项目完整版在&#xff1a; 文章目录 一、channel模块&#xff1a;事件管理Channel类实现二、提供的功能三、实现思想&#xff08;一&#xff09;功能&#xff08;二&#xff09;意义&#xff08;三&#xff09;功能设计 四、代码&#xff08;一&#xff09;框架&#xff08;二…

Mac卸载不了软件怎么办?2023年最新MacBook卸载大集合

很多朋友在卸载软件的时候&#xff0c;可能会遇到卸载不了的情况。软件正在使用、软件来自第三方等原因都可能导致我们无法将其成功卸载。本篇文章就将告诉您“Mac卸载不了软件”的解决办法。 为什么Mac卸载不了软件&#xff1f; Mac卸载不了软件的原因很多&#xff0c;常见的…

Excel如何快速将表格纵向数据变成横向

第一&#xff0c;打开Excel文件&#xff0c;我们看到表格里面的内容为纵向排列&#xff1b; 第二&#xff0c;要将表格纵向排列变成横向&#xff0c;首先选中单元格内容&#xff0c;按CTRLC快速复制。 第三&#xff0c;选中一个空白单元格&#xff0c;点击鼠标右键&#xff0c;…

SentenceTransformer 之论文解读

摘要 原文标题&#xff1a;Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks 链接&#xff1a;https://arxiv.org/pdf/1908.10084.pdf 尽管Bert和RoBERTa在句子对回归任务上&#xff0c;例如语义文本相似度&#xff08;Semantic Text Similarity&#xff09;…

300元以内最好的开放式耳机是哪款、百元开放式耳机推荐

如果你是一位运动用户&#xff0c;或者耳道比较敏感的用户&#xff0c;耳机一定要选开放式的。开放式耳机的优点是在听歌的能够兼顾环境音&#xff0c;避免户外运动时出现的安全问题。在耳机的佩戴上舒适性上也比入耳式耳机好&#xff0c;不会对耳朵造成压迫导致不适。不足是音…