搜索
bottom↓
回复: 6

开源ESP32彩屏WIFI/BLE智能万用表制作记录(3.频率占空比测量)

[复制链接]

出0入8汤圆

发表于 2021-8-9 15:34:02 | 显示全部楼层 |阅读模式
上次制作无屏幕版本的万用表在测量交流信号时,没有频率和占空比测量功能,这次使用ESP32采集交流信号,计算频率和占空比。



频率为周期的倒数,占空比高电平时间T1与总周期时间T的比值,范围0%-100%

下面介绍3种实现方法,也是我逐一调试程序验证的过程。

1、先试一下用MCPWM捕获功能实现频率测量,参照官方例程配置比较简单


  1. void M_pwm_cap_start()
  2. {
  3.     M_pwm_init();
  4.     //7. Capture configuration
  5.     //comment if you don't want to use capture submodule, also u can comment the capture gpio signals
  6.     //configure CAP0, CAP1 and CAP2 signal to start capture counter on rising edge
  7.     //we generate a gpio_test_signal of 20ms on GPIO 12 and connect it to one of the capture signal, the disp_captured_function displays the time between rising edge
  8.     //In general practice you can connect Capture  to external signal, measure time between rising edge or falling edge and take action accordingly
  9.     mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, MCPWM_POS_EDGE, 0);  //capture signal on rising edge, prescale = 0 i.e. 800,000,000 counts is equal to one second
  10.     //enable interrupt, so each this a rising edge occurs interrupt is triggered
  11.    
  12.    //create queue
  13.     cap_queue = xQueueCreate(1, sizeof(capture)); //comment if you don't want to use capture module
  14.     MCPWM[MCPWM_UNIT_0]->int_ena.val = CAP0_INT_EN;  //Enable interrupt on  CAP0, CAP1 and CAP2 signal
  15.     mcpwm_isr_register(MCPWM_UNIT_0, isr_handler, NULL, ESP_INTR_FLAG_IRAM, NULL);  //Set ISR Handler
  16.     xTaskCreate(disp_captured_signal, "mcpwm_config", 4096, NULL, 5, NULL);  //comment if you don't want to use capture module
  17. }
复制代码
  1. static void IRAM_ATTR isr_handler()
  2. {
  3.     uint32_t mcpwm_intr_status;
  4.     capture evt;
  5.     mcpwm_intr_status = MCPWM[MCPWM_UNIT_0]->int_st.val; //Read interrupt status
  6.     if (mcpwm_intr_status & CAP0_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
  7.         evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP0); //get capture signal counter value
  8.         evt.sel_cap_signal = MCPWM_SELECT_CAP0;
  9.         xQueueSendFromISR(cap_queue, &evt, NULL);
  10.     }

  11.     MCPWM[MCPWM_UNIT_0]->int_clr.val = mcpwm_intr_status;
  12. }
复制代码
  1. static void disp_captured_signal(void *arg)
  2. {
  3.         int add=0;
  4.     float frequency;
  5.     uint32_t *current_cap_value = (uint32_t *)malloc(2*sizeof(uint32_t));
  6.     uint32_t *previous_cap_value = (uint32_t *)malloc(2*sizeof(uint32_t));
  7.     capture evt;
  8.     while (1) {
  9.         xQueueReceive(cap_queue, &evt, portMAX_DELAY);
  10.         if (evt.sel_cap_signal == MCPWM_SELECT_CAP0) {
  11.             current_cap_value[0] = evt.capture_signal - previous_cap_value[0];
  12.             previous_cap_value[0] = evt.capture_signal;
  13.             current_cap_value[0] = (current_cap_value[0] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
  14.             
  15.                         if(++add>50)
  16.                         {
  17.                                 add=0;
  18.                 frequency=1000000/current_cap_value[0];
  19.                                 printf("CAP0 : %d us\n", current_cap_value[0]);//print high time
  20.                                 printf("frequency : %.2f Hz\n", frequency);
  21.                         }
  22.         }

  23.         vTaskDelay(10/portTICK_RATE_MS);
  24.     }
  25. }
复制代码

用信号发生器产生PWM信号,测试试一下测量频率



对于几百Hz以内测量还是很准确的,但是没找到测量占空比的方法,测试其他方法。

2、利用ESP32的pcnt脉冲计数器,先初始化

  1. static void pcnt_example_init(void)
  2. {
  3.     /* Prepare configuration for the PCNT unit */
  4.     pcnt_config_t pcnt_config = {
  5.         // Set PCNT input signal and control GPIOs
  6.         .pulse_gpio_num = PCNT_INPUT_SIG_IO,
  7.         .ctrl_gpio_num = PCNT_INPUT_CTRL_IO,
  8.         .channel = PCNT_CHANNEL_0,
  9.         .unit = PCNT_TEST_UNIT,
  10.         // What to do on the positive / negative edge of pulse input?
  11.         .pos_mode = PCNT_COUNT_INC,   // Count up on the positive edge
  12.         .neg_mode = PCNT_COUNT_DIS,   // Keep the counter value on the negative edge
  13.         // What to do when control input is low or high?
  14.         .lctrl_mode = PCNT_MODE_KEEP, // Reverse counting direction if low
  15.         .hctrl_mode = PCNT_MODE_KEEP,    // Keep the primary counter mode if high
  16.         // Set the maximum and minimum limit values to watch
  17.         .counter_h_lim = PCNT_H_LIM_VAL,
  18.         .counter_l_lim = PCNT_L_LIM_VAL,
  19.     };
  20.     /* Initialize PCNT unit */
  21.     pcnt_unit_config(&pcnt_config);

  22.     /* Configure and enable the input filter */
  23.     pcnt_set_filter_value(PCNT_TEST_UNIT, 100);
  24.     pcnt_filter_enable(PCNT_TEST_UNIT);

  25.     /* Set threshold 0 and 1 values and enable events to watch */
  26.     pcnt_set_event_value(PCNT_TEST_UNIT, PCNT_EVT_THRES_1, PCNT_THRESH1_VAL);
  27.     pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_THRES_1);
  28.     pcnt_set_event_value(PCNT_TEST_UNIT, PCNT_EVT_THRES_0, PCNT_THRESH0_VAL);
  29.     pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_THRES_0);
  30.     /* Enable events on zero, maximum and minimum limit values */
  31.     pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_ZERO);
  32.     pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_H_LIM);
  33.     pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_L_LIM);

  34.     /* Initialize PCNT's counter */
  35.     pcnt_counter_pause(PCNT_TEST_UNIT);
  36.     pcnt_counter_clear(PCNT_TEST_UNIT);

  37.     /* Register ISR handler and enable interrupts for PCNT unit */
  38.     pcnt_isr_register(pcnt_example_intr_handler, NULL, 0, &user_isr_handle);
  39.     pcnt_intr_enable(PCNT_TEST_UNIT);

  40.     /* Everything is set up, now go to counting */
  41.     pcnt_counter_resume(PCNT_TEST_UNIT);
  42. }
