上次制作无屏幕版本的万用表在测量交流信号时,没有频率和占空比测量功能,这次使用ESP32采集交流信号,计算频率和占空比。
频率为周期的倒数,占空比高电平时间T1与总周期时间T的比值,范围0%-100%
下面介绍3种实现方法,也是我逐一调试程序验证的过程。
1、先试一下用MCPWM捕获功能实现频率测量,参照官方例程配置比较简单
- void M_pwm_cap_start()
- {
- M_pwm_init();
- //7. Capture configuration
- //comment if you don't want to use capture submodule, also u can comment the capture gpio signals
- //configure CAP0, CAP1 and CAP2 signal to start capture counter on rising edge
- //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
- //In general practice you can connect Capture to external signal, measure time between rising edge or falling edge and take action accordingly
- 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
- //enable interrupt, so each this a rising edge occurs interrupt is triggered
- //create queue
- cap_queue = xQueueCreate(1, sizeof(capture)); //comment if you don't want to use capture module
- MCPWM[MCPWM_UNIT_0]->int_ena.val = CAP0_INT_EN; //Enable interrupt on CAP0, CAP1 and CAP2 signal
- mcpwm_isr_register(MCPWM_UNIT_0, isr_handler, NULL, ESP_INTR_FLAG_IRAM, NULL); //Set ISR Handler
- xTaskCreate(disp_captured_signal, "mcpwm_config", 4096, NULL, 5, NULL); //comment if you don't want to use capture module
- }
复制代码
- static void IRAM_ATTR isr_handler()
- {
- uint32_t mcpwm_intr_status;
- capture evt;
- mcpwm_intr_status = MCPWM[MCPWM_UNIT_0]->int_st.val; //Read interrupt status
- if (mcpwm_intr_status & CAP0_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
- evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP0); //get capture signal counter value
- evt.sel_cap_signal = MCPWM_SELECT_CAP0;
- xQueueSendFromISR(cap_queue, &evt, NULL);
- }
- MCPWM[MCPWM_UNIT_0]->int_clr.val = mcpwm_intr_status;
- }
复制代码
- static void disp_captured_signal(void *arg)
- {
- int add=0;
- float frequency;
- uint32_t *current_cap_value = (uint32_t *)malloc(2*sizeof(uint32_t));
- uint32_t *previous_cap_value = (uint32_t *)malloc(2*sizeof(uint32_t));
- capture evt;
- while (1) {
- xQueueReceive(cap_queue, &evt, portMAX_DELAY);
- if (evt.sel_cap_signal == MCPWM_SELECT_CAP0) {
- current_cap_value[0] = evt.capture_signal - previous_cap_value[0];
- previous_cap_value[0] = evt.capture_signal;
- current_cap_value[0] = (current_cap_value[0] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
- if(++add>50)
- {
- add=0;
- frequency=1000000/current_cap_value[0];
- printf("CAP0 : %d us\n", current_cap_value[0]);//print high time
- printf("frequency : %.2f Hz\n", frequency);
- }
- }
- vTaskDelay(10/portTICK_RATE_MS);
- }
- }
复制代码
用信号发生器产生PWM信号,测试试一下测量频率
对于几百Hz以内测量还是很准确的,但是没找到测量占空比的方法,测试其他方法。
2、利用ESP32的pcnt脉冲计数器,先初始化
- static void pcnt_example_init(void)
- {
- /* 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,
- .ctrl_gpio_num = PCNT_INPUT_CTRL_IO,
- .channel = PCNT_CHANNEL_0,
- .unit = PCNT_TEST_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_KEEP, // 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 */
- pcnt_set_filter_value(PCNT_TEST_UNIT, 100);
- pcnt_filter_enable(PCNT_TEST_UNIT);
- /* Set threshold 0 and 1 values and enable events to watch */
- pcnt_set_event_value(PCNT_TEST_UNIT, PCNT_EVT_THRES_1, PCNT_THRESH1_VAL);
- pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_THRES_1);
- pcnt_set_event_value(PCNT_TEST_UNIT, PCNT_EVT_THRES_0, PCNT_THRESH0_VAL);
- pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_THRES_0);
- /* Enable events on zero, maximum and minimum limit values */
- pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_ZERO);
- pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_H_LIM);
- pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_L_LIM);
- /* Initialize PCNT's counter */
- pcnt_counter_pause(PCNT_TEST_UNIT);
- pcnt_counter_clear(PCNT_TEST_UNIT);
- /* Register ISR handler and enable interrupts for PCNT unit */
- pcnt_isr_register(pcnt_example_intr_handler, NULL, 0, &user_isr_handle);
- pcnt_intr_enable(PCNT_TEST_UNIT);
- /* Everything is set up, now go to counting */
- pcnt_counter_resume(PCNT_TEST_UNIT);
- }
复制代码
然后用定时器,每秒钟获取一次计数值,这样就能算出信号频率,每秒钟的脉冲个数就是频率。
- void test_timer_periodic_cb(void *arg) {
- int16_t count = 0;
- pcnt_get_counter_value(PCNT_TEST_UNIT, &count);
- printf("frequency :%d Hz\n ", count);
- pcnt_counter_clear(PCNT_TEST_UNIT);
- }
复制代码
这个方法可以测量比较高的频率,下图是测量4513Hz频率信号,但是和第一种方法一样,测量不了占空比,继续测量其他方法。
3、利用ESP32外部中断引脚+定时器计数器,在上升沿中断时使能计数器,下降沿中断时获取高电平计数值n1,继续计数,直到又一个上升沿中断获取计数值n2,根据计数器的周期,可以得到计数1次的周期,从而得到高电平时间和整个周期的时间,然后就能算出占空比和频率了。
这种方式可以精确的测量出占空比和频率,但存在一个问题,当测量频率比较高时,要频繁的进入中断程序,大大降低了CPU的效率,继续改进。
4、由于我们不需要在每个信号周期都计算一次占空比和频率,所以可以再加入一个定时器,每隔1秒中断一次,在中断程序中开启外部中断,在开启外部中断测量了一次完整的信号后,关闭外部中断,这样测量速率就由这个定时器决定,不会频繁进入外部中断,经过几个小时的程序调试,最终完成占空比和频率的测量程序,下图是测量120Hz,20%的PWM信号。
|