EVSYS issue ATSAMD51

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

As an offshoot to my ADC timing issue, tech support discussion migrated from polling TC0 to EVSYS.  Sounds like a great idea, except something is amiss.

 

I set up the ADC after this and i can reverse that if needed.  After some fiddling I found I could not even write to EVSYS->Channel[0].CHANNEL without flipping around the order to that below. 

In order to isolate an issue with TC0, I set a SWEVT on channel 1 to toggle the GPIO B03.  It also did not work.  I discovered by memory inspection that neither user is ready.

Both of them have an event defined and at least Port if as fully set up as I can see how.

	// initialize EVSYS
	// TC0 will generate OVF events.  
	//EVSYS->SWEVT.reg = 0x80000000; how to manually trigger.
	MCLK->APBBMASK.bit.EVSYS_ = 1;
	GCLK->PCHCTRL[11].bit.GEN = 2; // channel 0
	GCLK->PCHCTRL[11].bit.CHEN = 1;
	GCLK->PCHCTRL[12].bit.GEN = 2; //channel 1
	GCLK->PCHCTRL[12].bit.CHEN = 1;
	EVSYS->CTRLA.reg = 1;
	TC0->COUNT8.EVCTRL.bit.OVFEO = 1; // use event system for dac and adc.
	//ADC0->EVCTRL.bit.STARTEI = 1;
	PORT->Group[1].EVCTRL.bit.PORTEI0 = 1;
	PORT->Group[1].EVCTRL.bit.EVACT0 = 3;
	PORT->Group[1].EVCTRL.bit.PID0 = 3;
	// ADC0 start is user 55d
	uint32_t ptr = &EVSYS->USER[55];
	EVSYS->USER[55].reg = 1; // need to turn on and off?  think it is done in user peripheral
	EVSYS->USER[2].reg = 2;  // user 2 is portb
	
	// DAC0 start is 61d, DAC1 is 62d 
	// EVSYS->USER[61].reg = 1;
	// EVSYS->USER[62].reg = 1;
	EVSYS->Channel[0].CHANNEL.bit.EVGEN = 0x49; // TC0 OVF is 0x49
	EVSYS->Channel[0].CHANNEL.bit.EDGSEL = 1;   // rising edge
	EVSYS->Channel[0].CHANNEL.bit.PATH = 2;     // async path.
	EVSYS->Channel[0].CHANNEL.bit.ONDEMAND = 0;
	EVSYS->Channel[0].CHINTENCLR.reg = 0x2; // disable and clear
	
	EVSYS->Channel[1].CHANNEL.bit.EVGEN = 0x49; // TC0 OVF is 0x49
	EVSYS->Channel[1].CHANNEL.bit.EDGSEL = 1;   // rising edge
	EVSYS->Channel[1].CHANNEL.bit.PATH = 2;     // async path.
	EVSYS->Channel[1].CHANNEL.bit.ONDEMAND = 0;
	EVSYS->Channel[1].CHINTENCLR.reg = 0x2; // disable and clear
	EVSYS->SWEVT.reg = 0x00000002;

 

This topic has a solution.

jeff

Last Edited: Sat. Apr 21, 2018 - 08:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The order of initialization is in the datasheet under "Basic Operation".
Your CHANNEL register setup has a few problems comparing with the datasheet:
* "To write to this register, do a single, 32-bit write of all the configuration data."
* EDGSEL "These bits must be written to zero when using the asynchronous path."

/Lars

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

Lajon, thanks.  i had the write as a single reg=, but missed the EDGSEL comment.  I broke it up when I was failing to write anything.

 

Modified as below and placed after all associated peripherals have been initialized:

entering 1), all channels are 'on demand' and all users (none set) are ready.  As soon as I set user[55]=1 the ready bit is lowered.  Setting user[2]=2 does not lower its ready bit; however, setting Channel[1] does.

The loop test of READYUSR never completes.

 

