ESP32IDF — 硬件I2C使用教程

news2024/11/30 0:26:14

前言

(1)最近刚做完ESP32的一个模块的驱动移植,使用到了I2C。感觉ESP32的硬件I2C还是挺容易使用的。
(2)本文将只会介绍ESP32的硬件I2C使用,如果想知道软件I2C使用,可看其他的任意一款芯片软件I2C实现流程,都是一样的东西。
(3)注意,本人只会介绍常用的函数接口。其他的可自行阅读乐鑫官方I2C驱动文档。

ESP32S3的I2C引脚简单介绍

(1)如果玩过多款MCU会发现,一般来说,一款MCU只有指定的引脚支持硬件I2C的。所以,根据惯性思维,我认为要使用ESP32的硬件I2C,就需要先看看datasheet,看看这块芯片的哪些引脚支持硬件I2C。
注:看datasheet的3.10章节。
(2)后面发现如下图,任意GPIO管脚!!!当时我是懵逼的,啥玩意?!任意管脚是啥东西,根据我接触过的这么多款芯片经验来说,还是头一次遇到这种情况。

在这里插入图片描述

(3)后面在交流群里面说了这个发现之后,一位大佬就提出了,ESP32的GPIO 交换矩阵。这个GPIO 交换矩阵能够让所有的引脚实现复用功能,猛的鸭皮。
(4)后面我找到了ESP32S3的技术参考手册,对GPIO交换矩阵感兴趣的可以查看第6章节
(5)因此,外面可以从手册上得知ESP32S3拥有两个I2C,他们支持任意引脚的复用,并且可以当从机,也可以当主机使用。

ESP32当主机的API函数介绍

i2c_param_config()初始化I2C配置

(1)初始化I2C配置

/**
 * @brief   初始化I2C配置	
 *
 * @param   i2c_num    配置的I2C端口
 *         -i2c_conf   指向I2C配置的结构体指针
 *
 * @return  ESP_OK               配置成功
 *         -ESP_ERR_INVALID_ARG  传入参数错误
 */
 esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf);

i2c_driver_install()注册I2C

(1)注册I2C,因为我们这里的ESP32是用于当主机使用,所以最后三个参数都传入0。第二个参数,mode传入I2C_MODE_MASTER

/**
 * @brief   注册I2C	
 *
 * @param   i2c_num          配置的I2C端口
 *         -mode             设置主机模式还是从机模式
 *         -slv_rx_buf_len   接收缓冲区大小,单位字节。只有从机模式才会使用该值,主站模式下该值被忽略,一般写0。
 *         -slv_tx_buf_len   发送缓冲区大小,单位字节。只有从机模式才会使用该值,主站模式下该值被忽略,一般写0。
 *         -intr_alloc_flags 用于分配中断的标志。如果不使用I2C中断功能,写0
 *
 * @return  ESP_OK               注册成功
 *         -ESP_ERR_INVALID_ARG  传入参数错误
 *         -ESP_FAIL             注册失败
 */
 esp_err_t i2c_driver_install(i2c_port_t i2c_num, const i2c_config_t *i2c_conf);

i2c_cmd_link_create()动态创建一个I2C命令缓冲区

(1)使用给定的缓冲区创建并初始化 I2C 命令缓冲区。完成 I2C 事务后,需要调用 i2c_cmd_link_delete() 释放并返回资源。
(2)在ESP32中,如果我们想要使用硬件I2C传输数据,需要先向一个缓冲区写入自己要发送的指令,然后再调用i2c_master_cmd_begin()将这个缓冲区的数据输出。
(3)需要注意的一点是,调用i2c_master_cmd_begin()将这个缓冲区的数据输出之后,i2c_cmd_link_create()创建的命令缓冲区的数据不会被清除,依旧存在,所以需要调用i2c_cmd_link_delete()函数,将这个命令缓冲区数据手动清空。

