[Logo] TCC discussion forum
  [Search] Search   [Recent Topics] Recent Topics   [Hottest Topics] Hottest Topics   [Top Downloads] Top Downloads   [Groups] Back to home page 
[Register] Register /  [Login] Login 


This forum is read only and new users cannot register, please ask all new questions either using GitHub discussions, or in Arduino forum tagging @davetcc.

Rapid pulse count RSS feed
Forum Index » IoAbstraction & TaskManagerIO
Author Message
kitsen13


Joined: Jul 29, 2021
Messages: 15
Offline
Hello,

I'm reworking on a projet to manage flow meter.

After reading tons of IoAbstraction doc, I don't have solution.

I want to work with digital interrupt, how can i implement a fairly simple code like this in my code with IoAbstraction?
Like this -> https://www.bc-robotics.com/tutorials/using-a-flow-sensor-with-arduino/

I want to be sure that all edge will be be memorised.

Thank you.
davetcc


Joined: Jan 19, 2019
Messages: 686
Offline
What I would do here, is use an event, you can read about events here. https://www.thecoderscorner.com/products/arduino-libraries/taskmanager-io/using-interrupt-threaded-events-taskmanager/

There are several event examples within the TaskManagerIO examples directory.

* In short, this would entail creating a class that represented the event
* Registering an instance of the above-created event class with taskManager.
* Attach a raw interrupt handler that was of type CHANGE:
* In the ISR, just change the state of variables in the event class (make sure these are marked volatile).
* Mark the event as triggered by calling markTriggeredAndNotify on the event instance created above.

This will allow you to update the event as often as needed, just updating the counter, while taskManager will run later to handle any other business, such as updating logs, displays etc.

Please make sure you have read the section "A work about safety critical systems" in the above link.
kitsen13


Joined: Jul 29, 2021
Messages: 15
Offline
Ok thanks,

Question : if an interrupt appears based on event system, Does the task manager terminates the task running or stop it and execute interrupt ? What about the initial task ?

Same question for Marshalling interrupts methods. That differences?

Thanks again.
davetcc


Joined: Jan 19, 2019
Messages: 686
Offline
Marshalling interrupts are no longer recommended. There can only be one "interrupt task" and it's usually taken by switches in most projects. We strongly recommend away from them for new code.

With event processing, task manager runs the exec() method of the event as soon as the current task completes, or if the current task yields using yieldForMicros(). It is a cooperative scheduler basically by design.

This is why events are better, let's say you have a counter that counts the amount of liquid going through the device. You can update your counter in the ISR (make sure variable is atomic - IE use std::atomic or volatile if using a machine length word), but then trigger the event to handle screen updating, logging etc later with the events exec() method.

The bottom line is, in an interrupt do as little as possible, update variables, handle any possible emergency situation, then farm most of the work out to user-level code.

EDIT - improved readability of event processing para.
davetcc


Joined: Jan 19, 2019
Messages: 686
Offline
On ESP32 or mbed, you can even create additional threads or tasks that handle this, or even start a second taskManager on another thread and use it as a queue.

You can notify events from within an interrupt, but you must never call any execute or schedule method in an ISR, as they can lock.
kitsen13


Joined: Jul 29, 2021
Messages: 15
Offline
Ok thanks for these informations.

On my application side, pulse counts rising edge of flow meter is a priority.

On the other side, my main task (1 second scanning timer) has a lot of calculations.

If i use event method, some pulse will be forgive by the process. Thereby Marshalling interrupts methods is more reliable in case of my needs?
davetcc


Joined: Jan 19, 2019
Messages: 686
Offline
I think you've misunderstood slightly. I'll just clarify:

The marshalled interrupt will have exactly the same characteristics, both triggering an event and a marshalled interrupt do the same inside task manager.

The most effective way to proceed using taskmanager and guarantee you don't miss a count is to use an event as described above. You would do the counting in the actual ISR, and then trigger the event that would call the exec() on the event at some point later, to do the non-critical stuff.
kitsen13


Joined: Jul 29, 2021
Messages: 15
Offline
Hello,

I have tested :

- Marshall interrupt
- Event based interrupt "V1", like this https://github.com/davetcc/TaskManagerIO/blob/master/examples/eventHandling/eventHandling.ino
- Event based interrupt "V2", like this https://www.thecoderscorner.com/products/arduino-libraries/taskmanager-io/using-interrupt-threaded-events-taskmanager/ (-> please modify "overrride" to "override")
-> ยง Simple example of event triggering from interrupt