#if USE_EVENT_ADC
	// initialize EVSYS
	// TC0 will generate OVF events.  
	//EVSYS->SWEVT.reg = 0x80000000; how to manually trigger.
	MCLK->APBBMASK.bit.EVSYS_ = 1;
	GCLK->PCHCTRL[11].bit.GEN = 2; // channel 0
	GCLK->PCHCTRL[11].bit.CHEN = 1;
	GCLK->PCHCTRL[12].bit.GEN = 2; //channel 1
	GCLK->PCHCTRL[12].bit.CHEN = 1;
	EVSYS->CTRLA.reg = 1;
	
	// 1) enable source

	TC0->COUNT8.EVCTRL.bit.OVFEO = 1; // use event system for dac and adc.
	
	// 2a) config users
	// ADC0 start is user 55d
	uint32_t ptr = &EVSYS->USER[55];
	EVSYS->USER[55].reg = 1; // need to turn on and off?  think it is done in user peripheral
	
	// DAC0 start is 61d, DAC1 is 62d 
	//EVSYS->USER[61].reg = 1;
	//EVSYS->USER[62].reg = 1;
	
	#if EVENT_TOGGLES_B3
		// port is normally toggled when the peripheral acts
		EVSYS->USER[2].reg = 2;  // user 2 is portb-3
	#endif

	// 2b) config channels
	EVSYS->Channel[0].CHANNEL.reg = 0x249; // TC0 OVF is 0x49, async
	//EVSYS->Channel[0].CHINTENCLR.reg = 0x2; // disable and clear
	
	EVSYS->Channel[1].CHANNEL.reg = 0x249; // TC0 OVF is 0x49, async
	//EVSYS->Channel[1].CHINTENCLR.reg = 0x2; // disable and clear
	
	// 3) config actions
	PORT->Group[1].EVCTRL.bit.EVACT0 = 3;
	PORT->Group[1].EVCTRL.bit.PID0 = 3;
	
	// 4) enable user
// These will be done in ArmXXX() and undone in DisarmXXX() calls.
	//ADC0->EVCTRL.bit.STARTEI = 1;
	//DAC->EVCTRL.bit.STARTEI1 = 1;
	
	// This is conditional debug
	#if EVENT_TOGGLES_B3
		PORT->Group[1].EVCTRL.bit.PORTEI0 = 1;
		
		while (!EVSYS->READYUSR.bit.READYUSR1)
			delay_us(10);
		
		
		while (1)
		{
			EVSYS->SWEVT.reg = 0x00000002;
			delay_us (10);
		}
	#endif
#endif

 

jeff

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

CHSTATUS Bit 0 – RDYUSR: Ready User
This bit is cleared when at least one of the event users connected to the channel is not ready.
This bit is set when all event users connected to channel are ready to handle incoming events on the
channel.
When the event channel path is asynchronous, this bit is always read zero.

 

Bits 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 – READYUSR: Ready User for Channel n
This bit is set when all event users connected to channel n are ready to handle incoming events on
channel n.
This bit is cleared when at least one of the event users connected to the channel is not ready.
When the event channel n path is asynchronous, this bit is always read zero.

 

OK, READYUSR is not the issue.

 

Backing up, pin B03 is my testpoint and 

((Port *)PORT)->Group[GPIO_PORTB].OUTTGL.reg = 8; in my non-Event version works.

GPIO_PORTB is 1 and PID0=3 picks pin 3.  EVACT0 should toggle it.

 

As a sanity test I toggle manually instead of SWEVT and the pin toggles.

As a sanity test I set SWEVT = 0xFFFFFFFF;  no events cause toggles.

 

jeff

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

There must be more setup of TC0 that you are not showing?

/Lars

 

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

Possibly you have TC0 enabled when you do

	TC0->COUNT8.EVCTRL.bit.OVFEO = 1; // use event system for dac and adc.

this will not work, the register is enable protected.

/Lars

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

Lars,

 

At that point I do; however before this I have

 

void DAC_initTC0 (uint16_t subband)
{
	// reconfigure DPLL1.  Fail if the values are out of range
	if (subband >=NUM_TEST_FREQ)
		FatalError(ERR_DAC_BANDS, subband);

	TC0->COUNT8.CTRLA.bit.SWRST = 1;
	while (TC0->COUNT8.SYNCBUSY.bit.SWRST)
		;
	TC0->COUNT8.DBGCTRL.bit.DBGRUN = 0;
	TC0->COUNT8.CTRLBCLR.bit.DIR = 1; // clear to count up, just in case?
	#if USE_EVENT_ADC
		TC0->COUNT8.EVCTRL.bit.OVFEO = 1; // use event system for dac and adc.
	#endif 
	TC0->COUNT8.CTRLA.reg = 0b0110; // 8 bit and enabled
	TC0->COUNT8.PERBUF.reg = DAC_TC0_COUNTS[subband]; // uint16_t subband DAC_TC0_COUNTS[NUM_TEST_FREQ]
	while (TC0->COUNT8.SYNCBUSY.bit.PER)
		;

	// TC0 uses FPLL1 --> GCLK2 --> PCLK??.
	MCLK->APBAMASK.bit.TC0_ = 1;
	GCLK->PCHCTRL[TC0_GCLK_ID].bit.GEN = 1;
	GCLK->PCHCTRL[TC0_GCLK_ID].bit.CHEN = 1;

}

so the TC0 event is already enabled.

