Skip to content

Commit 02f859f

Browse files
committed
ESP32 correct PWM output
1 parent f713301 commit 02f859f

File tree

6 files changed

+48
-57
lines changed

6 files changed

+48
-57
lines changed

src/AudioPWM/PWMAudioBase.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ class DriverPWMBase {
161161
virtual size_t write(const uint8_t *wrt_buffer, size_t size){
162162
if (is_blocking_write && availableForWrite()==0){
163163
LOGD("Waiting for buffer to clear");
164-
while (availableForWrite()==0) delay(1);
164+
while (availableForWrite()==0) delay(5);
165165
}
166166

167167
size_t available = min((size_t)availableForWrite(),size);

src/AudioPWM/PWMAudioESP32.h

Lines changed: 24 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ class PWMDriverESP32;
1313
* @brief Please use DriverPWMBase!
1414
*/
1515
using PWMDriver = PWMDriverESP32;
16-
static PWMDriverESP32 *accessAudioPWM = nullptr;
17-
void IRAM_ATTR defaultPWMAudioOutputCallback();
1816

1917

2018
/**
@@ -38,50 +36,45 @@ typedef PinInfoESP32 PinInfo;
3836

3937
class PWMDriverESP32 : public DriverPWMBase {
4038
public:
41-
friend void defaultPWMAudioOutputCallback();
39+
//friend void pwm_callback(void*ptr);
4240

4341
PWMDriverESP32(){
4442
TRACED();
45-
accessAudioPWM = this;
4643
}
4744

4845
// Ends the output
4946
virtual void end(){
5047
TRACED();
51-
timerAlarmDisable(timer);
48+
timer.end();
49+
is_timer_started = false;
5250
for (int j=0;j<audio_config.channels;j++){
5351
ledcDetachPin(pins[j].gpio);
5452
}
55-
is_timer_started = false;
5653
}
5754

5855
/// when we get the first write -> we activate the timer to start with the output of data
5956
virtual void startTimer(){
60-
if (!is_timer_started){
57+
if (!timer){
58+
TRACEI();
6159
audio_config = audioInfo();
62-
LOGI("timerAlarmEnable");
60+
timer.begin(pwm_callback, audio_config.sample_rate, HZ);
6361
is_timer_started = true;
64-
timerAlarmEnable(timer);
6562
}
6663
}
6764

6865
/// Setup LED PWM
6966
virtual void setupPWM(){
70-
TRACED();
7167
// frequency is driven by selected resolution
72-
uint32_t freq = frequency(audio_config.resolution)*1000;
73-
audio_config.pwm_frequency = freq;
68+
audio_config.pwm_frequency = frequency(audio_config.resolution) * 1000;
7469

7570
pins.resize(audio_config.channels);
7671
for (int j=0;j<audio_config.channels;j++){
77-
LOGD("Processing channel %d", j);
7872
int pwmChannel = j;
7973
pins[j].pwm_channel = pwmChannel;
8074
pins[j].gpio = audio_config.pins()[j];
81-
LOGI("-> ledcSetup: frequency=%d / resolution=%d", freq, audio_config.resolution);
82-
ledcSetup(pwmChannel, freq, audio_config.resolution);
83-
LOGD("-> ledcAttachPin: %d", pins[j].gpio);
84-
ledcAttachPin(pins[j].gpio, pwmChannel);
75+
ledcSetup(pins[j].pwm_channel, audio_config.pwm_frequency, audio_config.resolution);
76+
ledcAttachPin(pins[j].gpio, pins[j].pwm_channel);
77+
LOGI("setupPWM: pin=%d, channel=%d, frequency=%d, resolution=%d", pins[j].gpio, pins[j].pwm_channel, audio_config.pwm_frequency, audio_config.resolution);
8578
}
8679
logPins();
8780
}
@@ -94,31 +87,18 @@ class PWMDriverESP32 : public DriverPWMBase {
9487

9588
/// Setup ESP32 timer with callback
9689
virtual void setupTimer() {
97-
TRACED();
98-
99-
// Attach timer int at sample rate
100-
int prescale = 2;
101-
bool rising_edge = true;
102-
timer = timerBegin(audio_config.timer_id, prescale, rising_edge); // Timer at full 40Mhz, prescaling 2
103-
uint32_t counter = 20000000 / audio_config.sample_rate;
104-
LOGI("-> timer counter is %zu", counter);
105-
LOGD("-> timerAttachInterrupt");
106-
bool interrupt_edge_type = true;
107-
timerAttachInterrupt(timer, defaultPWMAudioOutputCallback, interrupt_edge_type);
108-
LOGD("-> timerAlarmWrite");
109-
bool auto_reload = true;
110-
timerAlarmWrite(timer, counter, auto_reload); // Timer fires at ~44100Hz [40Mhz / 907]
90+
timer.setCallbackParameter(this);
91+
timer.setIsSave(false);
11192
}
11293

11394
/// write a pwm value to the indicated channel. The max value depends on the resolution
11495
virtual void pwmWrite(int channel, int value){
115-
ledcWrite(pins[channel].pwm_channel, value);
96+
ledcWrite(pins[channel].pwm_channel, value);
11697
}
11798

11899
protected:
119100
Vector<PinInfo> pins;
120-
hw_timer_t * timer = nullptr;
121-
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
101+
TimerAlarmRepeating timer;
122102

123103
/// provides the max value for the indicated resulution
124104
int maxUnsignedValue(int resolution){
@@ -143,17 +123,18 @@ class PWMDriverESP32 : public DriverPWMBase {
143123
case 11: return 39.0625;
144124
}
145125
return 312.5;
146-
}
126+
}
127+
128+
/// timer callback: write the next frame to the pins
129+
static void pwm_callback(void*ptr){
130+
PWMDriverESP32 *accessAudioPWM = (PWMDriverESP32*)ptr;
131+
if (accessAudioPWM!=nullptr){
132+
accessAudioPWM->playNextFrame();
133+
}
134+
}
135+
147136
};
148137

149-
/// timer callback: write the next frame to the pins
150-
void IRAM_ATTR defaultPWMAudioOutputCallback() {
151-
if (accessAudioPWM!=nullptr){
152-
portENTER_CRITICAL_ISR(&(accessAudioPWM->timerMux));
153-
accessAudioPWM->playNextFrame();
154-
portEXIT_CRITICAL_ISR(&(accessAudioPWM->timerMux));
155-
}
156-
}
157138

158139
}
159140

src/AudioPWM/PWMAudioRenesas.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,6 @@ class PWMDriverRenesas : public DriverPWMBase {
105105
/// write a pwm value to the indicated channel. The max value depends on the resolution
106106
virtual void pwmWrite(int channel, int value){
107107
pins[channel]->pulse_perc(value);
108-
//char buffer[80];
109-
//sprintf(buffer,"channel %d - value: %d", channel, value);
110-
//LOGD("%s", buffer);
111108
}
112109

113110
/// timer callback: write the next frame to the pins

src/AudioTimer/AudioTimer.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@ class TimerAlarmRepeating {
3737
virtual ~TimerAlarmRepeating() = default;
3838

3939
bool begin(repeating_timer_callback_t callback_f, uint32_t time, TimeUnit unit = MS) {
40-
return p_driver->begin(callback_f, time, unit);
40+
is_active = p_driver->begin(callback_f, time, unit);
41+
return is_active;
4142
}
4243
bool end() {
44+
is_active = false;
4345
return p_driver->end();
4446
};
4547

@@ -59,8 +61,16 @@ class TimerAlarmRepeating {
5961
p_driver->setTimerFunction(function);
6062
}
6163

64+
void setIsSave(bool is_save){
65+
p_driver->setIsSave(is_save);
66+
}
67+
68+
/// @brief Returns true if the timer is active
69+
operator bool() { return is_active; }
70+
6271
protected:
6372
void* object=nullptr;
73+
bool is_active = false;;
6474
TimerAlarmRepeatingDriver driver; // platform specific driver
6575
TimerAlarmRepeatingDriverBase *p_driver=&driver;
6676

src/AudioTimer/AudioTimerBase.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class TimerAlarmRepeatingDriverBase {
3535

3636
virtual void setTimer(int timer){}
3737
virtual void setTimerFunction(TimerFunction function=DirectTimerCallback){}
38+
/// @brief Not used
39+
virtual void setIsSave(bool is_save){}
3840

3941
protected:
4042
void* object=nullptr;

src/AudioTimer/AudioTimerESP32.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66

77
namespace audio_tools {
88

9-
typedef void (* simple_callback )(void);
10-
11-
129
/**
1310
* @brief Internal class to manage User callbacks. An optinal parameter can be passed to the callback method
11+
* We support 4 timers
1412
* @author Phil Schatzmann
1513
* @copyright GPLv3
1614
*/
@@ -138,7 +136,7 @@ class TimerAlarmRepeatingDriverESP32 : public TimerAlarmRepeatingDriverBase {
138136
}
139137
}
140138

141-
virtual void setTimerFunction(TimerFunction function=DirectTimerCallback) override{
139+
void setTimerFunction(TimerFunction function=DirectTimerCallback) override{
142140
this->function = function;
143141
}
144142

@@ -162,7 +160,6 @@ class TimerAlarmRepeatingDriverESP32 : public TimerAlarmRepeatingDriverBase {
162160
LOGI("Timer every: %u us", timeUs);
163161
adc_timer = timerBegin(timer_id, 80, true); // divider=80 -> 1000000 calls per second
164162

165-
166163
switch (function) {
167164
case DirectTimerCallback:
168165
setupDirectTimerCallback(callback_f);
@@ -175,7 +172,6 @@ class TimerAlarmRepeatingDriverESP32 : public TimerAlarmRepeatingDriverBase {
175172
case SimpleThreadLoop:
176173
setupSimpleThreadLoop(callback_f);
177174
break;
178-
179175
}
180176

181177
started = true;
@@ -201,13 +197,19 @@ class TimerAlarmRepeatingDriverESP32 : public TimerAlarmRepeatingDriverBase {
201197
void setCore(int core){
202198
this->core = core;
203199
}
200+
201+
/// Selects the TimerFunction: default is DirectTimerCallback, when set to save we use TimerCallbackInThread
202+
void setIsSave(bool is_save) override{
203+
setTimerFunction(is_save?TimerCallbackInThread : DirectTimerCallback);
204+
}
205+
204206

205207
protected:
206208
int timer_id=0;
207209
volatile bool started = false;
208210
TaskHandle_t handler_task = nullptr;
209211
hw_timer_t* adc_timer = nullptr; // our timer
210-
UserCallback user_callback;
212+
UserCallback user_callback; // for task
211213
TimerFunction function;
212214
int core = 1;
213215
int priority = configMAX_PRIORITIES -1;
@@ -227,12 +229,11 @@ class TimerAlarmRepeatingDriverESP32 : public TimerAlarmRepeatingDriverBase {
227229
else if (timer_id==3) timerAttachInterrupt(adc_timer, userCallback3, false);
228230

229231
timerAlarmWrite(adc_timer, timeUs, true);
230-
//timerSetAutoReload(adc_timer, true);
231232
timerAlarmEnable(adc_timer);
232233

233234
}
234235

235-
/// timer callback is notifiying task
236+
/// timer callback is notifiying task: supports functionality which can not be called in an interrupt e.g. i2c
236237
void setupTimerCallbackInThread(repeating_timer_callback_t callback_f){
237238
TRACED();
238239
// we start the timer which runs the callback in a seprate task

0 commit comments

Comments
 (0)