Build Your Own Clone Message Board

It is currently Fri May 23, 2025 1:58 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 3 posts ] 
Author Message
PostPosted: Tue Jul 09, 2024 1:13 pm 
Offline

Joined: Wed Jun 26, 2024 7:47 pm
Posts: 2
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 ESP32
Untested cheaper MCU option that should work ~$12 Waveshare ESP32 on Amazon
Display $11 SparkFun Qwiic Alphanumeric Display on Amazon


EDIT: 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)
  }

}


Attachments:
delay chrono 03.jpg
delay chrono 03.jpg [ 58.38 KiB | Viewed 2524 times ]
File comment: Mod installed
delay chrono 01 copy.jpg
delay chrono 01 copy.jpg [ 95.85 KiB | Viewed 2524 times ]
File comment: oscilloscope measuring time of flight input-output
Audio signal Time of flight slowest setting 1330ms.png
Audio signal Time of flight slowest setting 1330ms.png [ 10.34 KiB | Viewed 2524 times ]
Top
 Profile  
 
PostPosted: Fri Jul 19, 2024 2:38 pm 
Offline
Site Admin
User avatar

Joined: Sun Jan 22, 2006 1:39 pm
Posts: 6045
Location: Richland, WA
Very impressive. Did you replace the PIC with the Arduino or is the Arduino reading the pulse to the tempo LED?

_________________
*patience is a virtue*

Please do not PM me. email is prefered. keith@buildyourownclone.com


Top
 Profile  
 
PostPosted: Fri Jul 19, 2024 5:08 pm 
Offline

Joined: Wed Jun 26, 2024 7:47 pm
Posts: 2
Thank you! No, the original circuit with PIC and tempo LED is still intact. The arduino is only reading the clocks off the PT2399 chips and controlling the display.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group