Timing of DAC

26 Dec 2011

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)

26 Dec 2011

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:)

26 Dec 2011

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?

26 Dec 2011

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

26 Dec 2011

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 ?

26 Dec 2011

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.

27 Dec 2011

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

27 Dec 2011

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 :-)

28 Dec 2011

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?

28 Dec 2011

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.

28 Dec 2011

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 ?

28 Dec 2011

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.

28 Dec 2011

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.

29 Dec 2011

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.

29 Dec 2011

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

29 Dec 2011

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.

31 Dec 2011

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

/media/uploads/gertk/_scaled_speedtest.jpg

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..

01 Jan 2012

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.
}
02 Jan 2012

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...

02 Jan 2012

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.

02 Jan 2012

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

02 Jan 2012

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' :-)

05 Jan 2012

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:(

05 Jan 2012

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.

05 Jan 2012

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:)

05 Jan 2012

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.

05 Jan 2012

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.

05 Jan 2012

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).

05 Jan 2012

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:)

/media/uploads/mbed2f/_scaled_attr_1.jpg

/media/uploads/mbed2f/_scaled_monica2_detailed2.jpg

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

05 Jan 2012

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.