logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

audio_pwm_hardware.c (11170B)


  1. /* Copyright 2016 Jack Humbert
  2. * Copyright 2020 JohSchneider
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include "audio.h"
  18. #include "gpio.h"
  19. #include <avr/interrupt.h>
  20. extern bool playing_note;
  21. extern bool playing_melody;
  22. extern uint8_t note_timbre;
  23. #define CPU_PRESCALER 8
  24. /*
  25. Audio Driver: PWM
  26. drive up to two speakers through the AVR PWM hardware-peripheral, using timer1 and/or timer3 on Atmega32U4.
  27. the primary channel_1 can be connected to either pin PC4 PC5 or PC6 (the later being used by most AVR based keyboards) with a PMW signal generated by timer3
  28. and an optional secondary channel_2 on either pin PB5, PB6 or PB7, with a PWM signal from timer1
  29. alternatively, the PWM pins on PORTB can be used as only/primary speaker
  30. */
  31. #if defined(AUDIO_PIN) && (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6) && (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7) && (AUDIO_PIN != D5)
  32. # error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under the AVR settings for available options."
  33. #endif
  34. #if (AUDIO_PIN == C4) || (AUDIO_PIN == C5) || (AUDIO_PIN == C6)
  35. # define AUDIO1_PIN_SET
  36. # define AUDIO1_TIMSKx TIMSK3
  37. # define AUDIO1_TCCRxA TCCR3A
  38. # define AUDIO1_TCCRxB TCCR3B
  39. # define AUDIO1_ICRx ICR3
  40. # define AUDIO1_WGMx0 WGM30
  41. # define AUDIO1_WGMx1 WGM31
  42. # define AUDIO1_WGMx2 WGM32
  43. # define AUDIO1_WGMx3 WGM33
  44. # define AUDIO1_CSx0 CS30
  45. # define AUDIO1_CSx1 CS31
  46. # define AUDIO1_CSx2 CS32
  47. # if (AUDIO_PIN == C6)
  48. # define AUDIO1_COMxy0 COM3A0
  49. # define AUDIO1_COMxy1 COM3A1
  50. # define AUDIO1_OCIExy OCIE3A
  51. # define AUDIO1_OCRxy OCR3A
  52. # define AUDIO1_PIN C6
  53. # define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPA_vect
  54. # elif (AUDIO_PIN == C5)
  55. # define AUDIO1_COMxy0 COM3B0
  56. # define AUDIO1_COMxy1 COM3B1
  57. # define AUDIO1_OCIExy OCIE3B
  58. # define AUDIO1_OCRxy OCR3B
  59. # define AUDIO1_PIN C5
  60. # define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPB_vect
  61. # elif (AUDIO_PIN == C4)
  62. # define AUDIO1_COMxy0 COM3C0
  63. # define AUDIO1_COMxy1 COM3C1
  64. # define AUDIO1_OCIExy OCIE3C
  65. # define AUDIO1_OCRxy OCR3C
  66. # define AUDIO1_PIN C4
  67. # define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPC_vect
  68. # endif
  69. #endif
  70. #if defined(AUDIO_PIN) && defined(AUDIO_PIN_ALT) && (AUDIO_PIN == AUDIO_PIN_ALT)
  71. # error "Audio feature: AUDIO_PIN and AUDIO_PIN_ALT on the same pin makes no sense."
  72. #endif
  73. #if ((AUDIO_PIN == B5) && ((AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B6) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B7) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6)))
  74. # error "Audio feature: PORTB as AUDIO_PIN and AUDIO_PIN_ALT at the same time is not supported."
  75. #endif
  76. #if defined(AUDIO_PIN_ALT) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7)
  77. # error "Audio feature: the pin selected as AUDIO_PIN_ALT is not supported."
  78. #endif
  79. #if (AUDIO_PIN == B5) || (AUDIO_PIN == B6) || (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7) || (AUDIO_PIN == D5)
  80. # define AUDIO2_PIN_SET
  81. # define AUDIO2_TIMSKx TIMSK1
  82. # define AUDIO2_TCCRxA TCCR1A
  83. # define AUDIO2_TCCRxB TCCR1B
  84. # define AUDIO2_ICRx ICR1
  85. # define AUDIO2_WGMx0 WGM10
  86. # define AUDIO2_WGMx1 WGM11
  87. # define AUDIO2_WGMx2 WGM12
  88. # define AUDIO2_WGMx3 WGM13
  89. # define AUDIO2_CSx0 CS10
  90. # define AUDIO2_CSx1 CS11
  91. # define AUDIO2_CSx2 CS12
  92. # if (AUDIO_PIN == B5) || (AUDIO_PIN_ALT == B5)
  93. # define AUDIO2_COMxy0 COM1A0
  94. # define AUDIO2_COMxy1 COM1A1
  95. # define AUDIO2_OCIExy OCIE1A
  96. # define AUDIO2_OCRxy OCR1A
  97. # define AUDIO2_PIN B5
  98. # define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
  99. # elif (AUDIO_PIN == B6) || (AUDIO_PIN_ALT == B6)
  100. # define AUDIO2_COMxy0 COM1B0
  101. # define AUDIO2_COMxy1 COM1B1
  102. # define AUDIO2_OCIExy OCIE1B
  103. # define AUDIO2_OCRxy OCR1B
  104. # define AUDIO2_PIN B6
  105. # define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPB_vect
  106. # elif (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B7)
  107. # define AUDIO2_COMxy0 COM1C0
  108. # define AUDIO2_COMxy1 COM1C1
  109. # define AUDIO2_OCIExy OCIE1C
  110. # define AUDIO2_OCRxy OCR1C
  111. # define AUDIO2_PIN B7
  112. # define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPC_vect
  113. # elif (AUDIO_PIN == D5) && defined(__AVR_ATmega32A__)
  114. # pragma message "Audio support for ATmega32A is experimental and can cause crashes."
  115. # undef AUDIO2_TIMSKx
  116. # define AUDIO2_TIMSKx TIMSK
  117. # define AUDIO2_COMxy0 COM1A0
  118. # define AUDIO2_COMxy1 COM1A1
  119. # define AUDIO2_OCIExy OCIE1A
  120. # define AUDIO2_OCRxy OCR1A
  121. # define AUDIO2_PIN D5
  122. # define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
  123. # endif
  124. #endif
  125. // C6 seems to be the assumed default by many existing keyboard - but sill warn the user
  126. #if !defined(AUDIO1_PIN_SET) && !defined(AUDIO2_PIN_SET)
  127. # pragma message "Audio feature enabled, but no suitable pin selected - see docs/feature_audio under the AVR settings for available options. Don't expect to hear anything... :-)"
  128. // TODO: make this an error - go through the breaking-change-process and change all keyboards to the new define
  129. #endif
  130. // -----------------------------------------------------------------------------
  131. #ifdef AUDIO1_PIN_SET
  132. static float channel_1_frequency = 0.0f;
  133. void channel_1_set_frequency(float freq) {
  134. if (freq == 0.0f) // a pause/rest is a valid "note" with freq=0
  135. {
  136. // disable the output, but keep the pwm-ISR going (with the previous
  137. // frequency) so the audio-state keeps getting updated
  138. // Note: setting the duty-cycle 0 is not possible on non-inverting PWM mode - see the AVR data-sheet
  139. AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
  140. return;
  141. } else {
  142. AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1); // enable output, PWM mode
  143. }
  144. channel_1_frequency = freq;
  145. // set pwm period
  146. AUDIO1_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
  147. // and duty cycle
  148. AUDIO1_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
  149. }
  150. void channel_1_start(void) {
  151. // enable timer-counter ISR
  152. AUDIO1_TIMSKx |= _BV(AUDIO1_OCIExy);
  153. // enable timer-counter output
  154. AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1);
  155. }
  156. void channel_1_stop(void) {
  157. // disable timer-counter ISR
  158. AUDIO1_TIMSKx &= ~_BV(AUDIO1_OCIExy);
  159. // disable timer-counter output
  160. AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
  161. }
  162. #endif
  163. #ifdef AUDIO2_PIN_SET
  164. static float channel_2_frequency = 0.0f;
  165. void channel_2_set_frequency(float freq) {
  166. if (freq == 0.0f) {
  167. AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
  168. return;
  169. } else {
  170. AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
  171. }
  172. channel_2_frequency = freq;
  173. AUDIO2_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
  174. AUDIO2_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
  175. }
  176. float channel_2_get_frequency(void) {
  177. return channel_2_frequency;
  178. }
  179. void channel_2_start(void) {
  180. AUDIO2_TIMSKx |= _BV(AUDIO2_OCIExy);
  181. AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
  182. }
  183. void channel_2_stop(void) {
  184. AUDIO2_TIMSKx &= ~_BV(AUDIO2_OCIExy);
  185. AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
  186. }
  187. #endif
  188. void audio_driver_initialize_impl(void) {
  189. #ifdef AUDIO1_PIN_SET
  190. channel_1_stop();
  191. gpio_set_pin_output(AUDIO1_PIN);
  192. #endif
  193. #ifdef AUDIO2_PIN_SET
  194. channel_2_stop();
  195. gpio_set_pin_output(AUDIO2_PIN);
  196. #endif
  197. // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B
  198. // Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation
  199. // OC3A -- PC6
  200. // OC3B -- PC5
  201. // OC3C -- PC4
  202. // OC1A -- PB5
  203. // OC1B -- PB6
  204. // OC1C -- PB7
  205. // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)
  206. // OCR3A - PC6
  207. // OCR3B - PC5
  208. // OCR3C - PC4
  209. // OCR1A - PB5
  210. // OCR1B - PB6
  211. // OCR1C - PB7
  212. // Clock Select (CS3n) = 0b010 = Clock / 8
  213. #ifdef AUDIO1_PIN_SET
  214. // initialize timer-counter
  215. AUDIO1_TCCRxA = (0 << AUDIO1_COMxy1) | (0 << AUDIO1_COMxy0) | (1 << AUDIO1_WGMx1) | (0 << AUDIO1_WGMx0);
  216. AUDIO1_TCCRxB = (1 << AUDIO1_WGMx3) | (1 << AUDIO1_WGMx2) | (0 << AUDIO1_CSx2) | (1 << AUDIO1_CSx1) | (0 << AUDIO1_CSx0);
  217. #endif
  218. #ifdef AUDIO2_PIN_SET
  219. AUDIO2_TCCRxA = (0 << AUDIO2_COMxy1) | (0 << AUDIO2_COMxy0) | (1 << AUDIO2_WGMx1) | (0 << AUDIO2_WGMx0);
  220. AUDIO2_TCCRxB = (1 << AUDIO2_WGMx3) | (1 << AUDIO2_WGMx2) | (0 << AUDIO2_CSx2) | (1 << AUDIO2_CSx1) | (0 << AUDIO2_CSx0);
  221. #endif
  222. }
  223. void audio_driver_stop_impl(void) {
  224. #ifdef AUDIO1_PIN_SET
  225. channel_1_stop();
  226. #endif
  227. #ifdef AUDIO2_PIN_SET
  228. channel_2_stop();
  229. #endif
  230. }
  231. void audio_driver_start_impl(void) {
  232. #ifdef AUDIO1_PIN_SET
  233. channel_1_start();
  234. if (playing_note) {
  235. channel_1_set_frequency(audio_get_processed_frequency(0));
  236. }
  237. #endif
  238. #if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
  239. channel_2_start();
  240. if (playing_note) {
  241. channel_2_set_frequency(audio_get_processed_frequency(0));
  242. }
  243. #endif
  244. }
  245. static volatile uint32_t isr_counter = 0;
  246. #ifdef AUDIO1_PIN_SET
  247. ISR(AUDIO1_TIMERx_COMPy_vect) {
  248. isr_counter++;
  249. if (isr_counter < channel_1_frequency / (CPU_PRESCALER * 8)) return;
  250. isr_counter = 0;
  251. bool state_changed = audio_update_state();
  252. if (!playing_note && !playing_melody) {
  253. channel_1_stop();
  254. # ifdef AUDIO2_PIN_SET
  255. channel_2_stop();
  256. # endif
  257. return;
  258. }
  259. if (state_changed) {
  260. channel_1_set_frequency(audio_get_processed_frequency(0));
  261. # ifdef AUDIO2_PIN_SET
  262. if (audio_get_number_of_active_tones() > 1) {
  263. channel_2_set_frequency(audio_get_processed_frequency(1));
  264. } else {
  265. channel_2_stop();
  266. }
  267. # endif
  268. }
  269. }
  270. #endif
  271. #if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
  272. ISR(AUDIO2_TIMERx_COMPy_vect) {
  273. isr_counter++;
  274. if (isr_counter < channel_2_frequency / (CPU_PRESCALER * 8)) return;
  275. isr_counter = 0;
  276. bool state_changed = audio_update_state();
  277. if (!playing_note && !playing_melody) {
  278. channel_2_stop();
  279. return;
  280. }
  281. if (state_changed) {
  282. channel_2_set_frequency(audio_get_processed_frequency(0));
  283. }
  284. }
  285. #endif