ESP32学习笔记_Peripherals(2)——i2C

【TM1650】STM8S TM1650芯片控制程序 以下为.h文件: #ifndef __TM1650_I2C_H #define __TM1650_I2C_H #include "stm8s.h" #include "stm8s_gpio.h" #include "tim1.h" #include "usart2.h" #include <iostm8s103f3.h> #include <intrinsics.... 阅读详情
AI摘要

该博客围绕ESP32的I2C通信展开,介绍了I2C协议基础知识、ESP32硬件支持及API使用方法。涵盖通信原理、总线特性、上拉电阻选择等内容,对I2C主从模式的安装、卸载、数据读写进行说明,还给出ADXL345加速度传感器案例代码。

I2C

简介(from AI):
这篇博客以ESP32的I2C通信为核心,深入讲解了I2C协议基础知识、ESP32硬件支持及其API使用方法,适合嵌入式开发者快速上手I2C编程。文章逻辑清晰,从理论到实践层层展开,涵盖了I2C通信原理、总线特性、上拉电阻的选择等基础内容,并结合ESP-IDF的API对I2C主从模式的安装、卸载、数据读写进行了详尽说明。通过表格和代码示例,详细展示了API参数和返回值的用法,代码部分以ADXL345加速度传感器为案例,提供了完整的初始化和数据读取流程,注释详尽,便于实操应用。博文不仅技术内容全面,还注重实际工程需求,如线程安全、功耗优化等问题的讨论,贴合嵌入式开发场景。

前言:本文档是本人在依照乐鑫科技编写的ESP32 API文档进行学习时所做的学习笔记,可能存在疏漏和错误,如有发现,敬请指正。

参考资料:
I2C 接口 - ESP32 - — ESP-IDF 编程指南 latest 文档


I2C(Inter-Integrated Circuit)是一种串行同步半双工通信协议,I2C总线上可以同时挂载多个主机和从机。I2C 使用两条双向开漏线:串行数据线 (SDA) 和串行时钟线 (SCL),通过电阻上拉。通常,I2C 从机设备具有 7 位地址或 10 位地址

总线是连接计算机或嵌入式系统中各个组件之间的通信通道,用于在设备之间传输数据、地址和控制信号。它是一种共享资源,可以同时连接多个设备,按一定的协议进行数据交换。根据功能和用途,总线可以分为 数据总线(传输数据)、地址总线(指定存储位置)和 控制总线(传递控制信号)。常见的总线类型包括并行总线(如 PCI)和串行总线(如 I²C、SPI、UART),它们广泛用于芯片间通信、外设控制和数据传输

SCL(串行时钟线) 由主设备生成时钟信号
SDA(串行数据线) 用于传输数据,支持双向通信

通信角色

  • 支持一个主设备(Master)控制多个从设备(Slave)
  • 也支持多个主设备,但需要处理总线仲裁

总线仲裁是指在多主设备的通信系统中,当多个主设备同时尝试控制总线时,通过某种机制决定哪一个设备可以优先使用总线的过程

特性主设备(Master)从设备(Slave)
角色发起通信并控制总线响应主设备并传输数据
时钟生成生成 SCL 信号使用主设备的时钟
通信发起发送起始条件,选择从设备被动等待主设备选择
地址管理指定从设备的地址每个从设备有唯一地址
多设备支持支持多个主设备(多主模式)支持多个从设备

通信过程

  • 主设备启动通信: 主设备拉低 SDA,并开始时钟信号(SCL)
  • 发送从设备地址: 主设备发送 7 位或 10 位地址,并附加读/写位
  • 从设备应答: 目标从设备检测到地址匹配后,发送 ACK(低电平)
  • 数据传输:
    • 主设备发送或接收数据字节
    • 主/从设备在每个字节后发送 ACK
  • 停止通信: 主设备释放 SDA 和 SCL,总线恢复空闲状态

在 I2C协议中,地址是用来标识从机设备的唯一标识符。当主机(Master)与从机(Slave)通信时,需要通过这个地址选择要与之通信的从机设备

ESP32 有 2 个 I2C 控制器(也被称为端口),负责处理 I2C 总线上的通信。单个 I2C 控制器既可以是主机也可以是从机
ESP32 支持 I2C 标准模式 (Sm) 和快速模式 (Fm),可分别达到 100 kHz 和 400 kHz(主机模式的 SCL 时钟频率不应大于 400 kHz)

SCL 的频率受上拉电阻及导线电容的影响。因此,强烈建议选择适当的上拉电阻,确保频率准确。推荐的上拉电阻值通常在 1 kΩ 到 10 kΩ 之间
SCL 的频率越高,上拉电阻应该越小(但不能小于 1 kΩ)。较大的电阻会降低电流,增加时钟切换时间并降低频率。通常推荐 2 kΩ 到 5 kΩ 左右的电阻,也可根据电流需求进行一定调整

上拉电阻是一种连接在信号线和电源正极之间的电阻,用于将信号线默认保持在逻辑高电平状态,防止信号悬空导致不稳定。它广泛应用于数字电路和通信总线(如 I²C),起到稳定信号、电平匹配和设定默认逻辑状态的作用。阻值通常在 1 kΩ 至 10 kΩ 之间,选择时需权衡功耗和信号响应速度
当有低电平输入时,信号线会从高电平被拉低到低电平,直到低电平输入解除,信号线才会恢复到高电平。上拉电阻在这个过程中不会干扰低电平输入的有效性,只在信号线空闲时提供默认高电平状态

