raspberrypi / pico-playground Goto Github PK
View Code? Open in Web Editor NEWLicense: BSD 3-Clause "New" or "Revised" License
License: BSD 3-Clause "New" or "Revised" License
Not much going on here - dead project?
What needs to be written to the device to make this work?
I've tried img/pack.sh but uf2conf is missing, cant see any references to what this or where it comes from ?
Hi, I've tried to implement 96KHz support but I get weird pitch and distorted audio apparently because there's some sample rate mismatch inside the audio system that creates this issue and I would like to know how to make it work with higher frequencies than 48k/16 bits.
Here's the code I changed:
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/usb_device.h"
#include "pico/audio.h"
#include "pico/audio_i2s.h"
#include "pico/multicore.h"
#include "lufa/AudioClassCommon.h"
// todo forget why this is using core 1 for sound: presumably not necessary
// todo noop when muted
CU_REGISTER_DEBUG_PINS(audio_timing)
// ---- select at most one ---
//CU_SELECT_DEBUG_PINS(audio_timing)
// todo make descriptor strings should probably belong to the configs
static char *descriptor_strings[] =
{
"PiAudio",
"Test",
"13124324"
};
// todo fix these
#define VENDOR_ID 0x520au
#define PRODUCT_ID 0xf2ddu
#define AUDIO_OUT_ENDPOINT 0x01U
#define AUDIO_IN_ENDPOINT 0x82U
#undef AUDIO_SAMPLE_FREQ
#define AUDIO_SAMPLE_FREQ(frq) (uint8_t)(frq), (uint8_t)((frq >> 8)), (uint8_t)((frq >> 16))
#define AUDIO_MAX_PACKET_SIZE(freq) (uint8_t)(((freq + 999) / 1000) * 4)
#define FEATURE_MUTE_CONTROL 1u
#define FEATURE_VOLUME_CONTROL 2u
#define ENDPOINT_FREQ_CONTROL 1u
struct audio_device_config {
struct usb_configuration_descriptor descriptor;
struct usb_interface_descriptor ac_interface;
struct __packed {
USB_Audio_StdDescriptor_Interface_AC_t core;
USB_Audio_StdDescriptor_InputTerminal_t input_terminal;
USB_Audio_StdDescriptor_FeatureUnit_t feature_unit;
USB_Audio_StdDescriptor_OutputTerminal_t output_terminal;
} ac_audio;
struct usb_interface_descriptor as_zero_interface;
struct usb_interface_descriptor as_op_interface;
struct __packed {
USB_Audio_StdDescriptor_Interface_AS_t streaming;
struct __packed {
USB_Audio_StdDescriptor_Format_t core;
USB_Audio_SampleFreq_t freqs[3];
} format;
} as_audio;
struct __packed {
struct usb_endpoint_descriptor_long core;
USB_Audio_StdDescriptor_StreamEndpoint_Spc_t audio;
} ep1;
struct usb_endpoint_descriptor_long ep2;
};
static const struct audio_device_config audio_device_config = {
.descriptor = {
.bLength = sizeof(audio_device_config.descriptor),
.bDescriptorType = DTYPE_Configuration,
.wTotalLength = sizeof(audio_device_config),
.bNumInterfaces = 2,
.bConfigurationValue = 0x01,
.iConfiguration = 0x00,
.bmAttributes = 0x80,
.bMaxPower = 0x32,
},
.ac_interface = {
.bLength = sizeof(audio_device_config.ac_interface),
.bDescriptorType = DTYPE_Interface,
.bInterfaceNumber = 0x00,
.bAlternateSetting = 0x00,
.bNumEndpoints = 0x00,
.bInterfaceClass = AUDIO_CSCP_AudioClass,
.bInterfaceSubClass = AUDIO_CSCP_ControlSubclass,
.bInterfaceProtocol = AUDIO_CSCP_ControlProtocol,
.iInterface = 0x00,
},
.ac_audio = {
.core = {
.bLength = sizeof(audio_device_config.ac_audio.core),
.bDescriptorType = AUDIO_DTYPE_CSInterface,
.bDescriptorSubtype = AUDIO_DSUBTYPE_CSInterface_Header,
.bcdADC = VERSION_BCD(1, 0, 0),
.wTotalLength = sizeof(audio_device_config.ac_audio),
.bInCollection = 1,
.bInterfaceNumbers = 1,
},
.input_terminal = {
.bLength = sizeof(audio_device_config.ac_audio.input_terminal),
.bDescriptorType = AUDIO_DTYPE_CSInterface,
.bDescriptorSubtype = AUDIO_DSUBTYPE_CSInterface_InputTerminal,
.bTerminalID = 1,
.wTerminalType = AUDIO_TERMINAL_STREAMING,
.bAssocTerminal = 0,
.bNrChannels = 2,
.wChannelConfig = AUDIO_CHANNEL_LEFT_FRONT | AUDIO_CHANNEL_RIGHT_FRONT,
.iChannelNames = 0,
.iTerminal = 0,
},
.feature_unit = {
.bLength = sizeof(audio_device_config.ac_audio.feature_unit),
.bDescriptorType = AUDIO_DTYPE_CSInterface,
.bDescriptorSubtype = AUDIO_DSUBTYPE_CSInterface_Feature,
.bUnitID = 2,
.bSourceID = 1,
.bControlSize = 1,
.bmaControls = {AUDIO_FEATURE_MUTE | AUDIO_FEATURE_VOLUME, 0, 0},
.iFeature = 0,
},
.output_terminal = {
.bLength = sizeof(audio_device_config.ac_audio.output_terminal),
.bDescriptorType = AUDIO_DTYPE_CSInterface,
.bDescriptorSubtype = AUDIO_DSUBTYPE_CSInterface_OutputTerminal,
.bTerminalID = 3,
.wTerminalType = AUDIO_TERMINAL_OUT_SPEAKER,
.bAssocTerminal = 0,
.bSourceID = 2,
.iTerminal = 0,
},
},
.as_zero_interface = {
.bLength = sizeof(audio_device_config.as_zero_interface),
.bDescriptorType = DTYPE_Interface,
.bInterfaceNumber = 0x01,
.bAlternateSetting = 0x00,
.bNumEndpoints = 0x00,
.bInterfaceClass = AUDIO_CSCP_AudioClass,
.bInterfaceSubClass = AUDIO_CSCP_AudioStreamingSubclass,
.bInterfaceProtocol = AUDIO_CSCP_ControlProtocol,
.iInterface = 0x00,
},
.as_op_interface = {
.bLength = sizeof(audio_device_config.as_op_interface),
.bDescriptorType = DTYPE_Interface,
.bInterfaceNumber = 0x01,
.bAlternateSetting = 0x01,
.bNumEndpoints = 0x02,
.bInterfaceClass = AUDIO_CSCP_AudioClass,
.bInterfaceSubClass = AUDIO_CSCP_AudioStreamingSubclass,
.bInterfaceProtocol = AUDIO_CSCP_ControlProtocol,
.iInterface = 0x00,
},
.as_audio = {
.streaming = {
.bLength = sizeof(audio_device_config.as_audio.streaming),
.bDescriptorType = AUDIO_DTYPE_CSInterface,
.bDescriptorSubtype = AUDIO_DSUBTYPE_CSInterface_General,
.bTerminalLink = 1,
.bDelay = 1,
.wFormatTag = 1, // PCM
},
.format = {
.core = {
.bLength = sizeof(audio_device_config.as_audio.format),
.bDescriptorType = AUDIO_DTYPE_CSInterface,
.bDescriptorSubtype = AUDIO_DSUBTYPE_CSInterface_FormatType,
.bFormatType = 1,
.bNrChannels = 2,
.bSubFrameSize = 2,
.bBitResolution = 16,
.bSampleFrequencyType = count_of(audio_device_config.as_audio.format.freqs),
},
.freqs = {
AUDIO_SAMPLE_FREQ(44100),
AUDIO_SAMPLE_FREQ(48000),
AUDIO_SAMPLE_FREQ(96000)
},
},
},
.ep1 = {
.core = {
.bLength = sizeof(audio_device_config.ep1.core),
.bDescriptorType = DTYPE_Endpoint,
.bEndpointAddress = AUDIO_OUT_ENDPOINT,
.bmAttributes = 5,
.wMaxPacketSize = AUDIO_MAX_PACKET_SIZE(AUDIO_FREQ_MAX),
.bInterval = 1,
.bRefresh = 0,
.bSyncAddr = AUDIO_IN_ENDPOINT,
},
.audio = {
.bLength = sizeof(audio_device_config.ep1.audio),
.bDescriptorType = AUDIO_DTYPE_CSEndpoint,
.bDescriptorSubtype = AUDIO_DSUBTYPE_CSEndpoint_General,
.bmAttributes = 1,
.bLockDelayUnits = 0,
.wLockDelay = 0,
}
},
.ep2 = {
.bLength = sizeof(audio_device_config.ep2),
.bDescriptorType = 0x05,
.bEndpointAddress = AUDIO_IN_ENDPOINT,
.bmAttributes = 0x11,
.wMaxPacketSize = 3,
.bInterval = 0x01,
.bRefresh = 2,
.bSyncAddr = 0,
},
};
static struct usb_interface ac_interface;
static struct usb_interface as_op_interface;
static struct usb_endpoint ep_op_out, ep_op_sync;
static const struct usb_device_descriptor boot_device_descriptor = {
.bLength = 18,
.bDescriptorType = 0x01,
.bcdUSB = 0x0110,
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = 0x40,
.idVendor = VENDOR_ID,
.idProduct = PRODUCT_ID,
.bcdDevice = 0x0200,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01,
};
const char *_get_descriptor_string(uint index) {
if (index <= count_of(descriptor_strings)) {
return descriptor_strings[index - 1];
} else {
return "";
}
}
static struct {
uint32_t freq;
int16_t volume;
int16_t vol_mul;
bool mute;
} audio_state = {
.freq = 96000,
};
static struct audio_buffer_pool *producer_pool;
static void _as_audio_packet(struct usb_endpoint *ep) {
assert(ep->current_transfer);
struct usb_buffer *usb_buffer = usb_current_out_packet_buffer(ep);
DEBUG_PINS_SET(audio_timing, 1);
// todo deal with blocking correctly
struct audio_buffer *audio_buffer = take_audio_buffer(producer_pool, true);
DEBUG_PINS_CLR(audio_timing, 1);
assert(!(usb_buffer->data_len & 3u));
audio_buffer->sample_count = usb_buffer->data_len / 4;
assert(audio_buffer->sample_count);
assert(audio_buffer->max_sample_count >= audio_buffer->sample_count);
uint16_t vol_mul = audio_state.vol_mul;
uint16_t *out = (uint16_t *) audio_buffer->buffer->bytes;
uint16_t *in = (uint16_t *) usb_buffer->data;
for (unsigned int i = 0; i < audio_buffer->sample_count * 2; i++) {
out[i] = (uint16_t) ((in[i] * vol_mul) >> 15u);
}
give_audio_buffer(producer_pool, audio_buffer);
// keep on truckin'
usb_grow_transfer(ep->current_transfer, 1);
usb_packet_done(ep);
}
static void _as_sync_packet(struct usb_endpoint *ep) {
assert(ep->current_transfer);
DEBUG_PINS_SET(audio_timing, 2);
DEBUG_PINS_CLR(audio_timing, 2);
struct usb_buffer *buffer = usb_current_in_packet_buffer(ep);
assert(buffer->data_max >= 3);
buffer->data_len = 3;
// todo lie thru our teeth for now
uint feedback = (audio_state.freq << 14u) / 1000u;
buffer->data[0] = feedback;
buffer->data[1] = feedback >> 8u;
buffer->data[2] = feedback >> 16u;
// keep on truckin'
usb_grow_transfer(ep->current_transfer, 1);
usb_packet_done(ep);
}
static const struct usb_transfer_type as_transfer_type = {
.on_packet = _as_audio_packet,
.initial_packet_count = 1,
};
static const struct usb_transfer_type as_sync_transfer_type = {
.on_packet = _as_sync_packet,
.initial_packet_count = 1,
};
static struct usb_transfer as_transfer;
static struct usb_transfer as_sync_transfer;
static bool do_get_current(struct usb_setup_packet *setup) {
usb_debug("AUDIO_REQ_GET_CUR\n");
if ((setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK) == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
switch (setup->wValue >> 8u) {
case FEATURE_MUTE_CONTROL: {
usb_start_tiny_control_in_transfer(audio_state.mute, 1);
return true;
}
case FEATURE_VOLUME_CONTROL: {
/* Current volume. See UAC Spec 1.0 p.77 */
usb_start_tiny_control_in_transfer(audio_state.volume, 2);
return true;
}
}
} else if ((setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK) == USB_REQ_TYPE_RECIPIENT_ENDPOINT) {
if ((setup->wValue >> 8u) == ENDPOINT_FREQ_CONTROL) {
/* Current frequency */
usb_start_tiny_control_in_transfer(audio_state.freq, 3);
return true;
}
}
return false;
}
// todo this seemed like aood guess, but is not correct
uint16_t db_to_vol[91] = {
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002,
0x0002, 0x0002, 0x0003, 0x0003, 0x0004, 0x0004, 0x0005, 0x0005,
0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000d, 0x000e,
0x0010, 0x0012, 0x0014, 0x0017, 0x001a, 0x001d, 0x0020, 0x0024,
0x0029, 0x002e, 0x0033, 0x003a, 0x0041, 0x0049, 0x0052, 0x005c,
0x0067, 0x0074, 0x0082, 0x0092, 0x00a4, 0x00b8, 0x00ce, 0x00e7,
0x0104, 0x0124, 0x0147, 0x016f, 0x019c, 0x01ce, 0x0207, 0x0246,
0x028d, 0x02dd, 0x0337, 0x039b, 0x040c, 0x048a, 0x0518, 0x05b7,
0x066a, 0x0732, 0x0813, 0x090f, 0x0a2a, 0x0b68, 0x0ccc, 0x0e5c,
0x101d, 0x1214, 0x1449, 0x16c3, 0x198a, 0x1ca7, 0x2026, 0x2413,
0x287a, 0x2d6a, 0x32f5, 0x392c, 0x4026, 0x47fa, 0x50c3, 0x5a9d,
0x65ac, 0x7214, 0x7fff
};
// actually windows doesn't seem to like this in the middle, so set top range to 0db
#define CENTER_VOLUME_INDEX 91
#define ENCODE_DB(x) ((uint16_t)(int16_t)((x)*256))
#define MIN_VOLUME ENCODE_DB(-CENTER_VOLUME_INDEX)
#define DEFAULT_VOLUME ENCODE_DB(0)
#define MAX_VOLUME ENCODE_DB(count_of(db_to_vol)-CENTER_VOLUME_INDEX)
#define VOLUME_RESOLUTION ENCODE_DB(1)
static bool do_get_minimum(struct usb_setup_packet *setup) {
usb_debug("AUDIO_REQ_GET_MIN\n");
if ((setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK) == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
switch (setup->wValue >> 8u) {
case FEATURE_VOLUME_CONTROL: {
usb_start_tiny_control_in_transfer(MIN_VOLUME, 2);
return true;
}
}
}
return false;
}
static bool do_get_maximum(struct usb_setup_packet *setup) {
usb_debug("AUDIO_REQ_GET_MAX\n");
if ((setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK) == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
switch (setup->wValue >> 8u) {
case FEATURE_VOLUME_CONTROL: {
usb_start_tiny_control_in_transfer(MAX_VOLUME, 2);
return true;
}
}
}
return false;
}
static bool do_get_resolution(struct usb_setup_packet *setup) {
usb_debug("AUDIO_REQ_GET_RES\n");
if ((setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK) == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
switch (setup->wValue >> 8u) {
case FEATURE_VOLUME_CONTROL: {
usb_start_tiny_control_in_transfer(VOLUME_RESOLUTION, 2);
return true;
}
}
}
return false;
}
static struct audio_control_cmd {
uint8_t cmd;
uint8_t type;
uint8_t cs;
uint8_t cn;
uint8_t unit;
uint8_t len;
} audio_control_cmd_t;
static void _audio_reconfigure() {
switch (audio_state.freq) {
case 44100:
case 48000:
case 96000:
break;
default:
audio_state.freq = 44100;
}
// todo hack overwriting const
((struct audio_format *) producer_pool->format)->sample_freq = audio_state.freq;
}
static void audio_set_volume(int16_t volume) {
audio_state.volume = volume;
// todo interpolate
volume += CENTER_VOLUME_INDEX * 256;
if (volume < 0) volume = 0;
if (volume >= count_of(db_to_vol) * 256) volume = count_of(db_to_vol) * 256 - 1;
audio_state.vol_mul = db_to_vol[((uint16_t)volume) >> 8u];
// printf("VOL MUL %04x\n", audio_state.vol_mul);
}
static void audio_cmd_packet(struct usb_endpoint *ep) {
assert(audio_control_cmd_t.cmd == AUDIO_REQ_SetCurrent);
struct usb_buffer *buffer = usb_current_out_packet_buffer(ep);
audio_control_cmd_t.cmd = 0;
if (buffer->data_len >= audio_control_cmd_t.len) {
if (audio_control_cmd_t.type == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
switch (audio_control_cmd_t.cs) {
case FEATURE_MUTE_CONTROL: {
audio_state.mute = buffer->data[0];
usb_warn("Set Mute %d\n", buffer->data[0]);
break;
}
case FEATURE_VOLUME_CONTROL: {
audio_set_volume(*(int16_t *) buffer->data);
break;
}
}
} else if (audio_control_cmd_t.type == USB_REQ_TYPE_RECIPIENT_ENDPOINT) {
if (audio_control_cmd_t.cs == ENDPOINT_FREQ_CONTROL) {
uint32_t new_freq = (*(uint32_t *) buffer->data) & 0x00ffffffu;
usb_warn("Set freq %d\n", new_freq == 0xffffffu ? -1 : (int) new_freq);
if (audio_state.freq != new_freq) {
audio_state.freq = new_freq;
_audio_reconfigure();
}
}
}
}
usb_start_empty_control_in_transfer_null_completion();
// todo is there error handling?
}
static const struct usb_transfer_type _audio_cmd_transfer_type = {
.on_packet = audio_cmd_packet,
.initial_packet_count = 1,
};
static bool as_set_alternate(struct usb_interface *interface, uint alt) {
assert(interface == &as_op_interface);
usb_warn("SET ALTERNATE %d\n", alt);
return alt < 2;
}
static bool do_set_current(struct usb_setup_packet *setup) {
#ifndef NDEBUG
usb_warn("AUDIO_REQ_SET_CUR\n");
#endif
if (setup->wLength && setup->wLength < 64) {
audio_control_cmd_t.cmd = AUDIO_REQ_SetCurrent;
audio_control_cmd_t.type = setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK;
audio_control_cmd_t.len = (uint8_t) setup->wLength;
audio_control_cmd_t.unit = setup->wIndex >> 8u;
audio_control_cmd_t.cs = setup->wValue >> 8u;
audio_control_cmd_t.cn = (uint8_t) setup->wValue;
usb_start_control_out_transfer(&_audio_cmd_transfer_type);
return true;
}
return false;
}
static bool ac_setup_request_handler(__unused struct usb_interface *interface, struct usb_setup_packet *setup) {
setup = __builtin_assume_aligned(setup, 4);
if (USB_REQ_TYPE_TYPE_CLASS == (setup->bmRequestType & USB_REQ_TYPE_TYPE_MASK)) {
switch (setup->bRequest) {
case AUDIO_REQ_SetCurrent:
return do_set_current(setup);
case AUDIO_REQ_GetCurrent:
return do_get_current(setup);
case AUDIO_REQ_GetMinimum:
return do_get_minimum(setup);
case AUDIO_REQ_GetMaximum:
return do_get_maximum(setup);
case AUDIO_REQ_GetResolution:
return do_get_resolution(setup);
default:
break;
}
}
return false;
}
bool _as_setup_request_handler(__unused struct usb_endpoint *ep, struct usb_setup_packet *setup) {
setup = __builtin_assume_aligned(setup, 4);
if (USB_REQ_TYPE_TYPE_CLASS == (setup->bmRequestType & USB_REQ_TYPE_TYPE_MASK)) {
switch (setup->bRequest) {
case AUDIO_REQ_SetCurrent:
return do_set_current(setup);
case AUDIO_REQ_GetCurrent:
return do_get_current(setup);
case AUDIO_REQ_GetMinimum:
return do_get_minimum(setup);
case AUDIO_REQ_GetMaximum:
return do_get_maximum(setup);
case AUDIO_REQ_GetResolution:
return do_get_resolution(setup);
default:
break;
}
}
return false;
}
void usb_sound_card_init() {
//msd_interface.setup_request_handler = msd_setup_request_handler;
usb_interface_init(&ac_interface, &audio_device_config.ac_interface, NULL, 0, true);
ac_interface.setup_request_handler = ac_setup_request_handler;
static struct usb_endpoint *const op_endpoints[] = {
&ep_op_out, &ep_op_sync
};
usb_interface_init(&as_op_interface, &audio_device_config.as_op_interface, op_endpoints, count_of(op_endpoints),
true);
as_op_interface.set_alternate_handler = as_set_alternate;
ep_op_out.setup_request_handler = _as_setup_request_handler;
as_transfer.type = &as_transfer_type;
usb_set_default_transfer(&ep_op_out, &as_transfer);
as_sync_transfer.type = &as_sync_transfer_type;
usb_set_default_transfer(&ep_op_sync, &as_sync_transfer);
static struct usb_interface *const boot_device_interfaces[] = {
&ac_interface,
&as_op_interface,
};
__unused struct usb_device *device = usb_device_init(&boot_device_descriptor, &audio_device_config.descriptor,
boot_device_interfaces, count_of(boot_device_interfaces),
_get_descriptor_string);
assert(device);
audio_set_volume(DEFAULT_VOLUME);
_audio_reconfigure();
// device->on_configure = _on_configure;
usb_device_start();
}
static void core1_worker() {
audio_i2s_set_enabled(true);
}
int main(void) {
stdout_uart_init();
//gpio_debug_pins_init();
puts("USB SOUND CARD");
#ifndef NDEBUG
for(uint i=0;i<count_of(audio_device_config.as_audio.format.freqs);i++) {
uint freq = audio_device_config.as_audio.format.freqs[i].Byte1 |
(audio_device_config.as_audio.format.freqs[i].Byte2 << 8u) |
(audio_device_config.as_audio.format.freqs[i].Byte3 << 16u);
assert(freq <= AUDIO_FREQ_MAX);
}
#endif
// initialize for 48k we allow changing later
struct audio_format audio_format_48k = {
.format = AUDIO_BUFFER_FORMAT_PCM_U16,
.sample_freq = 96000,
.channel_count = 2,
};
struct audio_buffer_format producer_format = {
.format = &audio_format_48k,
.sample_stride = 4
};
producer_pool = audio_new_producer_pool(&producer_format, 8, 96); // todo correct size
bool __unused ok;
struct audio_i2s_config config = {
.data_pin = PICO_AUDIO_I2S_DATA_PIN,
.clock_pin_base = PICO_AUDIO_I2S_CLOCK_PIN_BASE,
.dma_channel = 0,
.pio_sm = 0,
};
const struct audio_format *output_format;
output_format = audio_i2s_setup(&audio_format_48k, &config);
if (!output_format) {
panic("PicoAudio: Unable to open audio device.\n");
}
ok = audio_i2s_connect_extra(producer_pool, false, 2, 96, NULL);
assert(ok);
usb_sound_card_init();
multicore_launch_core1(core1_worker);
printf("HAHA %04x %04x %04x %04x\n", MIN_VOLUME, DEFAULT_VOLUME, MAX_VOLUME, VOLUME_RESOLUTION);
// MSD is irq driven
while (1) __wfi();
}
In the CMake I did this change here:
AUDIO_FREQ_MAX=96000
Hope to get a response back as this project seems dead. I was thinking if it would also make more sense to recreate the entire soundcard using TinyUSB at this point...
It would be great to have some screenshots for the video demos. Other demos might benefit as well.
I know the floating head sprites are arrays of char hex but i cant find an image converter that seems to work. When i replace the original sprite hex with something i get from any converters i do find, nothing appears. Not even a few pixels ! Im not a great coder but i wanna display my own sprites atleast. I have been successful with changing everything but the sprite itself like speed, position, bg color
I think that the font files:
include a rendered copy of the font "Ubuntu Mono". If so, the licence (https://ubuntu.com/legal/font-licence) for that font states:
Each copy of the Font Software must contain the above copyright notice and this licence. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
I Am Not A Lawyer, and maybe rendering a font down to a bitmap doesn't constitute a "copy" of the font, but giving a link to Ubuntu as the source seems reasonable nonetheless. Some notes on how you rendered the font would also be handy, in case anyone wants a different font size.
Is there any possibility you could publish the video conversion tool and/or some samples for the Popcorn Media Player app in pico-playground.
This is what README.mb currently says:
This is a movie player for 30fps 320x240 movies with 44100 stereo sound, read in a custom format from SD card... it can even play backwards :-) TODO: add a link to big buck bunny in the right format TODO: add converter
Many thanks
I am currently experimenting with the sleep/dormant modes of the Raspberry Pi Pico. As a base I am using the examples hello_sleep respectively hello_dormant. The problem I encountered: After going to sleep, no printf() statements will be printed to the uart.
The only text I'll ever see using a serial monitor is the following:
[Connected]
Hello Sleep!
Switching to XOSC
I am using a Mac. Thus, I don't know whether Windows 10 and Linux have the same issue.
Is there anything I need to consider when sleeping or recovering from sleep? Saving the clock registers before sleep and restoring them after sleep did not work either.
The USB Sound Card Example doesn't compile anymore with the current SDK and seems to depend on the LUFA USB Stack, while the current PicoSDK uses TinyUSB.
I want to draw 640x320 display while also monitoring capacitive touch panel every ~5ms via i2c, so I tried to elaborate on flash-stream
example, but failed just to print out smth through usb.
I tried adding second core entry to output usb string with 1 second delay, or vice versa - to move vga routine to second core, while outputting string in main one: in first case its just halts, in second while moving vga to second core - it manages to draw pictures normally but usb tty device not even recognized.
After debugging a bit - I found out that everything works just fine right until __no_inline_not_in_flash_func(flash_bulk_read)
function is reached, and its first line ssi_hw->ssienr = 0;
breaks it. I'm a newbie to pico and not familiar with this code.
So does anyone knows if its possible to use usb cdc for communication in this large pictures dma example and how to do it, or it is because pico uses all its resources to render images and have no compute power for usb stack (and potentially my i2c touch panel routine on top of it too)? Thx.
I am getting below error when trying to compile pico-playground
repository
$ cmake .. -DBOARD=pico_w
Using PICO_SDK_PATH from environment ('/home/juan/repos/pico-sdk')
PICO_SDK_PATH is /home/juan/repos/pico-sdk
Defaulting PICO_PLATFORM to rp2040 since not specified.
Defaulting PICO platform compiler to pico_arm_gcc since not specified.
-- Defaulting build type to 'Release' since not specified.
PICO compiler is pico_arm_gcc
Defaulting PICO_EXTRAS_PATH as sibling of PICO_SDK_PATH: /home/juan/repos/pico-sdk/../pico-extras
-- The C compiler identification is GNU 10.3.1
-- The CXX compiler identification is GNU 10.3.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
...
...
...
Build type is Release
Defaulting PICO target board to pico since not specified.
Using board configuration from /home/juan/repos/pico-sdk/src/boards/include/boards/pico.h
-- Found Python3: /usr/bin/python3.10 (found version "3.10.12") found components: Interpreter
CMake Warning at /home/juan/repos/pico-sdk/src/rp2_common/tinyusb/CMakeLists.txt:10 (message):
TinyUSB submodule has not been initialized; USB support will be unavailable
hint: try 'git submodule update --init' from your SDK directory
(/home/juan/repos/pico-sdk).
CMake Error at tone/christmas_melody/CMakeLists.txt:12 (example_auto_set_url):
Unknown CMake command "example_auto_set_url".
-- Configuring incomplete, errors occurred!
See also "/home/juan/repos/pico-playground/build/CMakeFiles/CMakeOutput.log".
See also "/home/juan/repos/pico-playground/build/CMakeFiles/CMakeError.log".
$
Hi,
Since I've learned that this project does not use a full framebuffer I recalled the LVGL project, that does the same for low ram microcontrollers.
Do you think that the pico would be fast enough to render the lines on demand?
Thank you.
Hello everybody,
I'd like to make a screen with a bunch of sprites that are a combination of simpler images.
The base images would be stored in flash and composed into ram on demand. The display would work from RAM then...
I understand that the packed images might be rle encoded and some work will be needed to compose them if this code is not already there somewhere else in the project.
What do you think?
Thank you.
Hello,
It seems some resistors on 'Raspberry Pi Pico to VGA Connection Schematic.png' to GND pins instead of GPxx pins, and some do not go to the right pin.
For example, on the green output, 2k resistor should go to GP8/11 and 1k one should go to GP9/12.
I think it is impossible to test this I2S audio application with these pins configurations:
https://github.com/raspberrypi/pico-playground/tree/master/audio/sine_wave
audio/sine_wave/CMakeLists.txt
USE_AUDIO_I2S=1
# PICO_AUDIO_I2S_DATA_PIN=22 - is acessible
# PICO_AUDIO_I2S_CLOCK_PIN_BASE=23 - not acessible
/audio/sine_wave/sine_wave.c
#if PICO_ON_DEVICE
#include "pico/binary_info.h"
bi_decl(bi_3pins_with_names(PICO_AUDIO_I2S_DATA_PIN, "I2S DIN", PICO_AUDIO_I2S_CLOCK_PIN_BASE, "I2S BCK", PICO_AUDIO_I2S_CLOCK_PIN_BASE+1, "I2S LRCK"));
#endif
OK = PICO_AUDIO_I2S_DATA_PIN, "I2S DIN" = GPIO22
Not acessible = PICO_AUDIO_I2S_CLOCK_PIN_BASE, "I2S BCK" = GPIO23
Not acessible = PICO_AUDIO_I2S_CLOCK_PIN_BASE+1, "I2S LRCK" = GPIO24
Thanks
Line 290 states: todo lie thru our teeth for now
in the _as_sync_packet' function
. What exactly is meant by this? What does this function do?
Does this mean the asynchronous USB transfer type is not actually providing correct feedback to the USB Host and just spoofing the feedback loop to the host? The function can be seen below. When debugging I noticed that this function is called many times which is what could be expected for something that is used as feedback loop to a USB host device. I just do not comprehend where or when this feedback is returned to the USB host device.
static void _as_sync_packet(struct usb_endpoint *ep) {
assert(ep->current_transfer);
DEBUG_PINS_SET(audio_timing, 2);
DEBUG_PINS_CLR(audio_timing, 2);
struct usb_buffer *buffer = usb_current_in_packet_buffer(ep);
assert(buffer->data_max >= 3);
buffer->data_len = 3;
// todo lie thru our teeth for now
uint feedback = (audio_state.freq << 14u) / 1000u;
buffer->data[0] = feedback;
buffer->data[1] = feedback >> 8u;
buffer->data[2] = feedback >> 16u;
printf("_as_sync_packet function --> data[0]: %d, data[1]: %d, data[2]: %d\n\r");
// keep on truckin'
usb_grow_transfer(ep->current_transfer, 1);
usb_packet_done(ep);
}
I've tried multiple combinations with different bgar5515 converters (including the packtiles script) but all of them don't render properly (draws only 1 line, some lines are skipped). I'll provide images if anyone wants them. What is the proper way to convert images?
Entering sleep/dormant mode will work a couple of times, but then the Pico gets frozen. There must be configurations which are not recovered after wake-up. Saving and then restoring the clock registers solves only some issues, but does not prevent the system from getting stuck. Reenabling the ROSC does not help either: rosc_write(&rosc_hw->ctrl, ROSC_CTRL_ENABLE_BITS);
This looks a lot more interesting than the current demos, but the build system is Windows only.
Some co-operation between these two projects could produce great results. Currently I lack the skill to modify the build systems or change the PIO code, but maybe I can get someone with more skill than me interested in looking at this.
I have a couple of projects I'd like to use the USB Sound Card app for, however I Don't know enough about coding to fix the issue it has.
After aproximately 20 minutes it will stop working, the host usb port will also stop working and will not work again unless you reboot.
Is it possible that this issue could be fixed?
This isn't really an "issue", but posting here to mention that I've produced a version of the sine_wave
example that uses an interpolator to handle the sample generation instead of calculating the offsets into the wave table within the sample loop. It is I2S only, though.
Built with PICO_BOARD=vgaboard
the sprite_demo
fails immediately at runtime with the following message:
*** PANIC ***
System clock (192000000) must be an integer multiple of the requested pixel clock (25000000).
Changing the sys clock value to a multiple of 25MHz succeeds. Seemed to need 250MHz to render the whole screen but the OC is unstable after a minute or so on my Pimoroni VGA demo board:
set_sys_clock_khz(250000, true);
This may well not be the right fix :)
Hi,
I have noticed that enabling the TURBO_BOOST define breaks the correct colors display on the flash_stream sample vga_mode_640x480_60 .
Without TURBO_BOOST, the number is crisp and the blue gradient is perfect.
With TURBO_BOOST, a blue gradient turns to like 64 color dithered green.
Also, white blobs are leaking red pixels to the right.
Are these artifacts usually caused by vga timing error?
Thank you.
Any chance for a bit of extra info on how to use the USB sound card under Linux? On Windows it just pops up as a sound device.
After plugging in, this is what I get:
[242885.880287] usb 1-3: new full-speed USB device number 12 using xhci_hcd
[242885.898555] usb 1-3: New USB device found, idVendor=2e8a, idProduct=fedd, bcdDevice= 2.00
[242885.898563] usb 1-3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[242885.898568] usb 1-3: Product: Pico Examples Sound Card
[242885.898571] usb 1-3: Manufacturer: Raspberry Pi
[242885.898574] usb 1-3: SerialNumber: 0123456789AB
There's no USB interfaces detected, am I missing some kernel driver?
It currently ends with A simple web server implemented as
๐
Hello everybody,
I am experimenting with the flash stream example and a widescreen 800x480 tft.
Currently, with the project set to 640x480, the images are not completely shown as its corners are cut out.
The tft controller OSD shows 640x480 correctly. The PNG has the same format, but it is shown bigger than the screen.
I'd like to try higher resolutions as this txt supports 800x480.
setting VGA_MODE to vga_mode_tft_800x480_50 breaks with:
[ 44%] Linking CXX executable flash_stream.elf
/usr/lib/gcc/arm-none-eabi/9.2.1/../../../arm-none-eabi/bin/ld: CMakeFiles/flash_stream.dir/flash_stream.c.obj: in function `render_loop':
flash_stream.c:(.time_critical.render_loop+0x8c): undefined reference to `vga_mode_tft_800x480_50'
/usr/lib/gcc/arm-none-eabi/9.2.1/../../../arm-none-eabi/bin/ld: CMakeFiles/flash_stream.dir/flash_stream.c.obj: in function `vga_main':
flash_stream.c:(.text.vga_main+0x14): undefined reference to `vga_mode_tft_800x480_50'
collect2: error: ld returned 1 exit status
make[2]: *** [scanvideo/flash_stream/CMakeFiles/flash_stream.dir/build.make:747: scanvideo/flash_stream/flash_stream.elf] Error 1
make[1]: *** [CMakeFiles/Makefile2:3058: scanvideo/flash_stream/CMakeFiles/flash_stream.dir/all] Error 2
make: *** [Makefile:84: all] Error 2
It looks like pico_scanvideo_dpi is being used and is not including the proper vga configurations.
What can I do?
I'm new to pico and failing to understand why I'm getting messed up image rendering with flash_stream
example in scanline directory. Steps to reproduce screenshots I get:
./pack.sh
in img
directory - it will produce 3 bin files and cat
them into one pack.bin
sudo picotool load pack.bin -o 1003c000
to upload pack.bin
into pico. The original command uses uf2conv -f pico -b 0x1003c000 pack.bin -o pack.uf2
- but I don't know what uf2conv
is and where to find it, so as this person on raspberry forum, and I took his suggested command with picotool - uf2conv threadflash_stream.uf2
and drag it into pico folder with file managerHaving done those I see such strange result, I know little about image processing and what exactly wrong with those:
I tried this on two different monitors, one big 24 inch monitor, and one small 7 inch 40 pin ttl monitor through the scaler - the result is equal.
Running demo1
example with floating raspberry on a blue background works without any issue, so I doubt its a wiring problem.
I also tried to change packing command a bit - e.g. changing format and removing d
in ./packtiles -sdf bgar5515 Voss_by_fortuneblues.png voss.bin
, it changes some things but colors are still messed.
Hi,
I am expanding the flash_stream example with usb functionality can the build is failing with:
/usr/lib/gcc/arm-none-eabi/9.2.1/../../../arm-none-eabi/bin/ld: CMakeFiles/flash_stream.dir/flash_stream.c.obj: in function `render_loop':
flash_stream.c:(.time_critical.render_loop+0x8a): undefined reference to `tud_task'
/usr/lib/gcc/arm-none-eabi/9.2.1/../../../arm-none-eabi/bin/ld: CMakeFiles/flash_stream.dir/flash_stream.c.obj: in function `vga_main':
flash_stream.c:(.text.vga_main+0x3a): undefined reference to `tud_init'
collect2: error: ld returned 1 exit status
make[2]: *** [scanvideo/flash_stream/CMakeFiles/flash_stream.dir/build.make:1129: scanvideo/flash_stream/flash_stream.elf] Error 1
make[1]: *** [CMakeFiles/Makefile2:2439: scanvideo/flash_stream/CMakeFiles/flash_stream.dir/all] Error 2
make: *** [Makefile:84: all] Error 2
Something is not going right with the library include I suspect.
I have modified the example cmakelists.txt to be:
if (PICO_ON_DEVICE AND TARGET pico_scanvideo_dpi)
include(${PICO_SDK_PATH}/lib/tinyusb/hw/bsp/family_support.cmake)
family_initialize_project(flash_stream ${CMAKE_CURRENT_LIST_DIR})
add_executable(flash_stream
flash_stream.c
usb_descriptors.c
)
target_link_libraries(flash_stream PRIVATE
pico_multicore
pico_stdlib
pico_scanvideo_dpi
hardware_dma
tinyusb_board
tinyusb_device
)
target_compile_definitions(flash_stream PRIVATE
PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS=500
)
target_include_directories(flash_stream PUBLIC
${CMAKE_CURRENT_LIST_DIR})
pico_add_extra_outputs(flash_stream)
pico_enable_stdio_usb(flash_stream 0)
pico_enable_stdio_uart(flash_stream 0)
# Uncomment this line to enable fix for Errata RP2040-E5 (the fix requires use of GPIO 15)
#target_compile_definitions(flash_stream PUBLIC PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1)
pico_add_extra_outputs(flash_stream)
family_configure_device_example(flash_stream)
endif ()
And flash_stream.c includes:
#include "tusb_config.h"
#include "bsp/board.h"
#include "device/usbd.h"
#include "class/hid/hid_device.h"
#include "tusb.h"
It does compile the file containing the missing functions:
[ 80%] Building C object scanvideo/flash_stream/CMakeFiles/flash_stream.dir/home/barbiani/pico-sdk/lib/tinyusb/src/portable/raspberrypi/rp2040/rp2040_usb.c.obj
[ 81%] Building C object scanvideo/flash_stream/CMakeFiles/flash_stream.dir/home/barbiani/pico-sdk/lib/tinyusb/src/device/usbd.c.obj
[ 82%] Building C object scanvideo/flash_stream/CMakeFiles/flash_stream.dir/home/barbiani/pico-sdk/lib/tinyusb/src/device/usbd_control.c.obj
I have followed other tinyusb examples and included what I found to be necessary, but the linking step is still failing.
Any clues?
Thank you.
Hi! I've just got my VGA pico demo board and have tried running the examples on it, however I can't seem to get any VGA output at all, I have tested the pins for Hsync, Vsync and the colours and they seem to be outputting at around 31/16KHz or a multiple of them which seems correct to me.
So I'm wondering if my modern LCD monitor doesn't like these super-low resolution modes? My monitor detects a signal but just stays black.
Strangely, I also can't get audio to play, the USB sound card is the only example I could partially get running with the PWM outputting a very distorted sound in only the left channel.
I can't tell if I'm doing something wrong or my board is faulty.
On Rp2040 Datasheet page 324,
I can read: "The programmable input/output block (PIO) is a versatile hardware interface. It can support a variety of IO standards, including: 8080 and 6800 parallel bus..."
Is there an example about 8080 parallel bus ?
Tnx
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.