Skip to content

Commit

Permalink
Interrupts for rotary encoder and buttons, timer0 based sound generat…
Browse files Browse the repository at this point in the history
…or, timer1 based tap tempo, basic metronome
  • Loading branch information
wysockipiotr committed Nov 20, 2018
1 parent a346c93 commit 25bae8d
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 14 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
# metronome
# metronome

```
make flash
```
97 changes: 97 additions & 0 deletions interrupts.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#ifndef __interrupts_h__
#define __interrupts_h__

ISR(PCINT2_vect) {
clearBit(SPEAKER_DIR, SPEAKER);
bool btnPushed = bit_is_clear(ROTARY_PIN, ROTARY_BTN);
if (bit_is_clear(ROTARY_PIN, ROTARY_A))
{
if (bit_is_clear(ROTARY_PIN, ROTARY_B))
{
if (btnPushed)
{
if (cursor)
{
--cursor;
}
else
{
cursor = 3u;
}
}
else
{
updateActiveParam(-1);
}
}
else
{
if (btnPushed)
{
++cursor;
cursor %= 4u;
}
else
{
updateActiveParam(1);
}
}

t_beep = 6000u / bpm;
t_sleep = 54000u / bpm;

update_display();
// force a little down time before continuing
_delay_ms(2);
// wait until R1 comes back high
while (bit_is_clear(ROTARY_PIN, ROTARY_A))
;
}

if (bit_is_clear(TAP_PIN, TAP_BTN))
{
sound_locked = false;
play_note(120u, t_beep);
sound_locked = true;
// tap tempo handler
if (tap_started)
{
if (tap_interval_counter < 2001u && tap_interval_counter > 130u)
{
t_beep = tap_interval_counter >> 4;
t_sleep = tap_interval_counter - t_beep;
bpm = 60000u / tap_interval_counter;
update_display();
}
}
else
{
tap_started = true;
sound_locked = true;
}
// start counting
tap_interval_counter = 0u;
//TCCR2A = 0;
TCCR2B = 0x4;
TCNT2 = TAP_TIMER_INITIAL_OFFSET; // 131 ticks until overflow
}
}

// tap tempo timer (1ms) overflow
ISR(TIMER2_OVF_vect)
{
if (tap_interval_counter < 2001u)
{
++tap_interval_counter;
TCNT2 = TAP_TIMER_INITIAL_OFFSET;
}
else
{
// dismiss tap tempo in case of timeout (over 2000ms)
tap_started = false;
TCCR2B &= ~(TCCR2B & 0x7);
sound_locked = false;
}
}

#endif //__interrupts_h__
4 changes: 2 additions & 2 deletions lcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ void lcd_init(void) {
_delay_ms(2);
}

