Electronics > Microcontrollers

[samc][tc] counter don't count

(1/2) > >>

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 /* Waveform Generation Control

Value | Name | Operation            | Top Value*  | Output Waveform | Output Waveform
      |      |                      |             | on Match        | on Wraparound
0x0   | NFRQ | Normal frequency     | PER* / Max  | Toggle          | No action
0x1   | MFRQ | Match frequency      | CC0         | Toggle          | No action
0x2   | NPWM | Normal PWM           | PER* / Max  | Set             | Clear
0x3   | 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 clock
0x1 PRESC Reload or reset the counter on next prescaler clock
0x2 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.

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
#ifdef TC0_mode_16
#define TC0_mode 0
#error TC0 16 bit mode has no code yet
#ifdef TC0_mode_32
#error TC0 32 bit mode has no code yet
#define TC0_mode 2
#ifndef TC0_mode
#error TC0 mode invalid

REG_MCLK_APBCMASK |= 1 << 12 ; // Enable counter on system bus   
    REG_TC0_WAVE = TC0_waveform_generation_mode ; // configure waveform generation mode
    REG_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_8
REG_TC0_COUNT8_CC0 = TC0_CH0_Compare_capture_value ; // capture compare value
REG_TC0_COUNT8_CC1 = TC0_CH1_Compare_capture_value ; // capture compare value
REG_TC0_COUNT8_PER = TC0_period_value ; // period value or top of counter

#ifdef TC0_mode_16
REG_TC0_COUNT16_CC0 = TC0_CH0_Compare_capture_value ; // capture compare value
REG_TC0_COUNT16_CC1 = TC0_CH1_Compare_capture_value ; // capture compare value
    /* 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

REG_TC0_CTRLA |= 0x1 << 1 ; // counter enable at end
    REG_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 /* PUBLICTC_H_ */

--- End code ---

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 warnings

typedef struct { port_group_registers_t* pt; uint8_t pn; bool onval; } pin_t;

static const pin_t LED = { &PORT_IOBUS_REGS->GROUP[0],9,1 }; //led, PA09, high=on

                inline static void
pin_toggle      (pin_t p)
                p.pt->PORT_DIRSET = 1<<p.pn;
                p.pt->PORT_OUTTGL = 1<<p.pn;

//tc clock sources

                typedef enum { TC_GCLK0,TC_GCLK1,TC_GCLK2,TC_GCLK3,TC_GCLK4,TC_GCLK5 }
                inline static void
tc_gclksel      (tc_registers_t* tcN, tc_gclk_t e)
                uint8_t id; //get clk id from tcN
                if( tcN == TC1_REGS ) id = 0x12; //same tc1,tc2 same on samd
                else if( tcN == TC2_REGS ) id = 0x12; //etc.
                GCLK_REGS->GCLK_CLKCTRL = (1<<14)|(e<<8)|id; //clken,gclkN,tcN
                inline static void
tc_busclk       (tc_registers_t* tcN, bool tf)
                { //tcN already limited by manufacturer's defines, so should not see an invalid tcN
                uint8_t tcbm; //tcN clock bitmask
                if( tcN == TC1_REGS ) tcbm = (1<<6); //TC1
                else if( tcN == TC2_REGS ) tcbm = (1<<7); //TC2
                if(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 clock
tc_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 bit
tc_enable       (tc_registers_t* tcN, uint16_t v) { tcN->COUNT8.TC_CTRLA = v|2; }
                inline static bool //also clears if set
tc_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 }
                typedef enum { TC_DIV1,TC_DIV2,TC_DIV4,TC_DIV8,TC_DIV16,TC_DIV64,TC_DIV256,TC_DIV1024 }

//tc count8 mode functions

                inline static void
tc_per8         (tc_registers_t* tcN, uint8_t v) { tcN->COUNT8.TC_PER = 0xFF; } //count8 mode

//init tc, specific modes

                static void
tc_NFRQ8        (tc_registers_t* tcN, tc_gclk_t gclk, tc_prescale_t pre, uint8_t period)
                //reset/disable before changing
                tc_reset( tcN ); //will also enable bus clock
                tc_gclksel( tcN, gclk );
                tc_per8( tcN, period );
                tc_enable( tcN, (TC_COUNT8<<2)|(pre<<8) );

                #define TC1 TC1_REGS //a nicer name to use

main            ()
                tc_NFRQ8( TC1, TC_GCLK0, TC_DIV8, 0xFF ); //TC1,GCLK0,div8,per=0xFF
                for( int n = 0; ;n++ ){
                    while( ! tc_isOvf(TC1) ){}
                    if( n >= 1000000/256/8/2 ){ n = 0; pin_toggle(LED); } //1Hz
--- End code ---

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.

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.

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.

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.


[0] Message Index

[#] Next page

There was an error while thanking
Go to full version