ESP32学习笔记_Peripherals(4)——MCPWM基础使用

ESP32S3学习——Motor Control Pulse Width Modulator (MCPWM) 芯片:esp32s3开发环境:espidfv4.4。 阅读详情
AI摘要

本文围绕ESP32的MCPWM展开,介绍其在电机控制、数字电源等场景的应用,阐述了MCPWM各子模块功能,如定时器、操作器、比较器等。还说明了资源配置及初始化方法,以及定时器和比较器的操作与事件,最后提及同步PWM波生成和多路PWM输出的实现。

MCPWM

摘要

ESP32 的 MCPWM(电机控制脉宽调制器)是一种多功能 PWM 生成器,广泛应用于电机控制、数字电源、LED 调光等场景,它通过多个定时器、操作器、比较器和生成器模块,实现高精度、高效率的 PWM 信号输出,并支持故障检测、同步控制和脉宽捕获等高级功能;这篇笔记介绍了 MCPWM 的原理、模块结构、初始化流程及多路 PWM 输出的实现方法,并配有代码示例


参考资料:
ESP-IDF 编程指南 MCPWM
bilibili # 没有专业术语!新手小白也能看懂的FOC科普
Espressif ESP32-S3 技术参考手册

PWM(Pulse Width Modulation,脉宽调制)是一种通过改变脉冲信号占空比来控制输出功率的技术;它在固定的频率下,通过调节高电平持续时间与整个周期时间的比例(占空比)来模拟不同的模拟电压输出

PWM 广泛应用于电机调速、LED 亮度控制、开关电源、音频信号处理等领域,具有控制精度高、效率高、数字化程度高等优点;例如,50%占空比的 PWM 信号可以提供相当于一半电源电压的平均功率输出

电机控制脉冲宽度调制器(MCPWM)
MCPWM 外设是一个多功能 PWM 生成器,集成多个子模块,在电力电子应用(如电机控制、数字电源等)中至关重要,通常适用于以下场景

  • 数字电机控制,如有刷/无刷直流电机、RC 伺服电机
  • 基于开关模式的数字电源转换
  • 功率数模转换器 (Power DAC),其中占空比等于 DAC 的模拟值
  • 计算外部脉宽,并将其转换为其他模拟值,如速度、距离
  • 为磁场定向控制 (FOC) 生成空间矢量调制 (SVPWM) 信号

FOC (Field Oriented Control,磁场定向控制) 是一种先进的电机控制算法,通过坐标变换将三相交流电机的复杂控制转换为类似直流电机的简单控制方式
将定子电流分解为产生磁通的 d 轴分量和产生转矩的 q 轴分量,实现磁通与转矩的解耦控制,从而获得高精度、快响应、高效率的电机控制性能;FOC 广泛应用于电动汽车驱动、工业伺服系统、变频家电等需要高性能电机控制的场合,是现代电机控制技术的核心算法

在使用前,需要在 ESP-IDF Project 的 main 文件夹的 CMakeLists.txt 中加入以下配置

PRIV_REQUIRES driver
// 如:
idf_component_register(SRCS "hello_world_main.c"
                    PRIV_REQUIRES spi_flash
                    INCLUDE_DIRS ""
                    PRIV_REQUIRES driver)

在这里插入图片描述

  • MCPWM Timer MCPWM 定时器模块 最终输出 PWM 信号的时间基准,也决定了其他子模块的事件时序
  • MCPWM Operator MCPWM 操作器模块 生成 PWM 波形的关键模块,它由其他子模块组成,如比较器、PWM 生成器、死区生成器和载波调制器
    • MCPWM Comparator MCPWM 比较器模块 输入时间基准值,并不断与配置的阈值进行比较;当定时器计数值等于任何一个阈值时,生成一个比较事件,MCPWM 生成器随即相应更新其电平,即控制 PWM 波形的高低电平
    • MCPWM Generator MCPWM 生成器模块 根据 MCPWM 定时器、MCPWM 比较器等子模块触发的各种事件,生成一对独立或互补的 PWM 波形
    • Dead Time 死区生成器模块 在此前生成的 PWM 边沿上插入额外的延迟。
    • Carrier Modulation 载波模块 可通过 PWM 波形生成器和死区生成器,将一个高频载波信号调制为 PWM 波形,这是控制功率开关器件的必需功能
  • MCPWM Fault MCPWM 故障检测模块 通过 GPIO 交换矩阵检测外部的故障情况;检测到故障信号时,MCPWM 操作器将强制所有生成器进入预先定义的状态,从而保护系统
  • MCPWM Sync MCPWM 同步模块 同步 MCPWM 定时器,以确保由不同的 MCPWM 生成器最终生成的 PWM 信号具有固定的相位差;可以通过 GPIO 交换矩阵和 MCPWM 定时器事件生成同步信号
  • Brake 制动控制 MCPWM 操作器支持配置检测到特定故障时生成器的制动控制方式;根据故障的严重程度,可以选择立即关闭或是逐周期调节 PWM 输出
  • MCPWM Capture MCPWM 捕获模块 独立子模块,不依赖于上述 MCPWM 操作器工作,捕获模块包括一个专用的定时器和几个独立的通道,每个通道都与 GPIO 相连;GPIO 上的脉冲触发捕获定时器以存储时间基准值,随后通过中断进行通知;此模块有助于更加精准地测量脉宽,此外,捕获定时器也可以通过 MCPWM 同步子模块进行同步

如果只是想要生成普通的 PWM 波形,只要了解以下内容:
时钟:控制频率
比较器:控制占空比
生成器:输出波形
同步模块:使波形同步

资源配置及初始化

MCPWM 定时器

调用 mcpwm_new_timer() 函数,以配置结构体 mcpwm_timer_config_t 为参数,分配一个 MCPWM 定时器为对象

结构体定义为:

配置参数描述范围/值
group_id指定 MCPWM 组 ID[0, SOC_MCPWM_GROUPS - 1]
intr_priority设置中断的优先级0:默认优先级,其他:指定优先级
clk_src设置定时器的时钟源
resolution_hz设置定时器的预期分辨率
count_mode设置定时器的计数模式
period_ticks设置定时器的周期Tick 单位
update_period_on_empty当定时器计数为零时是否更新周期值
update_period_on_sync当定时器接收同步信号时是否更新周期值
  • 不同组的定时器彼此独立

  • Tick 分辨率通过 resolution_hz 参数设置

  • 内部驱动会根据时钟源和分辨率自动设置合适的分频器

  • 分配成功后,mcpwm_new_timer() 将返回一个指向已分配定时器的指针

  • 当 MCPWM 组中没有空闲定时器时,将返回 ESP_ERR_NOT_FOUND 错误

  • 调用 mcpwm_del_timer() 函数将释放已分配的定时器

MCPWM 操作器

调用 mcpwm_new_operator() 函数,以配置结构体 mcpwm_operator_config_t 为参数,分配一个 MCPWM 操作器为对象

结构体定义为:

配置参数描述范围/值
group_id指定 MCPWM 组 ID[0, SOC_MCPWM_GROUPS - 1]
intr_priority设置中断的优先级0:默认优先级,其他:指定优先级
update_gen_action_on_tez当定时器计数为零时是否更新生成器操作
update_gen_action_on_tep当定时器计数达到峰值时是否更新生成器操作
update_gen_action_on_sync当定时器接收同步信号时是否更新生成器操作
update_dead_time_on_tez当定时器计数为零时是否更新死区时间
update_dead_time_on_tep当定时器计数达到峰值时是否更新死区时间
update_dead_time_on_sync当定时器接收同步信号时是否更新死区时间
  • 不同组的操作器彼此独立
  • 定时器通过 mcpwm_operator_connect_timer() 连接到操作器

