当前位置: 首页 > news >正文

ESP-IDF基础入门(2)

这次针对外设API中的GPIO、LEDC和ADC进行入门。这些都属于外设API中
参考:https://docs.espressif.com/projects/esp-idf/zh_CN/v5.5.1/esp32c6/api-reference/peripherals/index.html

一、GPIO操作

  1. 基础知识:
    ESP32-C6 芯片具有 31 个物理 GPIO 管脚(GPIO0 ~ GPIO30)。每个管脚都可用作一个通用 IO,或连接一个内部的外设 信号。通过 GPIO 交换矩阵和 IO MUX,可配置外设模块的输入信号来源于任何的 IO 管脚,并且外设模块的输 出信号也可连接到任意 IO 管脚。
    IO 可以有两种使用方式:
    作为简单的 GPIO 输入读取引脚上的电平,或作为简单的 GPIO 输出以输出所需的电平。
    作为外设信号的输入/输出。

  2. 头文件和需求库

#include "driver/gpio.h"

为了支持GPIO,需要增加esp_driver_gpio到 CMakeLists.txt中,补充如下内容:

REQUIRES esp_driver_gpio
// or
PRIV_REQUIRES esp_driver_gpio
  1. 通过代码理解:

使用的核心板是SEEED XIAO ESP32-C6,LED引脚为GPIO15,按键引脚为BOOT GPIO9
代码通过初始化LED输出,初始化BOOT引脚输入,切换LED开关:

#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"#define LED_GPIO GPIO_NUM_15
#define KEY_GPIO GPIO_NUM_9
void app_main(void)
{// 创建配置结构体,初始化LED和KEYgpio_config_t led_config = {.pin_bit_mask = 1ULL << LED_GPIO,.mode = GPIO_MODE_OUTPUT,.pull_up_en = GPIO_PULLUP_DISABLE,.pull_down_en = GPIO_PULLDOWN_DISABLE,.intr_type = GPIO_INTR_DISABLE};esp_err_t esp_err = gpio_config(&led_config);gpio_config_t key_config = {.pin_bit_mask = 1ULL << KEY_GPIO,.mode = GPIO_MODE_INPUT,.pull_up_en = GPIO_PULLUP_ENABLE,.pull_down_en = GPIO_PULLDOWN_DISABLE,.intr_type = GPIO_INTR_DISABLE};esp_err = gpio_config(&key_config);while (1){if (gpio_get_level(KEY_GPIO) == 0)gpio_set_level(LED_GPIO, 1);    // 注意直接使用GPIO_NUM,而不是maskelsegpio_set_level(LED_GPIO, 0);vTaskDelay(2);}
}

二、中断GPIO

将KEY引脚设置为中断模式,下降沿触发,并控制LED切换状态。
由于ISR中不能够长时间等待(不能一直while等待按键放开之类的),且设置引脚电平状态也是不合适的,最佳的还是通过Freertos的任务和消息来保障实时性。