To be sure, I repeat initializing TC0 here and also make sure the event is cleared.  The events are not toggling B03 but if I uncomment the manual toggle, I see that OVF is occuring.

I have looked at the Start example using DAC and DMA but cannot really see anything it does that I do not.

 

 

	#if EVENT_TOGGLES_B3
	DAC_initTC0(4);
	TC0->COUNT8.CTRLA.bit.ENABLE = 0;
	while (TC0->COUNT8.SYNCBUSY.bit.ENABLE)
		;
	
	PORT->Group[GPIO_PORTB].EVCTRL.bit.PORTEI0 = 1;

	TC0->COUNT8.COUNT.reg = 0;
	TC0->COUNT8.CTRLA.bit.ENABLE = 1;
	TC0->COUNT8.INTFLAG.bit.OVF = 1;
	while (TC0->COUNT8.SYNCBUSY.bit.ENABLE)
		;
		
		while (1)
		{
			//EVSYS->SWEVT.reg = 0x00000002;
			//delay_us (10);
			while (!TC0->COUNT8.INTFLAG.bit.OVF)
				;
			//((Port *)PORT)->Group[GPIO_PORTB].OUTTGL.reg = 8;
			TC0->COUNT8.INTFLAG.bit.OVF = 1;
		}
	#endif

 

jeff

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

As a further sanity check, I went all the way to a fresh project

 

#include <atmel_start.h>

int main(void)
{
	/* Initializes MCU, drivers and middleware */
	atmel_start_init();
	
	TC0->COUNT8.CTRLA.bit.SWRST = 1;
	while (TC0->COUNT8.SYNCBUSY.bit.SWRST)
	;
	TC0->COUNT8.DBGCTRL.bit.DBGRUN = 0;
	TC0->COUNT8.CTRLBCLR.bit.DIR = 1; // clear to count up, just in case?
	#if USE_EVENT_ADC
	TC0->COUNT8.EVCTRL.bit.OVFEO = 1; // use event system for dac and adc.
	#endif
	TC0->COUNT8.CTRLA.reg = 0b0110; // 8 bit and enabled
	TC0->COUNT8.PERBUF.reg = 200; 
	while (TC0->COUNT8.SYNCBUSY.bit.PER)
	;
	
	PORT->Group[1].EVCTRL.bit.EVACT0 = 3;
	PORT->Group[1].EVCTRL.bit.PID0 = 3;
	PORT->Group[1].EVCTRL.bit.PORTEI0 = 1;

	/* Replace with your application code */
	while (1) 
	{
			while (!TC0->COUNT8.INTFLAG.bit.OVF)
			;
			//((Port *)PORT)->Group[GPIO_PORTB].OUTTGL.reg = 8;
			TC0->COUNT8.INTFLAG.bit.OVF = 1;
	}
}

only toggles the pin if I uncomment the OUTTGL.  IE TC0 is running but the events are either not happening or not causing an action.

jeff

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

Confused, for sure you have to do this

	MCLK->APBAMASK.bit.TC0_ = 1;
	GCLK->PCHCTRL[TC0_GCLK_ID].bit.GEN = 1;
	GCLK->PCHCTRL[TC0_GCLK_ID].bit.CHEN = 1;

before the rest of the TC0 setup. In the last main you have no clock setup at all for TC0, is there something from atmel start in both cases maybe?

/Lars

 

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

In my project i may be getting away with that mistake because I initialize TC0 once at startup and reinitialize it every time I use it to change the period.

The clock setup at the end of the first call must be covering it.

 

In the simple project, yes, Start is setting it up.  I am attaching the Start files.

 

In both cases, OVF is happening and if I loop on it, I can manually toggle the pin, etc.  In both cases, neither a TC0 OVF or a SWEVT causes an action.

 

TCO is overlowing.

PB03 is an output and can be toggled.

No event is resulting in an action.

Attachment(s): 

jeff

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

