您的位置:首页 > 教育 > 培训 > RP2040 C SDK PWM功能使用

RP2040 C SDK PWM功能使用

2024/11/9 7:49:43 来源:https://blog.csdn.net/weixin_42880082/article/details/142200985  浏览:    关键词:RP2040 C SDK PWM功能使用

RP2040 C SDK PWM功能使用


  • 📍参考文档:https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#group_hardware_pwm

📑RP2040 PWM概述

脉宽调制(PWM)是一种由数字信号提供一个平滑变化的平均电压的方案。这是通过一定的控制宽度的正脉冲实现的。花费在高的时间的比例被称为占空比。这可用于近似模拟输出,或控制开关模式电源电子设备。

RP2040 PWM块有8个相同的切片。每个切片可以驱动两个PWM输出信号,或测量一个输入信号的频率或占空比。这就总共提供了多达16个可控的PWM输出。所有30个GPIO大头针都可以由PWM块驱动。

  • RP2040的PWM只有向上计数法和中心对齐,

在这里插入图片描述

  • 图:单个PWM计数。一个16位的计数器从0计数到编程值,然后装载值到零,或再次计数,中心对齐模式。A和B根据当前的计数值和预先编程的A和B的阈值输出高低转换。计数器基于许多事件前进:它可能是自由移动的,或者通过B引脚上的输入信号的电平或边缘进行门控。分数分法器减慢总计数率,以更精细地控制输出频率。
  • 每个PWM通道都配有以下设备:
  • 16位计数器
  • 8.4分数时钟分频器两个独立的输出通道,占空比从0%到100%包括.
  • Dual slope and trailing edge modulation双斜率和后边沿调制。
    在这里插入图片描述
  • Edge-sensitive input mode for frequency measurement.频率测量
  • Level-sensitive input mode for duty cycle measurement。占空比测量
  • Configurable counter wrap value。可配置计数器的装载值
    • Wrap and level registers are double buffered and can be changed race-free while PWM is running.带双缓冲的,可以在PWM运行时改变更改。
  • Interrupt request and DMA request on counter wrap.计数器带中断请求和DMA请求功能
  • Phase can be precisely advanced or retarded while running (increments of one count)。

Slices can be enabled or disabled simultaneously via a single, global control register. The slices then run in perfect lockstep, so that more complex power circuitry can be switched by the outputs of multiple slices.可以通过单个全局控制寄存器同时启用或禁用通道。然后,这些切片以完美的锁步方式运行,以便通过多个切片的输出,可以对更复杂的电源电路进行切换。

📘PWM编程模式

  • RP2040上的所有30个GPIO引脚都可用于PWM:
    在这里插入图片描述
  • 将PWM通道映射到RP2040上的GPIO引脚。这也显示在主GPIO函数表中,
  • The 16 PWM channels (8 2-channel slices) appear on GPIO0 to GPIO15, in the order PWM0 A, PWM0 B, PWM1 A…
  • This repeats for GPIO16 to GPIO29. GPIO16 is PWM0 A, GPIO17 is PWM0 B, so on up to PWM6 B on GPIO29
  • The same PWM output can be selected on two GPIO pins; the same signal will appear on each GPIO.
  • If a PWM B pin is used as an input, and is selected on multiple GPIO pins, then the PWM slice will see the logical
    OR of those two GPIO inputs
通道和API函数
  • 🌿uint pwm_gpio_to_slice_num(uint gpio):获取PWM输出引脚对应的通道号

📐PWM频率计算和配置方法

  • RP2040默认时钟频率为125MHz.当然你可以使用set_sys_clock_khz(133000, true); 配置到133MHz.
    在这里插入图片描述
  • 🌿PWM频率 = 系统时钟频率/分频系数整数倍/计数值.
pwm_set_clkdiv(slice_num,125);//设置125分频,也就是1MHz(默认系统时钟125MHz)
  • ✨注意:每个通道的整数位分频器为8位,最大值:255,分数位分频器为4位。频率可设置范围: 7.5Hz -125MHz system clock.
    在这里插入图片描述
  • 📏分频系数带分数分频系数时
    在这里插入图片描述
  • 🌿PWM占空比
 pwm_set_clkdiv	(slice_num,125); // Set the clock divider to 125 (125MHz / 125 = 1MHz)// Set period of 1000 cycles (0 to 999 inclusive)pwm_set_wrap(slice_num, 999);//1000,000/1000=1KHz// Set channel A output high for one cycle before droppingpwm_set_chan_level(slice_num, PWM_CHAN_A, 250);//25% duty cycle// Set initial B output high for three cycles before droppingpwm_set_chan_level(slice_num, PWM_CHAN_B, 750);//75% duty cycle

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

