Problem with UNO R4 AGT timer with LOCO source

I am trying to use the Asynchronous General Purpose Timer (AGT) timer in the RA4M1 as an ELC source event for a synchronous ADC trigger. I have been successful in using it at high speeds using the PCLKB clock, but it doesn't seem to work as expected at lower speeds with the AGTLCLK clock. With the BCLKB/8 clock source, the slowest PCLKB clock available to the AGT (see the RA4M1 Hardware Manual section 23.2.5, page 514), the longest possible AGT period is about 22 milliseconds.

I a want to make continuous scans, like I did here, using the AGT / ELC for the trigger, but have the scans begin at timed intervals rather than by a software command or an external event. I would like to be able to set intervals of seconds to minutes for data logging.

One of the clock sources available to the AGT is AGTLCLK, which is the same as the LOCO click. The RA4M1 manual states in several places that the frequency of this clock is 32768 Hz. If I set the timer up with no divisor, it's more like 0.55 Hz, with a period of almost 2 seconds. That is over 55,000 times slower than BCLKB/8 (I had to check the math several times before I finally accepted this number). That's an enormous gap (5 orders of magnitude!), and I cannot get a period of, say, 1 or 2 seconds.

Not only is this rate several orders of magnitude slower than expected; it's the same no matter what value is loaded into the AGT0 register before starting the timer. Using counts of 1 and 65535 both produced the same result. The divisors that can be applied do work, but that is the only variation I see at this point.

This small sketch illustrates the problem; the register definitions are from the work of @susan-parker. Run it and the LED will blink slowly. If you try it, you will see that it does not matter what you put into AGT0, the rate is the same. I connected an oscilloscope between the LED pin and the ground pin to measure the rate.

#include "IRQManager.h"

// This sketch sets up the AGT1 timer with the source clock AGTLCLK (LOCO).
// It shows that this manner of operation does not work as expected

// ... #defines from Susan Parker's file:  susan_ra4m1_minima_register_defines.h
// interrupt controller
#define ICUBASE         0x40000000                                         // ICU Base - See 13.2.6 page 233, 32 bits -
#define ICU_IELSR       0x6300                                             // ICU Event Link Setting Register n
#define ICU_IELSR00     ((volatile unsigned int *)(ICUBASE + ICU_IELSR))   // IELSR register 0
#define ICU_IELSR_IR    0x10000                                           // Interrupt Status Flag bit (IR, bit 16), in the IELSRx register, ISR must clear
// Module Stop Control Register D
#define MSTP            0x40040000                                      // Module Registers
#define MSTP_MSTPCRD    ((volatile unsigned int   *)(MSTP + 0x7008))    // Module Stop Control Register D
#define MSTPD2          2                                               // AGT1   - Asynchronous General Purpose Timer 1 Module
#define AGTBASE         0x40084000
// Asynchronous General Purpose Timer (AGT)
#define AGT1_AGT        ((volatile unsigned short *)(AGTBASE + 0x100))  // AGT1 Counter Register
#define AGT1_AGTCR      ((volatile unsigned char  *)(AGTBASE + 0x108))  // AGT1 Control Register
#define AGT1_AGTMR1     ((volatile unsigned char  *)(AGTBASE + 0x109))  // AGT1 Mode Register 1
#define AGT1_AGTMR2     ((volatile unsigned char  *)(AGTBASE + 0x10A))  // AGT1 Mode Register 2
#define AGT1_AGTIOC     ((volatile unsigned char  *)(AGTBASE + 0x10C))  // AGT1 I/O Control Register
#define AGT1_AGTISR     ((volatile unsigned char  *)(AGTBASE + 0x10D))  // AGT Event Pin Select Register
#define AGT1_AGTCMSR    ((volatile unsigned char  *)(AGTBASE + 0x10E))  // AGT Compare Match Function Select Register
#define AGT1_AGTIOSEL   ((volatile unsigned char  *)(AGTBASE + 0x10F))  // AGT Pin Select Register
// ...

#define ICU_IELSR_IR    0x10000                                           // Interrupt Status Flag bit (IR, bit 16), in the IELSRx register, ISR must clear

#define AGTSRC_AGTLCLK  4                                   // divided clock specified by AGTLCLK
#define AGTSRC_AGT0UNDF 5                                   // AGT0 underflow as clock source
#define AGT_AGTCR_TUNDF 0x20                                // TUNDEF bit in AGTCR, clear in ISR to acknowledge

volatile bool bInterrupted;                                 // set to true in the ISR

GenericIrqCfg_t cfgAgtIrq;                                  // structure defined in IRQManager.h

bool nLEDStat;