void lcd_string(char *str) {
void lcd_string(char * str) {
int i;
for (i = 0; str[i] != 0; i++) /* Send each char of string till the NULL */
{
lcd_char(str[i]);
}
}

void lcd_string_xy(char row, char pos, char *str) {
void lcd_string_xy(char row, char pos, char * str) {
if (row == 0 && pos < 16)
lcd_command((pos & 0x0F) | LCD_FIRST_LINE); /* Command of first row and required position<16 */
else if (row == 1 && pos < 16)
Expand Down
7 changes: 4 additions & 3 deletions lcd.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#ifndef __lcd_h__
#define __lcd_h__

#define LCD_FIRST_LINE 0x80
#define LCD_SIZE 16u
#define LCD_FIRST_LINE 0x80
#define LCD_SECOND_LINE 0xC0

void lcd_init(void);
Expand All @@ -12,8 +13,8 @@ void lcd_char(unsigned char data);

void lcd_clear(void);

void lcd_string(char *str);
void lcd_string(char * str);

void lcd_string_xy(char row, char pos, char *str);
void lcd_string_xy(char row, char pos, char * str);

#endif //__lcd_h__
150 changes: 148 additions & 2 deletions main.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,153 @@
#define F_CPU 8000000

#ifndef BAUD
#define BAUD 9600
#endif

#include <util/setbaud.h>
#include <util/delay.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <stdio.h>

#include "util.h"
#include "setup.h"
#include "lcd.h"

volatile uint8_t cursor;
volatile uint16_t bpm;
volatile uint8_t signature; // e.g. 0100 0100 for 4/4 time signature
volatile uint16_t t_beep;
volatile uint16_t t_sleep;
volatile uint8_t volume;
volatile uint16_t tap_interval_counter;
volatile bool tap_started;
volatile bool sound_locked;
uint8_t signatures[2] = { 0x44, 0x34 };
char firstLineBuffer[LCD_SIZE+1];
char secondLineBuffer[LCD_SIZE+1];

static void setup(void);

static void loop(void);

inline static void update_display(void) {
snprintf(firstLineBuffer, 17, "%c %3uBPM %c %2u/%1u ",
cursorVisible(cursor, 0),
bpm,
cursorVisible(cursor, 1),
signatures[signature] >> 4,
signatures[signature] & 0x0f);
snprintf(secondLineBuffer, 17, "%c %3u%% %c %s",
cursorVisible(cursor, 2),
volume,
cursorVisible(cursor, 3),
"SOUND");
lcd_string_xy(0, 0, firstLineBuffer);
lcd_string_xy(1, 0, secondLineBuffer);
}

static void updateActiveParam(int delta) {
switch (cursor) {
case 0u:
if (bpm > 80u && bpm < 350u)
bpm += delta;
break;
case 1u:
signature += delta;
signature %= 2;
break;
case 2u:
if (volume > 0u && volume < 100u)
volume += delta;
break;
case 3u:
break;
default:
break;
}
}

inline static void init_interrupts(void) {
// enable pin change interrupts on PORTD
PCICR |= (1 << PCIE2);

// pin change interrupt mask
PCMSK2 |= ((1 << ROTARY_A) | (1 << ROTARY_B) | (1 << TAP_BTN));

// enable interrupts globally
sei();
}

inline static void init_tap_timer(void) {
TIMSK2 = (1 << TOIE2);
}

inline static void init_sound_timer(void) {
setBit(TCCR0A, WGM01);
setBit(TCCR0A, COM0A0);
setBit(TCCR0B, CS00);
setBit(TCCR0B, CS01);
}

inline static void play_note(uint8_t wavelength, uint8_t duration) {
OCR0A = wavelength;
if (!sound_locked) {
setBit(SPEAKER_DIR, SPEAKER);
while (duration) {
_delay_ms(1u);
--duration;
}
clearBit(SPEAKER_DIR, SPEAKER);
}
}

// IMPORTANT: this must be included right here
#include "interrupts.h"

int main(void) {
while (true);
setup();
loop();
return (0);
}
}

static void setup(void) {
// clk / 1
clock_prescale_set(CLK_NO_DIVIDE);

// TODO Read stored settings from internal EEPROM
cursor = 0u;
bpm = DEFAULT_BPM;
volume = DEFAULT_VOL;
signature = DEFAULT_SIGNATURE;
t_beep = 6000u / bpm;
t_sleep = 54000u / bpm;
tap_interval_counter = 0u;
tap_started = false;
sound_locked = false;

lcd_init();
init_tap_timer();
init_sound_timer();
init_interrupts();

setInputPullup(ROTARY_DIR, ROTARY_PORT, ROTARY_A);
setInputPullup(ROTARY_DIR, ROTARY_PORT, ROTARY_B);
setInputPullup(ROTARY_DIR, ROTARY_PORT, ROTARY_BTN);
setInputPullup(TAP_DIR, TAP_PORT, TAP_BTN);

update_display();
}

static void loop(void) {
uint8_t i = 0u;
while (true) {
play_note(100u, t_beep);
_delay_ms(t_sleep);
for (i = 0u; i < (signatures[signature] >> 4) - 1; ++i) {
play_note(120u, t_beep);
_delay_ms(t_sleep);
}
}
}

10 changes: 4 additions & 6 deletions setup.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
#ifndef __setup_h__
#define __setup_h__

#define F_CPU 8000000

#ifndef BAUD
#define BAUD 9600
#endif

#define LCD_DIR DDRC
#define LCD_PORT PORTC
#define RS PC4
Expand All @@ -31,4 +25,8 @@

#define TAP_TIMER_INITIAL_OFFSET 0x83

#define DEFAULT_BPM 140u
#define DEFAULT_VOL 50u
#define DEFAULT_SIGNATURE 0

#endif //__setup_h__
4 changes: 4 additions & 0 deletions util.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@

#define setInputPullup(ddr, port, pin) clearBit((ddr),(pin));setBit((port),(pin))

#define cursorVisible(cursorPos, currentPos) ((cursorPos) == (currentPos)) ? '>' : ' '

#define CLK_NO_DIVIDE 0

#endif //__util_h__

0 comments on commit 25bae8d

Please sign in to comment.