News:

SMF for DIYStompboxes.com!

Main Menu

ATtiny85 code issues

Started by patrick398, September 09, 2020, 06:24:12 AM

Previous topic - Next topic

ElectricDruid

I'd rather say that the Arduino model isn't really set up for doing stuff in parallel. After all, no processor (ok, no processor like these little ones) ever does more than one thing at once. "Parallel processing" on a little single core chip is jumping back and forth between different things quickly enough no-one notices. Arduino tends to screw that up because delay loops and many other processes hold up the whole chip until the delay is finished. Which is fine if that's the only thing you're doing, but often, you could be using that time when that process doesn't need dealing with to be doing other stuff instead. In my view, interrupts are only for stuff that is *time critical* - that has to be done at a *particular* time, like getting a sample out to a DAC. Any variation there in when it goes out leads to noise in the output. Not good. The *calculation* of that sample however could be done any time, as long as it's completed before it's needed. That gives you much more flexibility.

Cripes, I'm in a philosophical frame of mind tonight. Sorry, I'll shut up.

T.

patrick398

OK thanks for that, i think i may have underestimated what the chips are capable of and also how easily achievable what i want to do is. I think it's best for now if i use separate chips, one for the delayed trigger i've already done and one for the tempo measure and off beat trigger. That should simplify things for me ha.
Am i right in thinking i want to use the pulseIn function to measure the high and low state of the pin attached to the tap tempo? Then i can add them to get the period length and multiply that by something to get the BPM? Then i guess i'd need to delay that for the length of one high or low state, then trigger the output to go high and low with a delay of one period...that should match the input tempo but output on the offbeat right?
Maybe that made no sense

potul

I think you can do this in one chip, but you need to change something in the approach.

I would start by never using the delay() function. Instead, you can use millis() to record your initial time and simply compare this with the new millis() in every loop until you reach the needed delay. This way you don't stop the rest of activities.

I've done similar things with an attiny for a tap tempo chip I developed for the FV-1.

You need to base your coding on millis() and trigger state changes based on it and inputs coming from buttons, etc...



patrick398

Quote from: potul on September 12, 2020, 04:58:09 AM
I think you can do this in one chip, but you need to change something in the approach.

I would start by never using the delay() function. Instead, you can use millis() to record your initial time and simply compare this with the new millis() in every loop until you reach the needed delay. This way you don't stop the rest of activities.

I've done similar things with an attiny for a tap tempo chip I developed for the FV-1.

You need to base your coding on millis() and trigger state changes based on it and inputs coming from buttons, etc...

Using a separate chip isn't really an issue and if it makes things a lot easier i think i'd rather do that. I've been looking at some code for tap tempo using arduino and i feel like it's a bit above my pay grade.
This one specifically i can get working but i can't figure out how to take the tempo output and use if to set an output on a different pin.


// include the ArduinoTapTempo library
#include <ArduinoTapTempo.h>

// define the pin you want to attach your tap button to
const int BUTTON_PIN = 5;

// make an ArduinoTapTempo object
ArduinoTapTempo tapTempo;

void setup() {
  // begin serial so we can see the state of the tempo object through the serial monitor
  Serial.begin(9600);

  // setup your button as required by your project
  pinMode(BUTTON_PIN, INPUT);
  digitalWrite(BUTTON_PIN, HIGH);
}

void loop() {
  // get the state of the button
  boolean buttonDown = digitalRead(BUTTON_PIN) == LOW;
 
  // update ArduinoTapTempo
  tapTempo.update(buttonDown);

  // print out the beats per minute
  Serial.print("bpm: ");
  Serial.println(tapTempo.getBPM());
}

patrick398

Thanks Rob!

https://www.diystompboxes.com/smfforum/index.php?topic=121506.0

Got this working nicely, not sure if it's going to play nice with continuous pulses coming from the existing tap tempo. I'll try it out and report back

patrick398

As suspected, this tap tempo doesn't like receiving continuous pulses. Is there something in the code that could be modified so that the output is constantly following the input (i can worry about delaying it later). It also occured to me that surely this could be as simple as reading the high/low state of the input pin and just writing that state to the output pin?


void setup() {
  pinMode( 12, INPUT_PULLUP );   /*   /* tap button - press it to set the tempo */
  pinMode( 11, OUTPUT );  /* tempo display light - shows the current tempo */

}


int lastTapState = LOW;  /* the last tap button state */
unsigned long currentTimer[2] = { 500, 500 };  /* array of most recent tap counts */
unsigned long timeoutTime = 0;  /* this is when the timer will trigger next */

unsigned long indicatorTimeout; /* for our fancy "blink" tempo indicator */

