Merge pull request #3150 from HiFiPhile/xfer_isr

audio: manage ISO transfer in ISR
This commit is contained in:
Ha Thach
2025-07-09 11:48:01 +07:00
committed by GitHub
23 changed files with 975 additions and 1312 deletions

View File

@@ -31,10 +31,10 @@
* $ python3 plot_audio_samples.py
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bsp/board_api.h"
#include "tusb.h"
@@ -43,14 +43,14 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
#define AUDIO_SAMPLE_RATE CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE
#define AUDIO_SAMPLE_RATE CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE
/* Blink pattern
* - 250 ms : device not mounted
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
enum {
enum {
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 1000,
BLINK_SUSPENDED = 2500,
@@ -60,31 +60,29 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0
uint32_t sampFreq;
uint8_t clkValid;
// Range states
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// Audio test data, 4 channels muxed together, buffer[0] for CH0, buffer[1] for CH1, buffer[2] for CH2, buffer[3] for CH3
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000];
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX * CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000];
void led_blinking_task(void);
void audio_task(void);
/*------------- MAIN -------------*/
int main(void)
{
int main(void) {
board_init();
// init device stack on configured roothub port
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
if (board_init_after_tusb) {
@@ -101,25 +99,23 @@ int main(void)
sampleFreqRng.subrange[0].bRes = 0;
// Generate dummy data
uint16_t * p_buff = i2s_dummy_buffer;
uint16_t *p_buff = i2s_dummy_buffer;
uint16_t dataVal = 0;
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
{
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE / 1000; cnt++) {
// CH0 saw wave
*p_buff++ = dataVal;
// CH1 inverted saw wave
*p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal;
dataVal+= 32;
*p_buff++ = 3200 + AUDIO_SAMPLE_RATE / 1000 - dataVal;
dataVal += 32;
// CH3 square wave
*p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000;
*p_buff++ = cnt < (AUDIO_SAMPLE_RATE / 1000 / 2) ? 3400 : 5000;
// CH4 sinus wave
float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000);
*p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000);
float t = 2 * 3.1415f * cnt / (AUDIO_SAMPLE_RATE / 1000);
*p_buff++ = (uint16_t) ((int16_t) (sinf(t) * 750) + 6000);
}
while (1)
{
tud_task(); // tinyusb device task
while (1) {
tud_task();// tinyusb device task
led_blinking_task();
audio_task();
}
@@ -130,29 +126,25 @@ int main(void)
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void)
{
void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
void tud_suspend_cb(bool remote_wakeup_en) {
(void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
@@ -160,15 +152,15 @@ void tud_resume_cb(void)
// AUDIO Task
//--------------------------------------------------------------------+
void audio_task(void)
{
// Yet to be filled - e.g. read audio from I2S buffer.
// Here we simulate a I2S receive callback every 1ms.
// This task simulates an audio receive callback, one frame is received every 1ms.
// We assume that the audio data is read from an I2S buffer.
// In a real application, this would be replaced with actual I2S receive callback.
void audio_task(void) {
static uint32_t start_ms = 0;
uint32_t curr_ms = board_millis();
if ( start_ms == curr_ms ) return; // not enough time
if (start_ms == curr_ms) return;// not enough time
start_ms = curr_ms;
tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX);
tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX);
}
//--------------------------------------------------------------------+
@@ -176,8 +168,7 @@ void audio_task(void)
//--------------------------------------------------------------------+
// Invoked when audio class specific set request received for an EP
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
(void) pBuff;
@@ -189,14 +180,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t ep = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) ep;
(void) channelNum;
(void) ctrlSel;
(void) ep;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific set request received for an interface
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
(void) pBuff;
@@ -208,14 +200,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t itf = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) itf;
(void) channelNum;
(void) ctrlSel;
(void) itf;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific set request received for an entity
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -230,40 +223,37 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
// If request is for our feature unit
if ( entityID == 2 )
{
switch ( ctrlSel )
{
if (entityID == 2) {
switch (ctrlSel) {
case AUDIO_FU_CTRL_MUTE:
// Request uses format layout 1
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
return true;
case AUDIO_FU_CTRL_VOLUME:
// Request uses format layout 2
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t*) pBuff)->bCur;
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur;
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
return true;
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
return false;
}
}
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an EP
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -271,16 +261,17 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t ep = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) ep;
(void) channelNum;
(void) ctrlSel;
(void) ep;
// return tud_control_xfer(rhport, p_request, &tmp, 1);
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an interface
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -288,14 +279,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t itf = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) itf;
(void) channelNum;
(void) ctrlSel;
(void) itf;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an entity
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -305,12 +297,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// Input terminal (Microphone input)
if (entityID == 1)
{
switch ( ctrlSel )
{
case AUDIO_TE_CTRL_CONNECTOR:
{
if (entityID == 1) {
switch (ctrlSel) {
case AUDIO_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
audio_desc_channel_cluster_t ret;
@@ -321,9 +310,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
TU_LOG2(" Get terminal connector\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
}
break;
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
} break;
// Unknown/Unsupported control selector
default:
@@ -333,10 +321,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
// Feature unit
if (entityID == 2)
{
switch ( ctrlSel )
{
if (entityID == 2) {
switch (ctrlSel) {
case AUDIO_FU_CTRL_MUTE:
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
// There does not exist a range parameter block for mute
@@ -344,8 +330,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
case AUDIO_FU_CTRL_VOLUME:
switch ( p_request->bRequest )
{
switch (p_request->bRequest) {
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
@@ -355,21 +340,21 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
// Copy values - only for testing - better is version below
audio_control_range_2_n_t(1)
ret;
ret;
ret.wNumSubRanges = 1;
ret.subrange[0].bMin = -90; // -90 dB
ret.subrange[0].bMax = 90; // +90 dB
ret.subrange[0].bRes = 1; // 1 dB steps
ret.subrange[0].bMin = -90;// -90 dB
ret.subrange[0].bMax = 90; // +90 dB
ret.subrange[0].bRes = 1; // 1 dB steps
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
break;
// Unknown/Unsupported control
default:
@@ -379,14 +364,11 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
// Clock Source unit
if ( entityID == 4 )
{
switch ( ctrlSel )
{
if (entityID == 4) {
switch (ctrlSel) {
case AUDIO_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch ( p_request->bRequest )
{
switch (p_request->bRequest) {
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
// Buffered control transfer is needed for IN flow control to work
@@ -396,12 +378,12 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
TU_LOG2(" Get Sample Freq. range\r\n");
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
// Unknown/Unsupported control
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
break;
case AUDIO_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
@@ -416,59 +398,20 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
TU_LOG2(" Unsupported entity: %d\r\n", entityID);
return false; // Yet not implemented
}
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void) rhport;
(void) itf;
(void) ep_in;
(void) cur_alt_setting;
// In read world application data flow is driven by I2S clock,
// both tud_audio_tx_done_pre_load_cb() & tud_audio_tx_done_post_load_cb() are hardly used.
// For example in your I2S receive callback:
// void I2S_Rx_Callback(int channel, const void* data, uint16_t samples)
// {
// tud_audio_write_support_ff(channel, data, samples * N_BYTES_PER_SAMPLE * N_CHANNEL_PER_FIFO);
// }
return true;
}
bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void) rhport;
(void) n_bytes_copied;
(void) itf;
(void) ep_in;
(void) cur_alt_setting;
return true;
}
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void) rhport;
(void) p_request;
return true;
return false;// Yet not implemented
}
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void)
{
void led_blinking_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; // not enough time
if (board_millis() - start_ms < blink_interval_ms) return;// not enough time
start_ms += blink_interval_ms;
board_led_write(led_state);
led_state = 1 - led_state; // toggle
led_state = 1 - led_state;// toggle
}

View File

@@ -31,10 +31,10 @@
* $ python3 plot_audio_samples.py
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bsp/board_api.h"
#include "tusb.h"
@@ -43,38 +43,38 @@
// ESP-IDF need "freertos/" prefix in include path.
// CFG_TUSB_OS_INC_PATH should be defined accordingly.
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#define USBD_STACK_SIZE 4096
#define USBD_STACK_SIZE 4096
#else
#include "FreeRTOS.h"
#include "semphr.h"
#include "queue.h"
#include "semphr.h"
#include "task.h"
#include "timers.h"
// Increase stack size when debug log is enabled
#define USBD_STACK_SIZE (4*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
#define USBD_STACK_SIZE (4 * configMINIMAL_STACK_SIZE / 2) * (CFG_TUSB_DEBUG ? 2 : 1)
#endif
#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
#define AUDIO_STACK_SIZE configMINIMAL_STACK_SIZE
#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
#define AUDIO_STACK_SIZE configMINIMAL_STACK_SIZE
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
#define AUDIO_SAMPLE_RATE CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE
#define AUDIO_SAMPLE_RATE CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE
/* Blink pattern
* - 250 ms : device not mounted
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
enum {
enum {
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 1000,
BLINK_SUSPENDED = 2500,
@@ -82,13 +82,13 @@ enum {
// static task
#if configSUPPORT_STATIC_ALLOCATION
StackType_t blinky_stack[BLINKY_STACK_SIZE];
StackType_t blinky_stack[BLINKY_STACK_SIZE];
StaticTask_t blinky_taskdef;
StackType_t usb_device_stack[USBD_STACK_SIZE];
StackType_t usb_device_stack[USBD_STACK_SIZE];
StaticTask_t usb_device_taskdef;
StackType_t audio_stack[AUDIO_STACK_SIZE];
StackType_t audio_stack[AUDIO_STACK_SIZE];
StaticTask_t audio_taskdef;
#endif
@@ -96,25 +96,24 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0
uint32_t sampFreq;
uint8_t clkValid;
// Range states
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// Audio test data, 4 channels muxed together, buffer[0] for CH0, buffer[1] for CH1, buffer[2] for CH2, buffer[3] for CH3
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000];
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX * CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000];
void led_blinking_task(void* param);
void usb_device_task(void* param);
void audio_task(void* param);
void led_blinking_task(void *param);
void usb_device_task(void *param);
void audio_isr_task(void *param);
/*------------- MAIN -------------*/
int main(void)
{
int main(void) {
board_init();
// Init values
@@ -127,20 +126,19 @@ int main(void)
sampleFreqRng.subrange[0].bRes = 0;
// Generate dummy data
uint16_t * p_buff = i2s_dummy_buffer;
uint16_t *p_buff = i2s_dummy_buffer;
uint16_t dataVal = 0;
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
{
for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE / 1000; cnt++) {
// CH0 saw wave
*p_buff++ = dataVal;
// CH1 inverted saw wave
*p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal;
dataVal+= 32;
*p_buff++ = 3200 + AUDIO_SAMPLE_RATE / 1000 - dataVal;
dataVal += 32;
// CH3 square wave
*p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000;
*p_buff++ = cnt < (AUDIO_SAMPLE_RATE / 1000 / 2) ? 3400 : 5000;
// CH4 sinus wave
float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000);
*p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000);
float t = 2 * 3.1415f * cnt / (AUDIO_SAMPLE_RATE / 1000);
*p_buff++ = (uint16_t) ((int16_t) (sinf(t) * 750) + 6000);
}
#if configSUPPORT_STATIC_ALLOCATION
@@ -148,14 +146,15 @@ int main(void)
xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef);
// Create a task for tinyusb device stack
xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef);
xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, usb_device_stack, &usb_device_taskdef);
// Create a task for audio
xTaskCreateStatic(audio_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES-1, audio_stack, &audio_taskdef);
// Audio receive (I2S) ISR simulation
// To simulate a ISR the priority is set to the highest
xTaskCreateStatic(audio_isr_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, audio_stack, &audio_taskdef);
#else
xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
xTaskCreate(audio_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL);
xTaskCreate(audio_isr_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
#endif
// only start scheduler for non-espressif mcu
@@ -174,17 +173,15 @@ void app_main(void) {
// USB Device Driver task
// This top level thread process all usb events and invoke callbacks
void usb_device_task(void* param)
{
void usb_device_task(void *param) {
(void) param;
// init device stack on configured roothub port
// This should be called after scheduler/kernel is started.
// Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API.
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
if (board_init_after_tusb) {
@@ -192,8 +189,7 @@ void usb_device_task(void* param)
}
// RTOS forever loop
while (1)
{
while (1) {
// tinyusb device task
tud_task();
}
@@ -204,29 +200,25 @@ void usb_device_task(void* param)
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void)
{
void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
void tud_suspend_cb(bool remote_wakeup_en) {
(void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
@@ -234,14 +226,14 @@ void tud_resume_cb(void)
// AUDIO Task
//--------------------------------------------------------------------+
void audio_task(void* param)
{
// This task simulates an audio receive ISR, one frame is received every 1ms.
// We assume that the audio data is read from an I2S buffer.
// In a real application, this would be replaced with actual I2S receive callback.
void audio_isr_task(void *param) {
(void) param;
// Yet to be filled - e.g. read audio from I2S buffer.
// Here we simulate a I2S receive callback every 1ms.
while (1) {
vTaskDelay(1);
tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX);
tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX);
}
}
@@ -250,8 +242,7 @@ void audio_task(void* param)
//--------------------------------------------------------------------+
// Invoked when audio class specific set request received for an EP
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
(void) pBuff;
@@ -263,14 +254,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t ep = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) ep;
(void) channelNum;
(void) ctrlSel;
(void) ep;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific set request received for an interface
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
(void) pBuff;
@@ -282,14 +274,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t itf = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) itf;
(void) channelNum;
(void) ctrlSel;
(void) itf;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific set request received for an entity
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -304,40 +297,36 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
// If request is for our feature unit
if ( entityID == 2 )
{
switch ( ctrlSel )
{
if (entityID == 2) {
switch (ctrlSel) {
case AUDIO_FU_CTRL_MUTE:
// Request uses format layout 1
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
TU_LOG1(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
case AUDIO_FU_CTRL_VOLUME:
// Request uses format layout 2
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
volume[channelNum] = ((audio_control_cur_2_t*) pBuff)->bCur;
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
volume[channelNum] = ((audio_control_cur_2_t *) pBuff)->bCur;
TU_LOG1(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
return false;
}
}
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an EP
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -345,14 +334,15 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t ep = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) ep;
(void) channelNum;
(void) ctrlSel;
(void) ep;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an interface
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -360,14 +350,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t itf = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) itf;
(void) channelNum;
(void) ctrlSel;
(void) itf;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an entity
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -377,12 +368,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// Input terminal (Microphone input)
if (entityID == 1)
{
switch ( ctrlSel )
{
case AUDIO_TE_CTRL_CONNECTOR:
{
if (entityID == 1) {
switch (ctrlSel) {
case AUDIO_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
audio_desc_channel_cluster_t ret;
@@ -391,11 +379,10 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
ret.bmChannelConfig = 0;
ret.iChannelNames = 0;
TU_LOG2(" Get terminal connector\r\n");
TU_LOG1(" Get terminal connector\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
}
break;
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
} break;
// Unknown/Unsupported control selector
default:
@@ -405,43 +392,39 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
// Feature unit
if (entityID == 2)
{
switch ( ctrlSel )
{
if (entityID == 2) {
switch (ctrlSel) {
case AUDIO_FU_CTRL_MUTE:
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
// There does not exist a range parameter block for mute
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
TU_LOG1(" Get Mute of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
case AUDIO_FU_CTRL_VOLUME:
switch ( p_request->bRequest )
{
switch (p_request->bRequest) {
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
TU_LOG1(" Get Volume of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
case AUDIO_CS_REQ_RANGE:
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
TU_LOG1(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
audio_control_range_2_n_t(1)
ret;
audio_control_range_2_n_t(1) ret;
ret.wNumSubRanges = 1;
ret.subrange[0].bMin = -90; // -90 dB
ret.subrange[0].bMax = 90; // +90 dB
ret.subrange[0].bRes = 1; // 1 dB steps
ret.subrange[0].bMin = -90;// -90 dB
ret.subrange[0].bMax = 90; // +90 dB
ret.subrange[0].bRes = 1; // 1 dB steps
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
break;
// Unknown/Unsupported control
default:
@@ -451,33 +434,30 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
// Clock Source unit
if ( entityID == 4 )
{
switch ( ctrlSel )
{
if (entityID == 4) {
switch (ctrlSel) {
case AUDIO_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch ( p_request->bRequest )
{
switch (p_request->bRequest) {
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
TU_LOG1(" Get Sample Freq.\r\n");
// Buffered control transfer is needed for IN flow control to work
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
case AUDIO_CS_REQ_RANGE:
TU_LOG2(" Get Sample Freq. range\r\n");
TU_LOG1(" Get Sample Freq. range\r\n");
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
// Unknown/Unsupported control
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
break;
case AUDIO_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG2(" Get Sample Freq. valid\r\n");
TU_LOG1(" Get Sample Freq. valid\r\n");
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
// Unknown/Unsupported control
@@ -487,52 +467,14 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
}
TU_LOG2(" Unsupported entity: %d\r\n", entityID);
return false; // Yet not implemented
}
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void) rhport;
(void) itf;
(void) ep_in;
(void) cur_alt_setting;
// In read world application data flow is driven by I2S clock,
// both tud_audio_tx_done_pre_load_cb() & tud_audio_tx_done_post_load_cb() are hardly used.
// For example in your I2S receive callback:
// void I2S_Rx_Callback(int channel, const void* data, uint16_t samples)
// {
// tud_audio_write_support_ff(channel, data, samples * N_BYTES_PER_SAMPLE * N_CHANNEL_PER_FIFO);
// }
return true;
}
bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void) rhport;
(void) n_bytes_copied;
(void) itf;
(void) ep_in;
(void) cur_alt_setting;
return true;
}
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void) rhport;
(void) p_request;
return true;
TU_LOG1(" Unsupported entity: %d\r\n", entityID);
return false;// Yet not implemented
}
///--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void* param) {
void led_blinking_task(void *param) {
(void) param;
static uint32_t start_ms = 0;
static bool led_state = false;
@@ -543,6 +485,6 @@ void led_blinking_task(void* param) {
start_ms += blink_interval_ms;
board_led_write(led_state);
led_state = 1 - led_state; // toggle
led_state = 1 - led_state;// toggle
}
}

View File

@@ -31,8 +31,8 @@
* $ python3 plot_audio_samples.py
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bsp/board_api.h"
@@ -47,7 +47,7 @@
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
enum {
enum {
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 1000,
BLINK_SUSPENDED = 2500,
@@ -57,32 +57,30 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0
uint32_t sampFreq;
uint8_t clkValid;
// Range states
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// Audio test data
uint16_t test_buffer_audio[(CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2];
uint16_t test_buffer_audio[CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / 2];
uint16_t startVal = 0;
void led_blinking_task(void);
void audio_task(void);
/*------------- MAIN -------------*/
int main(void)
{
int main(void) {
board_init();
// init device stack on configured roothub port
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
if (board_init_after_tusb) {
@@ -98,9 +96,8 @@ int main(void)
sampleFreqRng.subrange[0].bMax = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
sampleFreqRng.subrange[0].bRes = 0;
while (1)
{
tud_task(); // tinyusb device task
while (1) {
tud_task();// tinyusb device task
led_blinking_task();
audio_task();
}
@@ -111,40 +108,45 @@ int main(void)
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void)
{
void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
void tud_suspend_cb(bool remote_wakeup_en) {
(void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
//--------------------------------------------------------------------+
// AUDIO Task
//--------------------------------------------------------------------+
void audio_task(void)
{
// Yet to be filled - e.g. put meas data into TX FIFOs etc.
// asm("nop");
// This task simulates an audio receive callback, one frame is received every 1ms.
// We assume that the audio data is read from an I2S buffer.
// In a real application, this would be replaced with actual I2S receive callback.
void audio_task(void) {
static uint32_t start_ms = 0;
uint32_t curr_ms = board_millis();
if (start_ms == curr_ms) return;// not enough time
start_ms = curr_ms;
for (size_t cnt = 0; cnt < sizeof(test_buffer_audio) / 2; cnt++) {
test_buffer_audio[cnt] = startVal++;
}
tud_audio_write((uint8_t *) test_buffer_audio, sizeof(test_buffer_audio));
}
//--------------------------------------------------------------------+
@@ -152,8 +154,7 @@ void audio_task(void)
//--------------------------------------------------------------------+
// Invoked when audio class specific set request received for an EP
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
(void) pBuff;
@@ -165,14 +166,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t ep = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) ep;
(void) channelNum;
(void) ctrlSel;
(void) ep;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific set request received for an interface
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
(void) pBuff;
@@ -184,14 +186,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t itf = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) itf;
(void) channelNum;
(void) ctrlSel;
(void) itf;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific set request received for an entity
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -206,40 +209,37 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
// If request is for our feature unit
if ( entityID == 2 )
{
switch ( ctrlSel )
{
if (entityID == 2) {
switch (ctrlSel) {
case AUDIO_FU_CTRL_MUTE:
// Request uses format layout 1
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
return true;
case AUDIO_FU_CTRL_VOLUME:
// Request uses format layout 2
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t*) pBuff)->bCur;
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur;
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
return true;
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
return false;
}
}
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an EP
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -247,16 +247,17 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t ep = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) ep;
(void) channelNum;
(void) ctrlSel;
(void) ep;
// return tud_control_xfer(rhport, p_request, &tmp, 1);
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an interface
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -264,14 +265,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t itf = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) itf;
(void) channelNum;
(void) ctrlSel;
(void) itf;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an entity
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -281,12 +283,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// Input terminal (Microphone input)
if (entityID == 1)
{
switch ( ctrlSel )
{
case AUDIO_TE_CTRL_CONNECTOR:
{
if (entityID == 1) {
switch (ctrlSel) {
case AUDIO_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
audio_desc_channel_cluster_t ret;
@@ -297,9 +296,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
TU_LOG2(" Get terminal connector\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
}
break;
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
} break;
// Unknown/Unsupported control selector
default:
@@ -309,43 +307,40 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
// Feature unit
if (entityID == 2)
{
switch ( ctrlSel )
{
if (entityID == 2) {
switch (ctrlSel) {
case AUDIO_FU_CTRL_MUTE:
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
// There does not exist a range parameter block for mute
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
case AUDIO_FU_CTRL_VOLUME:
switch ( p_request->bRequest )
{
switch (p_request->bRequest) {
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
case AUDIO_CS_REQ_RANGE:
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
audio_control_range_2_n_t(1)
ret;
ret;
ret.wNumSubRanges = 1;
ret.subrange[0].bMin = -90; // -90 dB
ret.subrange[0].bMax = 90; // +90 dB
ret.subrange[0].bRes = 1; // 1 dB steps
ret.subrange[0].bMin = -90;// -90 dB
ret.subrange[0].bMax = 90; // +90 dB
ret.subrange[0].bRes = 1; // 1 dB steps
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
break;
// Unknown/Unsupported control
default:
@@ -355,33 +350,30 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
// Clock Source unit
if ( entityID == 4 )
{
switch ( ctrlSel )
{
if (entityID == 4) {
switch (ctrlSel) {
case AUDIO_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch ( p_request->bRequest )
{
switch (p_request->bRequest) {
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
case AUDIO_CS_REQ_RANGE:
TU_LOG2(" Get Sample Freq. range\r\n");
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
// Unknown/Unsupported control
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
break;
case AUDIO_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG2(" Get Sample Freq. valid\r\n");
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
// Unknown/Unsupported control
default:
@@ -391,39 +383,10 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
TU_LOG2(" Unsupported entity: %d\r\n", entityID);
return false; // Yet not implemented
return false;// Yet not implemented
}
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void) rhport;
(void) itf;
(void) ep_in;
(void) cur_alt_setting;
tud_audio_write ((uint8_t *)test_buffer_audio, CFG_TUD_AUDIO_EP_SZ_IN - 2);
return true;
}
bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void) rhport;
(void) n_bytes_copied;
(void) itf;
(void) ep_in;
(void) cur_alt_setting;
for (size_t cnt = 0; cnt < (CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2; cnt++)
{
test_buffer_audio[cnt] = startVal++;
}
return true;
}
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
(void) p_request;
startVal = 0;
@@ -434,15 +397,14 @@ bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void)
{
void led_blinking_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; // not enough time
if (board_millis() - start_ms < blink_interval_ms) return;// not enough time
start_ms += blink_interval_ms;
board_led_write(led_state);
led_state = 1 - led_state; // toggle
led_state = 1 - led_state;// toggle
}

View File

@@ -12,11 +12,11 @@ if __name__ == '__main__':
# print(sd.query_devices())
fs = 48000 # Sample rate
duration = 100e-3 # Duration of recording
duration = 3 # Duration of recording
if platform.system() == 'Windows':
# MME is needed since there are more than one MicNode device APIs (at least in Windows)
device = 'Microphone (MicNode) MME'
device = 'Microphone (MicNode), Windows WASAPI'
elif platform.system() == 'Darwin':
device = 'MicNode'
else:

View File

@@ -117,7 +117,7 @@ extern "C" {
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor!
#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 8 : 1) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device
#ifdef __cplusplus
}

View File

@@ -31,8 +31,8 @@
* $ python3 plot_audio_samples.py
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bsp/board_api.h"
@@ -42,25 +42,26 @@
// ESP-IDF need "freertos/" prefix in include path.
// CFG_TUSB_OS_INC_PATH should be defined accordingly.
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#define USBD_STACK_SIZE 4096
#define USBD_STACK_SIZE 4096
#else
#include "FreeRTOS.h"
#include "semphr.h"
#include "queue.h"
#include "semphr.h"
#include "task.h"
#include "timers.h"
// Increase stack size when debug log is enabled
#define USBD_STACK_SIZE (4*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
#define USBD_STACK_SIZE (4 * configMINIMAL_STACK_SIZE / 2) * (CFG_TUSB_DEBUG ? 2 : 1)
#endif
#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
#define AUDIO_STACK_SIZE configMINIMAL_STACK_SIZE
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
@@ -71,7 +72,7 @@
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
enum {
enum {
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 1000,
BLINK_SUSPENDED = 2500,
@@ -82,34 +83,36 @@ enum {
StackType_t blinky_stack[BLINKY_STACK_SIZE];
StaticTask_t blinky_taskdef;
StackType_t usb_device_stack[USBD_STACK_SIZE];
StackType_t usb_device_stack[USBD_STACK_SIZE];
StaticTask_t usb_device_taskdef;
StackType_t audio_stack[AUDIO_STACK_SIZE];
StaticTask_t audio_taskdef;
#endif
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0
uint32_t sampFreq;
uint8_t clkValid;
// Range states
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// Audio test data
uint16_t test_buffer_audio[(CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2];
uint16_t test_buffer_audio[CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / 2];
uint16_t startVal = 0;
void led_blinking_task(void* param);
void usb_device_task(void* param);
void audio_task(void);
void led_blinking_task(void *param);
void usb_device_task(void *param);
void audio_isr_task(void *param);
/*------------- MAIN -------------*/
int main(void)
{
int main(void) {
board_init();
// Init values
@@ -126,10 +129,15 @@ int main(void)
xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef);
// Create a task for tinyusb device stack
xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef);
xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, usb_device_stack, &usb_device_taskdef);
// Audio receive (I2S) ISR simulation
// To simulate a ISR the priority is set to the highest
xTaskCreateStatic(audio_isr_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, audio_stack, &audio_taskdef);
#else
xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
xTaskCreate(audio_isr_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
#endif
// only start scheduler for non-espressif mcu
@@ -148,17 +156,15 @@ void app_main(void) {
// USB Device Driver task
// This top level thread process all usb events and invoke callbacks
void usb_device_task(void* param)
{
void usb_device_task(void *param) {
(void) param;
// init device stack on configured roothub port
// This should be called after scheduler/kernel is started.
// Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API.
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
if (board_init_after_tusb) {
@@ -166,8 +172,7 @@ void usb_device_task(void* param)
}
// RTOS forever loop
while (1)
{
while (1) {
// tinyusb device task
tud_task();
}
@@ -204,10 +209,18 @@ void tud_resume_cb(void) {
// AUDIO Task
//--------------------------------------------------------------------+
void audio_task(void)
{
// Yet to be filled - e.g. put meas data into TX FIFOs etc.
// asm("nop");
// This task simulates an audio receive ISR, one frame is received every 1ms.
// We assume that the audio data is read from an I2S buffer.
// In a real application, this would be replaced with actual I2S receive callback.
void audio_isr_task(void *param) {
(void) param;
while (1) {
vTaskDelay(1);
for (size_t cnt = 0; cnt < sizeof(test_buffer_audio) / 2; cnt++) {
test_buffer_audio[cnt] = startVal++;
}
tud_audio_write((uint8_t *) test_buffer_audio, sizeof(test_buffer_audio));
}
}
//--------------------------------------------------------------------+
@@ -215,8 +228,7 @@ void audio_task(void)
//--------------------------------------------------------------------+
// Invoked when audio class specific set request received for an EP
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
(void) pBuff;
@@ -228,14 +240,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t ep = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) ep;
(void) channelNum;
(void) ctrlSel;
(void) ep;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific set request received for an interface
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
(void) pBuff;
@@ -247,14 +260,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t itf = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) itf;
(void) channelNum;
(void) ctrlSel;
(void) itf;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific set request received for an entity
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -269,40 +283,36 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
// If request is for our feature unit
if ( entityID == 2 )
{
switch ( ctrlSel )
{
if (entityID == 2) {
switch (ctrlSel) {
case AUDIO_FU_CTRL_MUTE:
// Request uses format layout 1
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
TU_LOG1(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
case AUDIO_FU_CTRL_VOLUME:
// Request uses format layout 2
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t*) pBuff)->bCur;
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur;
TU_LOG1(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
return false;
}
}
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an EP
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -310,14 +320,15 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t ep = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) ep;
(void) channelNum;
(void) ctrlSel;
(void) ep;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an interface
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -325,14 +336,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t itf = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) itf;
(void) channelNum;
(void) ctrlSel;
(void) itf;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an entity
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -342,12 +354,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// Input terminal (Microphone input)
if (entityID == 1)
{
switch ( ctrlSel )
{
case AUDIO_TE_CTRL_CONNECTOR:
{
if (entityID == 1) {
switch (ctrlSel) {
case AUDIO_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
audio_desc_channel_cluster_t ret;
@@ -356,11 +365,10 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
ret.bmChannelConfig = (audio_channel_config_t) 0;
ret.iChannelNames = 0;
TU_LOG2(" Get terminal connector\r\n");
TU_LOG1(" Get terminal connector\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
}
break;
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
} break;
// Unknown/Unsupported control selector
default:
@@ -370,43 +378,40 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
// Feature unit
if (entityID == 2)
{
switch ( ctrlSel )
{
if (entityID == 2) {
switch (ctrlSel) {
case AUDIO_FU_CTRL_MUTE:
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
// There does not exist a range parameter block for mute
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
TU_LOG1(" Get Mute of channel: %u\r\n", channelNum);
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
case AUDIO_FU_CTRL_VOLUME:
switch ( p_request->bRequest )
{
switch (p_request->bRequest) {
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
TU_LOG1(" Get Volume of channel: %u\r\n", channelNum);
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
case AUDIO_CS_REQ_RANGE:
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
TU_LOG1(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
audio_control_range_2_n_t(1)
ret;
ret;
ret.wNumSubRanges = 1;
ret.subrange[0].bMin = -90; // -90 dB
ret.subrange[0].bMax = 90; // +90 dB
ret.subrange[0].bRes = 1; // 1 dB steps
ret.subrange[0].bMin = -90;// -90 dB
ret.subrange[0].bMax = 90; // +90 dB
ret.subrange[0].bRes = 1; // 1 dB steps
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
break;
// Unknown/Unsupported control
default:
@@ -416,33 +421,30 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
// Clock Source unit
if ( entityID == 4 )
{
switch ( ctrlSel )
{
if (entityID == 4) {
switch (ctrlSel) {
case AUDIO_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch ( p_request->bRequest )
{
switch (p_request->bRequest) {
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
TU_LOG1(" Get Sample Freq.\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
case AUDIO_CS_REQ_RANGE:
TU_LOG2(" Get Sample Freq. range\r\n");
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
TU_LOG1(" Get Sample Freq. range\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
// Unknown/Unsupported control
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
break;
case AUDIO_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG2(" Get Sample Freq. valid\r\n");
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
TU_LOG1(" Get Sample Freq. valid\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
// Unknown/Unsupported control
default:
@@ -451,40 +453,11 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
}
TU_LOG2(" Unsupported entity: %d\r\n", entityID);
return false; // Yet not implemented
TU_LOG1(" Unsupported entity: %d\r\n", entityID);
return false;// Yet not implemented
}
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void) rhport;
(void) itf;
(void) ep_in;
(void) cur_alt_setting;
tud_audio_write ((uint8_t *)test_buffer_audio, CFG_TUD_AUDIO_EP_SZ_IN - 2);
return true;
}
bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void) rhport;
(void) n_bytes_copied;
(void) itf;
(void) ep_in;
(void) cur_alt_setting;
for (size_t cnt = 0; cnt < (CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2; cnt++)
{
test_buffer_audio[cnt] = startVal++;
}
return true;
}
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
(void) p_request;
startVal = 0;
@@ -495,7 +468,7 @@ bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void* param) {
void led_blinking_task(void *param) {
(void) param;
static uint32_t start_ms = 0;
static bool led_state = false;
@@ -506,6 +479,6 @@ void led_blinking_task(void* param) {
start_ms += blink_interval_ms;
board_led_write(led_state);
led_state = 1 - led_state; // toggle
led_state = 1 - led_state;// toggle
}
}

View File

@@ -11,11 +11,11 @@ if __name__ == '__main__':
# print(sd.query_devices())
fs = 48000 # Sample rate
duration = 1000e-3 # Duration of recording
duration = 3 # Duration of recording
if platform.system() == 'Windows':
# MME is needed since there are more than one MicNode device APIs (at least in Windows)
device = 'Microphone (MicNode) MME'
device = 'Microphone (MicNode), Windows WASAPI'
elif platform.system() == 'Darwin':
device = 'MicNode'
else:

View File

@@ -123,7 +123,7 @@ extern "C" {
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor!
#define CFG_TUD_AUDIO_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 8 : 1) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device
#ifdef __cplusplus
}

View File

@@ -32,8 +32,8 @@
* $ python3 plot_audio_samples.py
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bsp/board_api.h"
@@ -49,7 +49,7 @@
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
enum {
enum {
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 1000,
BLINK_SUSPENDED = 2500,
@@ -59,8 +59,8 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// +1 for master channel 0
uint32_t sampFreq;
uint8_t bytesPerSample;
uint8_t clkValid;
@@ -68,39 +68,36 @@ uint8_t clkValid;
// Range states
// List of supported sample rates
static const uint32_t sampleRatesList[] =
{
32000, 48000, 96000
};
{
32000, 48000, 96000};
#define N_sampleRates TU_ARRAY_SIZE(sampleRatesList)
#define N_sampleRates TU_ARRAY_SIZE(sampleRatesList)
// Bytes per format of every Alt settings
static const uint8_t bytesPerSampleAltList[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] =
{
CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX,
CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX,
{
CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX,
CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX,
};
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];// Volume range state
// Audio test data
CFG_TUD_MEM_ALIGN uint8_t test_buffer_audio[CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX];
CFG_TUD_MEM_ALIGN uint8_t test_buffer_audio[(TUD_OPT_HIGH_SPEED ? 8 : 1) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX];
uint16_t startVal = 0;
void led_blinking_task(void);
void audio_task(void);
/*------------- MAIN -------------*/
int main(void)
{
int main(void) {
board_init();
// init device stack on configured roothub port
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
if (board_init_after_tusb) {
@@ -111,9 +108,8 @@ int main(void)
sampFreq = sampleRatesList[0];
clkValid = 1;
while (1)
{
tud_task(); // tinyusb device task
while (1) {
tud_task();// tinyusb device task
led_blinking_task();
audio_task();
}
@@ -127,29 +123,25 @@ int main(void)
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void)
{
void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
void tud_suspend_cb(bool remote_wakeup_en) {
(void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
@@ -157,10 +149,29 @@ void tud_resume_cb(void)
// AUDIO Task
//--------------------------------------------------------------------+
void audio_task(void)
{
// Yet to be filled - e.g. put meas data into TX FIFOs etc.
// asm("nop");
// This task simulates an audio receive callback, one frame is received every 1ms.
// We assume that the audio data is read from an I2S buffer.
// In a real application, this would be replaced with actual I2S receive callback.
void audio_task(void) {
static uint32_t start_ms = 0;
uint32_t curr_ms = board_millis();
if (start_ms == curr_ms) return;// not enough time
start_ms = curr_ms;
// 16bit
if (bytesPerSample == 2) {
uint16_t *pData_16 = (uint16_t *) ((void *) test_buffer_audio);
for (size_t cnt = 0; cnt < sampFreq / 1000; cnt++) {
pData_16[cnt] = startVal++;
}
}
// 24bit in 32bit slot
else if (bytesPerSample == 4) {
uint32_t *pData_32 = (uint32_t *) ((void *) test_buffer_audio);
for (size_t cnt = 0; cnt < sampFreq / 1000; cnt++) {
pData_32[cnt] = (uint32_t) startVal++ << 16U;
}
}
tud_audio_write((uint8_t *) test_buffer_audio, (uint16_t) (sampFreq / 1000 * bytesPerSample));
}
//--------------------------------------------------------------------+
@@ -168,23 +179,20 @@ void audio_task(void)
//--------------------------------------------------------------------+
// Invoked when set interface is called, typically on start/stop streaming or format change
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void)rhport;
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
//uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
// Clear buffer when streaming format is changed
if(alt != 0)
{
bytesPerSample = bytesPerSampleAltList[alt-1];
if (alt != 0) {
bytesPerSample = bytesPerSampleAltList[alt - 1];
}
return true;
}
// Invoked when audio class specific set request received for an EP
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
(void) pBuff;
@@ -196,14 +204,15 @@ bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t ep = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) ep;
(void) channelNum;
(void) ctrlSel;
(void) ep;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific set request received for an interface
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
(void) pBuff;
@@ -215,14 +224,15 @@ bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t itf = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) itf;
(void) channelNum;
(void) ctrlSel;
(void) itf;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific set request received for an entity
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -237,49 +247,45 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
// If request is for our feature unit
if ( entityID == UAC2_ENTITY_FEATURE_UNIT )
{
switch ( ctrlSel )
{
if (entityID == UAC2_ENTITY_FEATURE_UNIT) {
switch (ctrlSel) {
case AUDIO_FU_CTRL_MUTE:
// Request uses format layout 1
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
mute[channelNum] = ((audio_control_cur_1_t *) pBuff)->bCur;
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
return true;
case AUDIO_FU_CTRL_VOLUME:
// Request uses format layout 2
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t*) pBuff)->bCur;
volume[channelNum] = (uint16_t) ((audio_control_cur_2_t *) pBuff)->bCur;
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
return true;
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
return false;
}
}
// Clock Source unit
if ( entityID == UAC2_ENTITY_CLOCK )
{
switch ( ctrlSel )
{
if (entityID == UAC2_ENTITY_CLOCK) {
switch (ctrlSel) {
case AUDIO_CS_CTRL_SAM_FREQ:
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_4_t));
sampFreq = (uint32_t)((audio_control_cur_4_t *)pBuff)->bCur;
sampFreq = (uint32_t) ((audio_control_cur_4_t *) pBuff)->bCur;
TU_LOG2("Clock set current freq: %" PRIu32 "\r\n", sampFreq);
return true;
break;
break;
// Unknown/Unsupported control
default:
@@ -288,12 +294,11 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
}
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an EP
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -301,16 +306,17 @@ bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_re
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t ep = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) ep;
(void) channelNum;
(void) ctrlSel;
(void) ep;
// return tud_control_xfer(rhport, p_request, &tmp, 1);
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an interface
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -318,14 +324,15 @@ bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_r
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t itf = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) itf;
(void) channelNum;
(void) ctrlSel;
(void) itf;
return false; // Yet not implemented
return false;// Yet not implemented
}
// Invoked when audio class specific get request received for an entity
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
// Page 91 in UAC2 specification
@@ -335,12 +342,9 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// Input terminal (Microphone input)
if (entityID == UAC2_ENTITY_INPUT_TERMINAL)
{
switch ( ctrlSel )
{
case AUDIO_TE_CTRL_CONNECTOR:
{
if (entityID == UAC2_ENTITY_INPUT_TERMINAL) {
switch (ctrlSel) {
case AUDIO_TE_CTRL_CONNECTOR: {
// The terminal connector control only has a get request with only the CUR attribute.
audio_desc_channel_cluster_t ret;
@@ -351,9 +355,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
TU_LOG2(" Get terminal connector\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
}
break;
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
} break;
// Unknown/Unsupported control selector
default:
@@ -363,43 +366,40 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
// Feature unit
if (entityID == UAC2_ENTITY_FEATURE_UNIT)
{
switch ( ctrlSel )
{
if (entityID == UAC2_ENTITY_FEATURE_UNIT) {
switch (ctrlSel) {
case AUDIO_FU_CTRL_MUTE:
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
// There does not exist a range parameter block for mute
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
case AUDIO_FU_CTRL_VOLUME:
switch ( p_request->bRequest )
{
switch (p_request->bRequest) {
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
case AUDIO_CS_REQ_RANGE:
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
audio_control_range_2_n_t(1)
ret;
ret;
ret.wNumSubRanges = 1;
ret.subrange[0].bMin = -90; // -90 dB
ret.subrange[0].bMax = 30; // +30 dB
ret.subrange[0].bRes = 1; // 1 dB steps
ret.subrange[0].bMin = -90;// -90 dB
ret.subrange[0].bMax = 30; // +30 dB
ret.subrange[0].bRes = 1; // 1 dB steps
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void *) &ret, sizeof(ret));
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
break;
// Unknown/Unsupported control
default:
@@ -409,46 +409,40 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
// Clock Source unit
if ( entityID == UAC2_ENTITY_CLOCK )
{
switch ( ctrlSel )
{
if (entityID == UAC2_ENTITY_CLOCK) {
switch (ctrlSel) {
case AUDIO_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch ( p_request->bRequest )
{
switch (p_request->bRequest) {
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
case AUDIO_CS_REQ_RANGE:
{
case AUDIO_CS_REQ_RANGE: {
TU_LOG2(" Get Sample Freq. range\r\n");
audio_control_range_4_n_t(N_sampleRates) rangef =
{
.wNumSubRanges = tu_htole16(N_sampleRates)
};
{
.wNumSubRanges = tu_htole16(N_sampleRates)};
TU_LOG1("Clock get %d freq ranges\r\n", N_sampleRates);
for(uint8_t i = 0; i < N_sampleRates; i++)
{
rangef.subrange[i].bMin = (int32_t)sampleRatesList[i];
rangef.subrange[i].bMax = (int32_t)sampleRatesList[i];
rangef.subrange[i].bRes = 0;
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes);
for (uint8_t i = 0; i < N_sampleRates; i++) {
rangef.subrange[i].bMin = (int32_t) sampleRatesList[i];
rangef.subrange[i].bMax = (int32_t) sampleRatesList[i];
rangef.subrange[i].bRes = 0;
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes);
}
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &rangef, sizeof(rangef));
}
// Unknown/Unsupported control
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
break;
case AUDIO_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG2(" Get Sample Freq. valid\r\n");
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
// Unknown/Unsupported control
default:
@@ -458,53 +452,10 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
}
TU_LOG2(" Unsupported entity: %d\r\n", entityID);
return false; // Yet not implemented
return false;// Yet not implemented
}
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void) rhport;
(void) itf;
(void) ep_in;
(void) cur_alt_setting;
tud_audio_write((uint8_t *)test_buffer_audio, (uint16_t)(sampFreq / (TUD_OPT_HIGH_SPEED ? 8000 : 1000) * bytesPerSample));
return true;
}
bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void) rhport;
(void) n_bytes_copied;
(void) itf;
(void) ep_in;
(void) cur_alt_setting;
// 16bit
if(bytesPerSample == 2)
{
uint16_t* pData_16 = (uint16_t*)((void*)test_buffer_audio);
for (size_t cnt = 0; cnt < sampFreq / (TUD_OPT_HIGH_SPEED ? 8000 : 1000); cnt++)
{
pData_16[cnt] = startVal++;
}
}
// 24bit in 32bit slot
else if(bytesPerSample == 4)
{
uint32_t* pData_32 = (uint32_t*)((void*)test_buffer_audio);
for (size_t cnt = 0; cnt < sampFreq / (TUD_OPT_HIGH_SPEED ? 8000 : 1000); cnt++)
{
pData_32[cnt] = (uint32_t)startVal++ << 16U;
}
}
return true;
}
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
(void) p_request;
startVal = 0;
@@ -515,15 +466,14 @@ bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void)
{
void led_blinking_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; // not enough time
if (board_millis() - start_ms < blink_interval_ms) return;// not enough time
start_ms += blink_interval_ms;
board_led_write(led_state);
led_state = 1 - led_state; // toggle
led_state = 1 - led_state;// toggle
}

View File

@@ -133,7 +133,8 @@ extern "C" {
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX // Example write FIFO every 1ms, so it should be 8 times larger for HS device
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,8 @@
mcu:LPC11UXX
mcu:LPC13XX
mcu:NUC121
mcu:SAMD11
mcu:SAME5X
mcu:SAMG
board:stm32l052dap52
family:espressif

View File

@@ -39,6 +39,7 @@ extern uint32_t blink_interval_ms;
#endif
void led_blinking_task(void);
void audio_task(void);
/*------------- MAIN -------------*/
int main(void)
@@ -62,7 +63,7 @@ int main(void)
{
tud_task(); // TinyUSB device task
led_blinking_task();
audio_task();
#if (CFG_TUSB_MCU == OPT_MCU_RP2040)
// printf("Hello, world!\r\n");
#endif

View File

@@ -145,8 +145,8 @@ extern "C" {
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN)
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
@@ -154,8 +154,8 @@ extern "C" {
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT)
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT) // Maximum EP IN size for all AS alternate settings used
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes)
#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 2
@@ -164,8 +164,11 @@ extern "C" {
#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE 64
#define CFG_TUD_CDC_TX_BUFSIZE 64
#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
// CDC Endpoint transfer buffer size, more is faster
#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#ifdef __cplusplus
}

View File

@@ -49,17 +49,36 @@ uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
// Buffer for microphone data
int32_t mic_buf[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 4];
// Buffer for speaker data
int32_t spk_buf[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ / 4];
// Speaker data size received in the last frame
int spk_data_size;
uint16_t spk_data_size;
// Resolution per format
const uint8_t resolutions_per_format[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] = {CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX};
// Current resolution, update on format change
uint8_t current_resolution;
//--------------------------------------------------------------------+
// AUDIO Task
//--------------------------------------------------------------------+
// This task simulates an audio transfer callback, one frame is sent/received every 1ms.
// In a real application, this would be replaced with actual I2S send/receive callback.
void audio_task(void) {
static uint32_t start_ms = 0;
uint32_t curr_ms = board_millis();
if (start_ms == curr_ms) return;// not enough time
start_ms = curr_ms;
// 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 !
spk_data_size = tud_audio_read(spk_buf, sizeof(spk_buf));
if (spk_data_size) {
tud_audio_write((uint8_t *) spk_buf, spk_data_size);
}
}
// Helper for clock get requests
static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request)
{
@@ -265,8 +284,6 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_reque
blink_interval_ms = BLINK_STREAMING;
}
// Clear buffer when streaming format is changed
spk_data_size = 0;
if(alt != 0)
{
current_resolution = resolutions_per_format[alt-1];
@@ -275,30 +292,6 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_reque
return true;
}
bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting)
{
(void)rhport;
(void)func_id;
(void)ep_out;
(void)cur_alt_setting;
spk_data_size = tud_audio_read(spk_buf, n_bytes_received);
tud_audio_write(spk_buf, n_bytes_received);
return true;
}
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void)rhport;
(void)itf;
(void)ep_in;
(void)cur_alt_setting;
// This callback could be used to fill microphone data separately
return true;
}
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+

View File

@@ -116,7 +116,7 @@ uint8_t const * tud_descriptor_device_cb(void)
#define EPNUM_CDC_IN 0x84
#endif
uint8_t const desc_configuration[] =
uint8_t const desc_fs_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
@@ -128,13 +128,78 @@ uint8_t const desc_configuration[] =
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 6, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64)
};
#if TUD_OPT_HIGH_SPEED
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
// high speed configuration
uint8_t const desc_hs_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(2, EPNUM_AUDIO_OUT, EPNUM_AUDIO_IN | 0x80),
// CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 6, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512)
};
// other speed configuration
uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
tusb_desc_device_qualifier_t const desc_device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = 0x0100,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = 0x01,
.bReserved = 0x00
};
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
// device_qualifier descriptor describes information about a high-speed capable device that would
// change if the device were operating at the other speed. If not highspeed capable stall this request.
uint8_t const *tud_descriptor_device_qualifier_cb(void) {
return (uint8_t const *) &desc_device_qualifier;
}
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
(void) index; // for multiple configurations
// if link speed is high return fullspeed config, and vice versa
// Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG
memcpy(desc_other_speed_config,
(tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration,
CONFIG_TOTAL_LEN);
desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
return desc_other_speed_config;
}
#endif // highspeed
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
(void)index; // for multiple configurations
return desc_configuration;
#if TUD_OPT_HIGH_SPEED
// Although we are highspeed, host may be fullspeed.
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
#else
return desc_fs_configuration;
#endif
}
//--------------------------------------------------------------------+

View File

@@ -37,9 +37,9 @@
// List of supported sample rates
const uint32_t sample_rates[] = {44100, 48000};
uint32_t current_sample_rate = 44100;
uint32_t current_sample_rate = 44100;
#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
/* Blink pattern
* - 25 ms : streaming data
@@ -47,16 +47,14 @@ uint32_t current_sample_rate = 44100;
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
enum
{
enum {
BLINK_STREAMING = 25,
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 1000,
BLINK_SUSPENDED = 2500,
};
enum
{
enum {
VOLUME_CTRL_0_DB = 0,
VOLUME_CTRL_10_DB = 2560,
VOLUME_CTRL_20_DB = 5120,
@@ -75,8 +73,8 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];// +1 for master channel 0
// Buffer for microphone data
int32_t mic_buf[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 4];
@@ -95,15 +93,13 @@ void audio_task(void);
void audio_control_task(void);
/*------------- MAIN -------------*/
int main(void)
{
int main(void) {
board_init();
// init device stack on configured roothub port
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
if (board_init_after_tusb) {
@@ -112,9 +108,8 @@ int main(void)
TU_LOG1("Headset running\r\n");
while (1)
{
tud_task(); // TinyUSB device task
while (1) {
tud_task();// TinyUSB device task
audio_task();
audio_control_task();
led_blinking_task();
@@ -126,70 +121,57 @@ int main(void)
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void)
{
void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
(void)remote_wakeup_en;
void tud_suspend_cb(bool remote_wakeup_en) {
(void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
// Helper for clock get requests
static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request)
{
static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request) {
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ)
{
if (request->bRequest == AUDIO_CS_REQ_CUR)
{
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) {
if (request->bRequest == AUDIO_CS_REQ_CUR) {
TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate);
audio_control_cur_4_t curf = { (int32_t) tu_htole32(current_sample_rate) };
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &curf, sizeof(curf));
}
else if (request->bRequest == AUDIO_CS_REQ_RANGE)
{
audio_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)};
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &curf, sizeof(curf));
} else if (request->bRequest == AUDIO_CS_REQ_RANGE) {
audio_control_range_4_n_t(N_SAMPLE_RATES) rangef =
{
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)
};
{
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)};
TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES);
for(uint8_t i = 0; i < N_SAMPLE_RATES; i++)
{
for (uint8_t i = 0; i < N_SAMPLE_RATES; i++) {
rangef.subrange[i].bMin = (int32_t) sample_rates[i];
rangef.subrange[i].bMax = (int32_t) sample_rates[i];
rangef.subrange[i].bRes = 0;
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes);
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes);
}
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &rangef, sizeof(rangef));
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &rangef, sizeof(rangef));
}
}
else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID &&
request->bRequest == AUDIO_CS_REQ_CUR)
{
audio_control_cur_1_t cur_valid = { .bCur = 1 };
} else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID &&
request->bRequest == AUDIO_CS_REQ_CUR) {
audio_control_cur_1_t cur_valid = {.bCur = 1};
TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur);
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_valid, sizeof(cur_valid));
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_valid, sizeof(cur_valid));
}
TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n",
request->bEntityID, request->bControlSelector, request->bRequest);
@@ -197,25 +179,21 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t
}
// Helper for clock set requests
static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf)
{
(void)rhport;
static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) {
(void) rhport;
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR);
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ)
{
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) {
TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t));
current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur;
current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *) buf)->bCur;
TU_LOG1("Clock set current freq: %" PRIu32 "\r\n", current_sample_rate);
return true;
}
else
{
} else {
TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n",
request->bEntityID, request->bControlSelector, request->bRequest);
return false;
@@ -223,33 +201,25 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t
}
// Helper for feature unit get requests
static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request)
{
static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request) {
TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT);
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR)
{
audio_control_cur_1_t mute1 = { .bCur = mute[request->bChannelNumber] };
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) {
audio_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]};
TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur);
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &mute1, sizeof(mute1));
}
else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME)
{
if (request->bRequest == AUDIO_CS_REQ_RANGE)
{
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &mute1, sizeof(mute1));
} else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) {
if (request->bRequest == AUDIO_CS_REQ_RANGE) {
audio_control_range_2_n_t(1) range_vol = {
.wNumSubRanges = tu_htole16(1),
.subrange[0] = { .bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256) }
};
.wNumSubRanges = tu_htole16(1),
.subrange[0] = {.bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256)}};
TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber,
range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256);
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &range_vol, sizeof(range_vol));
}
else if (request->bRequest == AUDIO_CS_REQ_CUR)
{
audio_control_cur_2_t cur_vol = { .bCur = tu_htole16(volume[request->bChannelNumber]) };
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &range_vol, sizeof(range_vol));
} else if (request->bRequest == AUDIO_CS_REQ_CUR) {
audio_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])};
TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256);
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_vol, sizeof(cur_vol));
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_vol, sizeof(cur_vol));
}
}
TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n",
@@ -259,35 +229,29 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req
}
// Helper for feature unit set requests
static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf)
{
(void)rhport;
static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) {
(void) rhport;
TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT);
TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR);
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE)
{
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) {
TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t));
mute[request->bChannelNumber] = ((audio_control_cur_1_t const *)buf)->bCur;
mute[request->bChannelNumber] = ((audio_control_cur_1_t const *) buf)->bCur;
TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]);
return true;
}
else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME)
{
} else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) {
TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t));
volume[request->bChannelNumber] = ((audio_control_cur_2_t const *)buf)->bCur;
volume[request->bChannelNumber] = ((audio_control_cur_2_t const *) buf)->bCur;
TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256);
return true;
}
else
{
} else {
TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n",
request->bEntityID, request->bControlSelector, request->bRequest);
return false;
@@ -299,16 +263,14 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req
//--------------------------------------------------------------------+
// Invoked when audio class specific get request received for an entity
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request)
{
audio_control_request_t const *request = (audio_control_request_t const *)p_request;
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
audio_control_request_t const *request = (audio_control_request_t const *) p_request;
if (request->bEntityID == UAC2_ENTITY_CLOCK)
return tud_audio_clock_get_request(rhport, request);
if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
return tud_audio_feature_unit_get_request(rhport, request);
else
{
else {
TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n",
request->bEntityID, request->bControlSelector, request->bRequest);
}
@@ -316,9 +278,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
}
// Invoked when audio class specific set request received for an entity
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf)
{
audio_control_request_t const *request = (audio_control_request_t const *)p_request;
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) {
audio_control_request_t const *request = (audio_control_request_t const *) p_request;
if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
return tud_audio_feature_unit_set_request(rhport, request, buf);
@@ -330,108 +291,82 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
return false;
}
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void)rhport;
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0)
blink_interval_ms = BLINK_MOUNTED;
blink_interval_ms = BLINK_MOUNTED;
return true;
}
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void)rhport;
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
TU_LOG2("Set interface %d alt %d\r\n", itf, alt);
if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0)
blink_interval_ms = BLINK_STREAMING;
blink_interval_ms = BLINK_STREAMING;
// Clear buffer when streaming format is changed
spk_data_size = 0;
if(alt != 0)
{
current_resolution = resolutions_per_format[alt-1];
if (alt != 0) {
current_resolution = resolutions_per_format[alt - 1];
}
return true;
}
bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting)
{
(void)rhport;
(void)func_id;
(void)ep_out;
(void)cur_alt_setting;
spk_data_size = tud_audio_read(spk_buf, n_bytes_received);
return true;
}
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void)rhport;
(void)itf;
(void)ep_in;
(void)cur_alt_setting;
// This callback could be used to fill microphone data separately
return true;
}
//--------------------------------------------------------------------+
// AUDIO Task
//--------------------------------------------------------------------+
void audio_task(void)
{
// This task simulates an audio transfer callback, one frame is sent/received every 1ms.
// In a real application, this would be replaced with actual I2S send/receive callback.
void audio_task(void) {
static uint32_t start_ms = 0;
uint32_t curr_ms = board_millis();
if (start_ms == curr_ms) return;// not enough time
start_ms = curr_ms;
// 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)
{
if (current_resolution == 16)
{
int16_t *src = (int16_t*)spk_buf;
int16_t *limit = (int16_t*)spk_buf + spk_data_size / 2;
int16_t *dst = (int16_t*)mic_buf;
while (src < limit)
{
spk_data_size = tud_audio_read(spk_buf, sizeof(spk_buf));
if (spk_data_size) {
if (current_resolution == 16) {
int16_t *src = (int16_t *) spk_buf;
int16_t *limit = (int16_t *) spk_buf + spk_data_size / 2;
int16_t *dst = (int16_t *) mic_buf;
while (src < limit) {
// Combine two channels into one
int32_t left = *src++;
int32_t right = *src++;
*dst++ = (int16_t) ((left >> 1) + (right >> 1));
}
tud_audio_write((uint8_t *)mic_buf, (uint16_t) (spk_data_size / 2));
tud_audio_write((uint8_t *) mic_buf, (uint16_t) (spk_data_size / 2));
spk_data_size = 0;
}
else if (current_resolution == 24)
{
} else if (current_resolution == 24) {
int32_t *src = spk_buf;
int32_t *limit = spk_buf + spk_data_size / 4;
int32_t *dst = mic_buf;
while (src < limit)
{
while (src < limit) {
// Combine two channels into one
int32_t left = *src++;
int32_t right = *src++;
*dst++ = (int32_t) ((uint32_t) ((left >> 1) + (right >> 1)) & 0xffffff00ul);
}
tud_audio_write((uint8_t *)mic_buf, (uint16_t) (spk_data_size / 2));
tud_audio_write((uint8_t *) mic_buf, (uint16_t) (spk_data_size / 2));
spk_data_size = 0;
}
}
}
void audio_control_task(void)
{
void audio_control_task(void) {
// Press on-board button to control volume
// Open host volume control, volume should switch between 10% and 100%
@@ -440,27 +375,25 @@ void audio_control_task(void)
static uint32_t start_ms = 0;
static uint32_t btn_prev = 0;
if ( board_millis() - start_ms < interval_ms) return; // not enough time
if (board_millis() - start_ms < interval_ms) return;// not enough time
start_ms += interval_ms;
uint32_t btn = board_button_read();
if (!btn_prev && btn)
{
if (!btn_prev && btn) {
// Adjust volume between 0dB (100%) and -30dB (10%)
for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++)
{
for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++) {
volume[i] = volume[i] == 0 ? -VOLUME_CTRL_30_DB : 0;
}
// 6.1 Interrupt Data Message
const audio_interrupt_data_t data = {
.bInfo = 0, // Class-specific interrupt, originated from an interface
.bAttribute = AUDIO_CS_REQ_CUR, // Caused by current settings
.wValue_cn_or_mcn = 0, // CH0: master volume
.wValue_cs = AUDIO_FU_CTRL_VOLUME, // Volume change
.wIndex_ep_or_int = 0, // From the interface itself
.wIndex_entity_id = UAC2_ENTITY_SPK_FEATURE_UNIT, // From feature unit
.bInfo = 0, // Class-specific interrupt, originated from an interface
.bAttribute = AUDIO_CS_REQ_CUR, // Caused by current settings
.wValue_cn_or_mcn = 0, // CH0: master volume
.wValue_cs = AUDIO_FU_CTRL_VOLUME, // Volume change
.wIndex_ep_or_int = 0, // From the interface itself
.wIndex_entity_id = UAC2_ENTITY_SPK_FEATURE_UNIT,// From feature unit
};
tud_audio_int_write(&data);
@@ -472,8 +405,7 @@ void audio_control_task(void)
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void)
{
void led_blinking_task(void) {
static uint32_t start_ms = 0;
static bool led_state = false;

View File

@@ -146,8 +146,8 @@ extern "C" {
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN)*4
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense)
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
@@ -155,8 +155,8 @@ extern "C" {
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
#define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX)
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT)*2
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_OUT) // Maximum EP IN size for all AS alternate settings used
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX // Example read FIFO every 1ms, so it should be 8 times larger for HS device
// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes)
#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 2

View File

@@ -27,12 +27,12 @@
#include <string.h>
#include "bsp/board_api.h"
#include "common_types.h"
#include "tusb.h"
#include "usb_descriptors.h"
#include "common_types.h"
#ifdef CFG_QUIRK_OS_GUESSING
#include "quirk_os_guessing.h"
#include "quirk_os_guessing.h"
#endif
//--------------------------------------------------------------------+
@@ -41,14 +41,14 @@
// List of supported sample rates
#if defined(__RX__)
const uint32_t sample_rates[] = {44100, 48000};
const uint32_t sample_rates[] = {44100, 48000};
#else
const uint32_t sample_rates[] = {44100, 48000, 88200, 96000};
const uint32_t sample_rates[] = {44100, 48000, 88200, 96000};
#endif
uint32_t current_sample_rate = 44100;
uint32_t current_sample_rate = 44100;
#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
/* Blink pattern
* - 25 ms : streaming data
@@ -56,16 +56,14 @@ uint32_t current_sample_rate = 44100;
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
enum
{
enum {
BLINK_STREAMING = 25,
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 1000,
BLINK_SUSPENDED = 2500,
};
enum
{
enum {
VOLUME_CTRL_0_DB = 0,
VOLUME_CTRL_10_DB = 2560,
VOLUME_CTRL_20_DB = 5120,
@@ -84,11 +82,11 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];// +1 for master channel 0
// Buffer for speaker data
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ/2];
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ / 2];
void led_blinking_task(void);
void audio_task(void);
@@ -96,20 +94,18 @@ void audio_task(void);
#if CFG_AUDIO_DEBUG
void audio_debug_task(void);
uint8_t current_alt_settings;
uint16_t fifo_count;
uint32_t fifo_count_avg;
volatile uint16_t fifo_count;
volatile uint32_t fifo_count_avg;
#endif
/*------------- MAIN -------------*/
int main(void)
{
int main(void) {
board_init();
// init device stack on configured roothub port
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
if (board_init_after_tusb) {
@@ -118,9 +114,8 @@ int main(void)
TU_LOG1("Speaker running\r\n");
while (1)
{
tud_task(); // TinyUSB device task
while (1) {
tud_task();// TinyUSB device task
led_blinking_task();
#if CFG_AUDIO_DEBUG
audio_debug_task();
@@ -134,29 +129,25 @@ int main(void)
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void)
{
void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
(void)remote_wakeup_en;
void tud_suspend_cb(bool remote_wakeup_en) {
(void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
@@ -165,43 +156,34 @@ void tud_resume_cb(void)
//--------------------------------------------------------------------+
// Helper for clock get requests
static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request)
{
static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request) {
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ)
{
if (request->bRequest == AUDIO_CS_REQ_CUR)
{
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) {
if (request->bRequest == AUDIO_CS_REQ_CUR) {
TU_LOG1("Clock get current freq %lu\r\n", current_sample_rate);
audio_control_cur_4_t curf = { (int32_t) tu_htole32(current_sample_rate) };
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &curf, sizeof(curf));
}
else if (request->bRequest == AUDIO_CS_REQ_RANGE)
{
audio_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)};
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &curf, sizeof(curf));
} else if (request->bRequest == AUDIO_CS_REQ_RANGE) {
audio_control_range_4_n_t(N_SAMPLE_RATES) rangef =
{
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)
};
{
.wNumSubRanges = tu_htole16(N_SAMPLE_RATES)};
TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES);
for(uint8_t i = 0; i < N_SAMPLE_RATES; i++)
{
for (uint8_t i = 0; i < N_SAMPLE_RATES; i++) {
rangef.subrange[i].bMin = (int32_t) sample_rates[i];
rangef.subrange[i].bMax = (int32_t) sample_rates[i];
rangef.subrange[i].bRes = 0;
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes);
TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes);
}
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &rangef, sizeof(rangef));
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &rangef, sizeof(rangef));
}
}
else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID &&
request->bRequest == AUDIO_CS_REQ_CUR)
{
audio_control_cur_1_t cur_valid = { .bCur = 1 };
} else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID &&
request->bRequest == AUDIO_CS_REQ_CUR) {
audio_control_cur_1_t cur_valid = {.bCur = 1};
TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur);
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_valid, sizeof(cur_valid));
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_valid, sizeof(cur_valid));
}
TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n",
request->bEntityID, request->bControlSelector, request->bRequest);
@@ -209,25 +191,21 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t
}
// Helper for clock set requests
static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf)
{
(void)rhport;
static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) {
(void) rhport;
TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK);
TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR);
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ)
{
if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) {
TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t));
current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur;
current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *) buf)->bCur;
TU_LOG1("Clock set current freq: %ld\r\n", current_sample_rate);
return true;
}
else
{
} else {
TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n",
request->bEntityID, request->bControlSelector, request->bRequest);
return false;
@@ -235,33 +213,25 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t
}
// Helper for feature unit get requests
static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request)
{
static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request) {
TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT);
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR)
{
audio_control_cur_1_t mute1 = { .bCur = mute[request->bChannelNumber] };
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) {
audio_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]};
TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur);
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &mute1, sizeof(mute1));
}
else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME)
{
if (request->bRequest == AUDIO_CS_REQ_RANGE)
{
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &mute1, sizeof(mute1));
} else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) {
if (request->bRequest == AUDIO_CS_REQ_RANGE) {
audio_control_range_2_n_t(1) range_vol = {
.wNumSubRanges = tu_htole16(1),
.subrange[0] = { .bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256) }
};
.wNumSubRanges = tu_htole16(1),
.subrange[0] = {.bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256)}};
TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber,
range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256);
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &range_vol, sizeof(range_vol));
}
else if (request->bRequest == AUDIO_CS_REQ_CUR)
{
audio_control_cur_2_t cur_vol = { .bCur = tu_htole16(volume[request->bChannelNumber]) };
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &range_vol, sizeof(range_vol));
} else if (request->bRequest == AUDIO_CS_REQ_CUR) {
audio_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])};
TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256);
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_vol, sizeof(cur_vol));
return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_vol, sizeof(cur_vol));
}
}
TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n",
@@ -271,35 +241,29 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req
}
// Helper for feature unit set requests
static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf)
{
(void)rhport;
static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) {
(void) rhport;
TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT);
TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR);
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE)
{
if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) {
TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t));
mute[request->bChannelNumber] = ((audio_control_cur_1_t const *)buf)->bCur;
mute[request->bChannelNumber] = ((audio_control_cur_1_t const *) buf)->bCur;
TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]);
return true;
}
else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME)
{
} else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) {
TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t));
volume[request->bChannelNumber] = ((audio_control_cur_2_t const *)buf)->bCur;
volume[request->bChannelNumber] = ((audio_control_cur_2_t const *) buf)->bCur;
TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256);
return true;
}
else
{
} else {
TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n",
request->bEntityID, request->bControlSelector, request->bRequest);
return false;
@@ -307,16 +271,14 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req
}
// Invoked when audio class specific get request received for an entity
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request)
{
audio_control_request_t const *request = (audio_control_request_t const *)p_request;
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
audio_control_request_t const *request = (audio_control_request_t const *) p_request;
if (request->bEntityID == UAC2_ENTITY_CLOCK)
return tud_audio_clock_get_request(rhport, request);
if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT)
return tud_audio_feature_unit_get_request(rhport, request);
else
{
else {
TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n",
request->bEntityID, request->bControlSelector, request->bRequest);
}
@@ -324,9 +286,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
}
// Invoked when audio class specific set request received for an entity
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf)
{
audio_control_request_t const *request = (audio_control_request_t const *)p_request;
bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) {
audio_control_request_t const *request = (audio_control_request_t const *) p_request;
if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT)
return tud_audio_feature_unit_set_request(rhport, request, buf);
@@ -338,28 +299,26 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p
return false;
}
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void)rhport;
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
if (ITF_NUM_AUDIO_STREAMING == itf && alt == 0)
blink_interval_ms = BLINK_MOUNTED;
blink_interval_ms = BLINK_MOUNTED;
return true;
}
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void)rhport;
bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
(void) rhport;
uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex));
uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue));
TU_LOG2("Set interface %d alt %d\r\n", itf, alt);
if (ITF_NUM_AUDIO_STREAMING == itf && alt != 0)
blink_interval_ms = BLINK_STREAMING;
blink_interval_ms = BLINK_STREAMING;
#if CFG_AUDIO_DEBUG
current_alt_settings = alt;
@@ -368,37 +327,34 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_reque
return true;
}
void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t* feedback_param)
{
(void)func_id;
(void)alt_itf;
void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t *feedback_param) {
(void) func_id;
(void) alt_itf;
// Set feedback method to fifo counting
feedback_param->method = AUDIO_FEEDBACK_METHOD_FIFO_COUNT;
feedback_param->sample_freq = current_sample_rate;
}
#if CFG_AUDIO_DEBUG
bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting)
{
(void)rhport;
(void)n_bytes_received;
(void)func_id;
(void)ep_out;
(void)cur_alt_setting;
bool tud_audio_rx_done_isr(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) {
(void) rhport;
(void) n_bytes_received;
(void) func_id;
(void) ep_out;
(void) cur_alt_setting;
fifo_count = tud_audio_available();
// Same averaging method used in UAC2 class
fifo_count_avg = (uint32_t)(((uint64_t)fifo_count_avg * 63 + ((uint32_t)fifo_count << 16)) >> 6);
fifo_count_avg = (uint32_t) (((uint64_t) fifo_count_avg * 63 + ((uint32_t) fifo_count << 16)) >> 6);
return true;
}
#endif
#if CFG_QUIRK_OS_GUESSING
bool tud_audio_feedback_format_correction_cb(uint8_t func_id)
{
(void)func_id;
if(tud_speed_get() == TUSB_SPEED_FULL && quirk_os_guessing_get() == QUIRK_OS_GUESSING_OSX) {
bool tud_audio_feedback_format_correction_cb(uint8_t func_id) {
(void) func_id;
if (tud_speed_get() == TUSB_SPEED_FULL && quirk_os_guessing_get() == QUIRK_OS_GUESSING_OSX) {
return true;
} else {
return false;
@@ -409,25 +365,21 @@ bool tud_audio_feedback_format_correction_cb(uint8_t func_id)
// AUDIO Task
//--------------------------------------------------------------------+
void audio_task(void)
{
// Replace audio_task() with your I2S transmit callback.
// Here we simulate a callback called every 1ms.
// This task simulates an audio transmit callback, one frame is sent every 1ms.
// In a real application, this would be replaced with actual I2S transmit callback.
void audio_task(void) {
static uint32_t start_ms = 0;
uint32_t curr_ms = board_millis();
if ( start_ms == curr_ms ) return; // not enough time
if (start_ms == curr_ms) return;// not enough time
start_ms = curr_ms;
uint16_t length = (uint16_t) (current_sample_rate/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX);
uint16_t length = (uint16_t) (current_sample_rate / 1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX);
if (current_sample_rate == 44100 && (curr_ms % 10 == 0))
{
if (current_sample_rate == 44100 && (curr_ms % 10 == 0)) {
// Take one more sample every 10 cycles, to have a average reading speed of 44.1
// This correction is not needed in real world cases
length += CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX;
} else
if (current_sample_rate == 88200 && (curr_ms % 5 == 0))
{
} else if (current_sample_rate == 88200 && (curr_ms % 5 == 0)) {
// Take one more sample every 5 cycles, to have a average reading speed of 88.2
// This correction is not needed in real world cases
length += CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX;
@@ -439,8 +391,7 @@ void audio_task(void)
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void)
{
void led_blinking_task(void) {
static uint32_t start_ms = 0;
static bool led_state = false;
@@ -457,33 +408,30 @@ void led_blinking_task(void)
// HID interface for audio debug
//--------------------------------------------------------------------+
// Every 1ms, we will sent 1 debug information report
void audio_debug_task(void)
{
void audio_debug_task(void) {
static uint32_t start_ms = 0;
uint32_t curr_ms = board_millis();
if ( start_ms == curr_ms ) return; // not enough time
if (start_ms == curr_ms) return;// not enough time
start_ms = curr_ms;
audio_debug_info_t debug_info;
debug_info.sample_rate = current_sample_rate;
debug_info.alt_settings = current_alt_settings;
debug_info.fifo_size = CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ;
debug_info.fifo_count = fifo_count;
debug_info.sample_rate = current_sample_rate;
debug_info.alt_settings = current_alt_settings;
debug_info.fifo_size = CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ;
debug_info.fifo_count = fifo_count;
debug_info.fifo_count_avg = (uint16_t) (fifo_count_avg >> 16);
for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++)
{
for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++) {
debug_info.mute[i] = mute[i];
debug_info.volume[i] = volume[i];
}
if(tud_hid_ready())
if (tud_hid_ready())
tud_hid_report(0, &debug_info, sizeof(debug_info));
}
// Invoked when received GET_REPORT control request
// Unused here
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
{
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
// TODO not Implemented
(void) itf;
(void) report_id;
@@ -496,8 +444,7 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t
// Invoked when received SET_REPORT control request or
// Unused here
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
{
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {
// This example doesn't use multiple report and report ID
(void) itf;
(void) report_id;

View File

@@ -61,7 +61,7 @@ static board_pindef_t board_pindef[] = {
},
{ // Button
.port = GPIOC,
.pin_init = { .Pin = GPIO_PIN_13, .Mode = GPIO_MODE_INPUT, .Pull = GPIO_PULLDOWN, .Speed = GPIO_SPEED_FREQ_HIGH, .Alternate = 0 },
.pin_init = { .Pin = GPIO_PIN_13, .Mode = GPIO_MODE_INPUT, .Pull = GPIO_PULLUP, .Speed = GPIO_SPEED_FREQ_HIGH, .Alternate = 0 },
.active_state = 1
},
{ // UART TX

View File

@@ -109,17 +109,17 @@
// Put swap buffer in USB section only if necessary
#if USE_LINEAR_BUFFER
#define IN_SW_BUF_MEM_ATTR
#define IN_SW_BUF_MEM_ATTR TU_ATTR_ALIGNED(4)
#else
#define IN_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION
#define IN_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN
#endif
#if USE_LINEAR_BUFFER
#define OUT_SW_BUF_MEM_ATTR
#define OUT_SW_BUF_MEM_ATTR TU_ATTR_ALIGNED(4)
#else
#define OUT_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION
#define OUT_SW_BUF_MEM_ATTR CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN
#endif
// EP IN software buffers and mutexes
// EP IN software buffers
#if CFG_TUD_AUDIO_ENABLE_EP_IN
tu_static IN_SW_BUF_MEM_ATTR struct {
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
@@ -132,18 +132,6 @@ tu_static IN_SW_BUF_MEM_ATTR struct {
TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ);
#endif
} ep_in_sw_buf;
#if CFG_FIFO_MUTEX
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
tu_static osal_mutex_def_t ep_in_ff_mutex_wr_1;
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
tu_static osal_mutex_def_t ep_in_ff_mutex_wr_2;
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
tu_static osal_mutex_def_t ep_in_ff_mutex_wr_3;
#endif
#endif
#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
// Linear buffer TX in case:
@@ -162,7 +150,7 @@ tu_static CFG_TUD_MEM_SECTION struct {
} lin_buf_in;
#endif// CFG_TUD_AUDIO_ENABLE_EP_IN && USE_LINEAR_BUFFER
// EP OUT software buffers and mutexes
// EP OUT software buffers
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
tu_static OUT_SW_BUF_MEM_ATTR struct {
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
@@ -175,18 +163,6 @@ tu_static OUT_SW_BUF_MEM_ATTR struct {
TUD_EPBUF_DEF(buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ);
#endif
} ep_out_sw_buf;
#if CFG_FIFO_MUTEX
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
tu_static osal_mutex_def_t ep_out_ff_mutex_rd_1;
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
tu_static osal_mutex_def_t ep_out_ff_mutex_rd_2;
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
tu_static osal_mutex_def_t ep_out_ff_mutex_rd_3;
#endif
#endif
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
// Linear buffer RX in case:
@@ -371,35 +347,19 @@ typedef struct
//--------------------------------------------------------------------+
#if CFG_TUD_AUDIO_ENABLE_EP_IN
TU_ATTR_WEAK bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting) {
TU_ATTR_WEAK bool tud_audio_tx_done_isr(uint8_t rhport, uint16_t n_bytes_sent, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting) {
(void) rhport;
(void) n_bytes_sent;
(void) func_id;
(void) ep_in;
(void) cur_alt_setting;
return true;
}
TU_ATTR_WEAK bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting) {
(void) rhport;
(void) n_bytes_copied;
(void) func_id;
(void) ep_in;
(void) cur_alt_setting;
return true;
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
TU_ATTR_WEAK bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) {
(void) rhport;
(void) n_bytes_received;
(void) func_id;
(void) ep_out;
(void) cur_alt_setting;
return true;
}
TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) {
TU_ATTR_WEAK bool tud_audio_rx_done_isr(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) {
(void) rhport;
(void) n_bytes_received;
(void) func_id;
@@ -410,10 +370,6 @@ TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_byte
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
TU_ATTR_WEAK void tud_audio_fb_done_cb(uint8_t func_id) {
(void) func_id;
}
TU_ATTR_WEAK void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t *feedback_param) {
(void) func_id;
(void) alt_itf;
@@ -433,7 +389,7 @@ TU_ATTR_WEAK TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func
#endif
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
TU_ATTR_WEAK void tud_audio_int_done_cb(uint8_t rhport) {
TU_ATTR_WEAK void tud_audio_int_xfer_cb(uint8_t rhport) {
(void) rhport;
}
#endif
@@ -509,11 +465,11 @@ TU_ATTR_WEAK bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_reque
tu_static CFG_TUD_MEM_SECTION audiod_function_t _audiod_fct[CFG_TUD_AUDIO];
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t *audio, uint16_t n_bytes_received);
static bool audiod_rx_xfer_isr(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN
static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t *audio);
static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_sent);
#endif
static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const *p_request);
@@ -566,22 +522,19 @@ bool tud_audio_n_clear_ep_out_ff(uint8_t func_id) {
}
tu_fifo_t *tud_audio_n_get_ep_out_ff(uint8_t func_id) {
if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) return &_audiod_fct[func_id].ep_out_ff;
if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) {
return &_audiod_fct[func_id].ep_out_ff;
}
return NULL;
}
// This function is called once an audio packet is received by the USB and is responsible for putting data from USB memory into EP_OUT_FIFO.
static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t *audio, uint16_t n_bytes_received) {
uint8_t idxItf = 0;
static bool audiod_rx_xfer_isr(uint8_t rhport, audiod_function_t* audio, uint16_t n_bytes_received) {
uint8_t idxItf;
uint8_t const *dummy2;
uint8_t idx_audio_fct = 0;
idx_audio_fct = audiod_get_audio_fct_idx(audio);
uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio);
TU_VERIFY(audiod_get_AS_interface_index(audio->ep_out_as_intf_num, audio, &idxItf, &dummy2));
// Call a weak callback here - a possibility for user to get informed an audio packet was received and data gets now loaded into EP FIFO
TU_VERIFY(tud_audio_rx_done_pre_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
#if USE_LINEAR_BUFFER_RX
// Data currently is in linear buffer, copy into EP OUT FIFO
TU_VERIFY(tu_fifo_write_n(&audio->ep_out_ff, audio->lin_buf_out, n_bytes_received));
@@ -599,8 +552,8 @@ static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t *audio, uint16_t
}
#endif
// Call a weak callback here - a possibility for user to get informed decoding was completed
TU_VERIFY(tud_audio_rx_done_post_read_cb(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
// Call a weak callback here - a possibility for user to get informed an audio packet was received and data gets now loaded into EP FIFO
TU_VERIFY(tud_audio_rx_done_isr(rhport, n_bytes_received, idx_audio_fct, audio->ep_out, audio->alt_setting[idxItf]));
return true;
}
@@ -613,36 +566,24 @@ static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t *audio, uint16_t
#if CFG_TUD_AUDIO_ENABLE_EP_IN
/**
* \brief Write data to EP in buffer
*
* Write data to buffer. If it is full, new data can be inserted once a transmit was scheduled. See audiod_tx_done_cb().
* If TX FIFOs are used, this function is not available in order to not let the user mess up the encoding process.
*
* \param[in] func_id: Index of audio function interface
* \param[in] data: Pointer to data array to be copied from
* \param[in] len: # of array elements to copy
* \return Number of bytes actually written
*/
uint16_t tud_audio_n_write(uint8_t func_id, const void *data, uint16_t len) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
return tu_fifo_write_n(&_audiod_fct[func_id].ep_in_ff, data, len);
}
bool tud_audio_n_clear_ep_in_ff(uint8_t func_id)// Delete all content in the EP IN FIFO
{
bool tud_audio_n_clear_ep_in_ff(uint8_t func_id) {
TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
return tu_fifo_clear(&_audiod_fct[func_id].ep_in_ff);
}
tu_fifo_t *tud_audio_n_get_ep_in_ff(uint8_t func_id) {
if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) return &_audiod_fct[func_id].ep_in_ff;
if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL) {
return &_audiod_fct[func_id].ep_in_ff;
}
return NULL;
}
// This function is called once a transmit of an audio packet was successfully completed. Here, we encode samples and place it in IN EP's buffer for next transmission.
// n_bytes_copied - Informs caller how many bytes were loaded. In case n_bytes_copied = 0, a ZLP is scheduled to inform host no data is available for current frame.
static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t *audio) {
static bool audiod_tx_xfer_isr(uint8_t rhport, audiod_function_t * audio, uint16_t n_bytes_sent) {
uint8_t idxItf;
uint8_t const *dummy2;
@@ -650,17 +591,13 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t *audio) {
TU_VERIFY(audiod_get_AS_interface_index(audio->ep_in_as_intf_num, audio, &idxItf, &dummy2));
// Only send something if current alternate interface is not 0 as in this case nothing is to be sent due to UAC2 specifications
if (audio->alt_setting[idxItf] == 0) return false;
// Call a weak callback here - a possibility for user to get informed former TX was completed and data gets now loaded into EP in buffer (in case FIFOs are used) or
// if no FIFOs are used the user may use this call back to load its data into the EP IN buffer by use of tud_audio_n_write_ep_in_buffer().
TU_VERIFY(tud_audio_tx_done_pre_load_cb(rhport, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf]));
if (audio->alt_setting[idxItf] == 0) { return false; }
// Send everything in ISO EP FIFO
uint16_t n_bytes_tx;
#if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
// packet_sz_tx is based on total packet size
// packet_sz_tx is based on total packet size, here we want size for each support buffer.
n_bytes_tx = audiod_tx_packet_size(audio->packet_sz_tx, tu_fifo_count(&audio->ep_in_ff), audio->ep_in_ff.depth, audio->ep_in_sz);
#else
n_bytes_tx = tu_min16(tu_fifo_count(&audio->ep_in_ff), audio->ep_in_sz);// Limit up to max packet size, more can not be done for ISO
@@ -673,8 +610,8 @@ static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t *audio) {
TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, n_bytes_tx));
#endif
// Call a weak callback here - a possibility for user to get informed former TX was completed and how many bytes were loaded for the next frame
TU_VERIFY(tud_audio_tx_done_post_load_cb(rhport, n_bytes_tx, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf]));
// Call a weak callback here - a possibility for user to get informed former TX was completed and data gets now loaded into EP in buffer
TU_VERIFY(tud_audio_tx_done_isr(rhport, n_bytes_sent, idx_audio_fct, audio->ep_in, audio->alt_setting[idxItf]));
return true;
}
@@ -793,25 +730,16 @@ void audiod_init(void) {
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ > 0
case 0:
tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ, 1, true);
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_1), NULL);
#endif
break;
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ > 0
case 1:
tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ, 1, true);
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_2), NULL);
#endif
break;
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ > 0
case 2:
tu_fifo_config(&audio->ep_in_ff, ep_in_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ, 1, true);
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->ep_in_ff, osal_mutex_create(&ep_in_ff_mutex_wr_3), NULL);
#endif
break;
#endif
}
@@ -845,25 +773,16 @@ void audiod_init(void) {
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ > 0
case 0:
tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_1, CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ, 1, true);
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_1));
#endif
break;
#endif
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ > 0
case 1:
tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_2, CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ, 1, true);
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_2));
#endif
break;
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ > 0
case 2:
tu_fifo_config(&audio->ep_out_ff, ep_out_sw_buf.buf_3, CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ, 1, true);
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&audio->ep_out_ff, NULL, osal_mutex_create(&ep_out_ff_mutex_rd_3));
#endif
break;
#endif
}
@@ -1241,15 +1160,19 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
#if CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
audiod_parse_flow_control_params(audio, p_desc_parse_for_params);
#endif
// Schedule first transmit if alternate interface is not zero i.e. streaming is disabled - in case no sample data is available a ZLP is loaded
// It is necessary to trigger this here since the refill is done with an RX FIFO empty interrupt which can only trigger if something was in there
TU_VERIFY(audiod_tx_done_cb(rhport, &_audiod_fct[func_id]));
// Schedule first transmit if alternate interface is not zero, as sample data is available a ZLP is loaded
#if USE_LINEAR_BUFFER_TX
TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, 0));
#else
// Send everything in ISO EP FIFO
TU_VERIFY(usbd_edpt_xfer_fifo(rhport, audio->ep_in, &audio->ep_in_ff, 0));
#endif
}
#endif// CFG_TUD_AUDIO_ENABLE_EP_IN
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT)// Checking usage not necessary
{
// Checking usage not necessary
if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) {
// Save address
audio->ep_out = ep_addr;
audio->ep_out_as_intf_num = itf;
@@ -1264,10 +1187,12 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p
}
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 1)// Check if usage is explicit data feedback
{
// Check if usage is explicit data feedback
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN && desc_ep->bmAttributes.usage == 1) {
audio->ep_fb = ep_addr;
audio->feedback.frame_shift = desc_ep->bInterval - 1;
// Schedule first feedback transmit
audiod_fb_send(audio);
}
#endif
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT
@@ -1510,12 +1435,11 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
(void) result;
(void) xferred_bytes;
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
// Search for interface belonging to given end point address and proceed as required
for (uint8_t func_id = 0; func_id < CFG_TUD_AUDIO; func_id++) {
audiod_function_t *audio = &_audiod_fct[func_id];
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
// Data transmission of control interrupt finished
if (audio->ep_int == ep_addr) {
// According to USB2 specification, maximum payload of interrupt EP is 8 bytes on low speed, 64 bytes on full speed, and 1024 bytes on high speed (but only if an alternate interface other than 0 is used - see specification p. 49)
@@ -1525,16 +1449,33 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
// I assume here, that things above are handled by PHY
// All transmission is done - what remains to do is to inform job was completed
tud_audio_int_done_cb(rhport);
tud_audio_int_xfer_cb(rhport);
return true;
}
#endif
}
#else
(void) rhport;
(void) ep_addr;
#endif
return false;
}
bool audiod_xfer_isr(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) result;
(void) xferred_bytes;
// Search for interface belonging to given end point address and proceed as required
for (uint8_t func_id = 0; func_id < CFG_TUD_AUDIO; func_id++)
{
audiod_function_t* audio = &_audiod_fct[func_id];
#if CFG_TUD_AUDIO_ENABLE_EP_IN
// Data transmission of audio packet finished
if (audio->ep_in == ep_addr && audio->alt_setting != 0) {
if (audio->ep_in == ep_addr && audio->alt_setting != 0)
{
// USB 2.0, section 5.6.4, third paragraph, states "An isochronous endpoint must specify its required bus access period. However, an isochronous endpoint must be prepared to handle poll rates faster than the one specified."
// That paragraph goes on to say "An isochronous IN endpoint must return a zero-length packet whenever data is requested at a faster interval than the specified interval and data is not available."
// This can only be solved reliably if we load a ZLP after every IN transmission since we can not say if the host requests samples earlier than we declared! Once all samples are collected we overwrite the loaded ZLP.
@@ -1544,32 +1485,25 @@ bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3
// This is the only place where we can fill something into the EPs buffer!
// Load new data
TU_VERIFY(audiod_tx_done_cb(rhport, audio));
// Transmission of ZLP is done by audiod_tx_done_cb()
audiod_tx_xfer_isr(rhport, audio, (uint16_t) xferred_bytes);
return true;
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
// New audio packet received
if (audio->ep_out == ep_addr) {
TU_VERIFY(audiod_rx_done_cb(rhport, audio, (uint16_t) xferred_bytes));
if (audio->ep_out == ep_addr)
{
audiod_rx_xfer_isr(rhport, audio, (uint16_t) xferred_bytes);
return true;
}
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
// Transmission of feedback EP finished
if (audio->ep_fb == ep_addr) {
tud_audio_fb_done_cb(func_id);
// Schedule a transmit with the new value if EP is not busy
if (usbd_edpt_claim(rhport, audio->ep_fb)) {
// Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent
return audiod_fb_send(audio);
}
// Schedule next transmission - value is changed bytud_audio_n_fb_set() in the meantime or the old value gets sent
audiod_fb_send(audio);
return true;
}
#endif
#endif
@@ -1630,11 +1564,6 @@ static void audiod_fb_fifo_count_update(audiod_function_t *audio, uint16_t lvl_n
if (feedback > audio->feedback.max_value) feedback = audio->feedback.max_value;
if (feedback < audio->feedback.min_value) feedback = audio->feedback.min_value;
audio->feedback.value = feedback;
// Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value
if (usbd_edpt_claim(audio->rhport, audio->ep_fb)) {
audiod_fb_send(audio);
}
}
uint32_t tud_audio_feedback_update(uint8_t func_id, uint32_t cycles) {
@@ -1676,11 +1605,6 @@ bool tud_audio_n_fb_set(uint8_t func_id, uint32_t feedback) {
_audiod_fct[func_id].feedback.value = feedback;
// Schedule a transmit with the new value if EP is not busy - this triggers repetitive scheduling of the feedback value
if (usbd_edpt_claim(_audiod_fct[func_id].rhport, _audiod_fct[func_id].ep_fb)) {
return audiod_fb_send(&_audiod_fct[func_id]);
}
return true;
}
#endif

View File

@@ -26,8 +26,8 @@
* This file is part of the TinyUSB stack.
*/
#ifndef _TUSB_AUDIO_DEVICE_H_
#define _TUSB_AUDIO_DEVICE_H_
#ifndef TUSB_AUDIO_DEVICE_H_
#define TUSB_AUDIO_DEVICE_H_
#include "audio.h"
@@ -219,19 +219,19 @@ extern "C" {
// Application API (Multiple Interfaces)
// CFG_TUD_AUDIO > 1
//--------------------------------------------------------------------+
bool tud_audio_n_mounted (uint8_t func_id);
bool tud_audio_n_mounted(uint8_t func_id);
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
uint16_t tud_audio_n_available (uint8_t func_id);
uint16_t tud_audio_n_read (uint8_t func_id, void* buffer, uint16_t bufsize);
bool tud_audio_n_clear_ep_out_ff (uint8_t func_id); // Delete all content in the EP OUT FIFO
tu_fifo_t* tud_audio_n_get_ep_out_ff (uint8_t func_id);
uint16_t tud_audio_n_available (uint8_t func_id);
uint16_t tud_audio_n_read (uint8_t func_id, void* buffer, uint16_t bufsize);
bool tud_audio_n_clear_ep_out_ff (uint8_t func_id);
tu_fifo_t* tud_audio_n_get_ep_out_ff (uint8_t func_id);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN
uint16_t tud_audio_n_write (uint8_t func_id, const void * data, uint16_t len);
bool tud_audio_n_clear_ep_in_ff (uint8_t func_id); // Delete all content in the EP IN FIFO
tu_fifo_t* tud_audio_n_get_ep_in_ff (uint8_t func_id);
uint16_t tud_audio_n_write (uint8_t func_id, const void * data, uint16_t len);
bool tud_audio_n_clear_ep_in_ff (uint8_t func_id);
tu_fifo_t* tud_audio_n_get_ep_in_ff (uint8_t func_id);
#endif
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
@@ -241,24 +241,19 @@ bool tud_audio_int_n_write (uint8_t func_id, const audio_
//--------------------------------------------------------------------+
// Application API (Interface0)
//--------------------------------------------------------------------+
static inline bool tud_audio_mounted (void);
// RX API
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
static inline uint16_t tud_audio_available (void);
static inline bool tud_audio_clear_ep_out_ff (void); // Delete all content in the EP OUT FIFO
static inline uint16_t tud_audio_read (void* buffer, uint16_t bufsize);
static inline tu_fifo_t* tud_audio_get_ep_out_ff (void);
static inline uint16_t tud_audio_available (void);
static inline bool tud_audio_clear_ep_out_ff (void);
static inline uint16_t tud_audio_read (void* buffer, uint16_t bufsize);
static inline tu_fifo_t* tud_audio_get_ep_out_ff (void);
#endif
// TX API
#if CFG_TUD_AUDIO_ENABLE_EP_IN
static inline uint16_t tud_audio_write (const void * data, uint16_t len);
static inline bool tud_audio_clear_ep_in_ff (void);
static inline tu_fifo_t* tud_audio_get_ep_in_ff (void);
static inline uint16_t tud_audio_write (const void * data, uint16_t len);
static inline bool tud_audio_clear_ep_in_ff (void);
static inline tu_fifo_t* tud_audio_get_ep_in_ff (void);
#endif
// INT CTR API
@@ -280,18 +275,18 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
//--------------------------------------------------------------------+
#if CFG_TUD_AUDIO_ENABLE_EP_IN
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting);
bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting);
// Invoked in ISR context once an audio packet was sent successfully.
// Normally this function is not needed, since the data transfer should be driven by audio clock (i.e. I2S clock), call tud_audio_write() in I2S receive callback.
bool tud_audio_tx_done_isr(uint8_t rhport, uint16_t n_bytes_sent, uint8_t func_id, uint8_t ep_in, uint8_t cur_alt_setting);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
// Invoked in ISR context once an audio packet was received successfully.
// Normally this function is not needed, since the data transfer should be driven by audio clock (i.e. I2S clock), call tud_audio_read() in I2S transmit callback.
bool tud_audio_rx_done_isr(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
void tud_audio_fb_done_cb(uint8_t func_id);
// Note about feedback calculation
//
@@ -410,72 +405,56 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
// Inline Functions
//--------------------------------------------------------------------+
static inline bool tud_audio_mounted(void)
{
TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_mounted(void) {
return tud_audio_n_mounted(0);
}
// RX API
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
static inline uint16_t tud_audio_available(void)
{
TU_ATTR_ALWAYS_INLINE static inline uint16_t tud_audio_available(void) {
return tud_audio_n_available(0);
}
static inline uint16_t tud_audio_read(void* buffer, uint16_t bufsize)
{
TU_ATTR_ALWAYS_INLINE static inline uint16_t tud_audio_read(void* buffer, uint16_t bufsize) {
return tud_audio_n_read(0, buffer, bufsize);
}
static inline bool tud_audio_clear_ep_out_ff(void)
{
TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_clear_ep_out_ff(void) {
return tud_audio_n_clear_ep_out_ff(0);
}
static inline tu_fifo_t* tud_audio_get_ep_out_ff(void)
{
TU_ATTR_ALWAYS_INLINE static inline tu_fifo_t* tud_audio_get_ep_out_ff(void) {
return tud_audio_n_get_ep_out_ff(0);
}
#endif
// TX API
#if CFG_TUD_AUDIO_ENABLE_EP_IN
static inline uint16_t tud_audio_write(const void * data, uint16_t len)
{
TU_ATTR_ALWAYS_INLINE static inline uint16_t tud_audio_write(const void * data, uint16_t len) {
return tud_audio_n_write(0, data, len);
}
static inline bool tud_audio_clear_ep_in_ff(void)
{
TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_clear_ep_in_ff(void) {
return tud_audio_n_clear_ep_in_ff(0);
}
static inline tu_fifo_t* tud_audio_get_ep_in_ff(void)
{
TU_ATTR_ALWAYS_INLINE static inline tu_fifo_t* tud_audio_get_ep_in_ff(void) {
return tud_audio_n_get_ep_in_ff(0);
}
#endif
#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
static inline bool tud_audio_int_write(const audio_interrupt_data_t * data)
{
TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_int_write(const audio_interrupt_data_t * data) {
return tud_audio_int_n_write(0, data);
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
static inline bool tud_audio_fb_set(uint32_t feedback)
{
TU_ATTR_ALWAYS_INLINE static inline bool tud_audio_fb_set(uint32_t feedback) {
return tud_audio_n_fb_set(0, feedback);
}
#endif
//--------------------------------------------------------------------+
@@ -487,6 +466,7 @@ void audiod_reset (uint8_t rhport);
uint16_t audiod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
bool audiod_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
bool audiod_xfer_isr (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
void audiod_sof_isr (uint8_t rhport, uint32_t frame_count);
#ifdef __cplusplus

View File

@@ -163,6 +163,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = cdcd_open,
.control_xfer_cb = cdcd_control_xfer_cb,
.xfer_cb = cdcd_xfer_cb,
.xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -176,6 +177,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = mscd_open,
.control_xfer_cb = mscd_control_xfer_cb,
.xfer_cb = mscd_xfer_cb,
.xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -189,6 +191,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = hidd_open,
.control_xfer_cb = hidd_control_xfer_cb,
.xfer_cb = hidd_xfer_cb,
.xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -202,6 +205,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = audiod_open,
.control_xfer_cb = audiod_control_xfer_cb,
.xfer_cb = audiod_xfer_cb,
.xfer_isr = audiod_xfer_isr,
.sof = audiod_sof_isr
},
#endif
@@ -215,6 +219,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = videod_open,
.control_xfer_cb = videod_control_xfer_cb,
.xfer_cb = videod_xfer_cb,
.xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -228,6 +233,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.reset = midid_reset,
.control_xfer_cb = midid_control_xfer_cb,
.xfer_cb = midid_xfer_cb,
.xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -241,6 +247,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = vendord_open,
.control_xfer_cb = tud_vendor_control_xfer_cb,
.xfer_cb = vendord_xfer_cb,
.xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -254,6 +261,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = usbtmcd_open_cb,
.control_xfer_cb = usbtmcd_control_xfer_cb,
.xfer_cb = usbtmcd_xfer_cb,
.xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -267,6 +275,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = dfu_rtd_open,
.control_xfer_cb = dfu_rtd_control_xfer_cb,
.xfer_cb = NULL,
.xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -280,6 +289,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = dfu_moded_open,
.control_xfer_cb = dfu_moded_control_xfer_cb,
.xfer_cb = NULL,
.xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -293,7 +303,8 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = netd_open,
.control_xfer_cb = netd_control_xfer_cb,
.xfer_cb = netd_xfer_cb,
.sof = NULL,
.xfer_isr = NULL,
.sof = NULL,
},
#endif
@@ -306,6 +317,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
.open = btd_open,
.control_xfer_cb = btd_control_xfer_cb,
.xfer_cb = btd_xfer_cb,
.xfer_isr = NULL,
.sof = NULL
},
#endif
@@ -1248,6 +1260,32 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr)
send = true;
break;
case DCD_EVENT_XFER_COMPLETE: {
// Invoke the class callback associated with the endpoint address
uint8_t const ep_addr = event->xfer_complete.ep_addr;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const ep_dir = tu_edpt_dir(ep_addr);
send = true;
if(epnum > 0) {
usbd_class_driver_t const* driver = get_driver(_usbd_dev.ep2drv[epnum][ep_dir]);
if (driver && driver->xfer_isr) {
_usbd_dev.ep_status[epnum][ep_dir].busy = 0;
_usbd_dev.ep_status[epnum][ep_dir].claimed = 0;
send = !driver->xfer_isr(event->rhport, ep_addr, (xfer_result_t) event->xfer_complete.result, event->xfer_complete.len);
// xfer_isr() is deferred to xfer_cb(), revert busy/claimed status
if (send) {
_usbd_dev.ep_status[epnum][ep_dir].busy = 1;
_usbd_dev.ep_status[epnum][ep_dir].claimed = 1;
}
}
}
break;
}
default:
send = true;
break;

View File

@@ -57,6 +57,7 @@ typedef struct {
uint16_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len);
bool (* control_xfer_cb ) (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
bool (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
bool (* xfer_isr ) (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); // optional, return false to defer to xfer_cb()
void (* sof ) (uint8_t rhport, uint32_t frame_count); // optional
} usbd_class_driver_t;