输入输出与编程 / 编程实验发布 内容更新

ESP32 PWM 调光:用新版 LEDC 做一个五档小灯

使用Arduino-ESP32 3.3.12的ledcAttach和按引脚ledcWrite,制作0到255的五档按钮调光器,记录API返回值,并区分占空比、频率与肉眼亮度。

读完能做什么
用按钮遍历五档亮度,看到每次PWM配置与写入的实际返回值。
开始之前
能运行P03;已安装指定Arduino core,知道如何重编译。
所需器材
P02的WP7113ID LED与按钮电路、可选:示波器或逻辑分析仪
版本组合
Arduino CLI:1.5.1Arduino-ESP32:3.3.12 · 94afccf35fb1e401facddbcf9e13bcf7c76a31d8参考硬件:ESP32-S3-DevKitC-1 v1.1 · WROOM-1-N8R8FQBN:esp32:esp32:esp32s3:FlashSize=8M,PSRAM=opi,FlashMode=qio,PartitionScheme=default_8MB,CDCOnBoot=default,USBMode=hwcdc
适用范围

官方ESP32-S3-DevKitC-1 v1.1、ESP32-S3-WROOM-1-N8R8;Arduino CLI 1.5.1、Arduino-ESP32 3.3.12;USB-to-UART口。

这篇验证到了哪一步

资料核对已通过
docs/esp32-evidence/programming.md#p04版本:Arduino-ESP32 3.3.12验证日期:
构建 / 配置已通过
docs/esp32-evidence/logs/esp32-pwm-led-dimmer-esp32-pwm-led-dimmer.log对象:esp32:esp32:esp32s3:FlashSize=8M,PSRAM=opi,FlashMode=qio,PartitionScheme=default_8MB,CDCOnBoot=default,USBMode=hwcdc版本:Arduino CLI 1.5.1 / core 3.3.12验证日期:
仿真已通过
docs/esp32-evidence/host-simulation.json:主机HAL执行原样.ino,仅验证软件输入、时间戳与模拟PWM返回值;未模拟ESP32芯片、电气或物理外设验证日期:
本站实板待验证
本站未实板验证;正文提供可执行验收步骤

构建通过只说明指定环境中的代码或配置通过检查;实物功能、功耗和稳定性以对应的实板记录为准。

01 / 编程实验

让GPIO高速开关,把输出扩成亮度档位

普通数字灯只有开与关。PWM让GPIO在固定周期里快速切换,通过高电平所占时间比例改变LED平均导通时间。本篇选5kHz、8位分辨率,再用五项占空比表做可循环的按钮调光器。

PWM适用于这里的普通外接LED;板载可寻址RGB需要颜色数据协议,不能拿同一段LEDC代码驱动。更大负载也不能直接接GPIO,本篇不把LED调光例外推为电机或大功率灯驱动。

参考:[1] Arduino-ESP32 3.3.12:LEDC API

02 / 编程实验

先决定频率和分辨率,再处理边界

5kHz对应约200微秒一个周期;8位给出0–255的合法duty数值。频率与可用分辨率受硬件时钟和LEDC能力共同限制,不能同时把两个数随意调到最大。本例参数属于明确的一组配置,成功与否必须看ledcAttach返回值。

按钮依次选{0,64,128,192,255},最后回0。表内值天然限制边界,长按仍由30毫秒消抖与下降沿规则保证只前进一档。人眼对亮度的感知非线性,所以数值约等距不意味着主观看来五档等亮度递增。

本篇外接灯固定为Kingbright WP7113ID(规格书V.14A),使用330Ω、±5%、额定0.25W电阻;如果沿用F03的1kΩ电阻,也可保留,亮度更低,程序不变。更换电阻必须先断电。

参考:[1] Arduino-ESP32 3.3.12:LEDC API[2] Arduino-ESP32 3.3.12:ledcAttach和ledcWrite声明[5] Kingbright WP7113ID规格书 V.14A:极性、10mA条件压降与额定值

03 / 编程实验

接线不变,注意3.x按引脚写入

保持GPIO4→330Ω→普通红LED阳极,阴极→GND;GPIO5经常开按钮接GND,INPUT_PULLUP。使用官方v1.1 N8R8、core3.3.12,下载README提供了完整FQBN与UART日志设置。

3.x接口使用ledcAttach(pin,freq,resolution)配置,再用ledcWrite(pin,duty)按引脚写占空比。旧教程里的ledcSetup与ledcAttachPin属于另一版接口,不要把channel与pin参数混着用。代码每次配置和写入都报告布尔结果,失败时停止调光并使LED输出为低。

参数本例需要核对
pinGPIO4与外接LED接线一致
freq5000 Hz驱动是否接受;仪器可测
resolution8 bitduty必须0–255
buttonGPIO5,低电平按下30 ms稳定窗口

