Arduino Events


Hooking systick_isr() is useful in many situations such as this (and in TeensyThreads and many others). Even with this approach of accessing _VectorsRam directly there is still a problem if two libraries want to do that. To solve this conflict, a library can remember the last systick_isr() and call it. Something like:

Code:
static void (*previous_systick_isr)();

static void my_systick_isr() {
  (*previous_systick_isr)();
  /* do my own thing */
}

void MyClass::begin()
  {
        previous_systick_isr = _VectorsRam[15];
 	_VectorsRam[15] = my_systick_isr;
  }
 
Yes, we talked about this in the other thread. Its a standard technique - but only works if every lib does it.
I suggested to add some c++ code to the core that handles this chaning and makes it easy to use - maybe for every interrupt ?
One could add a function like "addToIntChain(intnum, function);"
 
Last edited:
I suggested to add some c++ code to the core that handles this chaning and makes it easy to use - maybe for every interrupt ?

MillisTimer is intended to fill this role, at least for access to SysTick.

The intention is most users would attach their functions to run normally from yield or as a lower priority interrupt. Of course EventResponder does give you the choice of having your function called immediately from SysTick's high priority. However, the goal is most people will follow the default path which gives best interoperability with other libraries and easiest access to shared data and the hundreds of Arduino features and libs that aren't reentrant.

One could add a function like "addToIntChain(intnum, function);"

How would that work?

I mean, we already have attachInterruptVector(). What would this do which attachInterruptVector does not? Where would it write the old value? Would it somehow know where to store it within any arbitrary interrupt routine which will be responsible for jumping to the prior vector when it completes?
 
Last edited:
Perhaps: attachInterruptVector() could return the current value of that vector. Ideally it will be zero. The 'aware' caller could then decide if they meant to supersede the prior vector, or whether they wish to work with the code already there. The caller could then call out to that vector address before or after their code completes. That wouldn't help with undo . . . but would provide a programmatic solution when chaining is needed.
 
For example like this:
Code:
class interruptChain {
  public:

    interruptChain() {
      prevInterruptPtr = NULL;
      intNum = 0;
    }

    void chainInterrupt(int intNumber, void (*interruptPtr)(void)) {
      if (intNumber > 0 && intNumber <= NVIC_NUM_INTERRUPTS + 16)  {
        intNum = intNumber;
        prevInterruptPtr = _VectorsRam[intNum];
        _VectorsRam[intNum] = interruptPtr;
      }
    }

    inline void callNext(void) {
      prevInterruptPtr();
    }

    void unChainInterrupt(void) {
      if (intNum) {
        _VectorsRam[intNum] = prevInterruptPtr;
      };
      prevInterruptPtr = NULL;
      intNum = 0;
    }

  protected:
    uint8_t intNum;
    void (*prevInterruptPtr)(void);
};


[B]//Test this :[/B]

[B]interruptChain myAdditionalSystick;[/B]
volatile int counter = 0;

[B]void mySystick(void) {
  counter++;
  myAdditionalSystick.callNext();
}
[/B]
void setup() {

  while (!Serial);
[B]  myAdditionalSystick.chainInterrupt(15, mySystick);[/B]

}

void loop() {
  static unsigned long t = millis();
  static int c = 0;
  unsigned long m = millis();
  if ( m - t > 500)  {
    Serial.print(m);
    Serial.print(" ");
    Serial.println(counter);
    t = m;
    c++;
    if (c > 10) {
      [B]myAdditionalSystick.unChainInterrupt();[/B]
    }
  }

}
Note, this one calls the new Interrupt before the others. Take it just as an example, not more. I spend a few minutes with it, and I'm sure it can be done better.
Perhaps we should write a class which can do both - insert befor all others or insert as last element.

It works with two or more int-functions, too. All needed is a interruptChain variable.
And, for example, you can add it to the SerialX interrupt and know when it's worth to call the event without polling available() (?!)


MillisTimer is intended to fill this role, at least for access to SysTick.
This class can be used to chain the MillisTimer.

