Skip to content

Commit

Permalink
Add an optional HID debug interface.
Browse files Browse the repository at this point in the history
  • Loading branch information
HiFiPhile committed Nov 21, 2023
1 parent 44ab68c commit db6b822
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 23 deletions.
62 changes: 62 additions & 0 deletions examples/device/uac2_speaker_fb/src/audio_debug.py
@@ -0,0 +1,62 @@
# Install python3 HID package https://pypi.org/project/hid/
import hid
from ctypes import *
import matplotlib.pyplot as plt
import matplotlib.animation as animation

# Example must be compiled with CFG_AUDIO_DEBUG=1
VID = 0xcafe
PID = 0x4014

CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX = 2

class audio_debug_info_t (Structure):
_fields_ = [("sample_rate", c_uint32),
("alt_settings", c_uint8),
("mute", (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1) * c_int8),
("volume", (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1) * c_int16),
("fifo_size", c_uint16),
("fifo_count", c_uint16),
("fifo_count_avg", c_uint16)
]

dev = hid.Device(VID, PID)

if dev:
# Create figure for plotting
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
fifo_avg = []
fifo_cnt = []
# This function is called periodically from FuncAnimation
def animate(i):

for i in range(100):
str_in = dev.read(64, 10)
if str_in:
info = audio_debug_info_t.from_buffer_copy(str_in)

global fifo_avg
global fifo_cnt
fifo_avg.append(info.fifo_count_avg)
fifo_cnt.append(info.fifo_count)

# Limit to 1000 items
fifo_avg = fifo_avg[-1000:]
fifo_cnt = fifo_cnt[-1000:]

# Draw x and y lists
ax.clear()
ax.plot(fifo_cnt, label='FIFO count')
ax.plot(fifo_avg, label='FIFO average')
ax.legend()
ax.set_ylim(bottom=0, top=info.fifo_size)

# Format plot
plt.title('FIFO information')
plt.grid()

print(f'Sample rate:{info.sample_rate} | Alt settings:{info.alt_settings} | Volume:{info.volume[:]}')

ani = animation.FuncAnimation(fig, animate, interval=100)
plt.show()
52 changes: 52 additions & 0 deletions examples/device/uac2_speaker_fb/src/common_types.h
@@ -0,0 +1,52 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2023 HiFiPhile
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/

#ifndef _COMMON_TYPES_H_
#define _COMMON_TYPES_H_

enum
{
ITF_NUM_AUDIO_CONTROL = 0,
ITF_NUM_AUDIO_STREAMING,
#if CFG_AUDIO_DEBUG
ITF_NUM_DEBUG,
#endif
ITF_NUM_TOTAL
};

#if CFG_AUDIO_DEBUG
typedef struct
{
uint32_t sample_rate;
uint8_t alt_settings;
int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];
uint16_t fifo_size;
uint16_t fifo_count;
uint16_t fifo_count_avg;
} audio_debug_info_t;
#endif

#endif
88 changes: 82 additions & 6 deletions examples/device/uac2_speaker_fb/src/main.c
Expand Up @@ -29,6 +29,7 @@
#include "bsp/board_api.h"
#include "tusb.h"
#include "usb_descriptors.h"
#include "common_types.h"

//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTOTYPES
Expand Down Expand Up @@ -88,6 +89,11 @@ 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);

#if CFG_AUDIO_DEBUG
void audio_debug_task(void);
uint8_t current_alt_settings;
#endif

/*------------- MAIN -------------*/
int main(void)
{
Expand All @@ -107,6 +113,9 @@ int main(void)
tud_task(); // TinyUSB device task
audio_task();
led_blinking_task();
#if CFG_AUDIO_DEBUG
audio_debug_task();
#endif
}
}

Expand Down Expand Up @@ -141,6 +150,10 @@ void tud_resume_cb(void)
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}

//--------------------------------------------------------------------+
// Application Callback API Implementations
//--------------------------------------------------------------------+

