Dlloydev / QuickPID

A fast PID controller with multiple options. Various Integral anti-windup, Proportional, Derivative and timer control modes.
MIT License
195 stars 50 forks source link

Suggestion: custom getClock() function to allow removing reliance on micros() #53

Closed ptashek closed 1 year ago

ptashek commented 2 years ago

First of all, thank you for the fork and continuing work on this useful library.

I have a suggestion that I use in my own custom version of Brett's library as seen in this project: https://github.com/ptashek/mb/tree/master/CruiseControl_W124 (PIDConfig and PIDController classes)

My approach is to use a virtual getClock() method, which allows me to override it as needed at the point of use. In the project above I'm using RTC ticks as my clock, incremented via the RTC periodic interrupt ISR. It's especially useful in projects which need additional timers, or re-configure the TCA clock, rendering millis() and micros() unusable.

The default implementation could still use micros() as it does now.

Dlloydev commented 2 years ago

Thanks for your interesting suggestion!

I do have an Arduino Every (somewhere) and didn't realize that it has an RTC. I'm extremely busy this summer, but sometime later this year, this would be something I'd like to try and implement. Also looks like the ESP32 dev boards have RTC, so I've got some learning and experimentation to do as I haven't yet worked with the RTC.

ptashek commented 2 years ago

While the 32k internal oscillator isn't great on the Atmega4809 it should be good enough for this use case. Probably the quickest way to start would be something like below. This gives a 1ms resolution, with a period of 32 clock cycles. Maximum resolution would be around 30 microseconds.

volatile uint32_t pitCounter = 0;

void RTC_init() {
  // Enable  internal 32k oscillator
  _PROTECTED_WRITE(CLKCTRL.OSC32KCTRLA, CLKCTRL_ENABLE_bm);
  // Wait for RTC synchronization
  while (RTC.STATUS > 0) {
    ;
  }
  // Enable RTC and set clock prescaler
  RTC.CTRLA |= RTC_PRESCALER_DIV1_gc | RTC_RTCEN_bm;
  // Set RTC to use internal 32k oscillator
  RTC.CLKSEL = RTC_CLKSEL_INT32K_gc;
  // Wait for PIT synchronization
  while (RTC.PITSTATUS > 0) {
    ;
  }
  // Set periodic interrupt period
  RTC.PITCTRLA |= RTC_PERIOD_CYC32_gc | RTC_PITEN_bm;
  // Enable periodic interrupt (used as clock)
  RTC.PITINTCTRL = RTC_PI_bm;
}

// RTC periodic interrupt handler (clock)
ISR(RTC_PIT_vect) {
  RTC.PITINTFLAGS = RTC_PI_bm;
  pitCounter++;
}