IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> ESP32-S2 PWM输入捕获 -> 正文阅读

[嵌入式]ESP32-S2 PWM输入捕获

一、概述

????????在STM32上配置输入捕获很简单,因为有硬件支持,同时有很好的库函数。而在ESP32-S2这个芯片是没有输入捕获这个功能,但是这个芯片有定时器和脉冲计数器,我们可以借助这两个功能实现输入捕获。所谓输入捕获就是捕捉输入信号的脉冲宽度与频率大小。

????????ESP32 芯片提供两组硬件定时器,每组包含两个通用硬件定时器。 所有定时器均为 64 位通用定时器,包括 16 位预分频器和 64 位自动重载向上/向下计数器。 定时器初始化 - 启动定时器前应设置的参数,以及每个设置提供的具体功能。

???????脉冲计数器(PCNT),用于统计输入信号的上升沿和/或下降沿的数量。ESP32-S2 集成了多个脉冲计数单元,1?每个单元都是包含多个通道的独立计数器。通道可独立配置为统计上升沿或下降沿数量的递增计数器或递减计数器。PCNT 通道可检测?边沿?信号及?电平?信号。对于比较简单的应用,检测边沿信号就足够了。PCNT 通道可检测上升沿信号、下降沿信号,同时也能设置为递增计数,递减计数,或停止计数。电平信号就是所谓的?控制信号,可用来控制边沿信号的计数模式。通过设置电平信号与边沿信号的检测模式,PCNT 单元可用作?正交解码器。

? ? ? ? 频率为周期的倒数?,而一分钟时间内的脉冲个数等于频率。例如下图,一分钟内脉冲个数为6个,频率为6Hz。

?????????占空比为一个周期内高电平的时间与一个周期总时间的比值? ? 占空比 =??t0_h? /? T。

二、程序设计

? ? ? ? 频率捕获程序设计,已知一分钟内的脉冲个数为频率,则需要配置一个计数器和一个一分钟的定时器。当计数器开始计数时开启定时器,一分钟后进入定时器中断,获取计数值。

? ? ? ? 占空比捕获程序设计,跟根据公式知道要计算高电平时间和周期总时间,则需要一个外部中断和一个定时器。当上升沿到来时清空定时器值,开始计数,下降沿到来获取定时器时间,这个时间为一个周期内高电平的时间,当第二个上升沿到来获取定时器时间,这个时间为一个周期的时间。

? ? ? ? 代码如下:

#include "bsp_capture.h"

extern EventGroupHandle_t xEventGroup_Handle;
esp_timer_handle_t esp_timer_handle; 
input_capture_t cap = {0};

static void IRAM_ATTR exti_isr_handler(void *arg)
{
    static uint8_t status = 0;
    static uint8_t count = 0;
    uint8_t gpio_level = gpio_get_level(EXTI_GPIO_PIN);
    
    if(status == 0 && gpio_level)
    {
        timer_set_counter_value(TIMER_GROUP,TIMER_INDEX,0x00000000ULL);
        status = 1;
    }
    else if(status == 1 && !gpio_level)
    {
        timer_get_counter_value(TIMER_GROUP,TIMER_INDEX,&cap.t0_h_time);
        status = 2;
    }
    else if(status == 2 && gpio_level)
    {
        timer_get_counter_value(TIMER_GROUP,TIMER_INDEX,&cap.cycle_time);
        status = 0;
        count++;
        if(count >= 3)
        {
            status = 3;
            BaseType_t xHigherPriorityTaskWoken, xResult;
            xResult = xEventGroupSetBitsFromISR(xEventGroup_Handle, 
                                                GET_DUTY_EVENT,
                                                &xHigherPriorityTaskWoken);
            if(xResult)
            {
                portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
            }
        }
    }
}


static void EXTI_Init(void)
{
   const gpio_config_t   exti_config = {
        .intr_type      = GPIO_INTR_ANYEDGE,
        .mode           = GPIO_MODE_INPUT,
        .pull_down_en   = GPIO_PULLDOWN_ENABLE,
        .pull_up_en     = GPIO_PULLUP_DISABLE,
        .pin_bit_mask   = (1ULL << EXTI_GPIO_PIN),
   };
   gpio_config(&exti_config);

   gpio_install_isr_service(ESP_INTR_FLAG_LEVEL2);
   gpio_isr_handler_add(EXTI_GPIO_PIN,exti_isr_handler,NULL);
}


