ESP32 - Micropython ESP-IDF 雙線教程 脈寬調制(PWM)
- PWM 的基本原理
- PWM 的應用
- PWM 的優點
- PWM 的實現方式
- ESP32-micropython 中的 PWM 功能
- 使用 micropython 控制 PWM 的代碼示例
- 代碼介紹
- ESP32-IDF 中的 PWM 功能
- 1. 初始化配置函數
- 2. 引腳綁定函數
- 3. 占空比設置函數
- 4. 讀取函數
- 5. 更改頻率函數
- 6. 其他功能函數
- 歸納
- 1. 初始化PWM
- 2. 配置GPIO引腳
- 3. 編寫呼吸效果函數
- 4. 編寫主循環
- 示例代碼
脈寬調制(PWM,Pulse Width Modulation)是一種模擬控制技術,通過數字手段來產生模擬效果。它基于一種思路:通過對一系列脈沖的寬度進行調制,從而等效地獲得所需要的波形(含形狀和幅值)。在電子電路中,PWM 波形通常用于控制模擬電路,因為它具有比傳統模擬方法更高的分辨率和更簡單的電路結構。
PWM 的基本原理
PWM 的基本原理是在一個固定的周期(或稱為“載波周期”)內,改變脈沖信號的高電平時間(或稱為“占空比”)來模擬不同的模擬信號。占空比是指在一個周期內,高電平時間(脈沖寬度)與整個周期時間的比值。例如,如果占空比為 50%,則在一個周期內,高電平時間等于低電平時間。
PWM 的應用
PWM 在許多領域都有廣泛的應用,包括但不限于:
-
LED 亮度控制:通過改變 PWM 的占空比,可以控制 LED 的平均電流,從而控制其亮度。這種方法比傳統的模擬電壓控制更為精確和高效。
-
電機速度控制:PWM 可以用于控制直流電機或步進電機的速度。通過改變 PWM 的占空比,可以控制電機的平均輸入電壓,從而控制其轉速。
-
音頻放大:PWM 可以用于音頻放大器的功率控制。與傳統的線性放大器相比,PWM 放大器具有更高的效率和更低的失真。
-
電源管理:PWM 可以用于電源管理中的電壓調節和電流控制。例如,在計算機電源的 DC-DC 轉換器中,PWM 用于控制輸出電壓。
-
通信和信號處理:在某些通信和信號處理系統中,PWM 可以用于編碼和解碼信息。
PWM 的優點
-
分辨率高:PWM 的分辨率僅受限于載波頻率和脈沖寬度的精度。通過提高載波頻率和使用高精度的脈沖寬度控制,可以實現非常高的分辨率。
-
效率高:由于 PWM 是一種數字控制方法,因此它可以利用數字電路的高效性。與傳統的模擬控制方法相比,PWM 控制通常具有更高的效率。
-
靈活性強:PWM 可以很容易地通過改變占空比來模擬不同的模擬信號。這使得 PWM 在許多應用中都非常靈活和方便。
-
抗干擾能力強:由于 PWM 是一種數字信號,因此它具有較強的抗干擾能力。即使在存在噪聲和干擾的情況下,PWM 信號也能保持較好的穩定性和可靠性。
PWM 的實現方式
PWM 的實現方式有很多種,包括軟件 PWM 和硬件 PWM。軟件 PWM 是通過編程來產生 PWM 信號的方法,它通常使用定時器中斷來周期性地改變脈沖的寬度。硬件 PWM 是通過專門的硬件電路來產生 PWM 信號的方法,它通常具有更高的精度和更低的噪聲。在 ESP32 這樣的微控制器中,通常提供了硬件 PWM 支持,使得用戶可以方便地實現 PWM 控制。
ESP32-micropython 中的 PWM 功能
在 ESP32-micropython 中,可以使用 machine
模塊中的 PWM
類來創建和操作 PWM 信號。PWM 對象可以配置為不同的頻率和占空比,以產生所需的輸出信號。
使用 micropython 控制 PWM 的代碼示例
以下是一個簡單的示例,展示了如何使用 ESP32-micropython 和 GPIO 來控制一個 LED 的亮度,模擬呼吸效果。我們將使用一個按鈕(連接到另一個 GPIO)來改變呼吸速度。
import machine
import utime# 配置 PWM 引腳和頻率
led_pin = machine.Pin(2, machine.Pin.OUT) # 假設 LED 連接到 GPIO 2
pwm = machine.PWM(led_pin)
pwm.freq(1000) # 設置 PWM 頻率為 1kHz# 配置按鈕引腳
button_pin = machine.Pin(0, machine.Pin.IN, machine.Pin.PULL_UP) # 假設按鈕連接到 GPIO 0,并啟用上拉電阻# 呼吸效果函數
def breathe(brightness_max, speed):brightness = 0increment = brightness_max / 10 # 分為 10 步增加/減少亮度while True:for i in range(brightness_max, 0, -increment):pwm.duty_u16(int(i * 65535 / brightness_max)) # 設置 PWM 占空比utime.sleep_ms(speed) # 等待一段時間以控制呼吸速度for i in range(0, brightness_max, increment):pwm.duty_u16(int(i * 65535 / brightness_max))utime.sleep_ms(speed)# 初始呼吸速度
speed = 50 # 毫秒try:while True:if not button_pin.value(): # 檢測到按鈕按下# 等待按鈕釋放while not button_pin.value():pass# 改變呼吸速度speed = speed * 2 if speed < 200 else 50 # 如果速度小于 200ms,則加倍;否則重置為 50msprint("Changed breath speed to:", speed, "ms")breathe(255, speed) # 調用呼吸效果函數
except KeyboardInterrupt:pwm.deinit() # 清理 PWM 對象machine.reset() # 重啟設備
代碼介紹
- 導入必要的模塊:我們導入了
machine
模塊,用于訪問 ESP32 的硬件功能,以及utime
模塊,用于精確的時間控制。 - 配置 PWM 和按鈕引腳:我們設置了 LED 和按鈕連接的 GPIO 引腳,并初始化了 PWM 對象,設置了其頻率。
- 定義呼吸效果函數:這個函數通過改變 PWM 的占空比來模擬呼吸效果。它使用兩個嵌套的 for 循環來逐漸增加和減少亮度。
- 主循環:在主循環中,我們不斷調用呼吸效果函數。當檢測到按鈕按下時,我們改變呼吸速度。注意,我們使用了簡單的去抖動邏輯來確保只檢測一次按鈕按下。
- 異常處理:我們使用
try-except
塊來處理可能的 KeyboardInterrupt 異常(例如,用戶按下了復位按鈕)。在異常處理程序中,我們清理了 PWM 對象并重啟了設備。
ESP32-IDF 中的 PWM 功能
ESP32的PWM庫函數主要用于配置和控制PWM(脈寬調制)信號。這些函數通常是在ESP-IDF(Espressif IoT Development Framework)中提供的,以下是對ESP32 PWM庫函數的一些講解:
1. 初始化配置函數
- ledcSetup(uint8_t channel, uint32_t freq, uint8_t resolution_bits)
- 功能:設置PWM通道的頻率和分辨率。
- 參數:
channel
:PWM通道號,范圍從0到15。freq
:PWM頻率,最大頻率由公式80000000 / 2^bit_num
給出,其中bit_num
是分辨率位數。resolution_bits
:PWM分辨率位數,支持1到16位。分辨率和頻率成反比。
2. 引腳綁定函數
- ledcAttachPin(uint8_t pin, uint8_t channel)
- 功能:將GPIO引腳綁定到指定的PWM通道。
- 參數:
pin
:要綁定的GPIO引腳號。channel
:PWM通道號,與ledcSetup
函數中設置的通道對應。
3. 占空比設置函數
- ledcWrite(uint8_t channel, uint32_t duty)
- 功能:設置指定PWM通道的占空比。
- 參數:
channel
:PWM通道號。duty
:占空比值,與PWM分辨率有關。
4. 讀取函數
- ledcRead(uint8_t channel)
- 功能:讀取指定PWM通道的當前占空比值。
- 參數:
channel
,PWM通道號。
5. 更改頻率函數
- ledcChangeFrequency(uint8_t chan, uint32_t freq, uint8_t bit_num)
- 功能:更改PWM通道的頻率和分辨率。
- 參數:
chan
:PWM通道號。freq
:新的PWM頻率。bit_num
:新的PWM分辨率位數。
6. 其他功能函數
- ledcWriteTone(通道,頻率) 和 ledcWriteNote(channel, note, oc)(注意:這些函數可能在某些庫版本中不存在或名稱略有不同)
- 功能:這些函數允許開發者以特定的頻率或音符播放PWM信號,通常用于音頻應用。
歸納
- 初始化:使用
ledcSetup
函數設置PWM通道的頻率和分辨率。 - 引腳綁定:使用
ledcAttachPin
函數將GPIO引腳綁定到PWM通道。 - 占空比控制:使用
ledcWrite
函數設置PWM通道的占空比。 - 讀取:使用
ledcRead
函數讀取PWM通道的當前占空比。 - 更改頻率:使用
ledcChangeFrequency
函數更改PWM通道的頻率和分辨率(如果需要)。 - 其他功能:使用其他函數(如
ledcWriteTone
和ledcWriteNote
)實現特定應用需求。
請注意,以上函數和參數是基于ESP-IDF庫的一般描述,實際使用時可能需要根據具體的庫版本和開發環境進行調整。建議查閱ESP-IDF的官方文檔以獲取最準確和最新的信息。
1. 初始化PWM
首先,需要初始化PWM模塊,并配置PWM通道的參數,如頻率、占空比等。
2. 配置GPIO引腳
需要配置用于PWM輸出的GPIO引腳,以及用于按鈕輸入的GPIO引腳。
3. 編寫呼吸效果函數
這個函數將循環改變PWM的占空比,以模擬呼吸效果。
4. 編寫主循環
在主循環中,檢測按鈕的輸入,并根據需要改變呼吸速度。
示例代碼
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/pwm.h"
#include "driver/gpio.h"#define LED_PWM_CHANNEL 0 // 假設使用PWM通道0
#define LED_GPIO_NUM 2 // 假設LED連接到GPIO 2
#define BUTTON_GPIO_NUM 0 // 假設按鈕連接到GPIO 0
#define PWM_HZ 1000// PWM頻率設置為1kHz
#define BREATHE_MAX 1023// 占空比最大值(10位PWM)static void breathe_led(uint16_t max_brightness, uint32_t speed_ms);void app_main(void)
{// 初始化PWMpwm_config_t pwm_config = {.freq_hz = PWM_HZ,.duty_mode = PWM_DUTY_MODE_MS,.intr_mode = PWM_INTR_DISABLE,.output_select_low = PWM_OUTPUT_LOW_HIGH,.clk_sel = PWM_SEL_APB_CLK,};pwm_init(LED_PWM_CHANNEL, &pwm_config, 1, NULL);pwm_set_pin(LED_PWM_CHANNEL, LED_GPIO_NUM);// 初始化GPIO(按鈕)gpio_pad_select_gpio(BUTTON_GPIO_NUM);gpio_set_direction(BUTTON_GPIO_NUM, GPIO_MODE_INPUT);gpio_set_pull_mode(BUTTON_GPIO_NUM, GPIO_PULLUP_ONLY);// 初始呼吸速度uint32_t speed_ms = 50;// 呼吸效果主循環while (1) {if (gpio_get_level(BUTTON_GPIO_NUM) == 0) { // 檢測到按鈕按下// 等待按鈕釋放(簡單去抖動)vTaskDelay(pdMS_TO_TICKS(20));if (gpio_get_level(BUTTON_GPIO_NUM) == 0) {// 改變呼吸速度speed_ms = (speed_ms < 200) ? speed_ms * 2 : 50;printf("Changed breath speed to: %d ms\n", speed_ms);// 稍微等待以確保按鈕完全釋放vTaskDelay(pdMS_TO_TICKS(20));}}breathe_led(BREATHE_MAX, speed_ms);}
}static void breathe_led(uint16_t max_brightness, uint32_t speed_ms)
{uint16_t brightness = 0;uint16_t increment = max_brightness / 10; // 分為10步增加/減少亮度while (1) {for (brightness = 0; brightness <= max_brightness; brightness += increment) {pwm_set_duty(LED_PWM_CHANNEL, 0, brightness);vTaskDelay(pdMS_TO_TICKS(speed_ms));}for (brightness = max_brightness; brightness > 0; brightness -= increment) {pwm_set_duty(LED_PWM_CHANNEL, 0, brightness);vTaskDelay(pdMS_TO_TICKS(speed_ms));}}
}