TEZ (Timer Equal Zero) - 定时器计数为零时触发
TEP (Timer Equal Period) - 定时器计数达到峰值时触发

  • 分配成功后,mcpwm_new_operator() 将返回一个指向已分配操作器的指针否则,函数将返回错误代码
  • 当 MCPWM 组中没有空闲操作器时,将返回 ESP_ERR_NOT_FOUND 错误
  • 调用 mcpwm_del_operator() 函数将释放已分配的操作器
MCPWM 比较器

调用 mcpwm_new_comparator() 函数,以一个 MCPWM 操作器句柄和配置结构体 mcpwm_comparator_config_t 为参数,分配一个 MCPWM 比较器为对象

操作器句柄由 mcpwm_new_operator() 生成,结构体定义为:

配置参数描述范围/值
intr_priority设置中断的优先级0:默认优先级,其他:指定优先级
update_cmp_on_tez当定时器计数为零时是否更新比较阈值
update_cmp_on_tep当定时器计数达到峰值时是否更新比较阈值
update_cmp_on_sync当定时器接收同步信号时是否更新比较阈值
  • 分配成功后,mcpwm_new_comparator() 将返回一个指向已分配比较器的指针
  • 当 MCPWM 操作器中没有空闲比较器时,将返回 ESP_ERR_NOT_FOUND 错误
  • 调用 mcpwm_del_comparator() 函数将释放已分配的比较器

MCPWM 中还有另外一种比较器 —— “事件比较器”,它不能直接控制 PWM 的输出,只能用来产生 EMT 子系统中使用到的事件,事件比较器能够设置的阈值也是可配的。调用 mcpwm_new_event_comparator() 函数可以申请一个事件比较器,该函数返回的句柄类型和 mcpwm_new_comparator() 函数一样,但是需要的配置结构体是不同的。事件比较器的配置位于 mcpwm_event_comparator_config_t

MCPWM 生成器

调用 mcpwm_new_generator() 函数,以 MCPWM 操作器句柄和配置结构体 mcpwm_generator_config_t 为参数,分配一个 MCPWM 生成器为对象

结构体定义为:

配置参数描述范围/值
gen_gpio_num设置生成器使用的 GPIO 编号GPIO 引脚编号
invert_pwm设置是否反相 PWM 信号
pull_up设置是否启用内部上拉电阻
pull_down设置是否启用内部下拉电阻
  • gen_gpio_num 指定输出 PWM 信号的 GPIO 引脚,GPIO 引脚需要支持 MCPWM 功能

  • invert_pwm 启用时,PWM 信号会被反相(高电平变低电平,低电平变高电平)

  • 分配成功后,mcpwm_new_generator() 将返回一个指向已分配生成器的指针

  • 当 MCPWM 操作器中没有空闲生成器时,将返回 ESP_ERR_NOT_FOUND 错误

  • 调用 mcpwm_del_generator() 函数将释放已分配的生成器

MCPWM 同步源

同步源是用于同步 MCPWM 定时器和 MCPWM 捕获定时器的,同步源有三种类型:来自 GPIO 的同步源、由软件生成的同步源以及由 MCPWM 定时器事件生成的同步源

要分配一个 GPIO 同步源,可以调用 mcpwm_new_gpio_sync_src() 函数,并将配置结构 mcpwm_gpio_sync_src_config_t 作为参数

配置结构定义如下:

配置参数描述范围/值
group_id设置 MCPWM 组 ID[0, SOC_MCPWM_GROUPS - 1]
gpio_num设置同步源使用的 GPIO 编号GPIO 引脚编号
active_neg设置同步信号是否在下降沿上激活
pull_up设置是否启用内部上拉电阻
pull_down设置是否启用内部下拉电阻
  • 不同组的 GPIO 同步源完全独立,组 0 中的 GPIO 同步源不能被组 1 中的计时器检测到
  • gpio_num 指定用于同步信号的 GPIO 引脚

如果分配成功, mcpwm_new_gpio_sync_src() 将返回一个指向已分配的同步源对象的指针。否则,它将返回一个错误代码。具体来说,当 MCPWM 组中没有更多可用的 GPIO 同步源时,此函数将返回 ESP_ERR_NOT_FOUND 错误

要分配一个计时器事件同步源,您可以调用 mcpwm_new_timer_sync_src() 函数,以配置结构 mcpwm_timer_sync_src_config_t 作为参数。配置结构定义如下

配置参数描述
timer_event指定在什么计时器事件上生成同步信号
propagate_input_sync设置是否传播输入同步信号
  • timer_event 定义触发同步信号的计时器事件类型

  • propagate_input_sync 为启用时,输入同步信号会被路由到同步输出端

  • 同步信号可用于多个计时器之间的协调工作

  • 如果分配成功, mcpwm_new_timer_sync_src() 将返回指向已分配的同步源对象的指针

  • 如果同步源之前已从同一定时器分配,此函数将返回 ESP_ERR_INVALID_STATE 错误

要分配一个软件同步源,可以调用 mcpwm_new_soft_sync_src() 函数,并将配置结构 mcpwm_soft_sync_config_t 作为参数;目前,此配置结构保留

  • 如果分配成功, mcpwm_new_soft_sync_src() 将返回指向已分配的同步源对象的指针
  • 当没有剩余内存用于同步源对象时,此函数将返回 ESP_ERR_NO_MEM 错误
  • 为了让软件同步源生效,需要调用 mcpwm_soft_sync_activate()

调用 mcpwm_del_sync_src() 函数将释放已分配的同步源对象。此函数适用于所有类型的同步源

定时器操作和事件

定时器周期

定时器周期由 mcpwm_timer_config_t 中的 mcpwm_timer_config_t::period_ticks 参数初始化

  • 可以通过调用 mcpwm_timer_set_period() 函数在运行时更新周期
  • 新周期将根据 mcpwm_timer_config_t 中设置的 mcpwm_timer_config_t::update_period_on_empty 和 mcpwm_timer_config_t::update_period_on_sync 参数而生效
  • 如果它们都没有被设置,定时器周期将立即生效
注册定时器回调

MCPWM 定时器可以在运行时生成不同的事件

  • 如果有一些在特定事件发生时应被调用的函数,应该通过调用 mcpwm_timer_register_event_callbacks() 将函数注册到中断服务例程中
  • 回调函数原型在 mcpwm_timer_event_cb_t 中声明,所有支持的事件回调函数列表在 mcpwm_timer_event_callbacks_t
配置参数描述范围/值
on_full设置定时器计数到峰值时的回调函数函数指针
on_empty设置定时器计数到零时的回调函数函数指针
on_stop设置定时器停止时的回调函数函数指针

上述回调函数在 ISR 上下文中被调用,因此不应尝试阻塞

  • 比如可以在函数中确保仅调用带 ISR 后缀的 FreeRTOS API

mcpwm_timer_register_event_callbacks() 函数的 user_data 参数用于保存上下文,它将直接传递给每个回调函数

这个函数将延迟安装 MCPWM 计时器的中断服务,而不会启用它,它只允许在 mcpwm_timer_enable() 之前调用,否则将返回 ESP_ERR_INVALID_STATE 错误。另见启用和禁用计时器以获取更多信息