The two methods brings me to miss count. (for information count frequency is about 70 hz, wired with a pull up resistance on a ESP32 pin 5)

The factor of missed count depends on some tasks periods:
I have 2 periodics task, one task of 1s, an other of 200ms. 2/3 counts are missed by the event interrupt task with this settings.

If i put my sencond task 200ms-> 1s, 1/2 counts are missed.

Counter is declared like this : volatile int PulsecountTot = 0;
This counter is incremented by the the class.

extract of my code :

class InterruptEvent : public BaseEvent {
    public:    
        InterruptEvent(pintype_t pin) {
            pinMode(pin, INPUT);
            ::attachInterrupt(digitalPinToInterrupt(pin), interruptHandler, RISING);
        }

        ~InterruptEvent() override = default;
        
        uint32_t timeOfNextCheck() override {
                                              return secondsToMicros(300);
                                              }
        void exec() override {
            // do the work here when triggered.
            
                  #ifdef DEBUG_DEBIT
                  Serial.print("Interrupt triggered : ");
                  #endif  
                  
                  PulsecountInst++; //Every time this function is called, increment "count" by 1
                  PulsecountTot++; //Every time this function is called, increment "count" by 1
                  #ifdef DEBUG_DEBIT
                  Serial.println(PulsecountTot);
                  #endif    
        }
    };


    InterruptEvent interruptEvent(FLOW_PIN_DEBIT);


    ISR_ATTR void interruptHandler() {
        // here we now mark the event as triggered.
        interruptEvent.markTriggeredAndNotify();
    }



An other question, when you say : " Attach a raw interrupt handler that was of type CHANGE:", in my case my need is to count RISING EDGE of the pulse, not RISING EDGE + FALLING EDGE. With CHANGE, that double the counter.

I'm searching a solution for this problem for about 12 hours...
Problem confirmed with an oscilloscope.


Thanks
davetcc


Joined: Jan 19, 2019
Messages: 686
Offline
As I said do not try and do the counting within task manager, it will miss some events when drawing the display or dealing with other long-running unavoidable tasks.

Do the counting within "ISR_ATTR void interruptHandler()" and just do the less important stuff in task manager. Then you will never miss a tick. Just ensure that PulsecountInst and PulsecountTot are declared volatile and not bigger than 32 bits length on ESP.

Depending on what else you're doing in the task manager loop there will be latency, it is a cooperative scheduler with unfair semantics.

Take the below as an example starting point, I've not confirmed it will compile:

class InterruptEvent : public BaseEvent {
public:    
    volatile uint32_t PulsecountTot = 0;
    volatile uint32_t PulsecountInst = 0;

    InterruptEvent(pintype_t pin) {
        pinMode(pin, INPUT);
        ::attachInterrupt(digitalPinToInterrupt(pin), interruptHandler, RISING);
    }

    ~InterruptEvent() override = default;
        
    uint32_t timeOfNextCheck() override {
        return secondsToMicros(300);
    }

    void exec() override {
       // ============> here we only do less critical stuff based on a change, there may be sometimes nothing to do here.

            // do the work here when triggered.
            Serial.print("Interrupt triggered : ");
        }
    };


    InterruptEvent interruptEvent(FLOW_PIN_DEBIT);


    ISR_ATTR void interruptHandler() {

       // ============> NOTE I moved the incrementing here

        // here we now mark the event as triggered.
        interruptEvent.PulsecountInst++; //Every time this function is called, increment "count" by 1
        interruptEvent.PulsecountTot++; //Every time this function is called, increment "count" by 1

       // ============> NOTE I moved the incrementing here


        interruptEvent.markTriggeredAndNotify();
    }
davetcc


Joined: Jan 19, 2019
Messages: 686
Offline
In terms of the raw interrupt handler mode - CHANGE, have you read the underlying Arduino documentation?

https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

 
Forum Index » IoAbstraction & TaskManagerIO
Go to:   
Mobile view
Powered by JForum 2.7.0 © 2020 JForum Team • Maintained by Andowson Chang and Ulf Dittmer

This site uses cookies to analyse traffic, serve ads by Google AdSense (non-personalized in EEA/UK), and to record consent. We also embed Twitter, Youtube and Disqus content on some pages, these companies have their own privacy policies.

Our privacy policy applies to all pages on our site

Should you need further guidance on how to proceed: External link for information about cookie management.