I2C 驱动程序提供以下服务:

  • 资源分配 包括如何使用正确的配置来分配 I2C 总线,以及如何在完成工作后回收资源
  • I2C 主机控制器 包括 I2C 主机控制器的行为,介绍了数据发送、数据接收和数据的双向传输
  • I2C 从机控制器包括 I2C 从机控制器的行为,涉及数据发送和数据接收
  • 电源管理 描述了不同时钟源对功耗的影响
  • IRAM 安全 描述了如何在 cache 被禁用时正常运行 I2C 中断
  • 线程安全 列出了驱动程序中线程安全的 API
  • Kconfig 选项 列出了支持的 Kconfig 选项,这些选项可以对驱动程序产生不同影响

在嵌入式开发中,IRAM 安全(IRAM-Safe) 通常是指某些函数或代码段被设计为在 IRAM(Internal RAM, 内部 RAM) 中运行,以确保在系统关键时刻(如中断或低功耗模式)仍然可用
线程安全(Thread-Safe) 是指某些代码或函数在多线程环境下能够正确运行,不会因并发访问共享资源而导致数据错误或异常行为。在 I²C 通信中,线程安全是非常重要的,尤其是在多任务操作系统(如 FreeRTOS)中,多个线程可能同时尝试访问 I²C 总线
Kconfig选项 是一种用于配置嵌入式系统(例如 Linux 内核、Zephyr、ESP-IDF 等)的配置机制。它允许开发者通过层级化的菜单选项为系统功能和模块进行配置。Kconfig 文件定义了这些配置选项的规则、依赖关系和默认值

Headers that need to be included in the I2C application

  • i2c_master.h 用于使用主机模式的新驱动程序的应用
  • i2c_slave.h 用于使从机模式的新驱动程序的应用

上述头文件中包含的公共头文件

  • i2c_types_legacy.h仅在旧驱动程序中使用的旧公共类型
  • i2c_types.h提供公共类型的头文件

Install/Uninstall master bus and device

Install I2C master bus and device

I2C 主机总线是基于总线-设备模型设计的,因此需要分别使用 i2c_master_bus_config_t 和 i2c_device_config_t 来分配 I2C 主机总线实例和 I2C 设备实例

i2c_new_master_bus()

分配和初始化 I2C 总线

esp_err_t i2c_new_master_bus(const i2c_master_bus_config_t *bus_config,
       i2c_master_bus_handle_t *ret_bus_handle)

参数
bus_config I2C 主机总线配置
ret_bus_handle I2C 总线句柄(传入地址,由函数写入)
bus_config 定义

// 示例
i2c_master_bus_config_t i2c_mst_config = {
    .clk_source = I2C_CLK_SRC_DEFAULT,
    .i2c_port = TEST_I2C_PORT,
    .scl_io_num = I2C_MASTER_SCL_IO,
    .sda_io_num = I2C_MASTER_SDA_IO,
    .glitch_ignore_cnt = 7,
    .flags.enable_internal_pullup = true,
};

clk_source I2C主总线的时钟源
i2c_port I2C端口号,-1 表示自动选择(不包括低功耗I2C实例)
scl_io_num I2C SCL信号的GPIO编号,内部支持上拉
sda_io_num I2C SDA信号的GPIO编号,内部支持上拉
glitch_ignore_cnt 设定线上的干扰周期过滤值,小于该值的干扰将被忽略,典型值为7(单位:I2C模块时钟周期)
flags.enable_internal_pullup 启用内部上拉(内部上拉强度不足以支持高频传输,推荐使用外部合适的上拉电阻)
intr_priority I2C中断优先级,若设置为0,则驱动程序使用默认优先级(通常为1、2或3)
trans_queue_depth 内部传输队列深度。值越大,支持的后台挂起传输越多,仅适用于异步传输,建议设置为 max_device_num per_transaction
allow_pd 配置驱动程序是否允许系统在睡眠模式下关闭外设电源。在进入睡眠之前,系统将备份 I2C 寄存器上下文,当系统退出睡眠模式时,这些上下文将被恢复。关闭外设可以节省更多功耗,但代价是消耗更多内存来保存寄存器上下文。你需要在功耗和内存消耗之间做权衡。此配置选项依赖于特定的硬件功能,如果在不支持的芯片上启用它,你将看到类似 not able to power down in light sleep 的错误消息

端口宏定义

#define I2C_NUM_0  (0)  // I2C 端口 0
#define I2C_NUM_1  (1)  // I2C 端口 1

时钟配置

i2c_clock_source_t::I2C_CLK_SRC_DEFAULT 默认的 I2C 时钟源
i2c_clock_source_t::I2C_CLK_SRC_APB 以 APB 时钟作为 I2C 时钟源