static void Hardware_Timer_Init(void)
{
    const timer_config_t timer_config = {
        .alarm_en = TIMER_ALARM_DIS,             //到达计数值启动报警(计数值溢出,进入中断)
        .counter_en = TIMER_PAUSE,              //调用timer_init()后不启动计数,调用timer_start()才开始计数
        .counter_dir = TIMER_COUNT_UP,          //向上计数
        .auto_reload = TIMER_AUTORELOAD_EN,     //使能自动重装载
        .divider = 8,                           //分频值
    };
    timer_init(TIMER_GROUP,TIMER_INDEX,&timer_config);

    timer_set_counter_value(TIMER_GROUP,TIMER_INDEX,0x00000000ULL);
    timer_start(TIMER_GROUP,TIMER_INDEX);
}

static void Pcnt_Init(void)
{
    const pcnt_config_t pcnt_config = {
        .pulse_gpio_num = PCNT_INPUT_SIG_IO,
        .ctrl_gpio_num = PCNT_INPUT_CTRL_IO,
        .channel = PCNT_CHANNEL_0,
        .unit = PCNT_UNIT,
        .pos_mode = PCNT_COUNT_INC,         //计算正边沿
        .neg_mode = PCNT_COUNT_DIS,         //计算反边沿
        .lctrl_mode = PCNT_MODE_REVERSE,    //如果计数方向低,则反向计数
        .hctrl_mode = PCNT_MODE_KEEP,       //如果高,则保持主计数器模式
        .counter_h_lim = PCNT_H_LIM_VAL,    //设置最大值
        .counter_l_lim = PCNT_L_LIM_VAL,
    };
    pcnt_unit_config(&pcnt_config);

    pcnt_set_filter_value(PCNT_UNIT, 100); // 第二个参数的注释为PCNT信号的滤波值,计数器在APB_CLK周期的任何持续时间比这短的脉冲将被忽略,当过滤器被启用时。
    pcnt_filter_enable(PCNT_UNIT);

    pcnt_counter_pause(PCNT_UNIT);          //暂停计数
    pcnt_counter_clear(PCNT_UNIT);          //清除计数

    pcnt_counter_resume(PCNT_UNIT);         //开始计数
}

//软件定时器回调函数
static void esp_timer_cb(void *arg)
{
    static uint8_t count = 0;
    BaseType_t xHigherPriorityTaskWoken, xResult;

    pcnt_get_counter_value(PCNT_UNIT,&cap.frequency);                     //每秒钟的脉冲个数就是频率
    pcnt_counter_clear(PCNT_UNIT);
    count++;
    if(count >= 3)
    {
        xResult = xEventGroupSetBitsFromISR(xEventGroup_Handle, 
                                            GET_FREQUENCY_EVENT,
                                            &xHigherPriorityTaskWoken);
        if(xResult)
        {
            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
        }
    }
}

static void Software_Timer_Init(void)
{
	const esp_timer_create_args_t fw_timer = {
			.callback = &esp_timer_cb,                            
			.arg = NULL,                                         
			.name = "esp_timer",                                   
	};
 
	esp_timer_create(&fw_timer,&esp_timer_handle);               
    esp_timer_start_periodic(esp_timer_handle,1000 * 1000);
}

void  capture_duty_install_service(void)
{
    Hardware_Timer_Init();
    EXTI_Init();
}

void  capture_duty_uninstall_service(void)
{
    gpio_isr_handler_remove(EXTI_GPIO_PIN);
    timer_deinit(TIMER_GROUP,TIMER_INDEX);
}

void  capture_frequency_install_service(void)
{
    Pcnt_Init();
    Software_Timer_Init();
}

void  capture_frequency_uninstall_service(void)
{
    esp_timer_stop(esp_timer_handle);
    esp_timer_delete(esp_timer_handle);
}
#ifndef _BSP_CAPTURE_H_
#define _BSP_CAPTURE_H_

#include "sdkconfig.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "driver/timer.h"
#include "esp_timer.h"
#include "driver/pcnt.h"

#define EXTI_GPIO_PIN           GPIO_NUM_10

#define TIMER_GROUP             TIMER_GROUP_0
#define TIMER_INDEX             TIMER_0
#define TIMER_INIR              TIMER_INTR_T0

#define PCNT_UNIT               PCNT_UNIT_0
#define PCNT_H_LIM_VAL          30000
#define PCNT_L_LIM_VAL          -30000
#define PCNT_INPUT_SIG_IO       GPIO_NUM_10         //脉冲输入GPIO
#define PCNT_INPUT_CTRL_IO      GPIO_NUM_NC         //控制GPIO     Control GPIO HIGH = count up, LOW = count down 

#define	GET_DUTY_EVENT		    (0X01 << 0)
#define	GET_FREQUENCY_EVENT		(0X01 << 1)

typedef struct 
{
    uint64_t   t0_h_time;
    uint64_t   cycle_time;
    int16_t    frequency;
}input_capture_t;