/**
 * @brief   动态创建一个I2C命令缓冲区
 *
 * @param   无
 *
 * @return  如果成功创建I2C命令缓冲区,返回i2c_cmd_handle_t句柄。否则返回NULL  
 */
i2c_cmd_handle_t i2c_cmd_link_create(void);

i2c_master_start()协议起始信号

(1)根据I2C的协议,每次通讯前,主机需要发送起始信号。I2C总线上的设备才会被唤醒,开始通讯。因此,每次通讯这个函数是必须放在写/读数据函数前面的。

/**
 * @brief   I2C协议的起始信号
 *
 * @param   cmd_handle           传入i2c_cmd_handle_t句柄
 *
 * @return  ESP_OK               起始信号成功写入缓冲区
 *         -ESP_ERR_INVALID_ARG  传入参数错误
 *         -ESP_ERR_NO_MEM       cmd_handle静态缓冲区太小。如果是调用的i2c_cmd_link_create()动态创建的I2C命令缓冲区,这个不用管。
 *         -ESP_FAIL             堆上不再有内存
 */
esp_err_t i2c_master_start(i2c_cmd_handle_t cmd_handle);

i2c_master_write_byte()写一个字节数据

(1)这个函数必须是在i2c_master_start()之后调用,他就是向I2C命令缓冲区写入一个uint8_t 的数据。
(2)因为I2C的协议要求,每次从机设备接受到主机信号都会发送一个ACK回应。所以主机可以根据这个ACK回应来判断从机是否收到了信号,但是主机可以不判断ACK回应。
<1>因此,第三个参数ack_en传入true表示启用ACK回应,只有主机收到ACK回应才会继续发送下一个数据。个人建议还是启用ACK回应。
<2>但是如果第三个参数ack_en传入false不启用ACK回应。那么主机就会一股脑的传输数据,不管你从机是否真正收到了信号。

/**
 * @brief   I2C协议的起始信号
 *
 * @param   cmd_handle           传入i2c_cmd_handle_t句柄
 *         -data                 端口上要发送的字节
 *         -ack_en               启用 ACK 信号
 *
 * @return  ESP_OK               成功写入缓冲区
 *         -ESP_ERR_INVALID_ARG  传入参数错误
 *         -ESP_ERR_NO_MEM       创建的I2C命令缓冲区静态缓冲区太小。如果是调用的i2c_cmd_link_create()动态创建的I2C命令缓冲区,这个不用管。
 *         -ESP_FAIL             堆上不再有内存
 */
esp_err_t i2c_master_write_byte(i2c_cmd_handle_t cmd_handle, uint8_t data, bool ack_en);

i2c_master_write()写任意字节数据

(1)这个函数就是对i2c_master_write_byte()进行了再一次的封装,唯一需要注意的是,多了一个传入参数data_len。他需要知道要写入的数据有多少个。

/**
 * @brief   I2C协议的起始信号
 *
 * @param   cmd_handle           传入i2c_cmd_handle_t句柄
 *         -data                 端口上要发送的字节
 *         -data_len             发送的数据长度
 *         -ack_en               启用 ACK 信号
 *
 * @return  ESP_OK               成功写入缓冲区
 *         -ESP_ERR_INVALID_ARG  传入参数错误
 *         -ESP_ERR_NO_MEM       创建的I2C命令缓冲区静态缓冲区太小。如果是调用的i2c_cmd_link_create()动态创建的I2C命令缓冲区,这个不用管。
 *         -ESP_FAIL             堆上不再有内存
 */
esp_err_t i2c_master_write(i2c_cmd_handle_t cmd_handle, const uint8_t *data, size_t data_len, bool ack_en);

i2c_master_read_byte()读一个字节数据