I2C_CLK_SRC_DEFAULT 是一种由硬件或驱动自动选择的默认时钟源模式,其具体时钟来源因平台而异,通常为内部模块的时钟。该模式适合对时钟精度和性能要求不高的应用场景,开发者无需关注具体的时钟配置,适用于低速或普通速率的通信(如 100kHz 或 400kHz)。这种模式具有灵活性,并能在大多数情况下提供足够的性能,同时优化功耗
I2C_CLK_SRC_APB 模式明确使用 APB(高级外设总线)时钟作为 I2C 的时钟源,提供高频率和稳定的时钟信号,通常与 CPU 的主频相关。这种模式适合对通信速率和时钟精度有较高要求的应用,例如高速 I2C(1MHz 或更高)或需要稳定传输的场景。尽管功耗相对较高,但它在高性能和时间敏感的任务中表现优异

返回值
ESP_OK I2C 主机总线初始化成功
ESP_ERR_INVALID_ARG I2C 总线初始化失败,因为参数无效
ESP_ERR_NO_MEM 由于内存不足,创建 I2C 总线失败
ESP_ERR_NOT_FOUND 没有可用的 I2C 端口

i2c_master_bus_add_device()

分配 I2C 设备实例,并将设备挂载到主机总线上

分成bus和device能够更好的管理总线。比如一个总线上挂在的设备有不同的地址和传输速率,在这种框架下只要对device操作就可以了。如果没有device的话,要频繁去改变bus的参数,很麻烦

esp_err_t i2c_master_bus_add_device(i2c_master_bus_handle_t bus_handle,
									const i2c_device_config_t *dev_config,
        							 i2c_master_dev_handle_t *ret_handle)

参数
bus_handle I2C 总线句柄
dev_config 设备配置
ret_handle 设备句柄(传入地址,由函数写入)
dev_config 定义

// 示例
i2c_device_config_t dev_cfg = {
    .dev_addr_length = I2C_ADDR_BIT_LEN_7,
    .device_address = 0x58,
    .scl_speed_hz = 100000,
};

dev_addr_length 从设备的地址长度
device_address I2C 设备原始地址
scl_speed_hz SCL 线路频率
scl_wait_us 超时时间(单位:微秒),该值不能过小,否则可能无法正确处理时钟拉伸/干扰,设为0表示使用默认值(默认值为 0,可不自己设置)
disable_ack_check 禁用ACK检查。如果设置为 false,表示启用ACK检查,当检测到NACK时,传输将停止并返回错误(默认值为 false)
返回值
ESP_OK 成功创建 I2C 主设备
ESP_ERR_INVALID_ARG I2C 总线初始化失败,因为参数无效
ESP_ERR_NO_MEM 由于内存不足,创建 I2C 总线失败

i2c_master_get_bus_handle()

该函数用于获取指定 I2C 端口号对应的主机总线句柄。前提是该端口的句柄已经初始化,函数仅返回已存在的句柄,且该句柄不能并发使用(适用于另一个模块中不方便获取该句柄时)

esp_err_t i2c_master_get_bus_handle(i2c_port_num_t port_num,
         i2c_master_bus_handle_t *ret_handle)

参数
port_num I2C端口号,用于指定需要获取句柄的I2C端口
ret_handle 指向一个变量的指针,用于存储检索到的句柄(传入地址,由函数写入)
返回值
ESP_OK 成功,句柄已成功检索
ESP_ERR_INVALID_ARG 参数无效,例如提供了非法的端口号
ESP_ERR_INVALID_STATE 状态无效,例如I2C端口尚未初始化

Uninstall I2C master bus and device

如果不再需要之前安装的 I2C 总线或设备,调用 i2c_master_bus_rm_device() 或 i2c_del_master_bus() 来回收资源,以释放底层硬件

i2c_master_bus_rm_device()

从 I2C 主总线上移除指定的设备

esp_err_t i2c_master_bus_rm_device(i2c_master_dev_handle_t handle)

参数
handle I2C设备句柄,需要删除的目标设备句柄
返回值
ESP_OK 设备成功被删除

i2c_del_master_bus()

反初始化 I2C 主总线,并删除对应的总线句柄

esp_err_t i2c_del_master_bus(i2c_master_bus_handle_t bus_handle)

参数
bus_handle I2C 总线句柄,需要删除的目标总线句柄
返回值
ESP_OK 总线成功被删除
其它 表示某些模块删除失败

Install I2C slave device

i2c_new_slave_device()

初始化一个 I2C 从设备,并返回对应的设备句柄

esp_err_t i2c_new_slave_device(const i2c_slave_config_t *slave_config,
          i2c_slave_dev_handle_t *ret_handle)

参数
slave_config 设备配置
ret_handle 设备句柄(传入地址,由函数写入)
slave_config 定义

// 示例
i2c_slave_config_t i2c_slv_config = {
    .i2c_port = I2C_SLAVE_NUM,
    .clk_source = I2C_CLK_SRC_DEFAULT,
    .scl_io_num = I2C_SLAVE_SCL_IO,
    .sda_io_num = I2C_SLAVE_SDA_IO,
    .slave_addr = ESP_SLAVE_ADDR,
    .send_buf_depth = 100,
    .receive_buf_depth = 100,
};

