Register / Login  |  Desktop view  |  Jump to bottom of page

IoAbstraction & TaskManagerIO » Rapid pulse count

Author: kitsen13
17/10/2021 15:57:31
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.

Author: davetcc
17/10/2021 16:22:34
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.

Author: kitsen13
18/10/2021 06:04:32
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.

Author: davetcc
18/10/2021 07:43:15
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.

Author: davetcc
18/10/2021 07:51:21
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.

Author: kitsen13
18/10/2021 16:47:34
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?

Author: davetcc
18/10/2021 17:30:21
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.

Author: kitsen13
31/10/2021 11:01:14
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

Author: davetcc
03/11/2021 09:50:58
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();
    }

Author: davetcc
03/11/2021 13:06:13
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/





Register / Login  |  Desktop view  |  Jump to top of page