Here is a self contained EVSYS example I came up with (it's pointless, just counting overflow events from TC0 in TC1). Unfortunately I don't have a SAMD51, this is for SAMC21 (there are register differences but not many in this program).

#include "sam.h"

void setupTimer0(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->COUNT8.CTRLA.bit.ENABLE = 0;
    while (TC0->COUNT8.SYNCBUSY.reg);
    TC0->COUNT8.CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT8_Val;
    TC0->COUNT8.CTRLA.bit.PRESCALER |= TC_CTRLA_PRESCALER_DIV1_Val;
    while (TC0->COUNT8.SYNCBUSY.reg);
    TC0->COUNT8.INTENSET.bit.OVF = 1;
    // Enable InterruptVector
    NVIC_EnableIRQ(TC0_IRQn);
    // Event on overflow
    TC0->COUNT8.EVCTRL.bit.OVFEO = 1;
    // Enable TC
    TC0->COUNT8.CTRLA.bit.ENABLE = 1;
}

void setupTimer1(void)
{
    MCLK->APBCMASK.bit.TC1_ = 1;
    GCLK->PCHCTRL[TC1_GCLK_ID].bit.GEN = 0;
    GCLK->PCHCTRL[TC1_GCLK_ID].bit.CHEN = 1;
    while(GCLK->PCHCTRL[TC1_GCLK_ID].bit.CHEN != 1);
    
    TC1->COUNT16.CTRLA.bit.ENABLE = 0;
    while (TC1->COUNT16.SYNCBUSY.reg);
    TC1->COUNT16.CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT16_Val;
    TC1->COUNT16.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV1_Val;
    while (TC1->COUNT16.SYNCBUSY.reg);
    TC1->COUNT16.INTENSET.bit.OVF = 1;
    // Enable InterruptVector
    NVIC_EnableIRQ(TC1_IRQn);
    // Count on event
    TC1->COUNT16.EVCTRL.bit.EVACT = TC_EVCTRL_EVACT_COUNT_Val;
    // This works but should be after EVSYS setup according to the data sheet
    //TC1->COUNT16.EVCTRL.bit.TCEI = 1;
    //TC1->COUNT16.CTRLA.bit.ENABLE = 1;
}

void TC0_Handler()
{
    if (TC0->COUNT8.INTFLAG.bit.OVF) {
        REG_TC0_INTFLAG = TC_INTFLAG_OVF;
    }
}

void TC1_Handler()
{
    if (TC1->COUNT16.INTFLAG.bit.OVF) {
        REG_TC1_INTFLAG = TC_INTFLAG_OVF;
    }
}

volatile uint16_t count0 = 0;
volatile uint16_t count1 = 0;

int main(void)
{
    /* Initialize the SAM system */
    SystemInit();
    MCLK->APBCMASK.bit.EVSYS_ = 1;
    setupTimer0();
    setupTimer1();
    // The channel can do without GCLK when async
    //GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.GEN = 0;
    //GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN = 1;
    //while(GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN != 1);
    
    EVSYS->USER[EVSYS_ID_USER_TC1_EVU].reg = 1;
    const EVSYS_CHANNEL_Type channel0Config = {
        .bit.EDGSEL = 0, // 0 when using async
        .bit.EVGEN = EVSYS_ID_GEN_TC0_OVF,
        .bit.PATH = EVSYS_CHANNEL_PATH_ASYNCHRONOUS_Val
    };
    EVSYS->CHANNEL[0].reg = channel0Config.reg;
    //while(EVSYS->CHSTATUS.bit.CHBUSY0);
    // Enable event input
    TC1->COUNT16.EVCTRL.bit.TCEI = 1;
    TC1->COUNT16.CTRLA.bit.ENABLE = 1;
        
    while (1) {
        TC0->COUNT8.CTRLBSET.bit.CMD = TC_CTRLBSET_CMD_READSYNC_Val;
        count0 = TC0->COUNT8.COUNT.reg;
        TC1->COUNT16.CTRLBSET.bit.CMD = TC_CTRLBSET_CMD_READSYNC_Val;
        count1 = TC1->COUNT16.COUNT.reg;
    }
}

/Lars

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

Wow, thanks.  So that works.  I added:

 

#define USE_TC1 1
#define USE_PB3 1

 

and modified

 

#if USE_TC1
	setupTimer1();
	EVSYS->USER[EVSYS_ID_USER_TC1_EVU].reg = 1;
#endif
#if USE_PB3
	PORT->Group[1].DIRSET.reg = 1<<3;	
	PORT->Group[1].EVCTRL.bit.EVACT0 = 3;
	PORT->Group[1].EVCTRL.bit.PID0 = 3;
	EVSYS->USER[EVSYS_ID_USER_PORT_EV_1].reg = 1;
#endif	
	const EVSYS_CHANNEL_Type channel0Config = 
	{
		.bit.EDGSEL = 0, // 0 when using async
		.bit.EVGEN = EVSYS_ID_GEN_TC0_OVF,
		.bit.PATH = EVSYS_CHANNEL_PATH_ASYNCHRONOUS_Val
	};
	EVSYS->Channel[0].CHANNEL.reg = channel0Config.reg;
	//while(EVSYS->CHSTATUS.bit.CHBUSY0);
	// Enable event input
#if USE_TC1
	TC1->COUNT16.EVCTRL.bit.TCEI = 1;
	TC1->COUNT16.CTRLA.bit.ENABLE = 1;
#endif
#if USE_PB3
	PORT->Group[1].EVCTRL.bit.PORTEI0 = 1;
#endif

TC1 still counts but PB03 does not toggle.

At least I know that events are generated.

 

 

 

jeff

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

OK, I had to splice it into a dummy Start project to get it moved to my device, etc.

I am unable to toggle a Port pin, which would be REALLY useful for debugging internal events without adding lines of code.  However, it is not my immediate problem.

 

On the bright side, I can trigger an ADC now.  

I do not have anything set up for the clocks, so whatever the chip defaulted to  - looks like 48MHz.  Time to figure out what is different from my code so i can use my clocks.

 

Thanks! 

 

#include <atmel_start.h>



#define USE_TC1 0
#define USE_PB3 0
#define USE_ADC 1

#define PB08 GPIO(GPIO_PORTB, 8)
 void ADC_0_PORT_init(void)
 {
	 // A0.2
	 // ADC_0_PORT_init(); // set the rest of the port settings
	 // Disable digital pin circuitry and set function
	 gpio_set_pin_direction(PB08, GPIO_DIRECTION_OFF);
	 gpio_set_pin_function(PB08, PINMUX_PB08B_ADC0_AIN2);
 }

 void ADC_0_CLOCK_init(void)
 {
	 hri_mclk_set_APBDMASK_ADC0_bit(MCLK);
	 // GCLK2 divides PLL0Freq by 4x to get 30MHz ADC clock source (/2 is 15 clocks per 1us sample)
	 // FDPLL0/4 is GCLK2, which is used as ADC*2 and we have 30 GCLK2 clocks (1us) to make a conversion
	 // fixed ADC timing regardless of sampling time TC0 between samples.
	 hri_gclk_write_PCHCTRL_reg(GCLK, ADC0_GCLK_ID, 0 | (1 << GCLK_PCHCTRL_CHEN_Pos)); 
 }

 void ADC_0_init(void)
 {
	 ADC_0_CLOCK_init();
	 ADC_0_PORT_init();
	 //adc_sync_init(&ADC_0, ADC0, (void *)NULL);
	 hri_adc_set_CTRLA_SWRST_bit(ADC0);
	 hri_adc_wait_for_sync(ADC0, ADC_SYNCBUSY_SWRST);
//	 hri_adc_write_CTRLB_reg(ADC0, 0x0002); // vanilla right just, free run, 12 bit
	 hri_adc_write_CTRLB_reg(ADC0, 0x0000); // vanilla right just, one shot, 12 bit
//	 hri_adc_write_CTRLB_reg(ADC0, 0x0010); // vanilla right just, one shot, 10 bit
	 hri_adc_write_REFCTRL_reg(ADC0, 0x0003); // VddANA
	 hri_adc_write_INPUTCTRL_reg(ADC0, 0x1802); // AN0.2 vs gnd
	 hri_adc_write_AVGCTRL_reg(ADC0, 0x0000);
	 hri_adc_write_SAMPCTRL_reg(ADC0, 0x0000); // want instantaneous point sample
	 hri_adc_write_DBGCTRL_reg(ADC0, 0);
	 hri_adc_write_CTRLA_reg(ADC0, 0x0402); 
 }



void setupTimer0(void)
{
	MCLK->APBAMASK.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->COUNT8.CTRLA.bit.ENABLE = 0;
	while (TC0->COUNT8.SYNCBUSY.reg);
	TC0->COUNT8.CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT8_Val;
	TC0->COUNT8.CTRLA.bit.PRESCALER |= TC_CTRLA_PRESCALER_DIV1_Val;
	while (TC0->COUNT8.SYNCBUSY.reg);
	TC0->COUNT8.INTENSET.bit.OVF = 1;
	
	
	// Enable InterruptVector
	NVIC_EnableIRQ(TC0_IRQn);
	
	
	// Event on overflow
	TC0->COUNT8.EVCTRL.bit.OVFEO = 1;
	// Enable TC
	TC0->COUNT8.CTRLA.bit.ENABLE = 1;
}

void setupTimer1(void)
{
	MCLK->APBAMASK.bit.TC1_ = 1;
	GCLK->PCHCTRL[TC1_GCLK_ID].bit.GEN = 0;
	GCLK->PCHCTRL[TC1_GCLK_ID].bit.CHEN = 1;
	while(GCLK->PCHCTRL[TC1_GCLK_ID].bit.CHEN != 1);
	
	TC1->COUNT16.CTRLA.bit.ENABLE = 0;
	while (TC1->COUNT16.SYNCBUSY.reg);
	TC1->COUNT16.CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT16_Val;
	TC1->COUNT16.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV1_Val;
	while (TC1->COUNT16.SYNCBUSY.reg);
	TC1->COUNT16.INTENSET.bit.OVF = 1;
	
	// Enable InterruptVector
	NVIC_EnableIRQ(TC1_IRQn);
	
	// Count on event
	TC1->COUNT16.EVCTRL.bit.EVACT = TC_EVCTRL_EVACT_COUNT_Val;
	// This works but should be after EVSYS setup according to the data sheet
	//TC1->COUNT16.EVCTRL.bit.TCEI = 1;
	//TC1->COUNT16.CTRLA.bit.ENABLE = 1;
}

void TC0_Handler()
{
	if (TC0->COUNT8.INTFLAG.bit.OVF) {
		REG_TC0_INTFLAG = TC_INTFLAG_OVF;
	}
}

void TC1_Handler()
{
	if (TC1->COUNT16.INTFLAG.bit.OVF) {
		REG_TC1_INTFLAG = TC_INTFLAG_OVF;
	}
}

volatile uint16_t count0 = 0;
volatile uint16_t count1 = 0;
volatile uint16_t count2 = 0;

int main(void)
{
	/* Initialize the SAM system */
	SystemInit();
	MCLK->APBBMASK.bit.EVSYS_ = 1;
	setupTimer0();
	setupTimer1();
#if USE_TC1
	EVSYS->USER[EVSYS_ID_USER_TC1_EVU].reg = 1;
#endif
	PORT->Group[1].DIRSET.reg = 1<<3;	
#if USE_PB3
	PORT->Group[1].EVCTRL.bit.EVACT0 = 3;
	PORT->Group[1].EVCTRL.bit.PID0 = 3;
	EVSYS->USER[EVSYS_ID_USER_PORT_EV_1].reg = 1;
#endif
#if USE_ADC
	ADC_0_init();
	EVSYS->USER[EVSYS_ID_USER_ADC0_START].reg = 1;
#endif	
	const EVSYS_CHANNEL_Type channel0Config = 
	{
		.bit.EDGSEL = 0, // 0 when using async
		.bit.EVGEN = EVSYS_ID_GEN_TC0_OVF,
		.bit.PATH = EVSYS_CHANNEL_PATH_ASYNCHRONOUS_Val
	};
	EVSYS->Channel[0].CHANNEL.reg = channel0Config.reg;
	//while(EVSYS->CHSTATUS.bit.CHBUSY0);
	// Enable event input
#if USE_TC1
	TC1->COUNT16.EVCTRL.bit.TCEI = 1;
	TC1->COUNT16.CTRLA.bit.ENABLE = 1;
#endif
#if USE_PB3
	PORT->Group[1].EVCTRL.bit.PORTEI0 = 1;
#endif
#if USE_ADC
	ADC0->CTRLA.bit.ENABLE = 0;
	while (ADC0->SYNCBUSY.bit.ENABLE)
		;
	ADC0->EVCTRL.bit.STARTEI = 1;
	ADC0->CTRLA.bit.ENABLE = 1;
	while (ADC0->SYNCBUSY.bit.ENABLE)
		;
#endif	
	while (1) 
	{
		//TC0->COUNT8.CTRLBSET.bit.CMD = TC_CTRLBSET_CMD_READSYNC_Val;
		//count0 = TC0->COUNT8.COUNT.reg;
		#if USE_TC1
			TC1->COUNT16.CTRLBSET.bit.CMD = TC_CTRLBSET_CMD_READSYNC_Val;
			count1 = TC1->COUNT16.COUNT.reg;
		#endif
		#if USE_PB3
			// uncomment to prove PB03 is an output that can be toggled
			//PORT->Group[1].OUTTGL.reg = 1<<3;
		#endif
		#if USE_ADC
			// uncomment to prove ADC works at all; comment to make event be the start trigger.
			//ADC0->SWTRIG.bit.START = 1;
			while (!ADC0->INTFLAG.bit.RESRDY)
				;
			count2++; 
			PORT->Group[1].OUTTGL.reg = 1<<3;
		#endif
	}
}	

 

jeff

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

OK, I have my code reading ADC at finely variable rates from 1.03us/Sa upwards using 12 bit one shot on event... Thank you Lars.

 

If I needed to double this rate, I accule I could use ADC1 with an event on CC at half of period, right?

 

(or on a chip with 3+ ADC's, use CC0 and CC1 match to make three samples per ADC minimum conversion cycle)?

jeff

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

Hi,

 

I'm struggling with a similar issue at the moment.  In my case I'm trying to do timer capture of a pulse on PA13.  I'm able to capture on the timer IO directly but when I try to route through EVSYS it is not working.  Right now I'm using the Adafruit Arduino port for SAMD51 because it is convenient:

 

https://gist.github.com/lgbeno/0...

 

#define PA13 22
#define J5E PA13

#define USE_EVSYS

void e_irq() {
  TC2->COUNT16.CTRLBSET.bit.CMD = 4;
  while(TC2->COUNT16.SYNCBUSY.bit.COUNT);
  Serial.println(TC2->COUNT16.COUNT.reg);
}

void setup() {
  Serial.begin(115200);
  while(!Serial);
  Serial.println("TC2 Test");
  
  MCLK->APBAMASK.reg |= MCLK_APBAMASK_EIC;
  MCLK->APBBMASK.reg |= MCLK_APBBMASK_TC2;
  
  GCLK->GENCTRL[7].bit.SRC = 4;
  GCLK->PCHCTRL[TC2_GCLK_ID].bit.GEN = 7;
  GCLK->PCHCTRL[TC2_GCLK_ID].bit.CHEN = 1;
  GCLK->GENCTRL[7].bit.GENEN = 1;
  
  TC2->COUNT16.CTRLA.bit.SWRST = 1;
  while(TC2->COUNT16.SYNCBUSY.bit.SWRST);
  
  TC2->COUNT16.INTENSET.reg = TC_INTENSET_MC0 | TC_INTENSET_MC1 | TC_INTENSET_OVF;
  
#ifdef USE_EVSYS
  TC2->COUNT16.EVCTRL.reg = TC_EVCTRL_EVACT_PPW | TC_EVCTRL_TCEI;
#endif
  TC2->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | 
                           TC_CTRLA_COPEN0 | TC_CTRLA_COPEN1 |
                           TC_CTRLA_CAPTEN0 | TC_CTRLA_CAPTEN1 |
                           TC_CTRLA_ENABLE;
  NVIC_EnableIRQ( TC2_IRQn ) ;
  
  // PA13 connectomux to E
#ifdef USE_EVSYS
  PORT->Group[0].PMUX[6].bit.PMUXO = 0;
#else
  PORT->Group[0].PMUX[6].bit.PMUXO = 4;
#endif
  PORT->Group[0].PINCFG[13].bit.PULLEN = 0;
  PORT->Group[0].PINCFG[13].bit.INEN = 1;
  PORT->Group[0].PINCFG[13].bit.PMUXEN = 1;
  
  //attachInterrupt(J5E,e_irq,RISING);
  EIC->INTENCLR.bit.EXTINT = (1<<13);
  EIC->EVCTRL.bit.EXTINTEO = (1<<13);

  EVSYS->USER[EVSYS_ID_USER_TC2_EVU].reg = 1;
  EVSYS->Channel[0].CHANNEL.reg =(uint32_t) (EVSYS_ID_GEN_EIC_EXTINT_13 | EVSYS_CHANNEL_PATH_ASYNCHRONOUS);
}

void TC2_Handler() {
  Serial.print("TC2 IRQ ");
  Serial.print(TC2->COUNT16.INTFLAG.reg,HEX);
  TC2->COUNT16.INTFLAG.reg = 0x33;
  Serial.print(" ");
  Serial.print(TC2->COUNT16.CC[0].reg);
  Serial.print(" ");
  Serial.print(TC2->COUNT16.CC[1].reg);
  Serial.print(" ");
  Serial.println(TC2->COUNT16.INTFLAG.reg,HEX);
}

void loop() {
  delay(500);
}

 

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

In further review, one thing that I missed in the code above is to enable MCLK_APBBMASK_EVSYS. Hopefully this is the issue, will test tomorrow.

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

Don't set the COPEN bits when capturing from events:

0   Event from Event System is selected as trigger source for capture operation on channel x.

1   I/O pin is selected as trigger source for capture operation on channel x.

/Lars
 

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

Lars, Thank you for your help.  Incredible what a little rest can do, I have it working right now.  Last night in desperation I was testing both ways with COPEN bits set and not set.  In reality it should have been encapsulated in a #ifndef USE_EVSYS.  I was able to solve my issues though.  It turned out to be a combination of a few things:

 

1) Very important to enable MCLK_APBBMASK_EVSYS (duh smiley)

2) EIC->EVCTRL.bit.EXTINTEO is Enable protected so critical to disable the EIC before writing

