This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SDK app scheduler

Hello all,

I'm looking at the example projects and ble_app_template in particular, and have noticed something that doesn't seem to make sense...

In main(), this project sets up the scheduler module, and then loops calling app_sched_execute(), so it appears to be a project actioning BLE events at "main task" level (as opposed to SW-interrupt level).

However, looking at the call to ble_stack_init(), NULL is passed to SOFTDEVICE_HANDLER_INIT, and then there are calls to both softdevice_ble_evt_handler_set() and softdevice_sys_evt_handler_set(). As far as I can see (from API documentation and code itself), this means that the scheduler won't be used, and BLE/system events will be actioned at SW-interrupt level?

Can anyone please clarify what's going on here?

Regards, Richard.

  • The BLE events and SYS events are not using the scheduler here. But Timers are. Please check the timers_init function to see that they want their handlers to be run in main context.

    Update: Note that the timers are initialized in a different way using the macro

    APP_TIMER_APPSH_INIT

  • Great, thanks. So if I want to receive BLE events in the scheduler, I pass the handler in as a parameter to SOFTDEVICE_HANDLER_INIT... But what if I want to receive system events in the scheduler also? Will this happen automatically, or do I need to do something else, or is this not possible?

  • scheduler is a very simple concept. You just postpone some execution of a function from highter priority context to lower priority context. It is known that interrupt handlers need to be fast and short so that reset of the interrupt are not masked too long. So if you want to postpone some non urgent processing from higher priority interrupt to a lower priority interrupt (or main) you just put that request in a queue by calling below

    app_sched_event_put  
    

    This takes the event, its size and a third argument which the handler (which will be called at time of deque) In lower priority context or main, you see if there is anything queued, by executing app_sched_execute in a endless loop. When this function finds something in the queue, it calls the handler you gave as argument to function while queuing it. This handler unpacks the first two arguments. These two can contain anything, events, handlers, function pointer or ANYTHING you can imagine that needs execution. This way we have transfered the exection from some higher priority context to lower priority context( or to any other context that has app_sched_execute in a loop)

    APP_TIMERS and SOFTDEVICE modules are using this concept to transfer their execution to main context. They do this by making the third argument, smart enough to unpack data and call the handlers within the unpacked data. I really hope i have not confused you more. Last but not the least, if you want receive both the events in the scheduler then just pass softdevice_evt_schedule to the second argument in SOFTDEVICE_HANDLER_INIT and it is done. Both events are queued to run in main context.

    example of how you can make your put and get functions

    1. initialize the scheduler with enough buffer to hold events that needed to be pulled from different thread (or ISR context).

    2. make a struct that could hold information that needs to be passed

      typedef struct { type1 data1; type2 data2; type3 * pointer1; type4 * callback_function(type1 data1, type2 data2, type3 * pointer1);
      } some_struct_t;

    3. make a function that wraps the data and pushes it into scheduler queue.

      static inline uint32_t app_module_evt_schedule(type4 * callback_handler, type1 d1, type2 d2, type3 *p1); { some_struct_t push_event; push_event.callback_function = callback_handler; push_event.data1 = d1; push_event.data2 = d2; push_event.pointer1 = p1;

      return app_sched_event_put((void *)&push_event, sizeof(push_event), pull_event_function);

    }

    What you have done now is that you made some function that wraps your data and callback function in one struct and asked the scheduler to put it in the queue. You also gave it a function that is smart enough to unwrap this struct and call the callback.

    1. make the pull_event_function

      static inline void pull_event_function(void * p_event_data, uint16_t event_size) { some_struct_t *p_some_struct = (some_struct_t *)p_event_data;

      ASSERT(event_size == sizeof(some_struct_t));
       p_some_struct->callback_function(p_some_struct->data1, p_some_struct->data2, p_some_struct->pointer1);
      

      }

    now you have something to push your event into scheduler queue and something to pull from scheduler queue. All you need to do is push by calling app_module_evt_schedule() in one contexxt and pull by calling app_sched_execute() in lower priority context and tadaaaa you have changed the execution of callback_function from one context to the other.

  • When you say "both events", I presume you mean BLE and SYS events... many thanks!

Related