1
0
Fork 0
forked from forks/qmk_firmware

Added support for audio using pins C4, C5, B6, B7

This commit is contained in:
Michael L. Walker 2018-04-12 08:16:35 -07:00 committed by Jack Humbert
parent 5319667c55
commit 22215a0e92
3 changed files with 137 additions and 78 deletions

View file

@ -61,10 +61,18 @@ This is a C header file that is one of the first things included, and will persi
* COL2ROW or ROW2COL - how your matrix is configured. COL2ROW means the black mark on your diode is facing to the rows, and between the switch and the rows. * COL2ROW or ROW2COL - how your matrix is configured. COL2ROW means the black mark on your diode is facing to the rows, and between the switch and the rows.
* `#define AUDIO_VOICES` * `#define AUDIO_VOICES`
* turns on the alternate audio voices (to cycle through) * turns on the alternate audio voices (to cycle through)
* `#define C4_AUDIO`
* enables audio on pin C4
* `#define C5_AUDIO`
* enables audio on pin C5
* `#define C6_AUDIO` * `#define C6_AUDIO`
* enables audio on pin C6 * enables audio on pin C6
* `#define B5_AUDIO` * `#define B5_AUDIO`
* enables audio on pin B5 (duophony is enable if both are enabled) * enables audio on pin B5 (duophony is enables if one of B[5-7]_AUDIO is enabled along with one of C[4-6]_AUDIO)
* `#define B6_AUDIO`
* enables audio on pin B6 (duophony is enables if one of B[5-7]_AUDIO is enabled along with one of C[4-6]_AUDIO)
* `#define B7_AUDIO`
* enables audio on pin B7 (duophony is enables if one of B[5-7]_AUDIO is enabled along with one of C[4-6]_AUDIO)
* `#define BACKLIGHT_PIN B7` * `#define BACKLIGHT_PIN B7`
* pin of the backlight - B5, B6, B7 use PWM, others use softPWM * pin of the backlight - B5, B6, B7 use PWM, others use softPWM
* `#define BACKLIGHT_LEVELS 3` * `#define BACKLIGHT_LEVELS 3`

View file

@ -1,6 +1,17 @@
# Audio # Audio
Your keyboard can make sounds! If you've got a Planck, Preonic, or basically any AVR keyboard that allows access to the C6 or B5 port (`#define C6_AUDIO` and/or `#define B5_AUDIO`), you can hook up a simple speaker and make it beep. You can use those beeps to indicate layer transitions, modifiers, special keys, or just to play some funky 8bit tunes. Your keyboard can make sounds! If you've got a Planck, Preonic, or basically any AVR keyboard that allows access to certain PWM-capable pins, you can hook up a simple speaker and make it beep. You can use those beeps to indicate layer transitions, modifiers, special keys, or just to play some funky 8bit tunes.
Up to two simultaneous audio voices are supported, one driven by timer 1 and another driven by timer 3. The following pins can be defined as audio outputs in config.h:
Timer 1:
`#define B5_AUDIO`
`#define B6_AUDIO`
`#define B7_AUDIO`
Timer 3:
`#define C4_AUDIO`
`#define C5_AUDIO`
`#define C6_AUDIO`
If you add `AUDIO_ENABLE = yes` to your `rules.mk`, there's a couple different sounds that will automatically be enabled without any other configuration: If you add `AUDIO_ENABLE = yes` to your `rules.mk`, there's a couple different sounds that will automatically be enabled without any other configuration:

View file