3) As you mentioned, TC2_CTRLA_COPEN0 and TC2_CTRLA_COPEN1 need to be cleared

 

Here is the functional code:

See diff here https://gist.github.com/lgbeno/0... (note that the line 20 change is unrelated, I wanted to use a faster clock)

#define PA13 22
#define J5E PA13

#define USE_EVSYS

void e_irq() {
  TC2->COUNT16.CTRLBSET.bit.CMD = 4;
  while(TC2->COUNT16.SYNCBUSY.bit.COUNT);
  Serial.println(TC2->COUNT16.COUNT.reg);
}

void setup() {
  Serial.begin(115200);
  while(!Serial);
  Serial.println("TC2 Test");
  
  MCLK->APBAMASK.reg |= MCLK_APBAMASK_EIC;
  MCLK->APBBMASK.reg |= MCLK_APBBMASK_TC2 | MCLK_APBBMASK_EVSYS;
  
  GCLK->GENCTRL[7].bit.SRC = 3;
  GCLK->PCHCTRL[TC2_GCLK_ID].bit.GEN = 7;
  GCLK->PCHCTRL[TC2_GCLK_ID].bit.CHEN = 1;
  GCLK->GENCTRL[7].bit.GENEN = 1;
  
  TC2->COUNT16.CTRLA.bit.SWRST = 1;
  while(TC2->COUNT16.SYNCBUSY.bit.SWRST);
  
  TC2->COUNT16.INTENSET.reg = TC_INTENSET_MC0 | TC_INTENSET_MC1 | TC_INTENSET_OVF;
  
#ifdef USE_EVSYS
  TC2->COUNT16.EVCTRL.reg = TC_EVCTRL_EVACT_PPW | TC_EVCTRL_TCEI;
#endif
  TC2->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | 
#ifndef USE_EVSYS
                           TC_CTRLA_COPEN0 | TC_CTRLA_COPEN1 |
#endif
                           TC_CTRLA_CAPTEN0 | TC_CTRLA_CAPTEN1 |
                           TC_CTRLA_ENABLE;
  NVIC_EnableIRQ( TC2_IRQn ) ;
  
  // PA13 connectomux to E
#ifdef USE_EVSYS
  PORT->Group[0].PMUX[6].bit.PMUXO = 0;
#else
  PORT->Group[0].PMUX[6].bit.PMUXO = 4;
#endif
  PORT->Group[0].PINCFG[13].bit.PULLEN = 0;
  PORT->Group[0].PINCFG[13].bit.INEN = 1;
  PORT->Group[0].PINCFG[13].bit.PMUXEN = 1;

#ifdef USE_EVSYS
  attachInterrupt(J5E,e_irq,RISING);
  EIC->CTRLA.bit.ENABLE = 0;
  EIC->INTENCLR.bit.EXTINT = (1<<13);
  EIC->EVCTRL.bit.EXTINTEO = (1<<13);
  EIC->CTRLA.bit.ENABLE = 1;

  EVSYS->USER[EVSYS_ID_USER_TC2_EVU].reg = 1;
  EVSYS->Channel[0].CHANNEL.reg =(uint32_t) (EVSYS_ID_GEN_EIC_EXTINT_13 | EVSYS_CHANNEL_PATH_ASYNCHRONOUS);
#endif
}

