您的位置:首页 > 教育 > 培训 > 免费自助建站代理_设计企业的网站_重庆seo报价_媒体软文发稿

免费自助建站代理_设计企业的网站_重庆seo报价_媒体软文发稿

2025/8/3 5:06:08 来源:https://blog.csdn.net/JackieCoo/article/details/143080635  浏览:    关键词:免费自助建站代理_设计企业的网站_重庆seo报价_媒体软文发稿
免费自助建站代理_设计企业的网站_重庆seo报价_媒体软文发稿

1. 简介

        脉宽调制器 (PWM) 广泛用于电机和电源的控制。在ESP32的PWM模块如下图所示。

        PWM外设中包含一个时钟分频器(预分频器),三个 PWM 定时器,三个 PWM 操作器和一个捕获模块。 定时器和操作器之间是可以相互绑定的,不仅能一对一,也可以一对多,多对多地绑定,这使得ESP32可以适应各种各样的应用需求。

1.1 定时器模块

        定时器模块包括预分频器和定时器。定时器模块只能使用160MHz的时钟输入,因此要通过预分配器分频到较低的频率。PWM的定时器有3个,定时器的输出既能通过自身的计数,也能通过外部的信号脉冲实现。

        定时器可以配置成递增、递减和递增递减循环计数模式,前面两种用于生成非对称PWM波,后一种用于生成对称PWM波。它们的工作原理如下:

  • 递增计数模式:定时器从0开始递增,计数器的值到达周期寄存器值时清零并重新开始计数;
  • 递减计数模式:定时器从周期寄存器的值开始递减,当计数器的值达到0时,计数器值恢复为周期寄存器的值,重新计数;
  • 递增递减循环计数模式:结合了前两种模式,定时器从0开始递增,直到达到周期值,再次递减为0。PWM周期为周期寄存器的周期值 × 2 + 1

        定时器运行时会产生以下的事件:

  • UTEP:定时器等于周期寄存器值定时器递增计数时生成的定时事件;
  • UTEZ:定时器等于0且定时器递增计数时生成的定时事件;
  • DTEP:定时器等于周期寄存器值定时器递减时生成的定时事件;
  • DTEZ:定时器等于0且定时器递减时生成的定时事件。

1.2 操作器模块

        PWM外设拥有3个操作器。它们的功能非常丰富,除了常规的生成PWM波形外;还可以生成死区用于电机控制;也可以生成PWM载波,但应用比较少。同时操作器可以接收错误事件,根据配置执行对应操作,如PWM拉高、拉低等等。

1.2.1 PWM波形生成

        操作器模块最基本的功能就是生成PWM波,不同占空比的波形由寄存器A和B来决定,它们分别决定PWMB和PWMA的波形(是反的,比较反人类)。

        下面是一个递增计数模式的波形示例,周期为6,寄存器A为3,寄存器B为5,高电平有效。

        PWMA的占空比由寄存器B决定,定时器开始计数时,PWMA为高电平;当计数器值到达寄存器B的值时,PWMA变为低电平;当定时器到达周期寄存器值,产生溢出时,电平由恢复为高电平。PWMB的占空比由寄存器A决定,流程与上面类似。

        下面再展示一个递增递减模式的例子,其他参数和上面一样。

        定时器开始计数时,PWMA输出同样为高电平,当计数器值到达寄存器B值时,输出为低电平;定时器值到达周期寄存器值时,计数器产生上溢并开始向下计数;当计数器的值再次达到寄存器B的值时,输出电平又变回高电平。PWMB的流程类似。

1.2.2 死区生成器

        在电力电子学中,常常会用到整流器和逆变器,这就涉及到了整流桥和逆变桥的应用。每个桥臂配有两个功率电子器件,例如MOSFET、IGBT等。同一桥臂上的两个MOSFET不能同时导通,否则会造成短路。所以在实际应用中,在PWM波形显示MOSFET开关已关闭后,仍需要一段时间窗口才能完全关闭MOSFET,这就是死区。

        死区生成器可以通过在波形的上升沿或下降沿增加延迟来生成死区。对信号对而言,可以生成:高电平有效互补(AHC)、低电平有效互补(ALC)、高电平有效(AH)和低电平有效(AL),4种模式,它们的形状分别如下。

