Enabling compare match interrupts for GPT on Arduino R4 WiFi

Hello everyone,
I am currently working with the Arduino R4 WiFi and I need some assistance with enabling compare martch interrupts for the GPT. Specifically, I would like to generate a break (toggle pin 2) when the GTCCRA register reaches 88 microseconds, and generate a match (toggle pin 2) when it reaches 96 microseconds in GTCCRB.
I have configured a timer in PWM saw mode and set all necessary registers, but I am unsure how to enable the interrupts and implement the Interrupt Service Routine (ISR). Any guidance or examples would be greatly appreciated!
Thank you!

As you know, RA4M1 has a complex architecture, so we cannot simply define an ISR based on the interrupt vector table like other architectures. Instead, we need to control the ICU (Interrupt Controller Unit) and ELC (Event Link Controller) combined with timer controller.

Renesas provides FSP (Flexible Software Package) as a solution to this.

For GPT, FspTimer.cpp and FspTimer.h are provided.

Unfortunately, I only have programming experience using the simple FspTimer::begin() and FspTimer::end(), but there are methods named FspTimer::setup_capture_a_irq() and FspTimer::setup_capture_b_irq() that may meet your requirements.

If you have any success in using these methods and would like to share your code here, it will be very helpful for other UNO R4 users.

1 Like

Thank you very much for your response. I am quite new to all of this things and i really get confused with some things. I haven´t used fsptimer or irqmanager on my code. I have configured the gpt0 registers, and was trying to configure the interruptions manually:

I define the event numbers:

#define GPT0_CCMPA_IRQn ((IRQn_Type)57)                                  // GTCCRA event number
#define GPT0_CCMPB_IRQn ((IRQn_Type)58)                                 // GTCCRB event number

I don´t know how the event numbers relate to the IRQnumbers in the vector table, or if I have to map them myself to any of the IESLR sources.

I algo configure NVIC like this:

#define NVIC_ISER    ((volatile uint32_t*)0xE000E100)                  // ISER registry 
#define NVIC_IPR     ((volatile uint8_t*)0xE000E400)                     // IPR registry
#define SCB_VTOR (*((volatile uint32_t*)0xE000ED08))               // VTOR registry>

void setup_gpt0_interrupts(void) {

    *((volatile uint32_t*) (0xE000ED00 + (GPT0_CCMPA_IRQn * 4))) = (uint32_t)&GPT0_CCMPA_IRQHandler;
    *((volatile uint32_t*) (0xE000ED00 + (GPT0_CCMPB_IRQn * 4))) = (uint32_t)&GPT0_CCMPB_IRQHandler;

    // Set priotirys
    NVIC_IPR[GPT0_CCMPA_IRQn] = 3 << 4;                            // GPT0_CCMPA priority
    NVIC_IPR[GPT0_CCMPB_IRQn] = 3 << 4;                            // GPT0_CCMPB priority

    // Enable NVIC interrupts
    NVIC_ISER[GPT0_CCMPA_IRQn / 32] |= (1 << (GPT0_CCMPA_IRQn % 32));
    NVIC_ISER[GPT0_CCMPB_IRQn / 32] |= (1 << (GPT0_CCMPB_IRQn % 32));
}

An I define my ISR like this:

void GPT0_CCMPA_IRQHandler(void) {
    // BREAK event
    GTST &= ~(1 << 0);                                        // Write 0 in TCFA flag
}

void GPT0_CCMPB_IRQHandler(void) {
    // MAB event
    GTST &= ~(1 << 1);                                       // Write 0 in TCFB flag

    GTCNT = 0;                                                   // Reset timer
}

I´m sure there are a few errors in my code and things I am forgetting in the interrupt configuration, so I will continue trying to set everything correctly.

Hi @dani361 ,

I think it's worth your trying low-level programming, such as configuring registers, since you need to read the hardware manual and understand the MCU architecture.

In that case, I think it would be useful to use the following header file, which defines all the registers described in the hardware manual.

Also, using FSP is not so easy, because I think it's essential to understand the MCU architecture.

For FSP, you can find the manual in below.

These are all the information I can share you. I wish you success :+1:

I´ve been working a little more on the proyect, but I still don´t understand how the event numbers in the vector table relate to the interrupt configuration.

The event numbers in the vector table are 057h GPT0_CCMPA and 058h GPT0_CCMPB so I define

#define GPT0_CCMPA_EVENT 57
#define GPT0_CCMPB_EVENT 58