Edit: Maybe, it's useful for the NMIs, too..
 
Last edited:
Note, this one calls the new Interrupt before the others. Take it just as an example, not more. I spend a few minutes with it, and I'm sure it can be done better.
Perhaps we should write a class which can do both - insert befor all others or insert as last element.

It works with two or more int-functions, too. All needed is a interruptChain variable.
And, for example, you can add it to the SerialX interrupt and know when it's worth to call the event without polling available() (?!)



This class can be used to chain the MillisTimer.

Edit: Maybe, it's useful for the NMIs, too..

Although I did advocate for "chaining" interrupts before, UNCHAINING is a bad idea. This causes problems in the scenario where you chain an interrupt, and then another interrupt chains on yours. If you need to disable your code, set a flag and enclose it in an "if" statement.

I think such complexity is overkill for a task with such limited applicability. But if you really want to do this, the Teensy system would need to keep a linked list of functions to call in order. Each interrupt would have such a list. Items can be added with a priority so they can be inserted anywhere on the list. Something like the code fragment below.

Code:
typedef void (*IsrFunc)();

class isr_list {
  std::list<IsrFunc> isr;
  void run() {
    for (IsrFunc fx : isr) {
      (*fx)()
    }
  }
  void add(IsrFunc f) { isr->push_back(f); }
  void remove(IsrFunc f) { isr->remove(f); };
  etc.
}

void systick_isr() {
  systick_list->run();
}
 
For the sake of peace and love the minimal version:

Code:
class interruptChain {
  public:

    void chainInterrupt(int intNumber, void (*interruptPtr)(void)) {
        prevInterruptPtr = _VectorsRam[intNumber];
        _VectorsRam[intNumber] = interruptPtr
    }

    inline void callNext(void) {
      prevInterruptPtr();
    }

  protected:
    void (*prevInterruptPtr)(void);
};

Paul, this answers your question where to store the vectors..
 
Last edited:
Just because you can doesn't mean you should....

I'm trying to keep an open mind about this, but that pretty much sums up my feelings so far on interrupt chaining. Convince me why we need this for something other than SysTick, or if for SysTick why MillisTimer isn't enough. Convince me this is actually a good idea. Convince me this isn't going to be seen as an ugly kludge and (in hindsight) a mistake that probably never should have been published, but can't be removed since it'll break some programs or libs using it?
 
Last edited:
Just because you can doesn't mean you should....

I'm trying to keep an open mind about this, but that pretty much sums up my feelings so far on interrupt chaining. Convince me why we need this for something other than SysTick, or if for SysTick why MillisTimer isn't enough. Convince me this is actually a good idea. Convince me this isn't going to be seen as an ugly kludge and (in hindsight) a mistake that probably never should have been published, but can't be removed since it'll break some programs or libs using it?

I fear, if these discussions are not enough to convince you, nothing is.
I'm just gonna have to stop saying "it's close to bare metal".
Evrything is good.

Edit: I'll make a library that disables all the things which are not needed by the vast majority who just want a fast simple single thread. I think this is more easy and faster done :)
 
Last edited:
@Frank and @Paul - For me I wonder if there is some happy middle ground.

That is I am assuming we need a system tick. i.e. for the millis() stuff..

Now as for the MillisTimer ? is it the issue that it brings in the event code or that something runs on the systick timer?

Currently the code that runs on each systick, does a few things:
Code:
void MillisTimer::runFromTimer()
{
	MillisTimer *timer = listActive;
	while (timer) {
		if (timer->_ms > 0) {
			timer->_ms--;
			break;
		} else {
			MillisTimer *next = timer->_next;
			if (next) next->_prev = nullptr;
			listActive = next;
			timer->_state = TimerOff;
			EventResponderRef event = *(timer->_event);
			event.triggerEvent(0, timer);
			if (timer->_reload) {
				timer->_ms = timer->_reload;
				timer->addToActiveList();
			}
			timer = listActive;
		}
	}
	bool irq = disableTimerInterrupt();
	MillisTimer *waiting = listWaiting;
	listWaiting = nullptr; // TODO: use STREX to avoid interrupt disable
	enableTimerInterrupt(irq);
	while (waiting) {
		MillisTimer *next = waiting->_next;
		waiting->addToActiveList();
		waiting = next;
	}
}
It seams to me, like we could easily short circuit this if there are no millis timers.
Could either have the systick ISR have pointer to method to call, or simple boolean that gets set when first/any millis timer is added...