启用和禁用定时器

在进行定时器 IO 控制之前,需要先启用定时器,通过调用 mcpwm_timer_enable() ,这个函数

  • 切换计时器状态从初始化到启用
  • 如果定时器已经被 mcpwm_timer_register_event_callbacks() 懒加载(lazy installed),则启用中断服务
  • 如果选择了特定的时钟源(例如 PLL_160M 时钟),需要获取正确的电源管理锁
  • 调用 mcpwm_timer_disable() 将使定时器驱动程序恢复到初始状态,禁用中断服务并释放电源管理锁

懒加载是指资源或服务不会在系统启动时立即分配,而是在第一次真正需要使用时才进行分配和初始化

启动和停止定时器

定时器的基本 IO 操作是启动和停止。通过调用 mcpwm_timer_start_stop() 使用不同的 mcpwm_timer_start_stop_cmd_t 命令可以立即启动定时器或在一个特定事件时停止定时器。此外,您甚至可以只启动定时器一轮,这意味着定时器会计数到峰值或零,然后自动停止

分配的 MCPWM 计时器应该通过调用 mcpwm_operator_connect_timer() 连接到一个 MCPWM 操作器,以便操作器可以将其作为时间基准,并生成所需的 PWM 波

  • 确保 MCPWM 计时器和操作器在同一组中,否则,该函数将返回 ESP_ERR_INVALID_ARG 错误。

mcpwm_timer_start_stop_cmd_t MCPWM 定时器命令枚举,用于指定启动或停止定时器的方式

枚举值描述
MCPWM_TIMER_STOP_EMPTYMCPWM 定时器在下一次计数达到零时停止
MCPWM_TIMER_STOP_FULLMCPWM 定时器在下一次计数达到峰值时停止
MCPWM_TIMER_START_NO_STOPMCPWM 定时器开始计数,直到收到停止命令才停止
MCPWM_TIMER_START_STOP_EMPTYMCPWM 定时器开始计数并在下一次计数达到零时停止
MCPWM_TIMER_START_STOP_FULLMCPWM 定时器开始计数并在下一次计数达到峰值时停止
  • “零” 指的是定时器计数值为 0 的状态
  • “峰值” 指的是定时器计数达到最大值(period_ticks)的状态

比较器操作和事件

分配的 MCPWM 计时器应该通过调用 mcpwm_operator_connect_timer() 连接到一个 MCPWM 操作器,以便操作器可以将其作为时间基准,并生成所需的 PWM 波

  • 确保 MCPWM 计时器和操作器在同一组中,否则函数将返回 ESP_ERR_INVALID_ARG 错误
  • mcpwm_comparator_event_callbacks_t::on_reach 设置当定时器计数器等于比较值时,比较器的回调函数

中断

  • 回调函数会提供类型为 mcpwm_compare_event_data_t 的事件特定数据;回调函数在 ISR 上下文中被调用,因此不应尝试阻塞
  • 参数 user_data 的 mcpwm_comparator_register_event_callbacks() 函数用于保存作者自己的上下文,它将直接传递给回调函数
  • 这个函数将延迟安装 MCPWM 比较器的中断服务,而该服务只能在 mcpwm_del_comparator() 中移除

为事件比较器注册事件回调是不支持的,因为它无法产生任何中断

设置比较值

通过调用 mcpwm_comparator_set_compare_value() 在运行时设置 MCPWM 比较器的比较值

注意事项
新设置的比较值不会立即生效,更新时机由以下配置参数控制:

  • update_cmp_on_tez - 在定时器计数为零时更新
  • update_cmp_on_tep - 在定时器计数达到峰值时更新
  • update_cmp_on_sync - 在接收到同步信号时更新

确保操作器已经通过 mcpwm_operator_connect_timer() 连接到一个 MCPWM 定时器,否则将返回错误代码 ESP_ERR_INVALID_STATE

比较值不应超过定时器的计数值峰值,否则比较事件将永远不会被触发

经典 PWM 波形生成器

波形是对称还是非对称由 MCPWM 计时器的计数模式决定
波形对的 激活电平 取决于占空比较小的 PWM 波形的电平
PWM 波形的周期由定时器的周期和计数模式决定
PWM 波形的占空比由发生器的各种动作组合决定

单边非对称波形 - 主动高

static void gen_action_config(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb, mcpwm_cmpr_handle_t cmpa, mcpwm_cmpr_handle_t cmpb)
{
    // 配置生成器 A 在定时器事件上的动作
    // 当定时器向上计数到空值(EMPTY)时,将生成器 A 的输出设置为高电平
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(gena,
                    MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH)));

    // 配置生成器 A 在比较器事件上的动作  
    // 当定时器向上计数且计数值等于比较器 cmpa 的阈值时,将生成器 A 的输出设置为低电平
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(gena,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpa, MCPWM_GEN_ACTION_LOW)));

    // 配置生成器B在定时器事件上的动作
    // 当定时器向上计数到空值(EMPTY)时,将生成器 B 的输出设置为高电平
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(genb,
                    MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH)));
    
    // 配置生成器B在比较器事件上的动作
    // 当定时器向上计数且计数值等于比较器 cmpb 的阈值时,将生成器 B 的输出设置为低电平  
    ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(genb,
                    MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, cmpb, MCPWM_GEN_ACTION_LOW)));
}

Example: 同步 PWM 波生成器

ESP-IDF 官方示例项目,项目地址:mcpwm_sync

Workflow
flowchart TD
	A[系统初始化] --> B[创建定时器,并配置每个定时器]
	B --> C[创建操作器,并初始化操作器]
	C --> D[配置操作器的定时器]
	D --> E[创建比较器]
	E --> F[配置比较器,并将比较器与操作器进行绑定]
	F --> G[设定比较器的阈值]
	G --> H[创建生成器,并配置生成器]
	H --> I[将操作器与生成器进行绑定]
	I --> J[设置生成器的工作模式]
	J --> K[逐个启动定时器]
	K --> L[同步波形]
同步波形的策略

MCPWM 定时器接收到同步信号后,定时器将强制进入一个预定义的相位,该相位由计数值和计数方向共同决定。调用 mcpwm_timer_set_phase_on_sync(),设置同步相位

同步相位配置定义在 mcpwm_timer_sync_phase_config_t 结构体中:

mcpwm_timer_sync_phase_config_t::sync_src 设置同步信号源,当这个参数设置为 NULL 时,驱动器将禁用 MCPWM 定时器的同步功能
mcpwm_timer_sync_phase_config_t::count_value 设置接收同步信号后加载至计数器的值
mcpwm_timer_sync_phase_config_t::direction 设置接收同步信号后的计数方向

当 MCPWM 定时器在 MCPWM_TIMER_COUNT_MODE_UP_DOWN 模式下工作时,需要特别注意:在该模式下,计数器范围 [0 -> peak-1] 属于 递增 阶段, [peak -> 1] 属于 递减 阶段。因此,如果将 mcpwm_timer_sync_phase_config_t:: count_value 设置为零,则可能还需要将 mcpwm_timer_sync_phase_config_t:: direction 设置为 MCPWM_TIMER_DIRECTION_UP,否则,计时器将继续维持递减阶段,计数值会下溢至峰值

  • 定时器间同步适合内部多路 PWM 协同
  • 外部 GPIO 同步适合与外部设备协同
  • 软件同步提供了最大灵活性,由软件随时触发