复制代码

然后用定时器,每秒钟获取一次计数值,这样就能算出信号频率,每秒钟的脉冲个数就是频率。
  1. void test_timer_periodic_cb(void *arg) {
  2.     int16_t count = 0;
  3.     pcnt_get_counter_value(PCNT_TEST_UNIT, &count);
  4.     printf("frequency :%d Hz\n ", count);
  5.     pcnt_counter_clear(PCNT_TEST_UNIT);
  6. }
复制代码


这个方法可以测量比较高的频率,下图是测量4513Hz频率信号,但是和第一种方法一样,测量不了占空比,继续测量其他方法。


3、利用ESP32外部中断引脚+定时器计数器,在上升沿中断时使能计数器,下降沿中断时获取高电平计数值n1,继续计数,直到又一个上升沿中断获取计数值n2,根据计数器的周期,可以得到计数1次的周期,从而得到高电平时间和整个周期的时间,然后就能算出占空比和频率了。



这种方式可以精确的测量出占空比和频率,但存在一个问题,当测量频率比较高时,要频繁的进入中断程序,大大降低了CPU的效率,继续改进。

4、由于我们不需要在每个信号周期都计算一次占空比和频率,所以可以再加入一个定时器,每隔1秒中断一次,在中断程序中开启外部中断,在开启外部中断测量了一次完整的信号后,关闭外部中断,这样测量速率就由这个定时器决定,不会频繁进入外部中断,经过几个小时的程序调试,最终完成占空比和频率的测量程序,下图是测量120Hz,20%的PWM信号。





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出1325入193汤圆

发表于 2021-8-9 16:00:27 | 显示全部楼层
由于我们不需要在每个信号周期都计算一次占空比和频率,所以可以再加入一个定时器,每隔1秒中断一次,在中断程序中开启外部中断,在开启外部中断测量了一次完整的信号后,关闭外部中断,这样测量速率就由这个定时器决定,不会频繁进入外部中断,

频率高了  会不会响应不过来

出235入235汤圆

发表于 2021-8-9 16:29:32 | 显示全部楼层
ESP32玩到极致啊

出0入8汤圆

 楼主| 发表于 2021-8-9 18:18:46 | 显示全部楼层
lb0857 发表于 2021-8-9 16:00
由于我们不需要在每个信号周期都计算一次占空比和频率,所以可以再加入一个定时器,每隔1秒中断一次,在中 ...

1秒开关一次外部中断,完全没问题

出0入8汤圆

 楼主| 发表于 2021-8-9 18:23:36 | 显示全部楼层
lb0857 发表于 2021-8-9 16:00
由于我们不需要在每个信号周期都计算一次占空比和频率,所以可以再加入一个定时器,每隔1秒中断一次,在中 ...

外部中断太快确实会响应不过来,不过我用在万用表上足够了

出5入42汤圆

发表于 2021-8-10 10:34:53 | 显示全部楼层
七云物联 发表于 2021-8-9 18:23
外部中断太快确实会响应不过来,不过我用在万用表上足够了

楼主,你这方法测高频信号不合适,误差会比较大,不信你用信号发生器试试。
原因是高频信号周期非常短,中断响应加上CPU开销,你只测一个脉冲时间,而且CPU算倒数,有计算误差。

出0入0汤圆

发表于 2021-8-20 08:54:45 | 显示全部楼层
七云物联 发表于 2021-8-9 18:18
1秒开关一次外部中断,完全没问题

有没有试过用RMT模块看看
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-16 12:26

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表