Code:
extern "C" volatile uint32_t systick_millis_count;
void systick_isr(void)
{
	systick_millis_count++;
        if (MillisTimer::haveTimers() {
	    MillisTimer::runFromTimer();
        }
}
I am assuming haveTimers would be inline... So just a simple test...
 
My last statement ;-)The easiest and most compatible shortcut is to override the old systick (which was a "systick_millis_count++;" ) only if there is a "#include "EventResponder" in the program (for those who want it).
This lets the user use the old empty yield(){} and adds events only if explictly wanted.

Just because you can doesn't mean you should....
Then, why you asked "how" ?
How would that work?
Your rhetorical trick led me think you were really interested.
 
Last edited:
Good evening all. Been playing with a new toy I just got and now back to my Teensy-botics projects:). Anyway, I just ran a test sketch I had for event responder based off of KurtE's sketch but I added the TeensyThread blink example and the event responder is not working with TeensyThreads. Not sure if this is the correct behavior? Separately they seem to work fine. Just trying to use them together there is a conflict. If its not a systick conflict I wonder if putting the event responder in its own thread would work, have to give that a try.

I am running Arduino IDE 1.8.5 with TD1.40 beta2 by the way.

Here is the sketch (proably doing something wrong):

Code:
#include <EventResponder.h>

#define EVENT2_TIME 850
#define EVENT3_TIME 1050
#define EVENT4_TIME 250
#define EVENT0_TIME 750

uint32_t event0_expected_time;
uint32_t event2_expected_time;
uint32_t event3_expected_time;
uint32_t event4_expected_time;

EventResponder myevent;
EventResponder mytimerevent;
EventResponder mytimerevent2;
EventResponder mytimerevent3;
EventResponder mytimerevent4;
MillisTimer mytimer;
MillisTimer mytimer3;
MillisTimer mytimer2;
MillisTimer mytimer4;

#include <Arduino.h>
#include "TeensyThreads.h"

const int LED = 13;

volatile int blinkcode = 0;

void blinkthread() {
  while(1) {
    if (blinkcode) {
      for (int i=0; i<blinkcode; i++) {
        digitalWrite(LED, HIGH);
        threads.delay(150);
        digitalWrite(LED, LOW);
        threads.delay(150);
      }
      blinkcode = 0;
    }
    threads.yield();
  }
}

void setup() {
  while (!Serial) ;
  Serial.println("EventResponder test");
  
  myevent.attach(dosomething);
  mytimerevent.attach(dotimer);
  mytimerevent2.attach(dotimer2);
  mytimerevent3.attach(dotimer3);
  mytimerevent4.attach(dotimer4);
  
  event0_expected_time = millis() +  EVENT0_TIME;
  mytimer2.beginRepeating( EVENT2_TIME, mytimerevent2);
  event2_expected_time = millis() +  EVENT2_TIME;
  mytimer3.beginRepeating(EVENT3_TIME, mytimerevent3);
  event3_expected_time = millis() +  EVENT3_TIME;
  mytimer4.beginRepeating(EVENT4_TIME, mytimerevent4);
  event4_expected_time = millis() +  EVENT4_TIME;

  pinMode(LED, OUTPUT);
  threads.addThread(blinkthread);

}

int count = 0;

void loop() {

  blinkcode = 2;
  
  Serial.println("loop trigger dosomething");
  myevent.triggerEvent();
  delay(249);
}

void dosomething(EventResponderRef event)
{
  Serial.println("dosomething event");
  mytimer.begin(EVENT0_TIME, mytimerevent);
  event0_expected_time = millis() +  EVENT0_TIME;
}

