C21 PPW Timer Overflows without capturing value?

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

I currently have pins PA22 and PA23 connected to a function generator sweeping from 100 Hz to 1kHz, trying to read pulsewidth and period on TC0 with my SAMC21 Xplained. If I put a breakpoint on my timer ISR, I show the overflow (OVF) flag set, but there's nothing in Count, CC0 or CC1. Reading other threads, I noted something about read access, so I added the following before reading count, however my Period and Pulsewidth still report '0'.

hri_tc_write_CTRLB_CMD_bf(TC0, TC_CTRLBSET_CMD_READSYNC_Val);

This is my current interrupt handler

volatile uint16_t pulseWidth = 0;
volatile uint16_t period = 0;

void TC0_Handler(void)
{
	//Overflow Occurred?
	if (hri_tc_get_interrupt_OVF_bit(TC0)) {
		hri_tc_clear_interrupt_OVF_bit(TC0);
	}
	//Error Occurred?
	if(hri_tc_get_interrupt_ERR_bit(TC0)) {
		hri_tc_clear_interrupt_ERR_bit(TC0);
	}
	//Handle normal values
	if(hri_tc_get_interrupt_MC1_bit(TC0)) {
		hri_tc_write_CTRLB_CMD_bf(TC0, TC_CTRLBSET_CMD_READSYNC_Val); //Required to read?
		hri_tc_clear_interrupt_MC1_bit(TC0);
		while(TC0->COUNT32.SYNCBUSY.bit.CC0);
		pulseWidth = hri_tccount32_read_CC_reg(TC0, 0);
		while(TC0->COUNT32.SYNCBUSY.bit.CC1);
		period = hri_tccount32_read_CC_reg(TC0, 1);
	}
}

driver_init.c code - I added GPIO settings for the timer pins, but maybe I shouldn't have?

