Programming an AVR Tap Tempo

When I first started AVR coding I wanted to make a nice Tap Tempo. At that time, I didn't find an easy to understand AVR code for it.

So today, we are going to make a LED blink in sync with the rhythm you tapped. It may not seem like much, but it's an important step in the making of a Tap Tempo, and a cool coding exercise.

Why AVR? I don't really know, to me it seemed a bit more friendly than PIC. With the popularity of Arduino it has a lot of support and I liked the idea of knowing a bit of C.

Keep in mind this isn't an Arduino project (even though it will probably work on an Arduino), coding an AVR chip without Arduino, allows a deeper understanding and control of the micro-controller in my opinion.

Summary :

The Circuit

As you can see, the circuit is really simple. You can connect the resistor and switch to any pin with General Input/Output capacities. ```#include <avr/io.h>

#define TAP_PIN PINB0
#define LED_PIN PINB1

int main(void)
{

while (1)
{
}
}
```
```#include <avr/io.h>

#define TAP_PIN PINB0
#define LED_PIN PINB1

int main(void)
{
DDRB |= (1<<LED_PIN); //Setting LED PIN as an output
PORTB |= (1<<TAP_PIN); //Setting TAP PIN high

while (1)  //Infinite Loop
{
}
}

```
```//Setting a bit in a register is done this way:
PORTA |= (1<<PINA0);

//Clearing a bit in a register is done this way:
PORTA &= ~(1<<PINA0);

//Toggling a bit in a register is done this way:
PORTA ^= (1<<PINA0);
```
```#include <avr/io.h>
#define F_CPU 1000000UL		//indicate the CPU clock frequency
#include <util/delay.h>		//including the delay header
```
```#include <avr/io.h>
#define F_CPU 1000000UL		//indicate the CPU clock frequency
#include <util/delay.h>		//including the delay header
#include <avr/sfr_defs.h>    //including special function register

#define TAP_PIN PINB0
#define LED_PIN PINB1
#define DEBOUNCE_TIME 1000	//define debounce time in µs

uint8_t debounce(void)		//declaring a debounce function
{
if (bit_is_clear(PINB,0))	//if TAP_PIN to ground
{
_delay_us(DEBOUNCE_TIME); //wait for debounce time
if (bit_is_clear(PINB,0))    //if TAP_PIN still to ground
{
return(1);	//function returns 1
}
else		//if TAP_PIN not to ground at any time, function returns 0
{
return(0);
}
}
else
{
return(0);
}
}            //end of debounce function

int main(void)
{
DDRB |= (1<<LED_PIN); //Setting LED PIN as an output
PORTB |= (1<<TAP_PIN); //Setting TAP PIN high
while (1) //infinite loop
{
}
}
```

Setting up the Timer

Now that we can sense the footswitch, let's start counting time between those taps. This is done by setting up a ms timer.

Most AVR chip have a 8bit timer/counter, usually it's Timer 0. We want that timer to count up constantly and to return to 0 when a ms has passed.

Our internal clock is set to 1MHz, so logically we need to count 1000 clock ticks. The problem with that is that a 8bit counter only counts up to 255. We can use a prescaler to avoid that problem, if we use a prescaler of 8 we only need to count 1000/8 = 125 ticks. To select the 8 prescaler we need to set CS01 in the TCCR0B register.

In order to have a counter that counts constantly up to 124 we need to use Fast PWM with a custom TOP value (TOP is 255 by default). The new TOP value will be OCR0A in the mode we choose. This table from the datasheet (p.81) shows us which Waveform Generation Mode to use. We'll change WGMXX bits in TCCR0A and TCCR0B in order to select the WGM mode we need. To count the ms that has passed we need to update a variable every time that the counter reaches its TOP value. To do that we need to enable interrupts. This is done with 3 lines. First, we #include the "interrupts.h" header. Then we enable the timer 0 overflow interrupt, and finally we enable global interrupts with the sei() command.

