2
\$\begingroup\$

Before I begin I'd like to mention that I'm relatively new to working with microcontrollers at this low level, so please bear with me.

I am trying to use an Adafruit Trinket M0 to process and generate signals to communicate over the Nintendo GameCube communications protocol. Namely, the SAMD21 microcontroller needs to be able to output bit sequences with a frequency of 1 microsecond. I know this is possible on 16MHz AVR microcontrollers with the existence of this library; therefore I imagine it would be possible to do so as well on the 48MHz SAMD21.

From some research online I found this code sample, based on which I made the following sketch to toggle the output pin on the Trinket M0 every 1μs:

// Physical pin labelled 3 on the Trinket M0
const uint8_t PIN = 7;

volatile uint8_t n = 0;
volatile uint8_t next_bit = 0;

void TC5_Handler(void)
{
    // Toggle pin
    PORT->Group[PORTA].OUTTGL.reg = (1 << PIN);

    // Reset timer interrupt flag
    TC5->COUNT16.INTFLAG.bit.MC0 = 1;
}

void setup()
{
    // Initialize serial
    Serial.begin(115200);

    // Configure pin as output
    PORT->Group[PORTA].PINCFG[PIN].reg = PORT_PINCFG_INEN;
    PORT->Group[PORTA].DIRSET.reg = (1ul << PIN);

    // Enable generic clock for Timer/Counter 4 and 5
    GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 |
        GCLK_CLKCTRL_ID(GCM_TC4_TC5);
    while (GCLK->STATUS.bit.SYNCBUSY);

    // Perform software reset
    TC5->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
    while (TC5->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY);
    while (TC5->COUNT16.CTRLA.bit.SWRST);

    // Configure TC5
    TC5->COUNT16.CTRLA.reg =
        TC_CTRLA_MODE_COUNT16 |     // Counter of 16 bits
        TC_CTRLA_WAVEGEN_MFRQ |     // Match frequency
        TC_CTRLA_PRESCALER_DIV1;    // Prescaler of 1 (no division)
    TC5->COUNT16.CC[0].reg = F_CPU / 1e6 - 1; // Trigger @ 1MHz=10^6Hz
    while (TC5->COUNT16.STATUS.bit.SYNCBUSY);

    // Configure interrupt
    uint32_t oldISER = NVIC->ISER[0];
    NVIC->ICER[0] |= ~0ul;  // Disable all interrupts
    NVIC->ICPR[0] = 1ul << TC5_IRQn;            // Clear pending timer interrupt
    NVIC->ISER[0] = 1ul << (TC5_IRQn & 0x1f);   // Enable TC5 interrupt
    TC5->COUNT16.INTENSET.bit.MC0 = 1;          // Match/Compare 0 interrupt
    while (TC5->COUNT16.STATUS.bit.SYNCBUSY);

    // Start counter
    TC5->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;  // Enable TC5
    while (TC5->COUNT16.STATUS.bit.SYNCBUSY);
}

void loop()
{
}

This works great. My logic analyzer shows me perfect 1μs pulses.

However, trying to add a small conditional statement in there to output "0" bit sequences according to the GameCube protocol causes the signal to instead adopt a frequency of closer to 2μs.

void TC5_Handler(void)
{
    // Toggle pin
    (next_bit ?
        PORT->Group[PORTA].OUTSET.reg :
        PORT->Group[PORTA].OUTCLR.reg) = (1 << PIN);
    next_bit = (n & 3) < 3 ? 0 : 1;
    ++n;

    // Reset timer interrupt flag
    TC5->COUNT16.INTFLAG.bit.MC0 = 1;
}

I didn't expect that little added code to suddenly make the function take that much longer than a microsecond. In that case, how can I make this task more efficient? Must I pursue a different route with generating these signals?

\$\endgroup\$
0
\$\begingroup\$

Yes, you do need to find another way. The original AVR code does not use 1 microsecond interrupts, it does not use interrupts at all. The SAMD interrupt example also does not say it can do 1 microsecond interrupts. If it could do 1us interrupts, it was most likely already using close to 100% of available CPU cycles to do it.

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ Thank you very much! I imagine in that case, a loop using inline assembly will be needed, along the lines of the original 16MHz AVR, only ported to the 48MHz Cortex-M0+? \$\endgroup\$ – Jacob Cazabon Aug 11 '19 at 23:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.