Skip to content

Commit

Permalink
General tidyup, better LED behaviour.
Browse files Browse the repository at this point in the history
  • Loading branch information
Gadgetoid committed Aug 16, 2024
1 parent 64879ee commit 8950ccd
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 81 deletions.
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
# Pico TinyUSB i2s Speaker
# Picade Max: Audio And Volume Control Board

A USB speaker firmware for the RP2040/Pico using TinyUSB and i2s.
Firmware for the Picade Max audio board. Includes encoder volume control with
push-button mute.

## Status LED

* Red - Lights up when board is muted
* Green - Should idle blink at 1s intervals, and flash quickly when audio is streaming
* Blue - Brightness indicates volume

## Updating the firmware for the board

Push the volume button in for 2 seconds and hold.

Alternatively run `echo "multiverse:_usb" > /dev/serial/by-id/usb-Pimoroni_Picade_USB_Audio_*-if02` via SSH or a terminal.

This will put the audio board into bootloader mode.

If you're using Recalbox, you may see a pop up that states a new USB device named RPI-RP2 has been discovered and asks you if you wish to initialise. You can ignore this screen.
If you're using Recalbox, you may see a pop up that states a new USB device named RPI-RP2 has been discovered and asks you if you wish to initialise. You can ignore this screen.

If you don't have any other external USB devices plugged in, you should be able to access the bootloader at:

Expand Down
120 changes: 42 additions & 78 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
int system_volume = 255;
int volume_speed = 10;

uint8_t led_red = 0;
uint8_t led_green = 0;
uint8_t led_blue = 0;

// Audio controls
// Current states
int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
Expand All @@ -115,7 +119,7 @@ uint8_t command_buffer[COMMAND_LEN];
std::string_view command((const char *)command_buffer, COMMAND_LEN);


void led_blinking_task(void);
void led_task(void);
void audio_task(void);
void usb_serial_init(void);
uint cdc_task(uint8_t *buf, size_t buf_len);
Expand Down Expand Up @@ -202,34 +206,12 @@ void serial_task(void) {
int main(void)
{

/*
gpio_init(LED_R);
gpio_set_function(LED_R, GPIO_FUNC_SIO);
gpio_set_dir(LED_R, GPIO_OUT);
gpio_put(LED_R, 1);
gpio_init(LED_G);
gpio_set_function(LED_G, GPIO_FUNC_SIO);
gpio_set_dir(LED_G, GPIO_OUT);
gpio_put(LED_G, 1);
gpio_init(LED_B);
gpio_set_function(LED_B, GPIO_FUNC_SIO);
gpio_set_dir(LED_B, GPIO_OUT);
gpio_put(LED_B, 1);
*/

/* HACK: To allow testing on a basic Pico with a button on Pin 12
gpio_init(PICO_UNICORN_A);
gpio_set_function(PICO_UNICORN_A, GPIO_FUNC_SIO);
gpio_set_dir(PICO_UNICORN_A, GPIO_IN);
gpio_pull_up(PICO_UNICORN_A);
*/

system_init();

board_init();

// Fetch the Pico serial (actually the flash chip ID) into `usb_serial`
// This has nothing to do with CDC serial!
usb_serial_init();

// init device stack on configured roothub port
Expand All @@ -238,15 +220,14 @@ int main(void)
i2s_audio_init();
i2s_audio_start();

TU_LOG1("Headset running\r\n");
TU_LOG1("Picade Max Audio Running\r\n");

while (1)
{
tud_task(); // TinyUSB device task
tud_task();
audio_task();
serial_task();
//led_blinking_task();
//i2s_audio_give_buffer(NULL, 0);
led_task();
}
}

Expand Down Expand Up @@ -387,6 +368,7 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req
}

// Helper for feature unit set requests
// This handles volume control and mute requests coming from the USB host to Picade Max Audio
static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf)
{
(void)rhport;
Expand All @@ -402,11 +384,8 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req

TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]);

if(mute[request->bChannelNumber]) {
system_led(255, 0, 0);
} else {
system_led(0, 0, 0);
}
// Set the red LED channel to indicate mute
led_red = mute[request->bChannelNumber] ? 255 : 0;

return true;
}
Expand All @@ -416,7 +395,8 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req

volume[request->bChannelNumber] = tu_le16toh(((audio_control_cur_2_t const *)buf)->bCur);

system_led(0, 0, MIN(255, volume[request->bChannelNumber] / 100));
// Set the blue LED channel to indicate volume
led_blue = MIN(255, volume[request->bChannelNumber] / 100);

system_volume = MIN(255u, volume[request->bChannelNumber] / 100);

Expand Down Expand Up @@ -532,24 +512,9 @@ void audio_task(void)
static uint32_t start_ms = 0;
uint32_t volume_interval_ms = 50;

// When new data arrived, copy data from speaker buffer, to microphone buffer
// and send it over
// Only support speaker & headphone both have the same resolution
// If one is 16bit another is 24bit be care of LOUD noise !
if (spk_data_size)
{
//led_b_state = !led_b_state;
//gpio_put(LED_B, led_b_state);

// "Hardware" volume is 0 - 100 in steps of 256, with a maximum value of 25600

//unsigned int current_volume = (unsigned int)volume[0] * volume_ramp[(uint8_t)system_volume] / 25600;

/*int current_volume = volume[0] / 100;
if (current_volume > 256) current_volume = 256;
if (current_volume < 0) current_volume = 0;
current_volume = volume_ramp[current_volume];*/

int current_volume = volume_ramp[system_volume];

if (mute[0]) {
Expand All @@ -560,18 +525,29 @@ void audio_task(void)
spk_data_size = 0;
}

// Only handle volume control changes every volume_interval_ms
// The encoder driver should - I believe - asynchronously gather a delta to be handled here
if (board_millis() - start_ms >= volume_interval_ms)
{
// This is just the raw delta from the encoder
int32_t volume_delta = get_volume_delta();

// Adjust the speed of volume control (number of volume steps per encoder turn)
volume_delta *= volume_speed;

// Long press triggers reset to bootloader
handle_mute_button_held();

if(get_mute_button_pressed()) {
// Toggle one channel and copy the mute value to the other
// We don't want to end up with one muted and one unmuted somehow...
mute[0] = !mute[0];
mute[1] = !mute[1];
mute[1] = mute[0];

// Illuminate the LED red if muted
led_red = mute[0] ? 255 : 0;

// Mute was changed
// Mute was changed - notify the host with an interrupt
// 6.1 Interrupt Data Message
const audio_interrupt_data_t data = {
.bInfo = 0, // Class-specific interrupt, originated from an interface
Expand All @@ -583,20 +559,13 @@ void audio_task(void)
};

tud_audio_int_write(&data);
// Call tud_task to handle the interrupt to host
tud_task();
}
/*if(volume_delta > 0) {
system_led(0, 255, 0);
} else if (volume_delta < 0) {
system_led(0, 0, 255);
} else {
system_led(0, 0, 0);
}*/

volume_delta *= volume_speed;

int old_system_volume = system_volume;


if(volume_delta + system_volume > 255) {
system_volume = 255;
} else if (volume_delta + system_volume < 0) {
Expand All @@ -605,21 +574,13 @@ void audio_task(void)
system_volume += volume_delta;
}

/* HACK: To allow testing on a basic Pico with a button on Pin 12
if(!gpio_get(PICO_UNICORN_A)) {
system_volume ++;
system_volume %= 255;
volume_delta = 1;
}
*/

if(system_volume != old_system_volume) {
system_led(0, system_volume, 0);
led_blue = system_volume;

volume[0] = system_volume * 100;
volume[1] = system_volume * 100;

// Volume has changed
// Volume has changed - notify the host with an interrupt
// 6.1 Interrupt Data Message
const audio_interrupt_data_t data = {
.bInfo = 0, // Class-specific interrupt, originated from an interface
Expand All @@ -631,6 +592,8 @@ void audio_task(void)
};

tud_audio_int_write(&data);
// Call tud_task to handle the interrupt to host
tud_task();
}

start_ms += volume_interval_ms;
Expand All @@ -640,17 +603,18 @@ void audio_task(void)
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void)
void led_task(void)
{
static uint32_t start_ms = 0;
static bool led_state = false;

// Blink every interval ms
if (board_millis() - start_ms < blink_interval_ms) return;
start_ms += blink_interval_ms;
if (board_millis() - start_ms >= blink_interval_ms) {
start_ms += blink_interval_ms;

led_green = led_state ? 64 : 0;
led_state = !led_state;
}

//board_led_write(led_state);
//gpio_put(LED_G, led_state);
system_led(255 * led_state, 0, 0);
led_state = 1 - led_state;
system_led(led_red, led_green, led_blue);
}

0 comments on commit 8950ccd

Please sign in to comment.