@ -35,44 +35,81 @@
// Timer Abstractions // Timer Abstractions
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// TIMSK3 - Timer/Counter #3 Interrupt Mask Register //Currently we support timers 1 and 3 used at the sime time, channels A-C,
// Turn on/off 3A interputs, stopping/enabling the ISR calls //pins PB5, PB6, PB7, PC4, PC5, and PC6
#ifdef C6_AUDIO #if defined(C6_AUDIO)
#define CPIN_AUDIO
#define CPIN_SET_DIRECTION DDRC |= _BV(PORTC6);
#define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30);
#define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A) #define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A)
#define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A) #define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A)
#endif
#ifdef B5_AUDIO
#define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1A)
#define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1A)
#endif
// TCCR3A: Timer/Counter #3 Control Register
// Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6
#ifdef C6_AUDIO
#define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1); #define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1);
#define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0)); #define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0));
#endif
#ifdef B5_AUDIO
#define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1A1);
#define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0));
#endif
// Fast PWM Mode Controls
#ifdef C6_AUDIO
#define TIMER_3_PERIOD ICR3 #define TIMER_3_PERIOD ICR3
#define TIMER_3_DUTY_CYCLE OCR3A #define TIMER_3_DUTY_CYCLE OCR3A
#define TIMER3_AUDIO_vect TIMER3_COMPA_vect
#endif
#if defined(C5_AUDIO)
#define CPIN_AUDIO
#define CPIN_SET_DIRECTION DDRC |= _BV(PORTC5);
#define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3B1) | (0 << COM3B0) | (1 << WGM31) | (0 << WGM30);
#define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3B)
#define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3B)
#define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3B1);
#define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3B1) | _BV(COM3B0));
#define TIMER_3_PERIOD ICR3
#define TIMER_3_DUTY_CYCLE OCR3B
#define TIMER3_AUDIO_vect TIMER3_COMPB_vect
#endif
#if defined(C4_AUDIO)
#define CPIN_AUDIO
#define CPIN_SET_DIRECTION DDRC |= _BV(PORTC4);
#define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3C1) | (0 << COM3C0) | (1 << WGM31) | (0 << WGM30);
#define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3C)
#define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3C)
#define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3C1);
#define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3C1) | _BV(COM3C0));
#define TIMER_3_PERIOD ICR3
#define TIMER_3_DUTY_CYCLE OCR3C
#define TIMER3_AUDIO_vect TIMER3_COMPC_vect
#endif #endif
#ifdef B5_AUDIO #if defined(B5_AUDIO)
#define BPIN_AUDIO
#define BPIN_SET_DIRECTION DDRC |= _BV(PORTB5);
#define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << WGM11) | (0 << WGM10);
#define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1A)
#define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1A)
#define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1A1);
#define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0));
#define TIMER_1_PERIOD ICR1 #define TIMER_1_PERIOD ICR1
#define TIMER_1_DUTY_CYCLE OCR1A #define TIMER_1_DUTY_CYCLE OCR1A
#define TIMER1_AUDIO_vect TIMER1_COMPA_vect
#endif
#if defined(B6_AUDIO)
#define BPIN_AUDIO
#define BPIN_SET_DIRECTION DDRC |= _BV(PORTB6);
#define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1B1) | (0 << COM1B0) | (1 << WGM11) | (0 << WGM10);
#define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1B)
#define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1B)
#define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1B1);
#define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1B1) | _BV(COM1B0));
#define TIMER_1_PERIOD ICR1
#define TIMER_1_DUTY_CYCLE OCR1B
#define TIMER1_AUDIO_vect TIMER1_COMPB_vect
#endif
#if defined(B7_AUDIO)
#define BPIN_AUDIO
#define BPIN_SET_DIRECTION DDRC |= _BV(PORTB7);
#define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1C1) | (0 << COM1C0) | (1 << WGM11) | (0 << WGM10);
#define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1C)
#define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1C)
#define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1C1);
#define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1C1) | _BV(COM1C0));
#define TIMER_1_PERIOD ICR1
#define TIMER_1_DUTY_CYCLE OCR1C
#define TIMER1_AUDIO_vect TIMER1_COMPC_vect
#endif #endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -147,44 +184,48 @@ void audio_init()
if (!audio_initialized) { if (!audio_initialized) {
// Set port PC6 (OC3A and /OC4A) as output // Set audio ports as output
#ifdef CPIN_AUDIO
#ifdef C6_AUDIO CPIN_SET_DIRECTION
DDRC |= _BV(PORTC6); #endif
//#else #ifdef BPIN_AUDIO
// DDRC |= _BV(PORTC6); // Why is PC6 being set as output low, if C6_audio isn't defined? BPIN_SET_DIRECTION
// PORTC &= ~_BV(PORTC6);
#endif #endif
#ifdef B5_AUDIO #ifdef CPIN_AUDIO
DDRB |= _BV(PORTB5);
//#else
// DDRB |= _BV(PORTB5); // Same as with PC6
// PORTB &= ~_BV(PORTB5);
#endif
#ifdef C6_AUDIO
DISABLE_AUDIO_COUNTER_3_ISR; DISABLE_AUDIO_COUNTER_3_ISR;
#endif #endif
#ifdef BPIN_AUDIO
#ifdef B5_AUDIO
DISABLE_AUDIO_COUNTER_1_ISR; DISABLE_AUDIO_COUNTER_1_ISR;
#endif #endif
// TCCR3A / TCCR3B: Timer/Counter #3 Control Registers // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B
// Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6 // Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation
// Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14 (Period = ICR3, Duty Cycle = OCR3A) // OC3A -- PC6
// OC3B -- PC5
// OC3C -- PC4
// OC1A -- PB5
// OC1B -- PB6
// OC1C -- PB7
// Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)
// OCR3A - PC6
// OCR3B - PC5
// OCR3C - PC4
// OCR1A - PB5
// OCR1B - PB6
// OCR1C - PB7
// Clock Select (CS3n) = 0b010 = Clock / 8 // Clock Select (CS3n) = 0b010 = Clock / 8
#ifdef CPIN_AUDIO
#ifdef C6_AUDIO INIT_AUDIO_COUNTER_3
TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30);
TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30); TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30);
TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER));
TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre);
#endif #endif
#ifdef BPIN_AUDIO
#ifdef B5_AUDIO INIT_AUDIO_COUNTER_1
TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << WGM11) | (0 << WGM10);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10); TCCR1B = (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10);
TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER)); TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER));
TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre); TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre);
#endif #endif
@ -207,13 +248,12 @@ void stop_all_notes()
} }
voices = 0; voices = 0;
#ifdef CPIN_AUDIO
#ifdef C6_AUDIO
DISABLE_AUDIO_COUNTER_3_ISR; DISABLE_AUDIO_COUNTER_3_ISR;
DISABLE_AUDIO_COUNTER_3_OUTPUT; DISABLE_AUDIO_COUNTER_3_OUTPUT;
#endif #endif
#ifdef B5_AUDIO #ifdef BPIN_AUDIO
DISABLE_AUDIO_COUNTER_1_ISR; DISABLE_AUDIO_COUNTER_1_ISR;
DISABLE_AUDIO_COUNTER_1_OUTPUT; DISABLE_AUDIO_COUNTER_1_OUTPUT;
#endif #endif
@ -259,11 +299,11 @@ void stop_note(float freq)
voice_place = 0; voice_place = 0;
} }
if (voices == 0) { if (voices == 0) {
#ifdef C6_AUDIO #ifdef CPIN_AUDIO
DISABLE_AUDIO_COUNTER_3_ISR; DISABLE_AUDIO_COUNTER_3_ISR;
DISABLE_AUDIO_COUNTER_3_OUTPUT; DISABLE_AUDIO_COUNTER_3_OUTPUT;
#endif #endif
#ifdef B5_AUDIO #ifdef BPIN_AUDIO
DISABLE_AUDIO_COUNTER_1_ISR; DISABLE_AUDIO_COUNTER_1_ISR;
DISABLE_AUDIO_COUNTER_1_OUTPUT; DISABLE_AUDIO_COUNTER_1_OUTPUT;
#endif #endif
@ -295,15 +335,15 @@ float vibrato(float average_freq) {
#endif #endif
#ifdef C6_AUDIO #ifdef CPIN_AUDIO
ISR(TIMER3_COMPA_vect) ISR(TIMER3_AUDIO_vect)
{ {
float freq; float freq;
if (playing_note) { if (playing_note) {
if (voices > 0) { if (voices > 0) {
#ifdef B5_AUDIO #ifdef BPIN_AUDIO
float freq_alt = 0; float freq_alt = 0;
if (voices > 1) { if (voices > 1) {
if (polyphony_rate == 0) { if (polyphony_rate == 0) {
@ -477,10 +517,10 @@ ISR(TIMER3_COMPA_vect)
} }
#endif #endif
#ifdef B5_AUDIO #ifdef BPIN_AUDIO
ISR(TIMER1_COMPA_vect) ISR(TIMER1_AUDIO_vect)
{ {
#if defined(B5_AUDIO) && !defined(C6_AUDIO) #if defined(BPIN_AUDIO) && !defined(CPIN_AUDIO)
float freq = 0; float freq = 0;
if (playing_note) { if (playing_note) {
@ -627,10 +667,10 @@ void play_note(float freq, int vol) {
} }
if (audio_config.enable && voices < 8) { if (audio_config.enable && voices < 8) {
#ifdef C6_AUDIO #ifdef CPIN_AUDIO
DISABLE_AUDIO_COUNTER_3_ISR; DISABLE_AUDIO_COUNTER_3_ISR;
#endif #endif
#ifdef B5_AUDIO #ifdef BPIN_AUDIO
DISABLE_AUDIO_COUNTER_1_ISR; DISABLE_AUDIO_COUNTER_1_ISR;
#endif #endif
@ -648,12 +688,12 @@ void play_note(float freq, int vol) {
voices++; voices++;
} }
#ifdef C6_AUDIO #ifdef CPIN_AUDIO
ENABLE_AUDIO_COUNTER_3_ISR; ENABLE_AUDIO_COUNTER_3_ISR;
ENABLE_AUDIO_COUNTER_3_OUTPUT; ENABLE_AUDIO_COUNTER_3_OUTPUT;
#endif #endif
#ifdef B5_AUDIO #ifdef BPIN_AUDIO
#ifdef C6_AUDIO #ifdef CPIN_AUDIO
if (voices > 1) { if (voices > 1) {
ENABLE_AUDIO_COUNTER_1_ISR; ENABLE_AUDIO_COUNTER_1_ISR;
ENABLE_AUDIO_COUNTER_1_OUTPUT; ENABLE_AUDIO_COUNTER_1_OUTPUT;
@ -676,10 +716,10 @@ void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat)
if (audio_config.enable) { if (audio_config.enable) {
#ifdef C6_AUDIO #ifdef CPIN_AUDIO
DISABLE_AUDIO_COUNTER_3_ISR; DISABLE_AUDIO_COUNTER_3_ISR;
#endif #endif
#ifdef B5_AUDIO #ifdef BPIN_AUDIO
DISABLE_AUDIO_COUNTER_1_ISR; DISABLE_AUDIO_COUNTER_1_ISR;
#endif #endif
@ -701,12 +741,12 @@ void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat)
note_position = 0; note_position = 0;
#ifdef C6_AUDIO #ifdef CPIN_AUDIO
ENABLE_AUDIO_COUNTER_3_ISR; ENABLE_AUDIO_COUNTER_3_ISR;
ENABLE_AUDIO_COUNTER_3_OUTPUT; ENABLE_AUDIO_COUNTER_3_OUTPUT;
#endif #endif
#ifdef B5_AUDIO #ifdef BPIN_AUDIO
#ifndef C6_AUDIO #ifndef CPIN_AUDIO
ENABLE_AUDIO_COUNTER_1_ISR; ENABLE_AUDIO_COUNTER_1_ISR;
ENABLE_AUDIO_COUNTER_1_OUTPUT; ENABLE_AUDIO_COUNTER_1_OUTPUT;
#endif #endif