SAM D10 clock generator and TCC0 problems

Go To Last Post
31 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello! 

 

Normaly, my generic clock generators of my SAMD10 D14work very fine with my own drivers (no ASF) for TC1, TC2,

ADC, SERCOM. But when I program TCC0, it does not show any life. I suspected no clock at its prescaler, and I

seem to be right. Clock generator 0 with XOSC works well, as all the drivers cooperate with it. When I enable

CLKTRL.CLKEN=1, and I read back this register, this bit is set to 0. If I use any other

CLKTRL.ID, which is not 0x11, CLKTRL.CLKEN=1 is correctly shown. Why???

 

Even if I enable CLKEN at the beginning, high level is reset, due to the ID=0x11, why?

 

Desperate regards,

 

Uwe

 

This topic has a solution.
Last Edited: Tue. Mar 6, 2018 - 05:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I add a short code example. To keep it short here, I do not use structures, bitfields and symbolic constants!!!!!

 

void test_TCC0(void)

{

     uint8_t *pointer8;

     uint16_t *pointer16,readback;

 

     //GENCTRL after reset has active GENERIC_CLKGEN0 and OSC8M (/8), => good preconditions

   

     pointer16=(uint16_t *)0x40000C02;             //address of GENERIC_CLKCTRL register

     *pointer16=0x4011;                                      //ID=0x11 (TCC0), source=GENERIC_CLKGEN0=0, CLKENABLE=1               

     

     pointer8=(uint8_t *)0x40000C01;                 //address of sync register           

     while((*pointer8 & 0x80) == 0x80){}             //sync

   

    readback=*pointer16;                                   //read back, 0x0011 is displayed, => clock was disabled by chip

                                                                         //Does ID=0x11 really exist?

}

Last Edited: Wed. Feb 28, 2018 - 03:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You have to have the pointers volatile (well, for the while loop read anyway). But I don't actually get the need to reduce the code size to the point you can't even use the register definitions.

I never had to read GENCTRL so I don't know if this is significant, you are not reading it according to the datasheet:

To read the CLKCTRL register, first do an 8-bit write to the CLKCTRL.ID bit group with the ID of the generic clock whose configuration is to be read, and then read the CLKCTRL register.

What other indication the TCC0 is not clocked is there (i.e.,  how do you determine "it does not show any life") ?
Is the PM configured?

/Lars 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just tried this on a SAMD21. I see the same thing that you are describing, no CLKEN in the readback for TCC0. But other than that TCC0 works just fine.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tried the "8-bit write to the CLKCTRL.ID" that Lars suggested. No difference. (It looks that there are others like that. For example, AC_GCLK_ID_ANA (again, on a SAMD21)).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello Lars!

 

Wow, life comes up here finally, thanks! I made the pointers volatile, and you are right with the

write byte access, but the result is ALWAYS the same, clock is disabled when reading back. And of course,

I switched on PM with those 2 lines:

 

pointer16=(uint16_t *)APBCMASK;

*pointer16=*pointer16 | 0x0020;              //TCC0 clock on

 

The basic timer (register COUNT) shoud count, but it remains 0. No wonder, if no clock reaches the prescaler.

I introduce the whole function now:

 

/***********************************************************************************************************************************/

void init_TCC0(void)

