mirror of
https://codeberg.org/eel4746_piano/avr_piano.git
synced 2024-11-22 09:10:30 -05:00
Vineet K
edd35cae3b
Besides moving the UART into an interrupt, we also need to read from UART to set the mode of playing from buttons or from serial notes
154 lines
2.8 KiB
C
154 lines
2.8 KiB
C
/*
|
|
* EEL4746C - AVR Piano - Speaker Section
|
|
* speaker is connected to PD2 and ground
|
|
* 15 buttons are connected to PD3-7, PC0-4, PB0-4
|
|
* the LCD Arduino UNO is connected to TX/RX (PD0/1)
|
|
*/
|
|
|
|
#include <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
|
|
#define T1_PRESCALAR 8
|
|
#define FREQ(f) (16e6 / f / T1_PRESCALAR / 2)
|
|
|
|
#define HIGH(sn) ((sn >> 8) & 0xff)
|
|
#define LOW(sn) (sn & 0xff)
|
|
|
|
#define P_RX 0
|
|
#define P_TX 1
|
|
#define P_SPKR 2
|
|
|
|
unsigned short freqs[] = {
|
|
// PD3-7
|
|
FREQ(100),
|
|
FREQ(200),
|
|
FREQ(250),
|
|
FREQ(300),
|
|
FREQ(350),
|
|
// PB0-4
|
|
FREQ(400),
|
|
FREQ(500),
|
|
FREQ(600),
|
|
FREQ(700),
|
|
FREQ(750),
|
|
// PC0-4
|
|
FREQ(1000),
|
|
FREQ(1250),
|
|
FREQ(1500),
|
|
FREQ(1750),
|
|
FREQ(2000),
|
|
|
|
[255] = 0 // used as a 'silent' note
|
|
};
|
|
unsigned char note = 0;
|
|
|
|
ISR (PCINT0_vect)
|
|
{
|
|
// check if any button in the whole port is pressed
|
|
for (unsigned char i = 0; i <= 5; i++) {
|
|
if (PINB & (1 << i)) {
|
|
note = i + 5;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ISR (PCINT1_vect)
|
|
{
|
|
// check if any button in the whole port is pressed
|
|
for (unsigned char i = 0; i <= 5; i++) {
|
|
if (PINC & (1 << i)) {
|
|
note = i + 10;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ISR (PCINT2_vect)
|
|
{
|
|
// check if any button in the whole port is pressed
|
|
for (unsigned char i = 3; i <= 7; i++) {
|
|
if (PIND & (1 << i)) {
|
|
note = i - 3;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
usart_init(void)
|
|
{
|
|
// 115200 bps, RX/TX enabled
|
|
UBRR0 = 8;
|
|
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
|
|
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
|
|
}
|
|
|
|
void
|
|
usart_send(unsigned char c)
|
|
{
|
|
while (!(UCSR0A & (1 << UDRE0)));
|
|
UDR0 = c;
|
|
}
|
|
|
|
unsigned char
|
|
usart_read(void)
|
|
{
|
|
while (!(UCSR0A & (1 << RXC0)));
|
|
return UDR0;
|
|
}
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
// Data Direction for Buttons, UART, Speaker
|
|
DDRB = 0x00;
|
|
DDRC = 0x00;
|
|
DDRD = (1 << P_TX) | (1 << P_SPKR);
|
|
|
|
// Enabling Pin Change Interrupt for All Buttons
|
|
PCMSK0 = 0b00011111;
|
|
PCMSK1 = 0b00011111;
|
|
PCMSK2 = 0b11111000;
|
|
PCICR = (1 << PCIE0) | (1 << PCIE1) | (1 << PCIE2);
|
|
|
|
TCCR1A = 0x00; // use timer1 CTC mode
|
|
TCCR1B = 1 << WGM12; // already prescaled by 8 in table
|
|
|
|
sei();
|
|
|
|
usart_init();
|
|
|
|
// Sends Square wave to the Speaker When a Button is Pressed
|
|
unsigned short cycles = 0;
|
|
unsigned char last_note = 0;
|
|
for (unsigned char n = 0;; n = (n + 1) % 2) {
|
|
// check if any buttons are pressed, otherwise don't play a note
|
|
if (((PINB & PCMSK0) | (PINC & PCMSK1) | (PIND & PCMSK2)) == 0) {
|
|
// if a button was released, then send note and cycles played
|
|
if (cycles > 0) {
|
|
usart_send(last_note);
|
|
usart_send(HIGH(cycles));
|
|
usart_send(LOW(cycles));
|
|
usart_send('\xff');
|
|
cycles = 0;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Prepare Timer/Counter
|
|
TCNT1 = 1;
|
|
last_note = note;
|
|
OCR1A = freqs[note];
|
|
TCCR1B |= 1 << CS11;
|
|
|
|
while ((TIFR1 & (1 << OCF1A)) == 0); // Monitor OutPut Compare Flag
|
|
|
|
// Toggles port For Speaker
|
|
PORTD ^= 1 << P_SPKR;
|
|
TIFR1 |= 1 << OCF1A;
|
|
TCCR1B &= ~(1 << CS11);
|
|
cycles = (cycles + 1) % 0xffff;
|
|
}
|
|
}
|