mirror of
https://github.com/LNH-team/dspico-usb-examples.git
synced 2026-06-08 12:17:13 +02:00
Added USB microphone example
This commit is contained in:
8
examples/usb-microphone/arm7/source/Arm7State.h
Normal file
8
examples/usb-microphone/arm7/source/Arm7State.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Enum representing the arm7 state.
|
||||
enum class Arm7State
|
||||
{
|
||||
Idle,
|
||||
ExitRequested
|
||||
};
|
||||
10
examples/usb-microphone/arm7/source/ExitMode.h
Normal file
10
examples/usb-microphone/arm7/source/ExitMode.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Enum representing the exit mode of launcher.
|
||||
enum class ExitMode
|
||||
{
|
||||
/// @brief Reset the system (DSi mode only).
|
||||
Reset,
|
||||
/// @brief Power off the system.
|
||||
PowerOff
|
||||
};
|
||||
5
examples/usb-microphone/arm7/source/common.h
Normal file
5
examples/usb-microphone/arm7/source/common.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include <nds.h>
|
||||
#include <libtwl/rtos/rtosMutex.h>
|
||||
|
||||
extern rtos_mutex_t gCardMutex;
|
||||
197
examples/usb-microphone/arm7/source/main.cpp
Normal file
197
examples/usb-microphone/arm7/source/main.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/rtos/rtosIrq.h>
|
||||
#include <libtwl/rtos/rtosThread.h>
|
||||
#include <libtwl/rtos/rtosEvent.h>
|
||||
#include <libtwl/sound/soundChannel.h>
|
||||
#include <libtwl/timer/timer.h>
|
||||
#include <libtwl/sound/sound.h>
|
||||
#include <libtwl/ipc/ipcSync.h>
|
||||
#include <libtwl/ipc/ipcFifoSystem.h>
|
||||
#include <libtwl/sys/sysPower.h>
|
||||
#include <libtwl/sio/sioRtc.h>
|
||||
#include <libtwl/sio/sio.h>
|
||||
#include <libtwl/gfx/gfxStatus.h>
|
||||
#include <libtwl/mem/memSwap.h>
|
||||
#include <libtwl/i2c/i2cMcu.h>
|
||||
#include <libtwl/spi/spiPmic.h>
|
||||
#include "ExitMode.h"
|
||||
#include "Arm7State.h"
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.h"
|
||||
#include "microphone.h"
|
||||
|
||||
rtos_mutex_t gCardMutex;
|
||||
|
||||
static rtos_thread_t sUsbThread;
|
||||
static u32 sUsbThreadStack[512];
|
||||
|
||||
static rtos_event_t sVBlankEvent;
|
||||
static ExitMode sExitMode;
|
||||
static Arm7State sState;
|
||||
static volatile u8 sMcuIrqFlag = false;
|
||||
|
||||
static void vblankIrq(u32 irqMask)
|
||||
{
|
||||
rtos_signalEvent(&sVBlankEvent);
|
||||
}
|
||||
|
||||
static void mcuIrq(u32 irq2Mask)
|
||||
{
|
||||
sMcuIrqFlag = true;
|
||||
}
|
||||
|
||||
static void checkMcuIrq(void)
|
||||
{
|
||||
// mcu only exists in DSi mode
|
||||
if (isDSiMode())
|
||||
{
|
||||
// check and ack the flag atomically
|
||||
if (mem_swapByte(false, &sMcuIrqFlag))
|
||||
{
|
||||
// check the irq mask
|
||||
u32 irqMask = mcu_getIrqMask();
|
||||
if (irqMask & MCU_IRQ_RESET)
|
||||
{
|
||||
// power button was released
|
||||
sExitMode = ExitMode::Reset;
|
||||
sState = Arm7State::ExitRequested;
|
||||
}
|
||||
else if (irqMask & MCU_IRQ_POWER_OFF)
|
||||
{
|
||||
// power button was held long to trigger a power off
|
||||
sExitMode = ExitMode::PowerOff;
|
||||
sState = Arm7State::ExitRequested;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void initializeVBlankIrq()
|
||||
{
|
||||
rtos_createEvent(&sVBlankEvent);
|
||||
rtos_setIrqFunc(RTOS_IRQ_VBLANK, vblankIrq);
|
||||
rtos_enableIrqMask(RTOS_IRQ_VBLANK);
|
||||
gfx_setVBlankIrqEnabled(true);
|
||||
}
|
||||
|
||||
static void usbThreadMain(void* arg)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
tud_task();
|
||||
}
|
||||
}
|
||||
|
||||
static void initializeArm7()
|
||||
{
|
||||
rtos_initIrq();
|
||||
rtos_startMainThread();
|
||||
ipc_initFifoSystem();
|
||||
|
||||
// clear sound registers
|
||||
dmaFillWords(0, (void*)0x04000400, 0x100);
|
||||
|
||||
pmic_setAmplifierEnable(true);
|
||||
sys_setSoundPower(true);
|
||||
|
||||
readUserSettings();
|
||||
pmic_setPowerLedBlink(PMIC_CONTROL_POWER_LED_BLINK_NONE);
|
||||
|
||||
sio_setGpioSiIrq(false);
|
||||
sio_setGpioMode(RCNT0_L_MODE_GPIO);
|
||||
|
||||
rtc_init();
|
||||
|
||||
snd_setMasterVolume(0);
|
||||
snd_setMasterEnable(false);
|
||||
|
||||
initializeVBlankIrq();
|
||||
|
||||
if (isDSiMode())
|
||||
{
|
||||
rtos_setIrq2Func(RTOS_IRQ2_MCU, mcuIrq);
|
||||
rtos_enableIrq2Mask(RTOS_IRQ2_MCU);
|
||||
mic_initialize();
|
||||
}
|
||||
|
||||
pmic_setTopBacklightEnable(false);
|
||||
pmic_setBottomBacklightEnable(false);
|
||||
|
||||
tusb_rhport_init_t dev_init =
|
||||
{
|
||||
.role = TUSB_ROLE_DEVICE,
|
||||
.speed = TUSB_SPEED_AUTO
|
||||
};
|
||||
tusb_init(0, &dev_init);
|
||||
|
||||
rtos_createThread(&sUsbThread, 3, usbThreadMain, NULL, sUsbThreadStack, sizeof(sUsbThreadStack));
|
||||
rtos_wakeupThread(&sUsbThread);
|
||||
|
||||
ipc_setArm7SyncBits(7);
|
||||
}
|
||||
|
||||
static void updateArm7IdleState()
|
||||
{
|
||||
checkMcuIrq();
|
||||
|
||||
if (sState == Arm7State::ExitRequested)
|
||||
{
|
||||
snd_setMasterVolume(0); // mute sound
|
||||
}
|
||||
}
|
||||
|
||||
static bool performExit(ExitMode exitMode)
|
||||
{
|
||||
switch (exitMode)
|
||||
{
|
||||
case ExitMode::Reset:
|
||||
{
|
||||
mcu_setWarmBootFlag(true);
|
||||
mcu_hardReset();
|
||||
break;
|
||||
}
|
||||
case ExitMode::PowerOff:
|
||||
{
|
||||
pmic_shutdown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (true); // wait infinitely for exit
|
||||
}
|
||||
|
||||
static void updateArm7ExitRequestedState()
|
||||
{
|
||||
performExit(sExitMode);
|
||||
}
|
||||
|
||||
static void updateArm7()
|
||||
{
|
||||
switch (sState)
|
||||
{
|
||||
case Arm7State::Idle:
|
||||
{
|
||||
updateArm7IdleState();
|
||||
break;
|
||||
}
|
||||
case Arm7State::ExitRequested:
|
||||
{
|
||||
updateArm7ExitRequestedState();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
sState = Arm7State::Idle;
|
||||
initializeArm7();
|
||||
|
||||
while (true)
|
||||
{
|
||||
rtos_waitEvent(&sVBlankEvent, true, true);
|
||||
updateArm7();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
282
examples/usb-microphone/arm7/source/microphone.cpp
Normal file
282
examples/usb-microphone/arm7/source/microphone.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/rtos/rtosIrq.h>
|
||||
#include <libtwl/spi/spiCodec.h>
|
||||
#include <libtwl/sound/twlMicrophone.h>
|
||||
#include <libtwl/sound/twlI2s.h>
|
||||
#include <libtwl/sys/swi.h>
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.h"
|
||||
#include "microphone.h"
|
||||
|
||||
#define AUDIO_BLOCK_SIZE_IN_BYTES 32
|
||||
#define NUMBER_OF_AUDIO_BUFFERS 128
|
||||
|
||||
static s16 sAudioBuffer[NUMBER_OF_AUDIO_BUFFERS][16];
|
||||
static volatile int sReadBlock;
|
||||
static volatile int sWriteBlock;
|
||||
|
||||
static bool sCaptureStarted = false;
|
||||
static int sOffset = 0;
|
||||
|
||||
static bool sChannelMute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
|
||||
static uint16_t sChannelVolume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
|
||||
|
||||
void mic_initialize()
|
||||
{
|
||||
twlmic_stop();
|
||||
REG_I2SCNT = I2SCNT_MIX_RATIO_DSP_0_NITRO_8 | I2SCNT_FREQUENCY_32728_HZ;
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE0_DAC_NDAC_VAL, 0x87);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_NADC_VAL, 0x87);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_PLL_J, 21);
|
||||
}
|
||||
REG_I2SCNT |= I2SCNT_ENABLE;
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE1_MICBIAS, 3);
|
||||
}
|
||||
bool adcOn, dacOn;
|
||||
codec_setPage(CODEC_PAGE_0);
|
||||
{
|
||||
adcOn = codec_readRegister(CODEC_REG_PAGE0_ADC_DIGITAL_MIC) & 0x80;
|
||||
dacOn = codec_readRegister(CODEC_REG_PAGE0_DAC_DATA_PATH_SETUP) & 0xC0;
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_DIGITAL_MIC, 0x80);
|
||||
if (!adcOn || !dacOn)
|
||||
{
|
||||
swi_waitByLoop(0x28E91F); // 20ms
|
||||
}
|
||||
codec_writeRegister(CODEC_REG_PAGE0_ADC_DIGITAL_VOLUME_CONTROL_FINE_ADJUST, 0);
|
||||
codec_writeRegister(CODEC_REG_PAGE0_AGC_CONTROL_1, 0);
|
||||
}
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
sChannelVolume[0] = 40; // dB
|
||||
codec_writeRegister(CODEC_REG_PAGE1_MIC_PGA, sChannelVolume[0] * 2); // gain
|
||||
}
|
||||
}
|
||||
|
||||
static void micIrq(u32 irq2Mask)
|
||||
{
|
||||
u32 data[8];
|
||||
data[0] = REG_MIC_FIFO;
|
||||
data[1] = REG_MIC_FIFO;
|
||||
data[2] = REG_MIC_FIFO;
|
||||
data[3] = REG_MIC_FIFO;
|
||||
data[4] = REG_MIC_FIFO;
|
||||
data[5] = REG_MIC_FIFO;
|
||||
data[6] = REG_MIC_FIFO;
|
||||
data[7] = REG_MIC_FIFO;
|
||||
int writeBlock = sWriteBlock;
|
||||
int nextWriteBlock = (writeBlock + 1) % NUMBER_OF_AUDIO_BUFFERS;
|
||||
if (nextWriteBlock != sReadBlock)
|
||||
{
|
||||
memcpy(&sAudioBuffer[writeBlock][0], data, sizeof(data));
|
||||
sWriteBlock = nextWriteBlock;
|
||||
}
|
||||
}
|
||||
|
||||
static void startMicrophoneCapture()
|
||||
{
|
||||
sReadBlock = 0;
|
||||
sWriteBlock = 0;
|
||||
sOffset = 0;
|
||||
twlmic_stop();
|
||||
twlmic_configure(MICCNT_FORMAT_NORMAL, MICCNT_RATE_DIV_1, MICCNT_IRQ_HALF_OVERFLOW);
|
||||
twlmic_clearFifo();
|
||||
rtos_setIrq2Func(RTOS_IRQ2_MIC, micIrq);
|
||||
rtos_ackIrq2Mask(RTOS_IRQ2_MIC);
|
||||
rtos_enableIrq2Mask(RTOS_IRQ2_MIC);
|
||||
twlmic_start();
|
||||
sCaptureStarted = true;
|
||||
}
|
||||
|
||||
static void stopMicrophoneCapture()
|
||||
{
|
||||
rtos_disableIrq2Mask(RTOS_IRQ2_MIC);
|
||||
twlmic_stop();
|
||||
rtos_ackIrq2Mask(RTOS_IRQ2_MIC);
|
||||
sCaptureStarted = false;
|
||||
}
|
||||
|
||||
static bool handleMicInputTerminalGetRequest(u8 rhport, const tusb_control_request_t* p_request)
|
||||
{
|
||||
u8 ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
switch (ctrlSel)
|
||||
{
|
||||
case AUDIO_TE_CTRL_CONNECTOR: // Get terminal connector
|
||||
{
|
||||
audio_desc_channel_cluster_t ret;
|
||||
ret.bNrChannels = 1;
|
||||
ret.bmChannelConfig = (audio_channel_config_t)0;
|
||||
ret.iChannelNames = 0;
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool handleFeatureUnitGetRequest(u8 rhport, const tusb_control_request_t* p_request)
|
||||
{
|
||||
u8 channelNum = TU_U16_LOW(p_request->wValue);
|
||||
u8 ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
switch (ctrlSel)
|
||||
{
|
||||
case AUDIO_FU_CTRL_MUTE: // Get Mute of channel
|
||||
{
|
||||
return tud_control_xfer(rhport, p_request, &sChannelMute[channelNum], 1);
|
||||
}
|
||||
case AUDIO_FU_CTRL_VOLUME:
|
||||
{
|
||||
switch (p_request->bRequest)
|
||||
{
|
||||
case AUDIO_CS_REQ_CUR: // Get Volume of channel
|
||||
{
|
||||
return tud_control_xfer(rhport, p_request, &sChannelVolume[channelNum], sizeof(sChannelVolume[channelNum]));
|
||||
}
|
||||
case AUDIO_CS_REQ_RANGE: // Get Volume range of channel
|
||||
{
|
||||
audio_control_range_2_n_t(1) ret;
|
||||
ret.wNumSubRanges = 1;
|
||||
ret.subrange[0].bMin = 0; // 0 dB
|
||||
ret.subrange[0].bMax = 59; // +59 dB
|
||||
ret.subrange[0].bRes = 1; // 1 dB steps
|
||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool handleFeatureUnitSetRequest(u8 rhport, const tusb_control_request_t* p_request, u8* pBuff)
|
||||
{
|
||||
u8 channelNum = TU_U16_LOW(p_request->wValue);
|
||||
u8 ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
switch (ctrlSel)
|
||||
{
|
||||
case AUDIO_FU_CTRL_MUTE: // Set Mute of channel
|
||||
{
|
||||
sChannelMute[channelNum] = ((audio_control_cur_1_t*)pBuff)->bCur;
|
||||
return true;
|
||||
}
|
||||
case AUDIO_FU_CTRL_VOLUME: // Set Volume of channel
|
||||
{
|
||||
// Request uses format layout 2
|
||||
sChannelVolume[channelNum] = (uint16_t) ((audio_control_cur_2_t*)pBuff)->bCur;
|
||||
codec_setPage(CODEC_PAGE_1);
|
||||
{
|
||||
codec_writeRegister(CODEC_REG_PAGE1_MIC_PGA, sChannelVolume[channelNum] * 2); // gain
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool handleClockGetRequest(u8 rhport, const tusb_control_request_t* p_request)
|
||||
{
|
||||
u8 ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||
switch (ctrlSel)
|
||||
{
|
||||
case AUDIO_CS_CTRL_SAM_FREQ:
|
||||
{
|
||||
switch (p_request->bRequest)
|
||||
{
|
||||
case AUDIO_CS_REQ_CUR: // Get Sample Freq.
|
||||
{
|
||||
uint32_t sampFreq = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
|
||||
return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
|
||||
}
|
||||
case AUDIO_CS_REQ_RANGE: // Get Sample Freq. range
|
||||
{
|
||||
audio_control_range_4_n_t(1) sampleFreqRng;
|
||||
sampleFreqRng.wNumSubRanges = 1;
|
||||
sampleFreqRng.subrange[0].bMin = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
|
||||
sampleFreqRng.subrange[0].bMax = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
|
||||
sampleFreqRng.subrange[0].bRes = 0;
|
||||
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AUDIO_CS_CTRL_CLK_VALID: // Get Sample Freq. valid
|
||||
{
|
||||
uint8_t clkValid = 1;
|
||||
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Invoked when audio class specific get request received for an entity
|
||||
bool tud_audio_get_req_entity_cb(u8 rhport, const tusb_control_request_t* p_request)
|
||||
{
|
||||
u8 entityId = TU_U16_HIGH(p_request->wIndex);
|
||||
switch (entityId)
|
||||
{
|
||||
case UAC2_ENTITY_MIC_INPUT_TERMINAL:
|
||||
{
|
||||
return handleMicInputTerminalGetRequest(rhport, p_request);
|
||||
}
|
||||
case UAC2_ENTITY_FEATURE_UNIT:
|
||||
{
|
||||
return handleFeatureUnitGetRequest(rhport, p_request);
|
||||
}
|
||||
case UAC2_ENTITY_CLOCK:
|
||||
{
|
||||
return handleClockGetRequest(rhport, p_request);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Invoked when audio class specific set request received for an entity
|
||||
bool tud_audio_set_req_entity_cb(u8 rhport, const tusb_control_request_t* p_request, u8* pBuff)
|
||||
{
|
||||
u8 entityId = TU_U16_HIGH(p_request->wIndex);
|
||||
switch (entityId)
|
||||
{
|
||||
case UAC2_ENTITY_FEATURE_UNIT:
|
||||
{
|
||||
return handleFeatureUnitSetRequest(rhport, p_request, pBuff);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tud_audio_tx_done_pre_load_cb(u8 rhport, u8 itf, u8 ep_in, u8 cur_alt_setting)
|
||||
{
|
||||
if (!sCaptureStarted)
|
||||
{
|
||||
startMicrophoneCapture();
|
||||
}
|
||||
|
||||
while (sReadBlock != sWriteBlock)
|
||||
{
|
||||
int bytesWritten = tud_audio_write(((u8*)&sAudioBuffer[sReadBlock][0]) + sOffset, AUDIO_BLOCK_SIZE_IN_BYTES - sOffset);
|
||||
int offset = AUDIO_BLOCK_SIZE_IN_BYTES - bytesWritten;
|
||||
sOffset = offset % AUDIO_BLOCK_SIZE_IN_BYTES;
|
||||
if (offset > 0)
|
||||
{
|
||||
// Could not write entire block
|
||||
break;
|
||||
}
|
||||
sReadBlock = (sReadBlock + 1) % NUMBER_OF_AUDIO_BUFFERS;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tud_audio_set_itf_close_EP_cb(u8 rhport, const tusb_control_request_t* p_request)
|
||||
{
|
||||
stopMicrophoneCapture();
|
||||
return true;
|
||||
}
|
||||
3
examples/usb-microphone/arm7/source/microphone.h
Normal file
3
examples/usb-microphone/arm7/source/microphone.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void mic_initialize();
|
||||
97
examples/usb-microphone/arm7/source/tusb_config.h
Normal file
97
examples/usb-microphone/arm7/source/tusb_config.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
#include "usb_descriptors.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Board Specific Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// RHPort number used for device can be defined by board.mk, default to port 0
|
||||
#ifndef BOARD_TUD_RHPORT
|
||||
#define BOARD_TUD_RHPORT 0
|
||||
#endif
|
||||
|
||||
// RHPort max operational speed can defined by board.mk
|
||||
#ifndef BOARD_TUD_MAX_SPEED
|
||||
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// defined by compiler flags for flexibility
|
||||
#ifndef CFG_TUSB_MCU
|
||||
#define CFG_TUSB_MCU OPT_MCU_DSPICO
|
||||
#endif
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
|
||||
#ifndef CFG_TUSB_OS
|
||||
#define CFG_TUSB_OS OPT_OS_CUSTOM
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_DEBUG
|
||||
#define CFG_TUSB_DEBUG 0
|
||||
#endif
|
||||
|
||||
// Enable Device stack
|
||||
#define CFG_TUD_ENABLED 1
|
||||
|
||||
// Default is max speed that hardware controller could support with on-chip PHY
|
||||
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
|
||||
|
||||
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
|
||||
* Tinyusb use follows macros to declare transferring memory so that they can be put
|
||||
* into those specific section.
|
||||
* e.g
|
||||
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
|
||||
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
|
||||
*/
|
||||
#ifndef CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#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
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// AUDIO CLASS DRIVER CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE 32728
|
||||
#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_ONE_CH_DESC_LEN
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 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_CTRL_BUF_SZ 64 // Size of control request buffer
|
||||
|
||||
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
|
||||
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 // 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
|
||||
#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 256 //(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
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
94
examples/usb-microphone/arm7/source/usb_descriptors.cpp
Normal file
94
examples/usb-microphone/arm7/source/usb_descriptors.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "common.h"
|
||||
#include "UsbStringDescriptor.h"
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.h"
|
||||
|
||||
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
|
||||
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
|
||||
*
|
||||
* Auto ProductID layout's Bitmap:
|
||||
* [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
|
||||
*/
|
||||
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
|
||||
#define USB_PID (0x5000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
|
||||
_PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
|
||||
|
||||
const u8* tud_descriptor_device_cb(void)
|
||||
{
|
||||
static const tusb_desc_device_t deviceDescriptor =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
|
||||
// Use Interface Association Descriptor (IAD) for Audio
|
||||
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = 0xCafe,
|
||||
.idProduct = USB_PID,
|
||||
.bcdDevice = 0x0100,
|
||||
|
||||
.iManufacturer = STRID_MANUFACTURER,
|
||||
.iProduct = STRID_PRODUCT,
|
||||
.iSerialNumber = STRID_SERIAL,
|
||||
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
return (const u8*)&deviceDescriptor;
|
||||
}
|
||||
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_AUDIO_MIC_ONE_CH_DESC_LEN)
|
||||
|
||||
const u8* tud_descriptor_configuration_cb(u8 index)
|
||||
{
|
||||
(void)index;
|
||||
|
||||
static const u8 configurationDescriptor[] =
|
||||
{
|
||||
// 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),
|
||||
|
||||
TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR(0, STRID_AUDIO_INTERFACE,
|
||||
CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * 8,
|
||||
EPNUM_AUDIO_IN, CFG_TUD_AUDIO_EP_SZ_IN)
|
||||
};
|
||||
|
||||
return configurationDescriptor;
|
||||
}
|
||||
|
||||
const u16* tud_descriptor_string_cb(u8 index, u16 langid)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case STRID_LANGID:
|
||||
{
|
||||
static const UsbStringDescriptor<2> descriptor(0x0409);
|
||||
return (const u16*)&descriptor;
|
||||
}
|
||||
case STRID_MANUFACTURER:
|
||||
{
|
||||
return USB_STRING_DESCRIPTOR(u"LNH");
|
||||
}
|
||||
case STRID_PRODUCT:
|
||||
{
|
||||
return USB_STRING_DESCRIPTOR(u"DSpico Microphone Example");
|
||||
}
|
||||
case STRID_SERIAL:
|
||||
{
|
||||
return USB_STRING_DESCRIPTOR(u"123456789");
|
||||
}
|
||||
case STRID_AUDIO_INTERFACE:
|
||||
{
|
||||
return USB_STRING_DESCRIPTOR(u"DSpico Microphone");
|
||||
}
|
||||
default:
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
examples/usb-microphone/arm7/source/usb_descriptors.h
Normal file
24
examples/usb-microphone/arm7/source/usb_descriptors.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
enum
|
||||
{
|
||||
STRID_LANGID = 0,
|
||||
STRID_MANUFACTURER,
|
||||
STRID_PRODUCT,
|
||||
STRID_SERIAL,
|
||||
STRID_AUDIO_INTERFACE
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ITF_NUM_AUDIO_CONTROL = 0,
|
||||
ITF_NUM_AUDIO_STREAMING,
|
||||
ITF_NUM_TOTAL
|
||||
};
|
||||
|
||||
#define UAC2_ENTITY_MIC_INPUT_TERMINAL 0x01
|
||||
#define UAC2_ENTITY_FEATURE_UNIT 0x02
|
||||
#define UAC2_ENTITY_MIC_OUTPUT_TERMINAL 0x03
|
||||
#define UAC2_ENTITY_CLOCK 0x04
|
||||
|
||||
#define EPNUM_AUDIO_IN 0x81
|
||||
Reference in New Issue
Block a user