Merge pull request #4399 from tyomitch/patch-2
[stm] implementation of audiopwmio
This commit is contained in:
commit
bf58afdb2a
@ -411,6 +411,10 @@ msgstr ""
|
||||
msgid "AnalogOut not supported on given pin"
|
||||
msgstr ""
|
||||
|
||||
#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c
|
||||
msgid "Another PWMAudioOut is already active"
|
||||
msgstr ""
|
||||
|
||||
#: ports/atmel-samd/common-hal/pulseio/PulseOut.c
|
||||
#: ports/cxd56/common-hal/pulseio/PulseOut.c
|
||||
msgid "Another send is already active"
|
||||
@ -525,6 +529,7 @@ msgid "Buffer is too small"
|
||||
msgstr ""
|
||||
|
||||
#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c
|
||||
#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c
|
||||
#, c-format
|
||||
msgid "Buffer length %d too big. It must be less than %d"
|
||||
msgstr ""
|
||||
@ -974,6 +979,10 @@ msgstr ""
|
||||
msgid "Failed to allocate wifi scan memory"
|
||||
msgstr ""
|
||||
|
||||
#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c
|
||||
msgid "Failed to buffer the sample"
|
||||
msgstr ""
|
||||
|
||||
#: ports/nrf/common-hal/_bleio/Adapter.c
|
||||
msgid "Failed to connect: internal error"
|
||||
msgstr ""
|
||||
|
@ -20,6 +20,8 @@ LD_FILE = boards/STM32F401xd_fs.ld
|
||||
# lto for this port, and if other stuff hasn't been added in the
|
||||
# meantime
|
||||
CIRCUITPY_ULAB = 0
|
||||
CIRCUITPY_AUDIOCORE = 0
|
||||
CIRCUITPY_AUDIOPWMIO = 0
|
||||
CIRCUITPY_BUSDEVICE = 0
|
||||
CIRCUITPY_BITMAPTOOLS = 0
|
||||
CIRCUITPY_FRAMEBUFFERIO = 0
|
||||
|
@ -11,3 +11,7 @@ MCU_PACKAGE = UFQFPN48
|
||||
|
||||
LD_COMMON = boards/common_default.ld
|
||||
LD_FILE = boards/STM32F411_fs.ld
|
||||
|
||||
# Too big for the flash
|
||||
CIRCUITPY_AUDIOCORE = 0
|
||||
CIRCUITPY_AUDIOPWMIO = 0
|
||||
|
@ -27,15 +27,15 @@
|
||||
#include "supervisor/board.h"
|
||||
#include "mpconfigboard.h"
|
||||
|
||||
#include "shared-bindings/audiopwmio/PWMAudioOut.h"
|
||||
#include "shared-bindings/board/__init__.h"
|
||||
#include "shared-bindings/displayio/FourWire.h"
|
||||
#include "shared-module/displayio/__init__.h"
|
||||
#include "shared-module/displayio/mipi_constants.h"
|
||||
#include "shared-bindings/busio/SPI.h"
|
||||
|
||||
#include "supervisor/spi_flash_api.h"
|
||||
|
||||
displayio_fourwire_obj_t board_display_obj;
|
||||
audiopwmio_pwmaudioout_obj_t board_buzz_obj;
|
||||
|
||||
#define DELAY 0x80
|
||||
|
||||
@ -113,6 +113,11 @@ void board_init(void) {
|
||||
60, // native_frames_per_second
|
||||
true, // backlight_on_high
|
||||
false); // SH1107_addressing
|
||||
|
||||
board_buzz_obj.base.type = &audiopwmio_pwmaudioout_type;
|
||||
common_hal_audiopwmio_pwmaudioout_construct(&board_buzz_obj,
|
||||
&pin_PB08, NULL, 0x8000);
|
||||
never_reset_pin_number(pin_PB08.port, pin_PB08.number);
|
||||
}
|
||||
|
||||
bool board_requests_safe_mode(void) {
|
||||
|
@ -1,8 +1,11 @@
|
||||
#include "shared-bindings/board/__init__.h"
|
||||
#include "supervisor/spi_flash_api.h"
|
||||
|
||||
#include "shared-bindings/audiopwmio/PWMAudioOut.h"
|
||||
#include "shared-module/displayio/__init__.h"
|
||||
|
||||
extern audiopwmio_pwmaudioout_obj_t board_buzz_obj;
|
||||
|
||||
STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_LED_RED), MP_ROM_PTR(&pin_PB04) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_LED_GREEN), MP_ROM_PTR(&pin_PB05) },
|
||||
@ -15,7 +18,7 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_DISP_RST), MP_ROM_PTR(&pin_PB10) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_DISP_BL), MP_ROM_PTR(&pin_PB03) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_BUZZ), MP_ROM_PTR(&pin_PB08) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BUZZ), MP_ROM_PTR(&board_buzz_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BTNA), MP_ROM_PTR(&pin_PB09) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BTNB), MP_ROM_PTR(&pin_PC03) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_RIGHT), MP_ROM_PTR(&pin_PB02) },
|
||||
|
@ -12,3 +12,7 @@ MCU_PACKAGE = UFQFPN48
|
||||
|
||||
LD_COMMON = boards/common_default.ld
|
||||
LD_FILE = boards/STM32F411_fs.ld
|
||||
|
||||
# Too big for the flash
|
||||
CIRCUITPY_AUDIOCORE = 0
|
||||
CIRCUITPY_AUDIOPWMIO = 0
|
||||
|
@ -15,3 +15,7 @@ MCU_PACKAGE = UFQFPN48
|
||||
|
||||
LD_COMMON = boards/common_default.ld
|
||||
LD_FILE = boards/STM32F411_fs.ld
|
||||
|
||||
# Too big for the flash
|
||||
CIRCUITPY_AUDIOCORE = 0
|
||||
CIRCUITPY_AUDIOPWMIO = 0
|
||||
|
@ -11,3 +11,7 @@ MCU_PACKAGE = LQFP100_f4
|
||||
|
||||
LD_COMMON = boards/common_default.ld
|
||||
LD_FILE = boards/STM32F411_fs.ld
|
||||
|
||||
# Too big for the flash
|
||||
CIRCUITPY_AUDIOCORE = 0
|
||||
CIRCUITPY_AUDIOPWMIO = 0
|
||||
|
376
ports/stm/common-hal/audiopwmio/PWMAudioOut.c
Normal file
376
ports/stm/common-hal/audiopwmio/PWMAudioOut.c
Normal file
@ -0,0 +1,376 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Artyom Skrobov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "common-hal/audiopwmio/PWMAudioOut.h"
|
||||
#include "shared-bindings/audiopwmio/PWMAudioOut.h"
|
||||
|
||||
#include "timers.h"
|
||||
|
||||
// TODO: support multiple concurrently active outputs.
|
||||
STATIC TIM_HandleTypeDef tim_handle;
|
||||
STATIC audiopwmio_pwmaudioout_obj_t *active_audio = NULL;
|
||||
|
||||
STATIC void set_pin(uint8_t channel, GPIO_PinState state) {
|
||||
HAL_GPIO_WritePin(pin_port(active_audio->pin[channel]->port),
|
||||
pin_mask(active_audio->pin[channel]->number), state);
|
||||
}
|
||||
|
||||
STATIC void toggle_pin(uint8_t channel) {
|
||||
HAL_GPIO_TogglePin(pin_port(active_audio->pin[channel]->port),
|
||||
pin_mask(active_audio->pin[channel]->number));
|
||||
}
|
||||
|
||||
STATIC void set_drive_mode(const mcu_pin_obj_t *pin, uint32_t mode) {
|
||||
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||
GPIO_InitStruct.Pin = pin_mask(pin->number);
|
||||
GPIO_InitStruct.Mode = mode;
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
||||
HAL_GPIO_Init(pin_port(pin->port), &GPIO_InitStruct);
|
||||
}
|
||||
|
||||
STATIC void start_timer(audiopwmio_pwmaudioout_obj_t *self) {
|
||||
if (self->buffer_ptr[0] >= self->buffer_length[0]) { // no more pulses
|
||||
return;
|
||||
}
|
||||
|
||||
self->period = self->buffer[0][self->buffer_ptr[0]];
|
||||
if (self->pin[1] && self->period > self->buffer[1][self->buffer_ptr[1]]) {
|
||||
self->period = self->buffer[1][self->buffer_ptr[1]];
|
||||
}
|
||||
|
||||
// Set the new period
|
||||
tim_handle.Init.Period = self->period - 1;
|
||||
HAL_TIM_Base_Init(&tim_handle);
|
||||
|
||||
// TIM7 has limited HAL support, set registers manually
|
||||
tim_handle.Instance->SR = 0; // Prevent the SR from triggering an interrupt
|
||||
tim_handle.Instance->CR1 |= TIM_CR1_CEN; // Resume timer
|
||||
tim_handle.Instance->CR1 |= TIM_CR1_URS; // Disable non-overflow interrupts
|
||||
__HAL_TIM_ENABLE_IT(&tim_handle, TIM_IT_UPDATE);
|
||||
}
|
||||
|
||||
STATIC bool fill_buffers(audiopwmio_pwmaudioout_obj_t *self) {
|
||||
// Naive PCM-to-PWM conversion
|
||||
int16_t threshold = 0x666; // 0.05; TODO: make configurable
|
||||
uint8_t *buffer;
|
||||
uint32_t buffer_length;
|
||||
audioio_get_buffer_result_t get_buffer_result;
|
||||
|
||||
bool average = (self->sample_channel_count > 1) && !self->pin[1];
|
||||
bool replicate = (self->sample_channel_count == 1) && self->pin[1];
|
||||
int8_t effective_channels = average ? 1 : self->sample_channel_count;
|
||||
|
||||
do {
|
||||
get_buffer_result = audiosample_get_buffer(self->sample, false, 0, &buffer, &buffer_length);
|
||||
if (get_buffer_result == GET_BUFFER_ERROR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t num_samples = buffer_length / self->bytes_per_sample / self->sample_channel_count;
|
||||
int16_t *buffer16 = (int16_t*)buffer;
|
||||
|
||||
while (num_samples--) {
|
||||
for (int8_t channel=0; channel < effective_channels; channel++) {
|
||||
int16_t val;
|
||||
if (self->bytes_per_sample == 1) {
|
||||
val = *buffer++ << 8;
|
||||
} else {
|
||||
val = *buffer16++;
|
||||
}
|
||||
val += self->sample_offset;
|
||||
|
||||
if (average) {
|
||||
int16_t next;
|
||||
if (self->bytes_per_sample == 1) {
|
||||
next = *buffer++ << 8;
|
||||
} else {
|
||||
next = *buffer16++;
|
||||
}
|
||||
next += self->sample_offset;
|
||||
val += (next - val) / 2;
|
||||
}
|
||||
|
||||
int8_t new_pos = (val > threshold) - (val < -threshold);
|
||||
if (new_pos == -self->pos[channel]) {
|
||||
if (self->len[channel] > 1) {
|
||||
self->buffer[channel][self->buffer_length[channel]++] = self->len[channel];
|
||||
if (replicate) {
|
||||
self->buffer[1-channel][self->buffer_length[1-channel]++] = self->len[channel];
|
||||
}
|
||||
self->len[channel] = 0;
|
||||
}
|
||||
self->pos[channel] = new_pos;
|
||||
}
|
||||
self->len[channel]++;
|
||||
}
|
||||
}
|
||||
} while (get_buffer_result == GET_BUFFER_MORE_DATA &&
|
||||
(!self->buffer_length[0] || (self->pin[1] && !self->buffer_length[1])));
|
||||
|
||||
if (get_buffer_result == GET_BUFFER_DONE) {
|
||||
// It's the final countdown
|
||||
for (int8_t channel=0; channel < effective_channels; channel++) {
|
||||
self->buffer[channel][self->buffer_length[channel]++] = self->len[channel];
|
||||
if (replicate) {
|
||||
self->buffer[1-channel][self->buffer_length[1-channel]++] = self->len[channel];
|
||||
}
|
||||
}
|
||||
|
||||
if (self->loop) {
|
||||
audiosample_reset_buffer(self->sample, false, 0);
|
||||
} else {
|
||||
self->stopping = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
STATIC void move_to_beginning(uint16_t *buffer, uint16_t *buffer_length, uint16_t *buffer_ptr) {
|
||||
if (*buffer_ptr < *buffer_length) {
|
||||
memmove(buffer, buffer + *buffer_ptr, *buffer_length - *buffer_ptr);
|
||||
*buffer_length -= *buffer_ptr;
|
||||
} else {
|
||||
*buffer_length = 0;
|
||||
}
|
||||
*buffer_ptr = 0;
|
||||
}
|
||||
|
||||
STATIC void pwmaudioout_event_handler(void) {
|
||||
// Detect TIM Update event
|
||||
if (__HAL_TIM_GET_FLAG(&tim_handle, TIM_FLAG_UPDATE) != RESET)
|
||||
{
|
||||
if (__HAL_TIM_GET_IT_SOURCE(&tim_handle, TIM_IT_UPDATE) != RESET)
|
||||
{
|
||||
__HAL_TIM_CLEAR_IT(&tim_handle, TIM_IT_UPDATE);
|
||||
if (!active_audio || active_audio->paused) {
|
||||
__HAL_TIM_DISABLE_IT(&tim_handle, TIM_IT_UPDATE);
|
||||
return;
|
||||
}
|
||||
|
||||
bool refill = false;
|
||||
|
||||
active_audio->buffer[0][active_audio->buffer_ptr[0]] -= active_audio->period;
|
||||
if (!active_audio->buffer[0][active_audio->buffer_ptr[0]]) {
|
||||
toggle_pin(0);
|
||||
if (++(active_audio->buffer_ptr[0]) >= active_audio->buffer_length[0]) {
|
||||
refill = true;
|
||||
}
|
||||
}
|
||||
if (active_audio->pin[1]) {
|
||||
active_audio->buffer[1][active_audio->buffer_ptr[1]] -= active_audio->period;
|
||||
if (!active_audio->buffer[1][active_audio->buffer_ptr[1]]) {
|
||||
toggle_pin(1);
|
||||
if (++(active_audio->buffer_ptr[1]) >= active_audio->buffer_length[1]) {
|
||||
refill = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (refill) {
|
||||
__HAL_TIM_DISABLE_IT(&tim_handle, TIM_IT_UPDATE);
|
||||
|
||||
move_to_beginning(active_audio->buffer[0], &active_audio->buffer_length[0], &active_audio->buffer_ptr[0]);
|
||||
if (active_audio->pin[1]) {
|
||||
move_to_beginning(active_audio->buffer[1], &active_audio->buffer_length[1], &active_audio->buffer_ptr[1]);
|
||||
}
|
||||
|
||||
if (active_audio->stopping || !fill_buffers(active_audio)) {
|
||||
// No more audio. Turn off output and don't restart.
|
||||
common_hal_audiopwmio_pwmaudioout_stop(active_audio);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Count up to the next given value.
|
||||
start_timer(active_audio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audiopwmout_reset() {
|
||||
if (active_audio) {
|
||||
common_hal_audiopwmio_pwmaudioout_stop(active_audio);
|
||||
}
|
||||
}
|
||||
|
||||
// Caller validates that pins are free.
|
||||
void common_hal_audiopwmio_pwmaudioout_construct(audiopwmio_pwmaudioout_obj_t *self,
|
||||
const mcu_pin_obj_t *left_channel, const mcu_pin_obj_t *right_channel, uint16_t quiescent_value) {
|
||||
|
||||
// Set up the pin(s) for output
|
||||
self->pin[0] = left_channel;
|
||||
self->pin[1] = right_channel;
|
||||
set_drive_mode(left_channel, GPIO_MODE_OUTPUT_PP);
|
||||
if (right_channel) {
|
||||
set_drive_mode(right_channel, GPIO_MODE_OUTPUT_PP);
|
||||
}
|
||||
|
||||
self->buffer[0] = NULL;
|
||||
self->buffer[1] = NULL;
|
||||
|
||||
self->quiescent_value = quiescent_value;
|
||||
}
|
||||
|
||||
bool common_hal_audiopwmio_pwmaudioout_deinited(audiopwmio_pwmaudioout_obj_t *self) {
|
||||
return !self->pin[0];
|
||||
}
|
||||
|
||||
STATIC void free_buffers(audiopwmio_pwmaudioout_obj_t *self) {
|
||||
m_free(self->buffer[0]);
|
||||
self->buffer[0] = NULL;
|
||||
m_free(self->buffer[1]);
|
||||
self->buffer[1] = NULL;
|
||||
}
|
||||
|
||||
void common_hal_audiopwmio_pwmaudioout_deinit(audiopwmio_pwmaudioout_obj_t *self) {
|
||||
if (common_hal_audiopwmio_pwmaudioout_deinited(self)) {
|
||||
return;
|
||||
}
|
||||
common_hal_audiopwmio_pwmaudioout_stop(self);
|
||||
|
||||
free_buffers(self);
|
||||
|
||||
self->pin[0] = 0;
|
||||
self->pin[1] = 0;
|
||||
}
|
||||
|
||||
void common_hal_audiopwmio_pwmaudioout_play(audiopwmio_pwmaudioout_obj_t *self, mp_obj_t sample, bool loop) {
|
||||
common_hal_audiopwmio_pwmaudioout_stop(self);
|
||||
if (active_audio) {
|
||||
mp_raise_RuntimeError(translate("Another PWMAudioOut is already active")); // TODO
|
||||
}
|
||||
self->sample = sample;
|
||||
self->loop = loop;
|
||||
|
||||
uint32_t sample_rate = audiosample_sample_rate(sample);
|
||||
self->bytes_per_sample = audiosample_bits_per_sample(sample) / 8;
|
||||
|
||||
uint32_t max_buffer_length;
|
||||
uint8_t spacing;
|
||||
bool single_buffer;
|
||||
bool samples_signed;
|
||||
audiosample_get_buffer_structure(sample, /* single channel */ false,
|
||||
&single_buffer, &samples_signed, &max_buffer_length, &spacing);
|
||||
self->sample_channel_count = audiosample_channel_count(sample);
|
||||
self->sample_offset = (samples_signed ? 0x8000 : 0) - self->quiescent_value;
|
||||
|
||||
free_buffers(self);
|
||||
|
||||
if (max_buffer_length > UINT16_MAX) {
|
||||
mp_raise_ValueError_varg(translate("Buffer length %d too big. It must be less than %d"), max_buffer_length, UINT16_MAX);
|
||||
}
|
||||
uint16_t buffer_length = (uint16_t)max_buffer_length / self->bytes_per_sample;
|
||||
self->buffer[0] = m_malloc(buffer_length * sizeof(uint16_t), false);
|
||||
self->buffer_ptr[0] = self->buffer_length[0] = 0;
|
||||
if (self->pin[1]) {
|
||||
self->buffer[1] = m_malloc(buffer_length * sizeof(uint16_t), false);
|
||||
self->buffer_ptr[1] = self->buffer_length[1] = 0;
|
||||
}
|
||||
|
||||
self->pos[0] = self->pos[1] = 1; // initially on
|
||||
self->len[0] = self->len[1] = 0;
|
||||
|
||||
audiosample_reset_buffer(self->sample, false, 0);
|
||||
self->stopping = false;
|
||||
self->paused = false;
|
||||
if (!fill_buffers(self)) {
|
||||
mp_raise_RuntimeError(translate("Failed to buffer the sample"));
|
||||
}
|
||||
|
||||
// Calculate period (TODO: supersample to 1 MHz?)
|
||||
TIM_TypeDef *tim_instance = stm_peripherals_find_timer();
|
||||
uint32_t source = stm_peripherals_timer_get_source_freq(tim_instance);
|
||||
uint32_t prescaler = source/sample_rate;
|
||||
|
||||
// Activate timer
|
||||
active_audio = self;
|
||||
stm_peripherals_timer_reserve(tim_instance);
|
||||
stm_peripherals_timer_preinit(tim_instance, 4, pwmaudioout_event_handler);
|
||||
|
||||
tim_handle.Instance = tim_instance;
|
||||
tim_handle.Init.Period = 100; //immediately replaced.
|
||||
tim_handle.Init.Prescaler = prescaler - 1;
|
||||
tim_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
tim_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
tim_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
||||
|
||||
HAL_TIM_Base_Init(&tim_handle);
|
||||
tim_handle.Instance->SR = 0;
|
||||
|
||||
// Alternate on and off, starting with on.
|
||||
set_pin(0, GPIO_PIN_SET);
|
||||
if (self->pin[1]) {
|
||||
set_pin(1, GPIO_PIN_SET);
|
||||
}
|
||||
|
||||
// Count up to the next given value.
|
||||
start_timer(self);
|
||||
}
|
||||
|
||||
void common_hal_audiopwmio_pwmaudioout_stop(audiopwmio_pwmaudioout_obj_t *self) {
|
||||
if (active_audio != self) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Turn off timer counter.
|
||||
tim_handle.Instance->CR1 &= ~TIM_CR1_CEN;
|
||||
stm_peripherals_timer_free(tim_handle.Instance);
|
||||
|
||||
active_audio = NULL;
|
||||
self->stopping = false;
|
||||
self->paused = false;
|
||||
|
||||
// Make sure pins are left low.
|
||||
set_pin(0, GPIO_PIN_RESET);
|
||||
if (self->pin[1]) {
|
||||
set_pin(1, GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
// Cannot free buffers here because we may be called from
|
||||
// the interrupt handler, and the heap is not reentrant.
|
||||
}
|
||||
|
||||
bool common_hal_audiopwmio_pwmaudioout_get_playing(audiopwmio_pwmaudioout_obj_t *self) {
|
||||
return active_audio == self;
|
||||
}
|
||||
|
||||
void common_hal_audiopwmio_pwmaudioout_pause(audiopwmio_pwmaudioout_obj_t *self) {
|
||||
self->paused = true;
|
||||
}
|
||||
|
||||
void common_hal_audiopwmio_pwmaudioout_resume(audiopwmio_pwmaudioout_obj_t *self) {
|
||||
self->paused = false;
|
||||
if (active_audio == self) {
|
||||
start_timer(self);
|
||||
}
|
||||
}
|
||||
|
||||
bool common_hal_audiopwmio_pwmaudioout_get_paused(audiopwmio_pwmaudioout_obj_t *self) {
|
||||
return self->paused;
|
||||
}
|
58
ports/stm/common-hal/audiopwmio/PWMAudioOut.h
Normal file
58
ports/stm/common-hal/audiopwmio/PWMAudioOut.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Artyom Skrobov
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MICROPY_INCLUDED_STM_COMMON_HAL_AUDIOPWM_AUDIOOUT_H
|
||||
#define MICROPY_INCLUDED_STM_COMMON_HAL_AUDIOPWM_AUDIOOUT_H
|
||||
|
||||
#include "common-hal/microcontroller/Pin.h"
|
||||
|
||||
typedef struct {
|
||||
mp_obj_base_t base;
|
||||
const mcu_pin_obj_t *pin[2];
|
||||
uint16_t quiescent_value;
|
||||
|
||||
uint16_t *buffer[2];
|
||||
uint16_t buffer_length[2];
|
||||
uint16_t buffer_ptr[2];
|
||||
|
||||
mp_obj_t *sample;
|
||||
int16_t sample_offset;
|
||||
uint8_t sample_channel_count;
|
||||
uint8_t bytes_per_sample;
|
||||
|
||||
// PCM-to-PWM conversion state
|
||||
int8_t pos[2]; // -1 for off, +1 for on
|
||||
uint16_t len[2];
|
||||
|
||||
uint16_t period;
|
||||
bool stopping;
|
||||
bool paused;
|
||||
bool loop;
|
||||
} audiopwmio_pwmaudioout_obj_t;
|
||||
|
||||
void audiopwmout_reset(void);
|
||||
|
||||
#endif
|
0
ports/stm/common-hal/audiopwmio/__init__.c
Normal file
0
ports/stm/common-hal/audiopwmio/__init__.c
Normal file
@ -12,9 +12,13 @@ ifeq ($(MCU_VARIANT),$(filter $(MCU_VARIANT),STM32F405xx STM32F407xx))
|
||||
endif
|
||||
|
||||
ifeq ($(MCU_SERIES),F4)
|
||||
# Audio via PWM
|
||||
CIRCUITPY_AUDIOIO = 0
|
||||
CIRCUITPY_AUDIOCORE ?= 1
|
||||
CIRCUITPY_AUDIOPWMIO ?= 1
|
||||
|
||||
# Not yet implemented common-hal modules:
|
||||
CIRCUITPY_AUDIOBUSIO ?= 0
|
||||
CIRCUITPY_AUDIOIO ?= 0
|
||||
CIRCUITPY_COUNTIO ?= 0
|
||||
CIRCUITPY_FREQUENCYIO ?= 0
|
||||
CIRCUITPY_I2CPERIPHERAL ?= 0
|
||||
|
@ -32,6 +32,9 @@
|
||||
|
||||
#include "common-hal/microcontroller/Pin.h"
|
||||
|
||||
#ifdef CIRCUITPY_AUDIOPWMIO
|
||||
#include "common-hal/audiopwmio/PWMAudioOut.h"
|
||||
#endif
|
||||
#if CIRCUITPY_BUSIO
|
||||
#include "common-hal/busio/I2C.h"
|
||||
#include "common-hal/busio/SPI.h"
|
||||
@ -229,6 +232,9 @@ void SysTick_Handler(void) {
|
||||
|
||||
void reset_port(void) {
|
||||
reset_all_pins();
|
||||
#if CIRCUITPY_AUDIOPWMIO
|
||||
audiopwmout_reset();
|
||||
#endif
|
||||
#if CIRCUITPY_BUSIO
|
||||
i2c_reset();
|
||||
spi_reset();
|
||||
|
@ -155,8 +155,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiopwmio_pwmaudioout___exit___obj,
|
||||
//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`.
|
||||
//|
|
||||
//| The sample itself should consist of 16 bit samples. Microcontrollers with a lower output
|
||||
//| resolution will use the highest order bits to output. For example, the SAMD21 has a 10 bit
|
||||
//| DAC that ignores the lowest 6 bits when playing 16 bit samples."""
|
||||
//| resolution will use the highest order bits to output."""
|
||||
//| ...
|
||||
//|
|
||||
STATIC mp_obj_t audiopwmio_pwmaudioout_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user