📗手册上对PWM例程介绍

  • 📍Pico Examples: https://github.com/raspberrypi/pico-examples/blob/master/pwm/hello_pwm/hello_pwm.c Lines 15 - 29
// Output PWM signals on pins 0 and 1#include "pico/stdlib.h"
#include "hardware/pwm.h"int main() {/// \tag::setup_pwm[]// Tell GPIO 0 and 1 they are allocated to the PWMgpio_set_function(0, GPIO_FUNC_PWM);gpio_set_function(1, GPIO_FUNC_PWM);// Find out which PWM slice is connected to GPIO 0 (it's slice 0)uint slice_num = pwm_gpio_to_slice_num(0);// Set period of 4 cycles (0 to 3 inclusive)pwm_set_wrap(slice_num, 3);// Set channel A output high for one cycle before droppingpwm_set_chan_level(slice_num, PWM_CHAN_A, 1);// Set initial B output high for three cycles before droppingpwm_set_chan_level(slice_num, PWM_CHAN_B, 3);// Set the PWM runningpwm_set_enabled(slice_num, true);/// \end::setup_pwm[]// Note we could also use pwm_set_gpio_level(gpio, x) which looks up the// correct slice and channel for a given GPIO.
}
  • 🧬波形图
    在这里插入图片描述

计数器从0到3重复计数,这被配置为TOP值。因此,输出波的周期为4。输出A在4中1周期高,所以平均输出电压为IO电源电压的1/4。输出B高,每4个有3个周期。注意,A和B的上升边总是对齐的。

  • 🌟 频率计算:125MHz/4= 31.25MHz,频率较高时,实际输出的占空比误差较大。
    在这里插入图片描述
    在这里插入图片描述
📝250KHz频率PWM输出

