[samc][tc] counter don't count - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: May 22, 2022, 06:53:02 pm
content: I am writing some setup code for the TC counter. I am using an explained dev board and the mplabx debugger. I can read register values, but the count register just sits at "0", should I be able to see the count when I pause the running session? If I load a value into the counter in the code I can read this in the debugger so I conclude that it's not ticking.my setup code is thus:Code:#ifndef PUBLICTC_H_#define PUBLICTC_H_#include <inttypes.h>/************************************************************TC0 & TC1 *********************************************************/#define TC_0_1_clock_gen_source 0 // select the GCLK generator the counters are connected to 0-8/************************************************************ TC0 ************************************************************/#define TC0_enable 1 // enable counter#define TC0_mode_8 // 16 bit 8 bit 32 bit mode, 32 bit mode will link this even numbered master counter to the next counter as a slave.#define TC0_prescaler 0x0 // 0-7 divides source GCLK by 1, 2, 4, 8, 16, 64, 256, 1024#define TC0_waveform_generation_mode 0x2 /* 35.7.2.9 Waveform Generation ControlValue | Name | Operation | Top Value* | Output Waveform | Output Waveform| | | | on Match | on Wraparound--------------------------------------------------------------------------------0x0 | NFRQ | Normal frequency | PER* / Max | Toggle | No action0x1 | MFRQ | Match frequency | CC0 | Toggle | No action0x2 | NPWM | Normal PWM | PER* / Max | Set | Clear0x3 | MPWM | Match PWM | CC0 | Set | Clear*PER is only available in 8 bit mode in other modes the max value of the counter is used */#define TC0_CH0_Compare_capture_value 127 // Comp/Capt value. Also used as the counter top value setup in 16/32 bit Match frequency/PWM generation.#define TC0_CH1_Compare_capture_value 0 // Comp or Capt value depending on mode.#define TC0_period_value 250 // 8 bit mode top count#define TC0_prescaler_preseync 2 /*0x0 GCLK Reload or reset the counter on next generic clock0x1 PRESC Reload or reset the counter on next prescaler clock0x2 RESYNC Reload or reset the counter on next generic clock. Reset the prescaler counter*/#define TC0_interrupt_overflow_en 0 // 1 to enable counter overflow interrupt.#define TC0_interrupt_match_ch0_en 0 // 1 to enable the compare match interrupt on channel 0#define TC0_interrupt_match_ch1_en 0 // 1 to enable the compare match interrupt on channel 1#define TC0_interrupt_error_en 0 // 1 to enable error interrupt/** End user setup *******************************************************************************************************************************************/static inline void clr_int_flags_TC0() // clears interrupt flag to prevent re-entering of routine on exit.{REG_TC0_INTFLAG = 0xFF ;}static inline void init_TC0( ) // set up counter as per public definitions, does not enable system interrupts only trigger flags{#ifdef TC0_mode_8#define TC0_mode 1#endif#ifdef TC0_mode_16#define TC0_mode 0#error TC0 16 bit mode has no code yet#endif#ifdef TC0_mode_32#error TC0 32 bit mode has no code yet#define TC0_mode 2#endif#ifndef TC0_mode#error TC0 mode invalid#endifREG_MCLK_APBCMASK |= 1 << 12 ; // Enable counter on system busREG_TC0_CTRLA = TC0_mode << 2 ; // MODE MUST BE SET FIRST, COUNTER WILL BE ENABLED LATERREG_TC0_WAVE = TC0_waveform_generation_mode ; // configure waveform generation modeREG_TC0_CTRLA |= TC0_prescaler << 8 | TC0_prescaler_preseync << 4 ;REG_TC0_INTENSET = TC0_interrupt_overflow_en << 0 | TC0_interrupt_match_ch1_en << 5 | TC0_interrupt_match_ch0_en << 4 | TC0_interrupt_error_en << 1 ; // enable interrupts#ifdef TC0_mode_8REG_TC0_COUNT8_CC0 = TC0_CH0_Compare_capture_value ; // capture compare valueREG_TC0_COUNT8_CC1 = TC0_CH1_Compare_capture_value ; // capture compare valueREG_TC0_COUNT8_PER = TC0_period_value ; // period value or top of counter#endif#ifdef TC0_mode_16REG_TC0_COUNT16_CC0 = TC0_CH0_Compare_capture_value ; // capture compare valueREG_TC0_COUNT16_CC1 = TC0_CH1_Compare_capture_value ; // capture compare value#endif/* enable TC0 interrupt channel in NVIC */#if (TC0_interrupt_overflow_en || TC0_interrupt_match_ch0_en || TC0_interrupt_match_ch0_en || TC0_interrupt_error_en)(*(volatile uint32_t*)(0xE000E100)) = (0x1 << 20) ; // enable TC0 channel in NVIC#endifREG_TC0_CTRLA |= 0x1 << 1 ;// counter enable at endREG_GCLK_PCHCTRL30 = TC_0_1_clock_gen_source | 1 << 6 ; // connect counter to generic clock generator}#if (TC0_period_value && TC0_mode != 1)#warning period value will not be used unless counter TC0 is in 8 bit mode#endif#endif /* PUBLICTC_H_ */