(1)这里就是传入一个1字节大小的内存data,最后读取到的数据会自动存入data中。
注意:data必须是uint8_t *类型指针!
(2)关于ACK 信号如何配置。
<1>因为I2C协议规定了,如果主机想读取从机信号,每次读取到了数据都需要返回一个ACK回应,告诉从机我收到信号了,你继续发。
<2>但是,主机有时候只想读取3个字节数据,有时候又只想读取2个字节数据。从机是无法知道主机要读取多少个数据的,因此,当主机不想读取数据的时候,主机需要发送一个NACK回应给从机,我读取到完数据了,你可以休息了。
<3>所以从上述分析即可知道,如果是主机读取数据,第三个参数ack应该传入的是I2C_MASTER_LAST_NACK

/**
 * @brief   I2C协议的起始信号
 *
 * @param   cmd_handle           传入i2c_cmd_handle_t句柄
 *         -data                 端口上要读取的字节
 *         -ack                  ACK 信号
 *
 * @return  ESP_OK               成功写入缓冲区
 *         -ESP_ERR_INVALID_ARG  传入参数错误
 *         -ESP_ERR_NO_MEM       创建的I2C命令缓冲区静态缓冲区太小。如果是调用的i2c_cmd_link_create()动态创建的I2C命令缓冲区,这个不用管。
 *         -ESP_FAIL             堆上不再有内存
 */
esp_err_t i2c_master_read_byte(i2c_cmd_handle_t cmd_handle, uint8_t *data, i2c_ack_type_t ack);

i2c_master_read()读任意字节数据

(1)这里就是传入一个data_len字节大小的内存data,最后读取到的数据会自动存入data中。
注意:data必须是uint8_t *类型指针!

/**
 * @brief   I2C协议的起始信号
 *
 * @param   cmd_handle           传入i2c_cmd_handle_t句柄
 *         -data                 端口上要读取的字节
 *         -data_len             要读取的数据长度,单位是字节
 *         -ack                  ACK 信号
 *
 * @return  ESP_OK               成功写入缓冲区
 *         -ESP_ERR_INVALID_ARG  传入参数错误
 *         -ESP_ERR_NO_MEM       创建的I2C命令缓冲区静态缓冲区太小。如果是调用的i2c_cmd_link_create()动态创建的I2C命令缓冲区,这个不用管。
 *         -ESP_FAIL             堆上不再有内存
 */
esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t *data, size_t data_len, i2c_ack_type_t ack);

i2c_master_stop()协议停止信号

(1)根据I2C的协议,每次通讯结束,主机需要发送停止信号。I2C总线上的设备就会从工作状态进入休眠,停止通讯。因此,每次这个函数是必须放在写/读数据函数后面的。

/**
 * @brief   I2C协议的停止信号
 *
 * @param   cmd_handle           传入i2c_cmd_handle_t句柄
 *
 * @return  ESP_OK               起始信号成功写入缓冲区
 *         -ESP_ERR_INVALID_ARG  传入参数错误
 *         -ESP_ERR_NO_MEM       cmd_handle静态缓冲区太小。如果是调用的i2c_cmd_link_create()动态创建的I2C命令缓冲区,这个不用管。
 *         -ESP_FAIL             堆上不再有内存
 */
esp_err_t i2c_master_start(i2c_cmd_handle_t cmd_handle);

i2c_master_cmd_begin()触发 I2C 控制器发送命令缓冲区

(1)我们调用了上面这么多函数之后,真正的硬件I2C并没有开始做任何工作。只有使用i2c_master_cmd_begin()这个函数之后,硬件I2C才会开始工作,并且将缓冲区的数据输出到总线上。
(2)需要注意的是,ticks_to_wait作为超时等待时间是什么意思,这个可能会有同学不明白,我简单提一下。
<1>我们上面说了,无论是主机或者从机,收到信号之后都会发送一个ACK或者NACK的回应信号。如果设备没有收到回应信号,那么就会进入等待状态。
<2>那么假设因为什么原因,从机一直不发送回应信号,难道我主机就一直死等着吗?显然是不合理的,所以第三个参数需要设置一个等待的最长时间。
<3>因为ESP-IDF 默认的 FreeRTOS 实现,所以第三个参数需要使用到pdMS_TO_TICKS()这个宏,将毫秒为单位表示的时间转换为FreeRTOS中的时钟滴答数(ticks)。例如第三个参数传入pdMS_TO_TICKS(10)表示超时等待时间为10ms。
(3)再次强调i2c_master_cmd_begin()这个函数并不会清空命令缓冲区的数据。