// Helper for clock get requests
static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request)
{
Expand Down Expand Up @@ -283,10 +296,6 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_req
}
}

//--------------------------------------------------------------------+
// Application Callback API Implementations
//--------------------------------------------------------------------+

// 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)
{
Expand Down Expand Up @@ -342,6 +351,10 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_reque
if (ITF_NUM_AUDIO_STREAMING == itf && alt != 0)
blink_interval_ms = BLINK_STREAMING;

#if CFG_AUDIO_DEBUG
current_alt_settings = alt;
#endif

return true;
}

Expand All @@ -353,14 +366,15 @@ void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedba
feedback_param->method = AUDIO_FEEDBACK_METHOD_FIFO_COUNT;
feedback_param->sample_freq = current_sample_rate;
}

//--------------------------------------------------------------------+
// AUDIO Task
//--------------------------------------------------------------------+

void audio_task(void)
{
// Yet to be filled - e.g. write audio to I2S buffer.
// Here we simulate a I2S transmit callback every 1ms.
// Replace audio_task() with your I2S transmit callback.
// Here we simulate a callback called every 1ms.
static uint32_t start_ms = 0;
uint32_t curr_ms = board_millis();
if ( start_ms == curr_ms ) return; // not enough time
Expand Down Expand Up @@ -399,3 +413,65 @@ void led_blinking_task(void)
board_led_write(led_state);
led_state = 1 - led_state;
}

#if CFG_AUDIO_DEBUG
//--------------------------------------------------------------------+
// HID interface for audio debug
//--------------------------------------------------------------------+

// Every 1ms, we will sent 1 debug information report
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
start_ms = curr_ms;

uint16_t fifo_count = tud_audio_available();
static uint32_t fifo_count_avg;

// Same averaging method used in UAC2 class
fifo_count_avg = (uint32_t)(((uint64_t)fifo_count_avg * 63 + ((uint32_t)fifo_count << 16)) >> 6);

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.fifo_count_avg = fifo_count_avg >> 16;
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];
}

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)
{
// TODO not Implemented
(void) itf;
(void) report_id;
(void) report_type;
(void) buffer;
(void) reqlen;

return 0;
}

// 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)
{
// This example doesn't use multiple report and report ID
(void) itf;
(void) report_id;
(void) report_type;
(void) buffer;
(void) bufsize;
}

#endif
26 changes: 21 additions & 5 deletions examples/device/uac2_speaker_fb/src/tusb_config.h
Expand Up @@ -32,6 +32,7 @@ extern "C" {
#endif

#include "usb_descriptors.h"

//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
Expand Down Expand Up @@ -59,6 +60,8 @@ extern "C" {
#define CFG_TUSB_OS OPT_OS_NONE
#endif

// It's recommanded to disable debug unless for control requests debugging,
// as the extra time needed will impact data stream !
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
Expand Down Expand Up @@ -88,16 +91,29 @@ extern "C" {
// DEVICE CONFIGURATION
//--------------------------------------------------------------------

// Expose audio class debug information via HID interface
#ifndef CFG_AUDIO_DEBUG
#define CFG_AUDIO_DEBUG 1
#endif

#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif

#define CFG_TUD_HID_EP_BUFSIZE 64

//------------- CLASS -------------//
#define CFG_TUD_AUDIO 1

#if CFG_AUDIO_DEBUG
#define CFG_TUD_HID 1
#else
#define CFG_TUD_HID 0
#endif

#define CFG_TUD_CDC 0
#define CFG_TUD_MSC 0
#define CFG_TUD_HID 0
#define CFG_TUD_MIDI 0
#define CFG_TUD_AUDIO 1
#define CFG_TUD_VENDOR 0

//--------------------------------------------------------------------
Expand Down Expand Up @@ -126,13 +142,13 @@ extern "C" {
#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

// Enable feedback EP
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 1
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 1

// 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 1
#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1

// Size of control request buffer
#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64
#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64

#ifdef __cplusplus
}
Expand Down

0 comments on commit db6b822

Please sign in to comment.