Display for PT2399 delay using Attiny85

Started by Ksander, August 06, 2024, 10:57:58 AM

Previous topic - Next topic

Ksander

There was this topic recently, on showing the delay of PT2399. I thought it would be a nice project for an Attiny85.

It works like this: the high frequency (up to some 22MHz) pulses from pin 5 are divided by 256 using a 74HC393 IC. The Attiny runs a watchdog timer to time periods of *approximately* 1s. In this second the pulses are counted. The number of pulses are converted to milliseconds using the equation

ms = 2670265 / ( (CORRECTION_FACTOR)*pulseCount);

This equation is a fit to the empirical data on the relation between frequency and delay that is available on the electrosmash website (corrected for the division by 256). The fit is near perfect (R^2 of some 0.99). The correction factor is necessary because the watchdog timer frequency on the Attiny85 is notoriously variable between chips. I determined it by feeding the Attiny85 a 65536Hz pulse train and measuring the number of pulses actually counted. The correction factor is their ratio.

The ms value is then sent to the display over I2C.

I only had a 100k pot, so we get very long delays.

This is the code:

/* Attiny85 used to estimate PT2399 delay and show the value on a miniature display

  By Ksander de Winkel
 
  CC BY 4.0
  Licensed under a Creative Commons Attribution 4.0 International license:
  http://creativecommons.org/licenses/by/4.0/

*/

#include <avr/wdt.h>
#include <TinyI2CMaster.h>
#include <Tiny4kOLED.h>

#define PULSE_PIN PB1                 // Pin for pulse input (must be T1 - PB1)
#define DEBUG_LED_PIN PB3             // Pin for the debug LED
#define CORRECTION_FACTOR 0.92

volatile bool secondElapsed = false;
volatile unsigned long pulseCount = 0;

void setup() {
  // Set up pulse pin as input
  pinMode(PULSE_PIN, INPUT);

  // Set up debug LED pin as output
  pinMode(DEBUG_LED_PIN, OUTPUT);

  // Enable pin change interrupt on PULSE_PIN (PB1)
  GIMSK |= (1 << PCIE);   // Enable pin change interrupts
  PCMSK |= (1 << PCINT1); // Enable pin change interrupt for PB1

  // Set up the Watchdog Timer for 1-second interval
  cli(); // Disable interrupts
  wdt_reset(); // Reset the WDT
  // Set up WDT interrupt and enable WDT
  WDTCR = (1 << WDIE) | (1 << WDP2) | (1 << WDP1); // 1-second interrupt

  // Setup display
  oled.begin();
  oled.setFont(FONT8X16);
  oled.clear();
  oled.on();
  oled.switchRenderFrame();
 
  sei(); // Enable interrupts
}

ISR(PCINT0_vect) {
  // Check if PB1 pin is high (pulse detected)
  if (PINB & (1 << PULSE_PIN)) {
    pulseCount++;
  }
}

ISR(WDT_vect) {
  secondElapsed = true; // Set flag to indicate one second has elapsed
  // Toggle the debug LED every second
  PORTB ^= (1 << DEBUG_LED_PIN);
}

void updateDisplay(uint16_t value) {
 
  oled.clear();
  oled.setCursor(0, 1);
  oled.print(F("ms: "));
  oled.print(value);
  oled.switchFrame();
}

void loop() {
  if (secondElapsed) {
    secondElapsed = false;

    // Disable interrupts while reading the pulse count
    cli();
    uint16_t delay_ms = 2670265/( (CORRECTION_FACTOR)*pulseCount);
    pulseCount = 0; // Reset pulse count
    sei();

    updateDisplay(delay_ms);
  }
}


And here is a demo:
https://drive.google.com/file/d/1_6Ar77FN9E4jYuBfah6tvOlW8gcumArC/view?usp=sharing

I tried using prescalers in the attiny85, as suggested in the other topic, but that seems to affect only the clock frequency; not the number of pulses counted at the pin. It might also be possible to use the fast peripheral clock (64MHz), but I haven't gotten that to work yet either. So for now it is the 74HC393.

Thoughts and suggestions are welcome :)

thomas642daniel

#1
Quote from: Ksander on August 06, 2024, 10:57:58 AMThere was <underhand link edited by thomas642daniel removed by moderators> topic recently, on showing the delay of PT2399. I thought it would be a nice project for an Attiny85.

It works like this: the high frequency (up to some 22MHz) pulses from pin 5 are divided by 256 using a 74HC393 IC. The Attiny runs a watchdog timer to time periods of *approximately* 1s. In this second the pulses are counted. The number of pulses are converted to milliseconds using the equation