1.2.3 PWM载波模块

        将PWM输出耦合到电机驱动器可能需要使用变压器隔离。变压器只提供交流信号,而PWM信号的占空比可能在0%到100%之间变化。PWM载波模块可以通过使用高频载波对其进行调制,将该信号传递给变压器。

        从上图可以看到,当PWM输出处于有效时,载波模块会进行调制输出,并且用户可以设置第一个脉冲的宽度。载波的占空比有8种选择,相当于分辨率为12.5%

        第一个脉冲的宽度有16种可能,满足公式:

T=T_{PWM\_clk}\times 8\times (PWM\_CARRIERx\_PRESCALE+1)\times (PWM\_CARRIERx\_OSHTWTH+1)

        TPMW_clk为PWM时钟周期 (PWM_clk) ;(PWM_CARRIERx_OSHTWTH + 1) 为一次性脉冲宽度值(取值范围:1-16);(PWM_CARRIERx_PRESCALE + 1) PWM载波时钟 (PC_clk) 预分频值。

1.3 故障检测模块

        故障检测模块比较简单,就是检测管脚的状态,从而发送一个故障事件,操作器再根据事件执行对应的操作。当故障事件发生时,可以配置以下的PWM输出信号状态:高、低、取反和无操作。故障操作可分为逐周期操作(CBC)和一次性操作(OST)

逐周期操作(CBC):

        故障事件来临时,根据寄存器的设置改变PWM的输出波形;当没有故障事件时,故障操作会在下一次定时器事件(如D/UTEP或D/UTEZ事件)时自动清除

一次性操作(OST):

        故障事件来临时,根据寄存器的设置改变PWM的输出波形;当没有故障事件时,故障操作会在不会清除,必须人为对寄存器取反进行清除

1.4 捕获模块

        捕获模块也较简单,配置捕获信号的极性和预分频对管脚信号进行捕获。 ESP32的PWM捕获模块有3个通道,可以配置任意的GPIO管脚进行连接;每个通道配有一个32位时间戳和一个捕获预分频器,预分频范围1-256;任何捕获通道的边沿极性(上升/下降沿)可独立选择。

2. 例程

        例程中演示PWM的输出和捕获,PWM输出25%、50%和75%占空比的波形,每2秒改变一次,输出管脚连接捕获管脚,对输出的波形进行捕获并计算占空比。