i2c_port 端口号,-1 表示自动选择端口
clk_source 总线的时钟源
slc_io_num 总线 SCL 信号的 GPIO 引脚编号
sda_io_num 总线 SDA 信号的 GPIO 引脚编号
slave_addr 从设备地址
send_buf_depth 从设备地址的位长度(通常为7位或10位)
receive_buf_depth 内部接收环形缓冲区的深度
这个参数有点奇怪,示例里面给了,但是文档中 i2c_slave_config_t 的解析里面和 “i2c_slave.h” 的定义又没有,从https://github.com/espressif/esp-idf/blob/master/components/esp_driver_i2c/include/driver/i2c_slave.h#L146 可以看出是文档疏漏
addr_bit_len 从设备地址的位长度(通常为7位或10位)
intr_priority 内部传输环形缓冲区的深度,值越大可支持更多后台挂起的传输
allow_pd 如果设置为 1,在进入/退出睡眠模式前后,驱动会备份/恢复I2C寄存器,从而允许系统关闭I2C的电源域以节省功耗,但会增加RAM消耗
返回值
ESP_OK 成功创建 I2C 从设备
ESP_ERR_INVALID_ARG 参数无效
ESP_ERR_NO_MEM 由于内存不足,创建从设备失败

Uninstall I2C slave device

如果不再需要之前安装的 I2C 总线,建议调用 i2c_del_slave_device() 来回收资源,以释放底层硬件

i2c_del_slave_device()

反初始化 I2C 从设备并释放对应的资源

esp_err_t i2c_del_slave_device(i2c_slave_dev_handle_t i2c_slave)

参数
i2c_slave I2C 设备句柄,需要删除的目标设备句柄
返回值
ESP_OK 设备成功被删除
ESP_ERR_INVALID_ARG 参数无效,例如传入的设备句柄为空或无效

I2C Master Controller

通过调用 i2c_new_master_bus() 安装好 I2C 主机控制器驱动程序后,ESP32 就可以与其他 I2C 设备进行通信了

I2C Master Write

在成功安装 I2C 主机总线之后,可以通过调用 i2c_master_transmit() 来向从机设备写入数据,通信过程如下

Master : START | Device address W     Data     STOP
Slave  :                          ACK      ACK

i2c_master_transmit()
在 I2C 总线上执行一次写入事务

  • 如果未注册回调函数,函数将阻塞直到传输完成或超时
  • 如果注册了回调函数,则事务为异步,函数立即返回,传输完成信息通过回调函数获取
esp_err_t i2c_master_transmit(i2c_master_dev_handle_t i2c_dev,
								const uint8_t *write_buffer,
								size_t write_size,
								int xfer_timeout_ms)

参数
i2c_dev 由 i2c_master_bus_add_device 创建的 I2C 主设备句柄
write_buffer 待发送的数据缓冲区
write_size write_buffer 的大小(字节数)
xfer_timeout_ms 输入参数,等待超时时间(单位:毫秒),-1 表示永不超时

返回值
ESP_OK 成功,数据已通过 I2C 总线发送
ESP_ERR_INVALID_ARG 参数无效,例如设备句柄为空、缓冲区无效或缓冲区大小为 0
ESP_ERR_TIMEOUT 超时,可能是总线忙或硬件故障,传输时间超过 xfer_timeout_ms

I2C Master Read

在成功安装 I2C 主机总线后,可以通过调用 i2c_master_receive() 从从机设备读取数据
通信过程如下

Master : START | Device address W          ACK          NACK STOP
Slave  :                          ACK Data     ACK Data

i2c_master_receive()
在 I2C 总线上执行一次读取,从从设备接收数据

esp_err_t i2c_master_receive(i2c_master_dev_handle_t i2c_dev, 
							uint8_t *read_buffer, 
							size_t read_size, 
							int xfer_timeout_ms)

参数
i2c_dev I2C 主设备句柄
read_buffer 用于存储从 I2C 总线接收到的数据
read_size 读取缓冲区的大小
xfer_timeout_ms 输入参数,设置等待超时时间(单位:毫秒),-1 表示无限等待直到操作完成。

返回值
ESP_OK 成功
ESP_ERR_INVALID_ARG 参数无效
ESP_ERR_TIMEOUT 超时

I2C Master Write and Read Permalink to this headline

从一些 I2C 设备中读取数据之前需要进行写入配置(如 ADXL345),可通过 i2c_master_transmit_receive() 接口进行配置
通信过程如下

Master : START | Device address W          Data    START   Device address R      NACK STOP
Slave  :                          ACK Data     ACK                          Data

i2c_master_transmit_receive()
在 I2C 总线上执行一次 写入-读取(Write-Read)

  • 主设备先向从设备发送数据(写操作),然后从从设备接收数据(读操作)
esp_err_t i2c_master_transmit_receive(i2c_master_dev_handle_t i2c_dev, 
										const uint8_t *write_buffer, 
										size_t write_size, 
										uint8_t *read_buffer, 
										size_t read_size, 
										int xfer_timeout_ms)

参数
i2c_dev I2C 主设备句柄
write_buffer 待发送的数据缓冲区
write_size 写缓冲区的大小(字节数)
read_buffer 用于存储从设备返回的数据
read_size 读缓冲区的大小(字节数)
xfer_timeout_ms 输入参数,设置等待超时时间(单位:毫秒),-1 表示无限等待直到操作完成

返回值
ESP_OK 成功
ESP_ERR_INVALID_ARG 参数无效
ESP_ERR_TIMEOUT 超时

Example Code:ESP32 I2C Communication with ADXL345 Accelerometer

/*
* Author: Lamonce
* Description: i2c communication test
* Date: 2024-12-5
* esp-idf Version: 5.3.1
*/