/**
 * @brief   触发 I2C 控制器发送命令缓冲区
 *
 * @param   i2c_num              进行数据传输的I2C端口
 *         -cmd_handle           传入i2c_cmd_handle_t句柄
 *         -ticks_to_wait        超时前的最大等待时间
 *
 * @return  ESP_OK                成功写入缓冲区
 *         -ESP_ERR_INVALID_ARG   传入参数错误
 *         -ESP_FAIL              发送命令错误,从属设备未返回ACK信号
 *         -ESP_ERR_INVALID_STATE 未安装 I2C 驱动程序或未处于主模式
 *         -ESP_ERR_TIMEOUT       由于总线繁忙,操作超时
 */
esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, TickType_t ticks_to_wait);

i2c_cmd_link_delete()释放I2C命令缓冲区使用的资源

(1)这个函数有两个调用的可能:
<1>这次I2C通讯结束
<2>利用I2C和某些模块通讯,他们会返回一个status,我们需要根据status进行后续的操作。因为我上述说了i2c_master_cmd_begin()这个函数并不会清空命令缓冲区的数据,所以需要调用i2c_cmd_link_delete()清除缓冲区,再调用i2c_cmd_link_create()重新创建一个缓冲区。

/**
 * @brief   释放I2C命令缓冲区使用的资源
 *
 * @param   cmd_handle           传入i2c_cmd_handle_t句柄
 *
 * @return  无
 */
void i2c_cmd_link_delete(i2c_cmd_handle_t cmd_handle);

ESP32主机示例

(1)如下为一个简单的I2C主机测试流程,因为代码太长了,所以只截取了部分。想知道更多的使用,可查看ESP-IDF的I2C部分例程。

/**
 * @brief   I2C0的主机初始化程序
 *
 * @param   i2c_sda1           I2C0的SDA引脚
 *         -i2c_scl1           I2C0的SCL引脚
 *
 * @return  无
 */
void i2c_bus_0_master_init(int i2c_sda1, int i2c_scl1)
{
    i2c_config_t conf;
    conf.mode = I2C_MODE_MASTER;                  
    conf.sda_io_num = (gpio_num_t)i2c_sda1;  
    conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
    conf.scl_io_num = (gpio_num_t)i2c_scl1;
    conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
    conf.master.clk_speed = 100000;
    conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL;

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

    ret = i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0);
    TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C0 install returned error");
	ESP_LOGI(TAG, "i2c_0_master_init OK");
}

/*=================== 一次I2C通讯流程 ==========================*/
typedef struct
{
	i2c_port_t bus;
	uint8_t dev_addr;
	uint8_t cmd_init[3];
	uint8_t trigger_buff[3];
	uint8_t status;
	uint8_t readbuff[6];	
}aht20_dev_t;
i2c_cmd_handle_t cmd;

cmd = i2c_cmd_link_create();
vTaskDelay(pdMS_TO_TICKS(40));
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, &sens->status, 1, 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, pdMS_TO_TICKS(10));
i2c_cmd_link_delete(cmd);

ESP32当从机的API函数介绍

i2c_param_config()初始化I2C配置

(1)初始化I2C配置。
<1>和ESP32当主机是使用的相同的API函数,不过区别在于初始化i2c_config_t结构体时候,mode参数传入的是I2C_MODE_SLAVE
<2>同时,这里需要设置ESP32当从机时候的地址信息。从机有两种地址信息,一种是7bit,一种是10bit。slave_addr配置7bit的地址,addr_10bit_en配置10bit的地址。一般都是使用的7bit的地址,所以addr_10bit_en=0即可。ESP32的从机7bit地址可以为任意值。

