Reading the TC_CV Counter of Timer 1 channel 0 ?

Hi

I studied a piece of code I found on the net and tried to document every possible line I could find info on (with info + datasheet reference). That way I have allready a better understanding of what the code does

Code here

volatile boolean ledOn;

void TC3_Handler() //TC1 ch 0
{
   TC_GetStatus(TC1, 0); 
   digitalWrite(13, ledOn = !ledOn);
}

void setup()
{
  pinMode(13, OUTPUT);

  pmc_set_writeprotect(false);  //Turn off write protection on Power Management Controller
   
  pmc_enable_periph_clk(TC3_IRQn);  // Enable the  peripheral clock by IRQ 
   
  TC_Configure(TC1, 0, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK4); // Configure the timer on its specified channel. Timer is stopped after config and must be started in code.
  // Used parameters
  // ---------------
  // TC_CMR_WAVE = Set the operating mode to WaveForm mode (datasheet p. 862)
  // TC_CMR_WAVSEL_UP_RC =  UP counting mode with automatic trigger on RC compare. When RC is reached the counter also resets (datasheet p. 884)
  // TC_CMR_TCCLKS_TIMER_CLOCK4 = Set counting speed to Master Clock divided by 128 (datasheet p. 884)
   
  uint32_t rc = VARIANT_MCK / 128 / 1;

  TC_SetRA(TC1, 0, rc >> 1); // Set the counter value where the output of  TIOA goes high in register RA (datasheet p.867)
   
  TC_SetRC(TC1, 0, rc); //Set the counter value where the output of TIOA goes low and the counter is reset in register RC (datasheet p.867)
   
  TC_Start(TC1, 0);  // Start the Timer Counter which was stopped after the TC_configure
   
  TC1->TC_CHANNEL[0].TC_IER=  TC_IER_CPCS | TC_IER_CPAS;
   
  TC1->TC_CHANNEL[0].TC_IDR=~(TC_IER_CPCS | TC_IER_CPAS);
   
  NVIC_EnableIRQ(TC3_IRQn); // Enables the interrupt so that in this case TC3_Handler can be called on overflow of the counter

  Serial.begin(250000); // opens serial port, sets data rate to 250000 bps
}

void loop()
{
  Serial.println("print current counter value of TC1 channel 0 here");
}

This code inits a counter on Timer 1 channel 0 and lets it count up until it reaches the value in register RC, after which it resets and the TC3_Handler() interrupHandler is called.

This is tested code and works very fine.

But now I would like to be able to read the value of the counter itself. It is a 32bits unsigned integer.

Would anybody know how I can READ the current value in the Timer Counter Value Register. On page 879 of the datasheet there is info about the register mapping of this TC_CV register, but I have no clue how to read it.

Also, since it is READ ONLY, I wonder how I can RESET the TC_CV register by code so it is 0 when I want it ? Maybe it is 0 on start? or can I do anything to set this value somehow to 0 ?

Kind regards,

Bart

You could see this thread :

timer library for arduino due - Arduino Due - Arduino Forum from #3

That was a very helpful piece of information , thanks,

I now added this to my loop()

int *TC_CV = (int *)(0x40084010); //for timer 1 channel 0 (see datasheet p.888)
while (true){
TC_Start(TC1, 0); // Start the Timer Counter which was stopped after the TC_configure
Serial.println(*TC_CV);
}

Result: The timer counts, I can print its value

Now 1 more small question about efficiency.

Can somebody tell me what this exactly does ? I see a variable for asterisk in front of it. Does this have to do something with memory pointers ? Is this the most optimal way to read the register or can I modify the INT part to indicate it is an unsigned 32 bits integer ?

PS. I also discovered that TC_Start(TC1,0) does effectively reset the counter, so that part is also solved. Now I only need to know if this can be made more efficient or if this is allready the most efficient way.

The * does mean it is a memory pointer. What you've done with the line

int *TC_CV = (int *)(0x40084010);

Is create a pointer to the memory address 0x40084010 and that pointer points to a 32 bit signed integer. You most certainly can use an unsigned integer if that is more appropriate. I like to use the standard C integer size types like so:

uint32_t *TC_CV = (int *)(0x40084010);

That way anyone looking at the code cannot mistake the size of the integer nor that it is unsigned.

When you have a variable that is a pointer like this (and thus stores a memory address) then * will cause the compiler to give you the value at that memory address instead of giving you the actual value of the variable (a memory address). It is called dereferencing. This is pretty much the most efficient means of reading a hardware register.

OK so now I know how to read the value.

I changed my code a little to remove all the interrupt stuff (don't need it and if not needed, then it doesn't have to occupy clockcycles).

I now have a working code that does nicely loop, start the timer (which is then 0) then reads the counter directly after starting and it shows a value of 8, so when I'm counting at 42MHz, that means the cpu has passed 16 clock cycles. Quite acceptable since my code takes cycles....

int startTime;
int endTime;
int *TC_CV = (int *)(0x40084010); //for timer 1 channel 0 (see datasheet p.888)

void setup()
{
  pmc_set_writeprotect(false);  //Turn off write protection on Power Management Controller
  pmc_enable_periph_clk(TC3_IRQn);  // Enable the  peripheral clock by IRQ 
  TC_Configure(TC1, 0, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1); // Configure the timer on its specified channel. Timer is stopped after config and must be started in code.
  // Used parameters
  // ---------------
  // TC_CMR_WAVE = Set the operating mode to WaveForm mode (datasheet p. 862)
  // TC_CMR_WAVSEL_UP_RC =  UP counting mode with automatic trigger on RC compare. When RC is reached the counter also resets (datasheet p. 884)
  // TC_CMR_TCCLKS_TIMER_CLOCK1 = Set counting speed to Master Clock divided by 2 (datasheet p. 884)
  uint32_t rc = VARIANT_MCK / 2;
  TC_SetRA(TC1, 0, rc >> 1); // Set the counter value where the output of  TIOA goes high in register RA (datasheet p.867)
  TC_SetRC(TC1, 0, rc); //Set the counter value where the output of TIOA goes low and the counter is reset in register RC (datasheet p.867)
  TC_Start(TC1, 0);  // Start the Timer Counter which was stopped after the TC_configure
  Serial.begin(250000); // opens serial port, sets data rate to 250000 bps
}