参考:[1] Arduino-ESP32 3.3.12:LEDC API[2] Arduino-ESP32 3.3.12:ledcAttach和ledcWrite声明[3] 乐鑫:ESP32-S3-DevKitC-1 v1.1,排针与RGB修订[4] Arduino-ESP32 3.3.12:GPIO API与模式

04 / 编程实验

完整调光器:成功返回才继续

setup配置LEDC,随后立即写level0;loop只在稳定按下后增加档位。applyLevel输出level、duty、write返回值及频率/分辨率,方便把API执行和物理灯光分开检查。

一旦写失败,程序释放LEDC并将GPIO4拉低,不在后台悄悄继续假装已经调光。Serial打印出来的frequency是请求参数,不是驱动外部波形的测量值。

代码 / 配置
// SPDX-License-Identifier: MIT
#include <Arduino.h>
constexpr uint8_t LED_PIN = 4, BUTTON_PIN = 5;
constexpr uint32_t FREQUENCY = 5000;
constexpr uint8_t RESOLUTION = 8;
constexpr uint16_t DUTIES[] = {0, 64, 128, 192, 255};
bool candidate, stable, ready = false;
uint32_t changedAt = 0;
uint8_t level = 0;
bool applyLevel() {
  const bool ok = ledcWrite(LED_PIN, DUTIES[level]);
  Serial.printf("level=%u duty=%u/255 write=%u frequency=%lu resolution=%u\n",
    level, DUTIES[level], ok, (unsigned long)FREQUENCY, RESOLUTION);
  return ok;
}
void setup() {
  Serial.begin(115200);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  candidate = stable = digitalRead(BUTTON_PIN);
  changedAt = millis();
  ready = ledcAttach(LED_PIN, FREQUENCY, RESOLUTION);
  Serial.printf("P04 core=3.3.12 attach=%u\n", ready);
  if (ready) ready = applyLevel();
  if (!ready) { pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); }
}
void loop() {
  if (!ready) return;
  const uint32_t now = millis();
  const bool raw = digitalRead(BUTTON_PIN);
  if (raw != candidate) { candidate = raw; changedAt = now; }
  if (candidate != stable && uint32_t(now - changedAt) >= 30) {
    stable = candidate;
    if (stable == LOW) {
      // Advance to the upper boundary; another press returns to off.
      level = (level + 1) % (sizeof(DUTIES) / sizeof(DUTIES[0]));
      ready = applyLevel();
      if (!ready) { ledcDetach(LED_PIN); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); }
    }
  }
}

参考:[1] Arduino-ESP32 3.3.12:LEDC API[2] Arduino-ESP32 3.3.12:ledcAttach和ledcWrite声明

05 / 编程实验

逐档记录,不把成功返回当成波形测量

在指定环境编译本篇示例并上传,串口115200。先保存core=3.3.12、attach=1以及初始duty=0、write=1的实际日志。以下是应该执行的检查,本站没有实板或示波器结果。

  1. 按5次,记录64、128、192、255、0的顺序,确认0熄灭、255满亮,长按不连跳。
  2. 逐档记录是否能区分亮度,同时保留每次write返回值。若肉眼难辨中高档,不据此断言频率错误。
  3. 有示波器时,探头接GPIO4、地夹接同一GND,在64/128/192档测周期与高电平宽度;0与255是静态端点,不能要求出现完整PWM周期。
  4. 将频率、分辨率、core、返回值及实际测量分别填写,不以参数日志替代测量。
做到这里,你应该看到
五档输出和边界检查有记录;物理波形只有仪器测过才能标记为实测。
06 / 编程实验

按接口、资源、接线的顺序排错

找不到ledcSetup等符号时先确认正在编译哪版core;不要为让旧代码编过而同时拼入两套接口。attach=0时核对引脚及其他库对LEDC资源的占用,在只有本例的干净程序上重试。

write=0时先保留日志再RESET,程序已进入安全熄灯路径。若所有档都很亮,断电查LED是否误接3V3或GPIO旁边的电源针;若所有档都灭,先运行P01排除LED与极性问题。恢复原来的5kHz、8bit参数再重编译,不靠随机更换管脚恢复。

BACK TO THE SOURCE

资料来源与版本说明

资料与固定版本源码核对;构建、仿真与实板为分开的验证维度。资料核对日期:。资料核对、程序构建与实板测试分别记录,不互相替代。

  1. Arduino-ESP32 3.3.12:LEDC API
  2. Arduino-ESP32 3.3.12:ledcAttach和ledcWrite声明
  3. 乐鑫:ESP32-S3-DevKitC-1 v1.1,排针与RGB修订
  4. Arduino-ESP32 3.3.12:GPIO API与模式
  5. Kingbright WP7113ID规格书 V.14A:极性、10mA条件压降与额定值