/**
 * @brief   初始化I2C配置	
 *
 * @param   i2c_num    配置的I2C端口
 *         -i2c_conf   指向I2C配置的结构体指针
 *
 * @return  ESP_OK               配置成功
 *         -ESP_ERR_INVALID_ARG  传入参数错误
 */
 esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t *i2c_conf);

i2c_driver_install()注册I2C

(1)因为是从机,所以mode需要配置为I2C_MODE_SLAVE
(2)主机的slv_rx_buf_lenslv_tx_buf_len是不需要配置的,但是如果是从机,你就需要根据需求配置缓冲区大小。如果不知道为多少,一般128即可。

/**
 * @brief   注册I2C	
 *
 * @param   i2c_num          配置的I2C端口
 *         -mode             设置主机模式还是从机模式
 *         -slv_rx_buf_len   接收缓冲区大小,单位字节。只有从机模式才会使用该值,主站模式下该值被忽略,一般写0
 *         -slv_tx_buf_len   发送缓冲区大小,单位字节。只有从机模式才会使用该值,主站模式下该值被忽略,一般写0
 *         -intr_alloc_flags 用于分配中断的标志。如果不使用I2C中断功能,写0
 *
 * @return  ESP_OK               注册成功
 *         -ESP_ERR_INVALID_ARG  传入参数错误
 *         -ESP_FAIL             注册失败
 */
 esp_err_t i2c_driver_install(i2c_port_t i2c_num, const i2c_config_t *i2c_conf);

i2c_slave_read_buffer()读取主机数据

(1)读取主机数据

/**
 * @brief   读取主机数据
 *
 * @param   i2c_num          I2C端口
 *         -data             存放读取到主机信号的缓冲区
 *         -max_size         读取的最大字节数
 *         -ticks_to_wait    最大等待时间
 *
 * @return  ESP_FAIL(-1)     传入参数错误
 *         -Others(>=0)      从 I2C 从站缓冲区读取的数据字节数
 */
int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t *data, size_t max_size, TickType_t ticks_to_wait);

i2c_slave_write_buffer()向主机写入数据

(1)向主机写入数据

/**
 * @brief   向主机写入数据
 *
 * @param   i2c_num          I2C端口
 *         -data             向主机写入数据的缓冲区
 *         -max_size         写入数据的字节数
 *         -ticks_to_wait    最大等待时间
 *
 * @return  ESP_FAIL(-1)     传入参数错误
 *         -Others(>=0)      推送到 I2C 从缓冲区的数据字节数
 */
int i2c_slave_write_buffer(i2c_port_t i2c_num, const uint8_t *data, int size, TickType_t ticks_to_wait);

ESP32从机示例

(1)注意:ESP32C2没有从机功能。
(2)从机初始化时候,接受和发送缓冲区是实际数据长度的2倍,这个设置是为了在I2C从机模式下提供足够的缓冲区空间,以存储来自主机的数据。

/*=================== ESP32从机初始化 ==========================*/
static esp_err_t i2c_slave_init(void)
{
    int i2c_slave_port = I2C_NUM_0;
    i2c_config_t conf_slave = {
        .sda_io_num = GPIO_NUM_5,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_io_num = GPIO_NUM_4,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .mode = I2C_MODE_SLAVE,
        .slave.addr_10bit_en = 0,
        .slave.slave_addr = 0x28,
    };
    esp_err_t err = i2c_param_config(i2c_slave_port, &conf_slave);
    if (err != ESP_OK) {
        return err;
    }
    return i2c_driver_install(i2c_slave_port, conf_slave.mode, 2*128, 2*128, 0);
}
/*=================== ESP32向主机写入数据 ==========================*/
uint8_t *data = (uint8_t *)malloc(128);
size_t d_size = i2c_slave_write_buffer(I2C_NUM_0, data, 128, pdMS_TO_TICKS(10));
/*=================== ESP32向主机读取数据 ==========================*/
size = i2c_slave_read_buffer(I2C_NUM_0, data, 128, pdMS_TO_TICKS(10));

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

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

