Important update: Arm Announces End of Life Timeline for Mbed. This site will be archived in July 2026. Read the full announcement.
Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
Timing of DAC
Gert van der Knokke wrote:
In the UM10360 manual it says:
Quote:
If either the CNT_ENA or the DBLBUF_ENA bits are 0, any writes to the DACR address will go directly to the DACR register.
But also:
Quote:
Maximum update rate of 1 MHz.
What does this mean ? If I write faster than 1 MHz will the write operation be delayed ?
I am asking this because I experience random but serious timing problems in my fast interrupt routine as soon as I try to write to the DACR register (in the main program) If I simply comment out the DACR write it is fine.
(I know I am proably pushing the mbed to its limits but I can not find an explanation for this behaviour)
I don't know if this will help you but the update rate is effected by the capacitor that you use on the output of dac, on page 583 is says do not exceed 100 pf to get 1us with 1MHZ update rate.. it has me thinking about my problem now tho:)
Quote:
I don't know if this will help you but the update rate is effected by the capacitor that you use on the output of dac, on page 583 is says do not exceed 100 pf to get 1us with 1MHZ update rate.. it has me thinking about my problem now tho:)
I can understand that it will affect the settling time, but does it affect writing time also (such in that it holds up the CPU)?
The interrupt routine must have the highest priority (it is written in 100% assembler) and cannot withstand more than 100 to 200 ns of extra delay. Normally a simple write to a register should not take more than 10 or 20 ns. As the DAC is a simple resistor ladder AFAIK I do not understand what holds up the write.
The main loop is creating samples from a sp0256 (speech chip) emulator and has to emit them at about 10kHz, since that is just speech it is not very critical and sounds already good enough. All the heavy DSP like calculations of the emulator have no effect on the interrupt routine, yet this simple three commands seem to stall it: (I have written this set_DAC routine in assembler as the mbed's own AnalogOut function is not fast enough and also hogs the interrupt(s))
; set DAC register from R0 set_DAC LDR R1,=0x4008C000 ; DACR STR R0,[R1] ; set DAC with supplied value BX LR ; return to caller
From the main loop it is simply called with:
extern "C" void set_DAC(int data); ........ set_DAC(sample_value); ........
If I remove the set_DAC call it is stable, if I remove the STR R0,[R1] in the assembly routine it is also stable so it really looks like the write to the register is the culprit unless it creates some kind of race condition or maybe a hidden interrupt?
Gert van der Knokke wrote:
Quote:
I don't know if this will help you but the update rate is effected by the capacitor that you use on the output of dac, on page 583 is says do not exceed 100 pf to get 1us with 1MHZ update rate.. it has me thinking about my problem now tho:)
I can understand that it will affect the settling time, but does it affect writing time also (such in that it holds up the CPU)?
The interrupt routine must have the highest priority (it is written in 100% assembler) and cannot withstand more than 100 to 200 ns of extra delay. Normally a simple write to a register should not take more than 10 or 20 ns. As the DAC is a simple resistor ladder AFAIK I do not understand what holds up the write.
The main loop is creating samples from a sp0256 (speech chip) emulator and has to emit them at about 10kHz, since that is just speech it is not very critical and sounds already good enough. All the heavy DSP like calculations of the emulator have no effect on the interrupt routine, yet this simple three commands seem to stall it: (I have written this set_DAC routine in assembler as the mbed's own AnalogOut function is not fast enough and also hogs the interrupt(s))
; set DAC register from R0 set_DAC LDR R1,=0x4008C000 ; DACR STR R0,[R1] ; set DAC with supplied value BX LR ; return to caller
From the main loop it is simply called with:
extern "C" void set_DAC(int data); ........ set_DAC(sample_value); ........
If I remove the set_DAC call it is stable, if I remove the STR R0,[R1] in the assmebly routine it is also stable so it really looks like the write to the register is the culprit unless it creates some kind of race condition or maybe a hidden interrupt?
I do not know, I guess is no way to tell without a high class debugger, but I think it will hold up the cpu:) I find it hard to understand that for a 31.25us 78.1 / 2.5 = 31.24us I would need 78.1 pf to get 31.24us and that is DACCR set to 1 which gives a 2.5us when used with 100pf or below??
try again 78.1 * 32000 = 2499200 capacitor
Some further testing resulted in the following:
Any write operation in the region 0x400nnnnn (APB 0/1 peripheral area) results in seemingly random delays.
If I change the 'write to a register' into a 'write to a known memory location' it runs fine. Changing the write location into for example GPREG0 (backup register 0) or a PWM match register it becomes as unstable as writing to the DACR address.
Philips Philips: This could also explain the glitches/pops you notice using the DAC..
Can anyone shed some light on this ?
Gert van der Knokke wrote:
Some further testing resulted in the following:
Any write operation in the region 0x400nnnnn (APB 0/1 peripheral area) results in seemingly random delays.
If I change the 'write to a register' into a 'write to a known memory location' it runs fine. Changing the write location into for example GPREG0 (backup register 0) or a PWM match register it becomes as unstable as writing to the DACR address.
Philips Philips: This could also explain the glitches/pops you notice using the DAC..
Can anyone shed some light on this ?
What can be done then nothing, what do you suggest is done to the http://mbed.org/handbook/USBAudio code, maybe this problem was fixed with lpc1769, maybe you should email nxp I don't think simon and Chris may know a great deal about this or what to do.
myself I think I will post on http://www.vworker.com/ when I get a job because I find it hard to code I am more of a creator of the actual hardware, a hardware designer if you will, that is where the real coding happens, right??? solder and components to pcb, it would be nice if I could code as well tho, but we don't live in a perfect world yet.
Hi Gert,
I suspect the differences in delay you are seeing are due to the writes having to propagate through the bus, from the fast ahb matrix across the ahb to apb bridge and to the peripheral. Not sure if this will be impacted by the DAC pclk, but I suspect the delays are quite bounded.
Btw, the mbed libraries don't block/mess with interrupts for the DAC; it just writes the register, so interested if the original post is still valid or whether the culprit is now the bus/register writes.
The user manual is a bit hefty to navigate on my phone so not sure if there is any more useful info about busses in there, but that would be my hypothesis, with the variance bounded by meeting the worst case frequency domain bridging requirement.
Simon
Hi Simon,
Simon Ford wrote:
I suspect the differences in delay you are seeing are due to the writes having to propagate through the bus, from the fast ahb matrix across the ahb to apb bridge and to the peripheral. Not sure if this will be impacted by the DAC pclk, but I suspect the delays are quite bounded.
As I mentioned, it makes no difference to what device I write on the APB bus it delays the response to the interrupt (not the DAC interrupt, I only have interrupts on two port0 pins depending on what function the mbed should perform. The mbed is emulating in realtime an (EP)ROM and some IO registers for another CPU (8048) and until I started using the DAC the performance was perfect. I had to write the interrupt routine in assembler and redirect the EINT3 to my own routine.
Quote:
Btw, the mbed libraries don't block/mess with interrupts for the DAC; it just writes the register, so interested if the original post is still valid or whether the culprit is now the bus/register writes.
Well since both versions crash, the mbed and my own assembler function that may be true, both may possibly trigger the same delay.
Quote:
The user manual is a bit hefty to navigate on my phone so not sure if there is any more useful info about busses in there, but that would be my hypothesis, with the variance bounded by meeting the worst case frequency domain bridging requirement.
I only could find out about the different internal bus structures, not what speeds they can handle or run at. On the web I found out that writes to different peripherals are buffered and so it is possible to clear an interrupt flag of a device, execute a 'return' and then the return is executed faster than the clear flag is executed and then the interrupt starts all over again because the flag has not yet been cleared :-)
Speaking of flags: since I am bending the EINT3 interrupt vector to my own routine I have a lot of 'clear interrupt flag' options. I now simply clear all GPIO0 flags and return.
I know I am pushing the mbed into strange (FPGA) territory but this could open some nice ideas for the mbed as peripheral or debug tool.
Here is my assembler function so far, (note I commented out the troublesome DACR write and replaced it by a software PWM routine which toggles the P0.26 (the former DAC out) With the software PWM routine toggling P0.26 the system is rock solid.
Pinout for the mbed is as follows:
P0.0 = not WRITE from host CPU (8048) generating falling edge interrupt (pulse width is 1 usec, data is available from the host CPU at rising edge)
P0.1 = not PSEN from host CPU generating falling edge interrupt (pulse width is 1 usec, data must be present at rising edge)
P2.0-P2.5 = A0-A5 host CPU
P0.15-P0.18 = A6-A9 host CPU
P0.23-P0.25 = A11 (A10 is not used) and two bank select pins P10 and P11 from host CPU
P1.30 = P14 from host CPU (it is low when the host wants to write to my emulated IO space, only used for speech function)
P1.31 = T0 which is connected to a testable input on the host CPU (handshake for speech function, not relevant for ROM emulation)
P0.4-P0.11 = D0-D7 host CPU (bidirectional bus)
AREA asm_func, CODE, READONLY ; Export my_asm function location so that C compiler can find it and link EXPORT my_irq EXPORT reset_T0 EXPORT set_DAC IMPORT testrom IMPORT voice_command IMPORT voice_reset IMPORT dummy_DAC ALIGN ; my irq assumes that no other interrupt sources exist ; R0 points to port0 ; R3 is used as general purpose register my_irq LDR R0,=0x2009c000 ; pointer to port0 LDR R3,[R0,#0x14] ; get port0 value TST R3,#0x00000001 ; write IRQ ? BEQ do_write do_read ; R1 points to port1 ; R2 points intially to port2, but contains calculcated address thereafter LDR R1,=0x2009c020 ; pointer to port1 LDR R2,=0x2009c054 ; initial pointer to port2 FIO2PIN0 LDRB R2,[R2] ; get byte value from port2 (destroy pointer to port2 but is not needed anymore) AND R2,#0x0000003F ; save bits for A0-A5 in R2 LDR R3,[R0,#0x14] ; get port0 value AND R3,#0x00078000 ; and of bits for A6-A9 LSR R3,#9 ; shift down ORR R2,R3 ; or bits A6-A9 into R2 LDR R3,[R0,#0x14] ; get port0 value AND R3,#0x03800000 ; and off bits A11, P10 and P11 EOR R3,#0x03000000 ; invert P10 and P11 LSR R3,#13 ; shift to A10,A11 and A12 ORR R2,R3 ; or in the bits as A10, A11 and A12 into R2 ; R2 now contains the complete address for reading or writing LDR R3,=testrom ; start address of our rom LDRB R3,[R3,R2] ; get a byte with calculated offset (destroy pointer to testrom, not needed anymore) LSL R3,#4 ; shift up 4 bits (data bus is P0.4-P0.11) STRH R3,[R0,#0x14] ; output the byte MOV R3,#0x0FF0 ; prepare output mask STRH R3,[R0] ; set port to output, emitting byte on the bus wait_for_psen_high LDR R3,[R0,#0x14] ; get port0 value TST R3, #0x00000002 ; and test psen (P0.1) BEQ wait_for_psen_high ; wait for PSEN to rise again MOV R3,#0x0000 ; prepare input mask STRH R3,[R0] ; set port to input again, releasing the bus B irq_exit ; exit ALIGN ; -------------------------------------------------------------- do_write ; we are going to do a write LDR R1,=0x2009c020 ; pointer to port1 LDR R2,=0x2009c054 ; initial pointer to port2 FIO2PIN0 LDR R3,[R1,#0x14] ; get port1 value TST R3,#0x40000000 ; test if P14 (P1.30) is low BNE irq_exit ; this is not for us LDRB R2,[R2] ; get byte value from port2 (destroy pointer to port2 but is not needed anymore) AND R2,#0x0000003F ; save bits for A0-A5 in R2 LDR R3,[R0,#0x14] ; get port0 value AND R3,#0x00078000 ; and of bits for A6-A9 LSR R3,#9 ; shift down ORR R2,R3 ; or bits A6-A9 into R2 LDR R3,[R0,#0x14] ; get port0 value AND R3,#0x03800000 ; and off bits A11, P10 and P11 EOR R3,#0x03000000 ; invert P10 and P11 LSR R3,#13 ; shift to A10,A11 and A12 ORR R2,R3 ; or in the bits as A10, A11 and A12 into R2 ; R2 now contains the complete address for reading or writing TST R2,#0x00000080 ; test if A7 is high in calculated address BEQ irq_exit ; this is also not for us wait_for_wr_high LDR R3,[R0,#0x14] ; get port0 value TST R3, #0x00000001 ; and test WR (P0.0) BEQ wait_for_wr_high ; loop until WR goes high again AND R3,#0x00000200 ; isolate D5 (reset signal) LDR R0,=voice_reset ; point to voice reset variable (destroys pointer to port0, but is not needed anymore) STR R3,[R0] ; write R3 into the voice_reset variable LDR R3,=voice_command ; point to voice command variable STR R2,[R3] ; store address in variable (only bits 0-6 are used) LDR R3,=0x80000000 ; set mask to P1.31 (T0) STR R3,[R1,#0x18] ; write to port1 FIOSET (signal busy) irq_exit LDR R2,=0x4002808C ; IO0INTCLR LDR R3,=0x7fff8fff ; clear mask STR R3,[R2] ; clear interrupt flag BX LR ; return to caller ALIGN set_DAC LDR R1,=0x2009C017 ; point to FIO0PIN3 STRB R0,[R1] ; store byte in bits 24-31 ; LDR R1,=0x4008C000 ; DACR ; STR R0,[R1] ; set DAC with supplied value (this instruction crashes the rom emulator due to random delays) BX LR ; return to caller ALIGN ; external callable function to drop T0 reset_T0 LDR R1,=0x2009c03c ; point to port1 FIOCLR LDR R0,=0x80000000 ; set mask P1.31 (T0) STR R0,[R1] ; write to FIOCLR (signal done) BX LR ; return to caller ALIGN END
I hope the code makes a bit of sense :-)
Gert van der Knokke wrote:
possible to clear an interrupt flag of a device, execute a 'return' and then the return is executed faster than the clear flag is executed and then the interrupt starts all over again because the flag has not yet been cleared :
So your faking Aout with this code, no sound ouput at the moment?
Could it be the flags in the usb code what causes the clicks and pops because interrupt starts all over again because the flag has not yet been cleared :
http://mbed.org/users/mbed2f/notebook/usbmainc--the-main-file-of-the-usb-reverse-audio-c/
LPC_TIM0->IR = 1; /* Clear Interrupt Flag */
in the http://mbed.org/handbook/USBAudio I don't think is any clearing of flag?
But the click and pops are the same with both code, should I be using a different number to clear the flags or option? Setting Priority to its highest?
What do you suggest to do?
Philips Philips wrote:
So your faking Aout with this code, no sound ouput at the moment?
No, my sound output is a software PWM function at the moment, timed with nops, so I still have (low quality) audio.
#define LIMIT 128 // 8 bit samples #define PWM_DELAY 6 // number of nops after setting the PWM output while (1){ if (((DAC_rptr +1) % DAC_fifo_size) != DAC_wptr) { // emit samples if available d=DAC_fifo[DAC_rptr++]; // get sample value DAC_rptr &= 0xff; // increment and limit pointer set_DAC(0x04); // PWM output high for (t=-LIMIT; t<d; t++) { for (l=0; l<PWM_DELAY; l++) __nop(); // busy wait loop, adjust for sample rate } set_DAC(0x00); // PWM output low for (t=d; t<LIMIT; t++) { for (l=0; l<PWM_DELAY; l++) __nop(); // busy wait loop, adjust for sample rate } } ........... }
Quote:
Could it be the flags in the usb code what causes the clicks and pops because interrupt starts all over again because the flag has not yet been cleared :
The clicks and pops could be the same timing errors which might occur during the writing of the DACR register.
Normally when using the mbed own libraries the clearing of the interrupt flags is already taken care of. In the assembler code 'my_irq' I have to deal with this myself because the overhead of the standard mbed interrupt functions was just too high. The main function of the 'my_irq' above is to provide a byte on a simulated databus within the 1 microsecond pulse width of the host CPU, the audio out function is just a secondary extra.
The speech emulator itself runs fine (as stand alone program), the rom emulator itself runs fine (as standalone program), only when I try to run the speech emulator in the spare cycles of the rom emulator I get bitten by the delays of the DACR writes.
Note from the manual:
Quote:
Split APB bus allows for higher throughput with fewer stalls between the CPU and DMA. A single level of write buffering allows the CPU to continue without waiting for completion of APB writes if the APB was not already busy.
Is there a way to check if the APB is busy ?
Maybe I have a collision between the IO0INTCLR (which is also in APB range) and the DACR write ?
It's problem with the lpc1768 then and not mbed libs, because I have the same problem with the other code which is written by keil and does not use mbed libs. I think maybe cortex M0 may not have this problem, I have a lpc1769 but I can not test it yet, you can not even buy the lpc1768 no more maybe this could be one of the reasons why? or at least most manufactors replace it with the lpc1769.
Gert,
Do you still have the lpcxpresso, maybe you could try that, I was gonna get the lpc1768 Blueboard H, and use hardware PWM output for audio, but is this a problem with the LPC1768 chip or just the mbed? I can not test my lpcxpresso that I have maybe you could try yours.
Philips Philips wrote:
Gert,
Do you still have the lpcxpresso, maybe you could try that, I was gonna get the lpc1768 Blueboard H, and use hardware PWM output for audio, but is this a problem with the LPC1768 chip or just the mbed? I can not test my lpcxpresso that I have maybe you could try yours.
I will try the lpcxpresso board and see if it makes any difference.
Hi,
Quote:
Maybe I have a collision between the IO0INTCLR (which is also in APB range) and the DACR write ?
That's my guess! The processor will have to stall if there are two quick transactions on the apb, which could be the interrupt clear followed by the DAC write. Should be easy to test/reproduce without having to fire interrupts. You could also clear the interrupt earlier on in the routine as one way to try and avoid this.
Simon
Small update:
It is definitely a 'clash' between the two writes on the (same) APB bus. Alas it seems not possible to do the two writes within 1 usec through this bus without having the CPU getting 'stalled'. Not even if I set the PCLK of the DAC and GPIOINT to CCLK instead of the default CCLK/4 .
I tested this by writing the IO0INTCLR at the very beginning and writing the DACR at the very end of the interrupt routine, they should then be at least 1 usec apart but the CPU was not able to respond to the next interrupt in time (approx 1.5 usecs later)
BTW: to clear the IO0INTCLR flags in the beginning of 'my_irq' routine I had to set the EXTMODE register to 'edge sensitive' interrupts for EINT3 (which is default set to 'level sensitive') otherwise it would not reset the flag.
Also: do I need to clear the EXTINT register myself in my_irq or is this done automatically by writing to the IOxINTCLR register(s) ?
I will try some small routines using only some DACR writes and INT0CLR to examine the timing and see if the PCLK is having any influence on the writes.
Speedtest :-)
This assembler routine toggles LED1 and meanwhile writes to DACR and I00INTCLR The PCLK of the DACR and GPIO is set to CCLK (max speed)
AREA asm_func, CODE, READONLY ; Export my asm function location so that C compiler can find it and link EXPORT speed_check ALIGN speed_check ; setup some pointers LDR R1,=0x2009c020 ; pointer to port1 LDR R2,=0x4008C000 ; pointer to DACR LDR R0,=0x4002808C ; pointer to IO0INTCLR loop LDR R3,=0x0000FFC0 ; max ADC STR R3,[R2] ; set DAC with supplied value LDR R3,=0x7fff8fff ; clear mask STR R3,[R0] ; clear interrupt flag LDR R3,=0x00000000 ; set LED 1 off STR R3,[R1,#0x14] ; set outputs LDR R3,=0x00000000 ; min ADC STR R3,[R2] ; set DAC with supplied value LDR R3,=0x7fff8fff ; clear mask STR R3,[R0] ; clear interrupt flag LDR R3,=0x00040000 ; set LED 1 on STR R3,[R1,#0x14] ; set outputs B loop ALIGN END
This is the C routine which calls the above code:
#include "mbed.h" DigitalOut myled(LED1); extern "C" void speed_check(); AnalogOut signal(p18); volatile uint32_t *PCLKSEL0=(volatile uint32_t *) (0x400FC1A8); volatile uint32_t *PCLKSEL1=(volatile uint32_t *) (0x400FC1AC); int main() { *PCLKSEL0|=1<<22; // set DAC PCLK to CCLK *PCLKSEL1|=1<<2; // set GPIO PCLK to CCLK speed_check(); // execute assembler routine while(1) { } }
I connected the oscilloscope to LED1 and a complete cycle lasts about 600 nsec (2 writes to the DACR and IO0INTCLR and two writes to port1) Only this time there is no real interrupt, just an endless loop
I checked the DAC output and is a nice 'triangular shape' (because of settling time) running in sync with the LED.. So it seems that the writes to the registers themselves are not the cause of my timing problem, maybe inside the interrupt things behave differently and is the write to IO0INTCLR delayed somehow. So the search continues..
What about this, I was thinking maybe I should try a software interrupt using a software delay like discussed in this blog but how you supposed to calculate 31.25us
The ADC does do the sample and hold as well
http://gvworks.blogspot.com/2011/01/interrupts-in-lpc1768.html
Now, we'll stop using the timer but use the timer interrupt ! This is basically a software interrupt.
#include "LPC17xx.h" volatile uint32_t del; void _delay(uint32_t delay); int main (void) { NVIC_EnableIRQ(TIMER0_IRQn); LPC_SC->PCONP |= ( 1 << 15 ); // power up GPIO LPC_GPIO1->FIODIR |= 1 << 29; // puts P1.29 into output mode. LED is connected to P1.29 while(1) { _delay(1 << 24); // Wait for about 1 second NVIC_SetPendingIRQ(TIMER0_IRQn); // Software interrupt } return 0; } void TIMER0_IRQHandler (void) { LPC_GPIO1->FIOPIN ^= 1 << 29; // Toggle the LED } void _delay(uint32_t delay) { uint32_t i; for(i = 0;i < delay;i++ ) del = i; // do this so that the compiler does not optimize away the loop. }
Philips Philips wrote:
What about this, I was thinking maybe I should try a software interrupt using a software delay like discussed in this blog but how you supposed to calculate 31.25us
The ADC does do the sample and hold as well
http://gvworks.blogspot.com/2011/01/interrupts-in-lpc1768.html
Now, we'll stop using the timer but use the timer interrupt ! This is basically a software interrupt.
void _delay(uint32_t delay) { uint32_t i; for(i = 0;i < delay;i++ ) del = i; // do this so that the compiler does not optimize away the loop. }
You can do quite strict timings with loops like this:
uint32_t i; for (i=0; i<delay; i++) __nop();
The nop also keeps the compiler from optimizing out the loop.
BTW, I managed to get my code working by optimising the interrupt code and using mbed's own routines to write to the AnalogOut pin. The mbed seems to react in very unpredictable ways when writing out of bounds values to the DAC.
My DAC write function is now something like this:
AnalogOut signal(p18); int DAC_fifo[DAC_fifo_size]; ...... int d; while (1) { if (((DAC_rptr +1) % DAC_fifo_size) != DAC_wptr) { // emit samples if available d=DAC_fifo[DAC_rptr++]; // get sample value DAC_rptr &= 0xff; // increment and limit pointer signal=0.5+(d/1024); // set DAC output for (t=0; t<1800; t++) __nop(); // busy wait loop for sample frequency } .......... }
And voila, even with the heavy interrupting going on it works fine now.... If I try and write the value 'd' shifted 6 bits left directly into the DACR then the mysterious delays happen all over again. I would very much like to see what the disassembled mbed library code is for writing the DAC...
I am running into problems at the moment with errors I don't really understand it look good to me but it uvision does not like it
/*---------------------------------------------------------------------------- * Name: usbmain.c * Purpose: USB Audio Class Demo * Version: V1.20 *---------------------------------------------------------------------------- * This software is supplied "AS IS" without any warranties, express, * implied or statutory, including but not limited to the implied * warranties of fitness for purpose, satisfactory quality and * noninfringement. Keil extends you a royalty-free right to reproduce * and distribute executable files created using this software for use * on NXP Semiconductors LPC microcontroller devices only. Nothing else * gives you the right to use this software. * * Copyright (c) 2009 Keil - An ARM Company. All rights reserved. *---------------------------------------------------------------------------*/ #include "LPC17xx.h" /* LPC17xx definitions */ #include "type.h" #include "usb.h" #include "usbcfg.h" #include "usbhw.h" #include "usbcore.h" #include "usbaudio.h" uint8_t Mute; /* Mute State */ uint32_t Volume; /* Volume Level */ #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 uint16_t DataOut; /* Data Out Index */ uint16_t DataIn; /* Data In Index */ uint8_t DataRun; /* Data Stream Run State */ uint16_t PotVal; /* Potenciometer Value */ uint32_t VUM; /* VU Meter */ uint32_t Tick; /* Time Tick */ volatile uint32_t del; void _delay(uint32_t delay); /* * Get Potenciometer Value */ void get_potval (void) { uint32_t val; LPC_ADC->ADCR |= 0x01000000; /* Start A/D Conversion */ do { val = LPC_ADC->ADGDR; /* Read A/D Data Register */ } while ((val & 0x80000000) == 0); /* Wait for end of A/D Conversion */ LPC_ADC->ADCR &= ~0x01000000; /* Stop A/D Conversion */ PotVal = ((val >> 8) & 0xF8) + /* Extract Potenciometer Value */ ((val >> 7) & 0x08); } /* * 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 (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 (val < 0) VUM -= val; /* Accumulate Neg Value */ else VUM += val; /* Accumulate Pos Value */ val *= Volume; /* Apply Volume Level */ val >>= 16; /* Adjust Value */ val += 0x8000; /* Add Bias */ val &= 0xFFFF; /* Mask Value */ } else { val = 0x8000; /* DAC Middle Point */ } if (Mute) { val = 0x8000; /* DAC Middle Point */ } /* 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 */ } void _delay(uint32_t delay) { uint32_t i; for(i = 0;i < delay;i++ ) del = i; // do this so that the compiler does not optimize away the loop. } //LPC_DAC->DACR = val & 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 */ /***************************************************************************** ** Main Function main() ******************************************************************************/ int main (void) { volatile uint32_t pclkdiv, pclk;//, del; SystemInit(); LPC_PINCON->PINSEL1 &=~((0x03<<18)|(0x03<<20)); /* P0.25, A0.0, function 01, P0.26 AOUT, function 10 */ LPC_PINCON->PINSEL1 |= ((0x01<<18)|(0x02<<20)); /* Enable CLOCK into ADC controller */ LPC_SC->PCONP |= (1 << 12); LPC_ADC->ADCR = 0x00200E04; /* ADC: 10-bit AIN2 @ 4MHz */ LPC_DAC->DACR = 0x00008000; /* DAC Output set to Middle Point */ /* By default, the PCLKSELx value is zero, thus, the PCLK for all the peripherals is 1/4 of the SystemFrequency. */ /* Bit 2~3 is for TIMER0 */ pclkdiv = (LPC_SC->PCLKSEL0 >> 2) & 0x03; switch ( pclkdiv ) { case 0x00: default: pclk = SystemFrequency/4; break; case 0x01: pclk = SystemFrequency; break; case 0x02: pclk = SystemFrequency/2; break; case 0x03: pclk = SystemFrequency/8; break; } //LPC_TIM0->MR0 = pclk/DATA_FREQ - 1; /* TC0 Match Value 0 */ //LPC_TIM0->MCR = 3; /* TCO Interrupt and Reset on MR0 */ //LPC_TIM0->TCR = 1; /* TC0 Enable */ NVIC_EnableIRQ(TIMER0_IRQn); USB_Init(); /* USB Initialization */ USB_Connect(TRUE); /* USB Connect */ /********* The main Function is an endless loop ***********/ void _delay(uint32_t delay); while( 1 ); NVIC_SetPendingIRQ(TIMER0_IRQn); // Software interrupt _delay = pclk/DATA_FREQ - 1; } return 0; /****************************************************************************** ** End Of File ******************************************************************************/
these errors
Build target 'Flash' compiling usbdmain.c... usbdmain.c(170): error: #169: expected a declaration usbdmain.c(227): warning: #12-D: parsing restarts here after previous syntax error usbdmain.c(231): warning: #1295-D: Deprecated declaration USB_Init - give arg types usbdmain.c(231): warning: #77-D: this declaration has no storage class or type specifier usbdmain.c(231): error: #147: declaration is incompatible with "void USB_Init(void)" (declared at line 83 of "usbhw.h") usbdmain.c(232): error: #79: expected a type specifier usbdmain.c(232): warning: #77-D: this declaration has no storage class or type specifier usbdmain.c(232): error: #147: declaration is incompatible with "void USB_Connect(uint32_t)" (declared at line 84 of "usbhw.h") usbdmain.c(238): error: #169: expected a declaration usbdmain.c(240): warning: #77-D: this declaration has no storage class or type specifier usbdmain.c(240): error: #92: identifier-list parameters may only be used in a function definition usbdmain.c(240): error: #147: declaration is incompatible with "void NVIC_SetPendingIRQ(IRQn_Type)" (declared at line 969 of "C:\Keil\ARM\CMSIS\Include\core_cm3.h") usbdmain.c(241): error: #77-D: this declaration has no storage class or type specifier usbdmain.c(241): error: #147: declaration is incompatible with "void _delay(uint32_t)" (declared at line 158) usbdmain.c(241): error: #20: identifier "pclk" is undefined usbdmain.c(242): error: #169: expected a declaration Target not created
Never understand why it gives me errors when pclk is identified, I need to use old core_cm3 header file but it's a little over my head tho. Not sure how to change the mbed usb audio code with it all been in mbed language so I tried it on this code.
I got this delay code tho
/*Copyright (C) 2011 by Sagar G V Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "delay.h" #ifdef __cplusplus extern "C" { #endif void few_ticks() { __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); __ASM("nop"); } void _delay_us(uint32_t time) { uint32_t i; for(i=0;i<time;i++) few_ticks(); } void _delay_ms(uint32_t time) { _delay_us(1000*time); } void _delay_s(uint32_t time) { _delay_ms(1000*time); } #ifdef __cplusplus } #endif
from http://cortex-m3-tutorials.googlecode.com/files/TIMER0_interrupt_blinky.rar
Assembler update:
This is the current code I use which has no conflicts anymore:
AREA asm_func, CODE, READONLY ; Export my_asm function location so that C compiler can find it and link EXPORT my_irq EXPORT reset_T0 IMPORT testrom IMPORT voice_command ALIGN ; my irq assumes that no other interrupt sources exist ; R0 points to port0 ; R1 points to port1 ; R2 points intially to port2, but contains calculcated address thereafter ; R3 is used as general purpose register my_irq LDR R0,=0x2009c000 ; pointer to port0 LDR R1,=0x2009c020 ; pointer to port1 LDR R2,=0x2009c054 ; initial pointer to port2 FIO2PIN0 LDR R3,[R0,#0x14] ; get port0 value TST R3,#0x00000001 ; write IRQ ? BEQ do_write do_read LDRB R2,[R2] ; get byte value from port2 (destroy pointer to port2 but is not needed anymore) AND R2,#0x0000003F ; save bits for A0-A5 in R2 AND R3,#0x00078000 ; and of bits for A6-A9 LSR R3,#9 ; shift down ORR R2,R3 ; or bits A6-A9 into R2 LDR R3,[R0,#0x14] ; get port0 value AND R3,#0x03800000 ; and off bits A11, P10 and P11 EOR R3,#0x03000000 ; invert P10 and P11 LSR R3,#13 ; shift to A10,A11 and A12 ORR R2,R3 ; or in the bits as A10, A11 and A12 into R2 ; R2 now contains the complete address for reading or writing LDR R3,=testrom ; start address of our rom LDRB R3,[R3,R2] ; get a byte with calculated offset (destroy pointer to testrom, not needed anymore) LSL R3,#4 ; shift up 4 bits (data bus is P0.4-P0.11) STRH R3,[R0,#0x14] ; output the byte MOV R3,#0x0ff0 ; prepare output mask STRH R3,[R0] ; set port to output, emitting byte on the bus wait_for_psen_high LDR R3,[R0,#0x14] ; get port0 value TST R3, #0x00000002 ; and test psen (P0.1) BEQ wait_for_psen_high ; wait for PSEN to rise again MOV R3,#0x0000 ; prepare input mask STRH R3,[R0] ; set port to input again, releasing the bus B.W irq_exit ALIGN ; -------------------------------------------------------------- do_write ; we are going to do a write LDR R3,[R1,#0x14] ; get port1 value TST R3,#0x40000000 ; test if P14 (P1.30) is low BNE irq_exit ; this is not for us LDRB R2,[R2] ; get byte value from port2 (destroy pointer to port2 but is not needed anymore) AND R2,#0x0000003F ; save bits for A0-A5 in R2 LDR R3,[R0,#0x14] ; get port0 value AND R3,#0x00018000 ; and of bits for A6 and A7 LSR R3,#9 ; shift down ORR R2,R3 ; or bits A6 and A7 into R2 ; R2 now contains the bits for A0-A7 TST R2,#0x00000080 ; test if A7 is high in calculated address BEQ irq_exit ; this is not for us wait_for_wr_high LDR R3,[R0,#0x14] ; get port0 value TST R3, #0x00000001 ; and test WR (P0.0) BEQ wait_for_wr_high ; loop until WR goes high again AND R3,#0x00000200 ; isolate D5 (reset signal) ORR R2,R3 ; combine reset and command into one LDR R0,=voice_command ; point to voice command variable STR R2,[R0] ; store address in variable (only bits 0-6 are valid) MOV R3,#0x80 ; set mask to P1.31 (T0) STRB R3,[R1,#0x1B] ; write to port1 FIOSET3 (signal busy) irq_exit LDR R2,=0x4002808C ; IO0INTCLR MOV R3,#0x03 ; P0.0 and P0.1 STRB R3,[R2] ; clear interrupt flag BX LR ; return to caller ALIGN reset_T0 LDR R1,=0x2009c03f ; point to port1 FIOCLR MOV R0,#0x80 ; set mask P1.31 (T0) STRB R0,[R1] ; write to FIOCLR3 (signal done) BX LR ; return to caller ALIGN END
Writing to the DAC is now as simple as:
AnalogOut signal(p18); .......... signal=0.5+(sample_value/1024); ..........
I optimized the assembler part as far as I could and changed long writes/reads to bytewise and wordwise varieties where possible. It works so good now that I can even add debug printf's over the 115200 baud USB serial without disturbing the interrupts/host cpu.. As my speech emulator would say: 'Incredible' :-)
You may as well talk in a different language Gert, because I just can not understand what you have have done, and what may be the problem with my code, it is just to much for my dyslexic little brain:(
Philips,
If your problem is still the compiler error reports from your post 2 days and 10 hrs ago: in the program "usbmain.c" (file usbdmain.c?), the cause is as follows -
In the function "void _delay(uint32_t delay){}" the first 'for()' loop is missing an opening curly brace between the right parenthesis and the statement that is the body of the loop. Alternately, since the body is a single statement, you could eliminate the (unmatched) closing curly brace.
Fred Scipione wrote:
Philips,
If your problem is still the compiler error reports from your post 2 days and 10 hrs ago: in the program "usbmain.c" (file usbdmain.c?), the cause is as follows -
In the function "void _delay(uint32_t delay){}" the first 'for()' loop is missing an opening curly brace between the right parenthesis and the statement that is the body of the loop. Alternately, since the body is a single statement, you could eliminate the (unmatched) closing curly brace.
Well the problems is the click and pops in the audio, and trying to understand what Gert has done in his code with the out of bound write to the Dac he talks of, my dyslexic brain can not understand the words it see on this page.
I just want to fix the clicking problem in the code that has plagued it for months now:( and after talking with somebody maybe a software interrupt will not be good enough and I should get gert to try his best to explain it for my dyslexic brain:)
If I am to add signal=0.5+(d/1024); to my code, I don't know how to because my dyslexic brain just can not compute this and the code I have and his are completely different:)
oh second look at it and I get it now I think
Well I need to add gert code rountine to my code so I don't have to use the speaker.write_u16((uint16_t)PlaySample); and then I can write signal=0.5+(PlaySample);
this mbed usb audio code
for keil code LPC_DAC->DACR = PlaySample & 0xFFC0; /* Set Speaker Output */
not sure what must be done for keil code I don't think I could just write LPC_DAC->DACR = PlaySample & 0.5+
I don't understand how to do so if gert could maybe help a little more that would be great:)
Quote:
If I am to add signal=0.5+(d/1024); to my code, I don't know how to because my dyslexic brain just can not compute this and the code I have and his are completely different:)
oh second look at it and I get it now I think
In the speech emulator code I use, samples are created within a signed integer with limits set to -511 and +511 (to avoid going over 10 bits). By dividing it by 1024 I will get a value between -0.5 and +0.5 which is then added to 0.5 to get a value between 0 and 1 for the DAC output function.
My guess is that the AnalogOut pin setting with the mbed libraries is writing only the lower 16 bit of the DACR register (did not disassemble the binary yet so it is just a guess) and probably trims the bits 0-5 since they are not valid to write according to the manual.
Quote:
Well I need to add gert code rountine to my code so I don't have to use the speaker.write_u16((uint16_t)PlaySample); and then I can write signal=0.5+(PlaySample);
For his to work your PlaySample value needs to be converted into a 'float' value between -0.5 and 0.5
Quote:
for keil code LPC_DAC->DACR = PlaySample & 0xFFC0; /* Set Speaker Output */
That is what I tried in my assembler version as well and it gave 'strange' effects.
So you think I could make the changes to this code
00001 // USBAudio speaker example 00002 00003 #include "mbed.h" 00004 #include "USBAudio.h" 00005 00006 // frequency: 48 kHz 00007 #define FREQ 48000 00008 00009 // 1 channel: mono 00010 #define NB_CHA 1 00011 00012 // length of an audio packet: each ms, we receive 48 * 16bits ->48 * 2 bytes. as there is one channel, the length will be 48 * 2 * 1 00013 #define AUDIO_LENGTH_PACKET 48 * 2 * 1 00014 00015 // USBAudio 00016 USBAudio audio(FREQ, NB_CHA, 0x7180, 0x7500); 00017 00018 // speaker connected to the AnalogOut output. The audio stream received over USb will be sent to the speaker 00019 AnalogOut speaker(p18); 00020 00021 // ticker to send data to the speaker at the good frequency 00022 Ticker tic; 00023 00024 // buffer where will be store one audio packet (LENGTH_AUDIO_PACKET/2 because we are storing int16 and not uint8) 00025 int16_t buf[AUDIO_LENGTH_PACKET/2]; 00026 00027 // show if an audio packet is available 00028 volatile bool available = false; 00029 00030 // index of the value which will be send to the speaker 00031 int index_buf = 0; 00032 00033 // previous value sent to the speaker 00034 uint16_t p_val = 0; 00035 00036 // function executed each 1/FREQ s 00037 void tic_handler() { 00038 float speaker_value; 00039 00040 if (available) { 00041 //convert 2 bytes in float 00042 speaker_value = (float)(buf[index_buf]); 00043 00044 // speaker_value between 0 and 65535 00045 speaker_value += 32768.0; 00046 00047 // adjust according to current volume 00048 speaker_value *= audio.getVolume(); 00049 00050 // as two bytes has been read, we move the index of two bytes 00051 index_buf++; 00052 00053 // if we have read all the buffer, no more data available 00054 if (index_buf == AUDIO_LENGTH_PACKET/2) { 00055 index_buf = 0; 00056 available = false; 00057 } 00058 } else { 00059 speaker_value = p_val; 00060 } 00061 00062 p_val = speaker_value; 00063 00064 // send value to the speaker 00065 speaker.write_u16((uint16_t)speaker_value); 00066 } 00067 00068 int main() { 00069 00070 // attach a function executed each 1/FREQ s 00071 tic.attach_us(tic_handler, 1000000.0/(float)FREQ); 00072 00073 while (1) { 00074 // read an audio packet 00075 audio.read((uint8_t *)buf); 00076 available = true; 00077 } 00078 }
it has
speaker_value += 32768.0;
what would need to be done here gert
speaker_value += 511.-511;
would 16 bits need to change, is any negative effect here of using smaller value
I guess the hardware sucks I think its Xilinx Spartan time for me you can't even write the full 16bits.. 24 bits is supposed to be Gods zone according to some audio phills
I guess it not a true 16 bit dac and we are only really getting 10 it seems what you think Gert.
It is nowhere written that the DAC of the mbed is 16 bit:
Quote:
10-bit Digital-to-Analog Converter (DAC) with dedicated conversion timer and DMA support.
For doing high end audio simply connect a real audio DAC to the I2S in/output. Have you seen this: http://mbed.org/cookbook/HQ-Audio ? With this you have stereo in- and output.
For me, getting the speech emulator working next to the realtime rom emulation was the challenge. I did/do not expect high end audio from a 10 bit DAC :-)
The mbed's I2S interface with DMA is rocksteady as the VGA project of Ivo van Poorten and me proved already (we 'abuse' the I2S output by generating 25 MHz pixeldata from mbed's memory).
Gert van der Knokke wrote:
It is nowhere written that the DAC of the mbed is 16 bit:
Quote:
10-bit Digital-to-Analog Converter (DAC) with dedicated conversion timer and DMA support.
For doing high end audio simply connect a real audio DAC to the I2S in/output. Have you seen this: http://mbed.org/cookbook/HQ-Audio ? With this you have stereo in- and output.
For me, getting the speech emulator working next to the realtime rom emulation was the challenge. I did/do not expect high end audio from a 10 bit DAC :-)
The mbed's I2S interface with DMA is rocksteady as the VGA project of Ivo van Poorten and me proved already (we 'abuse' the I2S output by generating 25 MHz pixeldata from mbed's memory).
Well I don't know how much you know about audio but that project you pointed to is a complete waste of time if you ask me.. it's a 100 times worse than the Mbed successive approximation register ADC + DAC. It may as well have been called low end audio, how to torture your ears with delta sigma, you now they did that in the war the Germans torture humans with delta sigma..
My designs are base around these beauty's here, this is real high end audio equipment, but I am stuck at the code part with a hole in pocket:)
On another note do you know how to change the http://mbed.org/users/samux/programs/USBAUDIO_speaker/m1h5sc/docs/main_8cpp_source.html
to 8bit resolution, I try changing the bSubFrameSize to 0x01 bBitResolution 8 still nothing
Oh dear, somebody bought the 'bring your own power supply' board of Altmann... Well, I am not going to start a flame war over 'high end audio' :-)
In the past I did a DAC with a TDA1543 and it sounded great, much better than the Crystal audio chip the sound was originally coming from.
I work with and repair audio equipment for a long long time, ranging from anything cheap to expensive tube amps. I don't have perfect hearing so things like 'jitter', 'the size of the stage' and 'placements of instruments' which I read about in the 'high end' magazines does not do me much.
I do know that you can have better signal-to-noise ratio with better DAC's, anything measurable I can understand. If the output accurately represents the original signal it does not matter to me if it is a Sigma-Delta, SAR, R2R or whatever type of DAC.
In the UM10360 manual it says:
Quote:
If either the CNT_ENA or the DBLBUF_ENA bits are 0, any writes to the DACR address will go directly to the DACR register.
But also:
Quote:
Maximum update rate of 1 MHz.
What does this mean ? If I write faster than 1 MHz will the write operation be delayed ?
I am asking this because I experience random but serious timing problems in my fast interrupt routine as soon as I try to write to the DACR register (in the main program) If I simply comment out the DACR write it is fine.
(I know I am probably pushing the mbed to its limits but I can not find an explanation for this behaviour)