{

volatile uint8_t *pointer8,*p8;

volatile uint16_t *pointer16,*p16,test;

volatile uint32_t *pointer32,*p32;

struct CTRLA_TCC0s data;

struct CTRLBSET_TCC0s value;

struct CLKCTRLs datas;

 

datas.ID=0x11;                     //clock for TCC0

datas.RES6_7=0;

datas.GEN=CLKGEN0;        //Generic clock generator 0

datas.RES12_13=0;             //reserved bits, always 0

datas.CLKEN=_ENABLE;    //clock enable

datas.WRTLOCK=0;

pointer16=(uint16_t *)CLKCTRL;

p16=(uint16_t *)&datas;

*pointer16=*p16;

sync_clkctrl_bus();

 

pointer8=(uint8_t *)CLKCTRL;              //read back for test only   

*pointer8=0x11;

sync_clkctrl_bus();

test=*pointer16;                                     //result is 0x0011 (:-(

sync_clkctrl_bus();

 

pointer16=(uint16_t *)APBCMASK;

*pointer16=*pointer16 | 0x0020;              //TCC0 clock on

 

pointer32=(uint32_t *)EVTCTRL;

*pointer32=0x0000;                                  //events disabled

 

data.SWRST=NO;

data.ENABLE=NO;

data.RES2_4=0;

data.RESOLUTION=0;            //no dithering

data.RES7=0;

data.PRESCALER=DIV16;

data.RUNSTBY=YES;

data.PRESYNC=_GCLK;

data.ALOCK=NO;

data.RES15_23=0;                 //reserved bits=0

data.CPTEN0=YES;               //use capture channel 0

data.CPTEN1=NO;

data.CPTEN2=NO;

data.CPTEN3=NO;

data.RES28_31=0;

pointer32=(uint32_t *)CTRLA_TCC0;

p32=(uint32_t *)&data;                       

*pointer32=*p32;

//sync_tcc0_bus(2);       //sync buses not necessary, syncing is not the  solution

 

value.DIR=UP;  

value.LUPD=0;                                 //always update buffers

value.ONESHOT=YES;                    //=1

value.IDXCMD=NO;                         //no ramp command,=0

value.CMD=0;

pointer8=(uint8_t *)CTRLBCLR_TCC0;     

p8=(uint8_t *)&value;

*pointer8=*p8;

sync_tcc0_bus(4);                              //sync buses, wait until synced 

 

pointer32=(uint32_t *)COMP_CAP0;  //load compare register 0

*pointer32=250*125;                          //20ms interrupts

sync_tcc0_bus(256);                         //sync buses, wait until synced */

 

pointer32=(uint32_t *)Int_Enable_Set; //interrupt on match capture channel 0

*pointer32=0x00010000;

enable_nvic_int(TCC0_IRQ);

 

data.ENABLE=YES;                              //now enable TCC0

pointer32=(uint32_t *)CTRLA_TCC0;

p32=(uint32_t *)&data;

*pointer32=*p32;

sync_tcc0_bus(2);

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do you count events? I want to count clocks from my clock generator (=timer function)!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is really hard to follow your code with all the hardcoded constants. As for "The basic timer (register COUNT) shoud count", how do you read it? It is Read-Synchronized and you need a TCC_CTRLBSET_CMD_READSYNC command to read it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks ezharkov for your hint TCC_CTRLBSET_CMD_READSYNC, I did not use. I wondered about these

commands, READSYNC is nowhere described! Must I also use the start/retrigger command to start the timer or is it

used only after a stop cmd? In past, I used this command without success.  The following function is wrong, as I do

not use the TCC_CTRLBSET_CMD_READSYNC yet. I will try it out now, but I do not expect the counter counting, as

no compare interrupt occurs. Even a overflow interrupt, when activated in the past, was missing. 

 

uint32_t read_tcc0(void)

{

uint32_t *pointer32,counter;

pointer32=(uint32_t *)COUNT_TCC0;

counter=*pointer32;

sync_tcc0_bus(16);     //sync buses, wait until synced

return counter;

}

 

Must TCC_CTRLBSET_CMD_READSYNC be used before reading the COUNTER register?

Last Edited: Wed. Feb 28, 2018 - 08:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is how I read it:

static Uint32 readCount(void) {
  Tcc* h = TCC0;
  while (h->SYNCBUSY.bit.CTRLB);
  while ((h->CTRLBSET.reg & TCC_CTRLBSET_CMD_Msk) != TCC_CTRLBSET_CMD_NONE);
  h->CTRLBSET.reg = TCC_CTRLBSET_CMD_READSYNC;
  while (h->SYNCBUSY.bit.COUNT);
  return h->COUNT.reg;
}

Edit: Well, without all those html br commands, of course...
Edit: Got rid of br's finally!

Last Edited: Wed. Feb 28, 2018 - 08:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, I will use this example, its algorithm, hand coded, looking much much better than the ASF jungle :-)

 

Last Edited: Wed. Feb 28, 2018 - 08:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello Ezharkov!

 

I have used your idea behind your code, and this is my legal version:

 

/***********************************************************************************************************************************/

uint32_t read_tcc0(void)                                  //working well since March,6th, 2018

{

    uint32_t *pointer32;

    uint8_t *pointer8;

 

    pointer32=(uint32_t *)COUNT_TCC0;           //initialize pointers

    pointer8=(uint8_t *)CTRLBSET_TCC0;

 

    sync_tcc0_bus(CTRLB_SYNC);    

    while((*pointer8 & _CMD)!=NOCMD){}           //is a command carried out? 

    *pointer8=READSYNC;                                   //force COUNT read synchronization

    

    sync_tcc0_bus(COUNT_SYNC);                     //sync buses, wait until synced, obsolete??

    return *pointer32;                                             //read TCC0 counter

}

 

Your nice code wakes up a question to the cumbersome synchronization (an ATMEL illness?). I synchronize after I

use a register with synchronization duty. So I do not need to synchronize it next time, before I use it,

because no one else access it. You do it the other way round. You synchronize before using such a register. 

 Your advantage is faster code, because while carrying out next code, synchronization still goes on in the background.

Has my way any other critical disadvatage (ok, I waste time)?

Last Edited: Tue. Mar 6, 2018 - 05:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

After correcting the read_counter function, the result is the same as expected (no compare interrupt), it

does not count. Any idea apart from the ID=0x11, which disabled clock in  GENERIC_CLKCTRL. What is

also very quirky, there are no control bits to distinguish between counter and timer. Perhaps I am in counter

mode and expect clocks at an input.....

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
data.CPTEN0=YES;               //use capture channel 0

Don't enable capture if you need compare.

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Counter vs timer is all about the GCLK generator setup, i.e., to have a counter you would setup a generator with GCLKIN source.

Edit: event based counting is possible also so it's not all about the GCLK.

/Lars

Last Edited: Thu. Mar 1, 2018 - 02:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lars, I know, compare refers to timer, and capture to external clocks. But SAMD uses the same registers for compare and capture,

but where can I tell some control bits, I want to compare? I have prepared a generic clock driver  as I use in other successful timers! Have a look

at comment #6, where I enable capture. Even if it is deactivated, register COUNT remains 0. Where is my fault in comment #6, my code?  

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Uwe60 wrote:
Lars, I know, compare refers to timer, and capture to external clocks.

No, there is no such relation, you can use an external clock to time something or input capture some other signal.

These bits are used to select the capture or compare operation on channel x...
Writing a one to CAPTENx enables capture on channel x.
Writing a zero to CAPTENx disables capture on channel x.

In my post #14 is what I consider the wrong setup for compare, don't set this bit if you need the compare.

/Lars

Last Edited: Thu. Mar 1, 2018 - 04:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, but even if I reset this bit, the counter remains 0. Which bit else for capture must be set? There is no alternative!

I quote page 652 of SAMD!0 technical manual: "Bits 27:24 – CPTENx [x=3..0]: Capture Channel x Enable
These bits are used to select the capture or compare operation on channel x."

Last Edited: Thu. Mar 1, 2018 - 04:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For compare this bit is 0 for capture 1, there is no need for another bit. But it also has nothing to do with the counter running or not running. That is something else. Find some other example here or from ASF that gets the TCC running and compare the register settings.
/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is a TCC example:
https://community.atmel.com/foru...
It's a capture example but at least for sure has the TCC running.

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Lars, I am analyzing it very thoroughly! The first result is, that clock at TCC exists, although the generic click driver says disabled.

When I used the different ID of your example, I could not synchronize ctrlbset register, it ways always busy. I have the feeling, that everbody

uses ASF, as nobody can answer detailed questions. That's the consequence, when complete drivers are used, as nobody knows, what is

going on in detail.... 

 

Last Edited: Fri. Mar 2, 2018 - 03:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well the example is not ASF, if it uses other ids is may be because it is for D21.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

deleted

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello Lars!

 

Reverse engineering is now the tried solution to find out, what is missing in the datasheet.

Therefore  use an ATMEL application note

 

http://ww1.microchip.com/downloa....

 

They use functions like 

 

static void configure_tcc_callbacks(void)
{
 tcc_register_callback(&tcc_instance, tcc_callback_to_toggle_led,
 TCC_CALLBACK_OVERFLOW);
 tcc_register_callback(&tcc_instance, tcc_callback_to_toggle_led,
 TCC_CALLBACK_CHANNEL_0);
 tcc_register_callback(&tcc_instance, tcc_callback_to_toggle_led,
 TCC_CALLBACK_CHANNEL_1);
 tcc_register_callback(&tcc_instance, tcc_callback_to_toggle_led,
 TCC_CALLBACK_CHANNEL_2);
 tcc_register_callback(&tcc_instance, tcc_callback_to_toggle_led,
 TCC_CALLBACK_CHANNEL_3);
 tcc_enable_callback(&tcc_instance, TCC_CALLBACK_OVERFLOW);
 tcc_enable_callback(&tcc_instance, TCC_CALLBACK_CHANNEL_0);
 tcc_enable_callback(&tcc_instance, TCC_CALLBACK_CHANNEL_1);
 tcc_enable_callback(&tcc_instance, TCC_CALLBACK_CHANNEL_2);
 tcc_enable_callback(&tcc_instance, TCC_CALLBACK_CHANNEL_3);
}

 

 

Of course, it is not compilable with Atmel Studio 7, because Atmel does not explain, what 

libraries must be included (for my Sam D10). I fear, it is ASF code. Can you call me the libraries or

what to to?

 

Kind regards,

 

Uwe

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is ASF code, to try that make a new "GCC C ASF Board Project", select your SAMD10. Then run the ASF wizard and add the TCC.

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you Lars for your good advice! Now I can compile ASF examples with Atmel Studio 7 rather easily!

I used the TCC example basic timer, which does not run yet. I had to prepare the clock for TCC,

which is normally no problem for me, as my other drivers work perfectly. Yet I will also use  an

ASF generic clock driver and I will solder a third sample of my SAM D10D14s on an adapter (100 mil),

so that I can try it out on a board.

Best regards,

 

Uwe

Last Edited: Sun. Mar 4, 2018 - 06:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

                                                                                                       Status

 

After I have downloaded http://ww1.microchip.com/downloa...,

including TCC descriptions for SAMD10/11,21 and so on. I have chosen example 9.3, which is for SAM D21/R21/L21/L22/DA1/C21 Xplained Pro/ SAM D11 Xplained Pro.

Not a single example for my SAM D10. Therefore example 9.3 does not work. The contents of the essential functions registers look fine, generator 1 enabled, clock routed to TCC,

clock for TCC in Power Manager enabled, compare registers initialized and so on. But the COUNT register remains 000000! There must be a small difference between Sam D11 and my

SAM D10, but which one?? It is impossible to find examples dedicated to SAM D10. Has someone a TCC0 code example for SAM D10? As much copy and paste was used when making the datasheet (there are some bset and bclr registers without functional difference), I fear, the SAM D10 manual is partly a copy & paste result of another microcontroller?

Is there any possibility to contact ATMEL as private person or must I buy some 100.000 samples to be supported?  

Desperate regards, 

 

Uwe

Last Edited: Mon. Mar 5, 2018 - 02:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So the LED not blinking or you have a break point in tcc_callback_to_toggle_led and it never gets there? Did you try with generator 0 also?
I seriously doubt there is a difference between D10 and D11 when it comes to the TCC.

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello Lars!

 

A miracle has happened! When you asked, whether the toggle_led_function is called, I set a breakpoint there and was surprised, that the debugger always stopped there, although the counter does not seem to count. But the LED did not flash. I examined some PORT registers and noticed, that the LED port was set as input. After adding two lines of code, the LED is

flashing, see photo of my oscilloscope attached. When using the memory watch function of my ICE debugger, the COUNTER is always displayed as 0, what is impossible. I will install my

read_tcc_counter function. Maybe, only the CTRLBSET function "force COUNT read synchronization" makes the true counter datas visible.

Thanks for your advice to set breakpoint in spite of the seemingly not working counter! I keep you informed!

 

Uwe

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello Lars,

 

the problems have been solved, and I have partly discovered an own goal. I do not dare to mention it,

but you and some other guys helped me, it is your award:

The results are:

 

1. My counter was counting all the time, but watching the registers showed only 000000. I had to believe,

    it is not counting.

 

2. When I was reading the COUNTER with my function, debugging line by line, 000000 was 

    also read. Ok, posting #10 showed me, I did not use the "force COUNT read synchronization".

   Its name is only mentioned in the technical documentation, not described. When I tried out ezharkov

   function in posting #10, it also read 000000 in single step operation. But when the debugger was

   running in delay functions for a while without single steps, the counter started counting, my own goal

   outside these functions. Yes, I know, there is a debug register to tell, go on counting in debug operation.....

   My version of ezharkov function does not work yet,

 

3. Well, and when I used the Atmel example, it pretended not to count, as the output direction of the LED

    was switched as input... Your advice, to use a breakpoint at the LED_toggle function was super, my turnaround!

    A heavy birth of a driver, a very stony way. Thank you for the help of some guys here!! 

    Tomorrow I will check my own code, counting secretly, and remove the bug in the read_counter function, and then I

     will mark the problem as solved!

 

Best regards from Bavaria,

 

Uwe

Last Edited: Tue. Mar 6, 2018 - 05:50 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am getting on slowly. Counting works including PERIOD. But an overflow interrupt is carried out only once. Then something

sets the ONESHOT bit, not me. Resetting it in the interrupt handler does not solve this problem. Debugging in the TCC0_handler

is quirky, because only some lines can be stepped, then a jump occurs to the function, where the interrupt happened.

I reset the interrupt and all other status flags (all FAULTx bits are set, why?) by reading the interrupt flag register and copying

its value back, which resets all set bits. 

 

Addition 6:30 pm: All problems solved. Ok, I admit, the the FAULTx interrupt flags are all set, but they do not bother me. The set Oneshot 

bit was an own goal, when I wanted to force a read synchronization of COUNTER (=4). I copied this value to the whole register instead of

the upper 3 bits, and Oneshot is also 4... Now I can read the counter, and overflow interrups appear frequently. So I can close this theme

now with pride and happyness. Thank you all for your help!!!

Kind regards,

 

Uwe

Last Edited: Tue. Mar 6, 2018 - 05:46 PM