I am working with an ESP32 Dev Kit C and the Arduino framework.
I want to use timers to output a sequence where in a 2 ms period an interrupt is fired after 150 µs and another interrupt after 1850 µs. Then there is some work to be done at 2 ms and the cycle will repeat.
I am trying to achieve this with 2 timers in one shot mode (alarm at 150µs and 1850µs) and a third timer with an alarm at 2000 µs and auto-reload.
In the ISR of the third timer both one shot timers will be reset.
After some back and forth with the arduino library I got one one shot timer working together with the periodic timer, but never both one shot timers regardless of the combination of timer channels used. I think the timers should be independent from each other but every time I add the third timer, things go south.
Now I tried to manipulate the registers directly using TIMERG0 and TIMERG1. But all I get from this is some spikes on the GPIO Pins and not the desired square wave. The spikes have a frequency of 2 ms so I think something is wrong with the interrupt handling.
I have no knowledge about the FreeRTOS so every input on how I can set this up is highly appreciated!
Current MWE producing spikes on both GPIO pins (GPIO action is a place holder for some slightly more complex logic which I want to implement later):
#include <Arduino.h>
#include "soc/timer_group_struct.h"
#include "soc/timer_group_reg.h"
hw_timer_t *timerA, *timerB, *timerO = NULL;
const uint32_t alarmValues[2] = {150, 1850};
void IRAM_ATTR timer_isr_A();
void IRAM_ATTR timer_isr_B();
void IRAM_ATTR timer_isr_O(); // "overflow"
const uint8_t pinA = 19;
const uint8_t pinB = 18;
void setup()
{
pinMode(pinA, OUTPUT);
pinMode(pinB, OUTPUT);
const uint16_t prescale = 80; // timer frequency = 1 MHz -> 1 µs
timerA = timerBegin(0, prescale, true);
timerB = timerBegin(1, prescale, true);
timerO = timerBegin(2, prescale, true);
timerAttachInterrupt(timerA, &timer_isr_A, true);
timerAttachInterrupt(timerB, &timer_isr_B, true);
timerAttachInterrupt(timerO, &timer_isr_O, true);
timerAlarmWrite(timerA, alarmValues[0], false);
timerAlarmWrite(timerB, alarmValues[1], false);
timerAlarmWrite(timerO, 2000, true);
timerAlarmEnable(timerA);
timerAlarmEnable(timerB);
timerAlarmEnable(timerO);
}
void IRAM_ATTR timer_isr_A()
{
digitalWrite(pinB, 1);
}
void IRAM_ATTR timer_isr_B()
{
digitalWrite(pinA, 1);
}
void IRAM_ATTR timer_isr_O()
{
digitalWrite(pinA, 0);
digitalWrite(pinB, 0);
// Reset timerA and timerB (group 0) directly with registers, because API seems limited when called from timer0 wich is on the other timer group 1.
TIMERG0.hw_timer[0].config.enable = 0;
TIMERG0.hw_timer[1].config.enable = 0;
TIMERG0.hw_timer[0].cnt_low = 0; // reset counter
TIMERG0.hw_timer[0].cnt_high = 0;
TIMERG0.hw_timer[0].alarm_low = alarmValues[0]; // reload alarm, necessary in one shot mode
TIMERG0.hw_timer[0].alarm_high = 0;
TIMERG0.hw_timer[0].config.alarm_en = 1; // re-enable alarm
TIMERG0.hw_timer[1].cnt_low = 0; // reset counter
TIMERG0.hw_timer[1].cnt_high = 0;
TIMERG0.hw_timer[1].alarm_low = alarmValues[1]; // reload alarm, necessary in one shot mode
TIMERG0.hw_timer[1].alarm_high = 0;
TIMERG0.hw_timer[1].config.alarm_en = 1; // re-enable alarm
TIMERG0.hw_timer[0].config.enable = 1;
TIMERG0.hw_timer[1].config.enable = 1;
}
void loop()
{
}
Addendum 1
Before using TIMERGx I tried to use all three timers with auto-load and just reset the timers for 150µs and 850µs in the 2ms ISR:
...
timerAlarmWrite(timerA, alarmValues[0], true);
timerAlarmWrite(timerB, alarmValues[1], true);
...
and
void IRAM_ATTR timer_isr_O()
{
digitalWrite(pinA, 0);
digitalWrite(pinB, 0);
timerAlarmDisable(timerA); // Disable the timer
timerAlarmDisable(timerB); // Disable the timer
timerWrite(timerA, 0); // Reset the timer counter to 0
timerWrite(timerB, 0); // Reset the timer counter to 0
timerAlarmEnable(timerA);
timerAlarmEnable(timerB);
}


