How fast can a Ticker tick?

08 Feb 2010

Hello all -- we're attempting to develop a rudimentary DSP system on the mBed as part of our second-year university project. We're using a Ticker with the following function to grab data from the ADC and bung it into a circular buffer (and the inverse with the DAC):

CircularBuffer inBuffer(1024), outBuffer(1024);
AnalogIn in(p20);
AnalogOut out(p18);
 
void pushPull() {
  inBuffer.writeShort(in.read_u16() - SHRT_MAX);
  out.write_u16(outBuffer.readShort() + SHRT_MAX);
}

We then have the main loop of our program take a window of audio from the in buffer, manipulate it, and write it to the out buffer. We've been cuckolded by the mBed's software floats, to the point where we can't even use a sin() call in our code for fear of introducing all sorts of mad (and strange-sounding) rounding errors, so everything's in short ints.

My question is: how fast can we clock a Ticker object? We've got it chugging along happily at about 80us now (but with some strange noise artefacts that might be inevitable). The problem is, we want a sample rate of about 32kHz, which is a period of 31.25us. Whenever I try to set the Ticker's interval to that kind of value, it seems like the mBed is so overwhelmed by the stress of interrupting so regularly that no other code gets executed!

Can anyone suggest a solution to this issue?

09 Feb 2010
Project Golf wrote:

Hello all -- we're attempting to develop a rudimentary DSP system on the mBed as part of our second-year university project. We're using a Ticker with the following function to grab data from the ADC and bung it into a circular buffer (and the inverse with the DAC):

CircularBuffer inBuffer(1024), outBuffer(1024);
AnalogIn in(p20);
AnalogOut out(p18);
 
void pushPull() {
  inBuffer.writeShort(in.read_u16() - SHRT_MAX);
  out.write_u16(outBuffer.readShort() + SHRT_MAX);
}

We then have the main loop of our program take a window of audio from the in buffer, manipulate it, and write it to the out buffer. We've been cuckolded by the mBed's software floats, to the point where we can't even use a sin() call in our code for fear of introducing all sorts of mad (and strange-sounding) rounding errors, so everything's in short ints.

My question is: how fast can we clock a Ticker object? We've got it chugging along happily at about 80us now (but with some strange noise artefacts that might be inevitable). The problem is, we want a sample rate of about 32kHz, which is a period of 31.25us. Whenever I try to set the Ticker's interval to that kind of value, it seems like the mBed is so overwhelmed by the stress of interrupting so regularly that no other code gets executed!

Can anyone suggest a solution to this issue?

use interrupts, see page 573 of LPC17xx manual. you probably want to override the CMSIS ADC interrupt complete function... Also don't use AnalogIn and operate on the ADC directly... Set AD0CR->BURST = 1 and AD0CR->START = 1; and set AD0INTEN->ADINTENx = 1;

09 Feb 2010

 

Michael Wei wrote:

use interrupts, see page 573 of LPC17xx manual. you probably want to override the CMSIS ADC interrupt complete function... Also don't use AnalogIn and operate on the ADC directly... Set AD0CR->BURST = 1 and AD0CR->START = 1; and set AD0INTEN->ADINTENx = 1;

Thanks for the idea -- we had a look at using the LPC's registers directly but the manual is pretty cryptic and heavily cross-referenced. It took us a long time to find out that the interrupt vector table was mapped to memory address 0 by default, then we didn't know whether we could just write directly into that memory address or whether some sort of protection was in place.

We tried out Simon Ford's AnalogIn code, with all sorts of modifications including slowing down the ADC and using burst mode, but the noise artefacts still persisted. Also, because we should be doing heavy processing on the audio we need a specific sample rate to guarantee some length of time for the processing to take place in. What you've recommended seems like it would call the interrupt whenever the ADC settles down at a value.

Using one of the LPC's timers to trigger an input seems like a good idea, but there are all sorts of tiny specifics we're not too sure of. Can somebody who's done this sort of low-level work outline the procedure for setting this up, and manually specifying an interrupt vector?

 

09 Feb 2010

Check here about implementing your own interrupt handlers.

09 Feb 2010 . Edited: 09 Feb 2010

Hi,

As Michael says, for something so specific you might want a dedicated interrupt timer. It is a good indication that we should have something in between the fully virtualised abstract event queue that Ticker/Timeout provide, and the low level coding of setting up a specific timer to create interrupts.

In the mean time, here is an example I just put together showing how to setup an LPC timer to repeatedly fire an interrupt. Hopefully this is an example of how you can use the mbed libraries to do things simply, but mix it with standard low-level coding when you need more control:

// Example to set up an interrupt based on the LPC TIMER0 match register, sford
 
#include "mbed.h"

DigitalOut myled(LED1);
DigitalOut irqled(LED2);

void myhandler() {
    // do something!
    irqled = !irqled;
    
    // clear the TIMER0 interrupt
    LPC_TIM0->IR = 1;
}

int main() {
    // power up TIMER0 (PCONP[1])
    LPC_SC->PCONP |= 1 << 1; 

    // reset and set TIMER0 to timer mode
    LPC_TIM0->TCR = 0x2;  
    LPC_TIM0->CTCR = 0x0; 
    
    // set no prescaler
    LPC_TIM0->PR = 0;

    // calculate period (1 interrupt every second)
    uint32_t period = SystemCoreClock / 4; 

    // set match register and enable interrupt    
	LPC_TIM0->MR0 = period;
	LPC_TIM0->MCR |= 1 << 0;    // interrupt on match
	LPC_TIM0->MCR |= 1 << 1;    // reset on match

    // enable the vector in the interrupt controller
    NVIC_SetVector(TIMER0_IRQn, (uint32_t)&myhandler);
    NVIC_EnableIRQ(TIMER0_IRQn);

    // start the timer
    LPC_TIM0->TCR = 1;

    // hang around!   
    while(1) {
        myled = 1;
        wait(0.2);
        myled = 0;
        wait(0.2);
    }
}

TimerInterruptExample (the above code, importable as a project)

Hope this is useful.

Simon

27 Feb 2010

Hi,

When you use

LPC_TIM0->MR0 = period;

What kind of measurement is period? Eg; I want the interupt to occur every 50, 60.... or 100us what formula should I apply?

Thanks.

Mike

17 Feb 2011

If you're having noise "artifacts" from your ADC results, check this thread.

http://mbed.org/forum/mbed/topic/1866/

12 Nov 2011

Hey just so you know, you've incorrectly set the MCR.

You did

   // set match register and enable interrupt    
	LPC_TIM0->MR0 = period;
	LPC_TIM0->MCR |= 1 << 0;    // interrupt on match
	LPC_TIM0->MCR |= 1 << 1;    // reset on match

should look like this

   // set match register and enable interrupt    
	LPC_TIM0->MR0 = period;
	LPC_TIM0->MCR = 0x3;    // interrupt on match, reset on match

Correct me if I'm mistaken pls, but it seems to be working now.

24 Dec 2011

The TimerInterruptExample does not seem to work with the latest (v31) mbed library. Any pointers why this is?