Our counter is now set up, the last thing to do is to write what to do in our overflow interrupt. Let's create a volatile global variable "ms" to increment every time the overflow interrupt is executed. The vector for our interrupt is "TIM0_OVF_vect".

Our code now looks like that:

```#include <avr/io.h>
#define F_CPU 1000000UL		//indicate the CPU clock frequency
#include <util/delay.h>		//including the delay header
#include <avr/sfr_defs.h>	//including special feature registers
#include <avr/interrupt.h>	//including interrupts

#define TAP_PIN PINB0
#define LED_PIN PINB1
#define DEBOUNCE_TIME 500	//define debounce time in µs

volatile uint16_t ms = 0; //declaring

uint8_t debounce(void)		//declaring a debounce function
{
if (bit_is_clear(PINB,0))	//if TAP_PIN to ground
{
_delay_us(DEBOUNCE_TIME); //wait for debounce time
if (bit_is_clear(PINB,0))    //if TAP_PIN still to ground
{
return(1);    //function returns 1
}
else        //if TAP_PIN not to ground at any time, function returns 0
{
return(0);
}
}
else
{
return(0);
}
}

int main(void)
{
DDRB |= (1<<LED_PIN); //Setting LED PIN as an output
PORTB |= (1<<TAP_PIN); //Setting TAP PIN high

/*timer0 setup*/
OCR0A = 124;		//counter max
TCNT0 = 0;		//Counter start
TCCR0A |= (1<<WGM00) | (1<<WGM01);	//Fast PWM mode OCR1A as TOP
TIMSK0 |= (1<<TOIE0);	//Overflow interrupt enable
TCCR0B |=  (1<<WGM02) | (1<<CS01); //Fast PWM mode, No prescaler

sei(); //Activate interrupts
while (1)
{
}
}

ISR(TIM0_OVF_vect) //Timer overflow interrupt
{
ms++;	//increment ms every ms
}
```

The Tap Tempo

Now that peripherals are set up, let's code that Tap Tempo!

We'll need to keep track of some things, so let's create variables for each one :

- Has the button been released ?: if the program doesn't know it will count hundreds of taps if we keep the button pressed. So we create a uint8_t variable buttonstate. 1 if pressed 0 if released. It is initialised as 0.

- How many taps did we do ?: you don't want to do the same thing for the first and 30th tap, so lets keep track of that. We create a uint8_t variable called nbtap.

- What's the maximum delay allowed ?: If we don't limit the maximum tempo the AVR will never stop counting and that can be a problem between tap sequences. Let's create a uint16_t variable called maxtempo. We'll choose 2000 ms.

- What tempo did we just tap? : The most obious variable needed, the tempo we want the LED to blink to. Let's create a uint16_t variable called tempo. With an initial tempo of 120bpm (500ms).

We need to declare these in the main function :

Now let's start at the beginning, what do we do at the first press of the button?

Well, we need to start counting the ms in case of a second tap. We also need to indicate that the button has been pressed once, and not released yet.

The code will be an "if" statement in the main loop:

Now let's notify the program when the button is released :

Let's add another "if" for the case if we exceed the maximum tempo. We'll need to reset  the nbtap variables in order to have a normal tap sequence next time.

And finally, when we tap more than once, let's update the tempo with the ms counted between the two taps. Not forgatting to update, buttonstate and nbtap variable. We will also reset the  counter to count for next tap too.

```int main(void)
{
uint8_t buttonstate = 0;    //Was the button released?
uint8_t nbtap = 0;     //Number of taps in sequence
uint16_t maxtempo = 2000;    //Maximum tempo allowed in ms
uint16_t tempo = 500;    //The current tempo
```
```if (debounce() == 1 && nbtap == 0 && buttonstate == 0) //first tap
{
TCNT0 = 0;	//starts counting
ms = 0;
nbtap++;    //the button was tapped once
buttonstate = 1; //the button isn't released yet
}
```
```if (debounce() == 0 && buttonstate == 1)	// if button released
{
buttonstate = 0;
}
```
```if (ms >= maxtempo)	// ms exceed maximum tempo
{
nbtap = 0;  //reset tap sequence
}
```
```if (debounce() == 1 && nbtap != 0 && buttonstate == 0) //not first tap
{
tempo = ms;		//update tempo
nbtap++;
buttonstate = 1;
TCNT0 = 0;		//reset counter
ms = 0;
}
```

