SAMC21 - Cannot Use TC Capture Inputs in PPW or PW Modes

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

After wasting enough time on this, I've about convinced myself it can't be done, but has anyone had any success using any TC Capture Inputs (WO[0,1]) for any of the PWP, PPW, or PW modes?

 

I was able to get them to work using EIC and the Event system, but the datasheet seems to say you can use Event Inputs or Capture Inputs for any of these modes (depending on whether or not CTRLA.COPENx is set). They even list specific Event Actions that can only be triggered by Event Inputs (not including PPW, etc...). So far, setting EVACT to anything just stops the timer (unless I'm using the EVENT system). This seems to imply these modes only work with the Event System, but that seems strangely limited and misleading in the datasheet. I've even tried feeding the MC0 output event back into the TC0 event input since it was working with the EIC event input, but that didn't work either. 

 

The only thing I've been able to do with the Capture Inputs is save the COUNT value in CC0 with the rising edge of WO[0]. Since I need to measure pulse-width, I can't even use interrupts because I can't flip the edge-sensing direction without disabling the timer first. Currently I can't use the EIC method without a new PCB since EXTINT12 happens to be in use already as I thought I could use the WO[0] input for what I needed. I will probably make another PCB rev anyway, but I wanted some kind of confirmation that this can or cannot be done...

 

I'm using SAMC21J18A Rev D. No ASF.

 

Thanks for your input.

 

Last Edited: Sun. Sep 24, 2017 - 01:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tried it and unfortunately I can only confirm your result (I finally have a Rev D C21, no point testing this with my Xplained Pro which is Rev B where capture from input pins is in the errata).

/Lars

 

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

Hello,

 

I have the same problem with inputs which are probably not inputs at all. But I couldn't solve the problem also with Event system, PW is not working at all, PPW/PWP are working wrong and give no values for CC1 register.

Could you please post a part of your source code here?

Thank you in advance,

Alex

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include "sam.h"
#include <stdlib.h>

static volatile uint16_t cap0[128]; // Period with PPW capture
static volatile uint16_t cap1[128]; // Pulse width with PPW capture
static volatile size_t nCap;

void setupInput(void)
{
    MCLK->APBAMASK.bit.EIC_ = 1;
    GCLK->PCHCTRL[EIC_GCLK_ID].bit.GEN = 0;
    GCLK->PCHCTRL[EIC_GCLK_ID].bit.CHEN = 1;
    while(GCLK->PCHCTRL[EIC_GCLK_ID].bit.CHEN != 1);
    // Input on PA22 which is EXTINT6
    PORT->Group[0].PMUX[22/2].bit.PMUXE = MUX_PA22A_EIC_EXTINT6;
    const PORT_PINCFG_Type pincfg = {
        .bit.PMUXEN = 1,
        .bit.INEN = 1,
        .bit.PULLEN = 1
    };
    PORT->Group[0].PINCFG[22].reg = pincfg.reg;
    EIC->CONFIG[0].bit.SENSE6 = EIC_CONFIG_SENSE6_HIGH_Val;
    EIC->EVCTRL.bit.EXTINTEO = 1 << 6;
    EIC->CTRLA.bit.ENABLE = 1;
    while (EIC->SYNCBUSY.bit.ENABLE == 1);
}

void setupTimer(void)
{
    MCLK->APBCMASK.bit.TC0_ = 1;
    GCLK->PCHCTRL[TC0_GCLK_ID].bit.GEN = 0;
    GCLK->PCHCTRL[TC0_GCLK_ID].bit.CHEN = 1;
    while(GCLK->PCHCTRL[TC0_GCLK_ID].bit.CHEN != 1);
    
    TC0->COUNT16.CTRLA.bit.ENABLE = 0;
    while (TC0->COUNT16.SYNCBUSY.reg);
    TC0->COUNT16.CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT16;
    TC0->COUNT16.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV1_Val;
    TC0->COUNT16.CTRLA.bit.CAPTEN0 = 1;
    TC0->COUNT16.CTRLA.bit.CAPTEN1 = 1;
    TC0->COUNT16.CTRLA.bit.COPEN0 = 0;
    TC0->COUNT16.CTRLA.bit.COPEN1 = 0;
    while (TC0->COUNT16.SYNCBUSY.reg);

    TC0->COUNT16.EVCTRL.bit.TCEI = 1;
    TC0->COUNT16.EVCTRL.bit.EVACT = TC_EVCTRL_EVACT_PPW_Val;
    // Interrupts
    TC0->COUNT16.INTENSET.bit.MC0 = 1;
    TC0->COUNT16.INTENSET.bit.MC1 = 0; // Not needed, can read out both in MC0 interrupt
    
    // Enable TC
    TC0->COUNT16.CTRLA.bit.ENABLE = 1;
    while (TC0->COUNT16.SYNCBUSY.reg);
    
    // Enable InterruptVector
    NVIC_EnableIRQ(TC0_IRQn);

    MCLK->APBCMASK.bit.EVSYS_ = 1;
    // EVSYS GCLK is needed for interrupts and sync path (so not now)
    EVSYS->USER[EVSYS_ID_USER_TC0_EVU].bit.CHANNEL = 2; // Channel n-1 selected so 1 here
    while(!EVSYS->CHSTATUS.bit.USRRDY1);
    
    // EVSYS_CHANNEL_EDGSEL_BOTH_EDGES // not usable with  PATH_ASYNCHRONOUS
    EVSYS->CHANNEL[1].reg = EVSYS_CHANNEL_PATH_ASYNCHRONOUS|EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_6);
    while(EVSYS->CHSTATUS.bit.CHBUSY1);
}

