ESP32S3学习——Pulse Counter (PCNT)脉冲计数

ESP32S3学习——Pulse Counter (PCNT)脉冲计数

不就输入捕获?

芯片:esp32s3

开发环境:espidfv4.4

一、官网相关资料(又是英文T_T)

1)介绍

PCNT(脉冲计数器)模块设计用于计数输入信号的上升沿和/或下降沿的数量。每个脉冲计数器单元都有一个16位带符号计数器寄存器两个通道,可以配置为递增或递减计数器。每个通道都有一个接受待检测信号边缘的信号输入,以及一个可用于启用或禁用信号输入的控制输入。输入具有可选滤波器,可用于丢弃信号中不需要的毛刺。

关键词:计数上升下降沿,有两个16为带输出的计数器,可递增递减;滤波器

2)功能概述

API可以分为四个部分
  • 配置-描述计数器的配置参数以及如何设置计数器。

  • 操作计数器-提供有关暂停、测量和清除计数器的控制功能的信息。

  • 滤波脉冲-描述了滤波脉冲和计数器控制信号的选项。

  • 使用中断-介绍如何在计数器的特定状态下触发中断。

3)配置流程

PCNT模块有8个独立的计数“单元”,编号从0到7。在API中,使用pcnt_unit_t来引用它们。每个单元有两个独立的通道,编号为0和1,并用pcnt_channel_t指定。共16个通道
使用pcnt_config_t为每个单元的通道单独提供配置,包括:
  • 此配置所指的单元和通道编号。

  • 脉冲输入和脉冲门输入的GPIO数。

  • 两对参数:pcnt_ctrl_mode_t和pcnt_count_mode_t,用于定义计数器如何根据控制信号的状态做出反应,以及如何在脉冲的正/负边缘进行计数。

  • 两个极限值(最小值/最大值),用于在脉冲计数达到特定极限时建立观察点并触发中断。

然后,通过调用函数pcnt_unit_config(),以上面的pcnt_config_t作为输入参数,来完成特定通道的设置。要禁用配置中的脉冲或控制输入引脚,使用PCNT_pin_NOT_USED而不是GPIO编号。

4)操作计数器

  • 使用pcnt_unit_config()进行设置后,计数器立即开始工作。可以通过调用pcnt_get_counter_value()来检查累积脉冲计数。

  • 有几个函数可以控制计数器的操作:pcnt_counter_pause()、pcnt_coounter_resume()和pcnt_cocounter_clear()

  • 还可以通过调用pcnt_set_mode(),使用pcnt_unit_config()动态更改先前设置的计数器模式。

  • 如果需要,可以使用pcnt_set_pin()“动态”更改脉冲输入引脚和控制输入引脚。要禁用特定输入,请提供PCNT_PIN_NOT_USED作为函数参数,而不是GPIO编号。

  • 为了使计数器不错过任何脉冲,脉冲持续时间应长于一个APB_CLK周期(12.5ns)。脉冲是在APB_CLK时钟的边缘上采样的,如果落在边缘之间,则可能会被错过。这适用于使用或不使用文件管理器的计数器操作。

5)过滤脉冲

  • PCNT单元在每个脉冲和控制输入上都有滤波器,增加了忽略信号中短故障的选项。 通过调用pcnt_set_filter_value(),在APB_CLK时钟周期中提供被忽略脉冲的长度。可以使用pcnt_get_filter_value()检查当前过滤器设置。APB_CLK时钟运行频率为80 MHz。 通过调用pcnt_filter_enable()/pcnt_filter_disable(),过滤器进入运行/挂起状态。

6) 使用中断

pcnt_evt_type_t中定义了五个计数器状态监视事件,它们能够触发中断。该事件发生在脉冲计数器达到特定值时:
  • 最小或最大计数值:如配置中所述,在pcnt_config_t中提供counter_l_lim或counter_h_lim

  • 使用函数pcnt_set_event_value()设置阈值0或阈值1值。

  • 脉冲计数=0

要注册、启用或禁用中断以服务上述事件,请调用pcnt_isr_register()、pcnt_intr_enable()。和pcnt_intr_disable()。要在达到阈值时启用或禁用事件,还需要调用函数pcnt_event_enable()和pcnt_eevent_disable()。为了检查当前设置的阈值,请使用函数pcnt_get_event_value()。

7)示例代码

带有控制信号和事件中断的脉冲计数器示例:peripherals/pcnt/pulse_count_event.

解析旋转编码器产生的信号: peripherals/pcnt/rotary_encoder.