Everything has been taken care of, we just need to make the LED blink now. What I propose to do is to add another ms counter for the LED. This way, the LED blinking is independant of the Tap tempo ms count.

So we add a uint16_t volatile general variable, called "ledms". We need to declare it the same way than "ms". This variable will be incremented in the timer overflow interrupt, just like "ms".

Now that our led variable is set up, let's make the LED blink with an "if" statement in the main loop.

This way the LED wil be on on the downbeat and stay on for 8ms, then turn off until the next downbeat.

```volatile uint16_t ms = 0; //declaring ms counting variables
volatile uint16_t ledms = 0;

ISR(TIM0_OVF_vect) //Timer overflow interrupt
{
ms++;	//increment every ms
ledms++;
}```
```if (ledms >= tempo)
{
ledms = 0;
PORTB |= (1<<LED_PIN);
_delay_ms(8);
PORTB &= ~(1<<LED_PIN);
}
```
```#include <avr/io.h>
#define F_CPU 1000000UL		//indicate the CPU clock frequency
#include <util/delay.h>		//including the delay header
#include <avr/interrupt.h>	//including interrupts

#define TAP_PIN PINB0
#define LED_PIN PINB1
#define DEBOUNCE_TIME 500	//define debounce time in us

volatile uint16_t ms = 0; //declaring ms counting variables
volatile uint16_t ledms = 0;

uint8_t debounce(void)		//declaring a debounce function
{
if (bit_is_clear(PINB,0))	//if TAP_PIN to ground
{
_delay_us(DEBOUNCE_TIME); //wait for debounce time
if (bit_is_clear(PINB,0))	//if TAP_PIN still to ground (switch still pressed)
{
return(1);	//function returns 1
}
else	//if TAP_PIN not to ground at any time, function returns 0
{
return(0);
}
}
else
{
return(0);
}
}

int main(void)
{
DDRB |= (1<<LED_PIN); //Setting LED PIN as an output
PORTB |= (1<<TAP_PIN); //Setting TAP PIN high

/*timer0 setup*/
OCR0A = 124;		//counter max
TCNT0 = 0;		//Counter start
TCCR0A |= (1<<WGM00) | (1<<WGM01);	//Fast PWM mode OCR1A as TOP
TIMSK0 |= (1<<TOIE0);	//Overflow interrupt enable
TCCR0B |=  (1<<WGM02) | (1<<CS01); //Fast PWM mode, No prescaler

uint8_t buttonstate = 0;	//Was the button released?
uint8_t nbtap;			// Number of Taps in sequence
uint16_t maxtempo = 2000;	//Maximum tempo allowed in ms
uint16_t tempo = 500;

sei(); //Activate interrupts

while (1)
{
if (debounce() == 1 && nbtap == 0 && buttonstate == 0) //first tap
{
TCNT0 = 0;	//starts counting
ms = 0;
nbtap++;
buttonstate = 1;
}

if (debounce() == 0 && buttonstate == 1)	// if button released
{
buttonstate = 0;
}

if (ms >= maxtempo)		//if maxtempo exeeded
{
nbtap = 0;			//reset tap sequence
}

if (debounce() == 1 && nbtap != 0 && buttonstate == 0) //not first tap
{
tempo = ms;		//update tempo
nbtap++;
buttonstate = 1;
TCNT0 = 0;		//reset counter
ms = 0;
}

if (ledms >= tempo)
{
ledms = 0;
PORTB |= (1<<LED_PIN);
_delay_ms(8);
PORTB &= ~(1<<LED_PIN);
}
}
}

ISR(TIM0_OVF_vect) //Timer overflow interrupt
{
ms++;	//increment ms every ms
ledms++;
}
```    Legal 