Mozzi  version v2.0
sound synthesis library for Arduino
MozziGuts_impl_STM32duino.hpp
1 /*
2  * MozziGuts_impl_STM32duino.hpp
3  *
4  * This file is part of Mozzi.
5  *
6  * Copyright 2023-2024 Thomas Friedrichsmeier and the Mozzi Team
7  *
8  * Mozzi is licensed under the GNU Lesser General Public Licence (LGPL) Version 2.1 or later.
9  *
10 */
11 
12 #include "HardwareTimer.h"
13 
14 namespace MozziPrivate {
15 ////// BEGIN analog input code ////////
16 
17 #if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD)
18 // Notes on ADC implementation: So in hours I could not get IRQ-driven ADC to work, much less in a way that should work across the whole STM32 family.
19 // Instead, this code resorts to polling, but contrary to the regular implementation, it sets does so non-blocking. Polling is done from inside audioHook().
20 // Not terribly efficient, but seems to work ok.
21 //
22 // All this still involves coping much, much more low level detail, than I would like. Most of that is moved to MozziGuts_impl_STM32duino_analog.hpp .
23 // If this core ever gets a more advanced ADC API, we should definitely switch to using that.
24 
25 #define getADCReading() HAL_ADC_GetValue(&AdcHandle)
26 
27 // NOTE: a single uint8_t for ADC channel is no good, here, as we may be dealing with serval distinct ADCs servicing the pins.
28 // However, there is a real danger of overflowing an int8_t storage (as is used as an intermediate), so subtract min pin number.
29 #define channelNumToIndex(channel) channel
31  return pin - PNUM_ANALOG_BASE;
32 }
33 
36 bool conversion_running = false;
38 
39 } // namespace MozziPrivate
40 #include "MozziGuts_impl_STM32duino_analog.hpp"
41 namespace MozziPrivate {
42 
44  if (pin != previously_sampled_pin) {
45  if (conversion_running) {
48  }
50  uint32_t mask = 1 << pin;
51  if (!(adc_pins_initialized & mask)) {
52  analogRead(pin+PNUM_ANALOG_BASE); // I have no idea, what black magic analogRead() performs, but it seems to be needed - once - on STM32F411
54  }
55  }
56  adc_setup_read(analogInputToPinName(pin+PNUM_ANALOG_BASE), 16); // resolution will be limited to max available, anyway, so let's request 16 bits
57  conversion_running = true;
58 }
59 
62  conversion_running = true;
63 }
64 
65 void setupMozziADC(int8_t /*speed*/) {
66 }
67 
68 #define AUDIO_HOOK_HOOK checkADCConversionComplete();
69 
71  if (!conversion_running) return;
73  conversion_running = false;
75  }
76 }
77 
78 #endif
79 
80 void setupFastAnalogRead(int8_t /*speed*/) {
81 }
82 
83 ////// END analog input code ////////
84 ////// END analog input code ////////
85 
86 
87 
88 //// BEGIN AUDIO OUTPUT code ///////
89 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED)
91 #elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM)
96 
97 # if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM)
100 # elif (MOZZI_AUDIO_CHANNELS > 1)
103 # endif
104 
105 inline void audioOutput(const AudioOutput f) {
106 # if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM)
109 # else
111 # if (MOZZI_AUDIO_CHANNELS > 1)
113 # endif
114 #endif
115 }
116 #endif
117 
118 static void startAudio() {
119 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED)
120  audio_update_timer.pause();
121  //audio_update_timer.setPeriod(1000000UL / MOZZI_AUDIO_RATE);
122  // Manually calculate prescaler and overflow instead of using setPeriod, to avoid rounding errors
123  uint32_t period_cyc = F_CPU / MOZZI_AUDIO_RATE;
124  uint16_t prescaler = (uint16_t)(period_cyc / 65535 + 1);
125  uint16_t overflow = (uint16_t)((period_cyc + (prescaler / 2)) / prescaler);
126  audio_update_timer.setPrescaleFactor(prescaler);
127  audio_update_timer.setOverflow(overflow);
128  audio_update_timer.setMode(/* channel */ 1, TIMER_OUTPUT_COMPARE);
129  audio_update_timer.setCaptureCompare(/* channel */ 1, 1); // Interrupt 1 count after each update
130  audio_update_timer.attachInterrupt(/* channel */ 1, defaultAudioOutput);
131  audio_update_timer.refresh();
132 #endif
133 
134 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM)
135  // Configure PWM output
136  pinMode(MOZZI_AUDIO_PIN_1, OUTPUT);
137 # if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM)
138  pinMode(MOZZI_AUDIO_PIN_1_LOW, OUTPUT);
139 # elif (MOZZI_AUDIO_CHANNELS > 1)
140  pinMode(MOZZI_AUDIO_PIN_2, OUTPUT);
141 # endif
142 
143 # define MAX_CARRIER_FREQ (F_CPU / (1 << MOZZI_AUDIO_BITS_PER_CHANNEL))
144  // static_assert(MAX_CARRIER_FREQ >= MOZZI_AUDIO_RATE); // Unfortunately, we cannot test this at compile time. F_CPU expands to a runtime variable
145  TIM_TypeDef *pwm_timer_tim = (TIM_TypeDef *) pinmap_peripheral(output_pin_1, PinMap_TIM);
146  pwm_timer_ht = new HardwareTimer(pwm_timer_tim);
147  pwm_timer_ht->setMode(pwm_timer_channel_1, TIMER_OUTPUT_COMPARE_PWM1, output_pin_1);
148 # if MAX_CARRIER_FREQ < (MOZZI_AUDIO_RATE * 5)
149  // Generate as fast a carrier as possible
150  pwm_timer_ht->setPrescaleFactor(1);
151 # else
152  // No point in generating arbitrarily high carrier frequencies. In fact, if
153  // there _is_ any headroom, give the PWM pin more time to swing from HIGH to
154  // LOW and BACK, cleanly
155  pwm_timer_ht->setPrescaleFactor((int)MAX_CARRIER_FREQ / (MOZZI_AUDIO_RATE * 5)); // as fast as possible
156 # endif
157 // Allocate enough room to write all intended bits
158  pwm_timer_ht->setOverflow(1 << MOZZI_AUDIO_BITS_PER_CHANNEL);
159  pwm_timer_ht->setCaptureCompare(pwm_timer_channel_1, MOZZI_AUDIO_BIAS /*, resolution */);
160 
161  pwm_timer_ht->resume();
162 #endif
163 
164 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED)
165  audio_update_timer.resume();
166 #endif
167 }
168 
169 void stopMozzi() {
170 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED)
171  audio_update_timer.pause();
172 #endif
173 }
174 
175 //// END AUDIO OUTPUT code ///////
176 
177 //// BEGIN Random seeding ////////
178 void MozziRandPrivate::autoSeed() {
179 #warning Automatic random seeding is not implemented on this platform
180 }
181 //// END Random seeding ////////
182 
183 } // namespace MozziPrivate