三、上手敲代码(官方加注释)

  1. 带有控制信号和事件中断的脉冲计数器示例:peripherals/pcnt/pulse_count_event.

  2. 头文件:#include "driver/pcnt.h"

  3. readme: GPIO4(ledc)产生1hz的脉冲,GPIO1 8作为输入脚进行计数;GPIO5为控制脚,悬空(内部上拉)-计数增加;接地-计数减小

    中断

    * - reaches 'thresh1' or 'thresh0' value,

    * - reaches 'l_lim' value or 'h_lim' value,

    * - will be reset to zero.

ESP32S3学习——Pulse Counter (PCNT)脉冲计数

不就输入捕获?

芯片:esp32s3

开发环境:espidfv4.4

## 一、官网相关资料(又是英文T_T)

### 1)介绍

PCNT(脉冲计数器)模块设计用于**计数输入信号的上升沿和/或下降沿**的数量。每个脉冲计数器单元都有一个1**6位带符号计数器寄存器**和**两个通道**,可以配置为**递增或递减计数器**。每个通道都有一个**接受待检测信号边缘的信号输入**,以及一个可用于启用或禁用信号输入的控制输入。输入具有**可选滤波器**,可用于丢弃信号中不需要的毛刺。



关键词:计数上升下降沿,有两个16为带输出的计数器,可递增递减;滤波器

### 2)功能概述

#### 		API可以分为四个部分

- 配置-描述计数器的配置参数以及如何设置计数器。
- 操作计数器-提供有关暂停、测量和清除计数器的控制功能的信息。
- 滤波脉冲-描述了滤波脉冲和计数器控制信号的选项。
- 使用中断-介绍如何在计数器的特定状态下触发中断。



## 3)配置流程

#### PCNT模块有8个独立的计数“单元”,编号从0到7。在API中,使用pcnt_unit_t来引用它们。每个单元有两个独立的通道,编号为0和1,并用pcnt_channel_t指定。共16个通道

#### 使用pcnt_config_t为每个单元的通道单独提供配置,包括:

- 此配置所指的单元和通道编号。
- 脉冲输入和脉冲门输入的GPIO数。
- 两对参数:pcnt_ctrl_mode_t和pcnt_count_mode_t,用于定义计数器如何根据控制信号的状态做出反应,以及如何在脉冲的正/负边缘进行计数。
- 两个极限值(最小值/最大值),用于在脉冲计数达到特定极限时建立观察点并触发中断。

#### 然后,通过调用函数pcnt_unit_config(),以上面的pcnt_config_t作为输入参数,来完成特定通道的设置。要禁用配置中的脉冲或控制输入引脚,使用PCNT_pin_NOT_USED而不是GPIO编号。

## 4)操作计数器

- 使用pcnt_unit_config()进行设置后,计数器立即开始工作。可以通过调用pcnt_get_counter_value()来检查累积脉冲计数。
- 有几个函数可以控制计数器的操作:pcnt_counter_pause()、pcnt_coounter_resume()和pcnt_cocounter_clear()
- 还可以通过调用pcnt_set_mode(),使用pcnt_unit_config()动态更改先前设置的计数器模式。
- 如果需要,可以使用pcnt_set_pin()“动态”更改脉冲输入引脚和控制输入引脚。要禁用特定输入,请提供PCNT_PIN_NOT_USED作为函数参数,而不是GPIO编号。
- 为了使计数器不错过任何脉冲,脉冲持续时间应长于一个APB_CLK周期(12.5ns)。脉冲是在APB_CLK时钟的边缘上采样的,如果落在边缘之间,则可能会被错过。这适用于使用或不使用文件管理器的计数器操作。

## 5)过滤脉冲

- PCNT单元在每个脉冲和控制输入上都有滤波器,增加了忽略信号中短故障的选项。
  通过调用pcnt_set_filter_value(),在APB_CLK时钟周期中提供被忽略脉冲的长度。可以使用pcnt_get_filter_value()检查当前过滤器设置。APB_CLK时钟运行频率为80 MHz。
  通过调用pcnt_filter_enable()/pcnt_filter_disable(),过滤器进入运行/挂起状态。

## 6) 使用中断

#### pcnt_evt_type_t中定义了五个计数器状态监视事件,它们能够触发中断。该事件发生在脉冲计数器达到特定值时:

- ​			最小或最大计数值:如配置中所述,在pcnt_config_t中提供counter_l_lim或counter_h_lim
- ​			使用函数pcnt_set_event_value()设置阈值0或阈值1值。
- ​			脉冲计数=0

#### 要注册、启用或禁用中断以服务上述事件,请调用pcnt_isr_register()、pcnt_intr_enable()。和pcnt_intr_disable()。要在达到阈值时启用或禁用事件,还需要调用函数pcnt_event_enable()和pcnt_eevent_disable()。为了检查当前设置的阈值,请使用函数pcnt_get_event_value()。

## 7)示例代码

带有控制信号和事件中断的脉冲计数器示例:[peripherals/pcnt/pulse_count_event](https://github.com/espressif/esp-idf/tree/v4.4.2/examples/peripherals/pcnt/pulse_count_event).

解析旋转编码器产生的信号: [peripherals/pcnt/rotary_encoder](https://github.com/espressif/esp-idf/tree/v4.4.2/examples/peripherals/pcnt/rotary_encoder).

## 三、上手敲代码(官方加注释)

1) 带有控制信号和事件中断的脉冲计数器示例:[peripherals/pcnt/pulse_count_event](https://github.com/espressif/esp-idf/tree/v4.4.2/examples/peripherals/pcnt/pulse_count_event).

2) 头文件:\#include "driver/pcnt.h"

3) readme: GPIO4(ledc)产生1hz的脉冲,GPIO1 8作为输入脚进行计数;GPIO5为控制脚,悬空(内部上拉)-计数增加;接地-计数减小

   ​	中断

    \*  - reaches 'thresh1' or 'thresh0' value,

    \*  - reaches 'l_lim' value or 'h_lim' value,

    \*  - will be reset to zero.

`#include "freertos/FreeRTOS.h"`
`#include "freertos/task.h"`
`#include "freertos/queue.h"`
`#include "driver/ledc.h"`
`#include "driver/pcnt.h"`
`#include "esp_attr.h"`
`#include "esp_log.h"`

`static const char *TAG = "example";`

`/**`

 * `TEST CODE BRIEF`
   `*`
 * `Use PCNT module to count rising edges generated by LEDC module.`
   `*`
 * `Functionality of GPIOs used in this example:`
 * - `GPIO18 - output pin of a sample 1 Hz pulse generator,`
 * - `GPIO4 - pulse input pin,`
 * - `GPIO5 - control input pin.`
     `*`
 * `Load example, open a serial port to view the message printed on your screen.`
   `*`
 * `To do this test, you should connect GPIO18 with GPIO4.`
 * `GPIO5 is the control signal, you can leave it floating with internal pull up,`
 * `or connect it to ground. If left floating, the count value will be increasing.`
 * `If you connect GPIO5 to GND, the count value will be decreasing.`
   `*`
 * `An interrupt will be triggered when the counter value:`
 * - `reaches 'thresh1' or 'thresh0' value,`
 * - `reaches 'l_lim' value or 'h_lim' value,`
 * - `will be reset to zero.`
     `*/`
     `#define PCNT_H_LIM_VAL      10`
     `#define PCNT_L_LIM_VAL     -10`
     `#define PCNT_THRESH1_VAL    5`
     `#define PCNT_THRESH0_VAL   -5`
     `#define PCNT_INPUT_SIG_IO   4  // Pulse Input GPIO`
     `#define PCNT_INPUT_CTRL_IO  5  // Control GPIO HIGH=count up, LOW=count down`
     `#define LEDC_OUTPUT_IO      18 // Output GPIO of a sample 1 Hz pulse generator`

`xQueueHandle pcnt_evt_queue;   // A queue to handle pulse counter events`

`/* A sample structure to pass events from the PCNT`

 * `interrupt handler to the main program.`
   `*/`
   `typedef struct {`
   `int unit;  // the PCNT unit that originated an interrupt`
   `uint32_t status; // information on the event type that caused the interrupt`
   `} pcnt_evt_t;`

`/* Decode what PCNT's unit originated an interrupt`

 * `and pass this information together with the event type`
 * `the main program using a queue.`
   `*/`
   `static void IRAM_ATTR pcnt_example_intr_handler(void *arg)
   {
   int pcnt_unit = (int)arg;
   pcnt_evt_t evt;
   evt.unit = pcnt_unit;
   /* Save the PCNT event type that caused an interrupt`
      `to pass it to the main program */`
   `pcnt_get_event_status(pcnt_unit, &evt.status);`
   `xQueueSendFromISR(pcnt_evt_queue, &evt, NULL);`
   `}`

