How do I set the usb audio code up for usb asynchronous transfers.
Do I just have to change bmAttributes
USB_ENDPOINT_TYPE_ISOCHRONOUS,
to
USB_ENDPOINT_SYNC_ASYNCHRONOUS
or is it more complexed than that.
Unsure about LSB but is this close or not
#if USB_SOF_EVENT void USB_SOF_Event (void) { #if USB_DMA == 1 >Calculate this value, Write the LSB 3 bytes of this calculated value (LSB first) into the IN 3 EP, using USB_WriteEP() if (USB_WriteEP(0x83,((current_timer_count - last_timer_count) << 14) / pclk/DATA_FREQ - 1; > - - Save current_timer_count into last_timer_count current_timer_count = last_timer_count LPC_USB->USBDMARSet = 1 << EPAdr(0x83);
I put timer counter in main.c like this
void TIMER1_IRQHandler(void) { if TIMER1_IRQHandler > 0 current_timer_count = TIMER1 else if TIMER1_IRQHandler < 1000 TIMER1 = last_timer_count LPC_TIM1->IR = 1; /* Clear Interrupt Flag */ LPC_SC->PCONP |= 1 << 2; // Power on Timer' LPC_TIM1->MR0 = 1000; // 1us LPC_TIM1->MCR = 3; /* TCO Interrupt and Reset on MR0 */ LPC_TIM1->TCR = 1; /* TC0 Enable */ NVIC_EnableIRQ(TIMER0_IRQn);
In above post, I've missed to pick up this mods to the streaming interface descriptor.
/* Interface 1, Alternate Setting 1, Audio Streaming - Operational */ USB_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ 0x01, /* bInterfaceNumber */ 0x01, /* bAlternateSetting */ 0x01, /* bNumEndpoints */ <----- 0x02 (plus one, for the feedback EP) USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ 0x00, /* iInterface */
>> - Run another timer on PCLK in free run > Is this what you mean by free run, just leaving it blank? What is this (VPB_CLOCK) you talk of.
Just enable the Timer1 on the default setting. No interrupt is required for the Timer1
current_timer_count is a readout of Timer1 register (T1TC), at the entry of USB_SOF_Event() callback. It represents the SOF timing measured on the PCLK, which is also the master sampling clock for DAC. Unfortunately, USB engines on LPC family don't have hardware SOF trigger to Timers. As a workaround, the timer value at the SOF timing is captured by the firmware at SOF (FRAME) interrupt.
> if (USB_WriteEP(0x83,((current_timer_count - last_timer_count) << 14) / pclk/DATA_FREQ - 1;
"- 1" is not required. The example sets up Timer0 match register with "VPB_CLOCK/DATA_FREQ - 1"
demo.c int main(void) { ... /* Setup Timer Counter 0: Periodic Timer with Interrupt at DATA_FREQ Rate */ T0MR0 = VPB_CLOCK/DATA_FREQ - 1; /* TC0 Match Value 0 */
It's because the full-count of the timer is less than the total count by one For the calculation of feedback value, the total count (VPB_CLOCK/DATA_FREQ) is applied. Also, round bracket is recommended around (pclk/DATA_FREQ), to control the calculation order.
Tsuneo
Ah, the second parameter of USB_WriteEP() takes a buffer, not a value.
uint32_t feedback_value; feedback_value = ((current_timer_count - last_timer_count) << 14) / (VPB_CLOCK/DATA_FREQ); USB_WriteEP( 0x83, &feedback_value, 3 );
>current_timer_count - last_timer_count
You have not explain why you have wrote this, I not sure how to read Timer and the examples don't have any values like the above, so if you could explain more it would help. Was the main.c I wrote correct of wrong?
On the startup section of the main(), just enable Timer1 on the default setting.
demo.c /* Main Program */ int main (void) { ... ... /* Setup Timer Counter 0 Interrupt */ VICVectAddr4 = (unsigned long)tc0_isr; /* TC0 Interrupt -> Vector 4 */ VICVectCntl4 = 0x02; /* TC0 Interrupt -> Priority 2 */ VICIntEnable = 1 << 4; /* Enable TC0 Interrupt */ /* Setup Timer1 for rate feedback */ T1TCR = 1; /* TC1 Enable */ lcd_init(); ...
Here is mods for USB_SOF_Event(). For rate feedback, the extra code (in red) is inserted
usbuser.c /* * USB Start of Frame Event Callback * Called automatically on USB Start of Frame Event */ DWORD last_timer_count; #if USB_SOF_EVENT void USB_SOF_Event (void) { DWORD current_timer_count; DWORD feedback_value; // feedback current_timer_count = T1TC; // capture current SOF timing on the Timer1 if ( USB_AltSetting[1] == 1 ) { // When interface 1 / alt 1 is enabled, // calculate master/SOF frequency ratio in 10.10 (10.14) format feedback_value = ((current_timer_count - last_timer_count) << 14) / (VPB_CLOCK/DATA_FREQ); // and send it to the feedback IN EP USB_WriteEP( 0x83, (BYTE*)&feedback_value, 3 ); } last_timer_count = current_timer_count; // update the last SOF timing #if USB_DMA == 0 ...
> current_timer_count - last_timer_count
This subtraction calculates SOF interval, on the tick of PCLK
My friend come down and we test with his board, but the code is different.
So we make modifaction to his code, it's working but I am not sure if I have done it yet.
Was I supposed to change the Timer to 1 here
/* * Timer Counter 0 Interrupt Service Routine * executed each 31.25us (32kHz frequency) */ void TIMER1_IRQHandler(void){ 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 */
Unsure about if current_timer_count = LPC_TIM1->TCR timer control register is right or not? or should it be LPC_TIM1->CCR the capture control register?
uint32_t last_timer_count; #if USB_SOF_EVENT void USB_SOF_Event (void) { uint32_t current_timer_count; uint32_t feedback_value; uint32_t pclk; // feedback current_timer_count = LPC_TIM1->TCR; // capture current SOF timing on the Timer1 if ( USB_AltSetting[1] == 1 ) { // When interface 1 / alt 1 is enabled, // calculate master/SOF frequency ratio in 10.10 (10.14) format feedback_value = ((current_timer_count - last_timer_count) << 14) / (pclk/DATA_FREQ); // and send it to the feedback IN EP USB_WriteEP( 0x83, (uint8_t *)&feedback_value, 3 ); } last_timer_count = current_timer_count; // update the last SOF timing #if USB_DMA == 0
LPC_TIM1->MR0 = pclk/DATA_FREQ; /* TC0 Match Value 0 */ LPC_TIM1->MCR = 3; /* TCO Interrupt and Reset on MR0 */ LPC_TIM1->TCR = 1; /* TC0 Enable */ NVIC_EnableIRQ(TIMER1_IRQn); /* Setup Timer1 for rate feedback */ //T1TCR = 1; /* TC1 Enable */ LPC_SC->PCONP |= 1 << 2; // Power on Timer' //LPC_TIM1->TCR = 1;
Ok thanks for any help as I say code is different to my board.
We can not figure out how to increase the isoch tranfer size it's still 640, have to do all manaul with his board no easy configwizard
/* 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 */
/* USB RAM Definitions */ #define USB_RAM_ADR 0x20080000 /* USB RAM Start Address */ #define USB_RAM_SZ 0x00004000 /* USB RAM Size (4kB) */ /* DMA Endpoint Descriptors */ #define DD_NISO_CNT 16 /* Non-Iso EP DMA Descr. Count (max. 32) */ #define DD_ISO_CNT 8 /* Iso EP DMA Descriptor Count (max. 32) */ #define DD_NISO_SZ (DD_NISO_CNT * 16) /* Non-Iso DMA Descr. Size */ #define DD_ISO_SZ (DD_ISO_CNT * 20) /* Iso DMA Descriptor Size */ #define DD_NISO_ADR (USB_RAM_ADR + 128) /* Non-Iso DMA Descr. Address */ #define DD_ISO_ADR (DD_NISO_ADR + DD_NISO_SZ) /* Iso DMA Descr. Address */ #define DD_SZ (128 + DD_NISO_SZ + DD_ISO_SZ) /* Descr. Size */ /* DMA Buffer Memory Definitions */ #define DMA_BUF_ADR (USB_RAM_ADR + DD_SZ) /* DMA Buffer Start Address */ #define DMA_BUF_SZ (USB_RAM_SZ - DD_SZ) /* DMA Buffer Size */
#if USB_DMA #pragma arm section zidata = "USB_RAM" uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */ uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */ uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */ #pragma arm section zidata uint32_t udca[USB_EP_NUM]; /* UDCA saved values */ uint32_t DDMemMap[2]; /* DMA Descriptor Memory Usage */ #endif
#if USB_DMA uint32_t *InfoBuf = (uint32_t *)(DMA_BUF_ADR); short *DataBuf = (short *)(DMA_BUF_ADR + 4*P_C); #else uint32_t InfoBuf[P_C]; short DataBuf[B_S]; /* Data Buffer */ #endif
Not sure where to make the modification here, am I limited to a 640 transfer? ok thanks.
I check and it was TCR timer control register I needed so the above posted post code above was right then?
> Was I supposed to change the Timer to 1 here
Leave the Timer0 code alone. You don't need to touch to Timer0 code at all.
Timer0 divides PCLK to make sampling frequency for DAC. Timer1 counts SOF interval with SOF interrupt, independently from Timer0.
> We can not figure out how to increase the isoch tranfer size
What do you mean? The example has these features, - 32kHz sampling - single channel (monaural) - 16bits PCM
These feature determines the packet size on each USB frame, and transfer size, after all. Windows binds 10 packets into single "transfer".
Do you want to change sampling frequency, stereo or 24bits?
we have try changing bSubFrameSize 0x03 and WBVAL to 96 and it won't work. I want to send more than 640 bytes, data 3 * 32 = 96 so 10 * 96 = 960 so I would like the isoch tranfer to be 960 instead of 640, is this possible. Only want 16 bit resolution and mono.
thanks.
> Only want 16 bit resolution and mono.
Increase the sampling frequency from 32kHz to 48kHz. And then, you'll get transfer size of 960 bytes.
Set the sampling frequency - tSamFreq field of the Type I Format descriptor - DATA_FREQ macro (demo.h)
As the packet size increases, you have to touch to these parameters, too. - wMaxPacketSize of the endpoint descriptor - P_S macro (demo.h)
usbdesc.c /* Audio Type I Format */ AUDIO_FORMAT_TYPE_I_DESC_SZ(1), /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ AUDIO_FORMAT_TYPE_I, /* bFormatType */ 0x01, /* bNrChannels */ 0x02, /* bSubFrameSize */ 16, /* bBitResolution */ 0x01, /* bSamFreqType */ B3VAL(32000), /* tSamFreq */ // <----- B3VAL(48000) /* Endpoint - Standard Descriptor */ AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_OUT(3), /* bEndpointAddress */ //USB_ENDPOINT_TYPE_ISOCHRONOUS, /* bmAttributes */ USB_ENDPOINT_TYPE_ISOCHRONOUS | USB_ENDPOINT_SYNC_ASYNCHRONOUS, WBVAL(66), /* wMaxPacketSize */ // <----- WBVAL(98) 0x01, /* bInterval */ 0x00, /* bRefresh */ 0x83, /* bSynchAddress */
demo.h /* Audio Definitions */ #define DATA_FREQ 32000 /* Audio Data Frequency */ // <--- 48000 #define P_S 32 /* Packet Size */ // <--- 48
We did what you say and it no work?
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */ AUDIO_FORMAT_TYPE_I, /* bFormatType */ 0x01, /* bNrChannels */ 0x02, /* bSubFrameSize */ 16, /* bBitResolution */ 0x01, /* bSamFreqType */ B3VAL(48000), /* tSamFreq */ /* Endpoint - Standard Descriptor */ AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_OUT(3), /* bEndpointAddress */ //USB_ENDPOINT_TYPE_ISOCHRONOUS, /* bmAttributes */ 0x05, WBVAL(98), /* wMaxPacketSize */ 0x01, /* bInterval */ 0x00, /* bRefresh */ 0x83, /* bSynchAddress */
/* Audio Definitions */ #define DATA_FREQ 48000 /* Audio Data Frequency */ #define P_S 48 /* 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 */
> We did what you say and it no work?
Hmmm.. I tested it on my MCB2300, and surely the board doesn't play any sound. But on a Hardware USB analyzer, everything works fine. - The device enumerates successfully - 96 bytes isoc OUT transactions on every frame, - 3 bytes feedback, which holds around 96 in 10.14 format (ex. 0x0C0041) on every 8 frames
Maybe the buffering and DAC part of the code require more retouch. I'm busy today, so it takes for a while to look into the problem. Meanwhile, check these code on your side, too.
we seem to be getting some unsuccessful tranfers as well, do you know how to fix.
URB Isoch Transfer failed Device Object 0000008e Driver Object usbccgp URB Function URB_FUNCTION_ISOCH_TRANSFER URB Status USBD_STATUS_ISOCH_REQUEST_FAILED Endpoint 83h 3 In, Isochronous Start Frame 4598208 Number of Packets 1 Error Count 1
Found it.
The buffering part of the code expects the buffer size, B_S (ie. P_S), as a number of power of 2, like 2, 4, 8, 16, 32, 64, ... The code abuses "mask" method to keep the buffer indices (DataIn/DataOut) within the array range. For example,
DataIn += P_S; /* Update Data In Index */ DataIn &= B_S - 1; /* Adjust Data In Index */
As we sets P_S to 48, not a number of power of 2, this method failed. Rewrite the buffering code, so that it accept ANY number.
usbuser.c void USB_SOF_Event (void) { DWORD cnt; ... ... #if USB_DMA == 0 if (USB_ReadEP(0x03, (BYTE *)&DataBuf[DataIn])) { /* Data Available */ DataIn += P_S; /* Update Data In Index */ // DataIn &= B_S - 1; /* Adjust Data In Index */ if ( DataIn >= B_S ) { DataIn -= B_S; } // if (((DataIn - DataOut) & (B_S - 1)) == (B_S/2)) { if ( DataIn >= DataOut ) { cnt = DataIn - DataOut; } else { cnt = B_S + DataIn - DataOut; } if ( cnt == (B_S/2)) { DataRun = 1; /* Data Stream running */ } } else { /* No Data */ DataRun = 0; /* Data Stream not running */ DataOut = DataIn; /* Initialize Data Indexes */ } #endif } #endif void USB_EndPoint3 (DWORD event) { #if USB_DMA USB_DMA_DESCRIPTOR DD; DWORD cnt; if (event & USB_EVT_OUT_DMA_EOT) { /* End of Transfer */ if (USB_DMA_BufAdr(0x03) != ((DWORD)DataBuf + 2*DataIn)) { /* Data Available */ DataIn += P_C*P_S; /* Update Data In Index */ // DataIn &= B_S - 1; /* Adjust Data In Index */ if ( DataIn >= B_S ) { DataIn -= B_S; } // if (((DataIn - DataOut) & (B_S - 1)) == (B_S/2)) { if ( DataIn >= DataOut ) { cnt = DataIn - DataOut; } else { cnt = B_S + DataIn - DataOut; } if ( cnt == (B_S/2)) { DataRun = 1; /* Data Stream running */ } } else { /* No Data */ DataRun = 0; /* Data Stream not running */ DataOut = DataIn; /* Initialize Data Indexes */ } }
demo.c void tc0_isr (void) __irq { long val; DWORD cnt; if (DataRun) { /* Data Stream is running */ val = DataBuf[DataOut]; /* Get Audio Sample */ // cnt = (DataIn - DataOut) & (B_S - 1); /* Buffer Data Count */ if ( DataIn >= DataOut ) { /* Buffer Data Count */ cnt = DataIn - DataOut; } else { cnt = B_S + DataIn - DataOut; } 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 */ if ( DataOut >= B_S ) /* Adjust Buffer Out Index */ DataOut -= B_S;
Ok great it worked, but I still have this problem with the code I am writing. I am writing code much like a reverse effects guitar pedal. But it is usb sound card as well and plays windows sound in reverse and you can plug guitar in and mix with windows sounds. My problem is with the timer0 at the moment anything above 8000khz and I get skipping audio becuase the timer is ticking too fast for the audio I think. Do you have any ideas as to how I can fix the skipping audio at 32khz and above.
Here is the code.
/* * Timer Counter 0 Interrupt Service Routine * executed each 31.25us (32kHz frequency) */ void TIMER0_IRQHandler(void) { long val; uint32_t cnt; unsigned short PlaySample; if (DataRun) { /* Data Stream is running */ val = DataBuf[DataOut]; /* Get Audio Sample */ // cnt = (DataIn - DataOut) & (B_S - 1); /* Buffer Data Count */ if ( DataIn >= DataOut ) { /* Buffer Data Count */ cnt = DataIn - DataOut; } else { cnt = B_S + DataIn - DataOut; } 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; if ( DataOut >= B_S ) /* Adjust Buffer Out Index */ DataOut -= B_S; //if (val < 0) VUM -= val; /* Accumulate Neg Value */ //else VUM += val; /* Accumulate Pos Value */ val *= Volume; /* Apply Volume Level */ val >>= 6; /* Adjust Value */ val += 0x8000; /* Add Bias */ val &= 0xFFFF; /* Mask Value */ } else { val = 0x8000; /* DAC Middle Point */ } if (Mute) { val = 0x8000; /* DAC Middle Point */ } //if (Mute){ //semihost_powerdown();} /* ADAMGR: Inserting my May 30th reversing code here!!! */ { static const unsigned int BufferSize = 10 * 1024 ; static unsigned short Buffer[BufferSize]; static int Index = 0; static int Direction = 1; static int Playback = FALSE; static int ChunkSize = BufferSize; unsigned short ReadSample; /* Default PlaySample to the current sample from USB buffer. */ PlaySample = val; /* Read out the sample from the buffer to be played back */ if (Playback) { PlaySample = Buffer[Index]; } /* Obtain current audio sample from the USB buffer. */ ReadSample = (unsigned short)val; /* Record the sample into the buffer right where a space was freed up from the PlaySample read above */ Buffer[Index] = ReadSample; /* Increment the buffer pointer */ Index += Direction; /* Check to see if the chunk has been filled */ if (Index < 0) { /* Now have a chunk to be played back */ Playback = TRUE; /* Reverse the direction of playback and recording */ Direction *= -1;//Direction; Index = 0; } else if (Index >= ChunkSize) { /* Now have a chunk to be played back */ Playback = TRUE; /* Reverse the direction of playback and recording */ Direction *= -1;//Direction; Index = ChunkSize - 1; } } LPC_DAC->DACR = PlaySample & 0xFFC0; /* Set Speaker Output */ //if ((Tick++ & 0x03FF) == 0) { /* On every 1024th Tick */ //get_potval(); /* Get Potenciometer Value */ if (VolCur == 0x8000) { /* Check for Minimum Level */ Volume = 0; /* No Sound */ } else { Volume = VolCur ;//* PotVal; /* Chained Volume Level */ } //val = VUM >> 20; /* Scale Accumulated Value */ //VUM = 0; /* Clear VUM */ //if (val > 7) val = 7; /* Limit Value */ LPC_TIM0->IR = 1; /* Clear Interrupt Flag */ }