Skip to main content
Visitor II
July 18, 2019
Question

STM32 USB Audio Device (microphone) with 24-bit resolution

  • July 18, 2019
  • 2 replies
  • 4078 views

As far as I know, STM32Cube does not offer support for creating an USB microphone (only speaker, as stated in the specification). However, I have managed to use the X-CUBE-MEMSMIC1 package to create an audio device, only replacing the usbd_audio.c and .h files.

The second problem that I have is that this package only offers support for 16-bit samples. By changing the descriptors below (in file usbd_audio_in.c), the device is recognized as a 24-bit device :

USBD_AUDIO_CfgDesc[index++] = 0x03; /* bSubFrameSize */ //For 16: 0x02
 USBD_AUDIO_CfgDesc[index++] = 24; /* bBitResolution */ // For 16: 16

To send the samples, we use a function called USBD_AUDIO_Data_Transfer :

/**
* @brief USBD_AUDIO_Data_Transfer
* Fills the USB internal buffer with audio data from user
* @param pdev: device instance
* @param audioData: audio data to be sent via USB
* @param dataAmount: number of PCM samples to be copyed
* @note Depending on the calling frequency, a coherent amount of samples must be passed to 
* the function. E.g.: assuming a Sampling frequency of 16 KHz and 1 channel, 
* you can pass 16 PCM samples if the function is called each millisecond, 
* 32 samples if called every 2 milliseconds and so on. 
* @retval status
*/
uint8_t USBD_AUDIO_Data_Transfer(USBD_HandleTypeDef *pdev, int32_t * audioData, uint32_t PCMSamples)
{
 
 USBD_AUDIO_HandleTypeDef *haudio;
 haudio = (USBD_AUDIO_HandleTypeDef *)pdev->pClassData;
 
 if(haudioInstance.state==STATE_USB_WAITING_FOR_INIT){ 
 return USBD_BUSY; 
 } 
 uint16_t dataAmount = PCMSamples * 2; /*Bytes*/ // Maybe here?
 uint16_t true_dim = haudio->buffer_length;
 uint16_t current_data_Amount = haudio->dataAmount;
 uint16_t packet_dim = haudio->paketDimension;
 
 if(haudio->state==STATE_USB_REQUESTS_STARTED || current_data_Amount!=dataAmount){ 
 
 /*USB parameters definition, based on the amount of data passed*/
 haudio->dataAmount=dataAmount; 
 uint16_t wr_rd_offset = (AUDIO_IN_PACKET_NUM/2) * dataAmount / packet_dim;
 haudio->wr_ptr=wr_rd_offset * packet_dim;
 haudio->rd_ptr = 0;
 haudio->upper_treshold = wr_rd_offset + 1;
 haudio->lower_treshold = wr_rd_offset - 1;
 haudio->buffer_length = (packet_dim * (dataAmount / packet_dim) * AUDIO_IN_PACKET_NUM);
 
 /*Memory allocation for data buffer, depending (also) on data amount passed to the transfer function*/
 if(haudio->buffer != NULL)
 {
 USBD_free(haudio->buffer); 
 }
 haudio->buffer = (uint8_t *)USBD_malloc(haudio->buffer_length + haudio->dataAmount);
 if(haudio->buffer == NULL)
 {
 return USBD_FAIL; 
 }
 memset(haudio->buffer,0,(haudio->buffer_length + haudio->dataAmount));
 haudio->state=STATE_USB_BUFFER_WRITE_STARTED;
 
 
 }else if(haudio->state==STATE_USB_BUFFER_WRITE_STARTED){
 if(haudio->timeout++==TIMEOUT_VALUE){
 haudio->state=STATE_USB_IDLE;
 ((USBD_AUDIO_ItfTypeDef *)pdev->pUserData)->Stop(); 
 haudio->timeout=0;
 }
 memcpy((uint8_t * )&haudio->buffer[haudio->wr_ptr], (uint8_t *)(audioData), dataAmount); 
 haudio->wr_ptr += dataAmount;
 haudio->wr_ptr = haudio->wr_ptr % (true_dim);
 if((haudio->wr_ptr-dataAmount) == 0){
 memcpy((uint8_t *)(((uint8_t *)haudio->buffer)+true_dim),(uint8_t *)haudio->buffer, dataAmount);
 }
 }
 return USBD_OK; 
}

Which changes the state of the USB transfer and copies the data to the buffer. I've changed it to 32-bit input so I can use it with my 24-bit samples.

The function USBD_AUDIO_DataIn is also called and handles the low level :

