ESP32: Pulse counter module released (#2739)

* ESP32: Added pulsecnt module

The pulsecnt module let's you use the ESP32's pulse counter capabilities from Lua.

* ESP32: Pulsecnt module. Better/faster callback.

Reduced the amount of callback variables to speed things up and shift more logic to Lua than in the C code.

* ESP32: Completed docs for pulsecnt

* ESP32: Final release of pulsecnt

* ESP32: Production release of pulsecnt

* ESP32: Release (tweaked docs)

* ESP32: Pulse Counter Release. Cleaned up .gitignore

* ESP32: Pulse counter release (changed ch1 gpio to int to match ch0)
This commit is contained in:
John Lauer 2019-06-12 13:34:25 -07:00 committed by Arnim Läuger
parent 6d0e45793f
commit 8b9794b99d
4 changed files with 1089 additions and 0 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ tools/toolchains
.cproject
.project
.settings/
.vscode/**

View File

@ -271,4 +271,10 @@ config LUA_MODULE_TIME
help
Includes the time module.
config LUA_MODULE_PULSECNT
bool "Pulse counter module"
default "n"
help
Includes the pulse counter module.
endmenu

View File

@ -0,0 +1,732 @@
/*
Pulse counter module for ESP32 to allow interfacing from Lua
Authored by: ChiliPeppr (John Lauer) 2019
ESP-IDF docs for Pulse Counter
https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/pcnt.html
This example code is in the Public Domain (or CC0 licensed, at your option.)
Make modifications at will and freely.
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "module.h"
#include "lauxlib.h"
#include "lmem.h"
#include "platform.h"
#include "task/task.h"
#include "driver/pcnt.h"
#include "esp_log.h"
#include "lextra.h"
#include <string.h>
pcnt_isr_handle_t user_isr_handle = NULL; //user's ISR service handle
/* A sample structure to pass events from the PCNT
* interrupt handler to the main program.
*/
typedef struct {
uint8_t unit; // the PCNT unit that originated an interrupt
uint32_t status; // information on the event type that caused the interrupt
} pcnt_evt_t;
typedef struct{
// PulsecntHandle_t pcnt;
int32_t cb_ref, self_ref;
uint8_t unit; //=0, -- Defaults to 0. ESP32 has 0 thru 7 units to count pulses on
bool is_initted;
bool is_debug;
int ch0_pulse_gpio_num; // needs to be signed to support PCNT_PIN_NOT_USED of -1
int ch0_ctrl_gpio_num; // needs to be signed to support PCNT_PIN_NOT_USED of -1
bool ch0_is_defined;
uint8_t ch0_pos_mode;
uint8_t ch0_neg_mode;
uint8_t ch0_lctrl_mode;
uint8_t ch0_hctrl_mode;
int16_t ch0_counter_l_lim;
int16_t ch0_counter_h_lim;
bool ch1_is_defined;
int ch1_pulse_gpio_num; // needs to be signed to support PCNT_PIN_NOT_USED of -1
int ch1_ctrl_gpio_num; // needs to be signed to support PCNT_PIN_NOT_USED of -1
uint8_t ch1_pos_mode;
uint8_t ch1_neg_mode;
uint8_t ch1_lctrl_mode;
uint8_t ch1_hctrl_mode;
int16_t ch1_counter_l_lim;
int16_t ch1_counter_h_lim;
int16_t thresh0; // thresh0 is for the unit, not the channel
int16_t thresh1; // thresh1 is for the unit, not the channel
uint32_t counter;
} pulsecnt_struct_t;
typedef pulsecnt_struct_t *pulsecnt_t;
// array of 8 pulsecnt_struct_t pointers so we can reference by unit number
// this array gets filled in as we define pulsecnt_struct_t's during the create() method
static pulsecnt_t pulsecnt_selfs[8];
// Task ID to get ISR interrupt back into Lua callback
static task_handle_t pulsecnt_task_id;
/* Decode what PCNT's unit originated an interrupt
* and pass this information together with the event type
* the main program.
*/
static void IRAM_ATTR pulsecnt_intr_handler(void *arg)
{
uint32_t intr_status = PCNT.int_st.val;
uint8_t i;
pcnt_evt_t evt;
// portBASE_TYPE HPTaskAwoken = pdFALSE;
for (i = 0; i < 8; i++) {
if (intr_status & (BIT(i))) {
evt.unit = i;
/* Save the PCNT event type that caused an interrupt
to pass it to the main program */
evt.status = PCNT.status_unit[i].val;
PCNT.int_clr.val = BIT(i);
// post using lua task posting technique
// on lua_open we set pulsecnt_task_id as a method which gets called
// by Lua after task_post_high with reference to this self object and then we can steal the
// callback_ref and then it gets called by lua_call where we get to add our args
task_post_high(pulsecnt_task_id, (evt.status << 8) | evt.unit );
}
}
}
/*
This method gets called from the IRAM interuppt method via Lua's task queue. That lets the interrupt
run clean while this method gets called at a lower priority to not break the IRAM interrupt high priority.
We will do the actual callback here for the user with the fully decoded state of the pulse count.
The format of the callback to your Lua code is:
function onPulseCnt(unit, isThr0, isThr1, isLLim, isHLim, isZero)
*/
static void pulsecnt_task(task_param_t param, task_prio_t prio)
{
(void)prio;
// we bit packed the unit number and status into 1 int in the IRAM interrupt so need to unpack here
uint32_t unit = (uint32_t)param & 0xffu;
int status = ((uint32_t)param >> 8);
// int16_t cur_count, evt_count = 0;
// pcnt_get_counter_value(unit, &cur_count);
// try to get the pulsecnt_struct_t from the pulsecnt_selfs array
pulsecnt_t pc = pulsecnt_selfs[unit];
if (pc->is_debug) ESP_LOGI("pulsecnt", "Cb for unit %d, gpio: %d, ctrl_gpio: %d, pos_mode: %d, neg_mode: %d, lctrl_mode: %d, hctrl_mode: %d, counter_l_lim: %d, counter_h_lim: %d", pc->unit, pc->ch0_pulse_gpio_num, pc->ch0_ctrl_gpio_num, pc->ch0_pos_mode, pc->ch0_neg_mode, pc->ch0_lctrl_mode, pc->ch0_hctrl_mode, pc->ch0_counter_l_lim, pc->ch0_counter_h_lim );
bool thr1 = false;
bool thr0 = false;
bool l_lim = false;
bool h_lim = false;
bool zero = false;
// char evt_str[20]; // "-32768 or 32768" is 15 chars long and is the max string len
// bool is_multi_lim = false;
/*0: positive value to zero; 1: negative value to zero; 2: counter value negative ; 3: counter value positive*/
// uint8_t moving_to = status & 0x00000003u; // get first two bits
if (status & PCNT_STATUS_THRES1_M) {
// printf("THRES1 EVT\n");
thr1 = true;
// evt_count = pc->thresh1;
}
if (status & PCNT_STATUS_THRES0_M) {
// printf("THRES0 EVT\n");
thr0 = true;
// evt_count = pc->thresh0;
}
if (status & PCNT_STATUS_L_LIM_M) {
// printf("L_LIM EVT\n");
l_lim = true;
/*
// see if there is a ch0 and ch1 limit. if so then pass back string. otherwise pass back just one int.
if (pc->ch0_is_defined && pc->ch1_is_defined) {
// we need to pass back both because it's indeterminate which limit triggered this and there is
// no way to know from the ESP32 API
is_multi_lim = true;
sprintf(evt_str, "%d or %d", pc->ch0_counter_l_lim, pc->ch1_counter_l_lim);
} else if (pc->ch0_is_defined) {
// we have a ch0 item, so use its val
evt_count = pc->ch0_counter_l_lim;
} else if (pc->ch1_is_defined) {
// we have a ch1 item, so use its val
evt_count = pc->ch1_counter_l_lim;
}
*/
}
if (status & PCNT_STATUS_H_LIM_M) {
// printf("H_LIM EVT\n");
h_lim = true;
/*
// see if there is a ch0 and ch1 limit. if so then pass back string. otherwise pass back just one int.
if (pc->ch0_is_defined && pc->ch1_is_defined) {
// we need to pass back both because it's indeterminate which limit triggered this and there is
// no way to know from the ESP32 API
is_multi_lim = true;
sprintf(evt_str, "%d or %d", pc->ch0_counter_h_lim, pc->ch1_counter_h_lim);
} else if (pc->ch0_is_defined) {
// we have a ch0 item, so use its val
evt_count = pc->ch0_counter_h_lim;
} else if (pc->ch1_is_defined) {
// we have a ch1 item, so use its val
evt_count = pc->ch1_counter_h_lim;
}
*/
}
if (status & PCNT_STATUS_ZERO_M) {
// printf("ZERO EVT\n");
zero = true;
// evt_count = 0;
}
// at the start of turning on the pulse counter you get a stat=255 which is like a
// 1st time callback saying it's alive
// if (status == 255) {
// evt_count = -1;
// }
lua_State *L = lua_getstate ();
if (pc->cb_ref != LUA_NOREF)
{
// lua_rawgeti (L, LUA_REGISTRYINDEX, pulsecnt_cb_refs[unit]);
lua_rawgeti (L, LUA_REGISTRYINDEX, pc->cb_ref);
lua_pushinteger (L, unit);
// if (is_multi_lim) {
// lua_pushstring(L, evt_str);
// } else {
// lua_pushinteger (L, evt_count);
// }
// lua_pushinteger (L, cur_count);
lua_pushboolean (L, thr0);
lua_pushboolean (L, thr1);
lua_pushboolean (L, l_lim);
lua_pushboolean (L, h_lim);
lua_pushboolean (L, zero);
// lua_pushinteger (L, moving_to);
// lua_pushinteger (L, status);
lua_call (L, 6, 0);
} else {
if (pc->is_debug) ESP_LOGI("pulsecnt", "Could not find cb for unit %d with ptr %d", unit, pc->cb_ref);
}
}
// Get the pulsecnt.pctr object from the stack which is the struct pulsecnt_t
static pulsecnt_t pulsecnt_get( lua_State *L, int stack )
{
return (pulsecnt_t)luaL_checkudata(L, stack, "pulsecnt.pctr");
}
// Lua: pc:setFilter(clkCyclesToIgnore)
// Example: pc:setFilter(100) -- Ignore any signal shorter than 100 clock cycles. 80Mhz clock.
// You can ignore from 0 to 1023 clock cycles
static int pulsecnt_set_filter( lua_State *L ) {
int stack = 0;
// when we're called from an object the stack index 1 has our self ref
pulsecnt_t pc = pulsecnt_get(L, ++stack);
// Get clkCyclesToIgnore -- first arg after self arg
int clkCyclesToIgnore = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, clkCyclesToIgnore >= 0 && clkCyclesToIgnore <= 1023, stack, "The clkCyclesToIgnore number allows 0 to 1023");
pcnt_set_filter_value(pc->unit, clkCyclesToIgnore);
if (pc->is_debug) ESP_LOGI("pulsecnt", "Setup filter for unit %d with clkCyclesToIgnore %d", pc->unit, clkCyclesToIgnore);
return 0;
}
// Lua: pc:setThres(thresh0_val, thresh1_val)
// Example: pc:setThres(-5, 5)
// When you set the threshold, the pulse counter will be reset and the callback will be attached.
static int pulsecnt_set_thres( lua_State *L ) {
int stack = 0;
// when we're called from an object the stack index 1 has our self ref
pulsecnt_t pc = pulsecnt_get(L, ++stack);
// Get thresh0_val -- first arg after self arg
int thresh0_val = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, thresh0_val >= -32768 && thresh0_val <= 32767, stack, "The thresh0_val number allows -32768 to 32767");
// Get thresh0_val -- first arg after self arg
int thresh1_val = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, thresh1_val >= -32768 && thresh1_val <= 32767, stack, "The thresh1_val number allows -32768 to 32767");
// store it so we have it during callback
pc->thresh0 = thresh0_val;
pc->thresh1 = thresh1_val;
/* Set threshold 0 and 1 values and enable events to watch */
pcnt_set_event_value(pc->unit, PCNT_EVT_THRES_0, thresh0_val);
pcnt_event_enable(pc->unit, PCNT_EVT_THRES_0);
pcnt_set_event_value(pc->unit, PCNT_EVT_THRES_1, thresh1_val);
pcnt_event_enable(pc->unit, PCNT_EVT_THRES_1);
/* Enable events on zero, maximum and minimum limit values */
pcnt_event_enable(pc->unit, PCNT_EVT_ZERO);
pcnt_event_enable(pc->unit, PCNT_EVT_H_LIM);
pcnt_event_enable(pc->unit, PCNT_EVT_L_LIM);
/* Initialize PCNT's counter */
pcnt_counter_pause(pc->unit);
pcnt_counter_clear(pc->unit);
// check if there's a callback otherwise don't trigger interrupt, instead they may just be polling
if (pc->cb_ref != LUA_NOREF) {
/* Register ISR handler and enable interrupts for PCNT unit */
pcnt_isr_register(pulsecnt_intr_handler, NULL, 0, &user_isr_handle);
pcnt_intr_enable(pc->unit);
}
/* Everything is set up, now go to counting */
pcnt_counter_resume(pc->unit);
if (pc->is_debug) ESP_LOGI("pulsecnt", "Setup threshold for unit %d with thr0 %d, thr1 %d", pc->unit, thresh0_val, thresh1_val);
return 0;
}
// Lua: pc:rawSetEventVal(enumEventItem, val)
// enumEventItem can be pulsecnt.PCNT_EVT_L_LIM, PCNT_EVT_H_LIM, PCNT_EVT_THRES_0, PCNT_EVT_THRES_1, PCNT_EVT_ZERO
// Example: pc:rawSetEventVal(pulsecnt.PCNT_EVT_THRES_1, 100)
// The pulse counter is not cleared using this method so you can make the change on-the-fly, however, in practice
// it appears the pulse counter module does not pay attention to on-the-fly changes for the threshold value.
static int pulsecnt_set_event_value( lua_State *L ) {
int stack = 0;
// when we're called from an object the stack index 1 has our self ref
pulsecnt_t pc = pulsecnt_get(L, ++stack);
// Get enum -- first arg after self arg
int enumEventItem = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, enumEventItem >= -1 && enumEventItem <= 15, stack, "The enumEventItem number allows -1 to 15");
// Get val -- 2nd arg after self arg
int val = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, val >= -32768 && val <= 32767, stack, "The val number allows -32768 to 32767");
/* Set value for this unit */
pcnt_set_event_value(pc->unit, enumEventItem, val);
// store it so we have it during callback and reset event or ESP32 won't accept in new val
if (enumEventItem == PCNT_EVT_THRES_0) {
pc->thresh0 = val;
pcnt_event_enable(pc->unit, PCNT_EVT_THRES_0);
} else if (enumEventItem == PCNT_EVT_THRES_1) {
pc->thresh1 = val;
pcnt_event_enable(pc->unit, PCNT_EVT_THRES_1);
}
// check if there's a callback otherwise don't trigger interrupt, instead they may just be polling
if (pc->cb_ref != LUA_NOREF) {
// even though we likely have the interrupt enabled, this re-reads the vals we just set?
pcnt_intr_enable(pc->unit);
}
if (pc->is_debug) ESP_LOGI("pulsecnt", "Set enumEventItem %d for unit %d with val %d", enumEventItem, pc->unit, val);
return 0;
}
// Lua: retVal = pc:rawGetEventVal(enumEventItem)
// enumEventItem can be pulsecnt.PCNT_EVT_L_LIM, PCNT_EVT_H_LIM, PCNT_EVT_THRES_0, PCNT_EVT_THRES_1, PCNT_EVT_ZERO
// Example: retVal = pc:rawGetEventVal(pulsecnt.PCNT_EVT_THRES_1)
static int pulsecnt_get_event_value( lua_State *L ) {
int stack = 0;
// when we're called from an object the stack index 1 has our self ref
pulsecnt_t pc = pulsecnt_get(L, ++stack);
// Get enum -- first arg after self arg
int enumEventItem = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, enumEventItem >= -1 && enumEventItem <= 15, stack, "The enumEventItem number allows -1 to 15");
/* Set threshold 0 and 1 values and enable events to watch */
// pcnt_get_event_value(pcnt_unit_tunit, pcnt_evt_type_tevt_type, int16_t *value)
int16_t val;
pcnt_get_event_value(pc->unit, enumEventItem, &val);
if (pc->is_debug) ESP_LOGI("pulsecnt", "Get enumEventItem %d for unit %d with val %d", enumEventItem, pc->unit, val);
lua_pushinteger (L, val);
return 1;
}
// TODO: Not implemented yet.
// Lua example call:
// pc:config({
// ch0: {
// pulse_gpio_num = 4,
// ctrl_gpio_num = 5,
// },
// ch1: {
// pulse_gpio_num = 12,
// ctrl_gpio_num = 14,
// }
// })
static int pulsecnt_config(lua_State *L) {
// We are passed in a complex Lua table
int stack = 0;
// when we're called from an object the stack index 1 has our self ref
pulsecnt_t pc = pulsecnt_get(L, ++stack);
// get and set a self reference if we don't have one (which we likely won't have until this call occurs)
if (pc->self_ref == LUA_NOREF) {
lua_pushvalue(L, 1);
pc->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
// check to make sure the next stack item is a Lua table
int top = lua_gettop( L );
for (int stack = 1; stack <= top; stack++) {
if (lua_type( L, stack ) == LUA_TNIL)
continue;
if (lua_type( L, stack ) != LUA_TTABLE) {
// ws2812_cleanup( L, 0 );
luaL_checktype( L, stack, LUA_TTABLE ); // trigger error
return 0;
}
//
// retrieve ch0
//
lua_getfield( L, stack, "ch0" );
if (lua_type( L, stack ) != LUA_TTABLE) {
// ws2812_cleanup( L, 1 );
return luaL_argerror( L, stack, "ch0 needs to be a table of settings" );
}
// int gpio_num = luaL_checkint( L, -1 );
lua_pop( L, 1 );
}
return 0;
}
// This is called internally, not from Lua
static int pulsecnt_channel_config( lua_State *L, uint8_t channel ) {
int stack = 0;
// when we're called from an object the stack index 1 has our self ref
pulsecnt_t pc = pulsecnt_get(L, ++stack);
// get and set a self reference if we don't have one (which we likely won't have until this call occurs)
if (pc->self_ref == LUA_NOREF) {
lua_pushvalue(L, 1);
pc->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
// Get pulse_gpio_num -- first arg after self arg
int pulse_gpio_num = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, pulse_gpio_num >= -1 && pulse_gpio_num <= 40, stack, "The pulse_gpio_num number allows -1 to 40");
// Get ctrl_gpio_num -- 2nd arg
int ctrl_gpio_num = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, ctrl_gpio_num >= -1 && ctrl_gpio_num <= 40, stack, "The ctrl_gpio_num number allows -1 to 40");
// Get pos_mode -- 3rd arg
int pos_mode = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, pos_mode >= 0 && pos_mode <= 2, stack, "The pos_mode number allows 0, 1, or 2");
// Get neg_mode -- 4th arg
int neg_mode = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, neg_mode >= 0 && neg_mode <= 2, stack, "The neg_mode number allows 0, 1, or 2");
// Get lctrl_mode -- 5th arg
int lctrl_mode = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, lctrl_mode >= 0 && lctrl_mode <= 2, stack, "The lctrl_mode number allows 0, 1, or 2");
// Get hctrl_mode -- 6th arg
int hctrl_mode = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, hctrl_mode >= 0 && hctrl_mode <= 2, stack, "The hctrl_mode number allows 0, 1, or 2");
// Get counter_l_lim -- 7th arg. Defaults to -32767. Range int16 [-32768 : 32767]
int counter_l_lim = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, counter_l_lim >= -32768 && counter_l_lim <= 32767, stack, "The counter_l_lim number allows -32768 to 32767");
// Get counter_l_lim -- 7th arg. Defaults to -32767. Range int16 [-32768 : 32767]
int counter_h_lim = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, counter_h_lim >= -32768 && counter_h_lim <= 32767, stack, "The counter_h_lim number allows -32768 to 32767");
if (channel == 0) {
pc->ch0_is_defined = true;
pc->ch0_pulse_gpio_num = pulse_gpio_num;
pc->ch0_ctrl_gpio_num = ctrl_gpio_num;
pc->ch0_pos_mode = pos_mode;
pc->ch0_neg_mode = neg_mode;
pc->ch0_lctrl_mode = lctrl_mode;
pc->ch0_hctrl_mode = hctrl_mode;
pc->ch0_counter_l_lim = counter_l_lim;
pc->ch0_counter_h_lim = counter_h_lim;
} else {
pc->ch1_is_defined = true;
pc->ch1_pulse_gpio_num = pulse_gpio_num;
pc->ch1_ctrl_gpio_num = ctrl_gpio_num;
pc->ch1_pos_mode = pos_mode;
pc->ch1_neg_mode = neg_mode;
pc->ch1_lctrl_mode = lctrl_mode;
pc->ch1_hctrl_mode = hctrl_mode;
pc->ch1_counter_l_lim = counter_l_lim;
pc->ch1_counter_h_lim = counter_h_lim;
}
/* Initialize PCNT functions:
* - configure and initialize PCNT
* - set up the input filter
* - set up the counter events to watch
*/
/* Prepare configuration for the PCNT unit */
pcnt_config_t pcnt_config = {
// Set PCNT input signal and control GPIOs
.pulse_gpio_num = pulse_gpio_num,
.ctrl_gpio_num = ctrl_gpio_num,
.channel = channel,
.unit = pc->unit, // PCNT_TEST_UNIT
// What to do on the positive / negative edge of pulse input?
.pos_mode = pos_mode, //PCNT_COUNT_INC, // Count up on the positive edge
.neg_mode = 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 = lctrl_mode, //PCNT_MODE_REVERSE, // Reverse counting direction if low
.hctrl_mode = hctrl_mode, //PCNT_MODE_KEEP, // Keep the primary counter mode if high
// Set the maximum and minimum limit values to watch
.counter_l_lim = counter_l_lim, //PCNT_L_LIM_VAL,
.counter_h_lim = counter_h_lim, //PCNT_H_LIM_VAL,
};
/* Initialize PCNT unit */
pcnt_unit_config(&pcnt_config);
/* Enable events on zero, maximum and minimum limit values */
if (pc->cb_ref != LUA_NOREF) { // if they didn't give callback, don't setup pcnt_isr_register
pcnt_event_enable(pc->unit, PCNT_EVT_ZERO);
pcnt_event_enable(pc->unit, PCNT_EVT_H_LIM);
pcnt_event_enable(pc->unit, PCNT_EVT_L_LIM);
}
/* Initialize PCNT's counter */
pcnt_counter_pause(pc->unit);
pcnt_counter_clear(pc->unit);
/* Register ISR handler and enable interrupts for PCNT unit */
if (pc->cb_ref != LUA_NOREF) { // if they didn't give callback, don't setup pcnt_isr_register
pcnt_isr_register(pulsecnt_intr_handler, NULL, 0, &user_isr_handle);
pcnt_intr_enable(pc->unit);
}
/* Everything is set up, now go to counting */
pcnt_counter_resume(pc->unit);
if (pc->is_debug) ESP_LOGI("pulsecnt", "Channel %d config for unit %d, gpio: %d, ctrl_gpio: %d, chn: %d, pos_mode: %d, neg_mode: %d, lctrl_mode: %d, hctrl_mode: %d, counter_l_lim: %d, counter_h_lim: %d", channel, pc->unit, pulse_gpio_num, ctrl_gpio_num, PCNT_CHANNEL_0, pos_mode, neg_mode, lctrl_mode, hctrl_mode, counter_l_lim, counter_h_lim );
return 0;
}
// Lua: pc:chan0Config(pulse_gpio_num, ctrl_gpio_num, pos_mode, neg_mode, lctrl_mode, hctrl_mode, counter_l_lim, counter_h_lim)
// Example: pc:chan0Config(2, 4, pulsecnt.PCNT_COUNT_INC, pulsecnt.PCNT_COUNT_DIS, pulsecnt.PCNT_MODE_REVERSE, pulsecnt.PCNT_MODE_KEEP, -100, 100)
static int pulsecnt_channel0_config( lua_State *L, uint8_t channel ) {
return pulsecnt_channel_config(L, 0);
}
// Lua: pc:chan1Config(pulse_gpio_num, ctrl_gpio_num, pos_mode, neg_mode, lctrl_mode, hctrl_mode, counter_l_lim, counter_h_lim)
// Example: pc:chan1Config(2, 4, pulsecnt.PCNT_COUNT_INC, pulsecnt.PCNT_COUNT_DIS, pulsecnt.PCNT_MODE_REVERSE, pulsecnt.PCNT_MODE_KEEP, -100, 100)
static int pulsecnt_channel1_config( lua_State *L, uint8_t channel ) {
return pulsecnt_channel_config(L, 1);
}
// Lua: pc = pulsecnt.create(unit, callback)
static int pulsecnt_create( lua_State *L ) {
int stack = 0;
// Get unit number -- first arg
int unit = luaL_checkinteger(L, ++stack);
luaL_argcheck(L, unit >= 0 && unit <= 7, stack, "The unit number allows 0 to 7");
// Get callback method -- 2nd arg
++stack;
// See if they even gave us a callback
bool isCallback = true;
if lua_isnoneornil(L, stack) {
// user did not provide a callback. that's ok. just don't give them one.
isCallback = false;
} else {
luaL_argcheck(L, lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION, stack, "Must be function");
}
// ok, we have our unit number which is required. good. now create our object
pulsecnt_t pc = (pulsecnt_t)lua_newuserdata(L, sizeof(pulsecnt_struct_t));
if (!pc) return luaL_error(L, "not enough memory");
luaL_getmetatable(L, "pulsecnt.pctr");
lua_setmetatable(L, -2);
pc->cb_ref = LUA_NOREF;
pc->self_ref = LUA_NOREF;
pc->is_initted = false;
pc->is_debug = false;
pc->counter = 99;
pc->unit = unit; // default to 0
//get the lua function reference
if (isCallback) {
luaL_unref(L, LUA_REGISTRYINDEX, pc->cb_ref);
lua_pushvalue(L, stack);
pc->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
// store in our global static pulsecnt_selfs array for later reference during callback
// where we only know the unit number
pulsecnt_selfs[unit] = pc;
// Get is_debug -- 3rd arg optional
++stack;
bool is_debug = luaL_optbool(L, stack, false);
if (is_debug == 1) {
pc->is_debug = true;
}
if (pc->is_debug) ESP_LOGI("pulsecnt", "Created obj for unit %d with callback ref of %d", pc->unit, pc->cb_ref );
return 1;
}
// Lua: pulsecnt:testCb( )
// This tests the callback where you can mimic in your code a sample callback as
// part of your debugging process. Make sure to set your callback during the create() call.
static int pulsecnt_testCb(lua_State* L)
{
// return 0;
pulsecnt_t pc = pulsecnt_get(L, 1);
// get a self reference if we don't have one
if (pc->self_ref == LUA_NOREF) {
lua_pushvalue(L, 1);
pc->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
if (pc->cb_ref == LUA_NOREF) {
luaL_error(L, "Callback not registered");
}
// post using lua task posting technique
// on lua_open we set pulsecnt_task_id as a method which gets called
// by Lua after task_post_high with reference to this self object and then we can steal the
// callback_ref and then it gets called by lua_call where we get to add our args
// task_post_low(pulsecnt_task_id, (task_param_t)pc);
task_post_low(pulsecnt_task_id, ( (0xffu) << 8) | (pc->unit) );
return 0;
}
// Lua: pulsecnt:getCnt( )
// Get's the pulse counter for a unit.
static int pulsecnt_getCnt(lua_State* L)
{
pulsecnt_t pc = pulsecnt_get(L, 1);
int16_t count = 0;
pcnt_get_counter_value(pc->unit, &count);
if (pc->is_debug) ESP_LOGI("pulsecnt", "Got ctr val for unit %d with count of %d", pc->unit, count );
pc->counter = count;
lua_pushinteger(L, pc->counter);
return 1;
}
// Lua: pulsecnt:clear( )
// Clear the pulse counter, i.e. set it back to 0.
static int pulsecnt_clear(lua_State* L)
{
pulsecnt_t pc = pulsecnt_get(L, 1);
pcnt_counter_clear(pc->unit);
int16_t count = 0;
pcnt_get_counter_value(pc->unit, &count);
if (pc->is_debug) ESP_LOGI("pulsecnt", "Cleared ctr for unit %d with new count of %d", pc->unit, count );
pc->counter = count;
lua_pushinteger(L, pc->counter);
return 1;
}
// Lua: pulsecnt:unregister( self )
static int pulsecnt_unregister(lua_State* L){
pulsecnt_t pc = pulsecnt_get(L, 1);
lua_pushinteger(L, pc->unit);
if (pc->self_ref != LUA_REFNIL) {
luaL_unref(L, LUA_REGISTRYINDEX, pc->self_ref);
pc->self_ref = LUA_NOREF;
}
luaL_unref(L, LUA_REGISTRYINDEX, pc->cb_ref);
pc->cb_ref = LUA_NOREF;
return 0;
}
static const LUA_REG_TYPE pulsecnt_dyn_map[] =
{
{ LSTRKEY( "getCnt" ), LFUNCVAL( pulsecnt_getCnt )},
{ LSTRKEY( "clear" ), LFUNCVAL( pulsecnt_clear )},
{ LSTRKEY( "testCb" ), LFUNCVAL( pulsecnt_testCb )},
{ LSTRKEY( "chan0Config" ), LFUNCVAL( pulsecnt_channel0_config )},
{ LSTRKEY( "chan1Config" ), LFUNCVAL( pulsecnt_channel1_config )},
{ LSTRKEY( "config" ), LFUNCVAL( pulsecnt_config )},
{ LSTRKEY( "setThres" ), LFUNCVAL( pulsecnt_set_thres )},
{ LSTRKEY( "setFilter" ), LFUNCVAL( pulsecnt_set_filter )},
{ LSTRKEY( "rawSetEventVal" ), LFUNCVAL( pulsecnt_set_event_value )},
{ LSTRKEY( "rawGetEventVal" ), LFUNCVAL( pulsecnt_get_event_value )},
// { LSTRKEY( "__tostring" ), LFUNCVAL( pulsecnt_tostring )},
{ LSTRKEY( "__gc" ), LFUNCVAL( pulsecnt_unregister ) },
{ LSTRKEY( "__index" ), LROVAL( pulsecnt_dyn_map ) },
{ LNILKEY, LNILVAL}
};
static const LUA_REG_TYPE pulsecnt_map[] =
{
{ LSTRKEY( "create" ), LFUNCVAL( pulsecnt_create )},
{ LSTRKEY( "PCNT_MODE_KEEP" ), LNUMVAL( 0 ) }, /*pcnt_ctrl_mode_t.PCNT_MODE_KEEP*/
{ LSTRKEY( "PCNT_MODE_REVERSE" ), LNUMVAL( 1 ) }, /*pcnt_ctrl_mode_t.PCNT_MODE_REVERSE*/
{ LSTRKEY( "PCNT_MODE_DISABLE" ), LNUMVAL( 2 ) }, /*pcnt_ctrl_mode_t.PCNT_MODE_DISABLE*/
{ LSTRKEY( "PCNT_COUNT_DIS" ), LNUMVAL( 0 ) }, /*pcnt_count_mode_t.PCNT_COUNT_DIS*/
{ LSTRKEY( "PCNT_COUNT_INC" ), LNUMVAL( 1 ) }, /*pcnt_count_mode_t.PCNT_COUNT_INC*/
{ LSTRKEY( "PCNT_COUNT_DEC" ), LNUMVAL( 2 ) }, /*pcnt_count_mode_t.PCNT_COUNT_DEC*/
// { LSTRKEY( "PCNT_COUNT_MAX " ), LNUMVAL( pcnt_count_mode_t.PCNT_COUNT_MAX ) },
{ LSTRKEY( "PCNT_H_LIM_VAL" ), LNUMVAL( 32767 ) },
{ LSTRKEY( "PCNT_L_LIM_VAL" ), LNUMVAL( -32768 ) },
{ LSTRKEY( "PCNT_EVT_L_LIM" ), LNUMVAL( 0 ) },
{ LSTRKEY( "PCNT_EVT_H_LIM" ), LNUMVAL( 1 ) },
{ LSTRKEY( "PCNT_EVT_THRES_0" ), LNUMVAL( 2 ) },
{ LSTRKEY( "PCNT_EVT_THRES_1" ), LNUMVAL( 3 ) },
{ LSTRKEY( "PCNT_EVT_ZERO" ), LNUMVAL( 4 ) },
{ LSTRKEY( "PCNT_PIN_NOT_USED" ), LNUMVAL( -1 ) }, /*PCNT_PIN_NOT_USED*/
{ LNILKEY, LNILVAL}
};
int luaopen_pulsecnt(lua_State *L) {
luaL_rometatable(L, "pulsecnt.pctr", (void *)pulsecnt_dyn_map);
pulsecnt_task_id = task_get_id(pulsecnt_task);
return 0;
}
NODEMCU_MODULE(PULSECNT, "pulsecnt", pulsecnt_map, luaopen_pulsecnt);

350
docs/modules/pulsecnt.md Normal file
View File

@ -0,0 +1,350 @@
# Pulse Counter Module
| Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ |
| 2019-04-07 | [ChiliPeppr](https://github.com/chilipeppr) | John Lauer | [pulsecnt.c](../../components/modules/pulsecnt.c)|
The pulsecnt library handles sophisticated and automatic pulse counting using the built-in hardware on ESP32.
The pulsecnt library gives you a means to not rely on GPIO triggers to do your pulse counting and instead offload the work to independent hardware. You gain the ability to count pulses up to 80Mhz (speed of APB clock).
You can get a callback to Lua when different counting thresholds are reached, or when upper or lower counting limits are hit. You can count pulses on all GPIO ports. There is also a way to provide
a control GPIO for ignoring or decrementing pulses when the control signal is high or low.
[Youtube video of examples using the pulsecnt](https://youtu.be/vk5QZnWdlAA) library including push button counting, stepper motor step counting, and hall effect sensor counting.
## pulsecnt.create()
Create the pulse counter object.
### Syntax
`pulsecnt.create(unit, callbackOnEvents)`
### Parameters
- `unit` Required. ESP32 has 0 thru 7 units to count pulses on.
- `callbackOnEvents` Optional. Your Lua method to call on event. myfunction(unit, isThr0, isThr1, isLLim, isHLim, isZero) will be called. Event will be PCNT_EVT_THRES_0 (Threshold 0 hit), PCNT_EVT_THRES_1 (Threshold 1 hit), PCNT_EVT_L_LIM (Minimum counter value), PCNT_EVT_H_LIM (Maximum counter value), or PCNT_EVT_ZERO (counter value zero event)
- `isDebug` Optional. Turn on extra logging by passing in true.
### Returns
`pulsecnt` object
### Example
```lua
-- Example Pulse Counter for 1 channel with polling of pulse count
pinPulseInput = 2
llim = -32768
hlim = 32767
pcnt = pulsecnt.create(7) -- Use unit 7 (0-7 are allowed)
pcnt:chan0Config(
pinPulseInput, --pulse_gpio_num
pulsecnt.PCNT_PIN_NOT_USED, --ctrl_gpio_num If no control is desired specify PCNT_PIN_NOT_USED
pulsecnt.PCNT_COUNT_INC, --pos_mode PCNT positive edge count mode
pulsecnt.PCNT_COUNT_DIS, --neg_mode PCNT negative edge count mode
pulsecnt.PCNT_MODE_KEEP, --lctrl_mode Ctrl low PCNT_MODE_KEEP, PCNT_MODE_REVERSE, PCNT_MODE_DISABLE
pulsecnt.PCNT_MODE_KEEP, --hctrl_mode Ctrl high PCNT_MODE_KEEP, PCNT_MODE_REVERSE, PCNT_MODE_DISABLE
llim, --counter_l_lim [Range -32768 to 32767]
hlim --counter_h_lim [Range -32768 to 32767]
)
-- Clear counting
pcnt:clear()
-- Poll the pulse counter
print("Current pulse counter val:" .. pcnt:getCnt())
```
### Example
```lua
-- Example Pulse Counter for 1 channel with callback
pinPulseInput = 2
thr0 = 2
thr1 = 4
llim = -6
hlim = 6
-- Callback on pulsecnt events
function onPulseCnt(unit, isThr0, isThr1, isLLim, isHLim, isZero)
print("unit:", unit, "isThr0:", isThr0, "isThr1:", isThr1)
print("isLLim:", isLLim, "isHLim:", isHLim, "isZero:", isZero)
-- Figure out what counter triggered this
if isThr0 then print("thr0 "..thr0.." triggered event") end
if isThr1 then print("thr1 "..thr1.." triggered event") end
if isLLim then print("llim "..llim.." triggered event") end
if isHLim then print("hlim "..hlim.." triggered event") end
if isZero then print("zero 0 triggered event") end
-- Get current count
print("Current pulse counter val:" .. pcnt:getCnt())
end
pcnt = pulsecnt.create(0, onPulseCnt) -- Use unit 0
pcnt:chan0Config(
pinPulseInput, --pulse_gpio_num
pulsecnt.PCNT_PIN_NOT_USED, --ctrl_gpio_num If no control is desired specify PCNT_PIN_NOT_USED
pulsecnt.PCNT_COUNT_INC, --pos_mode PCNT positive edge count mode PCNT_COUNT_DIS, PCNT_COUNT_INC, PCNT_COUNT_DEC
pulsecnt.PCNT_COUNT_DIS, --neg_mode PCNT negative edge count mode PCNT_COUNT_DIS, PCNT_COUNT_INC, PCNT_COUNT_DEC
pulsecnt.PCNT_MODE_KEEP, --lctrl_mode When Ctrl low PCNT_MODE_KEEP, PCNT_MODE_REVERSE, PCNT_MODE_DISABLE
pulsecnt.PCNT_MODE_KEEP, --hctrl_mode When Ctrl high PCNT_MODE_KEEP, PCNT_MODE_REVERSE, PCNT_MODE_DISABLE
llim, --counter_l_lim [Range -32768 to 32767]
hlim --counter_h_lim [Range -32768 to 32767]
)
-- Clear counting
pcnt:clear()
-- Set counter threshold for low and high
pcnt:setThres(thr0, thr1)
-- Get current pulse counter. Should be zero at start.
print("Current pulse counter val:" .. pcnt:getCnt())
```
### Example
```lua
-- Example Pulse Counter for 2 push buttons
-- Button 1 increments the counter (chan0)
-- Button 2 decrements the counter (chan1)
-- The pulsecnt filter is used to debounce
pinPulseInput = 2
pinPulseInput2 = 25
thr0 = 2
thr1 = 4
llim = -6
hlim = 6
-- Example callback
function onPulseCnt(unit, isThr0, isThr1, isLLim, isHLim, isZero)
print("unit:", unit, "isThr0:", isThr0, "isThr1:", isThr1)
print("isLLim:", isLLim, "isHLim:", isHLim, "isZero:", isZero)
-- Figure out what counter triggered this
if isThr0 then print("thr0 "..thr0.." triggered event") end
if isThr1 then print("thr1 "..thr1.." triggered event") end
if isLLim then print("llim "..llim.." triggered event") end
if isHLim then print("hlim "..hlim.." triggered event") end
if isZero then print("zero 0 triggered event") end
-- Get current count
print("Current pulse counter val:" .. pcnt:getCnt())
end
pcnt = pulsecnt.create(0, onPulseCnt) -- Use unit 0
-- Set clock cycles for filter. Any pulses lasting shorter than
-- this will be ignored when the filter is enabled. So, pulse
-- needs to be high or low for longer than this filter clock
-- cycle. Clock is 80Mhz APB clock, so one cycle is
-- 1000/80,000,000 = 0.0000125 ms. 1023 cycles = 0.0127875 ms
-- so not much of a debounce for manual push buttons.
pcnt:setFilter(1023)
-- Button 1
pcnt:chan0Config(
pinPulseInput, --pulse_gpio_num
pulsecnt.PCNT_PIN_NOT_USED, --ctrl_gpio_num If no control is desired specify PCNT_PIN_NOT_USED
pulsecnt.PCNT_COUNT_INC, --pos_mode PCNT positive edge count mode
pulsecnt.PCNT_COUNT_DIS, --neg_mode PCNT negative edge count mode
pulsecnt.PCNT_MODE_KEEP, --lctrl_mode
pulsecnt.PCNT_MODE_KEEP, --hctrl_mode
llim, --counter_l_lim
hlim --counter_h_lim
)
-- Button 2
pcnt:chan1Config(
pinPulseInput2, --pulse_gpio_num
pulsecnt.PCNT_PIN_NOT_USED, --ctrl_gpio_num If no control is desired specify PCNT_PIN_NOT_USED
pulsecnt.PCNT_COUNT_INC, --pos_mode PCNT positive edge count mode
pulsecnt.PCNT_COUNT_DIS, --neg_mode PCNT negative edge count mode
pulsecnt.PCNT_MODE_REVERSE, --lctrl_mode
pulsecnt.PCNT_MODE_KEEP, --hctrl_mode
llim, --counter_l_lim
hlim --counter_h_lim
)
-- Clear counting
pcnt:clear()
-- Set counter threshold for low and high
pcnt:setThres(thr0, thr1)
-- Buttons are now setup
```
## pulsecntObj:chan0Config()
Configure channel 0 of the pulse counter object you created from the create() method.
### Syntax
`pulsecntObj:chan0Config(pulse_gpio_num, ctrl_gpio_num, pos_mode, neg_mode, lctrl_mode, hctrl_mode, counter_l_lim, counter_h_lim)`
### Parameters
- `pulse_gpio_num` Required. Any GPIO pin can be used.
- `ctrl_gpio_num` Required (although you can specify pulsecnt.PIN_NOT_USED to ignore). Any GPIO pin can be used. If you are trying to use a pin you use as gpio.OUT in other parts of your code, you can instead configure the pin as gpio.IN_OUT and write high or low to it to achieve your gpio.OUT task while still enabling the pulse counter to successfully read the pin state.
- `pos_mode` Required. Positive rising edge count mode, i.e. count the pulse when the rising edge occurs.
- pulsecnt.PCNT_COUNT_DIS = 0 Counter mode: Inhibit counter (counter value will not change in this condition).
- pulsecnt.PCNT_COUNT_INC = 1 Counter mode: Increase counter value.
- pulsecnt.PCNT_COUNT_DEC = 2 Counter mode: Decrease counter value.
- `neg_mode` Required. Negative falling edge count mode, i.e. count the pulse when the falling edge occurs.
- pulsecnt.PCNT_COUNT_DIS = 0 Counter mode: Inhibit counter (counter value will not change in this condition).
- pulsecnt.PCNT_COUNT_INC = 1 Counter mode: Increase counter value.
- pulsecnt.PCNT_COUNT_DEC = 2 Counter mode: Decrease counter value.
- `lctrl_mode` Required. When `ctrl_gpio_num` is low how should the counter be influenced.
- pulsecnt.PCNT_MODE_KEEP = 0 Control mode: will not change counter mode.
- pulsecnt.PCNT_MODE_REVERSE = 1 Control mode: invert counter mode (increase -> decrease, decrease -> increase).
- pulsecnt.PCNT_MODE_DISABLE = 2 Control mode: Inhibit counter (counter value will not change in this condition).
- `hctrl_mode` Required. When `ctrl_gpio_num` is high how should the counter be influenced.
- pulsecnt.PCNT_MODE_KEEP = 0 Control mode: will not change counter mode.
- pulsecnt.PCNT_MODE_REVERSE = 1 Control mode: invert counter mode (increase -> decrease, decrease -> increase).
- pulsecnt.PCNT_MODE_DISABLE = 2 Control mode: Inhibit counter (counter value will not change in this condition).
- `counter_l_lim` Required. Range -32768 to 32767. The lower limit counter. You get a callback at this count and the counter is reset to zero after this lower limit is hit.
- `counter_h_lim` Required. Range -32768 to 32767. The high limit counter. You get a callback at this count and the counter is reset to zero after this high limit is hit.
### Returns
`nil`
## pulsecntObj:chan1Config()
Configure channel 1 of the pulse counter object you created from the create() method.
### Syntax
`pulsecntObj:chan1Config(pulse_gpio_num, ctrl_gpio_num, pos_mode, neg_mode, lctrl_mode, hctrl_mode, counter_l_lim, counter_h_lim)`
### Parameters
- `pulse_gpio_num` Required. Any GPIO pin can be used.
- `ctrl_gpio_num` Required (although you can specify pulsecnt.PIN_NOT_USED to ignore). Any GPIO pin can be used. If you are trying to use a pin you use as gpio.OUT in other parts of your code, you can instead configure the pin as gpio.IN and toggle gpio.PULL_UP or gpio.PULL_DOWN to achieve your gpio.OUT task while still enabling the pulse counter to successfully read the pin state.
- `pos_mode` Required. Positive rising edge count mode, i.e. count the pulse when the rising edge occurs.
- pulsecnt.PCNT_COUNT_DIS = 0 Counter mode: Inhibit counter (counter value will not change in this condition).
- pulsecnt.PCNT_COUNT_INC = 1 Counter mode: Increase counter value.
- pulsecnt.PCNT_COUNT_DEC = 2 Counter mode: Decrease counter value.
- `neg_mode` Required. Negative falling edge count mode, i.e. count the pulse when the falling edge occurs.
- pulsecnt.PCNT_COUNT_DIS = 0 Counter mode: Inhibit counter (counter value will not change in this condition).
- pulsecnt.PCNT_COUNT_INC = 1 Counter mode: Increase counter value.
- pulsecnt.PCNT_COUNT_DEC = 2 Counter mode: Decrease counter value.
- `lctrl_mode` Required. When `ctrl_gpio_num` is low how should the counter be influenced.
- pulsecnt.PCNT_MODE_KEEP = 0 Control mode: will not change counter mode.
- pulsecnt.PCNT_MODE_REVERSE = 1 Control mode: invert counter mode (increase -> decrease, decrease -> increase).
- pulsecnt.PCNT_MODE_DISABLE = 2 Control mode: Inhibit counter (counter value will not change in this condition).
- `hctrl_mode` Required. When `ctrl_gpio_num` is high how should the counter be influenced.
- pulsecnt.PCNT_MODE_KEEP = 0 Control mode: will not change counter mode.
- pulsecnt.PCNT_MODE_REVERSE = 1 Control mode: invert counter mode (increase -> decrease, decrease -> increase).
- pulsecnt.PCNT_MODE_DISABLE = 2 Control mode: Inhibit counter (counter value will not change in this condition).
- `counter_l_lim` Required. Range -32768 to 32767. The lower limit counter. You get a callback at this count and the counter is reset to zero after this lower limit is hit.
- `counter_h_lim` Required. Range -32768 to 32767. The high limit counter. You get a callback at this count and the counter is reset to zero after this high limit is hit.
### Returns
`nil`
### Example
```lua
-- Button 1 increment counter
pcnt:chan0Config(
pinPulseInput, --pulse_gpio_num
pulsecnt.PCNT_PIN_NOT_USED, --ctrl_gpio_num If no control is desired specify PCNT_PIN_NOT_USED
pulsecnt.PCNT_COUNT_INC, --pos_mode PCNT positive edge count mode
pulsecnt.PCNT_COUNT_DIS, --neg_mode PCNT negative edge count mode
pulsecnt.PCNT_MODE_KEEP, --lctrl_mode
pulsecnt.PCNT_MODE_KEEP, --hctrl_mode
llim, --counter_l_lim
hlim --counter_h_lim
)
-- Button 2 decrement counter
pcnt:chan1Config(
pinPulseInput2, --pulse_gpio_num
pulsecnt.PCNT_PIN_NOT_USED, --ctrl_gpio_num If no control is desired specify PCNT_PIN_NOT_USED
pulsecnt.PCNT_COUNT_INC, --pos_mode PCNT positive edge count mode
pulsecnt.PCNT_COUNT_DIS, --neg_mode PCNT negative edge count mode
pulsecnt.PCNT_MODE_REVERSE, --lctrl_mode
pulsecnt.PCNT_MODE_KEEP, --hctrl_mode
llim, --counter_l_lim
hlim --counter_h_lim
)
```
## pulsecntObj:setThres()
Set the threshold values to establish watchpoints for getting callbacks on.
### Syntax
`pulsecntObj:setThres(thr0, thr1)`
### Parameters
- `thr0` Required. Threshold 0 value. Range -32768 to 32767. This is a watchpoint value to get a callback with isThr0 set to true on this count being reached.
- `thr1` Required. Threshold 1 value. Range -32768 to 32767. This is a watchpoint value to get a callback with isThr1 set to true on this count being reached.
### Returns
`nil`
### Example
```lua
pcnt:setThres(-2000, 2000) -- get callbacks when counter is -2000 or 2000
```
```lua
pcnt:setThres(500, 10000) -- get callbacks when counter is 500 or 10000
```
## pulsecntObj:setFilter()
Set a filter to ignore pulses on the `pulse_gpio_num` pin and the `ctrl_gpio_num` to avoid short glitches. Any pulses lasting shorter than this will be ignored.
### Syntax
`pulsecntObj:setFilter(clkCycleCnt)`
### Parameters
- `clkCycleCnt` Required. 0 to 1023 allowd. Any pulses lasting shorter than this will be ignored. A pulse needs to be high or low for longer than this filter clock cycle. Clock is 80Mhz APB clock, so one cycle is 1000/80,000,000 = 0.0000125 ms. The longest value of 1023 cycles = 0.0127875 ms.
### Returns
`nil`
### Example
```lua
pcnt:setFilter(1023) -- set max filter clock cylce count to ignore pulses shorter than 12.7us
```
## pulsecntObj:clear()
Clear the counter. Sets it back to zero.
### Syntax
`pulsecntObj:clear()`
### Parameters
None
### Returns
`nil`
### Example
```lua
pcnt:clear() -- set counter back to zero
```
## pulsecntObj:getCnt()
Get the current pulse counter.
### Syntax
`pulsecntObj:getCnt()`
### Parameters
None
### Returns
`integer`
### Example
```lua
val = pcnt:getCnt() -- get counter
print("Pulse counter:", val)
```