mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
fix qor mailboxes + all is in place for audio with thread, dma and irq
This commit is contained in:
parent
51b202abfb
commit
3c652a3bde
11 changed files with 195 additions and 343 deletions
3
software/.vscode/settings.json
vendored
3
software/.vscode/settings.json
vendored
|
|
@ -36,6 +36,7 @@
|
|||
"numbers": "c",
|
||||
"systick.h": "c",
|
||||
"critical_section.h": "c",
|
||||
"serializers.h": "c"
|
||||
"serializers.h": "c",
|
||||
"cstring": "c"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,9 @@
|
|||
|
||||
|
||||
// C library
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// OST common files
|
||||
#include "ost_hal.h"
|
||||
#include "debug.h"
|
||||
|
|
@ -22,33 +27,10 @@
|
|||
#include "audio_player.h"
|
||||
#include "pico_i2s.h"
|
||||
|
||||
static volatile uint32_t msTicks = 0;
|
||||
static audio_ctx_t audio_ctx;
|
||||
// ===========================================================================================================
|
||||
// CONSTANTS / DEFINES
|
||||
// ===========================================================================================================
|
||||
|
||||
void __isr __time_critical_func(audio_i2s_dma_irq_handler)();
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// SYSTEM HAL
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define UART_ID uart0
|
||||
#define BAUD_RATE 115200
|
||||
|
||||
// We are using pins 0 and 1, but see the GPIO function select table in the
|
||||
// datasheet for information on which other pins can be used.
|
||||
#define UART_TX_PIN 0
|
||||
#define UART_RX_PIN 1
|
||||
|
||||
static struct repeating_timer sys_timer;
|
||||
|
||||
// static audio_i2s_config_t i2s_config = {28, 26, 0};
|
||||
|
||||
static __attribute__((aligned(8))) pio_i2s i2s;
|
||||
|
||||
void ost_system_delay_ms(uint32_t delay)
|
||||
{
|
||||
busy_wait_ms(delay);
|
||||
}
|
||||
const uint8_t LED_PIN = 14; // GP 14
|
||||
|
||||
const uint8_t LCD_DC = 8;
|
||||
|
|
@ -66,18 +48,36 @@ const uint8_t ROTARY_BUTTON = 3;
|
|||
const uint8_t SD_CARD_CS = 17;
|
||||
const uint8_t SD_CARD_PRESENCE = 24;
|
||||
|
||||
static bool sys_timer_callback(struct repeating_timer *t)
|
||||
{
|
||||
msTicks++;
|
||||
#define UART_ID uart0
|
||||
#define BAUD_RATE 115200
|
||||
|
||||
// qor_switch_context();
|
||||
// We are using pins 0 and 1, but see the GPIO function select table in the
|
||||
// datasheet for information on which other pins can be used.
|
||||
#define UART_TX_PIN 0
|
||||
#define UART_RX_PIN 1
|
||||
|
||||
return true;
|
||||
}
|
||||
// ===========================================================================================================
|
||||
// GLOBAL VARIABLES
|
||||
// ===========================================================================================================
|
||||
static __attribute__((aligned(8))) pio_i2s i2s;
|
||||
static volatile uint32_t msTicks = 0;
|
||||
static audio_ctx_t audio_ctx;
|
||||
|
||||
// ===========================================================================================================
|
||||
// PROTOTYPES
|
||||
// ===========================================================================================================
|
||||
extern void init_spi(void);
|
||||
|
||||
void dma_init();
|
||||
void __isr __time_critical_func(audio_i2s_dma_irq_handler)();
|
||||
|
||||
// ===========================================================================================================
|
||||
// OST HAL IMPLEMENTATION
|
||||
// ===========================================================================================================
|
||||
|
||||
void ost_system_delay_ms(uint32_t delay)
|
||||
{
|
||||
busy_wait_ms(delay);
|
||||
}
|
||||
|
||||
void gpio_callback(uint gpio, uint32_t events)
|
||||
{
|
||||
|
|
@ -167,15 +167,10 @@ void ost_system_initialize()
|
|||
.data_pin = 28,
|
||||
.clock_pin_base = 26};
|
||||
|
||||
// i2s_program_start_synched(pio0, audio_i2s_dma_irq_handler, &i2s, &config);
|
||||
|
||||
// pico_i2s_setup(&config);
|
||||
i2s_program_setup(pio0, audio_i2s_dma_irq_handler, &i2s, &config);
|
||||
|
||||
audio_init(&audio_ctx);
|
||||
|
||||
//------------------- System timer (1ms)
|
||||
// add_repeating_timer_ms(1, sys_timer_callback, NULL, &sys_timer);
|
||||
|
||||
// ------------ Everything is initialized, print stuff here
|
||||
debug_printf("System Clock: %lu\n", clock_get_hz(clk_sys));
|
||||
}
|
||||
|
|
@ -304,21 +299,20 @@ void ost_display_transfer_multi(uint8_t *buff, uint32_t btr)
|
|||
// AUDIO HAL
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// extern shared_state_t shared_state;
|
||||
|
||||
// #include "pico/critical_section.h"
|
||||
|
||||
// critical_section_t acrit;
|
||||
|
||||
void ost_audio_play(const char *filename)
|
||||
{
|
||||
|
||||
audio_play(&audio_ctx, filename);
|
||||
|
||||
// audio_i2s_set_enabled(true);
|
||||
// audio_process(&audio_ctx);
|
||||
i2s.buffer_index = 0;
|
||||
|
||||
// audio_step = 1;
|
||||
// On appelle une première fois le process pour récupérer et initialiser le premier buffer...
|
||||
audio_process(&audio_ctx);
|
||||
|
||||
// Puis le deuxième ... (pour avoir un buffer d'avance)
|
||||
audio_process(&audio_ctx);
|
||||
|
||||
// On lance les DMA
|
||||
i2s_start(&i2s);
|
||||
}
|
||||
|
||||
int ost_audio_process()
|
||||
|
|
@ -326,39 +320,39 @@ int ost_audio_process()
|
|||
return audio_process(&audio_ctx);
|
||||
}
|
||||
|
||||
static ost_audio_callback_t AudioCallBack = NULL;
|
||||
|
||||
void ost_audio_register_callback(ost_audio_callback_t cb)
|
||||
{
|
||||
AudioCallBack = cb;
|
||||
}
|
||||
|
||||
void ost_hal_audio_frame_end()
|
||||
void ost_hal_audio_new_frame(const void *buff, int dma_trans_number)
|
||||
{
|
||||
if (dma_trans_number > STEREO_BUFFER_SIZE)
|
||||
{
|
||||
// Problème
|
||||
return;
|
||||
}
|
||||
memcpy(i2s.out_ctrl_blocks[i2s.buffer_index], buff, dma_trans_number * sizeof(uint32_t));
|
||||
i2s.buffer_index = 1 - i2s.buffer_index;
|
||||
|
||||
// uint dma_channel = shared_state.dma_channel;
|
||||
// if (dma_irqn_get_channel_status(PICO_AUDIO_I2S_DMA_IRQ, dma_channel))
|
||||
// {
|
||||
// dma_irqn_acknowledge_channel(PICO_AUDIO_I2S_DMA_IRQ, dma_channel);
|
||||
// }
|
||||
}
|
||||
|
||||
void ost_hal_audio_frame_start(const volatile void *buff, int dma_trans_number)
|
||||
{
|
||||
//
|
||||
// dma_channel_transfer_from_buffer_now(shared_state.dma_channel, buff, dma_trans_number);
|
||||
// dma_channel_start(shared_state.dma_channel);
|
||||
|
||||
dma_hw->ints0 = 1u << i2s.dma_ch_out_data; // clear the IRQ
|
||||
// dma_hw->ints0 = 1u << i2s.dma_ch_out_data; // clear the IRQ
|
||||
}
|
||||
|
||||
void __isr __time_critical_func(audio_i2s_dma_irq_handler)()
|
||||
{
|
||||
/*
|
||||
uint dma_channel = shared_state.dma_channel;
|
||||
if (dma_irqn_get_channel_status(PICO_AUDIO_I2S_DMA_IRQ, dma_channel))
|
||||
{
|
||||
dma_irqn_acknowledge_channel(PICO_AUDIO_I2S_DMA_IRQ, dma_channel);
|
||||
}
|
||||
dma_hw->ints0 = 1u << i2s.dma_ch_out_data; // clear the IRQ
|
||||
|
||||
audio_process(&audio_ctx);
|
||||
*/
|
||||
// Warn the application layer that we have done on that channel
|
||||
if (AudioCallBack != NULL)
|
||||
{
|
||||
AudioCallBack();
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // legacy audio test
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@
|
|||
|
||||
#include "debug.h"
|
||||
|
||||
void pico_i2s_set_frequency(const audio_i2s_config_t *config)
|
||||
void pico_i2s_set_frequency(const pio_i2s *i2s, const audio_i2s_config_t *config)
|
||||
{
|
||||
float bitClk = config->freq * config->bps * 2.0 /* channels */ * 2.0 /* edges per clock */;
|
||||
// pio_sm_set_clkdiv(audio_pio, shared_state.pio_sm, (float)clock_get_hz(clk_sys) / bitClk);
|
||||
pio_sm_set_clkdiv(i2s->pio, i2s->sm_dout, (float)clock_get_hz(clk_sys) / bitClk);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
|
@ -64,7 +64,7 @@ void audio_i2s_set_enabled(bool enabled)
|
|||
|
||||
//---------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
static void dma_double_buffer_init(pio_i2s *i2s, void (*dma_handler)(void))
|
||||
static void dma_double_buffer_init(pio_i2s *i2s)
|
||||
{
|
||||
i2s->dma_ch_out_ctrl = dma_claim_unused_channel(true);
|
||||
i2s->dma_ch_out_data = dma_claim_unused_channel(true);
|
||||
|
|
@ -75,14 +75,25 @@ static void dma_double_buffer_init(pio_i2s *i2s, void (*dma_handler)(void))
|
|||
dma_channel_config c = dma_channel_get_default_config(i2s->dma_ch_out_ctrl);
|
||||
channel_config_set_read_increment(&c, true);
|
||||
channel_config_set_write_increment(&c, false);
|
||||
|
||||
// i2s->out_ctrl_blocks pointe sur deux emplacements, 4 octets par emplacement (qui est une adresse)
|
||||
// A chaque fois que le DMA de contrôle sera activé, l'adresse va s'incrémenter de 4 octets (DMA_SIZE_32)
|
||||
// Dès lors :
|
||||
// - On commence à l'index 0
|
||||
// - Puis l'index 1
|
||||
// On va revenir à l'index 0 tous les ... 8 octets, donc on programme ce décalage (3 bits)
|
||||
channel_config_set_ring(&c, false, 3);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
|
||||
dma_channel_configure(i2s->dma_ch_out_ctrl, &c, &dma_hw->ch[i2s->dma_ch_out_data].al3_read_addr_trig, i2s->out_ctrl_blocks, 1, false);
|
||||
|
||||
// la destination est l'alias al3_read_addr_trig qui va donc modifier l'adresse de début du transfer DMA et enclencher le démarrage
|
||||
// Nous avons deux actions, c'est pour cela que c'est un alias
|
||||
|
||||
c = dma_channel_get_default_config(i2s->dma_ch_out_data);
|
||||
channel_config_set_read_increment(&c, true);
|
||||
channel_config_set_write_increment(&c, false);
|
||||
channel_config_set_chain_to(&c, i2s->dma_ch_out_ctrl);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
|
||||
channel_config_set_chain_to(&c, i2s->dma_ch_out_ctrl); // On va chaîner ce DMA au contrôle: lorsque le DMA est terminé, le DMA de contrôle va s'activer... et ainsi de suite
|
||||
channel_config_set_dreq(&c, pio_get_dreq(i2s->pio, i2s->sm_dout, true));
|
||||
|
||||
dma_channel_configure(i2s->dma_ch_out_data,
|
||||
|
|
@ -91,15 +102,9 @@ static void dma_double_buffer_init(pio_i2s *i2s, void (*dma_handler)(void))
|
|||
NULL,
|
||||
STEREO_BUFFER_SIZE,
|
||||
false);
|
||||
|
||||
dma_channel_set_irq0_enabled(i2s->dma_ch_out_data, true);
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, dma_handler);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
|
||||
dma_channel_start(i2s->dma_ch_out_ctrl);
|
||||
}
|
||||
|
||||
static void i2s_sync_program_init(PIO pio, pio_i2s *i2s, audio_i2s_config_t *config)
|
||||
static void i2s_sync_program_init(PIO pio, pio_i2s *i2s, const audio_i2s_config_t *config)
|
||||
{
|
||||
uint offset = 0;
|
||||
i2s->pio = pio;
|
||||
|
|
@ -110,13 +115,46 @@ static void i2s_sync_program_init(PIO pio, pio_i2s *i2s, audio_i2s_config_t *con
|
|||
offset = pio_add_program(pio0, &i2s_out_master_program);
|
||||
// 4th argument is bit depth, 5th dout, 6th bclk pin base (lrclk is bclk pin + 1)
|
||||
i2s_out_master_program_init(pio, i2s->sm_dout, offset, config->bps, config->data_pin, config->clock_pin_base);
|
||||
// pio_sm_set_clkdiv(pio, i2s->sm_dout, 89); // Approximately 11KHz audio
|
||||
}
|
||||
|
||||
void i2s_program_start_synched(PIO pio, void (*dma_handler)(void), pio_i2s *i2s, audio_i2s_config_t *config)
|
||||
void i2s_program_setup(PIO pio, void (*dma_handler)(void), pio_i2s *i2s, const audio_i2s_config_t *config)
|
||||
{
|
||||
// i2s_sync_program_init(pio, i2s);
|
||||
pico_i2s_set_frequency(config);
|
||||
dma_double_buffer_init(i2s, dma_handler);
|
||||
if (((uint32_t)i2s & 0x7) != 0)
|
||||
{
|
||||
debug_printf("pio_i2s argument must be 8-byte aligned!\r\n");
|
||||
}
|
||||
|
||||
i2s_sync_program_init(pio, i2s, config);
|
||||
pico_i2s_set_frequency(i2s, config); // call after PIO configuration
|
||||
dma_double_buffer_init(i2s);
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, dma_handler);
|
||||
pio_enable_sm_mask_in_sync(i2s->pio, i2s->sm_mask);
|
||||
}
|
||||
|
||||
void i2s_start(pio_i2s *i2s)
|
||||
{
|
||||
// D'abord on va paramétrer les diverses interruptions
|
||||
dma_channel_set_irq0_enabled(i2s->dma_ch_out_data, true);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
|
||||
// YAAAAAAA on lance!
|
||||
dma_channel_start(i2s->dma_ch_out_ctrl);
|
||||
}
|
||||
|
||||
static void pico_gracefully_stop_dma(uint channel)
|
||||
{
|
||||
// See errata sheet to avoid spurious interrupts
|
||||
// disable the channel on IRQ0
|
||||
dma_channel_set_irq0_enabled(channel, false);
|
||||
// abort the channel
|
||||
dma_channel_abort(channel);
|
||||
// clear the spurious IRQ (if there was one)
|
||||
dma_channel_acknowledge_irq0(channel);
|
||||
}
|
||||
|
||||
void i2s_stop(pio_i2s *i2s)
|
||||
{
|
||||
pico_gracefully_stop_dma(i2s->dma_ch_out_ctrl);
|
||||
pico_gracefully_stop_dma(i2s->dma_ch_out_data);
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,158 +13,15 @@
|
|||
#include "hardware/irq.h"
|
||||
#include "hardware/pio.h"
|
||||
|
||||
#include "audio_player.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
|
||||
/** \file audio_i2s.h
|
||||
* \defgroup pico_audio_i2s pico_audio_i2s
|
||||
* I2S audio output using the PIO
|
||||
*
|
||||
* This library uses the \ref hardware_pio system to implement a I2S audio interface
|
||||
*
|
||||
* \todo Must be more we need to say here.
|
||||
* \todo certainly need an example
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PICO_AUDIO_I2S_DMA_IRQ
|
||||
#ifdef PICO_AUDIO_DMA_IRQ
|
||||
#define PICO_AUDIO_I2S_DMA_IRQ PICO_AUDIO_DMA_IRQ
|
||||
#else
|
||||
#define PICO_AUDIO_I2S_DMA_IRQ 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef PICO_AUDIO_I2S_PIO
|
||||
#ifdef PICO_AUDIO_PIO
|
||||
#define PICO_AUDIO_I2S_PIO PICO_AUDIO_PIO
|
||||
#else
|
||||
#define PICO_AUDIO_I2S_PIO 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !(PICO_AUDIO_I2S_DMA_IRQ == 0 || PICO_AUDIO_I2S_DMA_IRQ == 1)
|
||||
#error PICO_AUDIO_I2S_DMA_IRQ must be 0 or 1
|
||||
#endif
|
||||
|
||||
#if !(PICO_AUDIO_I2S_PIO == 0 || PICO_AUDIO_I2S_PIO == 1)
|
||||
#error PICO_AUDIO_I2S_PIO ust be 0 or 1
|
||||
#endif
|
||||
|
||||
#ifndef PICO_AUDIO_I2S_MAX_CHANNELS
|
||||
#ifdef PICO_AUDIO_MAX_CHANNELS
|
||||
#define PICO_AUDIO_I2S_MAX_CHANNELS PICO_AUDIO_MAX_CHANNELS
|
||||
#else
|
||||
#define PICO_AUDIO_I2S_MAX_CHANNELS 2u
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef PICO_AUDIO_I2S_BUFFERS_PER_CHANNEL
|
||||
#ifdef PICO_AUDIO_BUFFERS_PER_CHANNEL
|
||||
#define PICO_AUDIO_I2S_BUFFERS_PER_CHANNEL PICO_AUDIO_BUFFERS_PER_CHANNEL
|
||||
#else
|
||||
#define PICO_AUDIO_I2S_BUFFERS_PER_CHANNEL 3u
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef PICO_AUDIO_I2S_BUFFER_SAMPLE_LENGTH
|
||||
#ifdef PICO_AUDIO_BUFFER_SAMPLE_LENGTH
|
||||
#define PICO_AUDIO_I2S_BUFFER_SAMPLE_LENGTH PICO_AUDIO_BUFFER_SAMPLE_LENGTH
|
||||
#else
|
||||
#define PICO_AUDIO_I2S_BUFFER_SAMPLE_LENGTH 576u
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef PICO_AUDIO_I2S_SILENCE_BUFFER_SAMPLE_LENGTH
|
||||
#ifdef PICO_AUDIO_I2S_SILENCE_BUFFER_SAMPLE_LENGTH
|
||||
#define PICO_AUDIO_I2S_SILENCE_BUFFER_SAMPLE_LENGTH PICO_AUDIO_SILENCE_BUFFER_SAMPLE_LENGTH
|
||||
#else
|
||||
#define PICO_AUDIO_I2S_SILENCE_BUFFER_SAMPLE_LENGTH 256u
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Allow use of pico_audio driver without actually doing anything much
|
||||
#ifndef PICO_AUDIO_I2S_NOOP
|
||||
#ifdef PICO_AUDIO_NOOP
|
||||
#define PICO_AUDIO_I2S_NOOP PICO_AUDIO_NOOP
|
||||
#else
|
||||
#define PICO_AUDIO_I2S_NOOP 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef PICO_AUDIO_I2S_MONO_INPUT
|
||||
#define PICO_AUDIO_I2S_MONO_INPUT 0
|
||||
#endif
|
||||
#ifndef PICO_AUDIO_I2S_MONO_OUTPUT
|
||||
#define PICO_AUDIO_I2S_MONO_OUTPUT 0
|
||||
#endif
|
||||
|
||||
#ifndef PICO_AUDIO_I2S_DATA_PIN
|
||||
// #warning PICO_AUDIO_I2S_DATA_PIN should be defined when using AUDIO_I2S
|
||||
#define PICO_AUDIO_I2S_DATA_PIN 28
|
||||
#endif
|
||||
|
||||
#ifndef PICO_AUDIO_I2S_CLOCK_PIN_BASE
|
||||
// #warning PICO_AUDIO_I2S_CLOCK_PIN_BASE should be defined when using AUDIO_I2S
|
||||
#define PICO_AUDIO_I2S_CLOCK_PIN_BASE 26
|
||||
#endif
|
||||
|
||||
/** \brief Base configuration structure used when setting up
|
||||
* \ingroup pico_audio_i2s
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint32_t freq;
|
||||
uint32_t bps;
|
||||
uint8_t data_pin;
|
||||
uint8_t clock_pin_base;
|
||||
|
||||
} audio_i2s_config_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t freq;
|
||||
uint8_t pio_sm;
|
||||
uint8_t dma_channel;
|
||||
} shared_state_t;
|
||||
|
||||
typedef struct audio_format
|
||||
{
|
||||
uint32_t sample_freq; ///< Sample frequency in Hz
|
||||
uint16_t format; ///< Audio format \ref audio_formats
|
||||
uint16_t channel_count; ///< Number of channels
|
||||
} audio_format_t;
|
||||
|
||||
/** \brief Set up system to output I2S audio
|
||||
* \ingroup pico_audio_i2s
|
||||
*
|
||||
* \param intended_audio_format \todo
|
||||
* \param config The configuration to apply.
|
||||
*/
|
||||
void pico_i2s_setup(const audio_i2s_config_t *config);
|
||||
|
||||
/** \brief Set up system to output I2S audio
|
||||
* \ingroup pico_audio_i2s
|
||||
*
|
||||
* \param enable true to enable I2S audio, false to disable.
|
||||
*/
|
||||
void audio_i2s_set_enabled(bool enabled);
|
||||
|
||||
void audio_start_dma_transfer(const int32_t *bytes, uint32_t count);
|
||||
|
||||
#endif
|
||||
//----------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
#define AUDIO_BUFFER_FRAMES 48
|
||||
#define STEREO_BUFFER_SIZE AUDIO_BUFFER_FRAMES * 2
|
||||
#define AUDIO_BUFFER_FRAMES SIZE_OF_SAMPLES
|
||||
#define STEREO_BUFFER_SIZE AUDIO_BUFFER_FRAMES * 2 // for left + right
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
|
@ -177,16 +34,22 @@ extern "C"
|
|||
|
||||
typedef struct pio_i2s
|
||||
{
|
||||
int32_t output_buffer[STEREO_BUFFER_SIZE * 2]; // double buffering, so x2
|
||||
int32_t *out_ctrl_blocks[2];
|
||||
|
||||
PIO pio;
|
||||
uint8_t sm_mask;
|
||||
uint8_t sm_dout;
|
||||
uint dma_ch_out_ctrl;
|
||||
uint dma_ch_out_data;
|
||||
int32_t *out_ctrl_blocks[2];
|
||||
int32_t output_buffer[STEREO_BUFFER_SIZE * 2];
|
||||
int buffer_index;
|
||||
|
||||
} pio_i2s;
|
||||
|
||||
void i2s_program_start_synched(PIO pio, void (*dma_handler)(void), pio_i2s *i2s, audio_i2s_config_t *config);
|
||||
void i2s_program_setup(PIO pio, void (*dma_handler)(void), pio_i2s *i2s, const audio_i2s_config_t *config);
|
||||
|
||||
void i2s_start(pio_i2s *i2s);
|
||||
void i2s_stop(pio_i2s *i2s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,8 @@
|
|||
#include "ost_hal.h"
|
||||
#include "serializers.h"
|
||||
|
||||
#define SIZE_OF_SAMPLES (128) // in bytes
|
||||
|
||||
// Audio Double Buffer for DMA transfer
|
||||
int32_t audio_buf[2][SIZE_OF_SAMPLES];
|
||||
int32_t audio_buf[SIZE_OF_SAMPLES];
|
||||
|
||||
// int16_t audio_buf16[2][SIZE_OF_SAMPLES];
|
||||
// Audio Buffer for File Read
|
||||
|
|
@ -64,14 +62,10 @@ void audio_init(audio_ctx_t *ctx)
|
|||
|
||||
for (int i = 0; i < SIZE_OF_SAMPLES; i++)
|
||||
{
|
||||
audio_buf[0][i] = DAC_ZERO_VALUE;
|
||||
audio_buf[1][i] = DAC_ZERO_VALUE;
|
||||
|
||||
// audio_buf16[0][i] = DAC_ZERO_VALUE;
|
||||
// audio_buf16[1][i] = DAC_ZERO_VALUE;
|
||||
audio_buf[i] = DAC_ZERO_VALUE;
|
||||
}
|
||||
|
||||
ctx->dma_trans_number = SIZE_OF_SAMPLES / 4;
|
||||
ctx->transfer_size = SIZE_OF_SAMPLES / 4;
|
||||
}
|
||||
|
||||
static int list_chunk_is_info_type(audio_ctx_t *ctx)
|
||||
|
|
@ -188,7 +182,7 @@ static int get_audio_buf(audio_ctx_t *ctx, int32_t *buf_32b)
|
|||
{
|
||||
debug_printf("ERROR: f_read %d, data_offset = %d\n\r", (int)fr, (int)ctx->audio_info.data_offset);
|
||||
f_close(&ctx->fil);
|
||||
ctx->dma_trans_number = number / 4;
|
||||
ctx->transfer_size = number / 4;
|
||||
return 1;
|
||||
}
|
||||
if (ctx->audio_info.data_size <= ctx->audio_info.data_offset)
|
||||
|
|
@ -232,40 +226,31 @@ static int get_audio_buf(audio_ctx_t *ctx, int32_t *buf_32b)
|
|||
*/
|
||||
// ctx->audio_info.lvl_l = get_level(lvl_l / (number / 4));
|
||||
// ctx->audio_info.lvl_r = get_level(lvl_r / (number / 4));
|
||||
ctx->dma_trans_number = (number / 4); // 32 bytes tranfers
|
||||
ctx->transfer_size = (number / 4); // 32 bytes tranfers
|
||||
return _next_is_end;
|
||||
}
|
||||
|
||||
int audio_process(audio_ctx_t *ctx)
|
||||
{
|
||||
int nxt1 = (ctx->count & 0x1) ^ 0x1;
|
||||
int nxt2 = 1 - nxt1;
|
||||
// dma_flag_clear(DMA1, DMA_CH1, DMA_FLAG_FTF);
|
||||
// dma_channel_disable(DMA1, DMA_CH1);
|
||||
|
||||
ost_hal_audio_frame_end();
|
||||
|
||||
if (ctx->next_is_end)
|
||||
{
|
||||
ctx->playing = 0;
|
||||
ctx->pausing = 0;
|
||||
}
|
||||
// init_dma_i2s2(audio_buf[nxt1], dma_trans_number);
|
||||
// dma_channel_enable(DMA1, DMA_CH1);
|
||||
|
||||
ost_hal_audio_frame_start(audio_buf[nxt1], ctx->dma_trans_number);
|
||||
|
||||
if (ctx->playing && !ctx->pausing)
|
||||
{
|
||||
ctx->next_is_end = get_audio_buf(ctx, audio_buf[nxt2]);
|
||||
// Récupération du buffer audio à partir du disque
|
||||
ctx->next_is_end = get_audio_buf(ctx, &audio_buf[0]);
|
||||
ost_hal_audio_new_frame(&audio_buf[0], ctx->transfer_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < SIZE_OF_SAMPLES; i++)
|
||||
{
|
||||
audio_buf[nxt2][i] = DAC_ZERO_VALUE;
|
||||
audio_buf[i] = DAC_ZERO_VALUE;
|
||||
}
|
||||
ctx->dma_trans_number = SIZE_OF_SAMPLES / 4;
|
||||
ctx->transfer_size = SIZE_OF_SAMPLES / 4;
|
||||
}
|
||||
ctx->count++;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <ff.h>
|
||||
|
||||
#define SIZE_OF_SAMPLES (128) // in bytes
|
||||
#define FILENAME_MAX_SIZE 260
|
||||
|
||||
typedef struct
|
||||
|
|
@ -29,7 +30,7 @@ typedef struct
|
|||
{
|
||||
FIL fil;
|
||||
audio_info_type_t audio_info;
|
||||
int32_t dma_trans_number;
|
||||
int32_t transfer_size;
|
||||
uint16_t idx_play;
|
||||
int next_is_end;
|
||||
int playing;
|
||||
|
|
|
|||
|
|
@ -120,30 +120,6 @@ void UserTask_1(void *args)
|
|||
}
|
||||
}
|
||||
|
||||
void UserTask_2(void *args)
|
||||
{
|
||||
static ost_event_t wake_up;
|
||||
|
||||
wake_up.ev = 34;
|
||||
|
||||
while (1)
|
||||
{
|
||||
for (int i = 0; i < 65500; i++)
|
||||
{
|
||||
for (int j = 0; j < 300; j++)
|
||||
;
|
||||
}
|
||||
debug_printf("X\n");
|
||||
for (int i = 0; i < 65500; i++)
|
||||
{
|
||||
for (int j = 0; j < 100; j++)
|
||||
;
|
||||
}
|
||||
|
||||
qor_mbox_notify(&b, (void **)&wake_up, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================================================
|
||||
// SD CARD TASK
|
||||
// ===========================================================================================================
|
||||
|
|
@ -152,6 +128,8 @@ static uint32_t AudioStack[4096];
|
|||
|
||||
static qor_mbox_t AudioMailBox;
|
||||
|
||||
static ost_event_t wake_up;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t ev;
|
||||
|
|
@ -162,6 +140,8 @@ ost_audio_event_t audio_queue[10];
|
|||
// End of DMA transfer callback
|
||||
static void audio_callback(void)
|
||||
{
|
||||
qor_mbox_notify(&b, (void **)&wake_up, QOR_MBOX_OPTION_SEND_BACK);
|
||||
gpio_xor_mask(1 << 1);
|
||||
}
|
||||
|
||||
void AudioTask(void *args)
|
||||
|
|
@ -169,6 +149,8 @@ void AudioTask(void *args)
|
|||
picture_show("example.bmp");
|
||||
// ost_audio_play("out2.wav");
|
||||
|
||||
wake_up.ev = 34;
|
||||
|
||||
qor_mbox_init(&AudioMailBox, (void **)&audio_queue, 10);
|
||||
|
||||
ost_audio_register_callback(audio_callback);
|
||||
|
|
@ -182,37 +164,54 @@ void AudioTask(void *args)
|
|||
while (1)
|
||||
{
|
||||
|
||||
// Benchmark code
|
||||
#if 0
|
||||
if (onetime)
|
||||
{
|
||||
onetime = false;
|
||||
|
||||
gpio_put(1, 1);
|
||||
ost_audio_play("out2.wav");
|
||||
gpio_put(1, 0);
|
||||
|
||||
int isPlaying = 0;
|
||||
int count = 0;
|
||||
do
|
||||
{
|
||||
|
||||
gpio_put(1, 1);
|
||||
isPlaying = ost_audio_process();
|
||||
gpio_put(1, 0);
|
||||
count++;
|
||||
|
||||
} while (isPlaying);
|
||||
debug_printf("Packets: %d\r\n", count);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ost_event_t *e = NULL;
|
||||
// uint32_t res = qor_mbox_wait(&b, (void **)&e, 30);
|
||||
ost_audio_play("out2.wav");
|
||||
|
||||
// if (res == QOR_MBOX_OK)
|
||||
// {
|
||||
// }
|
||||
ost_event_t *e = NULL;
|
||||
|
||||
ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 0);
|
||||
qor_sleep(500);
|
||||
ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 1);
|
||||
qor_sleep(500);
|
||||
int isPlaying = 0;
|
||||
int count = 0;
|
||||
do
|
||||
{
|
||||
uint32_t res = qor_mbox_wait(&AudioMailBox, (void **)&e, 30); // On devrait recevoir un message toutes les 3ms (durée d'envoi d'un buffer I2S)
|
||||
|
||||
if (res == QOR_MBOX_OK)
|
||||
{
|
||||
isPlaying = ost_audio_process();
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
} while (isPlaying);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 0);
|
||||
qor_sleep(500);
|
||||
ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 1);
|
||||
qor_sleep(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,8 +107,7 @@ extern "C"
|
|||
// ----------------------------------------------------------------------------
|
||||
// AUDIO HAL
|
||||
// ----------------------------------------------------------------------------
|
||||
void ost_hal_audio_frame_end();
|
||||
void ost_hal_audio_frame_start(const volatile void *, int dma_trans_number);
|
||||
void ost_hal_audio_new_frame(const void *buffer, int size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,7 +221,6 @@ void qor_create_thread(qor_tcb_t *tcb, thread_func_t task, uint32_t *stack, uint
|
|||
|
||||
tcb->state = qor_tcb_state_active;
|
||||
tcb->wait_time = 0;
|
||||
tcb->state = qor_tcb_state_active;
|
||||
tcb->priority = priority;
|
||||
tcb->name = name;
|
||||
tcb->next = NULL;
|
||||
|
|
@ -341,7 +340,10 @@ void qor_scheduler(void)
|
|||
}
|
||||
else if (best_sleeping != NULL)
|
||||
{
|
||||
// On va réveiller un endormi, car son temps d'attente est dépassé (ok ou timeout, ça dépend si c'est un sleep volontaire ou attente de mailbox dépassée)
|
||||
RunPt = best_sleeping;
|
||||
RunPt->state = qor_tcb_state_active; // devient actif
|
||||
RunPt->ts = 0; // means timeout
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -382,6 +384,7 @@ void qor_mbox_init(qor_mbox_t *mbox, void **msgBuffer, uint32_t maxCount)
|
|||
mbox->read = 0;
|
||||
mbox->read = 0;
|
||||
mbox->head = NULL;
|
||||
mbox->count = 0;
|
||||
}
|
||||
|
||||
uint32_t qor_mbox_wait(qor_mbox_t *mbox, void **msg, uint32_t wait_ms)
|
||||
|
|
@ -396,6 +399,13 @@ uint32_t qor_mbox_wait(qor_mbox_t *mbox, void **msg, uint32_t wait_ms)
|
|||
RunPt->mbox = mbox;
|
||||
mbox->head = RunPt;
|
||||
qor_sleep(wait_ms);
|
||||
|
||||
disable_irq();
|
||||
if (RunPt->ts == 0)
|
||||
{
|
||||
enable_irq();
|
||||
return QOR_MBOX_TIMEOUT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -445,11 +455,12 @@ uint32_t qor_mbox_notify(qor_mbox_t *mbox, void *msg, uint32_t notifyOption)
|
|||
}
|
||||
mbox->count++;
|
||||
|
||||
// We warn all waiting threads that a new message is available
|
||||
// We warn waiting thread that a new message is available
|
||||
qor_tcb_t *t = mbox->head;
|
||||
if (t != NULL)
|
||||
{
|
||||
t->wait_time = 0;
|
||||
t->state = qor_tcb_state_active; // force wake up
|
||||
}
|
||||
|
||||
enable_irq();
|
||||
|
|
|
|||
|
|
@ -84,8 +84,9 @@ typedef struct
|
|||
} mbox_stats_t;
|
||||
|
||||
#define QOR_MBOX_OK 1
|
||||
#define QOR_MBOX_ERROR 2
|
||||
#define QOR_MBOX_FULL 3
|
||||
#define QOR_MBOX_TIMEOUT 2
|
||||
#define QOR_MBOX_ERROR 3
|
||||
#define QOR_MBOX_FULL 4
|
||||
|
||||
void qor_mbox_init(qor_mbox_t *mbox, void **msgBuffer, uint32_t maxCount);
|
||||
uint32_t qor_mbox_wait(qor_mbox_t *mbox, void **msg, uint32_t wait_ms);
|
||||
|
|
|
|||
|
|
@ -20,46 +20,6 @@
|
|||
@ The Cortex-M0 is limited on the thumb number of instructionss
|
||||
@ the context restore is therefore a bit more complex that M3 or M4 CPU
|
||||
@ One limitation is the POP instruction that cannot access to the HI registers (registers after R7)
|
||||
@
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@ | |
|
||||
@ -------------
|
||||
@
|
||||
@
|
||||
@
|
||||
@
|
||||
|
||||
|
||||
qor_go:
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue