[SOLVED] SAMD51 Event System Output on Pin

Hi, I'm looking for a way to output a SAMD51 event on a pin. I have followed @MartinL 's helpful posts on similar themes but these all seem to be using the event system as inputs. I'm using ItsyBitsy M4 for intial development but have access to Metro M4 if needed.

Here's what I have so far. I get 10 kHz square wave on D10, as expected as a TCC output pin, but nothing on D9, which is where I'm trying to route the event.

void setup() {
  init_tcc1(); // start a 10 kHz waveform on D10
  init_evsys(); // start the event system to feed count from TCC to D9

void loop() {
}

void init_evsys(void)
{
  // Event System ///////////////////////////////////////////////////////////////////////////////
  MCLK->APBBMASK.reg |= MCLK_APBBMASK_EVSYS;         // Switch on the event system peripheral

  // Select the event system user on channel 0 (USER number = channel number + 1)
  EVSYS->USER[EVSYS_ID_USER_PORT_EV_0].reg = EVSYS_USER_CHANNEL(1);         // Set the event user (receiver) as TCC1 Overflow (must be set async in generator)

  // Select the event system generator on channel 0
  EVSYS->Channel[0].CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT |              // No event edge detection
                                  EVSYS_CHANNEL_PATH_ASYNCHRONOUS |                 // Set event path as asynchronous
                                  EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_TCC1_OVF);   // Set event generator (sender) TCC1 overflow

  // For testing, set D9 (PA19) as EVENT output
  PORT->Group[PORTA].DIRSET.reg = PORT_PA19;
  PORT->Group[PORTA].EVCTRL.reg = PORT_EVCTRL_EVACT0_OUT |        // Set pin to follow event level output for event 0
                                  PORT_EVCTRL_PID0(19);           // Set Pin 19 (PA19/D9) as the output for event 0

// Have also tried, without success:

//  PORT->Group[g_APinDescription[9].ulPort].PINCFG[g_APinDescription[9].ulPin].bit.PMUXEN = 1;
  // Set the peripheral multiplexer to peripheral (even or odd port number) to mode E/O(5): TCC0, Channel x
//  PORT->Group[g_APinDescription[9].ulPort].PMUX[g_APinDescription[9].ulPin >> 1].reg |= PORT_PMUX_PMUXO(0); // PMUXO because PA19 is odd, (0) = peripheral function A = EIC
}

void init_tcc1(void)
{
  // Using TCC1 and enabling waveform outputs on channel 4 (PA20)
  // Set up the generic clock (GCLK7) to clock timer TCC1
  GCLK->GENCTRL[7].reg = GCLK_GENCTRL_DIV(1) |       // Divide the 48MHz clock source by divisor 1: 48MHz/1 = 48MHz
                         GCLK_GENCTRL_IDC |          // Set the duty cycle to 50/50 HIGH/LOW
                         GCLK_GENCTRL_GENEN |        // Enable GCLK7
                         GCLK_GENCTRL_SRC_DFLL;      // Select 48MHz DFLL clock source
  while (GCLK->SYNCBUSY.bit.GENCTRL7);               // Wait for synchronization

  GCLK->PCHCTRL[25].reg = GCLK_PCHCTRL_CHEN |        // Enable the TCC1 peripheral channel
                          GCLK_PCHCTRL_GEN_GCLK7;    // Connect generic clock 7 to TCC1

  // Enable the peripheral multiplexer on pins
  PORT->Group[g_APinDescription[10].ulPort].PINCFG[g_APinDescription[10].ulPin].bit.PMUXEN = 1;

  // Set the peripheral multiplexer to peripheral (even or odd port number) to mode E/O(5): TCC0, Channel x
  PORT->Group[g_APinDescription[10].ulPort].PMUX[g_APinDescription[10].ulPin >> 1].reg |= PORT_PMUX_PMUXE(5); // PMUXE because PA20 is even, (5) = peripheral function F

  TCC1->CTRLA.reg = TC_CTRLA_PRESCALER_DIV8 |        // Set prescaler to 8, 48MHz/8 = 6MHz
                    TC_CTRLA_PRESCSYNC_PRESC;        // Set the reset/reload to trigger on prescaler clock

  TCC1->WAVE.reg = TCC_WAVE_WAVEGEN_NFRQ;             /* Set-up TCC0 timer for single slope PWM mode with interrupt/event at the top (DSTOP) */
  while (TCC1->SYNCBUSY.bit.WAVE);                    // Wait for synchronization

  // set up event output on overflow for event channel 0
  TCC1->EVCTRL.reg = TCC_EVCTRL_OVFEO;

  TCC1->PER.reg = 299;                               // Set-up the PER (period) register 10kHz PWM
  while (TCC1->SYNCBUSY.bit.PER);                    // Wait for synchronization

  TCC1->CTRLA.bit.ENABLE = 1;                        // Enable timer TCC1
  while (TCC1->SYNCBUSY.bit.ENABLE);                 // Wait for synchronization
}

Please could anyone familiar with SAMD51 comment on what I might be doing wrong here?

I have had success. Here is how the PORT config needed to be set:

  PORT->Group[PORTA].DIRSET.reg = PORT_PA19;
  PORT->Group[PORTA].OUTSET.reg = PORT_PA19;                      // Needed to keep output in-phase with TCCn (generating OVF event) waveform output pin in NFRQ mode
  PORT->Group[PORTA].EVCTRL.reg = PORT_EVCTRL_PORTEI0 |
                                  PORT_EVCTRL_EVACT0_TGL |        // Set pin to toggle output on event for event 0
                                  PORT_EVCTRL_PID0(19);           // Set Pin 19 (PA19/D9) as the output for event 0

My mistake was thinking that PORT_EVCTRL_PORTEI0 was only needed if using the pin as an event generator.

Of note, the PA19 event-based waveform from TCC1 rises and falls around 150 ns before the PA20 TCC1 waveform output pin, which is slightly surprising given that datasheet section 32.6.4 says:

"For SET, CLEAR and TOGGLE event actions, the action will be executed up to three clock cycles after a rising edge."

Thanks again to @MartinL and @jefftech123 for their previous examples.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.