kopia lustrzana https://github.com/skuep/AIOC
Added support for multiple samplerates (48000, 24000, 22050)
rodzic
2c65b98b70
commit
b44eb5da4c
|
@ -6,7 +6,7 @@
|
|||
#include <math.h>
|
||||
|
||||
/* The one and only supported sample rate */
|
||||
#define AUDIO_SAMPLE_RATE 48000
|
||||
#define DEFAULT_SAMPLE_RATE 48000
|
||||
/* This is feedback average responsivity with a denominator of 65536 */
|
||||
#define SPEAKER_FEEDBACK_AVG 32
|
||||
/* This is buffer level average responsivity with a denominator of 65536 */
|
||||
|
@ -14,7 +14,14 @@
|
|||
/* This is the amount of buffer level to feedback coupling with a denominator of 65536 to prevent buffer drift */
|
||||
#define SPEAKER_BUFLVL_FB_COUPLING 1
|
||||
/* We try to stay on this target with the buffer level */
|
||||
#define SPEAKER_BUFFERLVL_TARGET (5 * CFG_TUD_AUDIO_EP_SZ_OUT) /* Keep our buffer at 5 frames, i.e. 5ms at full-speed USB */
|
||||
#define SPEAKER_BUFFERLVL_TARGET (5 * CFG_TUD_AUDIO_EP_SZ_OUT) /* Keep our buffer at 5 frames, i.e. 5ms at full-speed USB and maximum sample rate */
|
||||
|
||||
typedef enum {
|
||||
SAMPLERATE_48000,
|
||||
SAMPLERATE_24000,
|
||||
SAMPLERATE_22050, /* For APRSdroid support. NOTE: Has approx. 90 ppm of clock frequency error (ca. 22052 Hz) */
|
||||
SAMPLERATE_COUNT /* Has to be last element */
|
||||
} samplerate_t;
|
||||
|
||||
typedef enum {
|
||||
STATE_OFF,
|
||||
|
@ -23,6 +30,7 @@ typedef enum {
|
|||
} state_t;
|
||||
|
||||
/* Various state variables. N+1 because 0 is always the master channel */
|
||||
static volatile uint32_t sampleFreqCfg; /* Actually configured sample rate. May be different for odd sample rates */
|
||||
static volatile state_t microphoneState = STATE_OFF;
|
||||
static volatile state_t speakerState = STATE_OFF;
|
||||
static bool microphoneMute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1];
|
||||
|
@ -38,6 +46,21 @@ static uint32_t speakerBufferLvlAvg; /* 16.16 format */
|
|||
static uint16_t speakerBufferLvlMin;
|
||||
static uint16_t speakerBufferLvlMax;
|
||||
|
||||
static uint32_t sampleFreqCur = DEFAULT_SAMPLE_RATE;
|
||||
static audio_control_range_4_n_t(SAMPLERATE_COUNT) sampleFreqRng = {
|
||||
.wNumSubRanges = SAMPLERATE_COUNT,
|
||||
.subrange = {
|
||||
[SAMPLERATE_48000] = {.bMin = 48000, .bMax = 48000, .bRes = 0},
|
||||
[SAMPLERATE_24000] = {.bMin = 24000, .bMax = 24000, .bRes = 0},
|
||||
[SAMPLERATE_22050] = {.bMin = 22050, .bMax = 22050, .bRes = 0},
|
||||
}
|
||||
};
|
||||
|
||||
/* Prototypes of static functions */
|
||||
static void Timer_Init(void);
|
||||
static void ADC_Init(void);
|
||||
static void DAC_Init(void);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callback API Implementations
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -80,7 +103,7 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
|||
microphoneLinVolume[channelNum] = (microphoneLogVolume[channelNum] != 0x8000) ?
|
||||
(uint16_t) (65535 * pow(10, logVolume/20) + 0.5) : 0; /* log to linear with rounding */
|
||||
|
||||
TU_LOG2(" Set Volume: %f dB of channel: %u\r\n", logVolume, channelNum);
|
||||
TU_LOG2(" Set Volume: %u.%u dB of channel: %u\r\n", microphoneLogVolume[channelNum] / 256, microphoneLogVolume[channelNum] % 256, channelNum);
|
||||
return true;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
|
@ -113,7 +136,7 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
|||
speakerLinVolume[channelNum] = (speakerLogVolume[channelNum] != 0x8000) ?
|
||||
(uint16_t) (65535 * pow(10, logVolume/20) + 0.5) : 0; /* log to linear with rounding */
|
||||
|
||||
TU_LOG2(" Set Volume: %f dB of channel: %u\r\n", logVolume, channelNum);
|
||||
TU_LOG2(" Set Volume: %u.%u dB of channel: %u\r\n", microphoneLogVolume[channelNum] / 256, microphoneLogVolume[channelNum] % 256, channelNum);
|
||||
return true;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
|
@ -123,6 +146,37 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
|||
}
|
||||
}
|
||||
|
||||
if ( entityID == AUDIO_CTRL_ID_CLOCK )
|
||||
{
|
||||
switch ( ctrlSel )
|
||||
{
|
||||
case AUDIO_CS_CTRL_SAM_FREQ:
|
||||
// channelNum is always zero in this case
|
||||
switch ( p_request->bRequest )
|
||||
{
|
||||
case AUDIO_CS_REQ_CUR:
|
||||
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_4_t));
|
||||
sampleFreqCur = ((audio_control_cur_4_t*) pBuff)->bCur;
|
||||
TU_LOG2(" Set Sample Freq: %lu\r\n", sampleFreqCur);
|
||||
|
||||
Timer_Init();
|
||||
|
||||
return true;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
// Unknown/Unsupported control
|
||||
default:
|
||||
TU_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // Yet not implemented
|
||||
}
|
||||
|
||||
|
@ -313,20 +367,10 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
|
|||
{
|
||||
case AUDIO_CS_REQ_CUR:
|
||||
TU_LOG2(" Get Sample Freq.\r\n");
|
||||
uint32_t sampFreq = AUDIO_SAMPLE_RATE;
|
||||
return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
|
||||
return tud_control_xfer(rhport, p_request, &sampleFreqCur, sizeof(sampleFreqCur));
|
||||
|
||||
case AUDIO_CS_REQ_RANGE:
|
||||
TU_LOG2(" Get Sample Freq. range\r\n");
|
||||
audio_control_range_4_n_t(1) sampleFreqRng = {
|
||||
.wNumSubRanges = 1,
|
||||
.subrange[0] = {
|
||||
.bMin = AUDIO_SAMPLE_RATE,
|
||||
.bMax = AUDIO_SAMPLE_RATE,
|
||||
.bRes = 0
|
||||
}
|
||||
};
|
||||
|
||||
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
|
||||
|
||||
// Unknown/Unsupported control
|
||||
|
@ -458,7 +502,7 @@ void tud_audio_feedback_params_cb(uint8_t func_id, uint8_t alt_itf, audio_feedba
|
|||
{
|
||||
/* Configure parameters for feedback endpoint */
|
||||
feedback_param->frequency.mclk_freq = USB_SOF_TIMER_HZ;
|
||||
feedback_param->sample_freq = AUDIO_SAMPLE_RATE;
|
||||
feedback_param->sample_freq = sampleFreqCfg;
|
||||
feedback_param->method = AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED;
|
||||
}
|
||||
|
||||
|
@ -466,6 +510,7 @@ TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t
|
|||
{
|
||||
static uint32_t prev_cycles = 0;
|
||||
uint32_t this_cycles = USB_SOF_TIMER_CCR; /* Load from capture register, which is set in tu_stm32_sof_cb */
|
||||
uint32_t sampleFreq = sampleFreqCfg;
|
||||
uint32_t feedback;
|
||||
|
||||
/* Calculate number of master clock cycles between now and last call */
|
||||
|
@ -475,11 +520,11 @@ TU_ATTR_FAST_FUNC void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t
|
|||
prev_cycles = this_cycles;
|
||||
|
||||
/* Calculate the feedback value, taken from tinyusb stack */
|
||||
uint64_t fb64 = (((uint64_t) cycles) * AUDIO_SAMPLE_RATE) << 16;
|
||||
uint64_t fb64 = (((uint64_t) cycles) * sampleFreq) << 16;
|
||||
feedback = (uint32_t) (fb64 / USB_SOF_TIMER_HZ);
|
||||
|
||||
uint32_t min_value = (AUDIO_SAMPLE_RATE/1000 - 1) << 16; /* 1000 for full-speed USB */
|
||||
uint32_t max_value = (AUDIO_SAMPLE_RATE/1000 + 1) << 16;
|
||||
uint32_t min_value = (sampleFreq/1000 - 1) << 16; /* 1000 for full-speed USB */
|
||||
uint32_t max_value = (sampleFreq/1000 + 1) << 16;
|
||||
|
||||
/* Couple the buffer level bias to the feedback value to avoid buffer drift */
|
||||
if (speakerState == STATE_RUN) {
|
||||
|
@ -576,13 +621,22 @@ static void GPIO_Init(void)
|
|||
|
||||
static void Timer_Init(void)
|
||||
{
|
||||
/* Calculate clock rate divider for requested sample rate with rounding */
|
||||
uint32_t timerFreq = 2 * HAL_RCC_GetPCLK1Freq();
|
||||
uint32_t rateDivider = (timerFreq + sampleFreqCur / 2) / sampleFreqCur;
|
||||
|
||||
/* Store actually realized samplerate for feedback algorithm to use */
|
||||
sampleFreqCfg = timerFreq / rateDivider;
|
||||
|
||||
/* Enable clock and (re-) initialize timer */
|
||||
__HAL_RCC_TIM3_CLK_ENABLE();
|
||||
/* TODO: Use TIM6? */
|
||||
/* TIM3_TRGO triggers ADC2 */
|
||||
TIM3->CR1 &= ~TIM_CR1_CEN;
|
||||
TIM3->CR1 = TIM_CLOCKDIVISION_DIV1 | TIM_COUNTERMODE_UP | TIM_AUTORELOAD_PRELOAD_ENABLE;
|
||||
TIM3->CR2 = TIM_TRGO_UPDATE;
|
||||
TIM3->PSC = 0;
|
||||
TIM3->ARR = (2 * HAL_RCC_GetPCLK1Freq() / AUDIO_SAMPLE_RATE) - 1;
|
||||
TIM3->ARR = rateDivider - 1;
|
||||
TIM3->EGR = TIM_EGR_UG;
|
||||
#if 1 /* Output sample rate on compare channel 3 */
|
||||
TIM3->CCMR2 = TIM_OCMODE_PWM1 | TIM_CCMR2_OC3PE;
|
||||
|
@ -638,7 +692,7 @@ static void ADC_Init(void)
|
|||
NVIC_SetPriority(ADC1_2_IRQn, AIOC_IRQ_PRIO_AUDIO);
|
||||
}
|
||||
|
||||
void DAC_Init(void)
|
||||
static void DAC_Init(void)
|
||||
{
|
||||
__HAL_RCC_DAC1_CLK_ENABLE();
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ enum USB_DESCRIPTORS_ITF {
|
|||
/* Class-Specific AC Interface Header Descriptor(4.7.2) */ \
|
||||
TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, AUDIO_FUNC_CONVERTER, TUD_AUDIO_CTRL_TOTAL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \
|
||||
/* Clock Source Descriptor(4.7.2.1) */ \
|
||||
TUD_AUDIO_DESC_CLK_SRC(AUDIO_CTRL_ID_CLOCK, AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00), \
|
||||
TUD_AUDIO_DESC_CLK_SRC(AUDIO_CTRL_ID_CLOCK, AUDIO_CLOCK_SOURCE_ATT_INT_PRO_CLK, (AUDIO_CTRL_RW << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x01, /*_stridx*/ 0x00), \
|
||||
/* Speaker Terminals */ \
|
||||
TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ AUDIO_CTRL_ID_SPK_INPUT_STREAM, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ AUDIO_CTRL_ID_MIC_OUTPUT_STREAM, /*_clkid*/ AUDIO_CTRL_ID_CLOCK, /*_nchannelslogical*/ AUDIO_NUM_OUTCHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \
|
||||
TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ AUDIO_CTRL_ID_SPK_FUNIT, /*_srcid*/ AUDIO_CTRL_ID_SPK_INPUT_STREAM, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00),\
|
||||
|
|
Ładowanie…
Reference in New Issue