11#include < math.h>
2- #include < cstring>
3-
2+ #include < cstring>
3+ #include < cstddef>
4+
5+ #include " pico.h"
6+ #include " hardware/address_mapped.h"
7+ #include " hardware/structs/pio.h"
8+ #include " hardware/gpio.h"
9+ #include " hardware/regs/dreq.h"
10+ #include " hardware/pio_instructions.h"
411#include " hardware/dma.h"
512#include " hardware/irq.h"
613#include " hardware/adc.h"
@@ -48,10 +55,10 @@ static uint32_t audio_dma_channel;
4855namespace pimoroni {
4956
5057 GalacticUnicorn* GalacticUnicorn::unicorn = nullptr ;
51- PIO GalacticUnicorn::bitstream_pio = pio0 ;
58+ PIO GalacticUnicorn::bitstream_pio = nullptr ;
5259 uint GalacticUnicorn::bitstream_sm = 0 ;
5360 uint GalacticUnicorn::bitstream_sm_offset = 0 ;
54- PIO GalacticUnicorn::audio_pio = pio0 ;
61+ PIO GalacticUnicorn::audio_pio = nullptr ;
5562 uint GalacticUnicorn::audio_sm = 0 ;
5663 uint GalacticUnicorn::audio_sm_offset = 0 ;
5764
@@ -75,13 +82,15 @@ namespace pimoroni {
7582 pio_sm_unclaim (bitstream_pio, bitstream_sm);
7683 pio_remove_program (bitstream_pio, &galactic_unicorn_program, bitstream_sm_offset);
7784
78- dma_channel_unclaim (audio_dma_channel); // This works now the teardown behaves correctly
79- pio_sm_unclaim (audio_pio, audio_sm);
80- pio_remove_program (audio_pio, &audio_i2s_program, audio_sm_offset);
81- // irq_remove_handler(DMA_IRQ_0, dma_complete);
82- irq_remove_handler (DMA_IRQ_1, dma_complete);
85+ if (audio_rate > 0 ) {
86+ dma_channel_unclaim (audio_dma_channel); // This works now the teardown behaves correctly
87+ pio_sm_unclaim (audio_pio, audio_sm);
88+ pio_remove_program (audio_pio, &audio_i2s_program, audio_sm_offset);
89+ // irq_remove_handler(DMA_IRQ_0, dma_complete);
90+ irq_remove_handler (DMA_IRQ_1, dma_complete);
8391
84- unicorn = nullptr ;
92+ unicorn = nullptr ;
93+ }
8594 }
8695 }
8796
@@ -103,29 +112,37 @@ namespace pimoroni {
103112 dma_safe_abort (dma_channel);
104113
105114
106- // Stop the audio SM
107- pio_sm_set_enabled (audio_pio, audio_sm, false );
108-
109- // Reset the I2S pins to avoid popping when audio is suddenly stopped
110- const uint pins_to_clear = 1 << I2S_DATA | 1 << I2S_BCLK | 1 << I2S_LRCLK;
111- pio_sm_set_pins_with_mask (audio_pio, audio_sm, 0 , pins_to_clear);
112-
113- // Abort any in-progress DMA transfer
114- dma_safe_abort (audio_dma_channel);
115+ // Stop the audio SM if enabled
116+ if (audio_rate > 0 ) {
117+ pio_sm_set_enabled (audio_pio, audio_sm, false );
118+
119+ // Reset the I2S pins to avoid popping when audio is suddenly stopped
120+ const uint pins_to_clear = 1 << I2S_DATA | 1 << I2S_BCLK | 1 << I2S_LRCLK;
121+ pio_sm_set_pins_with_mask (audio_pio, audio_sm, 0 , pins_to_clear);
122+
123+ // Abort any in-progress DMA transfer
124+ dma_safe_abort (audio_dma_channel);
125+ }
115126 }
116127
117128 uint16_t GalacticUnicorn::light () {
118129 adc_select_input (2 );
119130 return adc_read ();
120131 }
121132
122- void GalacticUnicorn::init () {
133+ void GalacticUnicorn::init (uint audio_rate_, PIO pio ) {
123134
124135 if (unicorn != nullptr ) {
125136 // Tear down the old GU instance's hardware resources
126137 partial_teardown ();
127138 }
128-
139+
140+ audio_rate = audio_rate_;
141+ bitstream_pio = pio;
142+ if (audio_rate > 0 ) {
143+ audio_pio = pio;
144+ }
145+
129146 // for each row:
130147 // for each bcd frame:
131148 // 0: 00110110 // row pixel count (minus one)
@@ -233,7 +250,6 @@ namespace pimoroni {
233250 gpio_init (SWITCH_VOLUME_DOWN); gpio_pull_up (SWITCH_VOLUME_DOWN);
234251
235252 // setup the pio if it has not previously been set up
236- bitstream_pio = pio0;
237253 if (unicorn == nullptr ) {
238254 bitstream_sm = pio_claim_unused_sm (bitstream_pio, true );
239255 bitstream_sm_offset = pio_add_program (bitstream_pio, &galactic_unicorn_program);
@@ -310,38 +326,38 @@ namespace pimoroni {
310326 // start the control channel
311327 dma_start_channel_mask (1u << dma_ctrl_channel);
312328
313-
314- // setup audio pio program
315- audio_pio = pio0;
316- if (unicorn == nullptr ) {
317- audio_sm = pio_claim_unused_sm (audio_pio, true );
318- audio_sm_offset = pio_add_program (audio_pio, &audio_i2s_program);
319- }
320-
321- pio_gpio_init (audio_pio, I2S_DATA );
322- pio_gpio_init (audio_pio, I2S_BCLK );
323- pio_gpio_init (audio_pio, I2S_LRCLK);
324-
325- audio_i2s_program_init (audio_pio, audio_sm, audio_sm_offset, I2S_DATA, I2S_BCLK );
326- uint32_t system_clock_frequency = clock_get_hz (clk_sys);
327- uint32_t divider = system_clock_frequency * 4 / SYSTEM_FREQ; // avoid arithmetic overflow
328- pio_sm_set_clkdiv_int_frac (audio_pio, audio_sm, divider >> 8u , divider & 0xffu );
329-
330- audio_dma_channel = dma_claim_unused_channel ( true );
331- dma_channel_config audio_config = dma_channel_get_default_config (audio_dma_channel );
332- channel_config_set_transfer_data_size (&audio_config, DMA_SIZE_16);
333- // channel_config_set_bswap (&audio_config, false); // byte swap to reverse little endian
334- channel_config_set_dreq ( &audio_config, pio_get_dreq ( audio_pio, audio_sm, true ) );
335- dma_channel_configure (audio_dma_channel, &audio_config, &audio_pio-> txf [audio_sm], NULL , 0 , false );
336-
337- // dma_channel_set_irq0_enabled (audio_dma_channel, true);
338- dma_channel_set_irq1_enabled (audio_dma_channel, true );
339-
340- if (unicorn == nullptr ) {
341- // irq_add_shared_handler (DMA_IRQ_0, dma_complete, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY );
342- // irq_set_enabled(DMA_IRQ_0, true );
343- irq_add_shared_handler (DMA_IRQ_1, dma_complete, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY );
344- irq_set_enabled (DMA_IRQ_1, true );
329+ // setup audio pio program if enabled
330+ if (audio_rate > 0 ) {
331+ if (unicorn == nullptr ) {
332+ audio_sm = pio_claim_unused_sm (audio_pio, true );
333+ audio_sm_offset = pio_add_program (audio_pio, &audio_i2s_program );
334+ }
335+
336+ pio_gpio_init (audio_pio, I2S_DATA);
337+ pio_gpio_init (audio_pio, I2S_BCLK );
338+ pio_gpio_init (audio_pio, I2S_LRCLK );
339+
340+ audio_i2s_program_init (audio_pio, audio_sm, audio_sm_offset, I2S_DATA, I2S_BCLK);
341+ uint32_t system_clock_frequency = clock_get_hz (clk_sys );
342+ uint32_t divider = system_clock_frequency * 4 / audio_rate; // avoid arithmetic overflow
343+ pio_sm_set_clkdiv_int_frac (audio_pio, audio_sm, divider >> 8u , divider & 0xffu );
344+
345+ audio_dma_channel = dma_claim_unused_channel ( true );
346+ dma_channel_config audio_config = dma_channel_get_default_config (audio_dma_channel );
347+ channel_config_set_transfer_data_size (&audio_config, DMA_SIZE_16 );
348+ // channel_config_set_bswap (&audio_config, false); // byte swap to reverse little endian
349+ channel_config_set_dreq (&audio_config, pio_get_dreq (audio_pio, audio_sm, true ));
350+ dma_channel_configure (audio_dma_channel, &audio_config, & audio_pio-> txf [audio_sm], NULL , 0 , false );
351+
352+ // dma_channel_set_irq0_enabled(audio_dma_channel, true);
353+ dma_channel_set_irq1_enabled (audio_dma_channel, true );
354+
355+ if (unicorn == nullptr ) {
356+ // irq_add_shared_handler(DMA_IRQ_0, dma_complete, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
357+ // irq_set_enabled (DMA_IRQ_0, true );
358+ irq_add_shared_handler (DMA_IRQ_1, dma_complete, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY );
359+ irq_set_enabled (DMA_IRQ_1, true );
360+ }
345361 }
346362
347363 unicorn = this ;
@@ -379,6 +395,8 @@ namespace pimoroni {
379395 }
380396
381397 void GalacticUnicorn::play_sample (uint8_t *data, uint32_t length) {
398+ if (audio_rate == 0 ) { return ; }
399+
382400 stop_playing ();
383401
384402 if (unicorn == this ) {
@@ -390,6 +408,8 @@ namespace pimoroni {
390408 }
391409
392410 void GalacticUnicorn::play_synth () {
411+ if (audio_rate == 0 ) { return ; }
412+
393413 if (play_mode != PLAYING_SYNTH) {
394414 stop_playing ();
395415 }
@@ -410,6 +430,8 @@ namespace pimoroni {
410430 }
411431
412432 void GalacticUnicorn::next_audio_sequence () {
433+ if (audio_rate == 0 ) { return ; }
434+
413435 // Clear any interrupt request caused by our channel
414436 // dma_channel_acknowledge_irq0(audio_dma_channel);
415437 // NOTE Temporary replacement of the above until this reaches pico-sdk main:
@@ -429,14 +451,16 @@ namespace pimoroni {
429451 }
430452
431453 void GalacticUnicorn::populate_next_synth () {
454+ if (audio_rate == 0 ) { return ; }
455+
432456 int16_t *samples = tone_buffers[current_buffer];
433457 for (uint i = 0 ; i < TONE_BUFFER_SIZE; i++) {
434458 samples[i] = synth.get_audio_frame ();
435459 }
436460 }
437461
438462 void GalacticUnicorn::stop_playing () {
439- if (unicorn == this ) {
463+ if (unicorn == this && audio_rate != 0 ) {
440464 // Stop the audio SM
441465 pio_sm_set_enabled (audio_pio, audio_sm, false );
442466
0 commit comments