void TC0_Handler()
{
    if (TC0->COUNT16.INTFLAG.bit.MC0) {
        // The interrupt flag is cleared by reading CC
        cap0[nCap] = TC0->COUNT16.CC[0].bit.CC;
        cap1[nCap] = TC0->COUNT16.CC[1].bit.CC;
        if (++nCap == sizeof(cap0)/sizeof(cap0[0])) {
            static volatile size_t done;
            done++;
            nCap = 0;
        }
    }
}

int main(void)
{
    SystemInit();
    setupInput();
    setupTimer();
    while (1) {
    }
}

This is ported from a thread about this for SAMD21:

https://community.atmel.com/foru...

Works ok for me with SAMC21 Xplained Pro.

/Lars

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

Hello Lars,

 

thank you for your reply. I've found this manual on Microchip site:

https://microchipsupport.force.c...

It worked for my board, probably the problem was the External Interrupt, I have always configured it as Both edges, and the manual means it should be High level.

But anyway thanks!

 

Alex

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

Had similar issues.  I still don't know if it's possible to use the WOx pins for capture input for PPW or PWP capture.  Using a pin and making an EIC event for capture input, only HIGH or LOW for the pin SENSEx field completely work ie. CC0 having Period and CC1 having Pulse Width.  RISE, FALL, and BOTH only give partial information.

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

I'm about to start a similar project with a SAMC21, and from what I can find the only solution such as that above was to use an external interrupt and route it through the EVSYS into the TC or TCC.  

 

In my case, the h/w has been set and the input is PA08, which is the NMI, and I'm having trouble seeing how it plugs into EVSYS (I can see how the EXTINTs do it, but NMI?).  So I'm concerned it might not work entirely correctly. 

 

There might be an alternative, which sets up my question:

 

Has anybody succeeded in routing the event through the CCL instead of the external interrupt system?  Would there be any advantage/disadvantage to taking this route?

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

I agree that NMI has no connection with the event system. CCL should work and is in fact mentioned in the errata:

1.20.2 I/O Pins The input capture on I/O pins does not work.

Workaround Use the input capture through TC event and use the EIC or CCL as event generators.

No particular advantage I guess (except as in you case allowing input from a pin which is not an EXTINT).

/Lars

 

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

Lajon wrote:

I agree that NMI has no connection with the event system. CCL should work and is in fact mentioned in the errata:

1.20.2 I/O Pins The input capture on I/O pins does not work.

Workaround Use the input capture through TC event and use the EIC or CCL as event generators.

No particular advantage I guess (except as in you case allowing input from a pin which is not an EXTINT).

/Lars

 

 

Thanks.  I've been working on getting the CCL working and getting its events out.  The nice part is that once you are in the event system, you can pick whichever TC/TCC you want for capture - you aren't tied down due to I/O pins anymore. 

 

Figuring out how CCL truth tables work has been my largest stumbling block so far, but I found a writeup for the SAML that described it in much more useful detail than the SAMC datasheet.

 