/*CMSIS-DAP烧录命令:openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c  "adapter speed 5000"-c "program RP2040_PWM.elf verify reset exit"jlink命令: openocd -f interface/jlink.cfg -f target/rp2040.cfg  -c  "adapter speed 2000" -c  "program RP2040_PWM.elf verify reset exit"// Output PWM signals on pins 0 and 1
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/uart.h"
#include "hardware/gpio.h"
#include "hardware/divider.h"
#include "hardware/clocks.h"
#include "hardware/pwm.h"#define BUILTIN_LED PICO_DEFAULT_LED_PIN // LED is on the same pin as the default LED 25int main()
{//set_sys_clock_khz(133000, true); // 325usstdio_init_all();sleep_ms(2500);printf("PWM test!\n");// uart_init(UART_ID, BAUD_RATE);// GPIO initialisation.gpio_init(BUILTIN_LED);gpio_set_dir(BUILTIN_LED, 1);gpio_pull_up(BUILTIN_LED);// Tell GPIO 6 and 7 they are allocated to the PWMgpio_set_function(6, GPIO_FUNC_PWM);gpio_set_function(7, GPIO_FUNC_PWM);// Find out which PWM slice is connected to GPIO 0 (it's slice 0)uint slice_num = pwm_gpio_to_slice_num(6);pwm_set_clkdiv(slice_num,125); // Set the clock divider to 125 (125MHz / 125 = 1MHz)// Set period of 4 cycles (0 to 3 inclusive)pwm_set_wrap(slice_num, 3);//250,000// Set channel A output high for one cycle before droppingpwm_set_chan_level(slice_num, PWM_CHAN_A, 1);//25% duty cycle// Set initial B output high for three cycles before droppingpwm_set_chan_level(slice_num, PWM_CHAN_B, 3);//75% duty cycle// Set the PWM runningpwm_set_enabled(slice_num, true);// Note we could also use pwm_set_gpio_level(gpio, x) which looks up the// correct slice and channel for a given GPIO.while (true){sleep_ms(1000);gpio_xor_mask(1ul << BUILTIN_LED); // Toggle the LED// tight_loop_contents();//    measure_freqs();}}static void measure_freqs(void)
{uint f_pll_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_SYS_CLKSRC_PRIMARY);uint f_pll_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_USB_CLKSRC_PRIMARY);uint f_rosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC);uint f_clk_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);uint f_clk_peri = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_PERI);uint f_clk_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_USB);uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC);uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC);printf("pll_sys  = %dkHz\n", f_pll_sys);printf("pll_usb  = %dkHz\n", f_pll_usb);printf("rosc     = %dkHz\n", f_rosc);printf("clk_sys  = %dkHz\n", f_clk_sys);printf("clk_peri = %dkHz\n", f_clk_peri);printf("clk_usb  = %dkHz\n", f_clk_usb);printf("clk_adc  = %dkHz\n", f_clk_adc);printf("clk_rtc  = %dkHz\n", f_clk_rtc);// Can't measure clk_ref / xosc as it is the ref
}
  • 输出的PWM占空比相当准确。
    在这里插入图片描述
    在这里插入图片描述

📙电平检测或边沿信号检测

在这里插入图片描述

  • PWM触发事件选择。当计数器的启用输入高时,计数器前进,该启用分两个顺序阶段生成。首先,四种事件类型中的任何一种(总是打开,引脚B高,引脚B上升,引脚B下降)都可以为分数时钟分频器的启用脉冲。分配器可以在将启用脉冲传递到计数器之前降低启用脉冲的速率。
  • 每当启用触发时都将连续计数。还有其他三种选择:
  • 当在B针脚计数上检测到一个高水平时,连续计数一次,
  • 在B针脚上检测到每个上升边计数一次,
  • 在B针脚上检测到每一个下降边一次
  • ✨检测引脚只能安排在B通道引脚上:
    在这里插入图片描述
📑脉宽占空比测量例程

/*CMSIS-DAP烧录命令:openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c  "adapter speed 5000"-c "program RP2040_PWM.elf verify reset exit"jlink命令: openocd -f interface/jlink.cfg -f target/rp2040.cfg  -c  "adapter speed 2000" -c  "program RP2040_PWM.elf verify reset exit"// Output PWM signals on pins 6 and 7
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/uart.h"
#include "hardware/gpio.h"
#include "hardware/divider.h"
#include "hardware/clocks.h"
#include "hardware/pwm.h"#define UART_ID uart0
#define BAUD_RATE 9600
#define BUILTIN_LED PICO_DEFAULT_LED_PIN // LED is on the same pin as the default LED 25#define freq_CPU 125000000 // 125MHzconst uint OUTPUT_PIN = 7;
const uint MEASURE_PIN = 3;float measure_duty_cycle(uint gpio); // Measure duty cycle of PWM signal on specified GPIO
static void measure_freqs(void);int main()
{//set_sys_clock_khz(133000, true); // 325usstdio_init_all();sleep_ms(2500);printf("PWM test!\n");// uart_init(UART_ID, BAUD_RATE);// GPIO initialisation.gpio_init(BUILTIN_LED);gpio_set_dir(BUILTIN_LED, 1);gpio_pull_up(BUILTIN_LED);// Tell GPIO 6 and 7 they are allocated to the PWMgpio_set_function(6, GPIO_FUNC_PWM);gpio_set_function(7, GPIO_FUNC_PWM);// Find out which PWM slice is connected to GPIO 6 (it's slice 6)uint slice_num = pwm_gpio_to_slice_num(6);pwm_set_clkdiv	(	slice_num,125); // Set the clock divider to 125 (125MHz / 125 = 1MHz)// Set period of 4 cycles (0 to 3 inclusive)pwm_set_wrap(slice_num, 3);//250,000// Set channel A output high for one cycle before droppingpwm_set_chan_level(slice_num, PWM_CHAN_A, 1);//25% duty cycle// Set initial B output high for three cycles before droppingpwm_set_chan_level(slice_num, PWM_CHAN_B, 3);//75% duty cycle// Set the PWM runningpwm_set_enabled(slice_num, true);float output_duty_cycle = (float)3.0f/4; // 75% duty cyclewhile (true){sleep_ms(1000);gpio_xor_mask(1ul << BUILTIN_LED); // Toggle the LEDfloat measured_duty_cycle = measure_duty_cycle(MEASURE_PIN);printf("Output duty cycle = %.1f%%, measured input duty cycle = %.1f%%\n",output_duty_cycle * 100.f, measured_duty_cycle * 100.f);// tight_loop_contents();//    measure_freqs();}}float measure_duty_cycle(uint gpio) {// Only the PWM B pins can be used as inputs.assert(pwm_gpio_to_channel(gpio) == PWM_CHAN_B);uint slice_num = pwm_gpio_to_slice_num(gpio);// Count once for every 100 cycles the PWM B input is highpwm_config cfg = pwm_get_default_config();pwm_config_set_clkdiv_mode(&cfg, PWM_DIV_B_HIGH);pwm_config_set_clkdiv(&cfg, 100);pwm_init(slice_num, &cfg, false);gpio_set_function(gpio, GPIO_FUNC_PWM);pwm_set_enabled(slice_num, true);sleep_ms(10);pwm_set_enabled(slice_num, false);float counting_rate = clock_get_hz(clk_sys) / 100;float max_possible_count = counting_rate * 0.01;return pwm_get_counter(slice_num) / max_possible_count;
}static void measure_freqs(void)
{uint f_pll_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_SYS_CLKSRC_PRIMARY);uint f_pll_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_USB_CLKSRC_PRIMARY);uint f_rosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC);uint f_clk_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);uint f_clk_peri = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_PERI);uint f_clk_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_USB);uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC);uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC);printf("pll_sys  = %dkHz\n", f_pll_sys);printf("pll_usb  = %dkHz\n", f_pll_usb);printf("rosc     = %dkHz\n", f_rosc);printf("clk_sys  = %dkHz\n", f_clk_sys);printf("clk_peri = %dkHz\n", f_clk_peri);printf("clk_usb  = %dkHz\n", f_clk_usb);printf("clk_adc  = %dkHz\n", f_clk_adc);printf("clk_rtc  = %dkHz\n", f_clk_rtc);// Can't measure clk_ref / xosc as it is the ref
}

在这里插入图片描述

🎉利用RP2040 PWM Arduino库实现进行移植使用

  • 🥕RP2040 C SDK开发本身是支持C/C++开发方式的,移植对应的Arduino库代码使用修改的地方就不多。
  • 📍RP2040 PWM库:https://github.com/khoih-prog/RP2040_PWM

将相关打印的函数注释掉,包含头文件进来即可使用,使用方法就按照Arduino中PWM例程将相关代码拷贝过来即可。

  • 🍁工程文件:
    在这里插入图片描述

/*CMSIS-DAP烧录命令:openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c  "adapter speed 5000"-c "program RP2040_PWM_EXLIB.elf verify reset exit"jlink命令: openocd -f interface/jlink.cfg -f target/rp2040.cfg  -c  "adapter speed 2000" -c  "program RP2040_PWM_EXLIB.elf verify reset exit"*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/uart.h"
#include "hardware/gpio.h"
#include "hardware/divider.h"
#include "hardware/clocks.h"
#include "hardware/pwm.h"#define _PWM_LOGLEVEL_ 3
#include "RP2040_PWM.h"//库头文件导入进来#define BUILTIN_LED PICO_DEFAULT_LED_PIN // LED is on the same pin as the default LED 25
// creates pwm instance
RP2040_PWM *PWM_Instance;float frequency = 20000;
float dutyCycle = 60;#define pinToUse 6int main()
{stdio_init_all();// GPIO initialisation.gpio_init(BUILTIN_LED);gpio_set_dir(BUILTIN_LED, 1);gpio_pull_up(BUILTIN_LED);// Tell GPIO 6 and 7 they are allocated to the PWM//  gpio_set_function(6, GPIO_FUNC_PWM);//   gpio_set_function(7, GPIO_FUNC_PWM);// assigns pin 6 , with frequency of 20 KHz and a duty cycle of 0%PWM_Instance = new RP2040_PWM(pinToUse, 20000, 0);while (true){PWM_Instance->setPWM(pinToUse, frequency, dutyCycle);sleep_ms(1000);gpio_xor_mask(1ul << BUILTIN_LED); // Toggle the LED// sleep_ms(1000);// frequency = 20000;// dutyCycle = 90;// PWM_Instance->setPWM(pinToUse, frequency, dutyCycle);// sleep_ms(1000);// dutyCycle = 10;// PWM_Instance->setPWM(pinToUse, frequency, dutyCycle);tight_loop_contents();}//  return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com