This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

EK-TM4C1294XL: usb lib, usbdaudio usage example, isochronous interfaces

Part Number: EK-TM4C1294XL

Good Afternoon.

I would like to stream data at audio frequencies from a control computer to an instrument that I am building. I would like to use the usbdaudio driver and TIRTOS for the TMS1294 processor.

The device shows up as a USB audio device and, under Linux, I can open a screen to ‘test audio’ to the interface, however the buffer completion callback routine that is sent as the fourth parameter of the “USBAudioBufferOut” function is never being called.

I see counters increment on various events, but not on the bufferCallback event. I see that the code reaches breakpoints in the ‘HandleEndpoints’ function at about line 550 of usbdaudio.c, and that it gets to the receive interrupt pending routines at about line 580. However the test to see that the DMA has competed at about line 600 is never passed, hence the DMA completion event is never executed.

If do not make the call toe “USBAudioBufferOut” the data stream, from The Linux host appears to play the data stream correctly as long as I do not call the “USBAudioBufferOut” function. However, this function is necessary to get data from the USB interface. When the USBAudioBufferOut call is made, the USB bus on the Linux computer “locks up” forcing a reboot of the system.

I used the code from chapter 2.3, of the TivaWare USB Library User Guide as starting point.

My goal is to see the end of buffer completion events, in particular the “bufferCallback” function being called, and the associated counter incremented with new incoming packets. This is near line 204 of the included file.

My suspicion is that the USB DMA engine is not being properly initialized. Unfortunately, I do not have a working example to follow.

Thank you very much for your help with this.

Douglas Todd.

// USB_Composite.c
// Audio and serial communication classes.




/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>

/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/gates/GateMutex.h>
#include <ti/sysbios/hal/Hwi.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/knl/Task.h>

#include <stdbool.h>
#include <stdint.h>

/* driverlib Header files */
#include <inc/hw_ints.h>
#include <inc/hw_types.h>
#include <inc/hw_memmap.h>
#include <driverlib/usb.h>
#include <ti/drivers/GPIO.h>


/* usblib Header files */
#include <usblib/usb-ids.h>
#include <usblib/usblib.h>
#include <usblib/usbcdc.h>
#include <usblib/usblibpriv.h>
#include <usblib/device/usbdevice.h>
#include <usblib/device/usbdcomp.h>
#include <usblib/device/usbdcdc.h>
#include <usblib/device/usbdaudio.h>


#include "Board.h"

// Handlers for Audio messages.

static unsigned cbAudioHandler(void *cbData, unsigned event,unsigned eventMsg, void *eventMsgPtr);



/* Device Strings */
static const unsigned char g_pui8LangDescriptor[] = {
    4,
    USB_DTYPE_STRING,
    USBShort(USB_LANG_EN_US)
};


/* The manufacturer string. */
static const unsigned char g_pui8ManufacturerString[] = {
    (18 + 1) * 2,
    USB_DTYPE_STRING,
    'M',0,'a',0,'n',0,'u',0,'f',0,'a',0,'c',0,'t',0,
    'u',0,'r',0,'e',0,'r',0,'S',0,'t',0,'r',0,'i',0,
    'n',0,'g',0

};

static const unsigned char g_pui8ProductString[] = {
  ( 17+1)*2,
  USB_DTYPE_STRING,
  // Audio and Communications  port
  'A',0,'u',0,'d',0,'i',0,'o',0,' ',0,'a',0,'n',0,
  'd',0,' ',0,'C',0,'o',0,'m',0,'p',0,'o',0,'r',0,
  't',0
};


/* The serial number string. */
static const unsigned char g_pui8SerialNumberString[] = {
    (8 + 1) * 2,
    USB_DTYPE_STRING,
    '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0
};

//*****************************************************************************
static const unsigned char g_pui8HIDInterfaceString[] = {
      (15 + 1) * 2,
USB_DTYPE_STRING,
'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o',  0, ' ', 0, 'I', 0, 'n', 0,
't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, 'e', 0
};


const uint8_t g_pui8ConfigString[] = {
(20 + 1) * 2,
USB_DTYPE_STRING,
'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0, ' ' , 0, ' ', 0, 'C', 0,
'o', 0, 'n', 0, 'f', 0, 'i', 0, 'g', 0, 'u', 0, 'r', 0, 'a', 0,
't', 0, 'i', 0, 'o', 0, 'n', 0
};

const uint8_t * const g_ppui8StringDescriptors[] ={
    g_pui8LangDescriptor,
    g_pui8ManufacturerString,
    g_pui8ProductString,
    g_pui8SerialNumberString,
    g_pui8HIDInterfaceString,
    g_pui8ConfigString
};

#define NUM_STRING_DESCRIPTORS (sizeof(g_ppui8StringDescriptors) / sizeof(uint8_t *))

static  tUSBDAudioDevice g_sAudioDevice = {
  USB_VID_TI_1CBE,
  USB_PID_AUDIO,
  "Hello   ", // 8 byte vendor string
  "World           ",
  "9876",
  0,
  USB_CONF_ATTR_SELF_PWR,
  cbAudioHandler,
  g_ppui8StringDescriptors,
  NUM_STRING_DESCRIPTORS,
  0x100,                  // Max Volume.
  0x000,                 // Min volume
  0x014,                 // Step Volume
};


// Instance address returned by the USB audio functions.
static void * pvAudioDevice ;
unsigned bufferCount = 0;
#define ISOC_OUT_EP_MAX_SIZE ((48000*4)/1000)

uint16_t audioBufferA [ISOC_OUT_EP_MAX_SIZE/2];
uint16_t audioBufferB [ISOC_OUT_EP_MAX_SIZE/2];

static int supplyBuffer (void);
void bufferCallback ( void * pvBuffer , uint32_t ui32Param , uint32_t ui32Event  ) ;


int counters[16] = {0};
Semaphore_Handle semForground;
GateMutex_Handle gateForground;


static unsigned cbAudioHandler ( void * cbData, unsigned event,
				 unsigned eventMsg, void * eventMsgPtr){
    int retf;
  switch (event ){
  case USB_EVENT_CONNECTED:               // 0
      counters[0] ++;
      break;
  case USB_EVENT_DISCONNECTED:            //1
      counters[1] ++;
      break;
  case USB_EVENT_TX_COMPLETE:             // 5
      counters[2] ++;
      break;


  case  USBD_AUDIO_EVENT_IDLE:           // C000
      counters[3] ++;
      break;
  case USBD_AUDIO_EVENT_ACTIVE:          // C001
      counters[4] ++;
      supplyBuffer ();
      break;

      // Not reached...
  case  USBD_AUDIO_EVENT_DATAOUT:         //C002
      counters[5] ++;
      //Semaphore_post ( semForground );
      // eventMsg Ptr has data, eventMsg has the length.
      //supplyBuffer();
      break;

  case  USBD_AUDIO_EVENT_MUTE:           // C005
      counters[6] ++;
      break;
  case  USBD_AUDIO_EVENT_VOLUME:         // C004
      counters[7] ++;
      break;
  default:
      counters[8] ++;
      break;
  }
  return 0;
}




static Void USB_hwiHandler(UArg arg0) {
    counters[9] ++;
    USB0DeviceIntHandler();
}


/*
 * Function that _should_ be called when the interface needs another buffer.
 */

void bufferCallback ( void * pvBuffer , uint32_t ui32Param , uint32_t ui32Event  ) {
    counters[10] ++;
    supplyBuffer();
}

/*
 * Maintain two buffers, and operate them in ping-pong style.
 */
static int  supplyBuffer() {
    int retv = 0;
    void * pBuffer = (void*) (bufferCount ++ & 1 ? audioBufferA : audioBufferB) ;
    retv = USBAudioBufferOut( pvAudioDevice, pBuffer, ISOC_OUT_EP_MAX_SIZE, bufferCallback  );
    return retv;
}


/* Stub task for now.
 *
 */
static void taskUsbForground ( unsigned arg0, unsigned arg1 ){
    // int isInvalid;
    while (1 ) {
        Task_sleep(1000);
        //Semaphore_pend( semForground , BIOS_WAIT_FOREVER );
        counters[11] ++;

    }
}


/* Initialization routines
 *
 */