#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_err.h"
#include "esp_intr_alloc.h"#define LED_GPIO GPIO_NUM_15
#define KEY_GPIO GPIO_NUM_9static QueueHandle_t gpio_evt_queue = NULL;
static volatile int led_state = 0;// ISR必须放IRAM,且尽量短
static void IRAM_ATTR key_isr(void* arg)
{uint32_t gpio_num = (uint32_t) arg;// 直接把引脚号塞进队列给任务处理(从ISR版本的xQueueSend)BaseType_t xHigherPriorityTaskWoken = pdFALSE;xQueueSendFromISR(gpio_evt_queue, &gpio_num, &xHigherPriorityTaskWoken);if (xHigherPriorityTaskWoken) {portYIELD_FROM_ISR();  // 让高优先级任务立刻跑}
}static void led_task(void* arg)
{uint32_t io_num;for(;;) {if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {// 这里做消抖也比较好,比如延时20ms再确认按键仍为低vTaskDelay(pdMS_TO_TICKS(20));if (gpio_get_level(KEY_GPIO) == 0) { // 仍然按下,视为有效led_state = !led_state;gpio_set_level(LED_GPIO, led_state);}}}
}void app_main(void)
{// 配置LED为输出gpio_config_t io_conf_led = {.pin_bit_mask = 1ULL << LED_GPIO,.mode = GPIO_MODE_OUTPUT,.pull_up_en = GPIO_PULLUP_DISABLE,.pull_down_en = GPIO_PULLDOWN_DISABLE,.intr_type = GPIO_INTR_DISABLE};ESP_ERROR_CHECK(gpio_config(&io_conf_led));gpio_set_level(LED_GPIO, 0);// 配置KEY为输入并下降沿中断,内部上拉gpio_config_t io_conf_key = {.pin_bit_mask = 1ULL << KEY_GPIO,.mode = GPIO_MODE_INPUT,.pull_up_en = GPIO_PULLUP_ENABLE,.pull_down_en = GPIO_PULLDOWN_DISABLE,.intr_type = GPIO_INTR_NEGEDGE};ESP_ERROR_CHECK(gpio_config(&io_conf_key));// 创建队列gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));// 安装GPIO中断服务并把按键GPIO挂到我们的ISRESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_IRAM));ESP_ERROR_CHECK(gpio_isr_handler_add(KEY_GPIO, key_isr, (void*) KEY_GPIO));// 启动一个任务处理LED翻转逻辑xTaskCreate(led_task, "led_task", 2048, NULL, 10, NULL);// app_main不退出就行while(1){vTaskDelay(pdMS_TO_TICKS(1000));}
}

三、ADC操作

接下来是ADC,对于ESP32-C6,ADC比较有限。数量为 {SOC_ADC_PERIPH_NUM} 个 ADC 单元,引脚固定,为GPIO0-6共7个引脚,对应7个通道。可以时下按单词和连续扫描
在这里插入图片描述

单次转换模式

需要添加头文件和组件到CMakeLists.txt:

#include "hal/adc_types.h"
#include "esp_adc/adc_oneshot.h"
PRIV_REQUIRES esp_adc

基本流程如下

  1. 使用如下代码进行驱动初始化和资源分配
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_unit_init_cfg_t init_config1 = {.unit_id = ADC_UNIT_1,.ulp_mode = ADC_ULP_MODE_DISABLE,
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));
// ESP_ERROR_CHECK(adc_oneshot_del_unit(adc1_handle));	// 资源回收
  1. 配置ADC通道
adc_oneshot_chan_cfg_t config = {.bitwidth = ADC_BITWIDTH_DEFAULT,.atten = ADC_ATTEN_DB_12,
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN0, &config));
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN1, &config));
  1. 读取转换结果
    调用 adc_oneshot_read() 可以获取 ADC 通道的原始转换结果。函数 adc_oneshot_read() 可以安全使用,使用互斥锁,但避免与其他函数同时使用硬件,因此该函数不应在 ISR 上下文中使用。
ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN0, &adc_raw[0][0]));
ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN0, adc_raw[0][0]);ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN1, &adc_raw[0][1]));
ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN1, adc_raw[0][1]);
  1. 计算实际电压
Vout = Dout * Vmax / Dmax       (1)

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

  1. 但是上述代码测量的结果不一定准确,ESP32-C6 设计的 ADC 参考电压为 1100 mV。然而,不同芯片的真实参考电压可能会略有变化,范围在 1000 mV 到 1200 mV 之间。需要进行校准。
    添加头文件#include "esp_adc/adc_cali.h",使用如下代码进行校准获得校准函数,并通过提供的函数转换到真实的mV结果
ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
adc_cali_curve_fitting_config_t cali_config = {.unit_id = unit,.atten = atten,.bitwidth = ADC_BITWIDTH_DEFAULT,
};
ESP_ERROR_CHECK(adc_cali_create_scheme_curve_fitting(&cali_config, &handle));ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc_cali_handle, adc_raw[0][0], &voltage[0][0]));
  1. 完整的代码如下,
