Skip to content

Commit

Permalink
Making Bluetooth Great Again
Browse files Browse the repository at this point in the history
GalacticUnicorn init now takes optional audio rate and pio to allow audio to be disabled and pio selected to avoid clashes.
Fixing Bluetooth device name which was broken by some carelessness in cmake renovation.
  • Loading branch information
Kevin J Walters committed Dec 30, 2023
1 parent 72b8e66 commit c4985d3
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 68 deletions.
11 changes: 8 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,23 @@ target_link_libraries(${NAME2}
hardware_dma
)

message(NOTICE "Display: ${DISPLAY_NAME}")
# Create the Bluetooth device name with upper case first character
string(SUBSTRING ${UNICORN_MODEL} 0 1 FIRST_LETTER)
string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
string(REGEX REPLACE "^.(.*)" "${FIRST_LETTER}\\1 Unicorn" BLUETOOTH_DEVICE_NAME "${UNICORN_MODEL}")

message(NOTICE "Bluetooth device name: ${BLUETOOTH_DEVICE_NAME}")

target_compile_definitions(${NAME1} PRIVATE
PICO_AUDIO_I2S_DATA_PIN=9
PICO_AUDIO_I2S_CLOCK_PIN_BASE=10
BLUETOOTH_DEVICE_NAME="${DISPLAY_NAME}"
BLUETOOTH_DEVICE_NAME="${BLUETOOTH_DEVICE_NAME}"
AUDIO_SOURCES=AudioSourceBluetooth
)
target_compile_definitions(${NAME2} PRIVATE
PICO_AUDIO_I2S_DATA_PIN=9
PICO_AUDIO_I2S_CLOCK_PIN_BASE=10
BLUETOOTH_DEVICE_NAME="${DISPLAY_NAME}"
BLUETOOTH_DEVICE_NAME="${BLUETOOTH_DEVICE_NAME}"
AUDIO_SOURCES=AudioSourceDMAADC
)

Expand Down
134 changes: 79 additions & 55 deletions libraries/galactic_unicorn/galactic_unicorn.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
#include <math.h>
#include <cstring>

#include <cstring>
#include <cstddef>

#include "pico.h"
#include "hardware/address_mapped.h"
#include "hardware/structs/pio.h"
#include "hardware/gpio.h"
#include "hardware/regs/dreq.h"
#include "hardware/pio_instructions.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "hardware/adc.h"
Expand Down Expand Up @@ -48,10 +55,10 @@ static uint32_t audio_dma_channel;
namespace pimoroni {

GalacticUnicorn* GalacticUnicorn::unicorn = nullptr;
PIO GalacticUnicorn::bitstream_pio = pio0;
PIO GalacticUnicorn::bitstream_pio = nullptr;
uint GalacticUnicorn::bitstream_sm = 0;
uint GalacticUnicorn::bitstream_sm_offset = 0;
PIO GalacticUnicorn::audio_pio = pio0;
PIO GalacticUnicorn::audio_pio = nullptr;
uint GalacticUnicorn::audio_sm = 0;
uint GalacticUnicorn::audio_sm_offset = 0;

Expand All @@ -75,13 +82,15 @@ namespace pimoroni {
pio_sm_unclaim(bitstream_pio, bitstream_sm);
pio_remove_program(bitstream_pio, &galactic_unicorn_program, bitstream_sm_offset);

dma_channel_unclaim(audio_dma_channel); // This works now the teardown behaves correctly
pio_sm_unclaim(audio_pio, audio_sm);
pio_remove_program(audio_pio, &audio_i2s_program, audio_sm_offset);
//irq_remove_handler(DMA_IRQ_0, dma_complete);
irq_remove_handler(DMA_IRQ_1, dma_complete);
if (audio_rate > 0) {
dma_channel_unclaim(audio_dma_channel); // This works now the teardown behaves correctly
pio_sm_unclaim(audio_pio, audio_sm);
pio_remove_program(audio_pio, &audio_i2s_program, audio_sm_offset);
//irq_remove_handler(DMA_IRQ_0, dma_complete);
irq_remove_handler(DMA_IRQ_1, dma_complete);

unicorn = nullptr;
unicorn = nullptr;
}
}
}

Expand All @@ -103,29 +112,37 @@ namespace pimoroni {
dma_safe_abort(dma_channel);


// Stop the audio SM
pio_sm_set_enabled(audio_pio, audio_sm, false);

// Reset the I2S pins to avoid popping when audio is suddenly stopped
const uint pins_to_clear = 1 << I2S_DATA | 1 << I2S_BCLK | 1 << I2S_LRCLK;
pio_sm_set_pins_with_mask(audio_pio, audio_sm, 0, pins_to_clear);

// Abort any in-progress DMA transfer
dma_safe_abort(audio_dma_channel);
// Stop the audio SM if enabled
if (audio_rate > 0) {
pio_sm_set_enabled(audio_pio, audio_sm, false);

// Reset the I2S pins to avoid popping when audio is suddenly stopped
const uint pins_to_clear = 1 << I2S_DATA | 1 << I2S_BCLK | 1 << I2S_LRCLK;
pio_sm_set_pins_with_mask(audio_pio, audio_sm, 0, pins_to_clear);

// Abort any in-progress DMA transfer
dma_safe_abort(audio_dma_channel);
}
}