I try to configure IESLR0 for gtccra interrupt and IESLR1 for gtccrb interrupt (not really sure if here I should use de IRQ number or the event number:

#define ICU_IELSR0 ((volatile uint32_t*)0x40006300)  
#define ICU_IELSR1 ((volatile uint32_t*)0x40006304) 

void configure_ielsr() {
    *ICU_IELSR0 = 0;                                      // Clear registry
    *ICU_IELSR0 |= (1 << 0); 

    *ICU_IELSR1 = 0;                                      // Clear registry
    *ICU_IELSR1 |= (1 << 1); 
}

Reading this registers gives me this output:
ICU.IELSR0 value: 0x00000001
ICU.IELSR1 value: 0x00000002

I then configure IRQCR0 AND IRQCR1 for rising and falling edges

Then I configure NVIC_ISER0. Also not sure if I have to use the IRQ number or the event number

#define NVIC_ISER0 ((volatile uint32_t*)0xE000E100)

#define GPT0_CCMPA_IRQn ((IRQn_Type) 0) 
#define GPT0_CCMPB_IRQn ((IRQn_Type) 1)

void configure_nvic() {
    *NVIC_ISER0 |= (1 << GPT0_CCMPA_IRQn); 
    *NVIC_ISER0 |= (1 << GPT0_CCMPB_IRQn); 
}

The output for reading this registry is 0x0000001F.

At last I try to set prioritys:

#define NVIC_IPR0     ((volatile uint8_t*)0xE000E400)

void configure_nvic_ipr() {
    // IRQ 0 Priority
    *NVIC_IPR0 = (*NVIC_IPR0 & ~(0xFF << (GPT0_CCMPA_IRQn * 8))) | (1 << (GPT0_CCMPA_IRQn * 8));

    // IRQ 1 Priority
    *NVIC_IPR0 = (*NVIC_IPR0 & ~(0xFF << (GPT0_CCMPB_IRQn * 8))) | (2 << (GPT0_CCMPB_IRQn * 8));
}

This one seems its not written correctly:
NVIC_IPR0 value: 0x00000000

At the end, I realize Im not using the event numbers at all, and still can´t understand if I´m doing things correctly.

I got inspired by an example of GPT I found, and finally seems I achieved my objective. I setup GPT6 to compare match interrupt every 88 us (changing the state of my DMX_TX pin from LOW to HIGH) and overflow interrupt every 96 us, (changing the state of my DMX_TX pin from HIGH to LOW), then I use Serial1 to transmit data when overflow occurs. Seems to work by the moment:

#define DMX_TX_PIN 11 
#define DMX_RX_PIN 10 
#define DMX_DIR_PIN 2

#define DMX_UNIVERSE_SIZE 512
#define DMX_BUFFER_SIZE (DMX_UNIVERSE_SIZE + 1)

uint8_t dmxTXBuffer[DMX_BUFFER_SIZE];
uint8_t lastDMXValues[DMX_UNIVERSE_SIZE];

static volatile bool transmitDMXFlag = false;
static const IRQn_Type IRQn_OVF6 = IRQn_Type(16);
static const IRQn_Type IRQn_CCMPA = IRQn_Type(17);

//*********************************************************************

void gpt6_ccmpa_isr() {
    // Clear interrupt flag for compare match A
    R_ICU->IELSR_b[IRQn_CCMPA].IR = 0;
}

//*********************************************************************

void gpt6_ovf_isr() {
    // Clear interrupt flag for overflow
    R_ICU->IELSR_b[IRQn_OVF6].IR = 0;
    transmitDMXFlag = true; 
}

//*********************************************************************

void setup_gpt6() {
    R_MSTP->MSTPCRD_b.MSTPD6 = 0;

    // Select the IRQn_OVF6 interrupt to be triggered by the GPT6 overflow event
    R_ICU->IELSR_b[IRQn_OVF6].IELS = ELC_EVENT_GPT6_COUNTER_OVERFLOW;
    NVIC_SetVector(IRQn_OVF6, (uint32_t)gpt6_ovf_isr);
    NVIC_SetPriority(IRQn_OVF6, 12);
    NVIC_EnableIRQ(IRQn_OVF6);

    // Select the IRQn_CCMPA interrupt to be triggered by the GPT6 compare match event
    R_ICU->IELSR_b[IRQn_CCMPA].IELS = ELC_EVENT_GPT6_CAPTURE_COMPARE_A;
    NVIC_SetVector(IRQn_CCMPA, (uint32_t)gpt6_ccmpa_isr);
    NVIC_SetPriority(IRQn_CCMPA, 12);
    NVIC_EnableIRQ(IRQn_CCMPA);

    R_GPT6->GTCR = 0;                           // Saw-wave PWM mode, PS = 1
    R_GPT6->GTPR = 4607;                        // Duty cycle. OVF interrupt every 96 us
    R_GPT6->GTCNT = 0;                          // Start counter at 0
    R_GPT6->GTIOR = 1060000;                    // Enable GTIOCB output, GTIOCB pin output level at the start/stop of counting depends on the register setting, initial output is low, high output at GTCCRA/GTCCRB compare match       
    R_GPT6->GTCCR[0] = 4224;                    // GTCCRA value. Interrupt every 88 us 
    R_GPT6->GTCR_b.CST = 1;                     // Start timer
}

//*********************************************************************
void setup() {
    Serial.begin(115200);
 
    Serial1.begin(250000, SERIAL_8N2);   // Serial1 for DMX - 250000 bps

    setupTXmode();

    // Setup first 4 DMX channels
    setDMXChannel(1, 150);
    setDMXChannel(2, 150); 
    setDMXChannel(3, 150);
    setDMXChannel(4, 150);  
}

void setupTXmode() {
    setupTXpins();
    initializedmxTXBuffer();
    setup_gpt6();
}

void setupTXpins() {
    pinMode(DMX_TX_PIN, OUTPUT);
    pinMode(DMX_DIR_PIN, OUTPUT);  
    digitalWrite(DMX_DIR_PIN, HIGH); 
}

void initializedmxTXBuffer() {
    memset(dmxTXBuffer, 0, DMX_BUFFER_SIZE); 
    dmxTXBuffer[0] = 0; // Start code 0
    memcpy(lastDMXValues, dmxTXBuffer, DMX_BUFFER_SIZE); 
}

void setDMXChannel(uint16_t channel, uint8_t value) {
    if (channel > 0 && channel <= DMX_BUFFER_SIZE) {
        if (dmxTXBuffer[channel] != value) {
            dmxTXBuffer[channel] = value; 
            lastDMXValues[channel] = value; 
        }
    }
}

//*********************************************************************

void loop() {
    if (transmitDMXFlag) {
      Serial1.write(dmxTXBuffer, DMX_UNIVERSE_SIZE);
      transmitDMXFlag = false;
    }
}

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