#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "driver/i2c_master.h"
#include "esp_log.h"

#define ADXL345_I2C_ADDRESS 0x53  // ADXL345 地址
#define I2C_MASTER_SDA_IO 21      // SDA 引脚
#define I2C_MASTER_SCL_IO 22      // SCL 引脚
#define I2C_MASTER_FREQ_HZ 400000 // 通信速率
#define GRAVITY_ACCELERATION 32 / 8196.0 // 重力加速度

i2c_master_bus_handle_t i2c_master_bus_handle; // I2C 总线句柄
i2c_master_dev_handle_t i2c_master_ADXL345_handle; // ADXL345 设备句柄

// 在此处添加函数声明
void i2cMasterInit(void); // I2C 总线初始化
void i2cADXL345Init(void); // ADXL345 设备初始化
void ADXL345ReadData(void); // 读取 ADXL345 数据

void app_main(void)
{
    vTaskDelay(5000 / portTICK_PERIOD_MS); // 延时 5 秒,方便观察LOG
    i2cMasterInit(); // I2C 总线初始化
    i2cADXL345Init(); // ADXL345 设备初始化

    for (int i = 0; i < 10; i++)
    {
        ADXL345ReadData();
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }

    i2c_del_master_bus(i2c_master_bus_handle); // 删除 I2C 总线
    ESP_LOGI("I2C", "I2C master has been deleted.");
}

void i2cMasterInit(void)
{
    i2c_master_bus_config_t i2c_master_config = {
        .clk_source = I2C_CLK_SRC_DEFAULT, // 使用默认时钟源
        .i2c_port = I2C_NUM_0, // I2C 端口
        .sda_io_num = I2C_MASTER_SDA_IO, // SDA 引脚
        .scl_io_num = I2C_MASTER_SCL_IO, // SCL 引脚
        .glitch_ignore_cnt = 7, // 设置主机总线的毛刺周期
        .flags.enable_internal_pullup = true, // 启用内部上拉
    };

    ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_master_config, &i2c_master_bus_handle)); // 创建 I2C 总线
    ESP_LOGI("I2C", "I2C master has been initialized.");
}

void i2cADXL345Init(void)
{
    i2c_device_config_t i2c_device_config = {
        .dev_addr_length = I2C_ADDR_BIT_LEN_7, // 设备地址长度
        .device_address = ADXL345_I2C_ADDRESS, // 设备地址
        .scl_speed_hz = I2C_MASTER_FREQ_HZ, // 通信速率
    };

    ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_master_bus_handle, &i2c_device_config, &i2c_master_ADXL345_handle)); // 添加设备,注意传参顺序
    ESP_LOGI("I2C", "ADXL345 has been added to the bus.");

    uint8_t adxl345_config_data[] = {
        0x31, 0x0f, // 设置数据左对齐
        0x2c, 0x08, // 设置速率
        0x2d, 0x08, // 设置电源模式
        0x2e, 0x80, // 使能 DATA_READY 中断
        0x1e, 0x00, // x偏移量
        0x1f, 0x00, // y偏移量
        0x20, 0x00, // z偏移量
    };

    for (int i = 0; i < sizeof(adxl345_config_data); i += 2) // 写入配置数据
    {
        ESP_ERROR_CHECK(i2c_master_transmit(i2c_master_ADXL345_handle, &adxl345_config_data[i], 2, -1));
    }
    vTaskDelay(100 / portTICK_PERIOD_MS); // 延时 100ms,等待配置生效

    uint8_t init_check = 0;
    uint8_t reg_address = 0x00;

    ESP_ERROR_CHECK(i2c_master_transmit_receive(i2c_master_ADXL345_handle, &reg_address, 1, &init_check, 1, -1)); // 读取初始化检查寄存器
    ESP_LOGI("ADXL345", "init_check: %x", init_check);
    if (init_check == 0xE5) // 读出的数据为0XE5,表示正确
    {
        ESP_LOGI("ADXL345", "ADXL345 has been initialized successfully.");
    }
    else
    {
        ESP_LOGE("ADXL345", "ADXL345 initialization failed.");
    }
}

void ADXL345ReadData(void)
{
    uint8_t reg_address = 0x32;
    uint8_t adxl345_data[6];
    ESP_ERROR_CHECK(i2c_master_transmit_receive(i2c_master_ADXL345_handle, &reg_address, 1, adxl345_data, sizeof(adxl345_data), -1)); // 读取数据
    // ADXL345 数据为补码,需要转换为原码
    for (int i = 0; i < sizeof(adxl345_data); i++) // 将补码转换为原码
    {
        adxl345_data[i] = ~adxl345_data[i];
        adxl345_data[i] += 1;
    }
    int16_t x_data = (adxl345_data[1] << 8) | adxl345_data[0];
    int16_t y_data = (adxl345_data[3] << 8) | adxl345_data[2];
    int16_t z_data = (adxl345_data[5] << 8) | adxl345_data[4];

    // 格式化数据
    x_data >>= 3;
    y_data >>= 3;
    z_data >>= 3;
    if (x_data > 4096)
    {
        x_data -= 8192;
    }
    if (y_data > 4096)
    {
        y_data -= 8192;
    }
    if (z_data > 4096)
    {
        z_data -= 8192;
    }

    // 转换为重力加速度
    double x_data_g = x_data * GRAVITY_ACCELERATION;
    double y_data_g = y_data * GRAVITY_ACCELERATION;
    double z_data_g = z_data * GRAVITY_ACCELERATION;
    ESP_LOGI("ADXL345", "x_data: %fg, y_data: %fg, z_data: %fg", x_data_g, y_data_g, z_data_g);
}