void loop()
{
  /* read the button on pin 12, and only pay attention to the
     HIGH-LOW transition so that we only register when the
     button is first pressed down */
  int tapState = digitalRead( 12 );
  if ( tapState == LOW && tapState != lastTapState )
  {
    tap(); /* we got a HIGH-LOW transition, call our tap() function */
  }
  lastTapState = tapState; /* keep track of the state */

  /* check for timer timeout */
  if ( millis() >= timeoutTime )
  {
    /* timeout happened.  clock tick! */
    indicatorTimeout = millis() + 30;  /* this sets the time when LED 13 goes off */
    /* and reschedule the timer to keep the pace */
    rescheduleTimer();
  }

  /* display the button state on LED 2 */
  digitalWrite( 13, tapState );

  /* display the tap blink on LED 13 */
  for ( int i = 2 ; i < 12 ; i++ ) {
    if ( millis() < indicatorTimeout ) {
      digitalWrite( i, HIGH );
    } else {
      digitalWrite( i, LOW );
    }
  }
}

unsigned long lastTap = 0; /* when the last tap happened */
void tap()
{
  /* we keep two of these around to average together later */
  currentTimer[1] = currentTimer[0];
  currentTimer[0] = millis() - lastTap;
  lastTap = millis();
  timeoutTime = 0; /* force the trigger to happen immediately - sync and blink! */
}

void rescheduleTimer()
{
  /* set the timer to go off again when the time reaches the
     timeout.  The timeout is all of the "currentTimer" values averaged
     together, then added onto the current time.  When that time has been
     reached, the next tick will happen...
  */
  timeoutTime = millis() + ((currentTimer[0] + currentTimer[1]) / 2);
}

anotherjim

Interrupts are the obvious way to make the CPU multitask, but pin change interrupts are unexpectedly difficult to deal with. Setting them up is trivial, but the pin interrupts are shared between ports, so the interrupt handling code has to make time to read the pins to find out which pin did it and quickly before it changes! I have coded a Mega328 to read six pots and six trigger pins using pin interrupts while the ADC reading is in the main loop, but it did make my brain hurt! I programmed in AVR assembler and have no idea where to start in Arduino, but I would hope the Arduino interrupt library will do the fancy work for you.

Peripheral interrupts (ADC, UART etc) are far easier. An ADC interrupt (only one of them) is obvious to handle and the ADC can be made to automatically start a new conversion when the last value is read out of it.

Arduino Analog read is easy to use but inefficient. It takes time (many CPU clocks) from the start of conversion to completion during which a lot could be done. Directly coding in either assembler or C (you can easily include C code in an Arduino sketch*) lets you choose all the ADC options such as conversion speed and a slightly faster 8bit result. When reading multiple pins with the ADC, your own code can change the pin it reads Before completion and start another conversion right after reading the value of the previous pin. You can change the read pin selected early because a few clocks after starting the ADC, the last pin voltage is stored on the ADC sampling capacitor and disconnected from the pins while the conversion is taking place.

Tiny85 has a special trick - a PLL frequency multiplier that can either increase the rate of internal peripheral clocking, or the CPU or both. The CPU clock boost only allows doubling. The internal RC clock, when set for 8Mhz, can be doubled to 16Mhz!

*I would always have my doubts about what other Arduino functions you could wreck by making direct changes outside of the language.

patrick398

Thanks for the reply Jim, it's going to take a while to digest that! I have so much to learn  :icon_eek:
I'm fairly set on using a separate chip for this task now, using interrupts seems like a lot could go wrong considering how little i know. Just to clarify, are you talking about using interrupts for doing everything on one chip? Or are you talking about using interrupts to modify the code i just posted to get it to continually track the input?

Sorry i'm not making myself very clear and i seem to be getting more confused by the minute haha

anotherjim

Interrupts mean not having to have the code looping and waiting for one thing to happen before it can do something else. Interrupts have their own overheads and you must have some RAM reserved for the stack. It's possible to do many projects without interrupts though. Some I've done haven't even used a stack at all because I wanted all of the RAM for audio samples.

potul

Quote from: patrick398 on September 12, 2020, 07:54:13 AM
As suspected, this tap tempo doesn't like receiving continuous pulses. Is there something in the code that could be modified so that the output is constantly following the input (i can worry about delaying it later). It also occured to me that surely this could be as simple as reading the high/low state of the input pin and just writing that state to the output pin?


I don't see any reason why it does not work. It should output following the input clock. The only thing is that it is doing an average of the last 2 pulses, and the length of each pulse is fixed at 30ms. For very fast speeds it will not work. What clock rates are you trying to use?

patrick398

I'm actually reading the squarewave output of a stomplfo chip, I've just had a thought though, it's a pen output, is the arduino reading incredibly fast pulses rather than the overall squarewave output?
I did load a frequency counted onto the arduino and it was reading the stomplfo output as 18khz or something like that I think

ElectricDruid

Quote from: patrick398 on September 13, 2020, 06:35:30 AM
I'm actually reading the squarewave output of a stomplfo chip, I've just had a thought though, it's a pen output, is the arduino reading incredibly fast pulses rather than the overall squarewave output?
I did load a frequency counted onto the arduino and it was reading the stomplfo output as 18khz or something like that I think

