ESP32 PULSE CNT脉冲计数器使用
基于esp32 IDF进行开发,实现对直流有刷电机的控制,同时要对转速通过霍尔传感器进行计数。esp32内部的外设还是挺多,所以一个功能也可以用不同的思路来实现。
ESP32内部有个专门驱动电机的mcpwm,该模块可以产生直驱动电机需要的PWM信号,同时还带的有捕获功能,可以对引脚上的脉冲记性计数,脉宽测量。本来是想用MCPWM的捕获功能来实现霍尔计数的的。但是之后看esp32的参考手册发现还有个PULSE CNT功能是专门进行脉冲个数计数的,这个更贴合于霍尔计数的使用。mcpwm内的捕获确实也可以实现,只是他获取到的是发生跳边沿之后之前的脉宽长度。所以假如要根据脉宽做软件滤波的话这个功能会更合适。
而PULSE CNT功能它是可以直接硬件滤波,把变化过快的脉冲直接滤掉,避免进行对干扰信号计数。所以这个对于容易受电机干扰的霍尔传感器计数再适合不过。
下面就对PULSE CNT(下面就简称pcnt)功能做个大概介绍:他的功能很简单就是实现脉冲个数的计数。如果说要和STM32内部的一个功能类比的话,更像stm32定时器中的ENCODER编码器功能,我用stm32会比较多,所以自然而然的就会把他们进行对比。
pcnt内部一共有8组单元,每个单元有两个通道,每个通道都有一个脉冲输入脚和控制脚。这个脉冲输入脚很好理解,那么这个控制脚有什么用呢?这个脚也不难,通过软件配置可以用这个脚控制计数器是向上还是向下计数,当然软件里面也可以配置不使用。当然还有个地方需要注意,就是每个单元两个通道是连接在一个计数器上面的,所以通过这两个通道就可以实现对编码器的计数功能。但是假如我要对两路霍尔进行计数就必须要用两个单元。
pcnt的中断源呢一共有五个:
- L_LIM 最小计数值中断,意思就是达到最小计数值的时候就会触发该中断,最小计数值在初始化的时候有配置
- H_LIM 最大计数值中断,意思是达到最大计数值的时候回触发该中断
- THRES_0 阈值0 中断,也就是自己设定一个值,当计数到达该值的时候就会触发中断
- THRES_1 阈值1中断,和阈值0功能一样,只是可以设定两个阈值
- ZERO 计数为0 中断,当计数器值记到0时产生该中断
因为我本次没有用到中断,所以中断使用的例子请参考官方IDF中的examples。
在深入进去看ESP官方资料的时候发现一个问题,pcnt在SDK寄存器列表里面有status_unit寄存器但是在参考手册上是没有的。所以吐槽一下使用国产的IC,这些资料的完整一致性有时候还真是经不起排查推敲。之前还发现sdk的一个使用的bug,反馈给官方,说回复我一个多星期现在也没回复,所以有的bug只能自己趟过然后记录下来。
下面就列下我自己基于esp-idf写的hall计数函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
pcnt_unit_t hallPcntUint[2]={HALL_PCNT_UINT_L,HALL_PCNT_UINT_R}; void hallInit() { pcnt_config_t pcnt_config = { // Set PCNT input signal and control GPIOs .pulse_gpio_num = HALL_GPIO_L, .ctrl_gpio_num = -1, .channel = PCNT_CHANNEL_0, .unit = HALL_PCNT_UINT_L, // 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 = 30000, .counter_l_lim = 0, }; /* Initialize PCNT unit */ pcnt_unit_config(&pcnt_config); pcnt_config.pulse_gpio_num=HALL_GPIO_R; pcnt_config.unit=HALL_PCNT_UINT_R; pcnt_unit_config(&pcnt_config); //初始化pcnt uint1 /* Configure and enable the input filter */ pcnt_set_filter_value(HALL_PCNT_UINT_L, 1000); pcnt_filter_enable(HALL_PCNT_UINT_L); pcnt_set_filter_value(HALL_PCNT_UINT_R, 1000); pcnt_filter_enable(HALL_PCNT_UINT_R); /* Initialize PCNT's counter */ pcnt_counter_pause(HALL_PCNT_UINT_L); pcnt_counter_clear(HALL_PCNT_UINT_L); pcnt_counter_pause(HALL_PCNT_UINT_R); pcnt_counter_clear(HALL_PCNT_UINT_R); /* Everything is set up, now go to counting */ } void hallStartCounter(uint8_t hall) { pcnt_counter_pause(hallPcntUint[hall]); pcnt_counter_clear(hallPcntUint[hall]); pcnt_counter_resume(hallPcntUint[hall]); } void hallStopCounter(uint8_t hall) { pcnt_counter_pause(hallPcntUint[hall]); pcnt_counter_clear(hallPcntUint[hall]); } void hallClearCounter(uint8_t hall) { pcnt_counter_clear(hallPcntUint[hall]); } int16_t hallGetCounter(uint8_t hall) { int16_t count; pcnt_get_counter_value(hallPcntUint[hall],&count); return count; } |