author: cv007
author_rank: Frequent Contributor
timestamp: May 23, 2022, 03:11:35 am
content: Try enabling the gclck source before enabling tc. Also probably a good idea to disable (or reset, which will disable also) tc before doing any tc setup, even if you most likely only setup once (if done the second time in the future, you will wonder why it does not work so may as well take care of that now).Simple example for a samd10 on an xplained mini-Code:#include <sam.h>#include <stdbool.h>//samd10 xplained mini//'inline' to eliminate unused function warningstypedef struct { port_group_registers_t* pt; uint8_t pn; bool onval; } pin_t;static const pin_t LED = { &PORT_IOBUS_REGS->GROUP,9,1 }; //led, PA09, high=oninline static voidpin_toggle (pin_t p){p.pt->PORT_DIRSET = 1<<p.pn;p.pt->PORT_OUTTGL = 1<<p.pn;}//tc clock sourcestypedef enum { TC_GCLK0,TC_GCLK1,TC_GCLK2,TC_GCLK3,TC_GCLK4,TC_GCLK5 }tc_gclk_t;inline static voidtc_gclksel (tc_registers_t* tcN, tc_gclk_t e){uint8_t id; //get clk id from tcNif( tcN == TC1_REGS ) id = 0x12; //same tc1,tc2 same on samdelse if( tcN == TC2_REGS ) id = 0x12; //etc.GCLK_REGS->GCLK_CLKCTRL = (1<<14)|(e<<8)|id; //clken,gclkN,tcN}inline static voidtc_busclk (tc_registers_t* tcN, bool tf){ //tcN already limited by manufacturer's defines, so should not see an invalid tcNuint8_t tcbm; //tcN clock bitmaskif( tcN == TC1_REGS ) tcbm = (1<<6); //TC1else if( tcN == TC2_REGS ) tcbm = (1<<7); //TC2if(tf) PM_REGS->PM_APBCMASK |= tcbm; else PM_REGS->PM_APBCMASK &= ~tcbm;}//tc functions for any mode (8/16/32)inline static void //also enables bus clocktc_reset (tc_registers_t* tcN) { tc_busclk(tcN,true); tcN->COUNT8.TC_CTRLA = 1; }inline static void //set ctrla to v, we set the enable bittc_enable (tc_registers_t* tcN, uint16_t v) { tcN->COUNT8.TC_CTRLA = v|2; }inline static bool //also clears if settc_isOvf (tc_registers_t* tcN) { uint8_t v = tcN->COUNT8.TC_INTFLAG & 1; tcN->COUNT8.TC_INTFLAG = v; return v; }typedef enum { TC_COUNT16, TC_COUNT8, TC_COUNT32 }tc_mode_t;typedef enum { TC_DIV1,TC_DIV2,TC_DIV4,TC_DIV8,TC_DIV16,TC_DIV64,TC_DIV256,TC_DIV1024 }tc_prescale_t;//tc count8 mode functionsinline static voidtc_per8 (tc_registers_t* tcN, uint8_t v) { tcN->COUNT8.TC_PER = 0xFF; } //count8 mode//init tc, specific modesstatic voidtc_NFRQ8 (tc_registers_t* tcN, tc_gclk_t gclk, tc_prescale_t pre, uint8_t period){//reset/disable before changingtc_reset( tcN ); //will also enable bus clocktc_gclksel( tcN, gclk );tc_per8( tcN, period );tc_enable( tcN, (TC_COUNT8<<2)|(pre<<8) );}#define TC1 TC1_REGS //a nicer name to useintmain (){tc_NFRQ8( TC1, TC_GCLK0, TC_DIV8, 0xFF ); //TC1,GCLK0,div8,per=0xFFfor( int n = 0; ;n++ ){while( ! tc_isOvf(TC1) ){}if( n >= 1000000/256/8/2 ){ n = 0; pin_toggle(LED); } //1Hz}}This example will fail (no led blink) if the gclk source is set after the tc is enabled. I'm not sure what version these defines are, so may look different, and of course a samd is different than a samc so things are somewhat different, but still similar enough.Also note that these functions are designed to work for any tc instance, so you do not end up with a truckload of defines for each instance. You can also create specific init functions to lessen the need to pass info in (tc_init_NFRQ8 is nfrq/count8). These functions are static, so will be optimized, and inline so if any are unused you do not get a warning about unused functions. So you move the creation of defines to specify things you want over to the init arguments instead. Do whatever you want, though.

author: Simon
author_rank: Unknown
timestamp: May 23, 2022, 08:54:10 am
content: Yes the clock source is not mentioned by then I don't think this has much baring, it's just the ticking that the counter will count along with at least one other source. It's like once the counter is setup and enabled it will count, when these pulses arrive should be irrelevant and they may come from multiple sources.

author: Simon
author_rank: Unknown
timestamp: May 23, 2022, 12:46:52 pm
content: I got it to work. I set up a project with my colleague using the configurator, this also showed in the debugger that the count value was always 0 yet we had a PWM. So I spent nearly 2 days chasing a problem that was not there. It was my confusion over which pin to output the PWM on and some other code I had written but not thoroughly checked that was the issue.

author: Simon
author_rank: Unknown
timestamp: May 24, 2022, 07:29:07 am
content: hm, so I set it to frequency mode (mode 0), it will use the period register to generate the timing, but if I set the compare match value higher than the period it stops the output from working.

author: ajb
author_rank: Super Contributor
timestamp: May 24, 2022, 05:35:10 pm
content: Quote from: Simon on May 23, 2022, 12:46:52 pmAre you using Atmel Studio? I spent a bunch of time beating my head against the Analog Comparator on a project before I got suspicious of the register values shown in the debugger, it turns out that it will frequently just show absolute garbage which is really frustrating. It seems like for whatever reason the debug session will fail to read some registers and either display 0 or an old value, or sometimes it will read back all 0xAAAAAAAA. The latter case is easy to spot if you're looking at the whole word, but not if you're looking at the bitfield values