I'm building a USB microphone based on STM32F407 discovery board, it captures audio but produces quiet but noticeable hiss with its spectrum shown below:
I'm using oversampling to get additional 2 bits of resolution but the issue is present without oversampling as well (I just used DMA to write ADC samples directly into the USB output buffer to test this). Here's my current implementation:
DMA settings:
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
ADC settings:
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO; // runs at 768 kHz for 14 bit @ 48 kHz
// SYSCLK = 96 MHz, TIM8 ARR = 125 - 1
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
ADC read and oversampling functions:
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
USBD_AUDIO_HandleTypeDef *haudio = hUsbDeviceFS.pClassData;
int16_t *buf_part = haudio->in_buffer + (AUDIO_IN_PACKET / 2) * haudio->in_buffer_half; // get USB mic buffer pointer
/* Oversample for +2 bits and remove mic amplifier offset */
for (uint16_t i = 0; i < (AUDIO_IN_PACKET / 2); i++) {
int32_t avg_value = 0;
for (uint16_t j = 0; j < ADC_BUF_SIZE; j++) {
avg_value += adc_buffer[ADC_BUF_SIZE * i + j];
}
buf_part[i] = (avg_value / 4) - 6826;
}
}
void ADC_to_MIC(void)
{
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_BUF_SIZE * (AUDIO_IN_PACKET / 2));
}
Data IN request handler:
static uint8_t USBD_AUDIO_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
USBD_AUDIO_HandleTypeDef *haudio;
haudio = (USBD_AUDIO_HandleTypeDef*) pdev->pClassData;
if (epnum == (AUDIO_IN_EP & 0x7F))
{
haudio->in_buffer_half = !haudio->in_buffer_half;
uint16_t prev = (AUDIO_IN_PACKET / 2) * !haudio->in_buffer_half;
/* double inversion serves as initialization to 1 or 0 */
ADC_to_MIC();
USBD_LL_FlushEP (pdev, AUDIO_IN_EP);
USBD_LL_Transmit (pdev, AUDIO_IN_EP, (uint8_t*)(haudio->in_buffer + prev), AUDIO_IN_PACKET);
}
return (uint8_t)USBD_OK;
}
What may cause the hiss to appear? I've ruled out hardware issues by reading the ADC into the I2S DAC, there was just a bit of white noise, no hiss at all. Also when I connect ADC input to ground, hiss persists, so I think that the issue is somewhere in the USB part. 1 kHz peak on the spectrum and its harmonics suggest me that the ADC may be out of sync with DataIn host requests because they happen exactly at 1 kHz rate in the full-speed mode but I don't see how to solve this problem.