Take the 2-minute tour ×
Arduino Stack Exchange is a question and answer site for developers of open-source hardware and software that is compatible with Arduino. It's 100% free, no registration required.

My Due is interfaces with a DAC which I need to update with freq 1MHz. The nice solution seems to be a timer interrupt. I found a thread here called "Arduino Due - creating an 8Mhz clock signal". I tried the following code to see if I can get 4MHz on P41. No luck. Any help will be greatly appreciated. Thanks.

volatile boolean l = false;

void TC6_Handler()
{
    TC_GetStatus(TC2, 0);
    digitalWrite(41, l = !l);
}

void startTimer(Tc *tc, uint32_t channel, IRQn_Type irq) {
    pmc_set_writeprotect(false);
    pmc_enable_periph_clk((uint32_t)irq);

    TC_Configure(tc, channel,
    TC_CMR_WAVE |
    TC_CMR_WAVSEL_UP_RC |
    TC_CMR_TCCLKS_TIMER_CLOCK1|
    TC_CMR_ACPA_TOGGLE );  // RC compare TOGGLES TIOA);

    TC_SetRA(tc, channel, 1); //50% high, 50% low
    TC_SetRC(tc, channel, 1);


    PIO_Configure(PIOC,
    PIO_PERIPH_B,
    PIO_PC25B_TIOA6,
    PIO_DEFAULT);

TC_Start(tc, channel);

}

void setup(){
pinMode(41,OUTPUT);
startTimer(TC2, 0, TC6_IRQn);
}

void loop(){
}
share|improve this question

4 Answers 4

I'm not 100% certain without my Due in front of me to test this, but as far as I can see, you are configuring your clock to run at 42MHz with "TC_CMR_TCCLKS_TIMER_CLOCK1" and are getting the timer to trigger when the count is "1" with "TC_SetRC(tc, channel, 1);", which would give you 42MHz, not 4MHz.

Since the clock timer still increments during the TC6_Handler() function, I'm guessing that the interrupt triggers a second time before it has exited the function the first time causing a hang.

Try changing your code to "TC_SetRC(tc, channel, 42000000 / 4000000);" to see if that works. Your interrupt will still be triggered every 10 ticks of the timer which will only give you 20 ticks of the CPU clock to do your work and exit. I suspect this may still be too little time to exit before the next interrupt is triggered. If you still don't get the result you want, try reducing the frequency considerably to see if your timer is actually getting configured correctly. I don't think you can get exactly 4MHz as the Due timer frequencies are not divisible by 4000000, unless my brain is a little fuzzy this morning and I'm missing something obvious...

If you do need to optimise the code inside your handler so it can run faster, let me know and I'll post some ideas when I can get near my Due and my source code for reference.

You could always check out this link that I used when I needed to understand how to set up a timer for my own projects: http://2manyprojects.net/timer-interrupts Should that link go dead before you have a chance to read up, this is the example source from that page I modified for my own purposes:

// These are the clock frequencies available to the timers /2,/8,/32,/128
// 84Mhz/2 = 42.000 MHz
// 84Mhz/8 = 10.500 MHz
// 84Mhz/32 = 2.625 MHz
// 84Mhz/128 = 656.250 KHz
//
// 42Mhz/44.1Khz = 952.38
// 10.5Mhz/44.1Khz = 238.09 
// 2.625Hmz/44.1Khz = 59.5
// 656Khz/44.1Khz = 14.88 // 131200 / 656000 = .2 (.2 seconds)

// 84Mhz/44.1Khz = 1904 instructions per tick
const int led_pin = 13;
int state = false;
int interruptCtr = 1;
int S = 0;

void setup()
{
  pinMode(led_pin, OUTPUT);
  /* turn on the timer clock in the power management controller */
  pmc_set_writeprotect(false);       // disable write protection for pmc registers
  pmc_enable_periph_clk(ID_TC7);     // enable peripheral clock TC7

  /* we want wavesel 01 with RC */
  TC_Configure(/* clock */TC2,/* channel */1, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK4); 
  TC_SetRC(TC2, 1, 131200);
  TC_Start(TC2, 1);

  // enable timer interrupts on the timer
  TC2->TC_CHANNEL[1].TC_IER=TC_IER_CPCS;   // IER = interrupt enable register
  TC2->TC_CHANNEL[1].TC_IDR=~TC_IER_CPCS;  // IDR = interrupt disable register

  /* Enable the interrupt in the nested vector interrupt controller */
  /* TC4_IRQn where 4 is the timer number * timer channels (3) + the channel number (=(1*3)+1) for timer1 channel1 */
  NVIC_EnableIRQ(TC7_IRQn);
}

void loop()
{
  // do nothing timer interrupts will handle the blinking;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// INTERRUPT HANDLERS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void TC7_Handler()
{
  // We need to get the status to clear it and allow the interrupt to fire again
  TC_GetStatus(TC2, 1);
  state = !state;
  digitalWrite(led_pin, state);

  if( interruptCtr++ >= 6 )
    {
     interruptCtr = 1;
     S = !S; // are we flashing S or O
     if( S ) // set time till next interrupt
        TC_SetRC(TC2, 1, 131200); // 131200 / 656000 = .2 seconds
     else
        TC_SetRC(TC2, 1, 656000); // 656000/ 656000 = 1 second
    }
}
share|improve this answer
    
I just noticed the title mentions 1MHz as does your question, before it mentions 4MHz. Using "TC_SetRC(tc, channel, 42); // 42MHz / 1MHz" will give you the 1MHz timer you desire. This will give you approx. 80 ticks of the CPU clock to complete your interrupt. Still pretty low, but definitely doable to flip an IO line - you may need to avoid digitalWrite() though. –  Mick Waites May 28 at 8:51
    
i didnt read your answer, just vote up to bump it above comment-answers of the OP –  aaaaaa Jun 12 at 1:04

thanks for your comment. I kinda found the partial solution for the problem. This piece of code works much faster than digitalWrite

static __inline__ void digitalWriteDirect(int pin, boolean val){
if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;}

I'm using Atmel Studio with VisualMicro. Everything works fine, but I can't set the environment so the direct io port commands would be available. Everything like PORTD &= B11110111; would give the error "PORTD is not declared in the scope". That would be very handy if I find the way how to use those commands. What IDE are you using?

share|improve this answer
1  
Mostly Visual Studio, but sometimes the Arduino IDE when just testing out code. The "PORTD" macro you mention is for the Uno (and possibly other similar boards). The Due has completely different registers for these. Try "REG_PIOC_ODSR = 0;" and see if that will compile for you (it won't actually do anything when you run the code). If so, see if you can navigate to where REG_PIOC_ODSR is defined and you'll find all the definitions for direct IO writes. Combine that with a read of the datasheet mentioned above and you should be able to get a decent understanding of how to use them. –  Mick Waites Jun 1 at 13:59

thank you very much for your reply. You have a lot of interesting tutorials on your web site, I'm currently reading some.

With my Arduino Due I'm driving 12 bit DAC to generate waveforms for a piezo. So it is pretty much setting 12 bits from a pre-generated lookup table in every interrupt routine. The bigger the frequency firing up the interrupt, the smoother the wave would be (the more data points in the lookup table). From your experience, what is the maximum frequency I can get the interrupt fired with?

I'm still struggling with setting up the timers properly. For the testing purposes I'm just flipping the signal on one of the outputs. The max frequency I can get so far is 167kHz, meaning that the interrupt is fired 167*2 kHz.

I would certainly like to go higher if possible. Thanks for your help. I greatly appreciate it.

Alan

share|improve this answer
    
I'm assuming you are referring to my reply - if so, you are welcome, however the site is not mine, it is one I found when I was looking up how to set timers on the Due. –  Mick Waites May 29 at 12:13

!!!WOW!!! I just discovered that digitalWrite() instruction itself is 2.5us long! Such an unexpected surprise! Is there any way to assign IO signals faster? In my case setting 12 bits will take 2.5us * 12 = 30us.

share|improve this answer
    
Yeah, Digital Write is pretty inefficient. It is possible to set up to 32 IO lines in a single CPU instruction, meaning theoretically you could flip them at 84MHz, however that leaves no room for further instructions. I'd recommend reading the datasheet for the Atmel core (atmel.com/Images/…), having a look at the Parallel IO section, particularly section 31.5.5. The macros defined in the Arduino library are REG_PIO{x}_ODSR where {x} is the port register letter - A, B, C or D that you are setting. –  Mick Waites May 29 at 12:23

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.