void TC2_Handler() {
  Serial.print("TC2 IRQ ");
  Serial.print(TC2->COUNT16.INTFLAG.reg,HEX);
  TC2->COUNT16.INTFLAG.reg = 0x33;
  Serial.print(" ");
  Serial.print(TC2->COUNT16.CC[0].reg);
  Serial.print(" ");
  Serial.print(TC2->COUNT16.CC[1].reg);
  Serial.print(" ");
  Serial.println(TC2->COUNT16.INTFLAG.reg,HEX);
}

void loop() {
  delay(500);
}

 

Now the next thing to clean up, at the moment I am cheating some to us the arduino function attachInterrupt(J5E,e_irq,RISING); to setup the EIC (and possibly the PORT).  I'd like to use a more pure version.

 

So far this is not a functional substitute:


  Serial.println("Configuring PORT");
  // PA13 connectomux to E
  PORT->Group[0].PMUX[6].bit.PMUXO = 0;
  PORT->Group[0].PINCFG[13].bit.PULLEN = 0;
  PORT->Group[0].PINCFG[13].bit.INEN = 1;
  PORT->Group[0].PINCFG[13].bit.PMUXEN = 1;

  Serial.println("Resetting EIC");
  //EIC->CTRLA.bit.SWRST = 1;
  //while(EIC->SYNCBUSY.bit.SWRST);
  EIC->CTRLA.bit.ENABLE = 0;
  EIC->EVCTRL.reg = (1<<13);
  //EIC->ASYNCH.reg = (1<<13);
  EIC->CONFIG[1].bit.SENSE5 = 3;
  Serial.println("Enabling EIC");
  EIC->CTRLA.bit.ENABLE = 1;
  while(EIC->SYNCBUSY.bit.ENABLE);

It seems to hang when waiting for enable, if I uncomment EIC->ASYNCH.reg = (1<<13); the it does work.  Just guessing that a SWRST is disabling some clock that I need to go back and re-connect.