相关文章

双指针算法——移动零

双指针算法——移动零&#x1f60e; 前言&#x1f64c;题目详情&#xff1a;图解分析&#xff1a;代码分享&#xff1a;B站讲解视频链接&#xff1a; 总结撒花&#x1f49e; &#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神贯注的上吧…

数据集笔记:华盛顿共享单车OD数据

2010~2022 共享单车OD数据 数据地址&#xff1a;Index of bucket "capitalbikeshare-data"

STM32F4X UCOSIII任务消息队列

STM32F4X UCOSIII任务消息队列 任务消息队列和内核消息队列对比内核消息队列内核消息队列 UCOSIII任务消息队列API任务消息队列发送函数任务消息队列接收函数 UCOSIII任务消息队列例程 之前的章节中讲解过消息队列这个机制&#xff0c;UCOSIII除了有内核消息队列之外&#xff0…

【数据结构】队列实现+层序遍历详解+一些练题

欢迎来到我的&#xff1a;世界 希望作者的文章对你有所帮助&#xff0c;有不足的地方还请指正&#xff0c;大家一起学习交流 ! 目录 前言队列的实现层序遍历详解强化练习1.判断是不是完全二叉树求二叉树的最大深度 总结 前言 国庆到了&#xff0c;也要内卷一下&#xff0c;感…

集合-List集合