同步时,定时器会根据配置的相位值和方向重载计数器,实现精确的同步控制。

来自 GPIO 的同步源

这种同步策略需要额外占用一个 GPIO,定时器的同步输入源也可以来自 GPIO 矩阵的外部同步信号(如 SYNCO、SYNC1、SYNC2),这样可以通过外部事件(如外部控制器信号)来同步 PWM 定时器,实现与外部系统的协同控制

static void example_setup_sync_strategy(mcpwm_timer_handle_t timers[])
{
    //    +----GPIO----+
    //    |     |      |
    //    |     |      |
    //    v     v      v
    // timer0 timer1 timer2
    gpio_config_t sync_gpio_conf = {
        // 配置 GPIO 引脚
        .mode         = GPIO_MODE_OUTPUT,        // 设置为输出模式
        .pin_bit_mask = BIT(EXAMPLE_SYNC_GPIO),  // 设置同步 GPIO 引脚
    };
    ESP_ERROR_CHECK(gpio_config(&sync_gpio_conf));  // 配置 GPIO 引脚

    ESP_LOGI(TAG, "Create GPIO sync source");
    mcpwm_sync_handle_t gpio_sync_source = NULL;  // 创建 GPIO 同步源
    // 配置 GPIO 同步源
    mcpwm_gpio_sync_src_config_t gpio_sync_config = {
        .group_id         = 0,                  // GPIO 同步源所在的 MCPWM 组
        .gpio_num         = EXAMPLE_SYNC_GPIO,  // 同步 GPIO 引脚
        .flags.pull_down  = true,               // 同步 GPIO 引脚下拉
        .flags.active_neg = false,              // 不使用负边沿触发,这意味着同步事件将在GPIO信号的上升沿时触发
    };
    ESP_ERROR_CHECK(mcpwm_new_gpio_sync_src(&gpio_sync_config, &gpio_sync_source));  // 创建 GPIO 同步源

    ESP_LOGI(TAG, "Set timers to sync on the GPIO");
    mcpwm_timer_sync_phase_config_t sync_phase_config = {
        // 配置同步相位
        .count_value = 0,                         // 同步计数值,当同步事件发生时,计数器将被重置为此值
        .direction   = MCPWM_TIMER_DIRECTION_UP,  // 向上计数
        .sync_src    = gpio_sync_source,          // 同步源
    };
    for (int i = 0; i < 2; i++) {
        ESP_ERROR_CHECK(mcpwm_timer_set_phase_on_sync(timers[i], &sync_phase_config));  // 设置定时器同步相位
    }

    ESP_LOGI(TAG, "Trigger a pulse on the GPIO as a sync event");  // 触发 GPIO 同步事件
    ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_SYNC_GPIO, 0));
    ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_SYNC_GPIO, 1));
    ESP_ERROR_CHECK(gpio_reset_pin(EXAMPLE_SYNC_GPIO));  // 重置 GPIO 引脚
}
由软件生成的同步源

通过写寄存器触发软件同步事件,软件同步允许 MCU 通过代码控制定时器同步,适合需要灵活、可编程控制同步时机的应用

static void example_setup_sync_strategy(mcpwm_timer_handle_t timers[])
{
    //       soft
    //        |
    //        v
    //    +-timer0--+
    //    |         |
    //    v         v
    // timer1    timer2
    ESP_LOGI(TAG, "Create software sync source");
    mcpwm_sync_handle_t soft_sync_source      = NULL;  // 创建软件同步源
    mcpwm_soft_sync_config_t soft_sync_config = {};    // 配置软件同步源,目前,此配置结构留待未来使用
    ESP_ERROR_CHECK(mcpwm_new_soft_sync_src(&soft_sync_config, &soft_sync_source));  // 创建软件同步源

    ESP_LOGI(TAG, "Create timer sync source to propagate the sync event");
    mcpwm_sync_handle_t timer_sync_source;  // 创建定时器同步源
    // 配置定时器同步源
    mcpwm_timer_sync_src_config_t timer_sync_config = {
        .flags.propagate_input_sync = true,  // 允许同步事件传播到其他定时器
    };
    ESP_ERROR_CHECK(mcpwm_new_timer_sync_src(timers[0], &timer_sync_config, &timer_sync_source));

    ESP_LOGI(TAG, "Set sync phase for timers");
    // 设置定时器同步相位
    mcpwm_timer_sync_phase_config_t sync_phase_config = {
        .count_value = 0,                         // 同步计数值,当同步事件发生时,计数器将被重置为此值
        .direction   = MCPWM_TIMER_DIRECTION_UP,  // 向上计数
        .sync_src    = soft_sync_source,          // 同步源
    };
    ESP_ERROR_CHECK(mcpwm_timer_set_phase_on_sync(timers[0], &sync_phase_config));  // 设置第一个定时器的同步相位
    sync_phase_config.sync_src = timer_sync_source;  // 更新同步源为定时器同步源,以便其他定时器可以接收同步事件
    for (int i = 1; i < 2; ++i) {
        ESP_ERROR_CHECK(mcpwm_timer_set_phase_on_sync(timers[i], &sync_phase_config));  // 设置其他定时器的同步相位
    }

    ESP_LOGI(TAG, "Trigger the software sync event");
    ESP_ERROR_CHECK(mcpwm_soft_sync_activate(soft_sync_source));  // 触发软件同步事件
}
由 MCPWM 定时器事件生成的同步源

每个 MCPWM 定时器都可以选择来自其他定时器的同步输出作为自己的同步输入,例如,定时器 0 可以选择定时器 1 或定时器 2 的同步输出信号,实现多个定时器之间的相位锁定和同步启动,这种方式适合多路 PWM 输出需要严格同步的场景

static void example_setup_sync_strategy(mcpwm_timer_handle_t timers[])
{
    //          +->timer1
    // (TEZ)    |
    // timer0---+
    //          |
    //          +->timer2
    ESP_LOGI(TAG, "Create TEZ sync source from timer0");
    mcpwm_sync_handle_t timer_sync_source = NULL;  // 创建定时器同步源
    // 配置定时器同步源
    mcpwm_timer_sync_src_config_t timer_sync_config = {
        .timer_event = MCPWM_TIMER_EVENT_EMPTY,  // 在定时器空事件时生成同步事件
    };

    // 创建定时器同步源,以定时器 0 为源
    ESP_ERROR_CHECK(mcpwm_new_timer_sync_src(timers[0], &timer_sync_config, &timer_sync_source));

    ESP_LOGI(TAG, "Set other timers sync to the first timer");
    mcpwm_timer_sync_phase_config_t sync_phase_config = {
        .count_value = 0,                         // 同步计数值,当同步事件发生时,计数器将被重置为此值
        .direction   = MCPWM_TIMER_DIRECTION_UP,  // 向上计数
        .sync_src    = timer_sync_source,         // 同步源
    };
    for (int i = 1; i < 2; i++) {
        ESP_ERROR_CHECK(mcpwm_timer_set_phase_on_sync(timers[i], &sync_phase_config));  // 设置定时器同步相位
    }

    ESP_LOGI(TAG, "Wait some time for the timer TEZ event");
    vTaskDelay(pdMS_TO_TICKS(10));
}

多路 PWM 输出

以 ESP32-S3 为例

