Electronic – How to implement a synchronization signal with AVR (attiny45)

attinyavrinterruptspwmsynchronous

In pseudocode I want to do following:

i = 0
state = 0

while (1):

  compareState = readDigitalIn()
  if state == compareState:
    i = i+1
  else:
    i = 0
    state = compareState

  writeDigitalOut(dataVector[i])

In english, I want to reset the counter i every time I change the state of digitalIn-pin.

Is the above a good way of doing this? Does attiny have event listeners or similar?

The ultimate goal is to synchronize multiple pwm controllers in different chips (writeDigitalOut is changed to AdjustPwmFrequency…)

Edit: Can I use interrupts for this? How? Can create an interrupt that fires when digitalIn-pin is changed?

Best Answer

Yes, this is a pretty good application to use interrupts for.

What you are interested in is the external interrupt, but let's break down the general structure of an interrupt first.

If you have enabled a particular interrupt on your system, when that interrupt type occurs, the processor will look up into a table to find the function or service routine to jump to in order to service that particular event.

This function is called an Interrupt Service Routine, or ISR for short. This is a method that you must write.

So let's identify which interrupt you want and which ISR you want to write. To do this, we need to consult the datasheet. The type of interrupt that you're interested in is in the family of interrupts called external interrupts. An excerpt from the ATiny45 datasheet:

9.2 External Interrupts

The External Interrupts are triggered by the INT0 pin or any of the PCINT[5:0] pins. Observe that, if enabled, the interrupts will trigger even if the INT0 or PCINT[5:0] pins are configured as outputs. This feature provides a way of generating a software interrupt. Pin change interrupts PCI will trigger if any enabled PCINT[5:0] pin toggles. The PCMSK Register controls which pins contribute to the pin change interrupts. Pin change interrupts on PCINT[5:0] are detected asynchronously. This implies that these interrupts can be used for waking the part also from sleep modes other than Idle mode.

The INT0 interrupts can be triggered by a falling or rising edge or a low level. This is set up as indicated in the specification for the MCU Control Register – MCUCR. When the INT0 interrupt is enabled and is configured as level triggered, the interrupt will trigger as long as the pin is held low. Note that recognition of falling or rising edge interrupts on INT0 requires the presence of an I/O clock, described in “Clock Systems and their Distribution” on page 23.

So they've mentioned some registers of interest here: PCMSK and MCUCR

PCMSK controls which pins can trigger the interrupt - so you want to wire up your toggling signal to whichever pin(s) you specify here, and MCUCR defines how the pins must change in order to trigger the interrupt. In this case, you would probably be interested in configuring your interrupt for Any logical change on INT0 generates an interrupt request. As outlined in table 9-2 in the document. Continue reading what the registers do in the external interrupt section, they are as follows: MCUCR, GIMSK, GIFR, PCMSK. The information you want to get out of this is

  1. How to configure the interrupt (PCMSK and MCUCR)
  2. How to enable or disable the interrupt (GIMSK)
  3. How to check its current status (GIFR)

Now on to the actual coding. The structure will look something like this (pseudo code):

volatile int i; // volatile because this gets changed in the ISR, so the compiler will know to NOT optimize this when used in loops

int main(void)
{
     /* set-up your registers here ... */
     /* enable global interrupts */

     i = 0;
     while (1) {
          boundsSafeVectorPrinter(i);
          i++;
     }
}

ISR(PCINT0_ISR)
{
     i = 0;
}

Now whenever a pin change occurs on one of the pins you specified (given that you've set-up all the registers correctly), the ISR(PCINT0_ISR) routine will be executed.

So how did I know to use ISR(PCINT0_ISR)? You will need to lookup in whatever definitions file that you are using to see if they have provided the PCINT0_ISR mapping for you, if not, consult the datasheet again and look at section 9.1 Interrupt Vectors in ATtiny25/45/8 and define it yourself. If you are using avr-gcc, the ISR() macro is defined here.

I think this covers the basics without giving away too much code. Good luck and have fun!