The raw StompLFO output is a PDM signal at about 2MHz, iirc. So yeah, you should put a simple RC lowpass in before you feed it to your Arduino, or you could get very weird results.

patrick398

Ok great! I'll give that a go tomorrow and fingers crossed it'll work. Then I've just go to work out how to put the output on the off-beat. Expect many more irritating questions  :icon_redface:

patrick398

Quote from: potul on September 12, 2020, 05:52:38 PM
I don't see any reason why it does not work. It should output following the input clock. The only thing is that it is doing an average of the last 2 pulses, and the length of each pulse is fixed at 30ms. For very fast speeds it will not work. What clock rates are you trying to use?

You were spot on, it does work but at higher speeds it goes out of time. I'd say it goes out of time when it hits around 4 pulses per second. Why is this the case?

Thank you!

ElectricDruid

Quote from: patrick398 on September 14, 2020, 06:06:56 AM
You were spot on, it does work but at higher speeds it goes out of time. I'd say it goes out of time when it hits around 4 pulses per second. Why is this the case?

The timing accuracy is milliseconds, right? So 4 pulses per second is a timer value of 250 milliseconds, which seems big enough to avoid rounding errors or similar. I doubt that's the problem.

How fast does the code go around the loop? Millisecond-accurate timing is no good is you're only checking back to see if anything's happened every 100msecs.


potul

#35
I was expecting it to fail more in the range of 33 per second...

First thing you can try is to have a variable pulse width. Now the pulse width is always 30ms, but you can try to make it always equal to T/2 (being T the current period)

So Instead of this:

  if ( millis() >= timeoutTime )
  {
    /* timeout happened.  clock tick! */
    indicatorTimeout = millis() + 30;  /* this sets the time when LED 13 goes off */
    /* and reschedule the timer to keep the pace */
    rescheduleTimer();
  }


you can use this:


  if ( millis() >= timeoutTime )
  {
    /* timeout happened.  clock tick! */
    indicatorTimeout = millis() + (currentTimer[0] + currentTimer[1]) / 4;  /* this sets the time when LED 13 goes off */
    /* and reschedule the timer to keep the pace */
    rescheduleTimer();
  }


The other thing you can try is to comment this line out:

  timeoutTime = 0; /* force the trigger to happen immediately - sync and blink! */

As this is forcing to restart a pulse whenver a new pulse is received by the input, and can screw up the whole timing. The ides of the tap tempo is not to be tapping continously..... so this makes sense in a tap tempo context, but I don't think it's needed here.

patrick398

#36
Quote from: ElectricDruid on September 14, 2020, 09:25:47 AM
How fast does the code go around the loop? Millisecond-accurate timing is no good is you're only checking back to see if anything's happened every 100msecs.

I'm not sure, doesn't it just run through the code as fast as it can?

Quote from: potul on September 14, 2020, 09:32:42 AM
I was expecting it to fail more in the range of 33 per second...

First thing you can try is to have a variable pulse width. Now the pulse width is always 30ms, but you can try to make it always equal to T/2 (being T the current period)

So Instead of this:

  if ( millis() >= timeoutTime )
  {
    /* timeout happened.  clock tick! */
    indicatorTimeout = millis() + 30;  /* this sets the time when LED 13 goes off */
    /* and reschedule the timer to keep the pace */
    rescheduleTimer();
  }


you can use this:


  if ( millis() >= timeoutTime )
  {
    /* timeout happened.  clock tick! */
    indicatorTimeout = millis() + (currentTimer[0] + currentTimer[1]) / 4;  /* this sets the time when LED 13 goes off */
    /* and reschedule the timer to keep the pace */
    rescheduleTimer();
  }


The other thing you can try is to comment this line out:

  timeoutTime = 0; /* force the trigger to happen immediately - sync and blink! */

As this is forcing to restart a pulse whenver a new pulse is received by the input, and can screw up the whole timing. The ides of the tap tempo is not to be tapping continously..... so this makes sense in a tap tempo context, but I don't think it's needed here.


I tried both of these but no difference i'm afraid  :(
On slow speeds i can see that the indicator LED i'm using never fully dims. The output of the square LFO is 0.04v to 5v. Do i need to have an if statement saying that if level of input is below a certain threshhold the pin gets written low? I wonder if that could be affecting the timing?

EDIT: if i use pin 13 as the output it does turn completely off, still problem of going out of time though
...in fact if i use any other pin as output it's jittery as hell, even with RC filter

potul

Where did you put the low pass filter?

patrick398

Quote from: potul on September 14, 2020, 10:38:49 AM
Where did you put the low pass filter?

Just before the LED. Pin 8 into a 1k resistor, cap to ground, LED

potul

Quote from: patrick398 on September 14, 2020, 10:39:50 AM
Quote from: potul on September 14, 2020, 10:38:49 AM
Where did you put the low pass filter?

Just before the LED. Pin 8 into a 1k resistor, cap to ground, LED

Ok, this might be the problem. The low pass filter must be put between the stompLFO and your arduino input, not at the output of your arduino. Try to remove it and see how it performs.