• Join
  • Sign In with my.TI Login
Texas Instruments
  • Products
  • Applications
  • Tools & Software
  • Support & Community
  • Sample & Buy
  • About TI
Sample & Purchase Cart Sample & Purchase Cart
  • Search
  • Advanced
TI E2E™ Community
  • Support Forums
  • Blogs
  • Groups
  • Videos
  • 简体中文
  • More ...
TI Home » TI E2E Community » Support Forums » Microcontrollers » Stellaris® ARM® Microcontrollers » Stellaris® ARM® LM3S Microcontrollers Forum » How to transfer microphone data via usb?
Share
Stellaris® ARM® Microcontrollers
  • Forum
Options
  • Subscribe via RSS
Helpful Stellaris® LM4F Series Links
  • LM4F Series
  • Stellaris PinMux Utility
  • Stellaris® LM4F120 LaunchPad
  • LM4F MCU Applications
  • LM4F MCU Video
  • ARM Cortex-M4F Whitepaper
  • Stellaris MCU Brochure
  • LM4F232 Eval Kit
  • Forums

    How to transfer microphone data via usb?

    This question is not answered
    Andre Marianovich
    Posted by Andre Marianovich
    on Jul 14 2012 10:19 AM
    Intellectual975 points

    Hi

    in my project I use the lm3s9b92 eval kit to connect a digital microphone via I2S. Now I want to send the whole raw data from the microphone to a pc. The sample rate of the microphone is 48 kHz and every sample has a resolution of 24 bit and will be stored in a 32 bit integer variable, so I will get a total amount of 1.536 MBit/s = 192 kByte/s. This is to much to use UART for the transfer, even if I would lower the sample rate. Therefor I want to use USB but I never developed an usb application before. I thought it would make sense to use isochronous transfer mode to have a continious and correctly ordered data stream send to the host. Unfortunately I don't know where to start. Where do I have to take care and how do I read out the send data on the host?

    Thank you for any kind of help.,

    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    All Replies
    • Tsuneo Chinzei
      Posted by Tsuneo Chinzei
      on Aug 05 2012 05:09 AM
      Expert2650 points

      1.) Is it right that the whole enumartion would be handled by the USBDAudioInit() function which calls the USBDCDInit() function? Because in the "ubs_dev_audio" example there seems to be no additional code that deals with any kind of usb configuration processes. Also the usb peripheral won't be enabled explicitely.


      See USBDAudioInit() source code exactly, not by your speculation.
      First, USBDAudioInit() sets up the USB stack by calling USBDAudioCompositeInit() and USBDCDInit(), so that the stack can handle enumeration process interacting with host. This coding is common to every USB device implementation on this stack. After this coding, device-specific coding comes. USBDAudioInit() initialize a DMA channel of an isoc OUT endpoint for speaker output.

      2.) In the theory of usb, the host would send the device an IN token to signal the device that it can send new data from the IN endpoint fifo of the usb controller. Will this token be handled automaticly by the usb lib or do I have to add this functionality in my firmware?

      StellarisWare stack provides APIs which hide the details of USB registers setup, to make your device respond to IN transactions from host. You have to use them in right place, in right way.

      MAP_USBEndpointStatus()
      MAP_USBDevEndpointStatusClear()
      MAP_USBEndpointDataPut()
      MAP_USBEndpointDataSend()

      MAP_USBEndpointDMAChannel()
      MAP_USBEndpointDMAEnable()
      MAP_uDMAChannelTransferSet()
      MAP_uDMAChannelEnable()

      3.) What interrupts can occur that I need to handle in my firmware?

      These interrupts are handled by the stack.
      - bus reset
      - EP0 transactions
      optionally,
      - Suspend/Resume

      Your code has to handle these interrupts,
      - SOF interrupt - to synchronize to bus timing
      - Endpoint interrupt on isoc IN, and optionally DMA interrupt - to move data to the endpoint.


      Your questions are out of focus.
      With these Q & A, you don't get closer to real implementation so much.
      Ask right questions, if you don't like guided tour ;-)

      Tsuneo

      USB Audio
      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Andre Marianovich
      Posted by Andre Marianovich
      on Aug 05 2012 05:15 AM
      Intellectual975 points

      Thank you for your fast reply.

      Tsuneo Chinzei
      Your questions are out of focus.
      With these Q & A, you don't get closer to real implementation so much.
      Ask right questions, if you don't like guided tour ;-)

      Tell me please, what are the right questions? The whole library is very confusing to me.

      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Tsuneo Chinzei
      Posted by Tsuneo Chinzei
      on Aug 05 2012 09:46 AM
      Expert2650 points

      The whole library is very confusing to me.


      Agree.
      The USB stack on StellarisWare is an elaborate one, to hide most of details from user's eyes. It may be good for use, but not good to start USB study with it. Without decent understanding on the way how USB device works, you'll be lost in its complexity.

      \StellarisWare\usblib\device\usbdaudio.c is your target source code of modification.
      I'll give you brief explanation on the routines of this file, along with device working flow, so that you can ask right questions ;-)


      1) At startup initialization, USBDAudioInit() is called.
      USBDAudioInit()
      - USBDAudioCompositeInit() - make up descriptors from the templates.
      - USBDCDInit() - Initialize USB stack and hardware. Connect to bus (enable D+ pull-up register).
      - Device-specific DMA initialization (for isoc OUT endpoint)

      USBDAudioCompositeInit() makes descriptors of AudioControl and AudioStreaming interfaces using these arrays.
      AudioControl - g_pAudioControlInterface[]
      AudioStreaming - g_pAudioStreamInterface[]

      These arrays specify USB speaker.
      Here is an example of USB microphone descriptors, in this chapter of the USB Audio spec,

      USB Audio Device spec 1.0
      http://www.usb.org/developers/devclass_docs/audio10.pdf
      Appendix B. Example 1: USB Microphone
      B.3.3 AudioControl Interface Descriptor
      B.3.4 AudioStreaming Interface Descriptor

      Compare usbdaudio.c code with this example.
      What are the differences?

      With these descriptors given by device, host recognizes the function of your device.
      If your device would send speaker descriptors, naturally, host should regard your device as a speaker. Therefore, you have to change them.

      The descriptors also show options/capabilities which your device offers.
      For example of audio device, mono/stereo, sampling rate, 8/16/24 bits, etc.
      You'll modify the descriptors to declare your favorite options.

      to be continued..

      Tsuneo

      USB Audio
      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Tsuneo Chinzei
      Posted by Tsuneo Chinzei
      on Aug 05 2012 12:33 PM
      Expert2650 points

      2) Enumeration by host
      When host detects voltage change at D+ line (full-speed device), it starts enumeration sequence (requests). The outline of enumeration request sequence is,

      - bus reset
      - Get_Descriptor( DEVICE ) - to know bMaxPacket0
      - Set_Address
      - more Get_Descriptors for Device, Configuration set, and String descriptors
      - Set_Configuration
      - Class-specific requests

      Depending on OS, number of bus resets and Get_Descriptor requests differ.

      In this enumeration course, these requests relates to our target source.

      - Set_Configuration request - ConfigChangeHandler() callback is called by the stack
      This routine just passes "connection" event (USB_EVENT_CONNECTED) to user application AudioMessageHandler(). I don't like this macro name, because it's misleading with device plug in. USB_EVENT_CONFIGURED is better.

      - Class-specific requests - HandleRequests(), DataReceived()
      As this USB speaker offers mute and volume control on its Feature Unit descriptor, host puts a couple of class-specific request to ask current setting of these options, and to set them up.
      But, usually, USB microphone doesn't offer such Feature Unit options. Instead, if you would declare a couple of sampling rate options, 32k, 44.1k or 48kHz, you'll see these calls for Sampling Frequency Control.


      3) Audio streaming
      At the start and end of audio streaming, host puts Set_Interface requests.
      USB spec has a rule that isoc endpoint should start with zero-bandwidth at the default interface. To make the isoc endpoint work, host switches interface of the endpoint to one of alternate interfaces. When streaming finishes, host recover the default interface. Set_Interface request switches target interface from default to alternate, vice-versa.

      At Set_Interface request, the stack calls InterfaceChange().
      This routine passes USBD_AUDIO_EVENT_IDLE event (to default interface), or USBD_AUDIO_EVENT_ACTIVE event (to alternate interface), to user application AudioMessageHandler().
      Also, this routine enables DMA (MAP_USBEndpointDMAEnable) for isoc OUT endpoint at alternate interface.

      Now that audio streaming starts at last :-)
      When a packet is received at the isoc OUT endpoint, and after the packet is copied to audio data buffer by DMA, HandleEndpoints() is called by the USB ISR on the stack - actually, this routine is always called at every USB event. Therefore, the code confirms that the call is caused by DMA, seeing MAP_uDMAChannelModeGet() as UDMA_MODE_STOP. And then, this routine reports USBD_AUDIO_EVENT_DATAOUT to application USBBufferCallback(). Also, it releases the endpoint buffer by MAP_USBDevEndpointDataAck() for the next packet.

      USBBufferCallback() (in usb_dev_audio.c) maintains audio data buffer, so that sound play speed matches to data coming speed over USB. And then, it calls USBAudioBufferOut() (in usbdaudio.c). USBAudioBufferOut() sets up DMA for next transaction.


      4) Disconnection of device
      HandleDisconnect() is called from the stack, when device disconnects from bus.
      This routine just passes disconnection event (USB_EVENT_DISCONNECTED) to user application AudioMessageHandler().

      5) Routines not used on your implementation
      HandleDevice() - For composite device configuration
      USBDAudioTerm() - not called from anywhere


      OK, any question?

      Tsuneo

      USB Audio
      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Andre Marianovich
      Posted by Andre Marianovich
      on Aug 05 2012 14:12 PM
      Intellectual975 points

      So, I really must say thank you for your great effort :-)

      Your first answer, I immediately put into action, the second I'll work through tomorrow and then I'll ask you my questions ;-)

      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Andre Marianovich
      Posted by Andre Marianovich
      on Aug 06 2012 08:10 AM
      Intellectual975 points

      Tsuneo Chinzei
      2) Enumeration by host ...

      Do I understand it right that, in fact, I don't need to take care about the enumeration because the stack will handle the biggest part of it?

      Tsuneo Chinzei
      - Class-specific requests - HandleRequests(), DataReceived()
      As this USB speaker offers mute and volume control on its Feature Unit descriptor, host puts a couple of class-specific request to ask current setting of these options, and to set them up.
      But, usually, USB microphone doesn't offer such Feature Unit options. Instead, if you would declare a couple of sampling rate options, 32k, 44.1k or 48kHz, you'll see these calls for Sampling Frequency Control.

      What would be the most useful way to deal with this functions? I only use 48kHz so there is need for an option to change the sampling frequency.

      Tsuneo Chinzei
      Also, this routine enables DMA (MAP_USBEndpointDMAEnable) for isoc OUT endpoint at alternate interface.

      In this line I changed USB_EP_DEV_OUT to USB_EP_DEV_IN. Should be the necessary change at this point.

      Tsuneo Chinzei
      Now that audio streaming starts at last :-)
      When a packet is received at the isoc OUT endpoint, and after the packet is copied to audio data buffer by DMA, HandleEndpoints() is called by the USB ISR on the stack - actually, this routine is always called at every USB event. Therefore, the code confirms that the call is caused by DMA, seeing MAP_uDMAChannelModeGet() as UDMA_MODE_STOP. And then, this routine reports USBD_AUDIO_EVENT_DATAOUT to application USBBufferCallback(). Also, it releases the endpoint buffer by MAP_USBDevEndpointDataAck() for the next packet.

      USBBufferCallback() (in usb_dev_audio.c) maintains audio data buffer, so that sound play speed matches to data coming speed over USB. And then, it calls USBAudioBufferOut() (in usbdaudio.c). USBAudioBufferOut() sets up DMA for next transaction.

      In fact I "only" need to change the direction, right? So in every function I have to change the order of source and sink of the DMA transfers, I think. But what is the next task?

      Beside this, I read out the I2S buffer by DMA in ping pong mode. The DMA will cause an I2S interrupt if one of the two ping pong buffers is full. I think filling the usb fifo in this ISR might be a good, right?

      Once again, thank you for your very good help :-)

      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Tsuneo Chinzei
      Posted by Tsuneo Chinzei
      on Aug 06 2012 13:06 PM
      Expert2650 points

      Tsuneo Chinzei
      2) Enumeration by host ...

      Do I understand it right that, in fact, I don't need to take care about the enumeration because the stack will handle the biggest part of it?


      Ya, exactly.

      - Class-specific requests - HandleRequests(), DataReceived()

      What would be the most useful way to deal with this functions? I only use 48kHz so there is need for an option to change the sampling frequency.

      For just single frequency, host doesn't put any query/setup (Get_Cur/Set_Cur) request.
      You may leave HandleRequests(), DataReceived() alone. They aren't called.

      More exactly, "Sampling Frequency Control" option is enabled by D0 bit of bmAttributes on Class-specific Isoc Endpoint Descriptor. Set this bit to 0.

      Also, this routine enables DMA (MAP_USBEndpointDMAEnable) for isoc OUT endpoint at alternate interface.

      In this line I changed USB_EP_DEV_OUT to USB_EP_DEV_IN. Should be the necessary change at this point.

      We don't use DMA. The reason is told later.

      In fact I "only" need to change the direction, right?

      Not so simple.
      The reasons are twofold.

      1) Coding difference of IN from OUT endpoint
      OUT endpoint is straightforward. When we see endpoint interrupt, unload a packet from the OUT endpoint.
      As of IN endpoint, we have to load a packet to the endpoint, first, outside of the endpoint ISR. When we see endpoint interrupt, next packet is loaded.

      2) Synchronization
      We have to match the transfer speed over USB to the data production speed by I2S, so that no overflow/underflow occurs. I2S is driven by the clock on the Stellaris chip, or by the clock on the microphone. USB is driven by the clock on PC. Even though crystals are applied on both sides, the frequency of these clocks slightly differs.

      To carry 48kHz audio stream over USB, the IN endpoint moves 48 samples at every 1ms USB frame. When the mic frequency is slightly higher than USB clock, the IN endpoint still moves 48 samples in most of frames, but occasionally 49 samples, to escape from overflow. In this way, transaction size varies by +/-1 sample from frame to frame.

      Actual transaction size of the next frame is determined at SOF timing (*1).
      The samples from I2S are stored on a cyclic buffer once. At SOF interrupt, the increment of buffer from the last SOF timing is captured. In the endpoint ISR, this number of samples are loaded to the IN endpoint.

      As the transaction size is not always the max packet size of the endpoint, we can't use DMA, because of restriction of USB-uDMA of Stellaris. We use "programmed I/O method", MAP_USBEndpointDataPut() and MAP_USBEndpointDataSend(), instead.

      This type of synchronization is called as asynchronous. The isoc endpoint descriptor declares asynchronous at bmAttributes field. Asynchronous endpoint has to put "silent" (all zero) data for 30-40 frames at start up of streaming, until host locks to the device frequency.


      (*1) The original speaker example adjust I2S clock in USBBufferCallback(). So far, so good. But the problem of this implementation is that USBBufferCallback() is called at the timing of endpoint interrupt. The timing of transaction completion moves around in USB frame, affected by other transactions breaking before. For example, in the practice of OHCI host controller, interrupt transactions are placed before isoc transactions in a frame. This irregular timing causes jitter of I2S clock. The tuning of I2S clock should be done at SOF timing on this speaker example, too.

      Tsuneo

      USB Audio
      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Tsuneo Chinzei
      Posted by Tsuneo Chinzei
      on Aug 06 2012 21:11 PM
      Expert2650 points

      Umm..
      Above "Asynchronous" synchronization may be too complicated for the first-time project, though audio maniacs prefer this one because of fixed I2S clock frequency.

      "Synchronous" synchronization method may be easier. The original speaker example also works on this synchronization type. In this method, I2S clock frequency is tuned so that the data production rate of I2S matches to the rate of USB transfer. You may apply ping-pong buffering of I2S with a little modification. Also, USB-uDMA is applied on the USB side, because the packet size is always fixed to 48 samples.

      Host puts Set_Interface request at the start of streaming, which is caught by InterfaceChange(). In this routine,
      - I2S starts.
      The Stellaris I2S runs as a master. Instead of two-buffers ping-pong, the I2S fills four  (or more) buffers in turn. We assign greater number of buffers to make room, so that overflow/underflow doesn't immediately occur. The buffers are "pre-charged" by I2S, until isoc transactions start by host, regardless of overwrite on old data.
      - The isoc IN endpoint is loaded with "silent" (all zero) packet of 48 samples.
      With this "dummy" packet, we know the timing when the real isoc streaming starts.

      Host starts isoc transactions after around 20 frames delay from Set_Interface.
      We know this timing with HandleEndpoints(). In this routine,
      - We start DMA so that it fills the isoc IN endpoint with 48 samples packet from one of the "pre-charged" buffers. At the first time call of this routine, a buffer of two delay from the current I2S target is chosen. At the second and later call, the next buffer from the last call is transferred.
      - This routine raises a flag, to start tuning process at next SOF timing.

      On the SOF interrupt,
      - The I2S target buffer and transferred buffer is compared. To keep the distance of these buffers, I2S clock is tuned, like original USBBufferCallback() does.

      Tsuneo

      USB Audio
      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Andre Marianovich
      Posted by Andre Marianovich
      on Aug 07 2012 07:58 AM
      Intellectual975 points

      Tsuneo Chinzei
      Host puts Set_Interface request at the start of streaming, which is caught by InterfaceChange(). In this routine,
      - I2S starts.

      So I have to add the I2S start code in the else branche, where the USBD_AUDIO_EVENT_ACTIVE event occurs?

      Tsuneo Chinzei
      The Stellaris I2S runs as a master. Instead of two-buffers ping-pong, the I2S fills four  (or more) buffers in turn. We assign greater number of buffers to make room, so that overflow/underflow doesn't immediately occur. The buffers are "pre-charged" by I2S, until isoc transactions start by host, regardless of overwrite on old data.

      What should be a usefull buffer size? Four (or more) arrays containing 48 samples (long buffer[4][48])?

      Tsuneo Chinzei
      - The isoc IN endpoint is loaded with "silent" (all zero) packet of 48 samples.
      With this "dummy" packet, we know the timing when the real isoc streaming starts.

      Should this be done by using USBEndpointDataPut() in the InterfaceChange()?

      Tsuneo Chinzei
      This routine raises a flag, to start tuning process at next SOF timing

      You mean the line "psInst->sBuffer.pfnCallback(pucData, psInst->sBuffer.ulSize, USBD_AUDIO_EVENT_DATAOUT);" which calls the USBBufferCallback() ?

      Tsuneo Chinzei
      On the SOF interrupt,
      - The I2S target buffer and transferred buffer is compared. To keep the distance of these buffers, I2S clock is tuned, like original USBBufferCallback() does.

      So the distance between I2S target buffer and the one that should be transfered by usb should be one, right?


      I hope you have enough energy to endure my beginner questions :-)

      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Tsuneo Chinzei
      Posted by Tsuneo Chinzei
      on Aug 07 2012 13:19 PM
      Expert2650 points

      Host puts Set_Interface request at the start of streaming, which is caught by InterfaceChange(). In this routine,
      - I2S starts.

      So I have to add the I2S start code in the else branche, where the USBD_AUDIO_EVENT_ACTIVE event occurs?


      That's right.

      The Stellaris I2S runs as a master. Instead of two-buffers ping-pong, the I2S fills four  (or more) buffers in turn. We assign greater number of buffers to make room, so that overflow/underflow doesn't immediately occur. The buffers are "pre-charged" by I2S, until isoc transactions start by host, regardless of overwrite on old data.

      What should be a usefull buffer size? Four (or more) arrays containing 48 samples (long buffer[4][48])?

      Umm..
      I noticed that the original speaker example assigns 20 buffers.
      usb_dev_audio.c
      #define AUDIO_BUFFER_SIZE       (AUDIO_PACKET_SIZE*20)

      Required number of buffers depends on the response of tuning method.
      OK, we also start with 20 buffers, and titrate it later.

      Is the microphone monaural, 24 bits?
      Then "long buffer[20][48];" is fine, when you declare TypeI format descriptor, as follows.
      The 24 bits should be "left-justified" in 4-bytes "long", ie. the LSB one byte is 0.
      With this setting, the buffer is passed to endpoint without any conversion.

      usbdaudio.c
      const unsigned char g_pAudioStreamInterface[] =
      {
          ...
          //
          // Format type Audio Streaming descriptor.
          //
          11,                         // Size of the interface descriptor.
          USB_DTYPE_CS_INTERFACE,     // Interface descriptor is class specific.
          USB_ASDSTYPE_FORMAT_TYPE,   // Audio Streaming format type.
          USB_AF_TYPE_TYPE_I,         // Type I audio format type.
          1,                          // One audio channels.              // <-----
          4,                          // Four bytes per audio sub-frame.  // <-----
          24,                         // 24 bits per sample.              // <-----
          1,                          // One sample rate provided.
          USB3Byte(48000),            // Only 48000 sample rate supported.
          ...

      - The isoc IN endpoint is loaded with "silent" (all zero) packet of 48 samples.
      With this "dummy" packet, we know the timing when the real isoc streaming starts.

      Should this be done by using USBEndpointDataPut() in the InterfaceChange()?

      Exactly. USBEndpointDataPut() copies data to the endpoint buffer.
      Set up USB_EP_AUTO_SET to the isoc IN endpoint. And then, when the entire packet is written, the buffer is automatically validated without calling USBEndpointDataSend() - In this "Synchronous" method, we apply fixed size packet, which matches to wMaxPacketSize of the endpoint.

      usbdaudio.c
      const tFIFOConfig g_sUSBAudioFIFOConfig =
      {
          //
          // IN endpoints.
          //
          {
              { false, USB_EP_DEV_IN | USB_EP_AUTO_SET },  // <----
              { false, USB_EP_DEV_IN },
              ...

      If you would feel DMA setting troublesome, you may apply USBEndpointDataPut() for every packet copy. The packet size is not so large.

      This routine raises a flag, to start tuning process at next SOF timing

      You mean the line "psInst->sBuffer.pfnCallback(pucData, psInst->sBuffer.ulSize, USBD_AUDIO_EVENT_DATAOUT);" which calls the USBBufferCallback() ?

      No, we don't call USBBufferCallback() here. The tuning function of USBBufferCallback() moves to the SOF ISR. Instead, we make a new flag, which activates tuning function. This flag is enabled here.

      On the SOF interrupt,
      - The I2S target buffer and transferred buffer is compared. To keep the distance of these buffers, I2S clock is tuned, like original USBBufferCallback() does.

      So the distance between I2S target buffer and the one that should be transfered by usb should be one, right?

      Yes, if the buffers were 4.
      We increase the buffers into 20.
      The tuning function in USBBufferCallback() assigns threshold of 10 +/-2


      Now, your questions are well focused.
      Good job.

      Tsuneo

      USB Audio
      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Andre Marianovich
      Posted by Andre Marianovich
      on Aug 08 2012 08:27 AM
      Intellectual975 points

      Tsuneo Chinzei
      Is the microphone monaural, 24 bits?
      Then "long buffer[20][48];" is fine, when you declare TypeI format descriptor, as follows.
      The 24 bits should be "left-justified" in 4-bytes "long", ie. the LSB one byte is 0.
      With this setting, the buffer is passed to endpoint without any conversion.

      Thats right, 24 Bit mono left-justified.

      Tsuneo Chinzei
      Exactly. USBEndpointDataPut() copies data to the endpoint buffer.
      Set up USB_EP_AUTO_SET to the isoc IN endpoint. And then, when the entire packet is written, the buffer is automatically validated without calling USBEndpointDataSend() - In this "Synchronous" method, we apply fixed size packet, which matches to wMaxPacketSize of the endpoint.

      usbdaudio.c
      const tFIFOConfig g_sUSBAudioFIFOConfig =
      {
          //
          // IN endpoints.
          //
          {
              { false, USB_EP_DEV_IN | USB_EP_AUTO_SET },  // <----
              { false, USB_EP_DEV_IN },
              ...

      If you would feel DMA setting troublesome, you may apply USBEndpointDataPut() for every packet copy. The packet size is not so large.

      Is #define AUDIO_PACKET_SIZE       (((48000*4)/1000) * 2) and wMaxPacketSize the same?

      The usb controller offers one fifo for all endpoints that I can configure and split, as I understand right. Where/how do I have to configure the fifo size or would the whole fifo space automaticly be used if I only use one endpoint?

      I think it might be reasonable to directly use DMA so I set

          //
          // IN endpoints.
          //
          {
              { false, USB_EP_DEV_IN | USB_EP_DMA_MODE_1 |  USB_EP_AUTO_SET},
              { false, USB_EP_DEV_IN },

      Tsuneo Chinzei
      No, we don't call USBBufferCallback() here. The tuning function of USBBufferCallback() moves to the SOF ISR. Instead, we make a new flag, which activates tuning function. This flag is enabled here.

      So the HandleEndpoints() would set the flag? And the body of the USBBufferCallback() routine moves to the SOF ISR? Which one is the SOF ISR?

      Tsuneo Chinzei
      Now, your questions are well focused.
      Good job.

      That's all your earnings :-)

      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Tsuneo Chinzei
      Posted by Tsuneo Chinzei
      on Aug 08 2012 15:26 PM
      Expert2650 points

      Is #define AUDIO_PACKET_SIZE  (((48000*4)/1000) * 2) and wMaxPacketSize the same?


      The original example applies twice the wMaxPacketSize size, as the comment on the usb_dev_audio.c tells.
      In our case,
      #define AUDIO_PACKET_SIZE  ((48000*4)/1000)

      usbdaudio.c
      #define ISOC_IN_EP_MAX_SIZE  ((48000*4)/1000)


      The usb controller offers one fifo for all endpoints that I can configure and split, as I understand right. Where/how do I have to configure the fifo size or would the whole fifo space automaticly be used if I only use one endpoint?


      In the process of Set_Configuration request on enumeration, the stack reads out wMaxPacketSize from the endpoint descriptor. Stack writes wMaxPacketSize to USBTXMAXPn register. Also, stack determines FIFO size according to wMaxPacketSize and bDoubleBuffer flag (the first flag of each entry on g_sUSBAudioFIFOConfig). It sets the value to USBTXFIFOSZ

      Set_Configuration request
        --> USBDSetConfiguration()
          --> USBDeviceConfig()
            --> GetEndpointFIFOSize() --> USBFIFOConfigSet()
            --> USBDevEndpointConfigSet()

      Therefore, when we set up right value to wMaxPacketSize of the endpoint descriptor, the stack takes care of the rest.

      So the HandleEndpoints() would set the flag? And the body of the USBBufferCallback() routine moves to the SOF ISR? Which one is the SOF ISR?

      Exactly.

      Which one is the SOF ISR?


      The stack enables SOF interrupt, but it doesn't expose direct callback to users.
      The USB ISR is modified to call our SOF ISR, as follows.

      usbdhandler.c

      void
      USB0DeviceIntHandler(void)
      {
          unsigned long ulStatus;

          //
          // Get the controller interrupt status.
          //
          ulStatus = MAP_USBIntStatusControl(USB0_BASE);

                                 // <------- insert from here
          //
          // Start of Frame was received.
          //
          if(ulStatus & USB_INTCTRL_SOF)
          {
              // call our SOF ISR here
          }
                                 // <------- to here
          //
          // Call the internal handler.
          //
          USBDeviceIntHandlerInternal(0, ulStatus);
      }

      Tsuneo

      USB Audio
      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Andre Marianovich
      Posted by Andre Marianovich
      on Aug 10 2012 09:55 AM
      Intellectual975 points

      Tsuneo Chinzei
      So the HandleEndpoints() would set the flag? And the body of the USBBufferCallback() routine moves to the SOF ISR? Which one is the SOF ISR?

      Exactly.

      Which one is the SOF ISR?


      The stack enables SOF interrupt, but it doesn't expose direct callback to users.
      The USB ISR is modified to call our SOF ISR, as follows.

      usbdhandler.c

      void
      USB0DeviceIntHandler(void)
      {
          unsigned long ulStatus;

          //
          // Get the controller interrupt status.
          //
          ulStatus = MAP_USBIntStatusControl(USB0_BASE);

                                 // <------- insert from here
          //
          // Start of Frame was received.
          //
          if(ulStatus & USB_INTCTRL_SOF)
          {
              // call our SOF ISR here
          }
                                 // <------- to here
          //
          // Call the internal handler.
          //
          USBDeviceIntHandlerInternal(0, ulStatus);
      }

      So as a result, I can replace "// call our SOF ISR here" by the body of the USBBufferCallback() routine? And how would setting the flag in the HandleEndpoints() look like?

      EDIT: what's about HandleEndpoints() routine in general? I think this is the right place to start new DMA transfers for filling the fifo with data buffers from the I2S, isn't it?

      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Tsuneo Chinzei
      Posted by Tsuneo Chinzei
      on Aug 10 2012 12:28 PM
      Expert2650 points

      So as a result, I can replace "// call our SOF ISR here" by the body of the USBBufferCallback() routine?


      The body of the USBBufferCallback() refers to private variables, like g_sBuffer. If you would directly insert the body code here, you should move everything related to this usbdhandler.c.
      Make a new function for SOF ISR, and call it in usbdhandler.c
      And then, you may place the body of SOF ISR in any file as you like.

      And how would setting the flag in the HandleEndpoints() look like?

      Make a global variable for the flag, initialized FALSE as the default.
      Set it in HandleEndpoints()

      what's about HandleEndpoints() routine in general? I think this is the right place to start new DMA transfers for filling the fifo with data buffers from the I2S, isn't it?

      Exactly.
      HandleEndpoints() is called at every USB event, not just endpoint interrupt.
      At the entry of this routine, we have to confirm that this call is caused by our endpoint.

      HandleEndpoints(void *pvInstance, unsigned long ulStatus)
      {
          const tUSBDAudioDevice *psDevice;
          tAudioInstance *psInst;

          psDevice = (const tUSBDAudioDevice *)pvInstance;
          psInst = psDevice->psPrivateData;

          //
          // Make sure this was for the isochronous IN endpoint.
          //
          if(ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucINEndpoint)))
          {
              // fill endpoint with next packet
          }
      }

      Ah, tAudioInstance.ucOUTEndpoint (usbdaudio.h) is replaced to ucINEndpoint

      Tsuneo

      USB Audio
      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Andre Marianovich
      Posted by Andre Marianovich
      on Aug 13 2012 06:13 AM
      Intellectual975 points

      Tsuneo Chinzei
      usbdhandler.c

      void
      USB0DeviceIntHandler(void)
      {
          unsigned long ulStatus;

          //
          // Get the controller interrupt status.
          //
          ulStatus = MAP_USBIntStatusControl(USB0_BASE);

                                 // <------- insert from here
          //
          // Start of Frame was received.
          //
          if(ulStatus & USB_INTCTRL_SOF)
          {
              // call our SOF ISR here
          }
                                 // <------- to here
          //
          // Call the internal handler.
          //
          USBDeviceIntHandlerInternal(0, ulStatus);
      }

      One thing I don't understand yet. If the USB0DeviceIntHandler would call the SOF ISR everytime the ulStatus & USB_INTCTRL_SOF condition is true, why should the HandleEndpoints() routine explicitely raise a flag to start the tuning process?

      This is my current HandleEndpoints() routine. Beside the thing about the SOF ISR flag, this should work, shouldn't it?

      static void
      HandleEndpoints(void *pvInstance, unsigned long ulStatus)
      {
          unsigned long ulEPStatus;
          tAudioInstance *psInst;
          unsigned char *pucData;
          const tUSBDAudioDevice *psDevice;

          ASSERT(pvInstance != 0);

          //
          // Create the instance pointer.
          //
          psDevice = (const tUSBDAudioDevice *)pvInstance;

          //
          // Make a copy of this pointer for ease of use later in this function.
          //
          psInst = psDevice->psPrivateData;

          //
          // Make sure this was for the isochronous IN endpoint.
          //
          if(ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucINEndpoint)))
          {
              // fill endpoint with next packet
              //
              // Configure and enable DMA for the OUT transfer.
              //
              MAP_uDMAChannelTransferSet(psInst->ucINDMA, UDMA_MODE_BASIC,
                                         MyPufferPlaceholder,
                                         (void *)USBFIFOAddrGet(USB0_BASE,psInst->ucINEndpoint),
                                         ulSize >> 2);

              //
              // Start the DMA transfer.
              //
              MAP_uDMAChannelEnable(psInst->ucINDMA);

          }

      }

      Of course I renamed all "...OUT..." to "...IN...". Maybe I should ask for UDMA_MODE_STOP? And what about the endpoint status request in the original HandlEnpoints()?

      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    1234
    TI E2E™ Community
    • Support Forums
    • Blogs
    • Videos
    • Groups
    • Site Support & Feedback
    • Settings
    TI E2E™ Community Groups
    • TI University Program
    • Make the Switch
    • Microcontroller Projects
    • Motor Drive & Control
    Other Communities
    • Deyisupport
    • Designsomething.org
    • beagleboard.org
    • TI on Element 14
    • TI on TechXchangeSM
    Other Technical & Support Resources
    • WEBENCH® Design Center
    • Product Information Centers
    • Technical Documents
    • TI Design Network
    • TI Technical Articles
    • TI Training

    All content and materials on this site are provided "as is". TI and its respective suppliers and providers of content make no representations about the suitability of these materials for any purpose and disclaim all warranties and conditions with regard to these materials, including but not limited to all implied warranties and conditions of merchantability, fitness for a particular purpose, title and non-infringement of any third party intellectual property right. TI and its respective suppliers and providers of content make no representations about the suitability of these materials for any purpose and disclaim all warranties and conditions with respect to these materials. No license, either express or implied, by estoppel or otherwise, is granted by TI. Use of the information on this site may require a license from a third party, or a license from TI.

    Content on this site may contain or be subject to specific guidelines or limitations on use. All postings and use of the content on this site are subject to the Terms of Use of the site; third parties using this content agree to abide by any limitations or guidelines and to comply with the Terms of Use of this site. TI, its suppliers and providers of content reserve the right to make corrections, deletions, modifications, enhancements, improvements and other changes to the content and materials, its products, programs and services at any time or to move or discontinue any content, products, programs, or services without notice.

    Follow Us Texas Instruments on Facebook Texas Instruments on Twitter Texas Instruments on LinkedIn Texas Instruments on Google+
    TI Worldwide | Contact Us | my.TI Login | Site Map | Corporate Citizenship | mobile m.ti.com (Mobile Version)

    TI is a global semiconductor design and manufacturing company. Innovate with 100,000+ analog ICs and
    embedded processors, along with software, tools and the industry’s largest sales/support staff.

    © Copyright 1995-2013 Texas Instruments Incorporated. All rights reserved.
    Trademarks | Privacy Policy | Terms of Use