在这里插入图片描述

Potplayer + LAVFilters + madVR 配置教程 文章目录Potplayer + LAVFilters + madVR 配置教程工具安装篇设置调教1.potplayer播放器设置加载LAVFilters和madVR1.选择“源滤镜/分离器”选项,选择“滤镜/解码器管理”2.“添加外部滤镜”3.浏览到刚才LAVFilters解压的文件夹,分别添加图示项。4.添加后回到管理界面设置,点击左边的”LAV AudioDecoder”,将右边”音频解码器”... 阅读详情

相关推荐

Supermind量化实战:我如何给双均线策略加上“止损”和“仓位管理”,让回测收益更稳健

本文详细介绍了如何在Supermind平台上为双均线策略添加止损和仓位管理模块,以提升量化交易的稳健性。通过Python实现动态止损和智能仓位管理,策略的年化收益率从12.3%提升至15.8%,最大回撤从58.7%降至22.4%,Sharpe比率显著提高。这些实战技巧适用于量化交易爱好者,帮助他们在黑天鹅行情中保持收益稳定。

weixin_42567752的博客 363

ESP32 I2C详解

资源分配- 包括如何使用正确的配置来分配 I2C 总线,以及如何在完成工作后回收资源。I2C主机控制器- 包括 I2C 主机控制器的行为,介绍了数据发送、数据接收和数据的双向传输。I2C从机控制器- 包括 I2C 从机控制器的行为,涉及数据发送和数据接收。电源管理- 描述了不同时钟源对功耗的影响。IRAM安全- 描述了如何在 cache 被禁用时正常运行 I2C 中断。线程安全- 列出了驱动程序中线程安全的 API。Kconfig选项。

CCSU 25届毕业生 目前就职于移远通信 岗位是蓝牙软件工程师 学习蓝牙Spec中 文章是个人笔记 分享给大家 感谢关注~ 2571

Reeds-Shepp曲线公式推导及代码实现-第三部分(共三部分)

根据原始起点的坐标和航向,将上一小节计算出来的局部进行整体的平移和旋转(与归一化的步骤刚好相反),最终得到全局路径的轨迹。根据计算出来的路径,选择路径最短的轨迹(也可根据我们实际的需要定制选优规则),作为最终的轨迹输出。Reeds-Shepp曲线公式推导及代码实现-第一部分(共三部分)Reeds-Shepp曲线公式推导及代码实现-第二部分(共三部分)的值进行等步长插值,得到每段轨迹对应的生成一系列插值距离列表。根据上一步生成的插值距离列表的值,计算其对应的局部位姿值。后,我们需要根据字段类型(

jianmo的博客 1089

ESP iic驱动

本文介绍了如何配置ESP32芯片的I2C控制器作为主机(Master)与其他设备通信。核心步骤包括:1)使用i2c_master_bus_config_t结构体配置总线参数;2)调用i2c_new_master_bus()创建总线实例并获取bus_handle句柄;3)通过该句柄管理I2C总线和挂载设备。bus_handle作为驱动内部资源的标识指针,用于后续设备操作(如添加设备)。文中提供了完整的代码示例,展示了初始化过程和句柄管理方法,强调通过封装函数对外提供只读访问权限。

weixin_45038397的博客 439

Advanced Installer打包避坑指南:如何正确配置64位系统下的Java环境与注册表条件(以15.7版本为例)

本文详细解析了使用Advanced Installer 15.7版本在64位系统下打包Java应用时的常见问题与解决方案。重点介绍了注册表重定向机制、Java环境检测的双保险策略、环境变量设置的隐藏坑以及权限管理的进阶技巧,帮助开发者避免安装包在用户机器上无法运行的幽灵问题。

weixin_28676289的博客 324

先进封装Interposer仿真步骤及说明

摘要: Interposer仿真是2.5D/3DIC封装设计的关键环节,涵盖信号完整性(SI)、电源完整性(PI)、热及结构应力分析。流程分为五个阶段:1)前处理,定义层叠结构与材料属性;2)参数提取,通过3D电磁建模获取TSV、传输线等寄生参数;3)仿真设置,选择求解器并设定频率范围;4)结果分析,评估信号损耗、阻抗及电源网络性能;5)后处理,进行多物理场协同仿真并生成模型。核心挑战在于多尺度与多物理场耦合,需优先确保高速信号与电源网络的仿真精度。常用工具包括Ansys HFSS、Cadence Sigr

DSY_0324的博客 1351

ESP32 IIC 主机

2 新的库 主机和从机 分别对应不同的库文件 如果只esp只作为主机 只需要调用 主机头文derver/i2c_master.h即可 从机同理。1 老的库 头文件iic.h (官方 已经 弃用了)esp32 有两种 方法初始化IIC;

weixin_70341930的博客 548

三维(点云)语义分割概述以及方法总结