void dotimer(EventResponderRef event)
{
  uint32_t t = millis();
  Serial.printf("%u - T0(%d)\n", t, (t >= event0_expected_time)? t - event0_expected_time : event0_expected_time - t);
  event0_expected_time  += EVENT0_TIME;}

void dotimer3(EventResponderRef event)
{
  uint32_t t = millis();
  Serial.printf("%u - T3(%d)\n", t, (t >= event3_expected_time)? t - event3_expected_time : event3_expected_time - t);
  event3_expected_time  += EVENT3_TIME;
}

void dotimer4(EventResponderRef event)
{
  uint32_t t = millis();
  Serial.printf("%u - T4(%d)\n", t, (t >= event4_expected_time)? t - event4_expected_time : event4_expected_time - t);
  event4_expected_time  += EVENT4_TIME;
}


void dotimer2(EventResponderRef event)
{
  uint32_t t = millis();
  Serial.printf("%u - T2(%d)\n", t, (t >= event2_expected_time)? t - event2_expected_time : event2_expected_time - t);
  event2_expected_time  += EVENT2_TIME;
}

While the LED Blinks the output is as follows:
Code:
EventResponder test
loop trigger dosomething
dosomething event
loop trigger dosomething
dosomething event
loop trigger dosomething
dosomething event

Running without threads gives:
Code:
EventResponder test
loop trigger dosomething
dosomething event
loop trigger dosomething
dosomething event
3468 - T4(0)
loop trigger dosomething
dosomething event
3718 - T4(0)
loop trigger dosomething
dosomething event
3968 - T4(0)
4068 - T2(0)
loop trigger dosomething
dosomething event
4218 - T4(0)
4268 - T3(0)
loop trigger dosomething
dosomething event
4468 - T4(0)
loop trigger dosomething
dosomething event
4718 - T4(0)
4918 - T2(0)
loop trigger dosomething
dosomething event
4968 - T4(0)
loop trigger dosomething
dosomething event
5218 - T4(0)
5318 - T3(0)
loop trigger dosomething
dosomething event
5468 - T4(0)
loop trigger dosomething
dosomething event
5718 - T4(0)
5768 - T2(0)
loop trigger dosomething
dosomething event
5968 - T4(0)
loop trigger dosomething
dosomething event
6218 - T4(0)
6368 - T3(0)
loop trigger dosomething
dosomething event
6468 - T4(0)
6618 - T2(0)
loop trigger dosomething
dosomething event
6718 - T4(0)
loop trigger dosomething
dosomething event
6968 - T4(0)
loop trigger dosomething
dosomething event
7218 - T4(0)
7418 - T3(0)
loop trigger dosomething
dosomething event
7468 - T2(0)
7468 - T4(0)
 
Yes, neither EventResponder nor TT are aware of other libs and just overwrite intvectors. That's wanted.
 
@Frank B. Thanks for the explanation. Beginning to understand the issues here.
Thanks
Mike
 
@Frank B. Thanks for the explanation. Beginning to understand the issues here.
Thanks
Mike

For the time being, I updated the TeensyThreads library in github to save the previous systick_isr() and call it before doing it's thing, similar to the "chaining" discussed earlier in this thread. This should work with EventResponder as currently implemented.
 
@fritas. Man that was a quick update. Just gave the updated library a try with my test code above. Don't think it is working. It hangs after the first event response and no-blinking occurs. Here is the only thing I get:

Code:
EventResponder test
loop trigger dosomething
dosomething event

or if I change the position of where I call blinkcode I will get:
EventResponder test
8451 - T4(0)
8701 - T4(0)
8951 - T4(0)
9051 - T2(0)
9201 - T4(0)
loop trigger dosomething
dosomething event

v/R
Mike
 
@fritas. Man that was a quick update. Just gave the updated library a try with my test code above. Don't think it is working. It hangs after the first event response and no-blinking occurs.

Due to a long-forgotten modification in my test environment I didn't catch a problem with "chaining" the svcall_isr() interrupt. The default interrupt handler is unused_isr() and it causes the CPU to halt. I added a special case to look for this and updated Github. If you get a chance, let me know if this fixes it for you.
 
