-
-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
476 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"files.associations": { | ||
"functional": "cpp" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
#include "PWMTimerOutput.h" | ||
#include "freertos/semphr.h" | ||
#include "driver/sigmadelta.h" | ||
#include <string.h> | ||
#include <Arduino.h> | ||
|
||
|
||
IRAM_ATTR void onTimerCallbackPWM(void *param) | ||
{ | ||
PWMTimerOutput *output = (PWMTimerOutput *)param; | ||
timer_spinlock_take(TIMER_GROUP_0); | ||
timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0); | ||
timer_group_enable_alarm_in_isr(TIMER_GROUP_0, TIMER_0); | ||
timer_spinlock_give(TIMER_GROUP_0); | ||
output->onTimer(); | ||
} | ||
|
||
void PWMTimerOutput::start(uint32_t sample_rate) | ||
{ | ||
mSampleRate = sample_rate; | ||
|
||
ledcSetup(0, 50000, 8); | ||
ledcAttachPin(mPDMPin, 0); | ||
|
||
// create a timer that will fire at the sample rate | ||
timer_config_t timer_config = { | ||
.alarm_en = TIMER_ALARM_EN, | ||
.counter_en = TIMER_PAUSE, | ||
.intr_type = TIMER_INTR_LEVEL, | ||
.counter_dir = TIMER_COUNT_UP, | ||
.auto_reload = TIMER_AUTORELOAD_EN, | ||
.divider = 80}; | ||
esp_err_t result = timer_init(TIMER_GROUP_0, TIMER_0, &timer_config); | ||
if (result != ESP_OK) | ||
{ | ||
Serial.printf("Error initializing timer: %d\n", result); | ||
} | ||
result = timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0x00000000ULL); | ||
if (result != ESP_OK) | ||
{ | ||
Serial.printf("Error setting timer counter value: %d\n", result); | ||
} | ||
result = timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 1000000 / mSampleRate); | ||
if (result != ESP_OK) | ||
{ | ||
Serial.printf("Error setting timer alarm value: %d\n", result); | ||
} | ||
// timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 1000); | ||
result = timer_enable_intr(TIMER_GROUP_0, TIMER_0); | ||
if (result != ESP_OK) | ||
{ | ||
Serial.printf("Error enabling timer interrupt: %d\n", result); | ||
} | ||
result = timer_isr_register(TIMER_GROUP_0, TIMER_0, &onTimerCallbackPWM, this, ESP_INTR_FLAG_IRAM, NULL); | ||
if (result != ESP_OK) | ||
{ | ||
Serial.printf("Error registering timer interrupt: %d\n", result); | ||
// print the string error | ||
const char *err = esp_err_to_name(result); | ||
Serial.printf("Error: %s\n", err); | ||
} | ||
result = timer_start(TIMER_GROUP_0, TIMER_0); | ||
if (result != ESP_OK) | ||
{ | ||
Serial.printf("Error starting timer: %d\n", result); | ||
} | ||
Serial.println("PDM Started"); | ||
} | ||
|
||
void PWMTimerOutput::write(int8_t *samples, int count) | ||
{ | ||
// Serial.printf("Count %d\n", mCount); | ||
while (true) | ||
{ | ||
if(xSemaphoreTake(mBufferSemaphore, portMAX_DELAY)) { | ||
// is the second buffer empty? | ||
if (mSecondBufferLength == 0) | ||
{ | ||
//Serial.println("Filling second buffer"); | ||
// make sure there's enough room for the samples | ||
mSecondBuffer = (int8_t *)realloc(mSecondBuffer, count); | ||
// copy them into the second buffer | ||
memcpy(mSecondBuffer, samples, count); | ||
// second buffer is now full of samples | ||
mSecondBufferLength = count; | ||
// unlock the mutext and return | ||
xSemaphoreGive(mBufferSemaphore); | ||
return; | ||
} | ||
// no room in the second buffer so wait for the first buffer to be emptied | ||
xSemaphoreGive(mBufferSemaphore); | ||
} | ||
vTaskDelay(1 / portTICK_PERIOD_MS); | ||
} | ||
} | ||
|
||
void PWMTimerOutput::onTimer() | ||
{ | ||
// output a sample from the buffer if we have one | ||
if (mCurrentIndex < mBufferLength) | ||
{ | ||
mCount++; | ||
// get the first sample from the buffer | ||
int16_t sample = mBuffer[mCurrentIndex]; | ||
mCurrentIndex++; | ||
ledcWrite(0, sample+128); | ||
} | ||
if(mCurrentIndex >= mBufferLength) | ||
{ | ||
// do we have any data in teh second buffer? | ||
BaseType_t xHigherPriorityTaskWoken; | ||
if (xSemaphoreTakeFromISR(mBufferSemaphore, &xHigherPriorityTaskWoken) == pdTRUE) | ||
{ | ||
if (mSecondBufferLength > 0) { | ||
// swap the buffers | ||
int8_t *tmp = mBuffer; | ||
mBuffer = mSecondBuffer; | ||
mBufferLength = mSecondBufferLength; | ||
mSecondBuffer = tmp; | ||
mSecondBufferLength = 0; | ||
mCurrentIndex = 0; | ||
} | ||
xSemaphoreGiveFromISR(mBufferSemaphore, &xHigherPriorityTaskWoken); | ||
} | ||
if(xHigherPriorityTaskWoken) { | ||
portYIELD_FROM_ISR(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#pragma once | ||
#include <freertos/FreeRTOS.h> | ||
#include "AudioOutput.h" | ||
#include "freertos/FreeRTOS.h" | ||
#include "freertos/task.h" | ||
#include "freertos/semphr.h" | ||
#include "esp_log.h" | ||
#include "driver/sigmadelta.h" | ||
#include "driver/timer.h" | ||
|
||
/** | ||
* Base Class for both the ADC and I2S sampler | ||
**/ | ||
class PWMTimerOutput : public AudioOutput | ||
{ | ||
private: | ||
gpio_num_t mPDMPin; | ||
uint32_t mSampleRate; | ||
SemaphoreHandle_t mBufferSemaphore; | ||
int8_t *mBuffer=NULL;; | ||
int mCurrentIndex=0; | ||
int mBufferLength=0; | ||
int8_t *mSecondBuffer=NULL;; | ||
int mSecondBufferLength=0; | ||
int mCount = 0; | ||
void onTimer(); | ||
public: | ||
PWMTimerOutput(gpio_num_t pdm_pin) : AudioOutput() | ||
{ | ||
mPDMPin = pdm_pin; | ||
mBufferSemaphore = xSemaphoreCreateBinary(); | ||
xSemaphoreGive(mBufferSemaphore); | ||
} | ||
void write(int8_t *samples, int count); | ||
void start(uint32_t sample_rate); | ||
void stop() {} | ||
|
||
friend void onTimerCallbackPWM(void *arg); | ||
}; |
Oops, something went wrong.