Won't know for a couple weeks if my code is working, but if someone stumbles upon this post in the future and needs to route events thru CCL to TC/TCC, feel free to post and I'll help as I can.

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

brandonsmith79 wrote:
Won't know for a couple weeks if my code is working, but if someone stumbles upon this post in the future and needs to route events thru CCL to TC/TCC, feel free to post and I'll help as I can.

 

Hi brandonsmith79,

I am also trying to generate events for the TC or TCC capture input using the CCL in the ATSAMC21G17A to detect an external clock. Would you have an example for me. E.g. a Atemel START configuration so I can have a look at it. I don't know if I have the CCL and EVSYS configured correctly and due to the current supply issues of the microcontrollers I can't verify it in hardware.

Thanks in advance

 

ATSAMC21G17A -> PA16 --> CCL --> EVSYS --> TC/TCC (Capture)

ATSAMC21
ATSAME51
ATMEGA

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

VGS_MLN wrote:
ATSAMC21G17A -> PA16 --> CCL --> EVSYS --> TC/TCC (Capture)

PA16 is EXTINT[0]  so I guess in your case the CCL is going to do something and not just be a way to get events from a pin which is not an EXTINT? 

/Lars

 

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

Hi Lars,

 

I wanted to build the whole thing interrupt-free and get the clock signal on the PA16 without EXINT. I want to count signals from 100 Hz to 100 KHz there. The call via interrupts would cost me too much time. For this reason I wanted to link this via combinatorial logic directly with the TC/TCC.

 

kind regards

ATSAMC21
ATSAME51
ATMEGA

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

You don't need interrupts from EIC when using events (despite the naming EXTINT). The example in post #4 is not using interrupts from the EIC. Doing the whole thing interrupt-free is another thing though.

VGS_MLN wrote:
due to the current supply issues of the microcontrollers I can't verify it in hardware.

Do you at least have a SAMC21 xPlained Pro or similar development board? Seems to be in stock.

/Lars

 

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

Lajon wrote:

Do you at least have a SAMC21 xPlained Pro or similar development board? Seems to be in stock.

I have orderd this board in december last year. It should came next week.

 

 

In my Atmel START configurator I miss the CCL configurator. Like in the example in the following URL:

http://atmel-studio-doc.s3-websi...

 

 

ATSAMC21
ATSAME51
ATMEGA

Last Edited: Thu. Jan 27, 2022 - 02:47 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 

I would like to use the Atmel START configurator to generate PA16 events with the help of the CCL, which trigger the TCC2. I call the TCC2 cyclically with timer interrupts to read its counter values and reset the counter.

 

That was my idea. So that not every clock generates an interrupt externally. But I read out cyclically e.g. with a 500 to 1000 ms time slot.

ATSAMC21
ATSAME51
ATMEGA

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

VGS_MLN wrote:
In my Atmel START configurator I miss the CCL configurator. Like in the example in the following URL:

Possibly it exists only for AVR,  ATtiny817 in the example.

VGS_MLN wrote:
That was my idea. So that not every clock generates an interrupt externally. But I read out cyclically e.g. with a 500 to 1000 ms time slot.

Should work, one of the COUNT event actions in the TCC. Still not sure what you need the CCL for.

/Lars

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

I need CCL only to get the signal to the TCC counter. Nothing more. No logic. Simple pass through. Since the input capture I/Os do not work directly Errata 1.20.2 (Workaround)

ATSAMC21
ATSAME51
ATMEGA

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

As mentioned you can use EXTINT[0] which can be on PA16, an event from that can be used to count in the TCC just as well as an event from CCL can.

/Lars

 

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

Doesn't an interrupt generate more time/effort/interruption than CCL? I mean that in terms of performance? Or can I also generate events via EXTINT[0], which do not call an ISR?

ATSAMC21
ATSAME51
ATMEGA

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

VGS_MLN wrote:

Or can I also generate events via EXTINT[0], which do not call an ISR?

Yes, this is what the example in post #4 is doing, notice the only interrupt handler there is for the TC.

/Lars

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

Hi Lars,

 

I have now had the project created with Atmel START and successfully imported into IAR. I do not think that the project in the form correctly initialized the hardware. I think I have to modify it manually. Modufication in the hardware initialization we wanted to avoid in the company, so that each colleague can use the Atmel START configurator (also with configuration files => *.atstart).

 

