## 用PWM控制LED亮度 ### 1. RP2040的PWM 脉宽调制(PWM)是一种数字信号提供平滑变化的平均电压的方案。这是通过一定宽度的正脉冲以一定的间隔实现的。高消耗时间的比例称为占空比。这可以用来近似模拟输出,或控制开关模式电力电子。 RP2040 PWM模块有8个相同的切片。每个薄片可以驱动两个PWM输出信号,或测量一个输入信号的频率或占空比。这提供了总共多达16个可控PWM输出。所有30个GPIO引脚都可以由PWM模块驱动。 {{ :rp2040_pwm.png |}} 单个PWM片。一个16位计数器从0计数到一些编程值,然后wrap到零,或计数回来,取决于PWM模式。A和B输出过渡高和低基于当前计数值和预编程的A和B阈值。计数器根据若干事件前进:它可以是自由运行的,或者由B引脚上输入信号的电平或边缘门控。分数分频器降低了整体计数率,以更好地控制输出频率。 A single PWM slice. A 16-bit counter counts from 0 up to some programmed value, and then wraps to zero, or counts back down again, depending on PWM mode. The A and B outputs transition high and low based on the current count value and the preprogrammed A and B thresholds. The counter advances based on a number of events: it may be free- running, or gated by level or edge of an input signal on the B pin. A fractional divider slows the overall count rate for finer control of output frequency. 每个PWM片配置如下: * 16位计数器 * 8.4分数时钟分频器 * 两个独立的输出通道,占空比从0%到100%包含 * 用于频率测量的边缘敏感输入模式 * 用于占空比测量的电平敏感输入模式 * 可配置计数器warp值 * Wrap和水平寄存器是双缓冲,而PWM运行时,可以改变中断请求和DMA请求计数器Wrap以便没有竞争 * 相位可以在运行时精确地提前或延迟(增加一个计数) 可以通过单个全局控件寄存器同时启用或禁用片。然后这些薄片完美地同步运行,这样就可以通过多个薄片的输出来切换更复杂的电源电路。 {{ :rp2040_pwm_mach.png |}} {{ :rp2040_pwm_reg.png |}} ### 2. 参考示例 使用电位器的直流电压来控制LED的亮度,LED的亮度由PWM进行控制,这个任务在MicroPython中非常简单。 # Raspberry Pi Pico LED PWM Test # led-pwm.py # POT - Pico GPIO 28 ADC2 # RED LED - Pico GPIO 25 # EETree Info&Tech 2021 # https://www.eetree.cn import machine import utime led_red = machine.PWM(machine.Pin(25)) potentiometer = machine.ADC(28) led_red.freq(1000) while True: led_red.duty_u16(potentiometer.read_u16()) 在这个脚本中需要注意的一个关键项目是我们定义 "led_red "的方式。我们将其定义为 "PWM",而不是输出。电位器的定义与上一个脚本中的方式完全相同。 现在我们已经给输出赋予了 "PWM "的属性,它继承了许多其他参数。其中之一是PWM频率,我们将其设置为1000Hz。 在true循环中,我们不断地从电位器中获取无符号的16位值,并将其传递给LEDs占空比,也方便地指定为无符号的16位整数。 这就说明了两者保持相同的编号方案的价值,不需要将模拟值,真的是0到4095,映射到占空比,真的是0到100。 运行程序,你应该可以顺利地控制红色LED段的亮度。 ### 2. 卓晴老师的例子 控制板载LED的波形是PWM运行。 from machine import Pin, PWM import time pwm = PWM(Pin(25)) pwm.freq(1000) duty = 0 direction = 1 for _ in range(16*255): duty += direction if duty > 255: duty = 255 direction = -1 elif duty < 0: duty = 0 direction = 1 pwm.duty_u16(duty*duty) time.sleep(0.001) PWM是软件PWM,它可以设置在任意管脚上。初步测试过Pin0, 15, 16等等。都具有相类似波形。 ### 3. 呼吸灯的例子 from machine import Pin, PWM,Timer LED = PWM(Pin(25)) n = 0 def breathing(t): global n LED.duty_u16(abs(32000- n*1000)) n = (n + 1) % 64 T0 = Timer(-1) T0.init(period=20, mode=Timer.PERIODIC, callback=breathing) ### 3. Maker.io上的文章 [[https://www.digikey.com/en/maker/projects/raspberry-pi-pico-and-rp2040-micropython-part-1-blink/58b3c31ac93649849b58824caa00529c|使用MicroPython点灯]] ### 4. 使用DMA来传递PWM控制字 /** * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ // Fade an LED between low and high brightness. An interrupt handler updates // the PWM slice's output level each time the counter wraps. #include "pico/stdlib.h" #include #include "pico/time.h" #include "hardware/irq.h" #include "hardware/pwm.h" #ifdef PICO_DEFAULT_LED_PIN void on_pwm_wrap() { static int fade = 0; static bool going_up = true; // Clear the interrupt flag that brought us here pwm_clear_irq(pwm_gpio_to_slice_num(PICO_DEFAULT_LED_PIN)); if (going_up) { ++fade; if (fade > 255) { fade = 255; going_up = false; } } else { --fade; if (fade < 0) { fade = 0; going_up = true; } } // Square the fade value to make the LED's brightness appear more linear // Note this range matches with the wrap value pwm_set_gpio_level(PICO_DEFAULT_LED_PIN, fade * fade); } #endif int main() { #ifndef PICO_DEFAULT_LED_PIN #warning pwm/led_fade example requires a board with a regular LED #else // Tell the LED pin that the PWM is in charge of its value. gpio_set_function(PICO_DEFAULT_LED_PIN, GPIO_FUNC_PWM); // Figure out which slice we just connected to the LED pin uint slice_num = pwm_gpio_to_slice_num(PICO_DEFAULT_LED_PIN); // Mask our slice's IRQ output into the PWM block's single interrupt line, // and register our interrupt handler pwm_clear_irq(slice_num); pwm_set_irq_enabled(slice_num, true); irq_set_exclusive_handler(PWM_IRQ_WRAP, on_pwm_wrap); irq_set_enabled(PWM_IRQ_WRAP, true); // Get some sensible defaults for the slice configuration. By default, the // counter is allowed to wrap over its maximum range (0 to 2**16-1) pwm_config config = pwm_get_default_config(); // Set divider, reduces counter clock to sysclock/this value pwm_config_set_clkdiv(&config, 4.f); // Load the configuration into our PWM slice, and set it running. pwm_init(slice_num, &config, true); // Everything after this point happens in the PWM interrupt handler, so we // can twiddle our thumbs while (1) tight_loop_contents(); #endif } ## 返回目录 [[pico_micropython|MicroPython编程PICO训练板]]