Hi
if studied the keil usb-audio example: and there are two things I didn't understand. Maybe someone of you could give me some hints / explanations to these two code lines
DataIn &= B_S - 1; /* Adjust Data In Index */ if (((DataIn - DataOut) & (B_S - 1)) == (B_S/2)) { DataRun = 1; /* Data Stream running */ }
B_S = 1024 | DataIn shows the received data of the B_S pkt. But what means "adjust datain index? and why is the stream only running if the data pkt count size is B_S/2?
void USB_EndPoint3 (uint32_t event) { #if USB_DMA USB_DMA_DESCRIPTOR DD; if (event & USB_EVT_OUT_DMA_EOT) { /* End of Transfer */ if (USB_DMA_BufAdr(0x03) != ((uint32_t)DataBuf + 2*DataIn)) { /* Data Available */ DataIn += P_C*P_S; /* Update Data In Index */ DataIn &= B_S - 1; /* Adjust Data In Index */ if (((DataIn - DataOut) & (B_S - 1)) == (B_S/2)) { DataRun = 1; /* Data Stream running */ } } else { /* No Data */ DataRun = 0; /* Data Stream not running */ DataOut = DataIn; /* Initialize Data Indexes */ } } if (event & (USB_EVT_OUT_DMA_EOT) | (USB_EVT_OUT_DMA_NDR)) { /* End of Transfer or New Descriptor Request */ DD.BufAdr = (uint32_t)DataBuf + 2*DataIn; /* DMA Buffer Address */ DD.BufLen = P_C; /* DMA Packet Count */ DD.MaxSize = 0; /* Must be 0 for Iso Transfer */ DD.InfoAdr = (uint32_t)InfoBuf; /* Packet Info Buffer Address */ DD.Cfg.Val = 0; /* Initial DMA Configuration */ DD.Cfg.Type.IsoEP = 1; /* Iso Endpoint */ USB_DMA_Setup (0x03, &DD); /* Setup DMA */ USB_DMA_Enable(0x03); /* Enable DMA */ } #else event = event; #endif }
best regards Jan
When you have a ring buffer that is 2^n large, you can perform some tricks.
The "normal" way to step the insertion index for a ring buffer is:
buffer[in] = data; if (++in >= BUF_SIZE) in = 0;
or maybe
buffer[in] = data; in = ++in % BUF_SIZE;
But as a special case, when BUF_SIZE is 2^n, you can do:
buffer[in++ & (BUF_SIZE-1)] = data;
Most probably, the USB code is letting the read and write indices just tick up and instead perform an and operation with (BUF_SIZE-1) whenever they need to index into the buffer.
This would mean that:
DataIn &= B_S - 1; /* Adjust Data In Index */
is just a way to normalize DataIn as 0 <= DataIn < B_S before the code compares the input index with the output index.
The second line probably just waits until you have buffered a bit of code before starting the transmission. Then you have 50% of the buffer size as safety margin to handle jitter.
Think about normal CD music. The sample rate is 44100Hz. So the CD player will emit the data at this speed on a coaxial or optical output. But what happens if the DAC in the amplifier runs at a slightly different frequency? If the amplifier waits 50% of a buffer size before starting to play the music, it has 50% margin to accept more data in case the CD is slightly faster. And it has 50% buffer margin to play from the buffer in case the CD is slightly slower.
Of course, I might be wrong about the 50% issue, since I haven't seen the full code. if (((DataIn - DataOut) & (B_S - 1)) == (B_S/2)) { DataRun = 1; /* Data Stream running */ }
thanks Per for your explanations. Now I understand the whole Keil example programm.
Do you know which steps I have to include to use double buffer algorithm instead of ring-buffer algorithm?
ok if I'm right, using double buffer is according to the performance very good, but you have only two 64byte buffers per endpoint. If the whole packet involves more than 64byte data, you have to use a ring-buffer, too.
If a whole usb-packet contain 500bytes - how big do I have to size the ring buffer?
One small additional question to the audio example from keil
"Since DMA is used for ISO communication, the USB RAM is allocated for DMA purposes. A scatter file, dma.sct, is used in the Keil project file."
I couldn't find the information in the datasheet of the LPC17xx controllers where the usb memory for the endpoints will be allocated. But is it correct, that the DataBuf (located at 0x20080000) is already the buffer in the SRAM not the EP_Buf from the endpoint?
Moreover if I have two different endpoints using dma to tx data from the host to the LPC controller. How do I have to init the DD.BufAdr for both buffers? Because if I will use the same initialization as above, each endpoint will be overwritten by the other endpoint.
> Do you know which steps I have to include to use double buffer algorithm instead of ring-buffer algorithm?
You can't replace the ring buffer with double buffers, because the purpose of buffering doesn't match. On this example, the ring buffer is used for sampling rate adjustment. Double buffer, which is always enabled on bulk and isoc EPs of LPC17xx, is used to separate the buffer access from firmware and SIE.
I'll explain about the sampling rate adjustment on this example.
Keil audio example doesn't define synchronization type on the endpoint descriptor (no synchronization), which is out of the USB audio spec. Audio isoc endpoint should have one of synchronization type, Asynchronous, Synchronous or Adaptive.
usb.h #define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01 usbdesc.c const BYTE USB_ConfigDescriptor[] = { ... /* Endpoint - Standard Descriptor */ AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_OUT(3), /* bEndpointAddress */ USB_ENDPOINT_TYPE_ISOCHRONOUS, /* bmAttributes */ <--------- should add synchronization type WBVAL(64), /* wMaxPacketSize */ 0x01, /* bInterval */ 0x00, /* bRefresh */ 0x00, /* bSynchAddress */
No synchronization isoc EP is handled as a Synchronous EP on Windows audio class driver. For Synchronous OUT EP, the data is provided to the EP, sampled by bus clock. Usually bus clock derives from a crystal on PC, whose frequency slightly differs from the crystal on the device. Therefore, re-sample is required to play back the data on the device DAC.
Audio source ---(re-sample)---> USB bus clock ---(re-sample)---> Device local clock
On this example, this re-sample is done in TIMER0_IRQHandler() on the DataBuf[]. This process adjust the sampling rate by skipping a sample, or by re-using the same sample again, according to the difference of sampling rate. It is tuned by increment of read-out index, DataOut, as follows.
usbmain.c void TIMER0_IRQHandler(void) { long val; uint32_t cnt; if (DataRun) { /* Data Stream is running */ val = DataBuf[DataOut]; /* Get Audio Sample */ cnt = (DataIn - DataOut) & (B_S - 1); /* Buffer Data Count */ if (cnt == (B_S - P_C*P_S)) { /* Too much Data in Buffer */ DataOut++; /* Skip one Sample */ } if (cnt > (P_C*P_S)) { /* Still enough Data in Buffer */ DataOut++; /* Update Data Out Index */ } DataOut &= B_S - 1; /* Adjust Buffer Out Index */ ...
The size of ring buffer is set to 4 times greater than the packet (or DMA chunk)
usbaudio.h /* Audio Definitions */ #define DATA_FREQ 32000 /* Audio Data Frequency */ #define P_S 32 /* Packet Size */ #if USB_DMA #define P_C 4 /* Packet Count */ #else #define P_C 1 /* Packet Count */ #endif #define B_S (8*P_C*P_S) /* Buffer Size */ // <---- in bytes. Count in 2 bytes (16bits) / sample
As you see in above posts, the firmware pre-buffers up to the half of the DataBuf[]. TIMER0_IRQHandler() tunes the consumption of data samples at the threashold of 1/4 or 3/4 of the buffer size.
This process is simple, but the sound quality is not so good. Better way is tuning the DAC sampling rate so that it synchronize to the bus clock.
> I couldn't find the information in the datasheet of the LPC17xx controllers where the usb memory for the endpoints will be allocated.
EP_RAM, the 4K SRAM dedicated for USB. See 11.6.3 Endpoint RAM (EP_RAM) on the LPC17xx User manual > But is it correct, that the DataBuf (located at 0x20080000) is already the buffer in the SRAM not the EP_Buf from the endpoint?
11.6.5 DMA engine and bus master interface When enabled for an endpoint, the DMA Engine transfers data between RAM on the AHB bus and the endpoint's buffer in EP_RAM.
> Moreover if I have two different endpoints using dma to tx data from the host to the LPC controller. How do I have to init the DD.BufAdr for both buffers?
Each endpoint has an independent DMA Descriptors linked list. See Fig 30. UDCA Head register and DMA Descriptors
Tsuneo