Unfortunately this will not work. I see in the interrupts and in the timer (TCC2) no initialization of the event system.

 

Excerpts from the code:

/* External interrupts init */

void EXTERNAL_IRQ_0_init(void)
{
	hri_gclk_write_PCHCTRL_reg(GCLK, EIC_GCLK_ID, CONF_GCLK_EIC_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos));
	hri_mclk_set_APBAMASK_EIC_bit(MCLK);

	// Set pin direction to input
	gpio_set_pin_direction(xVelocityPulse, GPIO_DIRECTION_IN);

	gpio_set_pin_pull_mode(xVelocityPulse,
	                       // <y> Pull configuration
	                       // <id> pad_pull_config
	                       // <GPIO_PULL_OFF"> Off
	                       // <GPIO_PULL_UP"> Pull-up
	                       // <GPIO_PULL_DOWN"> Pull-down
	                       GPIO_PULL_OFF);

	gpio_set_pin_function(xVelocityPulse, PINMUX_PA16A_EIC_EXTINT0);

	//init other interrupts

	ext_irq_init();
}

//...

/**
 * \brief Initialize external irq component if any
 */
int32_t ext_irq_init(void)
{
	uint16_t i;

	for (i = 0; i < EXT_IRQ_AMOUNT; i++) {
		ext_irqs[i].pin = 0xFFFFFFFF;
		ext_irqs[i].cb  = NULL;
	}

	return _ext_irq_init(process_ext_irq);
}

/* Event system init */

void EVENT_SYSTEM_0_init(void)
{
	hri_gclk_write_PCHCTRL_reg(GCLK, EVSYS_GCLK_ID_0, CONF_GCLK_EVSYS_CHANNEL_0_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos));

	hri_mclk_set_APBCMASK_EVSYS_bit(MCLK);

	event_system_init();
}

//...

/**
 * \brief Initialize event system.
 */
int32_t event_system_init(void)
{
	return _event_system_init();
}

//...

/**
 * \brief Initialize event system
 */
int32_t _event_system_init(void)
{
	uint8_t i;

	/* configure user multiplexers */
	for (i = 0; i < EVSYS_USERS; i++) {
		hri_evsys_write_USER_reg(EVSYS, i, user_mux_confs[i]);
	}

	/* configure channels */
	for (i = 0; i < EVSYS_CHANNELS; i++) {
		hri_evsys_write_CHANNEL_reg(EVSYS, i, channel_confs[i]);
	}

	hri_evsys_write_INTEN_reg(EVSYS, interrupt_cfg);

	return ERR_NONE;
}

/* Timer 2 (TCC2) init */

void TIMER_2_init(void)
{
	TIMER_2_CLOCK_init();
	timer_init(&TIMER_2, TCC2, _tcc_get_timer());
}

//...

/**
 * \brief Initialize timer
 */
int32_t timer_init(struct timer_descriptor *const descr, void *const hw, struct _timer_hpl_interface *const func)
{
	ASSERT(descr && hw && func);
	descr->func = func;
	descr->func->init(&descr->device, hw);
	descr->time                           = 0;
	descr->device.timer_cb.period_expired = timer_process_counted;

	return ERR_NONE;
}

And here explicitly the sequence of the system init:

void system_init(void)
{
	init_mcu();

	// GPIO on PA00

	gpio_set_pin_level(xQRelease,
	                   // <y> Initial level
	                   // <id> pad_initial_level
	                   // <false"> Low
	                   // <true"> High
	                   false);

	// Set pin direction to output
	gpio_set_pin_direction(xQRelease, GPIO_DIRECTION_OUT);

	gpio_set_pin_function(xQRelease, GPIO_PIN_FUNCTION_OFF);

	// GPIO on PA01

	gpio_set_pin_level(xQReverse,
	                   // <y> Initial level
	                   // <id> pad_initial_level
	                   // <false"> Low
	                   // <true"> High
	                   false);

	// Set pin direction to output
	gpio_set_pin_direction(xQReverse, GPIO_DIRECTION_OUT);

	gpio_set_pin_function(xQReverse, GPIO_PIN_FUNCTION_OFF);

	// GPIO on PA12

	gpio_set_pin_level(CAN_STB,
	                   // <y> Initial level
	                   // <id> pad_initial_level
	                   // <false"> Low
	                   // <true"> High
	                   true);

	// Set pin direction to output
	gpio_set_pin_direction(CAN_STB, GPIO_DIRECTION_OUT);

	gpio_set_pin_function(CAN_STB, GPIO_PIN_FUNCTION_OFF);

	// GPIO on PA17

	gpio_set_pin_level(eFuse_SHDN,
	                   // <y> Initial level
	                   // <id> pad_initial_level
	                   // <false"> Low
	                   // <true"> High
	                   false);

	// Set pin direction to output
	gpio_set_pin_direction(eFuse_SHDN, GPIO_DIRECTION_OUT);

	gpio_set_pin_function(eFuse_SHDN, GPIO_PIN_FUNCTION_OFF);

	// GPIO on PA28

	gpio_set_pin_level(xQSoftStarter,
	                   // <y> Initial level
	                   // <id> pad_initial_level
	                   // <false"> Low
	                   // <true"> High
	                   false);

	// Set pin direction to output
	gpio_set_pin_direction(xQSoftStarter, GPIO_DIRECTION_OUT);

	gpio_set_pin_function(xQSoftStarter, GPIO_PIN_FUNCTION_OFF);

	ADC_1_init();

	ADC_2_init();

	EXTERNAL_IRQ_0_init();

	TIMER_0_init();

	I2C_0_init();

	USART_0_init();

	PWM_2_init();

	PWM_1_init();

	PWM_0_init();

	TIMER_1_init();

	TIMER_2_init();

	WDT_0_init();
	CAN_1_init();

	DAC_0_init();

	EVENT_SYSTEM_0_init();

	ADC_0_init();

	TEMPERATURE_SENSOR_0_init();
}

I attach again, in case someone is interested, the Atmel START configuration file

 

Kind regards
Michael

Attachment(s): 

ATSAMC21
ATSAME51
ATMEGA

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

The event system is initialized in 

 _event_system_init(void)

from the arrays 

static const uint8_t user_mux_confs[] = {REPEAT_MACRO(USER_MUX_CONF, i, EVSYS_USERS)};

static const uint16_t channel_confs[] = {REPEAT_MACRO(CHANNEL_CONF, i, EVSYS_CHANNELS)};

static const uint32_t interrupt_cfg = REPEAT_MACRO(INT_CFG, i, EVSYS_CHANNELS) 0;

The defines used there are from config/hpl_evsys_config.h, for example
 

#define CONF_EVGEN_0 14

so channel 0 is using this generator:
 

// <0xE=>EIC external interrupt 0

/Lars
 

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

Thank you Lars. I'm also browsing through the generated code right now. Now I see it too. That seems to be correct.
 

TCC2 Event Input 0 was also activated

 

// <q> Timer/Counter Event Input 0
// <i> This bit is used to enable input event 0 to the TCC
// <id> tcc_arch_tcei0
#ifndef CONF_TCC2_TCEI0
#define CONF_TCC2_TCEI0 1
#endif

Unfortunately, the Event Input 0 action is not correct:

 

// <o> Timer/Counter Event Input 0 Action
// <0=>Event action disabled
// <1=>Start restart or re-trigger on event
// <2=>Count on event
// <3=>Start on event
// <4=>Increment on event
// <5=>Count on active state of asynchronous event
// <6=>Capture overflow times (Max value)
// <7=>Non-recoverable fault
// <i> These bits define the action the TCC performs on TCE0 event input 0
// <id> arch_tcc_evact0
#ifndef CONF_TCC2_EVACT0
#define CONF_TCC2_EVACT0 0
#endif

 

This should be increment on event (option 4). I will fix this. Just found the item in Atmel START.

 

 

 

The cyclic reading of the counter and resetting to 0 will be done by my own task counter (TCC0). I need the value only every 400 to 500 miliseconds. This is not yet exactly defined.

 

Enclosed the current Atmel START configuration file for interested parties.

 

Kind regards
Michael

Attachment(s): 

ATSAMC21
ATSAME51
ATMEGA

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

Hello!

 

i have exactly the same issue. Can't use the TC Capture Inputs (WO[0,1]) directly for PPW etc.

Does anyone solved the issue without using the workaround? Regarding the datasheet it should be possible.

I'm using an SAM C21E Rev. F