#include <stdio.h>
#include "esp_log.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"#define TAG             "ADC_DEMO"// 硬件绑定:GPIO0 -> ADC1_CH0
#define MY_ADC_UNIT     ADC_UNIT_1
#define MY_ADC_CHANNEL  ADC_CHANNEL_0       // 若报错,请用你芯片里对应 ADC1_CH0 的枚举名
#define MY_ADC_ATTEN    ADC_ATTEN_DB_12     // 12 dB 衰减
#define MY_BITWIDTH     ADC_BITWIDTH_12     // 12 位void app_main(void)
{adc_oneshot_unit_handle_t adc_handle;adc_cali_handle_t cali_handle = NULL;// 1. 初始化 ADC 单次转换句柄adc_oneshot_unit_init_cfg_t init_cfg = {.unit_id  = MY_ADC_UNIT,.ulp_mode = ADC_ULP_MODE_DISABLE,};ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_cfg, &adc_handle));// 2. 配置通道adc_oneshot_chan_cfg_t chan_cfg = {.bitwidth = MY_BITWIDTH,.atten    = MY_ADC_ATTEN,};ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, MY_ADC_CHANNEL, &chan_cfg));// 3. 创建电压校准句柄(曲线拟合方案)adc_cali_curve_fitting_config_t cali_cfg = {.unit_id  = MY_ADC_UNIT,.chan     = MY_ADC_CHANNEL,.atten    = MY_ADC_ATTEN,.bitwidth = MY_BITWIDTH,};esp_err_t ret = adc_cali_create_scheme_curve_fitting(&cali_cfg, &cali_handle);if (ret == ESP_ERR_NOT_SUPPORTED) {ESP_LOGW(TAG, "No eFuse calibration data, voltage will be uncalibrated");cali_handle = NULL; // 没校准也照样跑,只是不给mV} else {ESP_ERROR_CHECK(ret);}// 4. 周期性读取while (1) {int raw = 0;int mv  = 0;ESP_ERROR_CHECK(adc_oneshot_read(adc_handle, MY_ADC_CHANNEL, &raw));if (cali_handle && adc_cali_raw_to_voltage(cali_handle, raw, &mv) == ESP_OK) {ESP_LOGI(TAG, "RAW=%d  VOL=%d mV", raw, mv);} else {ESP_LOGI(TAG, "RAW=%d  VOL=NA", raw);}vTaskDelay(pdMS_TO_TICKS(500));}// (演示用:常驻循环,一般不释放资源。如果你要在任务退出前清理:)// if (cali_handle) {//     adc_cali_delete_scheme_curve_fitting(cali_handle);// }// adc_oneshot_del_unit(adc_handle);
}
  1. 由于ADC受噪声影响大, 最好加一个对地电容,或者多次读取+滤波。

连续转换模式

  1. 下次补充:https://docs.espressif.com/projects/esp-idf/zh_CN/v5.5.1/esp32c6/api-reference/peripherals/adc_continuous.html

四、LEDC操作(PWM)

  1. LED 控制器 (LEDC) 主要用于控制 LED,也可产生 PWM 信号用于其他设备的控制。该控制器有 6 路通道,可以产生独立的波形,驱动 RGB LED 等设备。LED PWM 控制器可在无需 CPU 干预的情况下自动改变占空比,实现亮度和颜色渐变。

  2. 设置 LEDC 通道分三步完成。注意,与 ESP32 不同,ESP32-C6 仅支持设置通道为低速模式。
    在这里插入图片描述

  3. 首次 LEDC 配置时,建议先配置定时器(调用函数 ledc_timer_config()),再配置通道(调用函数 ledc_channel_config())。这样可以确保 IO 脚上的 PWM 信号自有输出开始其频率就是正确的。

  4. 定时器配置
    要设置定时器,可调用函数 ledc_timer_config(),并将包括如下配置参数的数据结构 ledc_timer_config_t 传递给该函数。不同时钟源特性如下:
    在这里插入图片描述

  5. 通道配置
    定时器设置好后,请配置所需的通道(ledc_channel_t 之一)。配置通道需调用函数 ledc_channel_config()。通道的配置与定时器设置类似,需向通道配置函数传递包括通道配置参数的结构体 ledc_channel_config_t 。
    此时,通道会按照 ledc_channel_config_t 的配置开始运作,并在选定的 GPIO 上生成由定时器设置指定的频率和占空比的 PWM 信号。在通道运作过程中,可以随时通过调用函数 ledc_stop() 将其暂停。

  6. 改变PWM信号
    软件方式:
    调用函数 ledc_set_duty() 可以设置新的占空比。之后,调用函数 ledc_update_duty() 使新配置生效。要查看当前设置的占空比,可使用 get 函数 ledc_get_duty()。传递给函数的占空比数值范围取决于选定的 duty_resolution,应为 0 至 (2 ** duty_resolution)。
    硬件方式:
    LED PWM 控制器硬件可逐渐改变占空比的数值。要使用此功能,需用函数 ledc_fade_func_install() 使能渐变,之后用下列可用渐变函数之一配置:

ledc_set_fade_with_time()ledc_set_fade_with_step()ledc_set_fade()

ESP32-C6 的硬件额外支持多达 16 次,无需 CPU 介入的连续渐变。此功能可以更加有效便捷得实现一个带伽马校正的渐变。

  1. 改变PWM频率
    LED PWM 控制器 API 有多种方式即时改变 PWM 频率:

通过调用函数 ledc_set_freq() 设置频率。可用函数 ledc_get_freq() 查看当前频率。

通过调用函数 ledc_bind_channel_timer() 将其他定时器绑定到该通道来改变频率和占空比分辨率。

通过调用函数 ledc_channel_config() 改变通道的定时器。

  1. 头文件和依赖