With chaining, supported by the core, there would be an official interface to do it.. now, every lib has to have it's own implementation :)
 
@fritas. Just tested it and no problems, both work together. Love testing things in strange ways.

@Frank B. Wouldn't that only be the case if you actually incorporated CHAINING into the core or in eventResponder? Think @fritas did a workaround to allow both to work together with a pseudo chaining method in the lib itself. Can't talk to other libs, not sure what a good lib is to test eventResponder that uses systick - never dug that deep before until TeensyThreads.

v/R
Mike
 
@Frank B. Will do right after I clean the garage :( Printing it now, yes I still print to read :)


UPDATE: After re-reading the thread (especially this page) I realized that what I thought about interrupts was a little too simplified. I pulled out Yiu's book and read chapters 7/8 on interrupts. Have a few more links that I found. If you have any other sources please let me know.

Thanks
Mike
 
Last edited:
I do apologize if I appear to be hijacking the thread or side tracking the discussion, just trying to learn. I am really trying to understand more of how the internals work on eventresponder and eventually TeensyThreads. Guess curiosity or the challenge of something new. Anyway, what I’ve been through so far (putting this here for reference that may help other beginners):

  1. Joseph Yiu’s book – chapters 7 (Exceptions & Interrupts), 8 (Exception Handling in Detail) and 9 (Low Power and System Control Features)
  2. Chapter 41 Periodic Interrupt Timer (PIT), K64 Sub Family Reference Manual
  3. Chapters 1 and 2 of Cortex-M4 Devices Generic User Guide
  4. Interrupts and Timers, https://engineering.purdue.edu/ece4...e/LM4F-LaunchPad-04 - Interrupts & Timers.pdf
  5. Interrupts and Timers, http://www.mcutech.net/upload/hr/0pic2_10_29_1335492322.pdf
  6. Interrupts and Timers, http://users.ece.utexas.edu/~valvano/Volume1/E-Book/C12_Interrupts.htm, (Actually a pretty good site overall for learnings, includes videos, examples and demo applets)
  7. ARM Cortex-M, Interrupts and FreeRTOS: Part 1, https://mcuoneclipse.com/2016/08/14/arm-cortex-m-interrupts-and-freertos-part-1/
  8. A Beginner’s Guide on Interrupt Latency - and Interrupt Latency of the Arm Cortex-M processors, https://community.arm.com/processor...errupt-latency-of-the-arm-cortex-m-processors
  9. Cortex M4 exception return sequence, https://community.arm.com/processors/f/discussions/4557/cortex-m4-exception-return-sequence
  10. How to Create a Linked List C++ Introduction to Linked Lists, https://www.youtube.com/watch?v=o5wJkJJpKtM (actually this is from “Paul’s Programming” Channel, https://www.youtube.com/channel/UCcDGsN3JxMavDkM9INRLGFA, pretty good site for learning)
  11. What is a Doubly Linked List, https://www.youtube.com/watch?v=k0pjD12bzP0

One thing led to another and this little journey of mine kept expanding. I know now more than I did before but not enough. I would like to ask a few questions about what is going on with the event responder code to get a better understanding between what I read and what is in the code. Not sure if this is the thread to do it in or start a new thread more or not, just don't know what to title it.

v/R
Mike
 
Mike - you've looked deeper at this more than I have. But as I see it these are two unique extensions or changes to the standard Arduino repeated loop() processing.

> TeensyThreads sets up tasks that time slice on a timer and will pause and start threads as directed on the resulting schedule in a pre-emptive fashion.

> EventResponder code will queue a list of events at the program's direction and then watch for them to expire and call during normal code breaks [ outside loop(), on delay(), perhaps on the millisecond SysTimer interrupt ]. Those events result in callbacks to specified handler functions? By design and over time these call-backs may include response to library operations like replacing serialEvent() response processing?

Not sure if I have all that right or it is responsive to what question you may have?
 
Back
Top