Author Topic: [samc][tc] counter don't count  (Read 729 times)

0 Members and 1 Guest are viewing this topic.

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
[samc][tc] counter don't count
« on: May 22, 2022, 06:53:02 pm »
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: [Select]
#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 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.
{
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
#endif

   
REG_MCLK_APBCMASK |= 1 << 12 ; // Enable counter on system bus   
    REG_TC0_CTRLA = TC0_mode << 2 ; // MODE MUST BE SET FIRST, COUNTER WILL BE ENABLED LATER
    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
#endif

#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
#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
#endif   

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


#endif /* PUBLICTC_H_ */
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 825
Re: [samc][tc] counter don't count
« Reply #1 on: May 23, 2022, 03:11:35 am »
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: [Select]
#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 }
tc_gclk_t;
                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 }
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 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

                int
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
                    }
                }

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.

« Last Edit: May 23, 2022, 06:31:36 am by cv007 »
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [samc][tc] counter don't count
« Reply #2 on: May 23, 2022, 08:54:10 am »
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.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [samc][tc] counter don't count
« Reply #3 on: May 23, 2022, 12:46:52 pm »
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.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17814
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [samc][tc] counter don't count
« Reply #4 on: May 24, 2022, 07:29:07 am »
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.
 

Offline ajb

  • Super Contributor
  • ***
  • Posts: 2599
  • Country: us
Re: [samc][tc] counter don't count
« Reply #5 on: May 24, 2022, 05:35:10 pm »
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.

Are 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  :palm:
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf