文章目录一、PCNT1、PCNT2、核心能力3、ESP32_S3_PCNT组成结构二、PCNT原理1、PCNT框图2、硬件滤波模块 filter3、多路选择 增减逻辑单元 inc_dec4、加法器 adder 全局控制5、阈值比较与中断输出模块6、工程设计注意点三、PCNT API1、头文件/组件头文件组件2、枚举类型1. pcunt_count_mode_t 信号边沿计数动作2. pcnt_ctrl_mode_t Ctrl 方向电平策略3. pcnt_unit_id_t PCNT 单元编号4. pcnt_event_type_t 中断事件类型3、配置结构体1. pcnt_unit_config_t 单元全局配置2. pcnt_channel_config_t 单通道引脚与计数规则配置3. 回调事件数据 pcnt_event_data_t4. 回调结构体 pcnt_event_callbacks_t4、创建 / 销毁资源API5、运行控制类API6、中断回调配置函数7、动态阈值配置类8、开发流程四、示例代码1、示例代码2、执行结果一、PCNT1、PCNTPCNTPulse Counter脉冲计数控制器是ESP32-S3内置的专用硬件脉冲计数外设专为高频脉冲采集、正交编码器测速、脉冲信号统计设计。ESP32-S3搭载4个独立PCNT单元每个单元包含2路脉冲输入通道支持正交解码、单边计数、双向增减计数可同时采集多路脉冲设备信号。2、核心能力对外部 GPIO 脉冲上升 / 下降沿做硬件计数不需要 CPU 干预。支持方向控制信号自动加 / 减计数。设置阈值到达阈值产生中断。计数可过滤输入噪声输入滤波。16 位计数器范围‑32768 ~ 32767。3、ESP32_S3_PCNT组成结构PCNT 有四个称为“单元”的独立脉冲计数控制器这些单元拥有自己的寄存器。PCNT 模块仅有一个时钟为 APB_CLK。每个单元有两个通道ch0 和 ch1可以独立配置为递增或递减计数。每个通道有两个输入信号一个脉冲输入信号如 sig_ch0_un 为单元 n ch0 的脉冲输入信号。一个控制信号如 ctrl_ch0_un 为单元 n ch0 的控制信号。二、PCNT原理1、PCNT框图2、硬件滤波模块 filter输入sig脉冲信号、ctrl方向控制信号分别独立滤波。配置寄存器PCNT_FILTER_EN_Un滤波使能、PCNT_FILTER_THRES_Un滤波阈值。作用宽度小于阈值的高频毛刺直接过滤硬件消抖无需软件防抖适配机械编码器抖动场景。3、多路选择 增减逻辑单元 inc_dec配置寄存器分组边沿动作配置PCNT_CHx_POS_MODE_Unsig 上升沿动作加 / 减 / 不计数。PCNT_CHx_NEG_MODE_Unsig 下降沿动作加 / 减 / 不计数。方向控制配置PCNT_CHx_HCTRL_MODE_Unctrl 高电平时计数策略。PCNT_CHx_LCTRL_MODE_Unctrl 低电平时计数策略。工作逻辑sig提供脉冲边沿触发时机ctrl电平决定本次计数是1、-1、忽略。正交编码器模式下A 相接 sig、B 相接 ctrl可天然实现正反转自动增减计数。4、加法器 adder 全局控制输入来源CH0、CH1 两路inc_dec的计数增量共同送入加法器累加。全局控制信号PCNT_CNT_PAUSE_Un暂停计数冻结当前值。PCNT_PULSE_CNT_RST_Un硬件清零计数器CLR 端口。关键优势双通道同时接入编码器 AB 相可实现四倍频正交计数单通道仅能二倍频。5、阈值比较与中断输出模块加法器输出的pulse_cnt同时送入 5 组比较器6、工程设计注意点1.滤波优先开启机械编码器务必配置filter阈值从硬件层面抑制触点抖动2.计数模式选型普通单端脉冲计数仅用 CH0ctrl引脚悬空配置为无效正交编码器测速CH0CH1 双通道接入 AB 相实现四倍频提升测量精度3.溢出防护PCNT 原生 16 位计数范围-32768~32767利用高低限中断做软件累计值扩展突破位数限制4.低 CPU 占用全链路计数、滤波、比较均为硬件逻辑CPU 仅需定时读取数值或响应阈值中断不占用运算资源。三、PCNT API1、头文件/组件头文件#include “driver/pulse_cnt.h”组件REQUIRES esp_driver_pcnt2、枚举类型1. pcunt_count_mode_t 信号边沿计数动作typedefenum{PCNT_COUNT_DIS0,// 该边沿不计数PCNT_COUNT_INC,// 触发边沿时计数值 1PCNT_COUNT_DEC// 触发边沿时计数值 -1}pcnt_count_mode_t;2. pcnt_ctrl_mode_t Ctrl 方向电平策略typedefenum{PCNT_CTRL_MODE_KEEP0,// 保持sig边沿预设的增减规则PCNT_CTRL_MODE_REVERSE,// 反转sig边沿的增减方向PCNT_CTRL_MODE_INC,// Ctrl当前电平强制本次1PCNT_CTRL_MODE_DEC// Ctrl当前电平强制本次-1}pcnt_ctrl_mode_t;3. pcnt_unit_id_t PCNT 单元编号typedefenum{PCNT_UNIT_0,PCNT_UNIT_1,PCNT_UNIT_2,PCNT_UNIT_3,PCNT_UNIT_4,PCNT_UNIT_5,PCNT_UNIT_6,PCNT_UNIT_7}pcnt_unit_id_t;4. pcnt_event_type_t 中断事件类型typedefenum{PCNT_EVT_H_LIM,// 计数到达高上限阈值PCNT_EVT_L_LIM,// 计数到达低下限阈值PCNT_EVT_THRES_0,// 计数匹配阈值0PCNT_EVT_THRES_1,// 计数匹配阈值1PCNT_EVT_ZERO// 计数归零}pcnt_event_type_t;3、配置结构体1. pcnt_unit_config_t 单元全局配置typedefstruct{pcnt_unit_id_tunit_id;// 指定PCNT单元编号intlow_limit;// 计数下限(-32768~32767)触发L_LIM事件inthigh_limit;// 计数上限(-32768~32767)触发H_LIM事件}pcnt_unit_config_t;2. pcnt_channel_config_t 单通道引脚与计数规则配置typedefstruct{intsig_gpio_num;// SIG脉冲输入GPIO编号intctrl_gpio_num;// CTRL方向控制GPIO填-1表示不使用方向脚pcnt_count_mode_tpos_mode;// SIG上升沿计数动作pcnt_count_mode_tneg_mode;// SIG下降沿计数动作pcnt_ctrl_mode_thctrl_mode;// CTRL引脚高电平时的计数策略pcnt_ctrl_mode_tlctrl_mode;// CTRL引脚低电平时的计数策略uint32_tfilter_ns;// 硬件滤波时间(单位ns)0关闭滤波过滤窄毛刺}pcnt_channel_config_t;3. 回调事件数据 pcnt_event_data_ttypedefstruct{pcnt_event_type_ttype;// 触发的具体事件类型}pcnt_event_data_t;4. 回调结构体 pcnt_event_callbacks_ttypedef struct { // 阈值/上下限/零值事件回调中断上下文执行 bool (*on_reach)(pcnt_unit_handle_t unit, const pcnt_event_data_t *event_data, void *user_data); } pcnt_event_callbacks_t;4、创建 / 销毁资源APIesp_err_tpcnt_new_unit(constpcnt_unit_config_t*config,pcnt_unit_handle_t*ret_handle);//创建并初始化一个 PCNT 单元返回句柄esp_err_tpcnt_del_unit(pcnt_unit_handle_tunit);//禁用单元、释放硬件资源esp_err_tpcnt_new_channel(pcnt_unit_handle_tunit,constpcnt_channel_config_t*config);//为指定 Unit 配置 CH0/CH1 通道参数5、运行控制类APIesp_err_tpcnt_unit_enable(pcnt_unit_handle_tunit);//使能单元开始硬件计数esp_err_tpcnt_unit_disable(pcnt_unit_handle_tunit);//暂停单元计数esp_err_tpcnt_unit_clear_count(pcnt_unit_handle_tunit);//硬件清零当前计数值esp_err_tpcnt_unit_get_count(pcnt_unit_handle_tunit,int*count);//读取当前实时计数值6、中断回调配置函数esp_err_tpcnt_unit_register_event_callbacks(pcnt_unit_handle_tunit,constpcnt_event_callbacks_t*cbs,void*user_data);//注册事件中断回调函数可传入自定义用户参数esp_err_tpcnt_set_event_watchpoint(pcnt_unit_handle_tunit,pcnt_event_type_tevt_type,bool enable);//单独开启 / 关闭某一类事件的中断触发7、动态阈值配置类esp_err_tpcnt_set_thres0(pcnt_unit_handle_tunit,intthres);//动态修改阈值 0 数值esp_err_tpcnt_set_thres1(pcnt_unit_handle_tunit,intthres);//动态修改阈值 1 数值8、开发流程1.定义并填充 pcnt_unit_config_t调用 pcnt_new_unit 创建单元句柄。 2.填充 pcnt_channel_config_t调用 pcnt_new_channel 配置 CH0/CH1。 3.可选pcnt_unit_register_event_callbacks 注册阈值中断回调。 4.可选pcnt_set_thres0/thres1 设置自定义触发阈值。 5.pcnt_unit_enable 启动硬件计数。 6.业务循环pcnt_unit_get_count 读取数值做运算溢出中断内做软件累计扩展。 7.任务退出pcnt_unit_disable → pcnt_del_unit 释放资源。四、示例代码1、示例代码#includestdio.h#includefreertos/FreeRTOS.h#includefreertos/task.h#includedriver/pulse_cnt.h#includeesp_log.hstaticconstchar*TAGPCNT_HALL;#defineHALL_GPIO4#definePCNT_HIGH_LIMIT32767#definePCNT_LOW_LIMIT-32768pcnt_unit_handle_tpcnt_hall_unitNULL;voidpcnt_hall_init(void){// PCNT单元配置pcnt_unit_config_tunit_cfg{.high_limitPCNT_HIGH_LIMIT,.low_limitPCNT_LOW_LIMIT};ESP_ERROR_CHECK(pcnt_new_unit(unit_cfg,pcnt_hall_unit));// 毛刺滤波霍尔容易有抖动设置滤波pcnt_glitch_filter_config_tfilter_cfg{.max_glitch_ns2000,};ESP_ERROR_CHECK(pcnt_unit_set_glitch_filter(pcnt_hall_unit,filter_cfg));// PCNT通道配置edge引脚接霍尔信号level不使用pcnt_chan_config_tchan_cfg{.edge_gpio_numHALL_GPIO,.level_gpio_num-1};pcnt_channel_handle_tpcnt_chanNULL;ESP_ERROR_CHECK(pcnt_new_channel(pcnt_hall_unit,chan_cfg,pcnt_chan));// 动作配置上升沿计数1下降沿无动作ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan,PCNT_CHANNEL_EDGE_ACTION_INCREASE,// 上升沿PCNT_CHANNEL_EDGE_ACTION_HOLD// 下降沿));ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan,PCNT_CHANNEL_LEVEL_ACTION_KEEP,PCNT_CHANNEL_LEVEL_ACTION_KEEP));// 启用PCNTESP_ERROR_CHECK(pcnt_unit_enable(pcnt_hall_unit));ESP_ERROR_CHECK(pcnt_unit_clear_count(pcnt_hall_unit));ESP_ERROR_CHECK(pcnt_unit_start(pcnt_hall_unit));}voidhall_read_task(void*arg){intpulse_val;staticintlast_pulse0;while(1){ESP_ERROR_CHECK(pcnt_unit_get_count(pcnt_hall_unit,pulse_val));intdeltapulse_val-last_pulse;last_pulsepulse_val;ESP_LOGI(TAG,Hall Pulse: %d, Delta:%d,pulse_val,delta);vTaskDelay(pdMS_TO_TICKS(100));}}voidapp_main(void){pcnt_hall_init();xTaskCreate(hall_read_task,hall_task,2048,NULL,5,NULL);}2、执行结果
阅读完成 · 觉得有帮助?