Multitasking

Submitted by Ed_B on Fri, 04/23/2010 - 20:32

Printer-friendly versionPrinter-friendly version

Multitaskng gives the appearence of doing more than one thing at a time, when you're really switching between tasks as fast as you  can. When computeres do this it's usually under the control of an operating system. With microcontrollers, its usually done by writing a bunch of small functions that do unrelated jobs, then firing those functions off based on external events that arrive through interrupts, or by lining all the functions up and firing them off, in sequence, by a master timer. In an exterme case it's possible to have a program with an empty main loop that's busy doing lots of stuff. Two ideas often come into play to do this. Interrupts and call backs. To start an exploration of event driven programming, I'm using the Arduino's Wire library. I've maped out the event handling and generating calls to study. 

 

 

Here's a descripton of the calls Wire uses to handle a slave receive event.

 

wire_callbacks_2.txt

--------------------

In Arduino sketch:
receiveEvent()

A user defined callback function to fire off when data arrives
[aliased to user_onReceive() ]

--------------------

Large function called by the ISR. Calls user's function.

void TwoWire::onReceiveService(uint8_t* inBytes, int numBytes)
{
  ...
  if(!user_onReceive){ // don't bother if user hasn't registered a callback
    return;
  }
  ...
  //a bunch of buffer manipulation here
  ...
  user_onReceive(numBytes);  // ***********the Arduino callback****************

[ onReceiveService() is aliased to twi_onSlaveReceive() ]

[twi.c]
Interrupt Service Routine

SIGNAL(TWI_vect){
  ...
  twi_onSlaveReceive()
  ...
}
--------------------------------------------------------
---------attach callbacks/handlers/events---------------

[Wire.cpp]
void TwoWire::begin(uint8_t address)
{
  ...
  twi_attachSlaveRxEvent(onReceiveService);
  ...
}

void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) )
{
  twi_onSlaveReceive = function;
}
[declared in twi.c: static void (*twi_onSlaveReceive)(uint8_t*, int);]

[in Arduino sketch]
Wire.onReceive(receiveEvent); attaches callback
to user_onReceive()

here is the function:

void TwoWire::onReceive( void (*function)(int) )
{
  user_onReceive = function;
}
[declared in Wire.cpp: void (*TwoWire::user_onReceive)(int);]

-------------------------------
-------------------------------
-------------------------------

In Arduino sketch

  #include <Wire.h>

  setup(){
    ...
    Wire.begin(4);                // join i2c bus with address #4
    Wire.onReceive(receiveEvent); // register event
    ...
  }
 
  // my callback
  receiveEvent(){
    ...
    myStuff;
    ...
  }

---------------------------------

In the Arduino sketch, the user's
callback function
receiveEvent()

is alaised to the name
TwoWire::user_onReceive()

by using this
TwoWire::onReceive(*function)

The actual call looks like
Wire.onReceive(receiveEvent);

-----------------------------------
TwoWire::onReceiveService()

is called by the ISR. It does some housekeeping,
updates the data buffer, and finally calls
the user's callback. [The user's callback
is user_onReceive() ]

In the Arduino setup(), the user calls
Wire.begin()

Inside that functon call to begin()
TwoWire::begin()

the function
TwoWire::onReceiveService()

is aliased to
twi_onSlaveReceive()

by using this
twi_attachSlaveRxEvent(onReceiveService)

-----------------------------------

The interrupt handler
SIGNAL(TWI_vect)

will call
twi_onSlaveReceive()

which will in turn call
the user's callback by using its alias
user_onReceive()

--------------------------------------------------------
----declarations, instantitations, initializations------

***************
void TwoWire::onReceiveService(uint8_t* inBytes, int numBytes)

  declared in Wire.h
 
  defined in Wire.cpp

      class TwoWire
      {
        private:
          ...
          static void onReceiveService(uint8_t*, int);
        
***************
TwoWire::user_onReceive()

  declared in Wire.h
    class TwoWire
    {
      private:
      ....
      static void (*user_onReceive)(int);
     
  initialized in Wire.cpp   
    void (*TwoWire::user_onReceive)(int);
   

  example assignment of function pointer:
    void TwoWire::onReceive( void (*function)(int) )
    {
      user_onReceive = function;
    }
 
   
  example call in Wire.cpp:
    user_onReceive(numBytes);
   
   
***************
static void (*twi_onSlaveReceive)(uint8_t*, int);

  declared in twi.c
 
  example assignment of function pointer:   
    void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) )
    {
      twi_onSlaveReceive = function;
    }
 
  example call in twi.c
    // callback to user defined callback
    twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex);

***************
twi_attachSlaveRxEvent(onReceiveService)

  declared in twi.h
    void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) );
   
  defined in twi.c (it's short):
    void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) )
    {
      twi_onSlaveReceive = function;
    }

***************
TwoWire::begin()  [overloaded 3 times]

  declared in Wire.h
      public:
        void begin();
        void begin(uint8_t);
        void begin(int);

  defined in Wire.cpp:
    // Public Methods ///////////////////////////
    void TwoWire::begin(void)
    {

***************
user function defined in the user's arduino.pde file

void receiveEvent()

***************