语义分割 语义分割需要两部分 classification: 需要全局信息 segmentation:更加依赖全局信息和局部信息 一般的网络结构是: 提特征-特征映射-特征图压缩(降维)-全连接-分类,其实就是encoder-decoder的过程, 比如在二维的的pspnet,fcn等等 0. 三维表示的数据结构 Point cloud 无序性 点云实际上是无序的,比如有8个点云,你放到...

renhaofan的博客 3万+

【ESP32】ESP-IDF开发 | I2C控制器+I2C主从收发例程

I2C(),是由Philips公司在1980年代初开发的一种半双工的同步串行总线,它利用一根时钟线和一根数据线在连接总线的两个器件之间进行信息的传递,为设备之间数据交换提供了一种简单高效的方法。每个连接到总线上的器件都有唯一的地址,任何器件既可以作为主机也可以作为从机,但同一时刻只允许有一个主机。I2C在硬件上是采用了开漏输出,在这种模式下GPIO没有输出负载的能力,因此总线上的每根管脚都需要接上拉电阻才能正常通信。I2C每次通信都由主机发起,

JackieCoo的博客 3892

SRAM compiler中Power Gating与Retention的实战配置指南

本文深入解析了在SRAM compiler中配置Power Gating(电源门控)与Retention(数据保持)功能的实战指南。针对低功耗芯片设计,文章详细阐述了如何根据应用场景(如移动AP、IoT设备)权衡开关类型、尺寸、唤醒时序以及保持电压,以实现静态功耗优化与数据可靠性的最佳平衡,并强调了后端实现与签核检查的关键要点。

weixin_29204121的博客 296

利用随机矩阵理论(random matrix theory)确定微生物网络构建阈值

利用随机矩阵理论(random matrix theory)确定微生物网络构建阈值

微生物生态学分析 7639

【ESP32-IDF 笔记】04-I2C配置

基于ESP32,IDF环境,配置I2C,以及相关的注意实现

别看了,就这些信息 1844

Java 新纪元 — JDK 25 + Spring Boot 4 全栈实战(十九):微服务实战——Boot 4 + Spring Cloud 2026.x,构建高并发电商分布式系统

Component@Slf4j@Autowired// 白名单路径@Override// 白名单放行// 提取Token// 传递用户信息到下游服务.mutate().build();@Override// 最高优先级if (bearer!@Component技术点Boot 3 时代Boot 4 时代提升线程模型平台线程池(200线程上限)虚拟线程(百万级并发)50x+服务通信。

qq_17204557的博客 186

ESP32学习笔记_Peripherals(5)——SPI主机通信

SPI(串行外设接口)是一种高速全双工同步串行通信总线,采用主从架构,由主设备控制一个或多个从设备;SPI使用四条信号线(SCLK、MOSI、MISO、CS)实现通信,传输速度快(可达10MHz以上),具有接线简单、支持多从设备等优点。ESP32-S3芯片集成四个SPI控制器,其中SPI2和SPI3作为通用SPI总线使用;SPI通信过程包括主机初始化、选择从机、数据传输和结束通信四个阶段;ESP32的SPI传输事务由命令、地址、Dummy、写入和读取五个阶段组成,可通过多个结构体对通信进行配置

flashier的博客 1673

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

5步快速上手ESP32:Arduino-ESP32终极入门指南

还在为ESP32开发板的复杂配置而头疼吗?想要在10分钟内完成从零到一的ESP32开发环境搭建?今天我要与你分享一个超实用的快速入门方案:基于Arduino-ESP32开源项目,你只需要5个简单步骤就能开始你的物联网开发之旅!🚀 Arduino-ESP32项目是Espressif官方支持的Arduino核心库,为ESP32系列芯片提供了完整的Arduino开发框架支持。无论你是物联网初学者还是

gitblog_00598的博客 1066

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

ESP32与MAX30102血氧心率监测实战:从I2C驱动到数据滤波与血氧计算

光电容积脉搏波描记法(PPG)通过检测血液对特定波长光的吸收变化来反映心率与血氧水平。基于PPG原理,MAX30102传感器集成红光与红外LED及光电二极管,可输出原始光强数据,结合I2C通信协议与ESP32微控制器,实现数据采集与处理。其技术价值在于将医疗级监测功能低成本化,适用于家庭健康监护、可穿戴设备等场景。然而,从传感器原始数据到可靠的血氧值,需解决I2C配置、FIFO读取、信号滤波与血氧计算等工程问题。本文以ESP32和MAX30102为例,详细解析MicroPython驱动开发、数据预处理及实测

weixin_34242819的博客 372

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

3小时玩转ESP32:Arduino生态下的智能硬件开发终极指南

还在为复杂的嵌入式开发环境头疼吗?想要用简单的Arduino语法开发强大的ESP32项目却不知从何开始?今天,我要带你走进Arduino-ESP32的世界,这是一套让ESP32系列芯片在Arduino生态中焕发新生的开发框架。无论你是物联网新手还是经验丰富的开发者,都能在这里找到适合自己的开发路径。 **Arduino-ESP32**是Espressif官方支持的Arduino核心,它为ESP3

gitblog_01022的博客 951

ESP32开发板终极指南:3小时快速上手物联网项目开发