#include "driver/ledc.h"
PRIV_REQUIRES esp_driver_ledc
  1. 完整代码如下,控制LED进行亮暗变化
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_log.h"#include "driver/ledc.h"#define TAG "LEDC_PWM_DEMO"#define LED_GPIO            15                  // 要输出PWM的GPIO
#define LEDC_TIMER          LEDC_TIMER_0        // 我们用的定时器编号
#define LEDC_MODE           LEDC_LOW_SPEED_MODE // C6只有LOW_SPEED
#define LEDC_CHANNEL        LEDC_CHANNEL_0      // 我们用的通道编号
#define LEDC_FREQUENCY_HZ   1000                // PWM频率,1 kHz 够大不会闪
#define LEDC_DUTY_RES       LEDC_TIMER_8_BIT    // 8位分辨率 -> duty范围0~255void app_main(void)
{// 1. 配置并初始化 LEDC 定时器ledc_timer_config_t ledc_timer = {.speed_mode       = LEDC_MODE,          // 低速模式.timer_num        = LEDC_TIMER,         // 选择哪个定时器.duty_resolution  = LEDC_DUTY_RES,      // 占空比分辨率.freq_hz          = LEDC_FREQUENCY_HZ,  // PWM频率.clk_cfg          = LEDC_AUTO_CLK,      // 自动选择时钟源};ESP_ERROR_CHECK( ledc_timer_config(&ledc_timer) );// 2. 配置并初始化 LEDC 通道ledc_channel_config_t ledc_channel_cfg = {.speed_mode     = LEDC_MODE,.channel        = LEDC_CHANNEL,.timer_sel      = LEDC_TIMER,     // 绑定上面那个定时器.intr_type      = LEDC_INTR_DISABLE,.gpio_num       = LED_GPIO,.duty           = 0,              // 初始占空比 0(灯灭).hpoint         = 0,.flags.output_invert = 1,         // 如发现亮灭反了,可改成1};ESP_ERROR_CHECK( ledc_channel_config(&ledc_channel_cfg) );ESP_LOGI(TAG, "LEDC init done, start PWM on GPIO%d", LED_GPIO);// 3. 循环修改占空比,做一个“呼吸”式亮度变化示例//    占空比范围是 0 ~ (2^duty_resolution - 1) = 0~255 (因为8-bit)int duty = 0;int step = 5;while (1) {// 写入新的占空比ESP_ERROR_CHECK( ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, duty) );ESP_ERROR_CHECK( ledc_update_duty(LEDC_MODE, LEDC_CHANNEL) );// 简单的上下往返渐变duty += step;if (duty >= 255) {duty = 255;step = -step;} else if (duty <= 0) {duty = 0;step = -step;}vTaskDelay(pdMS_TO_TICKS(30)); // 调整这个可以改变呼吸速度}
}
http://www.dtcms.com/a/561830.html

相关文章:

  • 中国建设官方网站首页网上商城推广方案
  • 网站建设必须安装程序天眼查公司信息查询
  • 织梦网站首页是哪个文件网站手机访问跳转代码
  • 博弈dp|凸包|math分类
  • 网站浏览器兼容性问题wordpress手机端网站
  • 中国建设银行预约网站xampp做网站
  • VS2019+CUDA 编译通过但有错误提示
  • 有哪些做问卷调查挣钱的网站单页 网站模板
  • 承德网站制作数据库营销案例
  • 32位汇编:实验9分支程序结构使用
  • Kanass实践指南(3) - 开发团队如何通过kanass有效管控开发任务
  • 基于双向时序卷积网络与双向门控循环单元(BiTCN-BiGRU)混合模型的时间序列预测(Matlab源码)
  • 电子商务网站建设 精品课wordpress主题缓存
  • 站建设 app开发网站网站建设中怎么添加源码
  • qq推广网站建立企业网站 优帮云
  • 【AHE数据集】历史/未来全球网格热排放数据 PF-AHF
  • phy降速自愈到100M重试流程分析
  • Spring+LangChain4j工程搭建
  • Raft协议
  • 快速建设网站视频教程网站内容质量
  • 建设银行网站登不上牛商网做网站的思路
  • C语言程序代码(四)
  • 仿射变换的图像配准技术
  • wordpress看文网站芜湖营销网站建设
  • 泰州企业建站程序做购物网站的图标从哪里来
  • 1. Linux C++ muduo 库学习——库的编译安装
  • C++中的多态
  • C++ 使用 SQLite3 数据库
  • 郑州做网站的专业公司有哪些导购网站自己做电商
  • 松江品划网站建设推广美食网站建设策划书范文