void INTERFACE_INSTANCE_PORT_init(void)
{
    // Set up timer pins
	gpio_set_pin_direction(PA22, GPIO_DIRECTION_IN);

	gpio_set_pin_pull_mode(PA22,
	                       // <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(PA22, PINMUX_PA22E_TC0_WO0);
		// Set pin direction to input
	gpio_set_pin_direction(PA23, GPIO_DIRECTION_IN);

	gpio_set_pin_pull_mode(PA23,
	                       // <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(PA23, PINMUX_PA23E_TC0_WO1);
}

void TIMER_0_CLOCK_init(void)
{
	hri_mclk_set_APBCMASK_TC0_bit(MCLK);
	//! Configure channel clock for TC0
	hri_gclk_write_PCHCTRL_reg(GCLK, TC0_GCLK_ID, CONF_GCLK_TC0_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos));
}

And -the fun one- TIMER_0_init

int8_t TIMER_0_init()
{

	if (!hri_tc_is_syncing(TC0, TC_SYNCBUSY_SWRST)) {
		if (hri_tc_get_CTRLA_reg(TC0, TC_CTRLA_ENABLE)) {
			hri_tc_clear_CTRLA_ENABLE_bit(TC0);
			hri_tc_wait_for_sync(TC0, TC_SYNCBUSY_ENABLE);
		}
		hri_tc_write_CTRLA_reg(TC0, TC_CTRLA_SWRST);
	}
	hri_tc_wait_for_sync(TC0, TC_SYNCBUSY_SWRST);

	 hri_tc_write_CTRLA_reg(TC0,1 << TC_CTRLA_COPEN0_Pos /* Capture Pin 0 Enable: enabled */
			 | 1 << TC_CTRLA_COPEN1_Pos /* Capture Pin 1 Enable: enabled*/
			 | 1 << TC_CTRLA_CAPTEN0_Pos /* Capture Channel 0 Enable: enabled */
			 | 1 << TC_CTRLA_CAPTEN1_Pos /* Capture Channel 1 Enable: enabled */
			 | 0 << TC_CTRLA_ALOCK_Pos /* Auto Lock: disabled */
			 | 0 << TC_CTRLA_PRESCSYNC_Pos /* Prescaler and Counter Synchronization: 0 */
			 | 0 << TC_CTRLA_ONDEMAND_Pos /* Clock On Demand: disabled */
			 | 0 << TC_CTRLA_RUNSTDBY_Pos /* Run in Standby: disabled */
			 | TC_CTRLA_PRESCALER_DIV4_Val << TC_CTRLA_PRESCALER_Pos /* Setting: DIV4 (page 713) */
			 | 0x0 << TC_CTRLA_MODE_Pos); /* Operating Mode: 0x0 16 bit*/

	hri_tc_write_CTRLB_reg(TC0,
	                       0x4 << TC_CTRLBSET_CMD_Pos           /* Command: 0x4 - Readsync */
	                           | 0 << TC_CTRLBSET_ONESHOT_Pos /* One-Shot: disabled */
	                           | 0 << TC_CTRLBCLR_LUPD_Pos    /* Setting: disabled */
	                           | 0 << TC_CTRLBSET_DIR_Pos);   /* Counter Direction: disabled */

	// hri_tc_write_WAVE_reg(TC0,0); /* Waveform Generation Mode: 0 */

	// hri_tc_write_DRVCTRL_reg(TC0,0 << TC_DRVCTRL_INVEN1_Pos /* Output Waveform 1 Invert Enable: disabled */
	//		 | 0 << TC_DRVCTRL_INVEN0_Pos); /* Output Waveform 0 Invert Enable: disabled */

	// hri_tc_write_DBGCTRL_reg(TC0,0); /* Run in debug: 0 */

	//hri_tccount16_write_CC_reg(TC0, 0 ,0x0); /* Compare/Capture Value: 0x0 */

	//hri_tccount16_write_CC_reg(TC0, 1 ,0xFFFF); /* Compare/Capture Value: 0x0 */

	// hri_tccount16_write_COUNT_reg(TC0,0x0); /* Counter Value: 0x0 */

	 hri_tc_write_EVCTRL_reg(TC0,0 << TC_EVCTRL_MCEO0_Pos /* Match or Capture Channel 0 Event Output Enable: disabled
	 */
			 | 1 << TC_EVCTRL_MCEO1_Pos /* Match or Capture Channel 1 Event Output Enable: enabled */
			 | 1 << TC_EVCTRL_OVFEO_Pos /* Overflow/Underflow Event Output Enable: enabled */
			 | 1 << TC_EVCTRL_TCEI_Pos /* TC Async Event Input: Enabled */
			 | 0 << TC_EVCTRL_TCINV_Pos /* TC Inverted Event Input: disabled */
			 | TC_EVCTRL_EVACT_PPW_Val); /* Event Action: 5 - PPW Mode */

	 hri_tc_write_INTEN_reg(TC0,
				1 << TC_INTENSET_MC0_Pos /* Match or Capture Channel 0 Interrupt Enable: disabled */
			 | 1 << TC_INTENSET_MC1_Pos /* Match or Capture Channel 1 Interrupt Enable: enabled */
			 | 1 << TC_INTENSET_ERR_Pos /* Error Interrupt Enable: enabled */
			 | 1 << TC_INTENSET_OVF_Pos); /* Overflow Interrupt enable: enabled */
	
	//! Enable IRQ for TC0
	NVIC_DisableIRQ(TC0_IRQn);
	NVIC_ClearPendingIRQ(TC0_IRQn);
	NVIC_EnableIRQ(TC0_IRQn);

	hri_tc_write_CTRLA_ENABLE_bit(TC0, 1 << TC_CTRLA_ENABLE_Pos); /* Enable: enabled */

	return 0;
}

Any help would be very much appreciated!

This topic has a solution.
Last Edited: Wed. Aug 4, 2021 - 01:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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

PPW capture using IO pins is not possible if we trust that thread. Also check your chip revision, capture using IO pins (and not events) will not be possible at all if you have a rev B (my SAMC21 Xplained has a rev B).

READSYNC is only about the COUNT register.

/Lars

 

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

Thank you Lars, I'll check the revision when I get home.

 

I need to measure the duty cycle and frequency of an incoming square wave - I've always used timers on other platforms. How exactly is this supposed to work on the C21 if we can't connect signals to timer pins? 

 

 

Last Edited: Tue. Aug 3, 2021 - 01:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You use the event system and EIC. Check the thread I linked, it has example code (also a linked Microchip doc about doing this with atmel start).

/Lars

 

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks again for all the help, Lars! Seeing all the timer posts with your code/ comments makes me think you're looking at me like

 

As breadcrumbs for the next guy, here are some key points. I saw this article when I first started looking into how to do this on the C21, but I obviously didn't understand it at the time.

1) In the "configuration details" that I skimmed through the first time, it's important not to miss the following:

  • Pin PA16 is configured as a external interrupt level high.
  • Event system Channel 0 is configured as PA16(EXINT) as event generator and TC0 as event user in Asynchronous Path.

In other words, you have to use the External Interrupt Controller (EIC) to generate the event, and then pipe it to TC0 using the Event System Interface (EVSYS) asynchronously. The answer was spelled out for me in the first thing I found, I just didn't understand it.

 

In Lars' example code from the other page, at first I didn't see two peripherals were being used, so I re-wrote my implementation as follows for clarity

void EXT_INT_init(void)
{
    //Clock the EIC
    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);

    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 EVENT_SYS_init(void)
{
    //Clock EVSYS
    MCLK->APBCMASK.bit.EVSYS_ = 1;
    EVSYS->USER[EVSYS_ID_USER_TC0_EVU].bit.CHANNEL = 2; // Channel n-1 selected so 1 here
    while(!EVSYS->CHSTATUS.bit.USRRDY1);
    
    //! NOTE: 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);
}

 

2) The example is configuring the TC's for 32 bit mode, meaning it's chaining two together. This wouldn't be a big deal, except that the hri function hri_tccount32_read_CC_reg(TC0, 1); is meant to grab 32bits - you can't use it in normal 16 bit mode and expect sane results.