extern input_capture_t cap;

void  capture_duty_install_service(void);
void  capture_duty_uninstall_service(void);
void  capture_frequency_install_service(void);
void  capture_frequency_uninstall_service(void);
#endif

????????pwm程序

#include "bsp_pwm.h"

void PWM_Init(void)
{
    const ledc_channel_config_t  pwm_channel_config = {
        .gpio_num = PWM_GPIO_PIN,                 
        .speed_mode = PWM_MODE,                     
        .channel  = PWM_CHABNNEL,                  
        .intr_type = LEDC_INTR_DISABLE,            
        .timer_sel = TIMER_NUM,                     
        .duty = 0,                                  
        .hpoint = 0,                              
    };
    ledc_channel_config(&pwm_channel_config);

    const ledc_timer_config_t pwm_timer_config = {
        .duty_resolution = LEDC_TIMER_BIT,   
        .freq_hz = FREQUENCY,                   
        .speed_mode = PWM_MODE,     
        .timer_num = TIMER_NUM,                  
        .clk_cfg = LEDC_USE_APB_CLK,                
    };
    ledc_timer_config(&pwm_timer_config);

    ledc_fade_func_install(0);
}


void PWM_Set_Duty(uint32_t duty)
{
    if(duty > 100) 
    {
        duty = 100;
    }
    duty = (float)duty / 100 * LEDC_MAX_DUTY;

    ledc_set_duty(PWM_MODE,PWM_CHABNNEL,duty);
    ledc_update_duty(PWM_MODE,PWM_CHABNNEL);
}

#ifndef _BSP_PWM_H_
#define _BSP_PWM_H_

#include <math.h>
#include "driver/ledc.h"

#define FREQUENCY               500
#define TIMER_NUM               LEDC_TIMER_0
#define PWM_GPIO_PIN            GPIO_NUM_6
#define PWM_CHABNNEL            LEDC_CHANNEL_0
#define PWM_MODE                LEDC_LOW_SPEED_MODE
#define LEDC_TIMER_BIT          LEDC_TIMER_10_BIT
#define LEDC_MAX_DUTY          (int)(pow(2,LEDC_TIMER_BIT)-1)       //根据定时器位数,计算出可用分辨率的最大值

void PWM_Init(void);
void PWM_Set_Duty(uint32_t duty);
#endif

? ? ? ? 主程序


#include "bsp_capture.h"
#include "bsp_pwm.h"
#include "esp_log.h"

static const char *TAG = "cap_test";

EventGroupHandle_t   xEventGroup_Handle = NULL;

void capture_stak(void *arg)
{
    EventBits_t uxBits;

    capture_duty_install_service();
    PWM_Init();
    PWM_Set_Duty(50);

    while(1)
    {
        uxBits = xEventGroupWaitBits(xEventGroup_Handle,    
                                     GET_DUTY_EVENT | GET_FREQUENCY_EVENT, 
                                     pdTRUE,        
                                     pdFALSE,        
                                     portMAX_DELAY); 
        if(uxBits & GET_DUTY_EVENT)
        {
            ESP_LOGI(TAG,"t0_h:%llu T:%llu duty:%.1f%%\n",cap.t0_h_time,cap.cycle_time,(float)cap.t0_h_time / cap.cycle_time * 100);
            capture_duty_uninstall_service();
            capture_frequency_install_service();
        }                       
        else if(uxBits & GET_FREQUENCY_EVENT)
        {
           ESP_LOGI(TAG,"frequency :%d Hz\n",-cap.frequency); 
           capture_frequency_uninstall_service();
        }
    }
}

void app_main(void)
{
    xEventGroup_Handle = xEventGroupCreate();

	xTaskCreate(capture_stak,"capture_stak",1024 * 5,NULL,5,NULL);
}

三、总结

? ? ? ? 设置PWM输出 频率为500Hz 、占空比为1%的脉冲波形,软件捕获结果如下图。

???????? 设置PWM输出 频率为500Hz 、占空比为90%的脉冲波形,软件捕获结果如下图。

????????设置PWM输出 频率为19KHz 、占空比为10%的脉冲波形,软件捕获结果如下图。(问题:PWM占空比低于百分之10捕获后值不对)

设置PWM输出 频率为19KHz 、占空比为90%的脉冲波形,软件捕获结果如下图。

? ? ? ? 从数据采集过程中发现PWM频率越低,捕获效果越好,PWM频率越高,捕获效果越差。
工程示例,提取码:gwoxhttps://pan.baidu.com/s/1mZyTcogycGl1XSUEPd20fA

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-09-04 01:27:45  更:2022-09-04 01:30:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 22:32:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码