uint16_t GalacticUnicorn::light() {
adc_select_input(2);
return adc_read();
}

void GalacticUnicorn::init() {
void GalacticUnicorn::init(uint audio_rate_, PIO pio) {

if(unicorn != nullptr) {
// Tear down the old GU instance's hardware resources
partial_teardown();
}


audio_rate = audio_rate_;
bitstream_pio = pio;
if (audio_rate > 0) {
audio_pio = pio;
}

// for each row:
// for each bcd frame:
// 0: 00110110 // row pixel count (minus one)
Expand Down Expand Up @@ -233,7 +250,6 @@ namespace pimoroni {
gpio_init(SWITCH_VOLUME_DOWN); gpio_pull_up(SWITCH_VOLUME_DOWN);

// setup the pio if it has not previously been set up
bitstream_pio = pio0;
if(unicorn == nullptr) {
bitstream_sm = pio_claim_unused_sm(bitstream_pio, true);
bitstream_sm_offset = pio_add_program(bitstream_pio, &galactic_unicorn_program);
Expand Down Expand Up @@ -310,38 +326,38 @@ namespace pimoroni {
// start the control channel
dma_start_channel_mask(1u << dma_ctrl_channel);


// setup audio pio program
audio_pio = pio0;
if(unicorn == nullptr) {
audio_sm = pio_claim_unused_sm(audio_pio, true);
audio_sm_offset = pio_add_program(audio_pio, &audio_i2s_program);
}

pio_gpio_init(audio_pio, I2S_DATA);
pio_gpio_init(audio_pio, I2S_BCLK);
pio_gpio_init(audio_pio, I2S_LRCLK);

audio_i2s_program_init(audio_pio, audio_sm, audio_sm_offset, I2S_DATA, I2S_BCLK);
uint32_t system_clock_frequency = clock_get_hz(clk_sys);
uint32_t divider = system_clock_frequency * 4 / SYSTEM_FREQ; // avoid arithmetic overflow
pio_sm_set_clkdiv_int_frac(audio_pio, audio_sm, divider >> 8u, divider & 0xffu);

audio_dma_channel = dma_claim_unused_channel(true);
dma_channel_config audio_config = dma_channel_get_default_config(audio_dma_channel);
channel_config_set_transfer_data_size(&audio_config, DMA_SIZE_16);
//channel_config_set_bswap(&audio_config, false); // byte swap to reverse little endian
channel_config_set_dreq(&audio_config, pio_get_dreq(audio_pio, audio_sm, true));
dma_channel_configure(audio_dma_channel, &audio_config, &audio_pio->txf[audio_sm], NULL, 0, false);

//dma_channel_set_irq0_enabled(audio_dma_channel, true);
dma_channel_set_irq1_enabled(audio_dma_channel, true);

if(unicorn == nullptr) {
//irq_add_shared_handler(DMA_IRQ_0, dma_complete, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
//irq_set_enabled(DMA_IRQ_0, true);
irq_add_shared_handler(DMA_IRQ_1, dma_complete, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
irq_set_enabled(DMA_IRQ_1, true);
// setup audio pio program if enabled
if (audio_rate > 0) {
if(unicorn == nullptr) {
audio_sm = pio_claim_unused_sm(audio_pio, true);
audio_sm_offset = pio_add_program(audio_pio, &audio_i2s_program);
}

pio_gpio_init(audio_pio, I2S_DATA);
pio_gpio_init(audio_pio, I2S_BCLK);
pio_gpio_init(audio_pio, I2S_LRCLK);

audio_i2s_program_init(audio_pio, audio_sm, audio_sm_offset, I2S_DATA, I2S_BCLK);
uint32_t system_clock_frequency = clock_get_hz(clk_sys);
uint32_t divider = system_clock_frequency * 4 / audio_rate; // avoid arithmetic overflow
pio_sm_set_clkdiv_int_frac(audio_pio, audio_sm, divider >> 8u, divider & 0xffu);

audio_dma_channel = dma_claim_unused_channel(true);
dma_channel_config audio_config = dma_channel_get_default_config(audio_dma_channel);
channel_config_set_transfer_data_size(&audio_config, DMA_SIZE_16);
//channel_config_set_bswap(&audio_config, false); // byte swap to reverse little endian
channel_config_set_dreq(&audio_config, pio_get_dreq(audio_pio, audio_sm, true));
dma_channel_configure(audio_dma_channel, &audio_config, &audio_pio->txf[audio_sm], NULL, 0, false);

//dma_channel_set_irq0_enabled(audio_dma_channel, true);
dma_channel_set_irq1_enabled(audio_dma_channel, true);

if(unicorn == nullptr) {
//irq_add_shared_handler(DMA_IRQ_0, dma_complete, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
//irq_set_enabled(DMA_IRQ_0, true);
irq_add_shared_handler(DMA_IRQ_1, dma_complete, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
irq_set_enabled(DMA_IRQ_1, true);
}
}

unicorn = this;
Expand Down Expand Up @@ -379,6 +395,8 @@ namespace pimoroni {
}

void GalacticUnicorn::play_sample(uint8_t *data, uint32_t length) {
if (audio_rate == 0) { return; }

stop_playing();

if(unicorn == this) {
Expand All @@ -390,6 +408,8 @@ namespace pimoroni {
}

void GalacticUnicorn::play_synth() {
if (audio_rate == 0) { return; }

if(play_mode != PLAYING_SYNTH) {
stop_playing();
}
Expand All @@ -410,6 +430,8 @@ namespace pimoroni {
}

void GalacticUnicorn::next_audio_sequence() {
if (audio_rate == 0) { return; }

// Clear any interrupt request caused by our channel
//dma_channel_acknowledge_irq0(audio_dma_channel);
// NOTE Temporary replacement of the above until this reaches pico-sdk main:
Expand All @@ -429,14 +451,16 @@ namespace pimoroni {
}

void GalacticUnicorn::populate_next_synth() {
if (audio_rate == 0) { return; }

int16_t *samples = tone_buffers[current_buffer];
for(uint i = 0; i < TONE_BUFFER_SIZE; i++) {
samples[i] = synth.get_audio_frame();
}
}

void GalacticUnicorn::stop_playing() {
if(unicorn == this) {
if(unicorn == this && audio_rate != 0) {
// Stop the audio SM
pio_sm_set_enabled(audio_pio, audio_sm, false);

Expand Down
14 changes: 11 additions & 3 deletions libraries/galactic_unicorn/galactic_unicorn.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
#pragma once

#include "pico.h"
#include "hardware/address_mapped.h"
#include "hardware/structs/pio.h"
#include "hardware/gpio.h"
#include "hardware/regs/dreq.h"
#include "hardware/pio_instructions.h"
#include "hardware/pio.h"

#include "pico_graphics.hpp"
#include "common/pimoroni_common.hpp"
#include "../pico_synth/pico_synth.hpp"
Expand Down Expand Up @@ -53,7 +60,7 @@ namespace pimoroni {
static const uint32_t BCD_FRAME_BYTES = 60;
static const uint32_t ROW_BYTES = BCD_FRAME_COUNT * BCD_FRAME_BYTES;
static const uint32_t BITSTREAM_LENGTH = (ROW_COUNT * ROW_BYTES);
static const uint SYSTEM_FREQ = 22050;
static const uint DEFAULT_SYSTEM_FREQ = 22050;

private:
static PIO bitstream_pio;
Expand All @@ -64,6 +71,7 @@ namespace pimoroni {
static uint audio_sm;
static uint audio_sm_offset;

uint audio_rate = 0;
uint16_t brightness = 256;
uint16_t volume = 127;

Expand Down Expand Up @@ -91,7 +99,7 @@ namespace pimoroni {
public:
~GalacticUnicorn();

void init();
void init(uint audio_rate_ = DEFAULT_SYSTEM_FREQ, PIO = pio0);
static inline void pio_program_init(PIO pio, uint sm, uint offset);

void clear();
Expand Down Expand Up @@ -126,4 +134,4 @@ namespace pimoroni {
void populate_next_synth();
};

}
}
Expand Down
8 changes: 8 additions & 0 deletions uad/audiosourcebluetooth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ static audio_buffer_pool_t *init_audio(uint32_t sample_frequency, uint8_t channe
// num channels requested by application
btstack_audio_pico_channel_count = channel_count;

printf("init_audio 1\n");
// This is the output format for i2s and is passed to pico-extras audio_i2s_setup
// always use stereo
btstack_audio_pico_audio_format.format = AUDIO_BUFFER_FORMAT_PCM_S16;
Expand All @@ -88,17 +89,24 @@ static audio_buffer_pool_t *init_audio(uint32_t sample_frequency, uint8_t channe
config.clock_pin_base = PICO_AUDIO_I2S_CLOCK_PIN_BASE;
config.dma_channel = (int8_t) dma_claim_unused_channel(true);
config.pio_sm = 0;
printf("init_audio 2\n");
sleep_ms(100);

// audio_i2s_setup claims the channel again https://github.com/raspberrypi/pico-extras/issues/48
dma_channel_unclaim(config.dma_channel);
printf("init_audio 3\n");
sleep_ms(100);
const audio_format_t * output_format = audio_i2s_setup(&btstack_audio_pico_audio_format, &config);
if (!output_format) {
panic("PicoAudio: Unable to open audio device.\n");
}
printf("init_audio 4\n");
sleep_ms(100);

bool ok = audio_i2s_connect(producer_pool);
assert(ok);
(void)ok;
printf("init_audio 5\n");

return producer_pool;
}
Expand Down
Loading

0 comments on commit c4985d3

Please sign in to comment.