ESP32-S3 有两个 MCPWM 控制器,分别为 MCPWM0 和 MCPWM1。这两个控制器每个都包含一个时钟分频器、三个 PWM 定时器、三个 PWM 操作器和一个捕获模块,可用于驱动数字马达和智能灯等
详见: Espressif ESP32-S3 技术参考手册 P1245 第 16 章

因为每个 MCPWM 操作器有两个 MCPWM 生成器,可以输出一对 PWM 波形(即两路输出),因此每个 MCPWM 外设可以输出 6 路 PWM;两个 MCPWM 外设合计,ESP32-S3 可以同时输出 12 路 MCPWM 信号

每个 MCPWM 操作器有两个比较器。每个 MCPWM 控制器包含三个操作器,因此单个 MCPWM 控制器最多有 6 个比较器。ESP32-S3 有两个 MCPWM 控制器,所以总共可以有 12 个 MCPWM 比较器。这意味着可以实现 12 路占空比独立可调的 PWM 输出,满足如多舵机控制等需求

在官方给的 mcpwm_sync 示例中,使用了一个 MCPWM 外设,其中的每个 MCPWM 生成器只生成了一路 PWM,因此,当需要程序生成 >3 路的 PWM 时有两种选择:

  1. 通过两个 MCPWM 外设进行输出
    1. 对于 >6 路的 PWM 输出或 >3 路频率不同的 PWM 输出,只能采用这种方式
  2. 通过一个 MCPWM 的两个 MCPWM 生成器分别输出
通过两个 MCPWM 外设进行多路输出
/**
 * @brief 使用两个 MCPWM 外设,每个外设输出两路 PWM 波形
 * @brief Use two MCPWM peripherals, each outputs two PWM waveforms
 */
void app_main(void)
{
    ESP_LOGI(TAG, "Create timers");
    // 创建两组定时器,分别用于两个 MCPWM 外设
    mcpwm_timer_handle_t timers_group_0[2];
    mcpwm_timer_handle_t timers_group_1[2];

    // 配置定时器
    mcpwm_timer_config_t timer_config_0 = {
        .clk_src       = MCPWM_TIMER_CLK_SRC_DEFAULT,  // 时钟源
        .group_id      = 0,                            // 定时器组ID
        .resolution_hz = EXAMPLE_TIMER_RESOLUTION_HZ,  // 定时器分辨率
        .period_ticks  = EXAMPLE_TIMER_PERIOD,         // 定时器周期
        .count_mode    = MCPWM_TIMER_COUNT_MODE_UP,    // 计数模式为向上计数
    };
    mcpwm_timer_config_t timer_config_1 = {
        .clk_src       = MCPWM_TIMER_CLK_SRC_DEFAULT,
        .group_id      = 1,
        .resolution_hz = EXAMPLE_TIMER_RESOLUTION_HZ,
        .period_ticks  = EXAMPLE_TIMER_PERIOD,
        .count_mode    = MCPWM_TIMER_COUNT_MODE_UP,
    };

    for (int i = 0; i < 2; i++) {
        ESP_ERROR_CHECK(mcpwm_new_timer(&timer_config_0, &timers_group_0[i]));
        ESP_ERROR_CHECK(mcpwm_new_timer(&timer_config_1, &timers_group_1[i]));
    }

    // 创建两组操作器,分别用于两个 MCPWM 外设
    ESP_LOGI(TAG, "Create operators");
    mcpwm_oper_handle_t operators_0[2];
    mcpwm_oper_handle_t operators_1[2];
    // 配置操作器
    mcpwm_operator_config_t operator_config_0 = {
        .group_id = 0,
        // 操作器需要在与定时器相同的组中
    };
    mcpwm_operator_config_t operator_config_1 = {
        .group_id = 1,
    };

    for (int i = 0; i < 2; ++i) {
        ESP_ERROR_CHECK(mcpwm_new_operator(&operator_config_0, &operators_0[i]));
        ESP_ERROR_CHECK(mcpwm_new_operator(&operator_config_1, &operators_1[i]));
    }

    // 设置操作器相应的定时器
    ESP_LOGI(TAG, "Connect timers and operators with each other");
    for (int i = 0; i < 2; i++) {
        ESP_ERROR_CHECK(mcpwm_operator_connect_timer(operators_0[i], timers_group_0[i]));
        ESP_ERROR_CHECK(mcpwm_operator_connect_timer(operators_1[i], timers_group_1[i]));
    }

    // 创建比较器
    ESP_LOGI(TAG, "Create comparators");
    mcpwm_cmpr_handle_t comparators_0[2];
    mcpwm_cmpr_handle_t comparators_1[2];
    mcpwm_comparator_config_t compare_config = {
        .flags.update_cmp_on_tez = true,
        // 当定时器计数为零时更新比较阈值
    };

    // 设置操作器相应的比较器
    for (int i = 0; i < 2; i++) {
        ESP_ERROR_CHECK(mcpwm_new_comparator(operators_0[i], &compare_config, &comparators_0[i]));
        ESP_ERROR_CHECK(mcpwm_new_comparator(operators_1[i], &compare_config, &comparators_1[i]));
        // 初始化每个比较器的比较值
        ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(comparators_0[i], 500));
        ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(comparators_1[i], 500));
        // 当比较器的值为设定值时,比较器将触发事件,此时定时器设置值为1000
        // 即生成的 PWM 波形的占空比为 比较值 / 定时器设定值 * 100%
    }

    // 创建生成器
    ESP_LOGI(TAG, "Create generators");
    mcpwm_gen_handle_t generators_0[2];
    mcpwm_gen_handle_t generators_1[2];
    // 配置生成器
    const int gen_gpios_0[2]            = {EXAMPLE_GEN_GPIO0, EXAMPLE_GEN_GPIO1};
    const int gen_gpios_1[2]            = {EXAMPLE_GEN_GPIO2, EXAMPLE_GEN_GPIO3};
    mcpwm_generator_config_t gen_config = {};
    for (int i = 0; i < 2; i++) {
        gen_config.gen_gpio_num = gen_gpios_0[i];
        // 设置操作器相应的生成器
        ESP_ERROR_CHECK(mcpwm_new_generator(operators_0[i], &gen_config, &generators_0[i]));
        gen_config.gen_gpio_num = gen_gpios_1[i];
        ESP_ERROR_CHECK(mcpwm_new_generator(operators_1[i], &gen_config, &generators_1[i]));
    }

    // 设置生成器的工作模式
    ESP_LOGI(TAG, "Set generator actions on timer and compare event");
    for (int i = 0; i < 2; i++) {
        ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(
            generators_0[i],
            // 当定时器计数为零且向上计数时,输出高电平
            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(
            generators_0[i],
            // 当比较匹配且向上计数时,输出低电平
            MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparators_0[i], MCPWM_GEN_ACTION_LOW)));

        ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(
            generators_1[i],
            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(
            generators_1[i],
            MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparators_1[i], MCPWM_GEN_ACTION_LOW)));
    }

    // 逐个使能并启动定时器
    ESP_LOGI(TAG, "Start timers one by one, so they are not synced");
    for (int i = 0; i < 2; i++) {
        ESP_ERROR_CHECK(mcpwm_timer_enable(timers_group_0[i]));
        ESP_ERROR_CHECK(mcpwm_timer_start_stop(timers_group_0[i], MCPWM_TIMER_START_NO_STOP));
        ESP_ERROR_CHECK(mcpwm_timer_enable(timers_group_1[i]));
        ESP_ERROR_CHECK(mcpwm_timer_start_stop(timers_group_1[i], MCPWM_TIMER_START_NO_STOP));
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    vTaskDelay(pdMS_TO_TICKS(100));

    // 在同步之前添加一个“空闲”阶段,可以帮助区分同步前后的波形
    ESP_LOGI(TAG, "Force the output level to low, timer still running");
    // 强制输出低电平,定时器仍在运行
    for (int i = 0; i < 2; i++) {
        ESP_ERROR_CHECK(mcpwm_generator_set_force_level(generators_0[i], 0, true));
        ESP_ERROR_CHECK(mcpwm_generator_set_force_level(generators_1[i], 0, true));
    }

    ESP_LOGI(TAG, "Setup sync strategy");
    // 设置同步策略
    example_setup_sync_strategy(timers_group_0);
    example_setup_sync_strategy(timers_group_1);

    ESP_LOGI(TAG, "Now the output PWMs should in sync");
    for (int i = 0; i < 2; ++i) {
        // 移除生成器上的强制电平,这样可以再次看到PWM
        ESP_ERROR_CHECK(mcpwm_generator_set_force_level(generators_0[i], -1, true));
        ESP_ERROR_CHECK(mcpwm_generator_set_force_level(generators_1[i], -1, true));
    }
    vTaskDelay(pdMS_TO_TICKS(100));
}
通过一个 MCPWM 的两个 MCPWM 生成器分别输出
/**
 * @brief 使用一个 MCPWM 外设中的两个操作器,输出四路 PWM 波形
 * @brief Use two operators in one MCPWM peripheral to output four PWM waveforms
 */
void app_main(void)
{
    // 创建定时器组
    ESP_LOGI(TAG, "Create timers");
    mcpwm_timer_handle_t timers_group[2];

    // 配置定时器
    mcpwm_timer_config_t timer_config = {
        .clk_src       = MCPWM_TIMER_CLK_SRC_DEFAULT,  // 时钟源
        .group_id      = 0,                            // 定时器组ID
        .resolution_hz = EXAMPLE_TIMER_RESOLUTION_HZ,  // 定时器分辨率
        .period_ticks  = EXAMPLE_TIMER_PERIOD,         // 定时器周期
        .count_mode    = MCPWM_TIMER_COUNT_MODE_UP,    // 计数模式为向上计数
    };

    for (int i = 0; i < 2; i++) {
        ESP_ERROR_CHECK(mcpwm_new_timer(&timer_config, &timers_group[i]));
    }

    // 创建操作器
    ESP_LOGI(TAG, "Create operators");
    mcpwm_oper_handle_t operators[2];
    mcpwm_operator_config_t operator_config = {
        .group_id = 0,
    };

    for (int i = 0; i < 2; ++i) {
        ESP_ERROR_CHECK(mcpwm_new_operator(&operator_config, &operators[i]));
    }

    // 设置操作器的定时器
    ESP_LOGI(TAG, "Connect timers and operators with each other");
    for (int i = 0; i < 2; i++) {
        ESP_ERROR_CHECK(mcpwm_operator_connect_timer(operators[i], timers_group[i]));
    }

    // 创建比较器
    // 为每一个生成器(a,b 两路)创建一个比较器,一共四个比较器
    ESP_LOGI(TAG, "Create comparators");
    mcpwm_cmpr_handle_t comparators_a[2];
    mcpwm_cmpr_handle_t comparators_b[2];
    mcpwm_comparator_config_t compare_config = {
        .flags.update_cmp_on_tez = true,
        // 当定时器计数为零时更新比较阈值
    };
    for (int i = 0; i < 2; i++) {
        // 分配比较器
        ESP_ERROR_CHECK(mcpwm_new_comparator(operators[i], &compare_config, &comparators_a[i]));
        ESP_ERROR_CHECK(mcpwm_new_comparator(operators[i], &compare_config, &comparators_b[i]));
        // 初始化每个比较器的比较值
        ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(comparators_a[i], 500));
        ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(comparators_b[i], 300));
    }

    // 创建生成器
    ESP_LOGI(TAG, "Create generators");
    mcpwm_gen_handle_t generators_a[2];
    mcpwm_gen_handle_t generators_b[2];
    // 配置生成器
    // 每个生成器对应一个GPIO引脚
    const int gen_gpios_a[2]            = {EXAMPLE_GEN_GPIO0, EXAMPLE_GEN_GPIO1};
    const int gen_gpios_b[2]            = {EXAMPLE_GEN_GPIO2, EXAMPLE_GEN_GPIO3};
    mcpwm_generator_config_t gen_config = {};
    for (int i = 0; i < 2; i++) {
        gen_config.gen_gpio_num = gen_gpios_a[i];
        // 设置操作器相应的生成器
        ESP_ERROR_CHECK(mcpwm_new_generator(operators[i], &gen_config, &generators_a[i]));
        gen_config.gen_gpio_num = gen_gpios_b[i];
        ESP_ERROR_CHECK(mcpwm_new_generator(operators[i], &gen_config, &generators_b[i]));
    }

    // 设置生成器的工作模式
    ESP_LOGI(TAG, "Set generator actions on timer and compare event");
    for (int i = 0; i < 2; i++) {
        ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(
            generators_a[i],
            // 当定时器计数为零且向上计数时,输出高电平
            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(
            generators_a[i],
            // 当比较匹配且向上计数时,输出低电平
            MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparators_a[i], MCPWM_GEN_ACTION_LOW)));

        ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(
            generators_b[i],
            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(
            generators_b[i],
            MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparators_b[i], MCPWM_GEN_ACTION_LOW)));
    }

    // 逐个启动定时器
    ESP_LOGI(TAG, "Start timers one by one, so they are not synced");
    for (int i = 0; i < 2; i++) {
        ESP_ERROR_CHECK(mcpwm_timer_enable(timers_group[i]));
        ESP_ERROR_CHECK(mcpwm_timer_start_stop(timers_group[i], MCPWM_TIMER_START_NO_STOP));
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    vTaskDelay(pdMS_TO_TICKS(100));

    // 在同步之前添加一个“空闲”阶段,可以帮助区分同步前后的波形
    ESP_LOGI(TAG, "Force the output level to low, timer still running");
    // 强制输出低电平,定时器仍在运行
    for (int i = 0; i < 2; i++) {
        ESP_ERROR_CHECK(mcpwm_generator_set_force_level(generators_a[i], 0, true));
        ESP_ERROR_CHECK(mcpwm_generator_set_force_level(generators_b[i], 0, true));
    }

    ESP_LOGI(TAG, "Setup sync strategy");
    // 设置同步策略
    example_setup_sync_strategy(timers_group);

    ESP_LOGI(TAG, "Now the output PWMs should in sync");
    for (int i = 0; i < 2; ++i) {
        // 移除生成器上的强制电平,这样就可以再次看到PWM
        ESP_ERROR_CHECK(mcpwm_generator_set_force_level(generators_a[i], -1, true));
        ESP_ERROR_CHECK(mcpwm_generator_set_force_level(generators_b[i], -1, true));
    }
    vTaskDelay(pdMS_TO_TICKS(100));
}
ESP32 学习笔记(十二)MCPWM MCPWM 概述 ESP32有两个MCPWM单元,可用于控制不同的电机。 框图 MCPWM单元的框图如图所示。 应用示例 使用MCPWM进行电机控制的示例:peripherals/mcpwm. API Reference Header File driver/include/driver/mcpwm.h ... 阅读详情