void USB_init(void ) {
  Hwi_Handle hwi;
  Error_Block eb;
  Task_Handle taskHandle;
  Error_init(&eb);


  Semaphore_Params semParams;
  Semaphore_Params_init( &semParams);
  semParams.mode = Semaphore_Mode_BINARY;
  

  // Task_Params
  taskHandle = Task_create (&taskUsbForground, NULL, NULL);


  /* Install interrupt handler */

  hwi = Hwi_create(INT_USB0, USB_hwiHandler, NULL, &eb);
  if (hwi == NULL) {
    System_abort("Can't create USB Hwi");
  }

  semForground = Semaphore_create (0, &semParams, &eb) ;
  if ( semForground == NULL ){
      System_abort ("Could not create forground semaphore");
  }


  USBStackModeSet(0, eUSBModeDevice, 0);
  pvAudioDevice = USBDAudioInit(0, &g_sAudioDevice);

  // Provision the audio device with the first buffer.
  // Note that this will cause the USB bus to crash. We don't see received data.
  supplyBuffer();
}



/*
 *  ======== main ========
 */
int main(void){

    Task_Params taskParams;

    /* Call board init functions */
    Board_initGeneral();
    Board_initGPIO();
    Board_initUSB(Board_USBDEVICE);
    USB_init();

    /* Turn on user LED */
    GPIO_write(Board_LED0, Board_LED_ON);

    System_printf("Starting USB SimpleAudio\n");
    System_flush();

    BIOS_start();

    return (0);
}



 

 

  • Hello Douglas,

    I need more time to look into this issue, I will try and have some sort of update tomorrow.
  • Hello Todd,

    Sorry about the delay. I take it that you have taken an existing example and made changes to it? I can't find any USB audio examples with the TM4C being an audio device unfortunately. Add in the TI-RTOS element that I am not as familiar with it, and it's a bit of a challenge to try and figure out what might be going on.

    Let's start with the USB DMA portion first though, the DMA would need to be configured as part of the Audio Device Instance. So you'd have to setup In and Out channels, and a tUSBDMAInstance. If you haven't done these steps, then the DMA wouldn't work.

    Were you able to get any sort of operation without the DMA by the way? If not, we may want to investigate without DMA to remove one possible poit of failure.
  • Good afternoon Ralph,

    Thank you for your input on this.

    I wanted to let you know that I have found a workaround and am testing the isochronous interface. I'm including the major changes from the code that I sent you earlier.

    It turns out that Ti removed the usb_audio_dev example when they retired the Stellaris processor, I believe that code example was the only one that was related to the Tiva. The code that I gave you was drawn mostly from the USB library user’s manual, section 2.

    Thinking in c++ terms, my workaround subclasses the tCustomHandlers structure. It replaces the pfnEndpointHandler with a handler that uses “normal” USB calls to move data out of the FIFO to the userspace. The library function “USBEndpointGetData can be optimized, we know that our data is aligned to 4-byte boundaries, so the number of movements is limited to 48 per 1 ms packet. At that rate, if we transfer word size entities, it’s hardly worth setting up the USB DMA to handle the transfer.

    In any case, have fun with the code. 

    Doug.

     

     

    /*
    Mote that these are changes to the previous code from above.
    Need to add these header files
    */
    
    #include <driverlib/rom.h>
    #include <driverlib/rom_map.h>
    #include <driverlib/debug.h>
    
    
    #include <string.h>            // For memset.
    
    /*
    Additional code...
    */
    
    
    /*
     * Overr-ride the "HandleEndpoints method within the tCustomeHandlers
     * data structure. Have it point to our own method handler, which
     * uses non-dma copy methods.
     */
    
    static void  handleEndpoints(void *pvAudioDevice, uint32_t ui32Status)
    {
        uint32_t ui32EPStatus;
        tAudioInstance *psInst;
        tUSBDAudioDevice *psAudioDevice;
    
        ASSERT(pvAudioDevice != 0);
    
        //
        // The audio device structure pointer.
        //
        psAudioDevice = (tUSBDAudioDevice *)pvAudioDevice;
    
        //
        // Create a pointer to the audio instance data.
        //
        psInst = &psAudioDevice->sPrivateData;
    
        //
        // Read out the current endpoint status.
        //
        ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ui8OUTEndpoint);
    
        //ui32Size = USBEndpointDataAvail(psInst->ui32USBBase,
        //                          psInst->ui8OUTEndpoint);
    
        if ( USB_DEV_RX_FIFO_FULL == ui32Status ||
                USB_DEV_RX_PKT_RDY  == ui32Status  ) {
            uint32_t uiSize = psInst->sBuffer.ui32Size;
            int getDataError = USBEndpointDataGet ( psInst->ui32USBBase,
                                                psInst->ui8OUTEndpoint,
                                                psInst->sBuffer.pvData,
                                                &uiSize);
    
            MAP_USBDevEndpointDataAck(USB0_BASE, psInst->ui8OUTEndpoint, 0);
            psInst->sBuffer.pfnCallback(psInst->sBuffer.pvData,
                                 psInst->sBuffer.ui32Size,
                                 USBD_AUDIO_EVENT_DATAOUT);
            //
            // Clear the status bits.
            //  -
            MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ui8OUTEndpoint,
                                                ui32EPStatus);
        }
    }
    
    /*
    *  Replace the method handler for the USB endpoint with our custom method handler
    */
    static tCustomHandlers newCallbacks;
    void setHandlerEndpoints ( void * pvAudioDevice ){
        tUSBDAudioDevice * s_AudioDevice = ( tUSBDAudioDevice * ) pvAudioDevice ;
        tDeviceInfo  * sDeviceInfo = &s_AudioDevice->sPrivateData.sDevInfo;
        const tCustomHandlers * pCustomHandlers = sDeviceInfo->psCallbacks;
        memcpy ( & newCallbacks, pCustomHandlers, sizeof ( tCustomHandlers ));
        newCallbacks.pfnEndpointHandler = handleEndpoints;
        sDeviceInfo ->psCallbacks = & newCallbacks;
    }
    
    
    /* Changes to previous functions for testing.
    */
    
    
    /*
     * Function that _should_ be called when the interface needs another buffer.
     */
    
    void bufferCallback ( void * pvBuffer , uint32_t ui32Param , uint32_t ui32Event  ) {
        int increment = 0;
        if ( bufferCount & 1 )
            increment =  audioBufferB[0] != 0;
        else
            increment = audioBufferA[0] != 0 ;
    
        if ( increment )
            counters[10] ++;
    
        supplyBuffer();
    }
    
    /*
     * Maintain two buffers, and operate them in ping-pong style.
     */
    static int  supplyBuffer() {
        int retv = 0;
        void * pBuffer = (void*) (bufferCount ++ & 1 ? audioBufferA : audioBufferB) ;
        retv = USBAudioBufferOut( pvAudioDevice, pBuffer, ISOC_OUT_EP_MAX_SIZE, bufferCallback  );
        return retv;
    }
    
    
    /* Only change in USB_init is the call to setHandlerEndpoints near the end of the initialization function.
    */
    
    
    /* Initialization routines
     *
     */
    void USB_init(void ) {
      Hwi_Handle hwi;
      Error_Block eb;
      Task_Handle taskHandle;
      Error_init(&eb);
    
    
      Semaphore_Params semParams;
      Semaphore_Params_init( &semParams);
      semParams.mode = Semaphore_Mode_BINARY;
      
    
      // Task_Params
      taskHandle = Task_create (&taskUsbForground, NULL, NULL);
    
    
      /* Install interrupt handler */
    
      hwi = Hwi_create(INT_USB0, USB_hwiHandler, NULL, &eb);
      if (hwi == NULL) {
        System_abort("Can't create USB Hwi");
      }
    
      semForground = Semaphore_create (0, &semParams, &eb) ;
      if ( semForground == NULL ){
          System_abort ("Could not create forground semaphore");
      }
    
      memset ( audioBufferA , 0xaa , sizeof ( audioBufferA));
      memset ( audioBufferB, 0xbb, sizeof ( audioBufferB ));
    
      USBStackModeSet(0, eUSBModeDevice, 0);
      pvAudioDevice = USBDAudioInit(0, &g_sAudioDevice);
      // Apply fixups so that we handle the endpoint.
      setHandlerEndpoints (pvAudioDevice);
    
      // Provision the audio device with the first buffer.
      // Note that this will cause the USB bus to crash. We don't see received data.
      supplyBuffer();
    }
    
    
    // You may need to fix various header files.
    // 
    
    

     

  • Hello Doug,

    Great to hear you were able to work this out already! And thanks a lot for sharing what you've done, it will surely help others in community in the future. :)

    Also good to know Stellaris had that example, I'll take a look into it and see if it's worth pulling into TivaWare. I wasn't supporting these devices back in the Stellaris days so I didn't think to look at those examples.