driver_avr_pwm_hardware.c 10 KB

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