系列文章目录 1.集合-Collection-CSDN博客​​​​​​ 2.集合-List集合-CSDN博客 文章目录 目录 系列文章目录 文章目录 前言 一 . 什么是List? 二 . List集合的特点 三 . 常用方法 1.void add(int index, E element): 将指定的元素插入到列表的指定位置。 2.E remove(int in…

1.4.C++项目:仿mudou库实现并发服务器之buffer模块的设计

一、buffer模块&#xff1a; 缓冲区模块 Buffer模块是一个缓冲区模块&#xff0c;用于实现通信中用户态的接收缓冲区和发送缓冲区功能。 二、提供的功能 存储数据&#xff0c;取出数据 三、实现思想 1.实现换出去得有一块内存空间&#xff0c;采用vector ,vector底层是一个…

opencv图像数组坐标系

在OpenCV的Python接口&#xff08;cv2&#xff09;中&#xff0c;加载的图像数组遵循以下坐标系和方向约定&#xff1a; 1. **坐标系&#xff1a;** OpenCV的坐标系遵循数学中的坐标系&#xff0c;原点&#xff08;0, 0&#xff09;位于图像的左上角。横轴&#xff08;X轴&…

算法通过村第十一关-位运算|青铜笔记|初始位运算

文章目录 前言1. 数字在计算中的表示拓展&#xff1a;为什么要有原码、反码和补码? 2. 位运算规则2.1 与、或、异或和取反2.2 位移运算2.3 位移运算和乘除的关系2.4 位运算的常用技巧 总结 前言 提示&#xff1a;我的父亲从我出生起便认识我&#xff0c;可他对我的了解却那么少…

自定义Unity组件——AudioManager(音频管理器)

需求描述 在游戏开发中&#xff0c;音频资源是不可或缺的&#xff0c;通常情况下音频资源随机分布&#xff0c;各个音频的操作和管理都是各自负责&#xff0c;同时对于音频的很多操作逻辑都是大同小异的&#xff0c;这就造成了许多冗余代码的堆叠&#xff0c;除此之外在获取各类…

ESP32S3的MPU-6050组件移植教程

前言 &#xff08;1&#xff09;实习公司要搞ESP32BOX的驱动移植&#xff0c;所有资料自己找还是比较折磨人的现在我分享几个官方的组件移植资料&#xff1a; <1>Find the most exciting ESP-IDF components&#xff08;ESP32的官方组件都可以在里面查&#xff0c;按照他…

【李沐深度学习笔记】损失函数

课程地址和说明 损失函数p2 本系列文章是我学习李沐老师深度学习系列课程的学习笔记&#xff0c;可能会对李沐老师上课没讲到的进行补充。 损失函数 损失函数是用来衡量预测值 y ^ \hat{y} y^​或 y ′ y y′与真实值 y y y的差别&#xff0c;下面给出常见的损失函数类型&am…

9.30号作业

1.消息队列实现进程间的通信 服务端 #include <myhead.h>#define ERR_MSG(msg) do{\fprintf(stderr,"__%d__:",__LINE__);\perror(msg);\ }while(0)typedef struct{ long msgtype; //消息类型char data[1024]; //消息正文 }Msg;#define SIZE sizeof(Msg)-s…

网站被上传webshell

1。原因 2.工具使用 3.步骤

1038 统计同成绩学生

输入样例&#xff1a; 10 60 75 90 55 75 99 82 90 75 50 3 75 90 88 输出样例&#xff1a; 3 2 0 solution #include <stdio.h> int main(){int n, d, k, hash[101] {0}, a[100000];scanf("%d", &n);for(int i 0; i < n; i){scanf("%d&quo…

SpringCloud网关服务

为什么需要网关 官网&#xff1a; https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.3.RELEASE/single/spring-cloud-gateway.html 使用 导入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>s…

第7讲:v-bind属性绑定,v-model双向绑定,v-on事件监听使用

目录 1.v-bind双向绑定 2.v-model双向绑定 3.v-on事件绑定 一.v-bind双向绑定 1.v-bind 指令可以在其名称后面带一个参数&#xff0c;中间放一个冒号隔开&#xff0c;这个参数通常是HTML元素的特性&#xff08;attribute&#xff09;&#xff0c;Vue官方提供了一个简写方式…

阿里云ACP知识点(三)

1、弹性伸缩不仅提供了在业务需求高峰或低谷时自动调节ECS实例数量的能力&#xff0c;而且提供了ECS实例上自动部署应用的能力。弹性伸缩的伸缩配置支持多种特性&#xff0c;例如______,帮助您高效、灵活地自定义ECS实例配置&#xff0c;满足业务需求。 标签、密钥对、 实例RAM…

侯捷 C++ STL标准库和泛型编程 —— 3 容器(关联式容器)

3.3 关联式容器 3.3.0 RB-Tree 红黑树&#xff08;Red-Black Tree&#xff09;是一种自平衡的二叉搜索树 BST&#xff08;AVL 是另一种&#xff09; rb-tree 提供遍历操作和 iterators&#xff0c;按中序遍历遍历&#xff0c;便可以得到排序状态 不能用 iterator 去改变元素的…

CCC标准——PHY

1.介绍 UWB物理层使用基于频带受限脉冲的脉冲无线电信号波形。UWB物理层主要用于测距&#xff0c;但也可以用于数据通信。在CCC标准中&#xff0c;物理层的具体定义依然基于IEEE 802.15.4z标准中的HRP UWB PHY&#xff0c;支持更高的脉冲重复频率。 对于增强测距设备&#xf…

华为云云耀云服务器L实例评测|云耀云服务器L实例搭建个人镜像站

华为云云耀云服务器L实例评测&#xff5c;云耀云服务器L实例搭建个人镜像站 一、云耀云服务器L实例介绍1.1 云耀云服务器L实例简介1.2 云耀云服务器L实例特点 二、Apache介绍2.1 Apache简介2.2 Apache特点 三、本次实践介绍3.1 本次实践简介3.2 本次环境规划 四、远程登录华为云…