QUB-ASL / bzzz

Quadcopter with ESP32 and RaspberryPi
MIT License
7 stars 1 forks source link

Create timer interrupt #37

Closed alphaville closed 6 months ago

alphaville commented 1 year ago

We may need to create an ISR or two. In principle it is good practice.

alphaville commented 1 year ago

bump @jamie-54 @gunturiCM

alphaville commented 12 months ago

bump @jamie-54

alphaville commented 10 months ago

bump @jamie-54

alphaville commented 7 months ago

@Runway27, @pdavid747, @ejb-11, @Yuanbwcx, @Minanchi, please let me know if you can take care of this issue.

Runway27 commented 7 months ago

Sir, what exactly has to be done in this? I'm unable to understand the issue.

alphaville commented 7 months ago

If you see our code in src/main.cpp::loop, we have just put all functionality in the loop function. The rate at which loop runs is not fixed. It is standard practice to use an interrupt service routine (ISR) to guarantee that the loop function runs at a fixed rate.

I have written some few comments on this in CONTRIBUTING.md, Section 4.

alphaville commented 7 months ago

Here is a little example of using a timer to make an LED blink at a desired frequency:

#include <Arduino.h>

#define TIMER_ID 0
#define TIMER_PRESCALER 80
#define LED_PIN 14
#define TIMER_INTERVAL_uS 1000000

hw_timer_t *timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

/**
 * Here the timer state is declared as a global variable
 * so that it can be accessed by the loop function (and possibly
 * by other interrupts). When accessing `timerState` we should
 * be first acquiring its lock (i.e., the `timerMux`).
 */
volatile bool timerState = true;

/**
 * Callback, attached to the timer interrupt
 * It is generally advisable to keep the implementation of this
 * function as lean as possible (it should just toggle a flag).
 * This function is executed only ONCE every period.
 */
void IRAM_ATTR onTimer()
{
  taskENTER_CRITICAL_ISR(&timerMux);
  timerState = !timerState;
  taskEXIT_CRITICAL_ISR(&timerMux);
}

void setup()
{
  pinMode(LED_PIN, OUTPUT);

  timer = timerBegin(TIMER_ID, TIMER_PRESCALER, true);
  timerAttachInterrupt(timer, &onTimer, true);
  /*
   * timerAlarmWrite is simply a counter; we count a number of
   * timer periods before calling the callback function (onTimer).
   * The second argument is the sampling period in micros.
   */
  timerAlarmWrite(timer, TIMER_INTERVAL_uS, true);
  timerAlarmEnable(timer);
}

/**
 * Unlike the callback function (onTimer), the loop function
 * runs continuous and is therefore executed several timer
 * per period.
 */
void loop()
{
  taskENTER_CRITICAL_ISR(&timerMux);
  digitalWrite(LED_PIN, timerState);
  taskEXIT_CRITICAL_ISR(&timerMux);
}

@Runway27 @ejb-11 @jamie-54 @Yuanbwcx @Minanchi @pdavid747 This issue hasn't been assigned to anyone. Please let me know who can take care of it. If you have questions, I'll be happy to discuss.

alphaville commented 7 months ago

On a side note, I've seen the following simpler approach being proposed here and there:

#include <Arduino.h>

#define LED_PIN 14
#define TIMER_INTERVAL_uS 1000000

const int samplingPeriodMs = 1000; // in ms
unsigned long startTime;
bool timerState = true;

void setup()
{
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
  startTime = millis(); // Initialize
}

void loop()
{
  // Now the execution is controlled by `millis()`
  if (millis() > startTime + samplingPeriodMs)
  {
    digitalWrite(LED_PIN, timerState);
    timerState = !timerState;
    startTime = millis();
  }
}

The problem with this is that it won't work for low sampling periods because millis is not very accurate - the same goes for micros.

Runway27 commented 7 months ago

Sir, I have quite a few open issues currently. Probably after finishings a few this week I can subscribe to this.