1

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(){
}
  • Generally you should try to do something like this with DMA, creative use of some other on-chip peripheral, etc rather than the main processor core - at least if you want your MCU to be useful for much else. – Chris Stratton Dec 31 '16 at 18:40
2

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
    }
}
  • 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 '15 at 8:51
  • i didnt read your answer, just vote up to bump it above comment-answers of the OP – aaaaa says reinstate Monica Jun 12 '15 at 1:04
0

When I'm working with timers on my Due I always keep my code low level so my solution for your problem would be:

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

#define SPEED(x) 42 * x

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

void setup()
{

    pmc_set_writeprotect(false);       // disable write protection for pmc registers /**always the first thing to do*/

    // Enable pin 13 with direct port manipulation
    REG_PIOB_PER = 1<<27;
    REG_PIOB_OER = 1<<27;
    REG_PIOB_OWER = 1<<27;

    /* turn on the timer clock in the power management controller */
    pmc_enable_periph_clk(ID_TC0);     // enable peripheral clock TC0

    //Enable interrupt in NVIC before starting interrupt
    NVIC_EnableIRQ(TC0_IRQn);

    //Wavesel on rising edge with timer clock1 (42MHz)
    REG_TC0_CMR0 = TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1;

    //Enabel RC compare interrupt
    REG_TC0_IER0 = TC_IER_CPCS;

    //Set the timer register c 
    REG_TC0_RC0 = SPEED(200000);  //SPEED() converts us to clock ticks

    //Start timer
    REG_TC0_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKEN;
}

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

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// INTERRUPT HANDLERS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void TC0_Handler()
{
  // We need to get the status to clear it and allow the interrupt to fire again
  REG_TC0_SR0;

  //get curretn pin state (REG_PIOB_ODSR & PIO_PB27) and invert it. AND this result with the pin bit to only set pin 27 to high/low
  REG_PIOB_ODSR = (~(REG_PIOB_ODSR & PIO_PB27)) & PIO_PB27;

  if( interruptCtr++ >= 6 )
  {
    interruptCtr = 1;
    S = !S; // are we flashing S or O
    if( S ) // set time till next interrupt
      REG_TC0_RC0 = SPEED(200000); //0.2 seconds
    else
      REG_TC0_RC0 = SPEED(1000000); //1 second
    }
}

With this code you should be able to increase the switching frequency to theoretically 42MHz. But due to the time the interrupt needs to run it will be reduced to something about 10MHz or less but even this is much higher than your 334KHz.

0

I need to update with freq 1MHz. ...No luck.

no kidding.

think about interrupt overhead. and how much it takes away the processing power if you interrupt at 1 million times a second.

think DMA if available on your chip. If not, think a faster chip or compromise on your 1Mhz expectation.

  • Indeed, unless the processor needs to do essentially nothing else, using DMA to feed the DAC is probably necessary. – Chris Stratton Jul 21 '17 at 15:09
-1

This works

int unsigned A[2500];
int unsigned S;
void setup()
{
  Serial.begin(9600);
  //pinMode(30, OUTPUT);
  REG_PIOB_OWER = 0xFFFFFFFF;     // Enable PORT B
  REG_PIOB_OER =  0xFFFFFFFF;     // Set PORT B as output port
  REG_PIOD_OWER = 0xFFFFFFFF;     // Enable PORT D
  REG_PIOD_OER =  0xFFFFFFFF;     // Set PORT D as output port
  REG_PIOC_OWER = 0xFFFFFFFF;     // Enable PORT C
  REG_PIOC_OER =  0xFFFFFFFF;     // Set PORT C as output port
  /* 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_CLOCK1); 
  TC_SetRC(TC2, 1, 42);// 1 MHz 
  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()
{
int unsigned MSBs = 0;
int unsigned LSBs = 0;
int unsigned C = 0;
int unsigned B = 0;
int unsigned i = 0;
//int unsigned long B1 = 0;
//int unsigned B2 = 0;
int unsigned MSBMask = B11111111;
MSBMask << 8;

 //Serial.println(base, DEC);
//delay(100);
 //Serial.println(ratio, DEC);
//delay(100);
for(i=0; i<2500; i++)
{
  //C = 20000/2*sin(2*PI*(90000/0.0025*i+10000)*i);
  C = 20000*sin(2*PI*i);
 // A[i] = sin(2*PI*(10+90/25*i)*1/1000);
   //A[1] = sin(2*PI*(10+90/25*i)*1/1000);

  MSBs = C >> 8;
  LSBs = C & MSBMask;
   //Serial.println(C, BIN);
  //Serial.println(MSBs, BIN);
 // Serial.println(LSBs, BIN);
MSBs = MSBs << 12;
LSBs = LSBs << 1;
B = MSBs+LSBs;
A[i] = B;
 //Serial.println(B, BIN);
 //Serial.println(A[i], BIN);
//delay(100);
}

NVIC_EnableIRQ(TC7_IRQn);
while(1){}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// INTERRUPT HANDLERS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void TC7_Handler()
{
 TC_GetStatus(TC2, 1);// We need to get the status to clear it and allow the interrupt to fire again
  //int i=0;
  REG_PIOD_CODR = 0x1 << 9;// clear pin 30 on due
 // REG_PIOD_CODR = 0x1 << 10;// clear pin 32 on due


REG_PIOC_ODSR = A[S];//set outout pins to array entry

REG_PIOD_SODR = 0x1 << 9;// set pin 30 on due, commanding DAC to take commended Analog output,
   S++;
   if(S>2500){S=0;}



}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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