ms = 2670265 / ( (CORRECTION_FACTOR)*pulseCount);

This equation is a fit to the empirical data on the relation between frequency and delay that is available on the electrosmash website (corrected for the division by 256). The fit is near perfect (R^2 of some 0.99). The correction factor is necessary because the watchdog timer frequency on the Attiny85 is notoriously variable between chips. I determined it by feeding the Attiny85 a 65536Hz pulse train and measuring the number of pulses actually counted. The correction factor is their ratio.

The ms value is then sent to the display over I2C.

I only had a 100k pot, so we get very long delays.

This is the code:

/* Attiny85 used to estimate PT2399 delay and show the value on a miniature display

  By Ksander de Winkel
 
  CC BY 4.0
  Licensed under a Creative Commons Attribution 4.0 International license:
  http://creativecommons.org/licenses/by/4.0/

*/

#include <avr/wdt.h>
#include <TinyI2CMaster.h>
#include <Tiny4kOLED.h>

#define PULSE_PIN PB1                // Pin for pulse input (must be T1 - PB1)
#define DEBUG_LED_PIN PB3            // Pin for the debug LED
#define CORRECTION_FACTOR 0.92

volatile bool secondElapsed = false;
volatile unsigned long pulseCount = 0;

void setup() {
  // Set up pulse pin as input
  pinMode(PULSE_PIN, INPUT);

  // Set up debug LED pin as output
  pinMode(DEBUG_LED_PIN, OUTPUT);

  // Enable pin change interrupt on PULSE_PIN (PB1)
  GIMSK |= (1 << PCIE);  // Enable pin change interrupts
  PCMSK |= (1 << PCINT1); // Enable pin change interrupt for PB1

  // Set up the Watchdog Timer for 1-second interval
  cli(); // Disable interrupts
  wdt_reset(); // Reset the WDT
  // Set up WDT interrupt and enable WDT
  WDTCR = (1 << WDIE) | (1 << WDP2) | (1 << WDP1); // 1-second interrupt

  // Setup display
  oled.begin();
  oled.setFont(FONT8X16);
  oled.clear();
  oled.on();
  oled.switchRenderFrame();
 
  sei(); // Enable interrupts
}

ISR(PCINT0_vect) {
  // Check if PB1 pin is high (pulse detected)
  if (PINB & (1 << PULSE_PIN)) {
    pulseCount++;
  }
}

ISR(WDT_vect) {
  secondElapsed = true; // Set flag to indicate one second has elapsed
  // Toggle the debug LED every second
  PORTB ^= (1 << DEBUG_LED_PIN);
}

void updateDisplay(uint16_t value) {
 
  oled.clear();
  oled.setCursor(0, 1);
  oled.print(F("ms: "));
  oled.print(value);
  oled.switchFrame();
}

void loop() {
  if (secondElapsed) {
    secondElapsed = false;

    // Disable interrupts while reading the pulse count
    cli();
    uint16_t delay_ms = 2670265/( (CORRECTION_FACTOR)*pulseCount);
    pulseCount = 0; // Reset pulse count
    sei();

    updateDisplay(delay_ms);
  }
}


And here is a demo:
https://drive.google.com/file/d/1_6Ar77FN9E4jYuBfah6tvOlW8gcumArC/view?usp=sharing

I tried using prescalers in the attiny85, as suggested in the other topic, but that seems to affect only the clock frequency; not the number of pulses counted at the pin. It might also be possible to use the fast peripheral clock (64MHz), but I haven't gotten that to work yet either. So for now it is the 74HC393.

Thoughts and suggestions are welcome :)
This project is such a fantastic blend of creativity and technical skill! I love how you've combined the Attiny85 with the PT2399 to measure delay, which is often overlooked in DIY audio projects. The empirical approach to calibrating the correction factor is impressive and really highlights the importance of hands-on experimentation in electronics.

One question that comes to mind is whether you considered using any other microcontrollers or platforms for this project, like an ESP32 or Raspberry Pi, which could potentially add more features or processing power? Also, have you thought about expanding the display functionality? For instance, showing a graphical representation of delay changes over time could add an interesting layer of interactivity!

boogiesg

Quote from: thomas642daniel on October 26, 2024, 02:58:45 AMOne question that comes to mind is whether you considered using any other microcontrollers or platforms for this project, like an ESP32 or Raspberry Pi

