This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Using the Scheduler (8051 OSAL system to nRF51822)

Hi all,

I just had some general questions regarding events and timers on the nRF51822. I'm coming from a Ti CC2540 8051 which used an OSAL system. This made it quite easy to create events and sub-tasks, and handlers for each event and task. This allowed one class to trigger an event handler in another class quite easily. I'm trying to figure out how to perform equivalent custom event/task handling on the nRF51822. Case in point:

I used the Button Handler to catch button presses/releases. I use this to trigger timers in a Button_Timer.c class which monitor how long a button has been press. While pressed, the duration triggers different LED's to turn on (i.e. if 2sec has elasped, turn on Red led. After 3secs turn off LED, after 5secs trigger a buzzer...etc). I know I can do this by simply using app_timers, but I'd rather avoid using many many timers to handle tasks.

I can't seem to find any example code where the application creates its own events and at certain trigger points pushes them to the scheduler to execute via app_sched_event_put. I may be looking at this all wrong with how the Scheduler is supposed to work, so any advise on the matter is greatly appreciated. Thank you

  • Hi. Scheduler in nRF51822 is used to queue the non-real-time operation and release the interruption resources quickly. It is equivalent to the traditional flag setting/resetting in the IRQ processing. For instance, a traditional IRQ processing is like

    void xxx_IRQHandler(void)
    {
        ....
        xxx_flag = true;
        ....
    }
    
    int main(void)
    {
        for(;;)
        {
        	....
            if (xxx_flag == true) { /* Process interruption here */ }
            ....
        }
    }
    

    The alternative version using scheduler is

    void xxx_IRQHandler(void)
    {
        ....
        err_code = app_sched_event_put(..., xxx_func);
        ....
    }
    
    void xxx_func(void *p_event_data, uint16_t event_size)
    {
        /* Process interruption here */
    }
    
    int main(void)
    {
        ....
        for (;;)
        {
            err_code = sd_app_evt_wait();
            APP_ERROR_CHECK(err_code);
            app_sched_execute();
        }
    }
    

    The queued operation may be executed at anytime. For your application, if you need instant response occurred at the exact time, scheduler may be not a good solution. What you can do is first register a button event using app_button,

    static void button_evt_handler(uint8_t pin_no, uint8_t button_action)
    {
        ....
        if (pin_no == BUTTON_PIN_NO && button_action == APP_BUTTON_PUSH)
        {
            button_pushed = true;
            count = 0;
        } else if (pin_no == BUTTON_PIN_NO && button_action == APP_BUTTON_RELEASE)
        {
            button_pushed = false;
            count = 0;
        }
        ....
    }
    

    Then register an app_timer triggered every 1s,

    void timer_timeout_handler(void *p_context)
    {
        ....
        if (button_pushed == true)
        {
            count ++;
            switch(count)
            {
                case 2: /* Turn on Red LED */
                case 3: /* Turn off LED */
                case 5: /* Trigger a buzzer */
                ....
             }
         }
         ....
    }
    

    That should be fine. BTW, no matter how many app_timer instance you registered, there's only one hardware timer used. (In fact, nRF51822 has only two RTCs, and another is occupied if you're using the SoftDevice.) app_timer can respond to the RTC interruption, and distribute tasks among different app_timer instances. So don't worry about how many "timers" you're using. Particularly, when you initialize the app_timer using

    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, true);
    

    The last parameter of true means scheduler is enabled. All your app_timer event handler will be automatically queued and executed by the scheduler.

  • Thank you for the response. I actually had coded something quite similar to your suggestion. But I have a few issues:

    1. I believe the 2nd Scheduler implementation example you showed is what I am trying to do. I have a button_timer.c class similar to the 1sec timer you suggested, which could "put" events in the queue. The handler of for this event in main.c could then handle toggling LEDs. I don't need sub msec timing for execution as its controlling LED events.

    2. I'm not entirely clear what advantage there is to enabling the Scheduler parameter of a timer. I understand how it will execute from the main loop rather then have the timer handler run as an interrupt. But I don't see how that really makes a difference.

    3. I am trying to code things in a modular way. So all my peripheral timers are coded in separate classes. The button_timer class which handles the 1 second timing is controlled from main.c. But on timeout in button_timer.c I don't have a way to trigger main.c to toggle LEDs. I could have another timer with a handler residing in main.c which I start from button_timer to execute LED toggles on timeout. I just give it a very short timeout parameter. But now things would get quite messy having to code timers just to simulate real-time event handling.

    Can anyone else who has migrated from the CC2540/2541 comment on this? Thanks

    p.s. Again I don't need greater than 10msec timing accuracy for this as it is reacting to a user pressing a button.

    EDIT: Just to tie off this thread, I found an answer in the post.
    devzone.nordicsemi.com/.../

    Thanks guys

  • For question 1: Start your app_timer instance using "app_timer_start(your_timer_id, APP_TIMER_TICKS(1000, APP_TIMER_PRESCALER), NULL);" Your app_timer event will be triggered every 1s. app_timer provides a HAL thus you can define your own timer intervals but not fixed 0.03ms for RTC interruptions.

  • For question 2: You didn't see any advantage because your program is very simple. If your application needs to process intensive interruption requests and do complicated signal processing, intuitively, you may write many lines of code in the IRQ handler. In this case, a new interruption may be triggered when you're still running the code in the old interruption's handler. The CPU may lost the new interruption, or push the old handler into the stack, which easily overflows the stack during intensive operation. To avoid this, the best way is to make the IRQ handler as short as possible (enqueue the data), and execute the computation later. That's why we need the scheduler.

  • For question 3: I didn't get your idea. Can't you use one timer for toggling LED? You don't need another timer to detect button status, as app_button will tell you when button is pushed or released. It might be helpful if you can share your code snippet.

Related