I wanted to share/show off this mod I am wrapping up for the Echo Royal. I was inspired by the delay-time display on Providence Chrono pedal enough to give it a try with my Echo Royal. It turned out great. Comparisons of time of flight with the oscilloscope and the display are pretty much dead on. I'm using an Arduino Nano ESP32 board and an Alphanumeric I2C display. The only other circuitry necessary was a simple resistive voltage divider to reduce the 5V cmos clocks from the two PT2399 chips (pin 5) down to 3.3v for the arduino. This little ESP32 packs a 240Mhz dual core processor with an 8-channel 80Mhz pulse counter. This hardware makes measuring the 22Mhz PT2399 clock trivial. Right now the display reads milliseconds, but I plan on adding a mini toggle to change the display calculation to BPM.
MCU that I used ~$23
Arduino Nano ESP32Untested cheaper MCU option that should work ~$12
Waveshare ESP32 on AmazonDisplay $11
SparkFun Qwiic Alphanumeric Display on AmazonEDIT: Added hardware links and 2 versions of code for single or dual PT2399, wish there was a click to expand/collapse option
Arduino ESP32 code for dual PT2399
Code:
#include "arduino.h"
#include "stdio.h"
#include "driver/pcnt.h" // ESP32 library for pulse counter
#include "soc/pcnt_struct.h" // to avoid "'PCNT' was not declared in this scope" error
#include <Wire.h> // I2C Libray
#include <SparkFun_Alphanumeric_Display.h>
HT16K33 display;
#define ENC_L 6 // GPIO pin for PT2399_1
#define ENC_R 5 // GPIO pin for PT2399_2
#define PCNT_H_LIM_VAL 28500 // upper counting limit, max. 32767, write +1 to overflow counter, when reached
uint32_t overflow = 28500;
bool LFLAG = true;
bool RFLAG = true;
volatile double frequency_0 = 0;
volatile double frequency_1 = 0;
volatile double frequency_2;
volatile double ms_delay = 0;
uint16_t result_0 = 0;
uint16_t result_1 = 0;
esp_timer_create_args_t timer_args; // Create an esp_timer instance
esp_timer_handle_t timer_handle; // Create an single timer
portMUX_TYPE timer_mux = portMUX_INITIALIZER_UNLOCKED;
pcnt_unit_t units[2] = { PCNT_UNIT_0, PCNT_UNIT_1 }; // select ESP32 pulse counter units (out of 0 to 7)
int16_t PulseCounters[2] = { 0, 0 }; // pulse counters, max. value is 65536
uint32_t OverflowCounters[2] = { 0, 0 }; // overflow counters for pulse counters
uint16_t PCNT_FILTER_VAL= 1; // filter value for avoiding glitches in the count, max. 1023
// length of ignored pulses in APB_CLK clock cycles (running at 80 MHz)
pcnt_isr_handle_t user_isr_handle = NULL; // interrupt handler - not used
void CounterOverflow_Left(void *arg) { // Interrupt for overflow of pulse counter
OverflowCounters[0] = OverflowCounters[0] + 1; // Increase overflow counter
PCNT.int_clr.val = BIT(units[0]); // Clean overflow flag
pcnt_counter_clear(units[0]); // Zero and reset of pulse counter unit
}
void CounterOverflow_Right(void *arg) { // Similar, just for pulse counter of right encoder
OverflowCounters[1] = OverflowCounters[1] + 1;
PCNT.int_clr.val = BIT(units[1]);
pcnt_counter_clear(units[1]);
}
// Initialise pulse counters to detect rising edges on GPIOs defined by ENC_L and ENC_R
void initPulseCounters() {
pinMode(ENC_L, INPUT);
pinMode(ENC_R, INPUT);
int GPIOs[2] = { ENC_L, ENC_R }; // select GPIO pins
pcnt_config_t pcntFreqConfig = {}; // Instance of pulse counter
pcntFreqConfig.pos_mode = PCNT_COUNT_INC; // Count only rising edges as pulses
pcntFreqConfig.counter_h_lim = PCNT_H_LIM_VAL; // Set upper counting limit
pcntFreqConfig.channel = PCNT_CHANNEL_0; // select channel 0 of pulse counter unit (for both pulse counters)
for (int i = 0; i < 2; i++) {
pcntFreqConfig.pulse_gpio_num = GPIOs[i]; // Pin assignment for pulse counter
pcntFreqConfig.unit = units[i]; // select ESP32 pulse counter unit
pcnt_unit_config(&pcntFreqConfig); // configure registers of the pulse counter
pcnt_counter_pause(units[i]); // pause pulse counter unit
pcnt_counter_clear(units[i]); // zero and reset of pulse counter unit
pcnt_intr_enable(units[i]); //test
pcnt_isr_service_install(0); // Install PCNT ISR service, non-shared interrupt of level 1, 2 or 3
if (i == 0) { // pulse counter for left wheel
pcnt_isr_handler_add(units[i], CounterOverflow_Left, NULL);
} else { // pulse counter for right wheel
pcnt_isr_handler_add(units[i], CounterOverflow_Right, NULL);
}
pcnt_event_enable(units[i], PCNT_EVT_H_LIM); // enable event for interrupt on reaching upper limit of counting
//pcnt_set_filter_value(units[i], PCNT_FILTER_VAL); // set damping, inertia
pcnt_filter_disable(units[i]); // enable counter glitch filter (damping)
pcnt_counter_resume(units[i]); // resume counting on pulse counter unit
}
timer_args.callback = Read_PCNTs;
timer_args.arg = NULL;
timer_args.name = "one shot timer";
if (esp_timer_create(&timer_args, &timer_handle) != ESP_OK) {
ESP_LOGE(TAG, "timer create");
}
timer_args.callback = Read_PCNTs; // Set esp-timer argument
esp_timer_create(&timer_args, &timer_handle); // Create esp-timer instance
}
// Reads both pulse counters and resets them
void Read_Reset_PCNTs() {
for (int i = 0; i < 2; i++) {
pcnt_get_counter_value(units[i], &PulseCounters[units[i]]); // get pulse counter value - maximum value is 16 bits
OverflowCounters[units[i]] = 0; // set overflow counter to zero
pcnt_counter_clear(units[i]); // zero and reset of pulse counter unit
}
}
// Reads both pulse counters, results saved in Pulsecounters and Overflowcounters
void Read_PCNTs(void *p) {
pcnt_counter_pause(units[0]);
pcnt_counter_pause(units[1]);
pcnt_get_counter_value(units[0], &PulseCounters[0]); // get pulse counter value on unit 0
pcnt_get_counter_value(units[1], &PulseCounters[1]); // get pulse counter value on unit 1
LFLAG = true;
}
void setup() {
Serial.begin(115200); // Start serial connection
initPulseCounters();
Wire.begin(); // Begin I2C Interface with default I2C_SDA and I2C_SCL
if (display.begin() == false) {
Serial.println("Device did not acknowledge! Freezing.");
while (1)
;
}
display.setBrightness(7); //lcd.setBacklight(255);
display.print("MHz");
}
void loop() {
if (LFLAG == true) {
LFLAG = false;
//Read_PCNTs();
frequency_0 = PulseCounters[0] + (OverflowCounters[0] * 28500);
frequency_1 = PulseCounters[1] + (OverflowCounters[1] * 28500);
frequency_2 = frequency_0/2 + frequency_1/2;
Serial.print("f0= ");
Serial.println(frequency_0);
Serial.print("f1= ");
Serial.println(frequency_1);
Serial.print("f2= ");
Serial.println(frequency_2);
pcnt_counter_clear(units[0]);
pcnt_counter_clear(units[1]);
pcnt_counter_resume(units[0]);
pcnt_counter_resume(units[1]);
PulseCounters[0]=0;
OverflowCounters[0]=0;
PulseCounters[1]=0;
OverflowCounters[1]=0;
ms_delay = floor((1 / (frequency_2/ 2)) * 683210000);
display.print((int)ms_delay);
Serial.println(ms_delay);
esp_timer_start_once(timer_handle, 1000000); // Initialize High resolution timer (1 sec)
}
}
Code for single PT2399 delays:
Code:
#include "arduino.h"
#include "stdio.h"
#include "driver/pcnt.h"
#include "soc/pcnt_struct.h"
#include <Wire.h> // I2C Libray
#include <SparkFun_Alphanumeric_Display.h> //Click here to get the library: http://librarymanager/All#SparkFun_Qwiic_Alphanumeric_Display by SparkFun
HT16K33 display;
#define PCNT_H_LIM_VAL overflow
#define FREQ_PIN 6
bool flag = true;
uint32_t overflow = 28500;
int16_t pulses = 0;
uint32_t overflow_cnt = 0;
volatile double frequency = 0;
volatile double ms_delay = 0;
uint16_t result = 0;
//void pcnt_get_counter(void *p);
//void pcnt_event_handler(void *arg);
esp_timer_create_args_t timer_args; // Create an esp_timer instance
esp_timer_handle_t timer_handle; // Create an single timer
portMUX_TYPE timer_mux = portMUX_INITIALIZER_UNLOCKED;
pcnt_config_t pcnt_config = {
.pulse_gpio_num = FREQ_PIN,
.ctrl_gpio_num = -1,
.lctrl_mode = PCNT_MODE_KEEP,
.hctrl_mode = PCNT_MODE_KEEP,
.pos_mode = PCNT_COUNT_INC,
.neg_mode = PCNT_COUNT_INC,
.counter_h_lim = 28500,
.counter_l_lim = 0,
.unit = PCNT_UNIT_0,
.channel = PCNT_CHANNEL_0
};
void IRAM_ATTR pcnt_event_handler(void *arg) // Counting overflow pulses
{
portENTER_CRITICAL_ISR(&timer_mux); // disable interrupt
overflow_cnt++; // increment Overflow counter
PCNT.int_clr.val = BIT(PCNT_UNIT_0); // Clear Pulse Counter interrupt bit
portEXIT_CRITICAL_ISR(&timer_mux); // enable interrupt
}
//----------------------------------------------------------------------------------------
void setup() {
Serial.begin(9600);
init_frequencyMeter();
Wire.begin(); // Begin I2C Interface with default I2C_SDA and I2C_SCL
if (display.begin() == false) {
Serial.println("Device did not acknowledge! Freezing.");
while (1); // This is necessary for display init
}
display.setBrightness(7);
//display.print("MHz");
}
//----------------------------------------------------------------------------------
void init_frequencyMeter() {
pcnt_init();
}
void pcnt_init(void) {
pinMode(FREQ_PIN, INPUT);
pcnt_unit_config(&pcnt_config);
pcnt_isr_register(pcnt_event_handler, NULL, 0, NULL); // Setup Register ISR handler
pcnt_intr_enable(PCNT_UNIT_0);
// pcnt_set_filter_value(PCNT_UNIT_0, 1);
//pcnt_filter_enable(PCNT_UNIT_0);
pcnt_filter_disable(PCNT_UNIT_0);
pcnt_counter_pause(PCNT_UNIT_0); // Pause PCNT unit
pcnt_counter_clear(PCNT_UNIT_0); // Clear PCNT unit
pcnt_event_enable(PCNT_UNIT_0, PCNT_EVT_H_LIM); // Enable event to watch - max count
pcnt_counter_resume(PCNT_UNIT_0); // Resume PCNT unit - starts count
timer_args.callback = pcnt_get_counter;
timer_args.arg = NULL;
timer_args.name = "one shot timer";
if (esp_timer_create(&timer_args, &timer_handle) != ESP_OK) {
ESP_LOGE(TAG, "timer create");
}
timer_args.callback = pcnt_get_counter; // Set esp-timer argument
esp_timer_create(&timer_args, &timer_handle); // Create esp-timer instance
}
//----------------------------------------------------------------------------------
void pcnt_get_counter(void *p) { // Read Pulse Counter
pcnt_counter_pause(PCNT_UNIT_0);
pcnt_get_counter_value(PCNT_UNIT_0, (int16_t *)&result);
flag = true;
}
//---------------------------------------------------------------------------------
void loop() {
if (flag == true) {
flag = false;
frequency = result + (overflow_cnt * 28500);
overflow_cnt = 0;
pcnt_counter_clear(PCNT_UNIT_0);
pcnt_counter_resume(PCNT_UNIT_0);
overflow_cnt = 0;
ms_delay = floor((1/(frequency/2)) * 683210000);
Serial.print( floor( (1 / (frequency / 2) ) * 683210000),0);
Serial.println(" ms");
display.print(ms_delay,0);
pcnt_counter_clear(PCNT_UNIT_0);
esp_timer_start_once(timer_handle, 1000000); // Initialize High resolution timer (1 sec)
}
}