The recent topic that Ksander mentioned for displaying the delay time of a pt2399 is my thread.  I used an arduino esp32 board to shoehorn a delay meter into a byoc echo royal, which is a dual pt2399 delay unit.  The Arduino esp32 nano was chosen because of it's wide operating voltage range.  The echo royal runs off 9 volts but most other esp32 dev boards cannot, and it's very small footprint. https://www.diystompboxes.com/smfforum/index.php?topic=132147.0

Very cool Ksander, I like what you have done and I'm glad that I'm not alone in thinking this is a cool feature for delay pedals. I think your implementation lends itself to ground-up pt2399 builds better because you could fit this onto a homemade pcb easily. Especially the single chip delays whereas mine is easier to use if you want to modify an already complete delay pedal.

Ksander

Thanks guys, nice to see some interest!

I used the Attiny because of its simplicity, and because I have a bunch.

I have some more advanced microcontrollers too, like the pi pico, but have found that micropython to program it is useless for audio, and programming it in c is not as straightforward. The VS code environment is difficult to set up and I keep forgetting how to do all the make/cmake stuff, and the datasheet for the pico (or the chip on it) also is a lot harder to wrap your head around.

Basically, this was an exercise; to see if I could get it to work.

ElectricDruid

Quote from: Ksander on August 06, 2024, 10:57:58 AMIt works like this: the high frequency (up to some 22MHz) pulses from pin 5 are divided by 256 using a 74HC393 IC. The Attiny runs a watchdog timer to time periods of *approximately* 1s. In this second the pulses are counted. The number of pulses are converted to milliseconds using the equation

Can you use the input pulses to clock one of the 8-bit timers? If you can, then the timer overflow would give you the /256 action that is currently done externally with the 74HC393.
Just a thought - I don't know the peripherals on the AVRs, so I don't know how practical this is.

Ksander

Quote from: ElectricDruid on October 29, 2024, 06:29:16 PM
Quote from: Ksander on August 06, 2024, 10:57:58 AMIt works like this: the high frequency (up to some 22MHz) pulses from pin 5 are divided by 256 using a 74HC393 IC. The Attiny runs a watchdog timer to time periods of *approximately* 1s. In this second the pulses are counted. The number of pulses are converted to milliseconds using the equation

Can you use the input pulses to clock one of the 8-bit timers? If you can, then the timer overflow would give you the /256 action that is currently done externally with the 74HC393.
Just a thought - I don't know the peripherals on the AVRs, so I don't know how practical this is.

I tried, it would be nice to get rid of the additional IC, but couldn't get it to work. There is a fast 64MHz peripheral clock, but i couldn't get this to work for counting pulses. Doesn't mean it is impossible...

ElectricDruid

Quote from: Ksander on October 30, 2024, 06:17:15 AMI tried, it would be nice to get rid of the additional IC, but couldn't get it to work. There is a fast 64MHz peripheral clock, but i couldn't get this to work for counting pulses. Doesn't mean it is impossible...

Reading the datasheet a bit, I can't see any way to do it with Timer1 (there's no clock source selection - see Fig 12-3 on page 85 of the datasheet), but Timer0 has a "clock select" which allows a choice of the internal clock, prescaled versions of the internal clock, or an external clock on pin T0 (Fig 11-1 on page 65). This choice is set by register TCCR0B. You even get a choice of rising or falling edge detection.
Once that's set up, you'd still need to configure it to set a flag or cause an interrupt when the timer overflows, and that's probably some more timer register poking somewhere (TIMSK register looks likely).

Ksander

Yes, that is what I fiddled around with. However, it appears you can only select (scaled) options of the system clock or an external clock; not the peripheral clock (Table 11-6. Clock Select Bit Description).

ElectricDruid

Quote from: Ksander on October 31, 2024, 04:13:34 AMHowever, it appears you can only select (scaled) options of the system clock or an external clock; not the peripheral clock (Table 11-6. Clock Select Bit Description).

The external clock is what you want, isn't it? Feed the VCO output from the PT2399 to the T0 pin, and then the Timer overflow is the /256 signal to measure.

Ksander

Quote from: ElectricDruid on October 31, 2024, 06:26:12 AM
Quote from: Ksander on October 31, 2024, 04:13:34 AMHowever, it appears you can only select (scaled) options of the system clock or an external clock; not the peripheral clock (Table 11-6. Clock Select Bit Description).

The external clock is what you want, isn't it? Feed the VCO output from the PT2399 to the T0 pin, and then the Timer overflow is the /256 signal to measure.

Of course! I didn't think of it that way :icon_eek: