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.