相关推荐

ESP32 之 ESP-IDF 教学(十)—— 电机控制器(MCPWM)

本文章 来自原创专栏《ESP32教学专栏 (基于ESP-IDF)》,讲解如何使用 ESP-IDF 构建 ESP32 程序,发布文章并会持续为已发布文章添加新内容! 每篇文章都经过了精打细磨! ↓↓↓通过下方对话框进入专栏主页↓↓↓ 优快云 请求进入专栏       _ O x是否进入ESP32教学专栏(基于ESP-IDF)?       确定 文章目录一、电机控制器MCPWM简介 一、电机控制器MCPWM简介 ESP32有两个MCPWM单元,可用于控制不同类型的电机。每个单元有三对PWM输出。(如下图,

Augtons 的博客 1万+

esp32外设使用-MCPWM基本使用方法

有刷/无刷电机驱动开关电源DAC脉冲宽度计算,测距/测速FOC驱动。

啸风 9257

ESP32学习笔记_Peripherals(4)——MCPWM多路同步输出实战

本文详细介绍了ESP32的MCPWM模块在多路同步PWM输出中的应用,包括GPIO同步、软件同步和定时器事件同步三种策略,以及单外设和多外设架构设计。通过机械臂控制项目的实战经验,分享了同步相位配置、同步时机选择和抗干扰设计等关键技巧,帮助开发者实现精准的多路PWM同步输出。

weixin_30790841的博客 352

电机调试笔记(1.1)之MCPWM概述和原理设计

MCPWM(Motor Control PWM)是一种用于电机控制的 PWM 模块。它在电机控制系统中扮演着关键的角色,影响着电机的运行、效率和响应性。以下是MCPWM对电机控制的一些重要影响:精确控制电机速度和位置: MCPWM模块可以生成具有可调节占空比的PWM信号。通过调整占空比,您可以控制电机的平均电压,从而调整电机的速度。对于一些电机控制算法,如PI(比例积分)控制,MCPWM提供了调整控制算法输出的手段,从而实现对电机速度和位置的精确控制。

xia_chou的博客 1661

ESP32物联网参考设计筛选指南:从官方示例到社区项目的四梯队资源路径

在嵌入式物联网开发中,ESP32凭借Wi-Fi与蓝牙双模能力成为主流选择,但开发者常面临参考设计资源分散、版本兼容性差、筛选成本高等问题。理解芯片外设驱动、协议栈配置与低功耗管理的基本原理,是高效利用官方示例与社区项目的关键。通过建立从官方参考设计、应用笔记到社区实战项目的优先级筛选流程,可显著降低试错成本,提升从功能验证到产品化的效率。该路径适用于传感器采集、无线通信、人机交互及电机控制等典型场景,帮助开发者快速锁定可落地的方案骨架。

weixin_34221036的博客 303

esp32用mcpwm驱动电机

本篇为乐鑫官方文档,地址:Motor Control Pulse Width Modulator (MCPWM) - ESP32 - — ESP-IDF 编程指南 latest 文档 前言 ESP32 有两个 MCPWM 单元,可用于控制不同的电机。每个单元有三对 PWM 输出。 此外,在文件中,单个信号的输出单元标记为PWMxA/PWMxB。 MCPWM单元的更详细框图如下所示。每个A/B对可由三个定时器0、1和2中的任意一个定时器计时。同一定时器可用于对多对PWM输出进行计时。每个单元还..

qqliuzhitong的博客 1万+

ESP32学习笔记_Peripherals(5)——MCPWM同步策略与多路输出实战

本文深入探讨了ESP32 MCPWM模块的同步功能,针对机器人、机械臂等多路电机协同控制场景,详细解析了GPIO同步、定时器事件同步和软件同步三种策略的原理与应用。文章通过一个驱动12路舵机的实战案例,演示了如何跨MCPWM组实现全局同步,并提供了关键的代码配置与调试技巧,帮助开发者从独立控制跃升至复杂机电系统的协同驾驭。

book8的博客 441

避开奸商套路!手把手教你用Thaiphoon Burner和CPU-Z,一眼看穿内存SPD信息有没有被篡改

本文教你使用Thaiphoon Burner和CPU-Z工具,快速识别内存SPD信息是否被篡改,避免购买到虚标频率或假冒颗粒的内存条。通过详细步骤和实战案例,帮助用户掌握内存检测技巧,确保硬件购买的透明度和安全性。

weixin_32228501的博客 680

ESP-IDF v6.0 完整发布指南:MbedTLS v4 迁移、新增 ESP32 芯片与版本时间线详解

ESP-IDF v6.0 正按"渐进式发布"的节奏推进:2025 年 11 月起先后放出两个 beta 版本,2026 年 2 月交付 RC 版本后正式发布。这个主版本里真正需要开发者留意的内容,集中在三处——加密子系统换到 MbedTLS v4.x 与 PSA 接口、ESP32-C5 和 ESP32-C61 两款芯片的完整支持,以及构建与调试工具链的调整。下面按版本节奏展开,逐模块拆解改动点,最

gitblog_00032的博客 340

【科研学习】Demo3D2015安装包以及图文安装步骤

安装包 点击下图的.exe文件,安装 不需要解压,打开注册机 如下图: 点击上图的.exe文件,出现下图 先点击 patch 打开安装目录下的Demo3D.licensing.dll文件,然后点击Generate,生成dvt-license文件,保存即可 打开安装完成的Demo3D 2015(64-bit),一般破解没完成的,会出现一个request a license 的问题,点击find license 的那一行 点击后找到自己Generate,生成dvt

KaelCui的博客 1万+

lift off工艺为什么常用蒸发的方式镀膜?

电子束蒸发中,蒸发物质从源头直接升华并沿直线路径沉积到晶圆上,它可以避免金属在掩模侧壁的覆盖,只在表面进行沉积。在镀膜结束后,在进行光刻胶剥离时,去胶液无法与光刻胶接触反应,导致光刻胶剥离很困难。欢迎加入我的半导体制造知识星球社区,目前社区有1900人左右。我观察到晶圆厂中lift off工艺,镀膜都是用的电子束蒸发的方式,很少有用磁控溅射的方式,为什么?需要增加撕胶工序,将胶面上覆盖的膜层撕掉,使光刻胶露出,这样才容易剥离。上图为磁控溅射的膜层截面图,去胶液很难突破侧壁膜层,进行反应。

1685

MicroPython ESP32 入门指南:固件烧录、PWM 外设、寄存器直访与工厂复位

本指南以仓库 [docs/esp32/tutorial/](https://link.gitcode.com/i/71627e277b1bd2a554ad6701ff12af8b) 目录下官方教程为骨架,系统讲解在 ESP32 芯片上从零上手 MicroPython 的完整路径:包括固件获取与烧录、REPL 串口交互、基于 LEDC 的 PWM 输出、通过内存映射寄存器直接操作外设与 GPIO,以

gitblog_02409的博客 464

AnyGrasp实战指南:从环境配置到抓取检测与跟踪

本文详细介绍了AnyGrasp从环境配置到抓取检测与跟踪的实战指南。涵盖硬件要求、关键依赖安装避坑、抓取检测与跟踪的实战技巧,以及常见问题解决方案和性能优化建议,帮助开发者快速掌握这一高精度物体抓取检测与跟踪技术。

weixin_33712987的博客 274

ESP-IDF v6.0 完整指南:MbedTLS v4 升级、新芯片支持与从 v5.x 平滑迁移清单

ESP-IDF(Espressif IoT Development Framework)是乐鑫 SoC 的官方开发框架。v6.0 是 2026 年的第一个大版本:它把底层加密栈从 MbedTLS v2.x 整体替换为 MbedTLS v4.x 并转向 PSA Crypto API,同时新增对 ESP32-H4 芯片的预览支持,计划于 2026 年 2 月 27 日正式发布。如果你还在 v5.x 上

gitblog_00052的博客 400

手把手教你用RK3568开发板实现FPGA与Linux的DMA通信(含完整代码解析)

本文详细介绍了如何在RK3568开发板上实现FPGA与Linux系统的DMA通信,包括硬件配置、软件环境搭建、DMA通信原理、驱动开发及性能优化。通过完整的代码解析和实战案例,帮助开发者快速掌握异构系统间的高效数据传输技术,适用于物联网和边缘计算等场景。

weixin_29174385的博客 201

【数据结构】考点二十八 : 二叉树的概念:二叉树的度、完全二叉树、满二叉数结点数、哈夫曼结点数、完全二叉树的深度

二叉树(Binary Tree)是一种常见的数据结构,它或者为空(null),或者由一个根节点(root node)和两个子树(left subtree 和 right subtree)组成,这两个子树又分别是一棵二叉树。二叉树的每个节点最多有两个子节点,通常称为左子节点(left child)和右子节点(right child)。

超越的博客 1738

【嵌入式】ESP32S3主控底盘驱动代码及其原理(ESPIDF)

本文介绍了ESP32S3开发环境搭建及外设开发要点。主要内容包括:1)使用VSCode创建ESP-IDF项目及组件的方法;2)添加第三方组件的操作流程;3)重点讲解了PWM技术原理及其在LED调光、电机控制中的应用;4)详细阐述了编码器工作原理,包括脉冲计算、转速转换等关键参数。文章提供了相关API文档链接、视频教程参考和具体操作步骤,为ESP32S3的外设开发提供了实用指导,特别适合嵌入式开发者快速上手ESP32硬件开发。