/**
* @brief USBD_AUDIO_DataIn
* handle data IN Stage
* @param pdev: device instance
* @param epnum: endpoint index
* @retval status
*/
static uint8_t USBD_AUDIO_DataIn (USBD_HandleTypeDef *pdev,
 uint8_t epnum)
{
 
 USBD_AUDIO_HandleTypeDef *haudio;
 haudio = pdev->pClassData;
 uint32_t length_usb_pck;
 uint16_t app;
 uint16_t IsocInWr_app = haudio->wr_ptr;
 uint16_t true_dim = haudio->buffer_length;
 uint16_t packet_dim = haudio->paketDimension;
 uint16_t channels = haudio->channels;
 length_usb_pck = packet_dim; 
 haudio->timeout=0;
 if (epnum == (AUDIO_IN_EP & 0x7F))
 { 
 if (haudio->state == STATE_USB_IDLE) 
 {
 haudio->state=STATE_USB_REQUESTS_STARTED;
 ((USBD_AUDIO_ItfTypeDef *)pdev->pUserData)->Record(); 
 } 
 if (haudio->state == STATE_USB_BUFFER_WRITE_STARTED) 
 { 
 haudio->rd_ptr = haudio->rd_ptr % (true_dim); 
 if(IsocInWr_app<haudio->rd_ptr){
 app = ((true_dim) - haudio->rd_ptr) + IsocInWr_app;
 }else{
 app = IsocInWr_app - haudio->rd_ptr;
 } 
 if(app >= (packet_dim*haudio->upper_treshold)){ 
 length_usb_pck += channels*2;
 }else if(app <= (packet_dim*haudio->lower_treshold)){
 length_usb_pck -= channels*2;
 } 
 USBD_LL_Transmit (pdev,AUDIO_IN_EP,
 (uint8_t*)(&haudio->buffer[haudio->rd_ptr]),
 length_usb_pck); 
 haudio->rd_ptr += length_usb_pck; 
 
 if(app < haudio->buffer_length/10)
 {
 ((USBD_AUDIO_ItfTypeDef *)pdev->pUserData)->Stop();
 haudio->state = STATE_USB_IDLE; 
 haudio->timeout=0;
 memset(haudio->buffer,0,(haudio->buffer_length + haudio->dataAmount));
 } 
 }
 else 
 { 
 USBD_LL_Transmit (pdev,AUDIO_IN_EP,
 IsocInBuffDummy,
 length_usb_pck); 
 } 
 }
 return USBD_OK;
}
 
/**
* @brief USBD_AUDIO_EP0_RxReady
* handle EP0 Rx Ready event
* @param pdev: device instance
* @retval status
*/
 
static uint8_t USBD_AUDIO_EP0_RxReady (USBD_HandleTypeDef *pdev)
{ 
 USBD_AUDIO_HandleTypeDef *haudio;
 haudio = pdev->pClassData; 
 if (haudio->control.cmd == AUDIO_REQ_SET_CUR)
 { 
 if (haudio->control.unit == AUDIO_OUT_STREAMING_CTRL)
 {
 ((USBD_AUDIO_ItfTypeDef *)pdev->pUserData)->VolumeCtl(VOL_CUR); 
 
 haudio->control.cmd = 0;
 haudio->control.len = 0;
 haudio->control.unit = 0;
 haudio->control.data[0]=0;
 haudio->control.data[0]=0;
 }
 } 
 return USBD_OK;
}

And finally, at the initialization, we define the packet size. As I noticed by looking at the X-CUBE-USB-AUDIO package, the packet size should be multiplied by 3 instead of 2 for 24-bit instead of 16 bit :

haudioInstance.paketDimension = (samplingFrequency/1000*Channels*3); // For 16: *2
 haudioInstance.frequency=samplingFrequency;
 haudioInstance.buffer_length = haudioInstance.paketDimension * AUDIO_IN_PACKET_NUM;
 haudioInstance.channels=Channels; 
 haudioInstance.upper_treshold = 5;
 haudioInstance.lower_treshold = 2;
 haudioInstance.state = STATE_USB_WAITING_FOR_INIT;
 haudioInstance.wr_ptr = 3 * haudioInstance.paketDimension;
 haudioInstance.rd_ptr = 0; 
 haudioInstance.dataAmount=0;
 haudioInstance.buffer = 0;

But when I change this my microphone simply stops working (though it is still recognized by the host). And when I keep the same parameters as the 16-bit I do not get good results for the exit.

My best guesses are that it has something to do with the buffer size (dataAmount) or the writing pointer wr_prt.

Does anyone know how to solve this?

Regards,

Gabriel

    This topic has been closed for replies.

    2 replies

    Visitor II
    April 1, 2020

    Hello,

    I am trying to fix this issue also.

    did someone solve it already?

    Avi

    Visitor II
    July 24, 2020

    ran into the same issue... looking for a fix...

    Visitor II
    July 26, 2020

    Hi,

    I found the answer,

    In the function USBD_AUDIO_DataIn()

    You should change the calculation of length_usb_pck to be depended on the resolution.

    if(app >= (packet_dim*haudio->upper_treshold)){    

        length_usb_pck += channels*3;//2;

       }else if(app <= (packet_dim*haudio->lower_treshold)){

     length_usb_pck -= channels*3;//2;

    Avi

    Visitor II
    March 27, 2021

    Hi

    i have the same problem so please can you include all the modification needed for 24 bits audio