`/* Configure LED PWM Controller`

 * `to output sample pulses at 1 Hz with duty of about 10%`
   `*/`
   `static void ledc_init(void)`
   `{`
   `// Prepare and then apply the LEDC PWM timer configuration`
   `ledc_timer_config_t ledc_timer;`
   `ledc_timer.speed_mode       = LEDC_LOW_SPEED_MODE;`
   `ledc_timer.timer_num        = LEDC_TIMER_1;`
   `ledc_timer.duty_resolution  = LEDC_TIMER_10_BIT;`
   `ledc_timer.freq_hz          = 1;  // set output frequency at 1 Hz`
   `ledc_timer.clk_cfg = LEDC_AUTO_CLK;`
   `ledc_timer_config(&ledc_timer);`

   `// Prepare and then apply the LEDC PWM channel configuration`
   `ledc_channel_config_t ledc_channel;`
   `ledc_channel.speed_mode = LEDC_LOW_SPEED_MODE;`
   `ledc_channel.channel    = LEDC_CHANNEL_1;`
   `ledc_channel.timer_sel  = LEDC_TIMER_1;`
   `ledc_channel.intr_type  = LEDC_INTR_DISABLE;`
   `ledc_channel.gpio_num   = LEDC_OUTPUT_IO;`
   `ledc_channel.duty       = 100; // set duty at about 10%`
   `ledc_channel.hpoint     = 0;`
   `ledc_channel_config(&ledc_channel);`
   `}`

`/* Initialize PCNT functions:`

 * - `configure and initialize PCNT`

 * - `set up the input filter`

 * - `set up the counter events to watch`
     `*/
     static void pcnt_example_init(int unit)
     {
      /* Prepare configuration for the PCNT unit */
      pcnt_config_t pcnt_config = {
       // Set PCNT input signal and control GPIOs
       .pulse_gpio_num = PCNT_INPUT_SIG_IO,//GPIO4输入
       .ctrl_gpio_num = PCNT_INPUT_CTRL_IO,//GPIO5控制
       .channel = PCNT_CHANNEL_0,//0通道
       .unit = unit,//函数参数,单元
       // What to do on the positive / negative edge of pulse input?
       //上升沿/下降沿做什么
       .pos_mode = PCNT_COUNT_INC,   // 上升沿递增Count up on the positive edge
       .neg_mode = PCNT_COUNT_DIS,   // 下降沿保持Keep the counter value on the negative edge
       // What to do when control input is low or high?
       //控制引脚--高低电平时坐什么
       .lctrl_mode = PCNT_MODE_REVERSE, // 低电平反转技术方向Reverse counting direction if low
       .hctrl_mode = PCNT_MODE_KEEP,    // 高电平保持Keep the primary counter mode if high
       // Set the maximum and minimum limit values to watch
       //设置技术最大小值
       .counter_h_lim = PCNT_H_LIM_VAL,
       .counter_l_lim = PCNT_L_LIM_VAL,
      };
      /* Initialize PCNT unit */`
      `// 初始化结构体`
      `pcnt_unit_config(&pcnt_config);`

    `/* Configure and enable the input filter */`
    `// 配置使能滤波,第二个参数为时间,时钟个数计数,时钟为apb`
    `pcnt_set_filter_value(unit, 100);`
    `pcnt_filter_enable(unit);`

    `/* Set threshold 0 and 1 values and enable events to watch */
    // 配置使能阈值01
    pcnt_set_event_value(unit, PCNT_EVT_THRES_1, PCNT_THRESH1_VAL);
    pcnt_event_enable(unit, PCNT_EVT_THRES_1);
    pcnt_set_event_value(unit, PCNT_EVT_THRES_0, PCNT_THRESH0_VAL);
    pcnt_event_enable(unit, PCNT_EVT_THRES_0);
    /* Enable events on zero, maximum and minimum limit values */`
    `// 使能0,最大小值`
    `pcnt_event_enable(unit, PCNT_EVT_ZERO);`
    `pcnt_event_enable(unit, PCNT_EVT_H_LIM);`
    `pcnt_event_enable(unit, PCNT_EVT_L_LIM);`

    `/* Initialize PCNT's counter */`
    `// 初始化计数器,暂停-》清零`
    `pcnt_counter_pause(unit);`
    `pcnt_counter_clear(unit);`

    `/* Install interrupt service and add isr callback handler */`
    `// 添加中断回调`
    `pcnt_isr_service_install(0);`
    `pcnt_isr_handler_add(unit, pcnt_example_intr_handler, (void *)unit);`

    `/* Everything is set up, now go to counting */`
    `// 开始计数`
    `pcnt_counter_resume(unit);`
   `}`