void loop()
{
  while (true){
    TC_Start(TC1, 0);  // Start the Timer Counter which was stopped after the TC_configure
    startTime=*TC_CV;
    Serial.println(startTime);
    endTime=*TC_CV;
    Serial.println(endTime);
    Serial.println(endTime-startTime);
    Serial.println("--");
  }
}

Now I measure in the code above the startTime, then I print that value (which is 8 :wink: ) to serial and then I measure counter again in endTime. The result is that the startTime contains 8 and endTime contains 5025. A total of 5025-8 = 5017 cycles have passed by calling the serial write and setting endTime :wink:

That makes me thinking that I could use this technique for making a delay.

So I changed the following in my loop (ommited setup since it didn't change)

void loop()
{
  while (true){
    TC_Start(TC1, 0);  // Start the Timer Counter which was stopped after the TC_configure
    startTime=*TC_CV;
    while(*TC_CV < startTime+50000)
    {
     //do nothing
    }
    endTime=*TC_CV;
    Serial.println(endTime);
    Serial.println(endTime-startTime);
    Serial.println("--");
  }
}

Strangely, when I execute this, my code never gets to the Serial.println anymore. Nothing is written to serial so I expect the code never reaches that point.
That would mean that the code is hanging inside that loop.

I thought that this while loop actually meant that I wanted the cpu to wait until the current *TC_CV value is bigger than 50008 (8+50000) , so in normal english I thought that this loop would wait for aprox 50000 COUNTS. At 42MHZ counting that would be around 1.2ms

No luck, it doesn't do anything.

Do I do something wrong there ?

OK, found the solution myself, I'll post the code below for future reference and to help other people.

uint32_t startTime;
uint32_t wantedTime;
uint32_t endTime;
uint32_t teller = 0;
uint32_t *TC_CV = (uint32_t *)(0x40084010); //for timer 1 channel 0 (see datasheet p.888)

void setup()
{
  pmc_set_writeprotect(false);  //Turn off write protection on Power Management Controller
  pmc_enable_periph_clk(TC3_IRQn);  // Enable the  peripheral clock by IRQ 
  TC_Configure(TC1, 0, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1); // Configure the timer on its specified channel. Timer is stopped after config and must be started in code.
  // Used parameters
  // ---------------
  // TC_CMR_WAVE = Set the operating mode to WaveForm mode (datasheet p. 862)
  // TC_CMR_WAVSEL_UP_RC =  UP counting mode with automatic trigger on RC compare. When RC is reached the counter also resets (datasheet p. 884)
  // TC_CMR_TCCLKS_TIMER_CLOCK1 = Set counting speed to Master Clock divided by 2 (datasheet p. 884)
  uint32_t rc = VARIANT_MCK / 2;
  TC_SetRA(TC1, 0, rc >> 1); // Set the counter value where the output of  TIOA goes high in register RA (datasheet p.867)
  TC_SetRC(TC1, 0, rc); //Set the counter value where the output of TIOA goes low and the counter is reset in register RC (datasheet p.867)
  TC_Start(TC1, 0);  // Start the Timer Counter which was stopped after the TC_configure
  Serial.begin(250000); // opens serial port, sets data rate to 250000 bps
}

void loop()
{
  while (true){
    TC_Start(TC1, 0);  // Start the Timer Counter which was stopped after the TC_configure
    startTime=*TC_CV;
    wantedTime=startTime+100000;
    while(*TC_CV < wantedTime)
    {
     teller+=1;
    }
    endTime=*TC_CV;
    Serial.println(startTime);
    Serial.println(endTime);
    Serial.println(endTime-startTime);
    Serial.println("--");
  }
}

Looks like the WHILE loop can not be empty or something goes wrong . I now waste 1 or more clockcycles and everything is fine :wink:

It's better to use an interrupt for something like this.

There is a pretty damn good reason why I want to read directly and not using interrupt.

The question I had was actually how to read it and that is solved and usable now.

Interrupts are superb things, but in an environment where every clock cycle counts, I opted for this to use the loop and not the interrupt.

uint32_t endtime = 10000;
uint32_t *TC_CV = (uint32_t *)(0x40084010); //for timer 1 channel 0 (see datasheet p.888)

void setup()
{
  pmc_set_writeprotect(false);  //Turn off write protection on Power Management Controller
  pmc_enable_periph_clk(TC3_IRQn);  // Enable the  peripheral clock by IRQ 
  TC_Configure(TC1, 0, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1); // Configure the timer on its specified channel. Timer is stopped after config and must be started in code.
  uint32_t rc = VARIANT_MCK / 2;
  TC_SetRA(TC1, 0, rc >> 1); // Set the counter value where the output of  TIOA goes high in register RA (datasheet p.867)
  TC_SetRC(TC1, 0, rc); //Set the counter value where the output of TIOA goes low and the counter is reset in register RC (datasheet p.867)
  TC_Start(TC1, 0);  // Start the Timer Counter which was stopped after the TC_configure
  Serial.begin(250000); // opens serial port, sets data rate to 250000 bps
}

void loop()
{
if(*TC_CV>=endtime){
TC1->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG;
Serial.println("bingo");
}

//do other work here


}