使用ESP32 Arduino開發平台3.x版製作呼吸燈效果

超圖解ESP32應用實作》採用ESP32 Arduino開發平台2.0版(基於ESP-IDF 4.4),以Arduino IDE 1.8.19為例,請在「開發板管理員」中搜尋“esp32”,並且安裝2.x版,筆者安裝的是2.0.17版。

開發板管理員

2024年五月底推出的ESP32 Arduino開發平台3.0版(基於ESP-IDF 5.1),修改了一些語法,相關說明請參閱樂鑫官方的“Migration from 2.x to 3.0”(從2.x版遷移到3.0版)文件,本文將重點說明其中與PWM訊號有關的部分以及程式範例。

ESP32 Arduino開發平台3.x版的PWM訊號輸出指令改動,主要是把「通道(channel)」設置變成自動,並且刪除原先的ledcSetup()和ledcAttachPin()函式,改成ledcAttach()或ledcAttachChannel()函式。

ledcAttach()和ledcAttachChannel()函式

ledcAttach()函式用於設置PWM輸出的接腳、頻率和解析度,並自動選擇LEDC 通道,其原型如下。若設置成功,則傳回true;如果傳回false,代表發生錯誤且未配置LEDC 通道。

bool ledcAttach(uint8_t pin, uint32_t  freq, uint8_t resolution);

函式參數說明:

  • pin:PWM輸出的腳位
  • freq:PWM頻率
  • resolution:PWM的解析度,有效範圍為1~14位元(ESP32-S3為1~20 位元)。

ledcAttachChannel()函式的作用跟上面的函式相同,只是多了設置通道的“channel”參數,其原型如下:

bool ledcAttachChannel(uint8_t pin,  uint32_t freq, uint8_t resolution, int8_t channel);

輸出PWM訊號的函式同樣是ledcWrite(),但第一個參數改成“pin”(接腳編號),其原型如下。若設定PWM成功,函式將傳回 true;如果傳回false,則代表發生錯誤且未設定工作週期(佔空比)。

bool ledcWrite(uint8_t pin, uint32_t  duty);

為了提升跟其他非ESP32晶片的Arduino程式相容性,ESP32 Arduino開發平台也新增analogWrite()函式,不過,從函式的原始碼(位於esp32-hal-ledc.c)可看出,analogWrite()函式其實在內部呼叫了ledcWrite(),而且呼叫analogWrite()函式之前,仍要先執行ledcAttach()或ledcAttachChannel()設置PWM,所以在ESP32平台,沒有必要使用analogWrite()函式。

呼吸燈效果範例程式一

底下是採用ledcAttach()設置LED PWM值的呼吸燈效果程式,編譯執行此範例程式,需要把ESP32 Arduino開發平台升級到3.x版。

#define LED_PIN 5                     // LED腳位
#define MAX_BRIGHTNESS 255            // 亮度上限
#define PWM_RES 12                    // 12位元解析度
#define PWM_FREQ 5000                 // PWM頻率500Hz
#define MAXDUTY ((1 << PWM_RES) - 1)  // 工作週期上限
#define FADE_DELAY 30                 // 變化間隔時間

unsigned long lastTime = 0;

// 淡入淡出LED效果(呼吸燈)
void fadeLED() {
  static int brightness = 0;  // 亮度值
  static int fadeAmount = 5;  // 每次的亮度變化量
  int duty = (MAXDUTY / MAX_BRIGHTNESS) * min(brightness, MAX_BRIGHTNESS);

  ledcWrite(LED_PIN, duty);
  // 上面這一行可以改成:
  // analogWrite(LED_PIN, duty);

  brightness += fadeAmount;  // 調整亮度

  // 完成淡入或淡出效果後,反轉亮度變化量。
  if (brightness <= 0 || brightness >= MAX_BRIGHTNESS) {
    fadeAmount = -fadeAmount;
  }
}

void setup() {
  // 設定PWM輸出腳位、頻率和解析度
  ledcAttach(LED_PIN, PWM_FREQ, PWM_RES);
}

void loop() {
  unsigned long now = millis();

  if (now - lastTime >= FADE_DELAY) {
    lastTime = now;
    fadeLED();
  }
}

這個程式指定LEDC採用12位元解析度,因此工作週期(duty,佔空比)最大值為212 -1 = 4095;MAXDUTY透過左移運算子(<<)計算出指定解析度的最大工作週期值;亮度上限255,代表亮度有255個層次變化。

計算工作週期

呼吸燈效果範例程式二

ESP32 Arduino開發平台3.0版內建呼吸燈效果的ledcFade()函式,傳入LED腳位、起始工作週期值、目標工作週期值和變化時間,即可產生淡入(fade in,從低亮度逐漸調升到高亮度)或淡出(fade out,從高亮度逐漸降低為低亮度)效果,此函式原型如下:

bool ledcFade(uint8_t pin, uint32_t  start_duty, uint32_t target_duty, int max_fade_time_ms);

參數說明:

  • pin:LED腳位
  • start_duty:淡入淡出變化的起始工作週期。
  • target_duty:淡入淡出的目標週期。
  • max_fade_time_ms:淡入淡出的變化時間。

若配置成功,函式將傳回true;如果傳回 false,代表發生錯誤且未配置淡入淡出效果。

底下是採用ledcFade()函式編寫的呼吸燈效果,loop()將每隔3000毫秒交替「淡入」和「淡出」變化。

#define LED_PIN 5         // LED腳位
#define PWM_RES 12        // PWM解析度:12位元
#define PWM_FREQ 5000     // PWM頻率:5KHz
#define START_DUTY 0      // 起始工作週期
#define TARGET_DUTY 4095  // 目標工作週期
#define FADE_TIME 3000    // 變化時間

unsigned long lastTime = 0;
bool fadeIn = true;

void setup() {
  // 設定LED腳位、頻率和解析度,自動選擇PWM通道。
  ledcAttach(LED_PIN, PWM_FREQ, PWM_RES);
  // 設定淡出淡入變化的LED腳位、起始值、結束值、變化時間
  // 燈光的PWM值從0淡入變化到4095
  ledcFade(LED_PIN, START_DUTY, TARGET_DUTY, FADE_TIME);
  delay(FADE_TIME);  // 等到淡入結束
  fadeIn = false;
}

void loop() {
  unsigned long now = millis();

  if (now - lastTime >= FADE_TIME) {
    lastTime = now;

    if (fadeIn) {
      // 設定淡出淡入變化的LED腳位、起始值、結束值、變化時間
      // 燈光的PWM值從0淡入變化到4095
      ledcFade(LED_PIN, START_DUTY, TARGET_DUTY, FADE_TIME);
      fadeIn = false;
    } else {
      // 設定淡出淡入變化的LED腳位、起始值、結束值、變化時間
      // 燈光的PWM值從0淡入變化到4095
      ledcFade(LED_PIN, TARGET_DUTY, START_DUTY, FADE_TIME);
      fadeIn = true;
    }
  }
}

使用中斷常式的呼吸燈效果

ledcFadeWithInterrupt()同樣是執行「淡入淡出效果」的函式,但是透過中斷告知「變化完成」,函式原型如下:

bool ledcFadeWithInterrupt(uint8_t pin,  uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void  (*userFunc)(void));

參數說明:

  • pin:LED腳位
  • start_duty:淡入淡出變化的起始工作週期。
  • target_duty:淡入淡出的目標週期。
  • max_fade_time_ms:淡入淡出的變化時間。
  • 觸發中斷時呼叫的處理函式

若配置成功,函式將傳回true;如果傳回 false,代表發生錯誤且未配置淡入淡出效果。

底下是採用ledcFadeWithInterrupt ()函式編寫的呼吸燈效果,每隔3000毫秒交替「淡入」和「淡出」變化。

#define LED_PIN 5         // LED腳位
#define PWM_RES 12        // PWM解析度:12位元
#define PWM_FREQ 5000     // PWM頻率:5KHz
#define START_DUTY 0      // 起始工作週期
#define TARGET_DUTY 4095  // 目標工作週期
#define FADE_TIME 3000    // 變化時間

volatile bool fadeEnded = false;  // 表示LED變化結束與否
bool fadeOut = false;             // 是否為「淡出」狀態

// 中斷處理常式
void ARDUINO_ISR_ATTR FADE_ISR() {
  fadeEnded = true;  // 變化結束
}

void setup() {
  // 設定LED腳位、頻率和解析度,自動選擇PWM通道。
  ledcAttach(LED_PIN, PWM_FREQ, PWM_RES);

  // 使用中斷設置LED淡入效果
  // 燈光的PWM值從0淡出變化到4095
  ledcFadeWithInterrupt(LED_PIN, START_DUTY, TARGET_DUTY, FADE_TIME, FADE_ISR);
}

void loop() {
  if (fadeEnded) {  // 若「LED變化效果」結束…
    fadeEnded = false;

    if (fadeOut) {  // 若上次是「淡出」效果…
      // 開始進行「淡入」效果
      ledcFadeWithInterrupt(LED_PIN, START_DUTY, TARGET_DUTY, FADE_TIME, FADE_ISR);
      fadeOut = false;
    } else {
      // 開始進行「淡出」效果
      ledcFadeWithInterrupt(LED_PIN, TARGET_DUTY, START_DUTY, FADE_TIME, FADE_ISR);
      fadeOut = true;
    }
  }
}
Posts created 483

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top