Well, things still might work even at 10-20msecs. All depends on the timing of the connection event in relation to the interrupts. You have to miss a number of connection events for a connection to drop. Will be interesting to see how it performs in those circumstances.
> On Jan 24, 2017, at 11:35 PM, WangJiacheng <[email protected]> wrote: > > Thanks, Will, > > Yes, my semaphore code has problem to run, I have removed the code of release > the semaphore, and use “goto” to check the free time again after my task > wake up. > > The interrupt frequency depends on the phone’s status. For standby phone, > there will be an interrupt every 30s, this is not a big issue since 30s is a > quite long time. However, for active phone such as making a call, there will > be several interrupts, and the time separation will only be 10ms-20ms, this > will cause BLE connections to fail. I will continue to work on this issue. > > Best Regards, > > Jiacheng > > > >> 在 2017年1月25日,14:36,will sanfilippo <[email protected]> 写道: >> >> Jiacheng >> >> 1) Sorry about not converting msecs to os time ticks. Good catch! >> 2) I understand using a semaphore to wake up a task but looking at the exact >> code you have shown, I dont understand why the task would release the >> semaphore in this case. Doesnt the interrupt release the semaphore? >> 3) Blocking interrupts. If you block for 600-700 usecs you will cause >> failures in the underlying BLE stack. These wont be “catastrophic” (at >> least, I dont think so) but it can cause you to miss things like connection >> events, scan requests/responses, advertising events, etc. If your high >> priority interrupt fires off frequently you could possibly cause connections >> to fail. If you do it occasionally you should be ok. >> >>> On Jan 24, 2017, at 5:08 PM, WangJiacheng <[email protected]> wrote: >>> >>> Thanks, Will, you help me a lot. >>> >>> Since my task is triggered by a semaphore, and the semaphore is released by >>> another interrupt routine, so if my task have no enough time to running >>> and go to sleep, after wake up, it will release the semaphore again. >>> Another minor change is time unit conversion (ms -> OS tick) by function >>> os_time_ms_to_ticks(). >>> >>> The main body of my task will like >>> /********************************************************************************/ >>> while (1) >>> { >>> t = os_sched_get_current_task(); >>> assert(t->t_func == phone_command_read_handler); >>> >>> /* Wait for semaphore from ISR */ >>> err = os_sem_pend(&g_phone_command_read_sem, OS_TIMEOUT_NEVER); >>> assert(err == OS_OK); >>> >>>> time_till_next = ll_eventq_free_time_from_now(); >>>> if (time_till_next > X) { >>>> /* Take control of transceiver and do what you want */ >>>> } else { >>>> /* Delay task until LL services event. This assumes time_till_next is >>>> not negative. */ >>>> os_delay = os_cputime_ticks_to_usecs(time_till_next); >>>> os_time_delay(os_time_ms_to_ticks((os_delay + 999) / 1000)); >>> >>> /* Release the semaphore after wake up */ >>> err = os_sem_release(&g_phone_command_read_sem); >>> assert(err == OS_OK); >>> >>>> } >>> } >>> /********************************************************************************/ >>> >>> I will test if this can work. BTW, current test results show there will be >>> an event collision between 2 stacks about 3~4 hours running. >>> >>> I have a question about using interrupt disable, How long can the LL task >>> be blocked by interrupt disable? The high priority interrupt of Nordic’s >>> SoftDevice can be blocked only within 10us. I have an interrupt with most >>> high priority, it will take 600us~700us, is it safe to block LL task and >>> other interrupt such as Nimble Radio and OS time tick during this time? >>> >>> Best Regards, >>> >>> Jiacheng >>> >>> >>> >>>> 在 2017年1月25日,00:37,will sanfilippo <[email protected]> 写道: >>>> >>>> Jiacheng: >>>> >>>> Given that your task is lower in priority than the LL task, you are going >>>> to run into issues if you dont either disable interrupts or prevent the LL >>>> task from running. Using interrupt disable as an example (since this is >>>> easy), you would do this. The code below is a function that returns the >>>> time till the next event.: >>>> >>>> os_sr_t sr; >>>> uint32_t time_now; >>>> int32_t time_free; >>>> >>>> time_free = 100000000; >>>> OS_ENTER_CRITICAL(sr); >>>> time_now = os_cputime_get32(); >>>> sch = TAILQ_FIRST(&g_ble_ll_sched_q); >>>> if (sch) { >>>> time_free = (int32_t)(sch->start_time - time_now); >>>> } >>>> OS_EXIT_CRITICAL(); >>>> >>>> /* >>>> * NOTE: if time_free < 0 it means that you have to wait since the LL task >>>> * should be waking up and servicing that event soon. >>>> */ >>>> return time_free; >>>> >>>> Given that you are in control of what the LL is doing with your app, I >>>> guess you could do something like this in your task; >>>> >>>> time_till_next = ll_eventq_free_time_from_now(); >>>> if (time_till_next > X) { >>>> /* Take control of transceiver and do what you want */ >>>> } else { >>>> /* Delay task until LL services event. This assumes time_till_next is >>>> not negative. */ >>>> os_delay = os_cputime_ticks_to_usecs(time_till_next); >>>> os_time_delay((os_delay + 999) / 1000); >>>> } >>>> >>>> So the problem with the above code, and also with the code you have below >>>> is something I mentioned previously. If you check the sched queue and >>>> there is nothing on it, you might think you have time, but in reality you >>>> dont because the LL has pulled the item off the schedule queue and is >>>> executing it. The LL state will tell you if the LL task is doing anything. >>>> The API ble_ll_state_get() will return the current LL state. So you could >>>> modify the above to do this: >>>> >>>> OS_ENTER_CRITICAL(sr); >>>> time_now = os_cputime_get32(); >>>> sch = TAILQ_FIRST(&g_ble_ll_sched_q); >>>> if (sch) { >>>> time_free = (int32_t)(sch->start_time - time_now); >>>> } else { >>>> if (ble_ll_state_get() != BLE_LL_STATE_STANDBY) { >>>> /* LL is busy. You dont know how long it will be busy. You can >>>> * return some small time here and your task will just keep >>>> polling. */ >>>> } >>>> } >>>> OS_EXIT_CRITICAL(); >>>> Not sure if this will work, and I think there are other things that you >>>> would want to do to insure that the LL task does not grab the transceiver >>>> while your task is using it. >>>> >>>> Hope this helps. >>>> >>>>> On Jan 24, 2017, at 3:00 AM, WangJiacheng <[email protected]> >>>>> wrote: >>>>> >>>>> Hi, Will, >>>>> >>>>> My use scenario is when I have an event (with knowing running time) ready >>>>> to run, I’ll try to get a free time slot (with a required duration) in >>>>> the Nimble events queue. >>>>> >>>>> 1). Only consider Nimble connection events, do not consider scanning >>>>> events, so only look at the scheduled events queue in “g_ble_ll_sched_q”; >>>>> 2). Only consider the event start_time in the queue, since I only care >>>>> if there is enough free time slot from now to nearest future event >>>>> start_time to run my event, if there is no enough time, just wait. >>>>> 3). My code is in a lower priority task function than nimble stack task >>>>> (in blue_ll.c, task “ble_ll” is initialized with task priority 0),so when >>>>> I check the free time slot, there should be no Nimble events running. >>>>> 4). When I’m waiting the free time slot, since Nimble stack has higher >>>>> priority, the Nimble events in the queue will keep on running. >>>>> 5). If an event start_time in the queue already expired, I will get a >>>>> negative number of free time slot from now, since I always require a >>>>> positive number of free time slot, so I’ll wait the event to be done. >>>>> 6) For the periodic events, the event already in the queue is always >>>>> “earlier” than the following periodic events, so I do not care the >>>>> following events. The worst case is that the periodic time is less than >>>>> my event running time, I can not get any opportunity to run my event. >>>>> >>>>> Function for check free time slot from now is as >>>>> /********************************************************************************/ >>>>> int32_t ll_eventq_free_time_from_now(void) >>>>> { >>>>> struct ble_ll_sched_item *sch; >>>>> uint32_t cpu_time_now; >>>>> int32_t time_free; >>>>> int32_t time_diff; >>>>> >>>>> time_free = 100000000; >>>>> cpu_time_now = os_cputime_get32(); >>>>> >>>>> /* Look through the schedule queue */ >>>>> TAILQ_FOREACH(sch, &g_ble_ll_sched_q, link) >>>>> { >>>>> time_diff = sch->start_time - cpu_time_now; >>>>> if (time_diff < time_free) >>>>> { >>>>> time_free = time_diff; >>>>> } >>>>> } >>>>> >>>>> return (time_free); >>>>> } >>>>> /********************************************************************************/ >>>>> >>>>> When I have a event (require 10000 CPU time ticks) ready to run, the code >>>>> will be: >>>>> /********************************************************************************/ >>>>> /* wait for time slot to run event */ >>>>> while (ll_eventq_free_time_from_now( ) < 10000) >>>>> { >>>>> /* just loop to wait the free time slot > 10000 CPU time ticks */ >>>>> /* Nimble events have higher task priority, will keep on running */ >>>>> if (time_out) >>>>> { >>>>> return(1); >>>>> } >>>>> } >>>>> >>>>> /********** my event require 10000 CPU time ticks run here >>>>> **************/ >>>>> /********************************************************************************/ >>>>> >>>>> Does this make sense? >>>>> >>>>> Thanks, >>>>> >>>>> Jiacheng >>>>> >>>>> >>>>> >>>>> >>>>>> 在 2017年1月24日,14:25,WangJiacheng <[email protected]> 写道: >>>>>> >>>>>> Thanks, Will, >>>>>> >>>>>> It seems I can not get the things work by the simple way. I just want >>>>>> to find out a free time slot at high level to access PHY resource such >>>>>> as CPU and radio RF exclusively. With your explain, I should interleave >>>>>> my events into BLE events at low level in the same schedule queue. >>>>>> >>>>>> Best Regards, >>>>>> >>>>>> Jiacheng >>>>>> >>>>>> >>>>>>> 在 2017年1月24日,13:48,will sanfilippo <[email protected]> 写道: >>>>>>> >>>>>>> Jiacheng: >>>>>>> >>>>>>> First thing with the code excerpt below: TAILQ_FIRST always gives you >>>>>>> the head of the queue. To iterate through all the queue elements you >>>>>>> would use TAILQ_FOREACH() or you would modify the code to get the next >>>>>>> element using TAILQ_NEXT. I would just use TAILQ_FOREACH. There is an >>>>>>> example of this in ble_ll_sched.c. >>>>>>> >>>>>>> Some other things to note about scheduler queue: >>>>>>> 1) It is possible for items to be on the queue that have already >>>>>>> expired. That means that the current cputime might have passed >>>>>>> sch->start_time. Depending on how you want to deal with things, you are >>>>>>> might be better off doing a signed 32-bit subtract when calculating >>>>>>> time_tmp. >>>>>>> 2) You are not taking into account the end time of the scheduled event. >>>>>>> The event starts at sch->start_time and ends at sch->end_time. Well, if >>>>>>> all you care about is the time till the next event you wont have to >>>>>>> worry about the end time of the event, but if you want to iterate >>>>>>> through the schedule, the time between events is the start time of >>>>>>> event N minus the end time of event N - 1. >>>>>>> 3) When an event is executed it is removed from the scheduler queue. >>>>>>> Thus, if you asynchronously look at the first item in the scheduler >>>>>>> queue and compare it to the time now you have to be aware that an event >>>>>>> might be running and that the nimble stack is using the PHY. This could >>>>>>> also cause you to think that nothing is going to be done in the future, >>>>>>> but when the scheduled event is over that item gets rescheduled and >>>>>>> might get put back in the scheduler queue (see #4, below). >>>>>>> 4) Events in the scheduler queue appear only once. This is not an issue >>>>>>> if you are only looking at the first item on the queue, but if you >>>>>>> iterate through the queue this could affect you. For example, say there >>>>>>> are two items on the queue (item 1 is at head, item 2 is next and is >>>>>>> last). You see that the gap between the two events is 400 milliseconds >>>>>>> (I just made that number up). When item 1 is executed and done, that >>>>>>> event will get rescheduled. So lets say item 1 is a periodic event that >>>>>>> occurs every 100 msecs. Item 1 will get rescheduled causing you to >>>>>>> really only have 100 msecs between events. >>>>>>> 5) The “end_time” of the scheduled item may not be the true end time of >>>>>>> the underlying event. When scheduling connections we schedule them for >>>>>>> some fixed amount of time. This is done to guarantee that all >>>>>>> connections get a place in the scheduler queue. When the schedule item >>>>>>> executes at “start_time” and the item is a connection event, the >>>>>>> connection code will keep the current connection going past the >>>>>>> “end_time” of the scheduled event if there is more data to be sent and >>>>>>> the next scheduled item wont be missed. So you may think you have a gap >>>>>>> between scheduled events when in reality the underlying code is still >>>>>>> running. >>>>>>> 6) For better or worse, scanning events are not on the scheduler queue; >>>>>>> they are dealt with in an entirely different manner. This means that >>>>>>> the underlying PHY could be used when there is nothing on the schedule >>>>>>> queue. >>>>>>> >>>>>>> I have an idea of what you are trying to do and it might end up being a >>>>>>> bit tricky given the current code implementation. You may be better >>>>>>> served adding an item to the schedule queue but it all depends on how >>>>>>> you want to prioritize BLE activity with what you want to do. >>>>>>> >>>>>>> Will >>>>>>> >>>>>>>> On Jan 23, 2017, at 8:56 PM, WangJiacheng <[email protected]> >>>>>>>> wrote: >>>>>>>> >>>>>>>> Hi, >>>>>>>> >>>>>>>> I’m trying to find out a free time slot between Nimble scheduled >>>>>>>> events. >>>>>>>> >>>>>>>> I try to go through all items on the schedule queue global variable >>>>>>>> “g_ble_ll_sched_q” to find out all the scheduled LL events near >>>>>>>> future, function as >>>>>>>> /********************************************************************************/ >>>>>>>> uint32_t ll_eventq_free_time_from_now(void) >>>>>>>> { >>>>>>>> struct ble_ll_sched_item *sch; >>>>>>>> uint32_t cpu_time_now; >>>>>>>> uint32_t time_free; >>>>>>>> uint32_t time_tmp; >>>>>>>> >>>>>>>> time_free = 1000000000; >>>>>>>> cpu_time_now = os_cputime_get32(); >>>>>>>> >>>>>>>> /* Look through schedule queue */ >>>>>>>> while ((sch = TAILQ_FIRST(&g_ble_ll_sched_q)) != NULL) >>>>>>>> { >>>>>>>> time_tmp = sch->start_time - cpu_time_now; >>>>>>>> if (time_tmp < time_free) >>>>>>>> { >>>>>>>> time_free = time_tmp; >>>>>>>> } >>>>>>>> } >>>>>>>> >>>>>>>> return (time_free); >>>>>>>> } >>>>>>>> /********************************************************************************/ >>>>>>>> >>>>>>>> Does above function make sense to find out the free time at any given >>>>>>>> time point? or any suggestion to find out the free time slot between >>>>>>>> LL events? >>>>>>>> >>>>>>>> >>>>>>>> Thanks, >>>>>>>> >>>>>>>> Jiacheng >>>>>>>> >>>>>>> >>>>>> >>>>> >>>> >>> >> >