还在为物联网开发的高门槛而犹豫?想要快速掌握ESP32开发却不知从何入手?今天我要与你分享一个完整的ESP32开发入门方案,基于Arduino-ESP32核心框架,让你在3小时内就能完成从环境搭建到第一个物联网项目的全流程体验!🚀 ESP32开发板是学习物联网和嵌入式开发的绝佳选择。通过Arduino-ESP32项目,你可以轻松访问ESP32系列芯片的强大功能,无论是WiFi连接、蓝牙通信还是

gitblog_01135的博客 1007

esp-idf-hal核心功能解析:GPIO、SPI与I2C的Rust实现原理

esp-idf-hal是为ESP32和ESP-IDF平台提供的Rust嵌入式硬件抽象层(HAL)实现,支持几乎所有ESP IDF驱动,包括GPIO、SPI、I2C、TIMER、PWM等核心外设。本文将深入解析其GPIO、SPI与I2C三大核心功能的Rust实现原理,帮助开发者快速掌握硬件交互的关键技术。 ## 一、GPIO:灵活的引脚驱动系统 🚀 ### 1.1 驱动-外设分离架构 GPI

gitblog_00174的博客 354

如何编写嵌入式Rust驱动程序:std-training I2C驱动开发实战

想要学习嵌入式Rust开发吗?std-training项目为你提供了一个完整的I2C驱动开发实战教程!在这个嵌入式Rust培训项目中,你将掌握如何为Espressif ESP32-C3编写专业的I2C驱动程序。通过这个实战指南,即使是Rust新手也能快速上手嵌入式系统开发,理解I2C总线通信的核心原理,并学会如何编写可靠的硬件驱动程序。 ## 🚀 I2C总线通信基础 I2C(Inter-In

gitblog_00957的博客 1031

esp-idf-hal完全指南:Rust开发者的ESP32外设控制终极方案

esp-idf-hal是一个为ESP32和ESP-IDF平台提供Rust语言支持的嵌入式硬件抽象层(HAL)实现,它实现了embedded-hal V0.2和V1.0版本的阻塞与异步特性,让开发者能够轻松控制ESP32的各种外设。 ## 为什么选择esp-idf-hal? 对于Rust开发者来说,esp-idf-hal提供了一个强大而直观的接口,用于与ESP32的外设进行交互。它不仅实现了em

gitblog_01083的博客 344

ESP32 Arduino终极指南:从零到物联网专家的完整教程

还在为ESP32开发的复杂性而烦恼吗?想要用Arduino的简单语法控制强大的ESP32芯片吗?Arduino-ESP32项目为你提供了完美的解决方案——将ESP32系列芯片的强大功能封装成简单易用的Arduino API,让你在5分钟内就能开始物联网开发! 想象一下:你有一个绝佳的物联网创意,但面对复杂的底层驱动和配置感到无从下手。现在,有了Arduino-ESP32,你可以像使用Arduin

gitblog_00759的博客 236

I2C总线深度解析:用Rust读取传感器数据的完整教程 - std-training项目实践

想要在嵌入式系统中高效读取多个传感器数据吗?I2C总线协议是你的最佳选择!🚀 本教程将带你深入了解I2C总线的工作原理,并通过std-training项目的实际案例,展示如何用Rust语言在ESP32-C3上读取温湿度传感器和IMU传感器的数据。 I2C(Inter-Integrated Circuit)是一种串行通信协议,允许多个从设备与一个或多个主设备进行通信。这个协议只需要两根信号线(S

gitblog_00250的博客 494

ESP-HAL终极指南:如何用Rust轻松开发ESP32嵌入式系统

ESP-HAL是一个专为ESP32系列微控制器设计的no_std硬件抽象层,采用Rust语言编写,为嵌入式开发者提供了安全、高效、现代化的开发体验。如果您正在寻找一种既能保证内存安全又能充分发挥ESP32硬件性能的解决方案,ESP-HAL绝对是您的最佳选择。 ## 🚀 为什么选择ESP-HAL? ### 安全性与性能的完美结合 ESP-HAL最大的优势在于它将Rust语言的内存安全特性与E

gitblog_00881的博客 534

ESP32开发终极指南:从零到物联网项目的完整教程

还在为ESP32开发环境的配置而烦恼?想要快速上手ESP32物联网开发却不知从何开始?今天我将为你揭秘Arduino-ESP32项目的完整使用指南,让你在30分钟内完成开发环境搭建,开启物联网创作之旅!🚀 Arduino-ESP32项目是Espressif官方支持的ESP32系列芯片的Arduino核心库,它为ESP32、ESP32-S3、ESP32-C3等主流芯片提供了完整的Arduino

gitblog_00130的博客 760

ESP32物联网开发终极指南:3步快速上手智能硬件编程

还在为嵌入式开发的高门槛而烦恼吗?想要快速构建物联网项目却不知从何开始?Arduino-ESP32项目为你提供了一条通往智能硬件世界的捷径。这个由Espressif官方支持的Arduino核心库,将ESP32系列芯片的强大功能与Arduino生态的易用性完美结合,让你在短短3步内就能开始ESP32物联网开发之旅。 ## 🎯 为什么选择ESP32 Arduino开发? ### 问题:传统嵌入式

gitblog_02441的博客 456
上一篇: ESP32学习笔记_Peripherals(1)——UART
下一篇: ESP32学习笔记_FreeRTOS(4)——Semaphore
flashier
博客等级 码龄3年 381粉丝 79原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值