`void app_main(void)`
`{`
    `int pcnt_unit = PCNT_UNIT_0;`
    `/* Initialize LEDC to generate sample pulse signal */`
    `// 初始化ledc,配置timer和通道`
    `ledc_init();`

​    `/* Initialize PCNT event queue and PCNT functions */`
​    `// 初始化pcnt对了`
​    `pcnt_evt_queue = xQueueCreate(10, sizeof(pcnt_evt_t));`
​    `// pcnt初始化`
​    `pcnt_example_init(pcnt_unit);`

​    `int16_t count = 0;`
​    `pcnt_evt_t evt;`
​    `portBASE_TYPE res;`
​    `while (1) {`
​        `/* Wait for the event information passed from PCNT's interrupt handler.`

   * `Once received, decode the event type and print it on the serial monitor.`

     ​        `//接受中断事件数据`
     ​        `res = xQueueReceive(pcnt_evt_queue, &evt, 1000 / portTICK_PERIOD_MS);`
     ​        `if (res == pdTRUE) {//接收到数据`
     ​        `// 获取计数值`
     `pcnt_get_counter_value(pcnt_unit, &count);`
     `ESP_LOGI(TAG, "Event PCNT unit[%d]; cnt: %d", evt.unit, count);`
     `// status为中断返回的状态`
     `if (evt.status & PCNT_EVT_THRES_1) {`
     ​    `ESP_LOGI(TAG, "THRES1 EVT");`
     `}`
     `if (evt.status & PCNT_EVT_THRES_0) {`
     ​    `ESP_LOGI(TAG, "THRES0 EVT");`
     `}`
     `if (evt.status & PCNT_EVT_L_LIM) {`
     ​    `ESP_LOGI(TAG, "L_LIM EVT");`
     `}`
     `if (evt.status & PCNT_EVT_H_LIM) {`
     ​    `ESP_LOGI(TAG, "H_LIM EVT");`
     `}`
     `if (evt.status & PCNT_EVT_ZERO) {`
     ​    `ESP_LOGI(TAG, "ZERO EVT");`
     `}`
     ​        `} else {//一直打印计数值`
     `pcnt_get_counter_value(pcnt_unit, &count);`
     `ESP_LOGI(TAG, "Current counter value :%d", count);`
     ​        `}`
     ​    `}`
     `}`

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
学习ESP32S3开发板可以按照以下步骤进行: 1. 获取开发板:首先,你需要购买ESP32S3开发板。可以在电子市场或者在线商店购买到。 2. 安装开发环境:ESP32S3开发板使用ESP-IDF(Espressif IoT Development Framework)作为开发环境。你需要下载并安装ESP-IDF。 - 访问Espressif官方网站(https://www.espressif.com/),在下载页面找到ESP-IDF的最新版本。 - 根据官方提供的安装指南,按照你的操作系统(如Windows、Mac或Linux)进行安装。 3. 配置开发环境:在安装完成后,你需要进行一些配置。 - 配置路径:根据安装指南,将ESP-IDF的路径添加到你的系统环境变量中。 - 配置编译器:根据官方指南,设置合适的编译器路径。 4. 学习ESP-IDF文档:ESP-IDF提供了详细的文档,包括API参考、示例代码和开发指南等。仔细阅读文档,了解如何使用ESP32S3开发板和ESP-IDF进行开发。 - 可以从Espressif官方网站下载最新版本的文档。 - 阅读开发指南:开发指南将引导你建立第一个ESP32S3项目并编写代码。 - 查看示例代码:ESP-IDF提供了许多示例代码,可以帮助你学习各种功能和特性。 5. 尝试示例项目:ESP-IDF附带了一些示例项目,你可以尝试运行这些项目,了解开发流程和基本功能。 6. 实践项目:根据你的需求,设计和实践自己的ESP32S3项目。可以从简单的LED控制开始,逐渐扩展到更复杂的应用,如WiFi通信、传感器数据采集等。 7. 参考社区资源:ESP32S3有一个活跃的开发者社区,可以通过论坛、社交媒体或开发者博客等渠道获取更多帮助和资源。 记住,学习是一个不断实践和探索的过程。通过阅读文档、尝试示例代码和实践项目,你将逐渐掌握ESP32S3的开发技能。祝你学习愉快!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值