共享IT之美,共创机器未来 1080

R语言中的Gamma分布函数

在R语言中,我们可以使用最大似然估计(Maximum Likelihood Estimation,MLE)或最小二乘估计(Least Squares Estimation,LSE)等方法来估计Gamma分布的参数。本文介绍了在R语言中使用Gamma分布函数的基本操作,包括生成随机变量、计算概率密度函数和累积分布函数,以及参数估计和拟合。函数来生成Gamma分布的随机变量,计算概率密度函数(PDF)和累积分布函数(CDF),以及进行参数估计和拟合。要计算Gamma分布的累积分布函数(CDF),我们可以使用。

ByteWhiz的博客 1631

7个关键理由:为什么ESP-HAL是嵌入式开发的终极选择

在嵌入式系统开发领域,ESP-HAL(ESP硬件抽象层)正迅速成为ESP32微控制器开发者的首选工具。这个基于Rust语言的硬件抽象层项目为ESP32系列芯片提供了完整的no_std硬件抽象层支持,让嵌入式开发变得更加高效和安全。无论你是刚开始接触嵌入式系统,还是经验丰富的开发者,ESP-HAL都能为你提供强大的开发体验。 ## 🚀 项目价值主张:为什么选择ESP-HAL? ### 安全性与

gitblog_00659的博客 630

基于YOLO系列的安全帽检测系统:从理论到实现的完整指南

安全帽检测作为计算机视觉在工业安全领域的重要应用,近年来得到了广泛关注。本文详细介绍了基于YOLOv5、YOLOv6、YOLOv7和YOLOv8的安全帽检测系统的完整实现方案,包括算法原理、数据集构建、模型训练、性能优化以及完整的UI界面设计。通过对比分析不同YOLO版本的性能差异,本文为工业安全检测系统的开发提供了全面的技术参考和实践指导。

YOLO 364

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

如何把 ESP-IDF 老项目平滑升到 v6.0?加密栈、新芯片与工具链完整指南

如果你正打算把基于 v5.x 的固件搬到新版本,大概率会被 MbedTLS API 的变化拦住:原来一行调用的加密函数,现在要换整套 PSA Crypto 接口。这就是 **ESP-IDF v6.0**——Espressif 官方物联网开发框架的一次大版本更新,负责乐鑫全系列 SoC(ESP32、C 系列、S 系列、P4 等)的应用开发,这次它把加密引擎、芯片矩阵和调试工具链一起做了换代。本文带你

gitblog_00172的博客 339

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

CozyUI+×Xaero小地图搭配指南:Minecraft高清界面与探索工具无缝融合

🧭 **写在前面**:CozyUI+ 是一款 Minecraft 高清界面材质包(资源包),它把原版 UI 和许多实用客户端模组全部改造成简洁、柔和的高清风格;而 Xaero小地图(Xaero's Minimap)则是探索世界的经典工具。本指南带你用 5 个步骤完成 **CozyUI+ 搭配 Xaero小地图** 的安装与调优,新手也能轻松上手! [![CozyUI+ Minecraft高清界

gitblog_00411的博客 218

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

ESP-IDF v6.0深度剖析:物联网开发框架的三大技术革新与实战指南

ESP-IDF v6.0作为乐鑫物联网开发框架的重要版本更新,为开发者带来了前所未有的开发体验提升。本次升级不仅解决了长期困扰物联网开发者的三大核心痛点——安全与性能的平衡、多芯片兼容性挑战、以及复杂场景下的功耗优化,更通过架构重构和技术创新,为物联网应用开发树立了新的标杆。对于中级开发者和技术决策者而言,理解v6.0的技术革新意味着能够构建更安全、更高效、更易维护的物联网解决方案。 ## 一、

gitblog_00851的博客 652

ESP-IDF v6.0 升级指南:PSA Crypto 与驱动拆分到底改了什么

乐鑫官方开发框架 ESP-IDF v6.0 把大量你熟悉的 `mbedtls_sha_*`、`mbedtls_aes_*` 接口直接移除了——如果你的项目还在直接调用这些旧加密函数,升级后第一次编译就会报链接错误。别慌,这次改动的逻辑其实很直白,下面用二十分钟帮你把账算清楚。 ## 变更全景速览:这版动了哪些模块 先建立全局认知。v6.0 的官方迁移指南([docs/zh_CN/migrati

gitblog_00460的博客 319

ESP32 单片机学习笔记 - 03 - MCPWM脉冲输出/PCNT脉冲计数

ESP32 单片机学习笔记 - 03 - MCPWM脉冲输出/PCNT脉冲计数 前言,继续上一篇的内容。因为上一篇刚好实验了iic和spi,形成一对。接下来讲pwm另起一篇。 一、电机PWM输出 - MCPWM 官方例程: esp-idf/examples/peripherals/mcpwm/mcpwm_basic_config/,官方有4个例程,我先看了这个,这个例程对 mcpwm模块的所有功能的配置过程和使用方法 都有举例。开始就结合这个例程和编程指南一起看。 官方指南: MCPWM,虽然这次也是

Lovely_him的博客 1万+
上一篇: 基于LiteOS与SLE的多任务无线控制器项目实战
下一篇: ESP32学习笔记_Components(1)——使用LED Strip组件点亮LED灯带
flashier
博客等级 码龄3年 381粉丝 79原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值