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.
What "usb audio code" ?
Odds are the OP is just another new alias of that ... well, let's call it a "person", who recently so thoroughly expressed their belief that the best way of getting help was to spew verbal abuse here in dozens of sequential postings.
This code, I have looked and looked can't not seem to find much support on this subject, asynchronous transfers.
http://www.keil.com/download/files/lpc2368_usbaudio.zip
Here is a descriptor readout of a typical Async Sink device (Ayre QB9), taken from list.voyage.hk/.../013449.html
Compare it with the example's
lsusb -v Bus 001 Device 004: ID 21b4:0130 Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 1.00 bDeviceClass 0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x21b4 idProduct 0x0130 bcdDevice 1.71 iManufacturer 1 iProduct 2 iSerial 3 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 134 bNumInterfaces 3 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 (Bus Powered) MaxPower 500mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 0 bInterfaceClass 1 Audio bInterfaceSubClass 1 Control Device bInterfaceProtocol 0 iInterface 7 AudioControl Interface Descriptor: bLength 9 bDescriptorType 36 bDescriptorSubtype 1 (HEADER) bcdADC 1.00 wTotalLength 30 bInCollection 1 baInterfaceNr( 0) 1 AudioControl Interface Descriptor: bLength 12 bDescriptorType 36 bDescriptorSubtype 2 (INPUT_TERMINAL) bTerminalID 1 wTerminalType 0x0101 USB Streaming bAssocTerminal 0 bNrChannels 2 wChannelConfig 0x0003 Left Front (L) Right Front (R) iChannelNames 0 iTerminal 0 AudioControl Interface Descriptor: bLength 9 bDescriptorType 36 bDescriptorSubtype 3 (OUTPUT_TERMINAL) bTerminalID 6 wTerminalType 0x0301 Speaker bAssocTerminal 0 bSourceID 1 iTerminal 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 0 bInterfaceClass 1 Audio bInterfaceSubClass 2 Streaming bInterfaceProtocol 0 iInterface 9 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 1 bNumEndpoints 2 bInterfaceClass 1 Audio bInterfaceSubClass 2 Streaming bInterfaceProtocol 0 iInterface 8 AudioStreaming Interface Descriptor: bLength 7 bDescriptorType 36 bDescriptorSubtype 1 (AS_GENERAL) bTerminalLink 1 bDelay 1 frames wFormatTag 1 PCM AudioStreaming Interface Descriptor: bLength 20 bDescriptorType 36 bDescriptorSubtype 2 (FORMAT_TYPE) bFormatType 1 (FORMAT_TYPE_I) bNrChannels 2 bSubframeSize 3 bBitResolution 24 bSamFreqType 4 Discrete tSamFreq[ 0] 44100 tSamFreq[ 1] 48000 tSamFreq[ 2] 88200 tSamFreq[ 3] 96000 Endpoint Descriptor: bLength 9 bDescriptorType 5 bEndpointAddress 0x01 EP 1 OUT bmAttributes 5 Transfer Type Isochronous Synch Type Asynchronous Usage Type Data wMaxPacketSize 0x0246 1x 582 bytes bInterval 1 bRefresh 0 bSynchAddress 129 AudioControl Endpoint Descriptor: bLength 7 bDescriptorType 37 bDescriptorSubtype 1 (EP_GENERAL) bmAttributes 0x01 Sampling Frequency bLockDelayUnits 2 Decoded PCM samples wLockDelay 0 Decoded PCM samples Endpoint Descriptor: bLength 9 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 1 Transfer Type Isochronous Synch Type None Usage Type Data wMaxPacketSize 0x0003 1x 3 bytes bInterval 1 bRefresh 1 bSynchAddress 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 2 bAlternateSetting 0 bNumEndpoints 0 bInterfaceClass 254 Application Specific Interface bInterfaceSubClass 1 Device Firmware Update bInterfaceProtocol 0 iInterface 14 Device Firmware Upgrade Interface Descriptor: bLength 7 bDescriptorType 33 bmAttributes 7 Will Not Detach Manifestation Tolerant Upload Supported Download Supported wDetachTimeout 250 milliseconds wTransferSize 64 bytes
Tsuneo
The terms, Asynchronous / Synchronous / Adaptive on USB audio class mean the synchronization type. It suggests the place where the original sampling clock sits on.
- Asynchronous: the sampling clock is generated on the device side. - Synchronous: the clock derives from USB SOF by PLL - Adaptive: the clock is generated on the host side.
For Asynchronous Sink (output) device, like a speaker, the device has to notify to host on its sampling clock, to synchronize the host. For this purpose, a feedback endpoint (isoc IN) is associated to the isoc OUT endpoint of the audio stream, like above QB9
For Asynchronous Source (input) device, like a microphone, The sampling rate on the device is shown as the number of the samples in each isoc packet on the audio stream. The number is relative to the SOF frequency. Therefore, any feedback endpoint is not required.
I have changed it to
* 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(64), /* wMaxPacketSize */ 0x01, /* bInterval */ 0x00, /* bRefresh */ 0x00, /* bSynchAddress */
and it worked, I don't understand who ever wrote the bmAttributes in Endpoint Descriptor expected it to be wrote?
/* bmAttributes in Endpoint Descriptor */ #define USB_ENDPOINT_TYPE_MASK 0x03 #define USB_ENDPOINT_TYPE_CONTROL 0x00 #define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01 #define USB_ENDPOINT_TYPE_BULK 0x02 #define USB_ENDPOINT_TYPE_INTERRUPT 0x03 #define USB_ENDPOINT_SYNC_MASK 0x0C #define USB_ENDPOINT_SYNC_NO_SYNCHRONIZATION 0x00 #define USB_ENDPOINT_SYNC_ASYNCHRONOUS 0x04 #define USB_ENDPOINT_SYNC_ADAPTIVE 0x08 #define USB_ENDPOINT_SYNC_SYNCHRONOUS 0x0C #define USB_ENDPOINT_USAGE_MASK 0x30 #define USB_ENDPOINT_USAGE_DATA 0x00 #define USB_ENDPOINT_USAGE_FEEDBACK 0x10 #define USB_ENDPOINT_USAGE_IMPLICIT_FEEDBACK 0x20 #define USB_ENDPOINT_USAGE_RESERVED 0x30
of the keil code working
0 bLength 1 09h 1 bDescriptorType 1 05h Endpoint 2 bEndpointAddress 1 03h 3 Out 3 bmAttributes 1 05h Isochronous, Asynchronous, Data 1..0: Transfer Type ......01 Isochronous 3..2: Sync Type ....01.. Asynchronous 5..4: Usage Type ..00.... Data 7..6: Reserved 00...... 4 wMaxPacketSize 2 0040h 64 bytes 6 bInterval 1 01h 1 ms 7 bRefresh 1 00h 8 bSynchAddress 1 00h
Because of stuff like the above, I can not figure out how to change the isoch transfer size,bytes buffer, bytes data. I can not understand the code of the person who wrote it.
> I have changed it to ... and it worked,
Did you add an isoc IN endpoint for feedback? Windows and MacOSX ignore Asynchronous bits on the bmAttributes, if the streaming OUT endpoint isn't associated with a feedback IN endpoint. Without any feedback EP, these audio drivers "work" the device as a Synchronous one.
Here is an outline of the feedback endpoint of Async Sink.
Host side <---- | ----> Device side Counter +---------- synch IN EP <------ shift to <-------- gated by SOF | 10.10 format ^ | | | Base clock Feedback | | Divider | | V V Audio Source ===(rate conversion)===> isoc OUT EP =============> buffer ==========> DAC ===> Speaker
For the details, refer to this chapter on the USB2.0 spec (usb_20.pdf, in www.usb.org/.../usb_20_071411.zip ) 5.12.4.2 Feedback > I can not figure out how to change the isoch transfer size,bytes buffer, bytes data.
The format of the streaming packet is defined on the descriptor on your device. Above QB-9 declares these descriptors on the streaming interface.
AudioStreaming Interface Descriptor: bLength 7 bDescriptorType 36 bDescriptorSubtype 1 (AS_GENERAL) bTerminalLink 1 bDelay 1 frames wFormatTag 1 PCM AudioStreaming Interface Descriptor: bLength 20 bDescriptorType 36 bDescriptorSubtype 2 (FORMAT_TYPE) bFormatType 1 (FORMAT_TYPE_I) bNrChannels 2 bSubframeSize 3 bBitResolution 24 bSamFreqType 4 Discrete tSamFreq[ 0] 44100 tSamFreq[ 1] 48000 tSamFreq[ 2] 88200 tSamFreq[ 3] 96000
This descriptors declare, - Type I PCM (wFormatTag / bFormatType) - 2ch (L/R) (bNrChannels) - 3 bytes, 24bits (bSubframeSize / bBitResolution) - 44.1, 48, 88.2, 96 kHz (tSamFreq[])
For 96 kHz, every isoc OUT packet has the size, 3 bytes * 2 ch * 96 samples = 576 bytes, primarily. However, the sampling frequency of the device is not exactly 96 times of the SOF frequency. The crystal which sources 96 kHz has some tolerance. Also, USB spec defines the tolerance of Frame interval as 1.000 ms +/-500 ns
Therefore, actual packet size may have plus/minus by one sample 3 bytes * 2 ch * (96 + 1) samples = 582 bytes In this reason, above QB-9 example sets wMaxPacketSize to 582
>Did you add an isoc IN endpoint for feedback?
I believe so yes?
/* Audio Input Terminal */ AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */ 0x01, /* bTerminalID */ WBVAL(AUDIO_TERMINAL_USB_STREAMING), /* wTerminalType */ 0x00, /* bAssocTerminal */ 0x01, /* bNrChannels */ WBVAL(AUDIO_CHANNEL_M), /* wChannelConfig */ 0x00, /* iChannelNames */ 0x00, /* iTerminal */ /* Audio Feature Unit */ AUDIO_FEATURE_UNIT_DESC_SZ(1,1), /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */ 0x02, /* bUnitID */ 0x01, /* bSourceID */ 0x01, /* bControlSize */ AUDIO_CONTROL_MUTE | AUDIO_CONTROL_VOLUME, /* bmaControls(0) */ 0x00, /* bmaControls(1) */ 0x00, /* iTerminal */ /* Audio Output Terminal */ AUDIO_OUTPUT_TERMINAL_DESC_SIZE, /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */ 0x03, /* bTerminalID */ WBVAL(AUDIO_TERMINAL_SPEAKER), /* wTerminalType */ 0x00, /* bAssocTerminal */ 0x02, /* bSourceID */ 0x00, /* iTerminal */ /* Interface 1, Alternate Setting 0, Audio Streaming - Zero Bandwith */ USB_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ 0x01, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ 0x00, /* bNumEndpoints */ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ 0x00, /* iInterface */ /* Interface 1, Alternate Setting 1, Audio Streaming - Operational */ USB_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ 0x01, /* bInterfaceNumber */ 0x01, /* bAlternateSetting */ 0x01, /* bNumEndpoints */ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */ AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ 0x00, /* iInterface */ /* Audio Streaming Interface */ AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */ 0x01, /* bTerminalLink */ 0x01, /* bDelay */ WBVAL(AUDIO_FORMAT_PCM), /* wFormatTag */ /* 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 */ /* 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(64), /* wMaxPacketSize */ 0x04, /* bInterval */ 0x00, /* bRefresh */ 0x00, /* bSynchAddress */
I still am not sure how to change the isoch transfer with the keil code.
But I think it is 10 * 64 = 640. I did change WBVAL(64), to WBVAL(128), but it is still only 640
Interface 1 / Alt 1 Audio, 1 pipe Endpoint 03h 3 Out, Isochronous 128 bytes Endpoint 03h 3 Out, Isochronous Bytes to Transfer 280h (640)
>> Did you add an isoc IN endpoint for feedback? > I believe so yes?
Not yet. Add a descriptor of the feedback EP, Modify the standard / class-specific EP descriptors. Your Type I descriptor declares PCM of 2 bytes (16bits), single channel, 32kHz sampling. Therefore, wMaxPacketSize of the isoc OUT EP is 66 bytes. 2 bytes * 1 ch * (32 + 1) samples = 66 bytes
/* 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, // <----- USB_ENDPOINT_TYPE_ISOCHRONOUS | USB_ENDPOINT_SYNC_ASYNCHRONOUS WBVAL(64), /* wMaxPacketSize */ // <----- WBVAL(66) 0x04, /* bInterval */ // <----- 0x01 (every 1ms) 0x00, /* bRefresh */ 0x00, /* bSynchAddress */ // <----- 0x83 (IN 3) /* Endpoint - Audio Streaming */ AUDIO_STREAMING_ENDPOINT_DESC_SIZE, /* bLength */ AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_ENDPOINT_GENERAL, /* bDescriptor */ 0x00, /* bmAttributes */ 0x00, /* bLockDelayUnits */ // <----- 0x02 (Decoded PCM samples) WBVAL(0x0000), /* wLockDelay */ /* Endpoint - Standard Descriptor */ // <-------- additional feedback EP AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_IN(3), /* bEndpointAddress */ USB_ENDPOINT_TYPE_ISOCHRONOUS, /* bmAttributes */ WBVAL(3), /* wMaxPacketSize */ 0x01, /* bInterval */ 0x01, /* bRefresh, every 2ms */ 0x00, /* bSynchAddress */ /* Terminator */ 0 /* bLength */ };
With this addition of the feedback EP, the wTotalLength on the Config descriptor is modified, as follows
/* Configuration 1 */ USB_CONFIGUARTION_DESC_SIZE, /* bLength */ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */ WBVAL( /* wTotalLength */ USB_CONFIGUARTION_DESC_SIZE + USB_INTERFACE_DESC_SIZE + AUDIO_CONTROL_INTERFACE_DESC_SZ(1) + AUDIO_INPUT_TERMINAL_DESC_SIZE + AUDIO_FEATURE_UNIT_DESC_SZ(1,1) + AUDIO_OUTPUT_TERMINAL_DESC_SIZE + USB_INTERFACE_DESC_SIZE + USB_INTERFACE_DESC_SIZE + AUDIO_STREAMING_INTERFACE_DESC_SIZE + AUDIO_FORMAT_TYPE_I_DESC_SZ(1) + AUDIO_STANDARD_ENDPOINT_DESC_SIZE + AUDIO_STREAMING_ENDPOINT_DESC_SIZE + // <---------- AUDIO_STANDARD_ENDPOINT_DESC_SIZE // <---------- ),
The feedback endpoint is supplied with the frequency ratio (master sampling / SOF frequency) in 10.10 fixed-point format on 3 bytes (left-justified, ie. 10.14 format).
In this example, 32kHz master clock derives from PCLK, divided by (VPB_CLOCK/DATA_FREQ) at Timer0. To generate feedback value, - Run another timer on PCLK in free run - At USB_SOF_Event() (usbuser.c), - - read this timer count. - - Calculate this value ((current_timer_count - last_timer_count) << 14) / (VPB_CLOCK / DATA_FREQ) - - Write the LSB 3 bytes of this calculated value (LSB first) into the IN 3 EP, using USB_WriteEP() - - Save current_timer_count into last_timer_count
>- 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.
void TIMER1_IRQHandler(void) { LPC_TIM1->IR = 1; /* Clear Interrupt Flag */ LPC_SC->PCONP |= 1 << 2; // Power on Timer' LPC_TIM1->MR0 = pclk/DATA_FREQ - 1; /* TC0 Match Value 0 */ LPC_TIM1->MCR = 3; /* TCO Interrupt and Reset on MR0 */ LPC_TIM1->TCR = 1; /* TC0 Enable */ NVIC_EnableIRQ(TIMER0_IRQn); }
>- At USB_SOF_Event() (usbuser.c), > - - read this timer count. > - - Calculate this value
I have been goolge but have not found out how to do this yet, I had a go at it.
uint32_t current_timer_count
uint32_t last_timer_count
#if USB_SOF_EVENT void USB_SOF_Event (void) { #if TIMER1_IRQHandler > 0 current_timer_count = TIMER1 else if current_timer_count = TIMER1 TIMER1 = last_timer_count
#if USB_SOF_EVENT void USB_SOF_Event (void) { #if TIMER1_IRQHandler > 0 current_timer_count = TIMER1 else if #if TIMER1_IRQHandler < 1000 TIMER1 = last_timer_count LPC_TIM1->MR0 = 1000;
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.
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 );