How do I interrupt an event handler?

Hi,

I'm extremely new to Nordic (and embedded development in general), but I'm writing some code that turns on various LED colors based on input from an Android app (BLE). This app may send multiple channels right after the other (e.g. R=100,G=90,B=80), so multiple things have to be updated. It also has a transition period so that colors are slowly scaled up, rather than immediately jumping.

Currently I'm using event handlers to set a global variable when a new channel value comes in. Then there's a busy loop in main() that updates each channel accordingly.

while(1){
    if (red_now < red_goal)
      *(color_channels.red) = ++red_now;
    if (red_now > red_goal)
      *(color_channels.red) = --red_now;
...
    nrf_delay_us(200);
}

This works well enough, but I'd like to learn how to do this without a busy loop. If I update everything within each event handler, it will do only one channel at a time - so it would turn red, then red+green, and finally red+green+blue.

So ideally, I'd like to interrupt this event handler when new data comes in, and restart the update_levels() function, rather than waiting for update_levels() to complete before updating global variables.

I've tried writing an update_levels() like this but it has the same results as original (transitioning each channel separately).

void update_levels(){
  while (red_now != red_goal ||
         blue_now != blue_goal ||
         green_now != green_goal) {
    if (red_now < red_goal)
      *(color_channels.red) = ++red_now;
    if (red_now > red_goal)
      *(color_channels.red) = --red_now;
    ...
    nrf_delay_us(200);
   }
}

Development setup:
OS: Linux
Devkit: PCA10040 3.0.0
Segger version: SES for ARM V4.18
SDK version: 17.02

Sorry if this is super basic, just trying to learn :)

Parents
  • Hi ellamoss,

    It is nice to have someone joining the same field. All the more when they are trying to start with our solution. Welcome abroad! :D

    To better understand this, could you please explain what kind of event handler are we talking about here? What triggers the handler? What kind of data transmission do you have?

    It is generally not a good idea to do a lot of things in the event handlers, since it happens in the interrupt context and blocks execution of other lower-priority handlers and the main loop. Let's take a look at your setup and see if it is okay in this case, or if there is any better approach.

    Best regards,

    Hieu

  • The basic event handler just sets global variables, e.g. red_goal = [what comes in from the BLE app]. It's triggered on the BLE_GATTS_EVT_WRITE event.

    Then, once those variables are all updated, I want to run the update_levels() code, which I've provided above.

    I've just read about the __WFI() functionality. I think this is the solution to my problem. I've moved the update_levels() out of the event handler, and then moved this code into main:

    while(1){
        __WFI();
        update_levels();
    }

    Is this doing what I think it is? That is, does it just wait for an interrupt in a low-power state, then update_levels() when an interrupt comes in?

    Thanks for your help.

  • Sorry for the long delay. I got some backlog piling up. 

    Here are some pseudo code of what I understand your setups:

    // Setup 1
    void new_red_goal_handler(new_red_goal)     { red_goal      = new_red_goal; }
    void new_green_goal_handler(new_green_goal) { green_goal    = new_green_goal; }
    void new_blue_goal_handler(new_blue_goal)   { blue_goal     = new_blue_goal; }
    
    void main(void) {
        ...
        while (1) {
            if (red_now < red_goal) red_now++;
            if (red_now > red_goal) red_now--;
            if (green_now < green_goal) green_now++;
            if (green_now > green_goal) green_now--;
            if (blue_now < blue_goal) blue_now++;
            if (blue_now > blue_goal) blue_now--;
        }
    }

    // Setup 2
    
    void update_levels(void) {
        while (1) {
            if (red_now < red_goal) red_now++;
            if (red_now > red_goal) red_now--;
            if (green_now < green_goal) green_now++;
            if (green_now > green_goal) green_now--;
            if (blue_now < blue_goal) blue_now++;
            if (blue_now > blue_goal) blue_now--;
            nrf_delay_us(200);
        }
    }
    
    void new_red_goal_handler(new_red_goal) { 
        red_goal      = new_red_goal;
        update_levels();
    }
    void new_green_goal_handler(new_green_goal) {
        green_goal    = new_green_goal;
        update_levels();
    }
    void new_blue_goal_handler(new_blue_goal) {
        blue_goal     = new_blue_goal;
        update_levels();
    }
    
    void main(void) {
        ...
        while (1) {
            // Nothing
        }
    }

    // Setup 3
    
    void update_levels(void) {
        while (1) {
            if (red_now < red_goal) red_now++;
            if (red_now > red_goal) red_now--;
            if (green_now < green_goal) green_now++;
            if (green_now > green_goal) green_now--;
            if (blue_now < blue_goal) blue_now++;
            if (blue_now > blue_goal) blue_now--;
            nrf_delay_us(200);
        }
    }
    
    void new_red_goal_handler(new_red_goal)     { red_goal      = new_red_goal; }
    void new_green_goal_handler(new_green_goal) { green_goal    = new_green_goal; }
    void new_blue_goal_handler(new_blue_goal)   { blue_goal     = new_blue_goal; }
    
    void main(void) {
        ...
        while (1) {
            __WFI();
            update_levels();
        }
    }

    Is that about correct?

    If that is correct, then:

    Setup 1 will work.

    Setup 2 will also work. But as you said, only one color channel can be updated at a time.
    The reason is that all the work is done in the handler. As you probably have guessed given your question, the other handlers cannot interrupt this, because they are all of the same priority.
    Performing significant amount of work in a handler is not a good idea. When it is a loop that takes a significant amount of time, it is even worse.
    Setup 2 is not recommended at all.

    Setup 3 will work only if you have a consistent source of events or interrupts. If your device is advertising or keeping a connection, that will do. It can be better regarding power consumption. However, behavior wise, it should be no difference.
    There is technically the risk that if a connection is dropped, and the device does not advertise anymore, and thus run out of event to wake up with, then it will go to sleep forever. That is unless you have a different source of periodic events/interrupts.

    The strategy in Setup 1 is alright if your application is simple. Is there a reason you don't like having the code run in a busy loop?

    If you wish for all three color channels to change at the same time, maybe you can setup so they are all updated in the same handler?

    Finally, it can be improved in response time and power consumption with an App Timer. The risk in Setup 3 will also be eliminated. If you are interested, the ble_app_hrs uses it.

    If you need further help to set it up, or have any questions, just let me know. This comment got kind of long.

  • Yes, this is perfect! I'm using setup 3 now but I'll look into app timers with the ble_app_hrs example. Thank you!

Reply Children
No Data
Related