void setup() {
  pinMode(13, OUTPUT);
  Serial.begin(115200);
  while(!Serial);
// set up AGT1
  cfgAgtIrq.irq = FSP_INVALID_VECTOR;                       // set up structure
  cfgAgtIrq.ipl = 12;                                       // priority level
  cfgAgtIrq.event = (elc_event_t)0x21;                      // AGT1_AGTI interrupt
  IRQManager::getInstance().addGenericInterrupt(cfgAgtIrq, 
                                                 AGT_ISR);  // attach the AGT ISR
  *MSTP_MSTPCRD &= (0xFFFFFFFF - (0x01 << MSTPD2));         // clear MSTPD2 bit in Module Stop Control Register D to run AGT1
  *AGT1_AGTCR = 0;                                          // clear all AGT1 registers
  *AGT1_AGTMR2 = 0;
  *AGT1_AGTIOC = 0;
  *AGT1_AGTISR = 0;
  *AGT1_AGTCMSR = 0;
  *AGT1_AGTIOSEL = 0;

  *AGT1_AGT = 10;                                           // count of 10, with a clock of 32768 Hz rate should be near 328 Hz
  *AGT1_AGTMR1 = AGTSRC_AGTLCLK << 4;                       // timer mode, clock source is AGTLCLK
  *AGT1_AGTMR2 = 0;                                         // divisor code, 1/1 (codes are 0 for 1/1 to 7 for 1/128)
  *AGT1_AGTCR = 1;                                          // start the timer
}

void loop()
{
  if(bInterrupted) {                                        // set by the ISR
    nLEDStat = !nLEDStat;                                   // toggle LED dtatus
    digitalWrite(13, nLEDStat ? HIGH : LOW);                // turn LED on or off
    bInterrupted = false;
    }
}

void AGT_ISR()
// ISR for the AGT
{
  *(ICU_IELSR00 + cfgAgtIrq.irq) &= ~(ICU_IELSR_IR);        // reset interrupt controller using Susan Parker's #defines
  *AGT1_AGTCR &= ~(AGT_AGTCR_TUNDF);                        // reset the flag for the timer
  bInterrupted = true;                                      // tell loop() to toggle the LED
}

Does anyone out there know enough about the AGT system to address this issue? I have looked, but I can't find any discussion of this in the Forum. Indeed, I have not found any posts that discuss the AGT in general except the one library that I mentioned here.

I am using AGT1 because AGT0 is used by delay() and millis(). AGT1 can use the underflow signal from AGT0 as a clock input. AGT0 is set up by the core code to run at 1kHz, so by using AGT0, AGT1 can run at rates with periods from 1 msec to a little over 1 minute (65.535 seconds). It works as expected. If you want to try it change the line

  *AGT1_AGTMR1 = AGTSRC_AGTLCLK << 4;

in setup() to

  *AGT1_AGTMR1 = AGTSRC_AGT0UNDF << 4;

If I put count of 1000 in AGT1_AGT results in a rate of 1 kHz.

The AGT has some operating modes that count external events or measure pulses. The RA4M1 pins for AGT1 are not connected to the outside world, although some of the AGT0 pins are, so these modes cannot be used on the Arduino.

How about using RTC with calendar count mode?

The accuracy of the RTC is not so good :thinking:, but I think it may be sufficient for obtaining logs at long intervals :relieved:

Once again, I have solved my own problem. It seems that when setting up the AGT, the order of some operations is important. In my original sketch, I cleared the MSTPD2 bit in Module Stop Control Register D (MSTPCRD) to activate AGT1; then I cleared the AGT registers to zero and then set up the AGT, AGTMR1 and AGTMR2 registers before setting the TSTART bit in AGTCR.

void setup() {
  pinMode(13, OUTPUT);
  Serial.begin(115200);
  while(!Serial);
// set up AGT1
  cfgAgtIrq.irq = FSP_INVALID_VECTOR;                       // set up structure
  cfgAgtIrq.ipl = 12;                                       // priority level
  cfgAgtIrq.event = (elc_event_t)0x21;                      // AGT1_AGTI interrupt
  IRQManager::getInstance().addGenericInterrupt(cfgAgtIrq, 
                                                 AGT_ISR);  // attach the AGT ISR
  *MSTP_MSTPCRD &= (0xFFFFFFFF - (0x01 << MSTPD2));         // activate AGT1
  *AGT1_AGTCR = 0;                                          // clear all AGT1 registers
  *AGT1_AGTMR1 = 0;
  *AGT1_AGTMR2 = 0;
  *AGT1_AGTIOC = 0;
  *AGT1_AGTISR = 0;
  *AGT1_AGTCMSR = 0;
  *AGT1_AGTIOSEL = 0;
  *AGT1_AGT = 50000;                                        // AGT count
  *AGT1_AGTMR1 = AGTSRC_AGTLCLK << 4;                       // TIMER mode with clock source
  *AGT1_AGTMR2 = 3;                                         // divisor code
  *AGT1_AGTCR = 1;                                          // start the timer
}

It seems that the registers need to be cleared before clearing the MSTPD2 bit in MSTPCRD; I suppose that the AGTCR register is the key. I moved the MSTPCRD operation down like this.

void setup() {
  pinMode(13, OUTPUT);
  Serial.begin(115200);
  while(!Serial);
// set up AGT1
  cfgAgtIrq.irq = FSP_INVALID_VECTOR;                       // set up structure
  cfgAgtIrq.ipl = 12;                                       // priority level
  cfgAgtIrq.event = (elc_event_t)0x21;                      // AGT1_AGTI interrupt
  IRQManager::getInstance().addGenericInterrupt(cfgAgtIrq, 
                                                 AGT_ISR);  // attach the AGT ISR
  *AGT1_AGTCR = 0;                                          // clear all AGT1 registers
  *AGT1_AGTMR1 = 0;
  *AGT1_AGTMR2 = 0;
  *AGT1_AGTIOC = 0;
  *AGT1_AGTISR = 0;
  *AGT1_AGTCMSR = 0;
  *AGT1_AGTIOSEL = 0;
  *MSTP_MSTPCRD &= (0xFFFFFFFF - (0x01 << MSTPD2));         // Activate AGT1 **MOVED**
  *AGT1_AGT = 50000;                                        // AGT count
  *AGT1_AGTMR1 = AGTSRC_AGTLCLK << 4;                       // TIMER mode with clock source
  *AGT1_AGTMR2 = 3;                                         // divisor code
  *AGT1_AGTCR = 1;                                          // start the timer
}

Now it works as expected. The big mystery is why this affected one clock mode and not the others.

A wide range of clock rates is available, from 24 MHz to a period of 512 seconds, about 8½ hours. This table shows the range of clock rates possible with the AGT system.

Clock Source Frequency Period Max Period Min Frequency
PCLKB 24 MHz 0.042 µsec 2.7 msec 366 Hz
PCLKB/2 12 MHz 0.083 µsec 5.5 msec 183 Hz
PCLKB/8 3 MHz 0.333 µsec 21.8 msec 45.8 Hz
AGTLCLK/1 32.768 kHz 30.5 µsec 2.0 sec 0.5 Hz
AGTLCLK/2 16.384 kHz 61.0 µsec 4.0 sec 0.25 Hz
AGTLCLK/4 8.192 kHz 122 µsec 8.0 sec 0.125 Hz
AGTLCLK/8 4.096 kHz 244 µsec 16.0 sec 0.0625 Hz
AGTLCLK/16 2.048 kHz 488 µsec 32.0 sec 0.0313 Hz
AGTLCLK/32 1.024 kHz 976 µsec 64.0 sec 0.0156 Hz
AGTLCLK/64 512 Hz 1.95 msec 128 sec 0.00781 Hz
AGTLCLK/128 256 Hz 3.91 msec 512 sec 0.00391 Hz
AGT0 1 kHz 1 msec 65.5 sec 0.0153 Hz

The pins connected to AGT1 are not connected to any Arduino pins, so it cannot be used for timing external operations or counting external events. It can be used with an interrupt, as shown in the sketch, or it can be the event source to the ELC for a timed trigger for the ADC, as I am doing, and some for some other uses.

The AGTSCLK (SOCO) clock source cannot be used. The SOCO clock requires an external crystal, but the Arduino does not have one, and the pins for the crystal are not connected to the outside world.

To @embeddedkiddie. I am, for now, unfamiliar with the RTC; I'll get to it soon. The AGT cannot use the RTC as a clock source, but its rate is stated in the RA4M1 hardware manual to be the same 32768 Hz as the AGTLCLK (LOCO) clock, so that would be redundant.

1 Like

I just meant that instead of using the RTC as the clock source for the AGT, you can simply use the RTC interrupt to perform periodic processing.

Anyway, congrats on solving your problem :+1:

Thanks @embeddedkiddie. I read that page only a few hours ago. It looks like you can get just about any long-term interval you want from the calendar count mode.

The periodic interrupt looks interesting, too. I was mistaken when I stated that the periodic interrupt was 32768 Hz; I don't know where that came from. The page you cite says that the interrupt can be anywhere from 2 seconds to 128 Hz. Useful stuff.

1 Like

Correction to my original post:

I said that the maximum period available from the AGT was "512 seconds, about 8½ hours." 512 seconds is, of course, about 8½ minutes not hours.

That figure of 512 seconds is also incorrect. The maximum number of microseconds that can be input to the function that sets the AGT rate is a little over 299,000,000, or 299 seconds, about 4 minutes and 16 seconds. I'll have more on that soon.