#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "driver/mcpwm_timer.h"
#include "driver/mcpwm_oper.h"
#include "driver/mcpwm_gen.h"
#include "driver/mcpwm_cap.h"
#include "driver/mcpwm_cmpr.h"#define TAG "app"#define MCPWM_FREQ 1000000
#define MCPWM_PERIOD 20000
#define DUTY_CHANGE_PERIOD_MS 2000
#define MCPWM_CAP_FREQ 1000000typedef struct {uint32_t period;uint32_t duty;
} cap_result;static QueueHandle_t noti;static bool mcpwm_capture_cb(mcpwm_cap_channel_handle_t cap_channel, const mcpwm_capture_event_data_t *edata, void *user_ctx)
{static uint32_t last_period = 0;static uint32_t last_duty = 0;static uint32_t last_pos = 0;static uint32_t cur_pos = 0;static uint32_t neg = 0;BaseType_t higher_task_woken = pdFALSE;if (edata->cap_edge == MCPWM_CAP_EDGE_POS) {last_pos = cur_pos;cur_pos = edata->cap_value;uint32_t period = cur_pos - last_pos;uint32_t duty = neg - last_pos;if (last_period != period || last_duty != duty) {  // 只有在改变时才通知cap_result result = {period, duty};xQueueSendFromISR(noti, &result, &higher_task_woken);last_period = period;last_duty = duty;}} else {neg = edata->cap_value;}return higher_task_woken == pdTRUE;
}static void capture_log_task(void *args)
{mcpwm_cap_timer_handle_t *timer = (mcpwm_cap_timer_handle_t *) args;while (1) {cap_result result = {0};if (pdTRUE == xQueueReceive(noti, &result, portMAX_DELAY)) {uint32_t cap_freq = 0;mcpwm_capture_timer_get_resolution(*timer, &cap_freq);float period = result.period * 1000.0f / cap_freq;float duty = result.duty * 100.0f / result.period;ESP_LOGI(TAG, "capture period: %.2f ms, duty: %.2f%%", period , duty);}}
}void app_main()
{/* 初始化定时器 */static mcpwm_timer_handle_t timer = NULL;mcpwm_timer_config_t timer_config = {.group_id = 0,  // 定时器组0.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,  // 默认时钟源,默认为160MHz.resolution_hz = MCPWM_FREQ,  // 频率1MHz.period_ticks = MCPWM_PERIOD,  // 周期20ms.count_mode = MCPWM_TIMER_COUNT_MODE_UP,  // 向上计数};ESP_ERROR_CHECK(mcpwm_new_timer(&timer_config, &timer));/* 初始化操作器 */static mcpwm_oper_handle_t oper = NULL;mcpwm_operator_config_t operator_config = {.group_id = 0,  // 操作器组0};ESP_ERROR_CHECK(mcpwm_new_operator(&operator_config, &oper));/* 绑定定时器与操作器 */ESP_ERROR_CHECK(mcpwm_operator_connect_timer(oper, timer));/* 初始化生成器 */static mcpwm_gen_handle_t generator = NULL;mcpwm_generator_config_t generator_config = {.gen_gpio_num = 17,  // 输出管脚};ESP_ERROR_CHECK(mcpwm_new_generator(oper, &generator_config, &generator));/* 初始化比较器 */static mcpwm_cmpr_handle_t comparator = NULL;mcpwm_comparator_config_t comparator_config = {.flags.update_cmp_on_tez = true,  // 计数器为0时更新比较器值};ESP_ERROR_CHECK(mcpwm_new_comparator(oper, &comparator_config, &comparator));ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(comparator, 5000));  // 初始占空比25%/* 初始化输出波形 */ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(generator,MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH)));ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(generator,MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparator, MCPWM_GEN_ACTION_LOW)));/* 使能波形输出 */ESP_ERROR_CHECK(mcpwm_timer_enable(timer));ESP_ERROR_CHECK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));/* 初始化捕获定时器 */static mcpwm_cap_timer_handle_t cap_timer = NULL;mcpwm_capture_timer_config_t cap_conf = {.clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT,  // 默认时钟源,默认为80MHz.group_id = 1,  // 定时器组1};ESP_ERROR_CHECK(mcpwm_new_capture_timer(&cap_conf, &cap_timer));/* 初始化捕获通道 */static mcpwm_cap_channel_handle_t cap_chan = NULL;mcpwm_capture_channel_config_t cap_ch_conf = {.gpio_num = 18,  // 捕获管脚.prescale = 1,  // 预分频系数.flags.pos_edge = true,  // 上升沿捕获.flags.neg_edge = true,  // 下降沿捕获.flags.pull_up = true,  // 内部上拉};ESP_ERROR_CHECK(mcpwm_new_capture_channel(cap_timer, &cap_ch_conf, &cap_chan));/* 注册捕获回调 */mcpwm_capture_event_callbacks_t cap_cbs = {.on_cap = mcpwm_capture_cb,};ESP_ERROR_CHECK(mcpwm_capture_channel_register_event_callbacks(cap_chan, &cap_cbs, NULL));/* 创建捕获任务 */noti = xQueueCreate(1, sizeof(cap_result));xTaskCreate(capture_log_task, "cap_task", 2048, &cap_timer, 5, NULL);/* 使能捕获 */ESP_ERROR_CHECK(mcpwm_capture_channel_enable(cap_chan));ESP_ERROR_CHECK(mcpwm_capture_timer_enable(cap_timer));ESP_ERROR_CHECK(mcpwm_capture_timer_start(cap_timer));while (1) {static int stage = 0;uint32_t tick = (stage == 0 ? 5000 : (stage == 1 ? 10000 : (stage == 2 ? 15000 : 5000)));mcpwm_comparator_set_compare_value(comparator, tick);ESP_LOGI(TAG, "duty change to %.1f%%", tick * 100.0f / MCPWM_PERIOD);stage = (stage + 1) % 3;vTaskDelay(DUTY_CHANGE_PERIOD_MS / portTICK_PERIOD_MS);}
}

        这个例程的代码量是比较大的,主要分为PWM波输出和PWM波捕获两部分。

1. PWM波生成

        第一步,先初始化定时器,初始化结构体如下。

typedef struct {int group_id;mcpwm_timer_clock_source_t clk_src;uint32_t resolution_hz;mcpwm_timer_count_mode_t count_mode;uint32_t period_ticks;int intr_priority;struct {uint32_t update_period_on_empty: 1;uint32_t update_period_on_sync: 1;} flags;
} mcpwm_timer_config_t;
  • group_id:定时器组号(0-2);
  • clk_src:定时器时钟源;
  • resolution_hz:定时器分辨率,即频率;
  • count_mode:计数模式;
  • period_ticks:周期寄存器值;
  • intr_priority:中断优先级;
  • update_period_on_empty:计数器值为0时更新周期寄存器值;
  • update_period_on_sync:同步事件更新周期寄存器值。

        第二步,初始化操作器,初始化结构体如下。

typedef struct {int group_id;int intr_priority;struct {uint32_t update_gen_action_on_tez: 1;uint32_t update_gen_action_on_tep: 1;uint32_t update_gen_action_on_sync: 1;uint32_t update_dead_time_on_tez: 1;uint32_t update_dead_time_on_tep: 1;uint32_t update_dead_time_on_sync: 1;} flags;
} mcpwm_operator_config_t;
  • group_id:操作器组号(0-2);
  • intr_priority:中断优先级;
  • update_gen_action_on_tez:当计数器值为0时更新生成器操作;
  • update_gen_action_on_tep:当计数器溢出时更新生成器操作;
  • update_gen_action_on_sync:同步事件时更新生成器操作;
  • update_dead_time_on_tez:当计数器值为0时更新死区时间;
  • update_dead_time_on_tep:当计数器溢出时更新死区时间;
  • update_dead_time_on_sync:同步事件时更新死区时间。

        第三步,绑定定时器与操作器。调mcpwm_operator_connect_timer函数即可。

        第四步,初始化生成器,初始化结构体如下。

typedef struct {int gen_gpio_num;struct {uint32_t invert_pwm: 1;uint32_t io_loop_back: 1;uint32_t io_od_mode: 1;uint32_t pull_up: 1;uint32_t pull_down: 1;} flags;
} mcpwm_generator_config_t;
  • gen_gpio_num:输出管脚;
  • invert_pwm:输出极性反转;
  • io_loop_back:输出同时连接到输入通路;
  • io_od_mode:输出开漏;
  • pull_up:内部上拉;
  • pull_down:内部下拉。

        第五步,初始化比较器,初始化结构体如下。

typedef struct {int intr_priority;struct {uint32_t update_cmp_on_tez: 1;uint32_t update_cmp_on_tep: 1;uint32_t update_cmp_on_sync: 1;} flags;
} mcpwm_comparator_config_t;
  • intr_priority:中断优先级;
  • update_cmp_on_tez:当计数器值为0时更新比较器;
  • update_cmp_on_tep:当计数器溢出时更新比较器;
  • update_cmp_on_sync:同步事件时更新比较器。

        第六步,初始化生成器规则。我这里设置两个;一个是当计数器值为0时PWM输出拉高;另一个是当比较器值到达设置值时PWM输出拉低。

        第七步,使能定时器和启动定时器。

2. PWM波捕获

        第一步,初始化捕获定时器,初始化结构体如下。

typedef struct {int group_id;mcpwm_capture_clock_source_t clk_src;uint32_t resolution_hz;
} mcpwm_capture_timer_config_t;
  • group_id:定时器组号(0-2);
  • resolution_hz:定时器分辨率,即频率(默认是不能设置的,默认80MHz频率)。

        第二步,初始化捕获通道,初始化结构体如下。

typedef struct {int gpio_num;int intr_priority;uint32_t prescale;struct extra_flags {uint32_t pos_edge: 1;uint32_t neg_edge: 1;uint32_t pull_up: 1;uint32_t pull_down: 1;uint32_t invert_cap_signal: 1;uint32_t io_loop_back: 1;uint32_t keep_io_conf_at_exit: 1;} flags;
} mcpwm_capture_channel_config_t;
  • gpio_num:捕获管脚号;
  • prescale:捕获分频;
  • pos_edge:上升沿捕获;
  • neg_edge:下降沿捕获;
  • pull_up:内部上拉;
  • pull_down:内部下拉;
  • invert_cap_signal:捕获信号极性反转;
  • io_loop_back:捕获信号同时连接到管脚输出;
  • keep_io_conf_at_exit:当捕获通道删除时保持GPIO配置。

        第三步,注册捕获回调,主要用于获取捕获数据并发送给上层。捕获回调可以获取到捕获的信号沿和当前捕获计数器的值。当捕获到下降沿时保存值,当捕获到上升沿时发送数据;数据有两个,一个是下降沿与上一个上升沿的差值,另一个是当前上升沿与上一个上升沿的差值,通过这两个值就可以计算PWM波的周期和占空比。

        第四步,创建捕获任务(可选)。任务负责接收回调函数发来的数据并计算出周期和占空比。

        第五步,使能捕获通道、使能捕获定时器和启动捕获定时器。

        主循环主要就是定时改变PWM输出的占空比,下面就是程序运行的log。

版权声明:

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

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