2015-03-12 09:21:58 +00:00
|
|
|
/* mbed Microcontroller Library
|
2016-11-14 09:01:07 +00:00
|
|
|
* Copyright (c) 2006-2016 ARM Limited
|
2015-03-12 09:21:58 +00:00
|
|
|
*
|
2016-11-14 09:01:07 +00:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
2015-03-12 09:21:58 +00:00
|
|
|
*
|
2016-11-14 09:01:07 +00:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2015-03-12 09:21:58 +00:00
|
|
|
*
|
2016-11-14 09:01:07 +00:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
2015-03-12 09:21:58 +00:00
|
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
|
|
#include "us_ticker_api.h"
|
|
|
|
#include "PeripheralNames.h"
|
2016-10-25 13:33:40 +00:00
|
|
|
#include "hal_tick.h"
|
2015-03-12 09:21:58 +00:00
|
|
|
|
2016-10-25 16:23:09 +00:00
|
|
|
// A 16-bit timer is used
|
2016-10-25 13:33:40 +00:00
|
|
|
#if TIM_MST_16BIT
|
2015-03-12 09:21:58 +00:00
|
|
|
|
2016-11-02 16:06:31 +00:00
|
|
|
TIM_HandleTypeDef TimMasterHandle;
|
2015-03-12 09:21:58 +00:00
|
|
|
|
2016-10-25 13:33:40 +00:00
|
|
|
volatile uint32_t SlaveCounter = 0;
|
2015-03-12 09:21:58 +00:00
|
|
|
volatile uint32_t oc_int_part = 0;
|
|
|
|
|
2016-11-14 08:56:54 +00:00
|
|
|
void us_ticker_init(void)
|
|
|
|
{
|
2017-07-25 09:50:50 +00:00
|
|
|
/* NOTE: assuming that HAL tick has already been initialized! */
|
2015-03-12 09:21:58 +00:00
|
|
|
}
|
|
|
|
|
2016-11-14 08:56:54 +00:00
|
|
|
uint32_t us_ticker_read()
|
|
|
|
{
|
2016-11-04 12:48:18 +00:00
|
|
|
uint16_t cntH_old, cntH, cntL;
|
2016-10-26 15:30:46 +00:00
|
|
|
do {
|
|
|
|
cntH_old = SlaveCounter;
|
2017-05-31 07:17:57 +00:00
|
|
|
/* SlaveCounter needs to be checked before AND after we read the
|
|
|
|
* current counter TIM_MST->CNT, in case it wraps around.
|
|
|
|
* there are 2 possible cases of wrap around
|
|
|
|
* 1) in case this function is interrupted by timer_irq_handler and
|
|
|
|
* the SlaveCounter is updated. In that case we will loop again.
|
2017-06-02 11:09:35 +00:00
|
|
|
* 2) in case this function is called from interrupt context during
|
2017-05-31 07:17:57 +00:00
|
|
|
* wrap-around condtion. That would prevent/delay the timer_irq_handler
|
2017-06-02 11:09:35 +00:00
|
|
|
* from being called so we need to locally check the FLAG_UPDATE and
|
2017-05-31 07:17:57 +00:00
|
|
|
* update the cntH accordingly. The SlaveCounter variable itself will
|
|
|
|
* be updated in the interrupt handler just after ...
|
|
|
|
*/
|
2016-10-26 15:30:46 +00:00
|
|
|
if (__HAL_TIM_GET_FLAG(&TimMasterHandle, TIM_FLAG_UPDATE) == SET) {
|
|
|
|
cntH_old += 1;
|
|
|
|
}
|
|
|
|
cntL = TIM_MST->CNT;
|
|
|
|
cntH = SlaveCounter;
|
|
|
|
if (__HAL_TIM_GET_FLAG(&TimMasterHandle, TIM_FLAG_UPDATE) == SET) {
|
|
|
|
cntH += 1;
|
|
|
|
}
|
|
|
|
} while(cntH_old != cntH);
|
|
|
|
// Glue the upper and lower part together to get a 32 bit timer
|
2016-11-04 12:48:18 +00:00
|
|
|
return (uint32_t)(cntH << 16 | cntL);
|
2016-10-25 13:33:40 +00:00
|
|
|
}
|
2015-03-12 09:21:58 +00:00
|
|
|
|
2016-11-14 08:56:54 +00:00
|
|
|
void us_ticker_set_interrupt(timestamp_t timestamp)
|
|
|
|
{
|
2017-06-02 11:10:41 +00:00
|
|
|
// NOTE: This function must be called with interrupts disabled to keep our
|
|
|
|
// timer interrupt setup atomic
|
2017-07-25 09:50:50 +00:00
|
|
|
|
2017-06-02 11:10:41 +00:00
|
|
|
// Set new output compare value
|
|
|
|
__HAL_TIM_SET_COMPARE(&TimMasterHandle, TIM_CHANNEL_1, timestamp & 0xFFFF);
|
|
|
|
// Ensure the compare event starts clear
|
|
|
|
__HAL_TIM_CLEAR_FLAG(&TimMasterHandle, TIM_FLAG_CC1);
|
|
|
|
// Enable IT
|
|
|
|
__HAL_TIM_ENABLE_IT(&TimMasterHandle, TIM_IT_CC1);
|
|
|
|
|
Ticker: add fire interrupt now function
fire_interrupt function should be used for events in the past. As we have now
64bit timestamp, we can figure out what is in the past, and ask a target to invoke
an interrupt immediately. The previous attemps in the target HAL tickers were not ideal, as it can wrap around easily (16 or 32 bit counters). This new
functionality should solve this problem.
set_interrupt for tickers in HAL code should not handle anything but the next match interrupt. If it was in the past is handled by the upper layer.
It is possible that we are setting next event to the close future, so once it is set it is already in the past. Therefore we add a check after set interrupt to verify it is in future.
If it is not, we fire interrupt immediately. This results in
two events - first one immediate, correct one. The second one might be scheduled in far future (almost entire ticker range),
that should be discarded.
The specification for the fire_interrupts are:
- should set pending bit for the ticker interrupt (as soon as possible),
the event we are scheduling is already in the past, and we do not want to skip
any events
- no arguments are provided, neither return value, not needed
- ticker should be initialized prior calling this function (no need to check if it is already initialized)
All our targets provide this new functionality, removing old misleading if (timestamp is in the past) checks.
2017-06-27 11:18:59 +00:00
|
|
|
/* Set the number of timer wrap-around loops before the actual timestamp
|
|
|
|
* is reached. If the calculated delta time is more than halfway to the
|
|
|
|
* next compare event, check to see if a compare event has already been
|
|
|
|
* set, and if so, add one to the wrap-around count. This is done to
|
|
|
|
* ensure the correct wrap count is used in the corner cases where the
|
|
|
|
* 16 bit counter passes the compare value during the process of
|
|
|
|
* configuring this interrupt.
|
|
|
|
*
|
|
|
|
* Assumption: The time to execute this function is less than 32ms
|
|
|
|
* (otherwise incorrect behaviour could result)
|
|
|
|
*
|
|
|
|
* Consider the following corner cases:
|
|
|
|
* 1) timestamp is 1 us in the future:
|
|
|
|
* oc_int_part = 0 initially
|
|
|
|
* oc_int_part left at 0 because ((delta - 1) & 0xFFFF) < 0x8000
|
|
|
|
* Compare event should happen in 1 us and us_ticker_irq_handler()
|
|
|
|
* called
|
|
|
|
* 2) timestamp is 0x8000 us in the future:
|
|
|
|
* oc_int_part = 0 initially
|
|
|
|
* oc_int_part left at 0 because ((delta - 1) & 0xFFFF) < 0x8000
|
|
|
|
* There should be no possibility of the CC1 flag being set yet
|
|
|
|
* (see assumption above). When the compare event does occur in
|
|
|
|
* 32768 us, us_ticker_irq_handler() will be called
|
|
|
|
* 3) timestamp is 0x8001 us in the future:
|
|
|
|
* oc_int_part = 0 initially
|
|
|
|
* ((delta - 1) & 0xFFFF) >= 0x8000 but there should be no
|
|
|
|
* possibility of the CC1 flag being set yet (see assumption above),
|
|
|
|
* so oc_int_part will be left at 0, and when the compare event
|
|
|
|
* does occur in 32769 us, us_ticker_irq_handler() will be called
|
|
|
|
* 4) timestamp is 0x10000 us in the future:
|
|
|
|
* oc_int_part = 0 initially
|
|
|
|
* ((delta - 1) & 0xFFFF) >= 0x8000
|
|
|
|
* There are two subcases:
|
|
|
|
* a) The timer counter has not incremented past the compare
|
|
|
|
* value while setting up the interrupt. In this case, the
|
|
|
|
* CC1 flag will not be set, so oc_int_part will be
|
|
|
|
* left at 0, and when the compare event occurs in 65536 us,
|
|
|
|
* us_ticker_irq_handler() will be called
|
|
|
|
* b) The timer counter has JUST incremented past the compare
|
|
|
|
* value. In this case, the CC1 flag will be set, so
|
|
|
|
* oc_int_part will be incremented to 1, and the interrupt will
|
|
|
|
* occur immediately after this function returns, where
|
|
|
|
* oc_int_part will decrement to 0 without calling
|
|
|
|
* us_ticker_irq_handler(). Then about 65536 us later, the
|
|
|
|
* compare event will occur again, and us_ticker_irq_handler()
|
|
|
|
* will be called
|
|
|
|
* 5) timestamp is 0x10001 us in the future:
|
|
|
|
* oc_int_part = 1 initially
|
|
|
|
* oc_int_part left at 1 because ((delta - 1) & 0xFFFF) < 0x8000
|
|
|
|
* CC1 flag will not be set (see assumption above). In 1 us the
|
|
|
|
* compare event will cause an interrupt, where oc_int_part will be
|
|
|
|
* decremented to 0 without calling us_ticker_irq_handler(). Then
|
|
|
|
* about 65536 us later, the compare event will occur again, and
|
|
|
|
* us_ticker_irq_handler() will be called
|
|
|
|
* 6) timestamp is 0x18000 us in the future:
|
|
|
|
* oc_int_part = 1 initially
|
|
|
|
* oc_int_part left at 1 because ((delta - 1) & 0xFFFF) < 0x8000
|
|
|
|
* There should be no possibility of the CC1 flag being set yet
|
|
|
|
* (see assumption above). When the compare event does occur in
|
|
|
|
* 32768 us, oc_int_part will be decremented to 0 without calling
|
|
|
|
* us_ticker_irq_handler(). Then about 65536 us later, the
|
|
|
|
* compare event will occur again, and us_ticker_irq_handler() will
|
|
|
|
* be called
|
|
|
|
* 7) timestamp is 0x18001 us in the future:
|
|
|
|
* oc_int_part = 1 initially
|
|
|
|
* ((delta - 1) & 0xFFFF) >= 0x8000 but there should be no
|
|
|
|
* possibility of the CC1 flag being set yet (see assumption above),
|
|
|
|
* so oc_int_part will be left at 1, and when the compare event
|
|
|
|
* does occur in 32769 us, oc_int_part will be decremented to 0
|
|
|
|
* without calling us_ticker_irq_handler(). Then about 65536 us
|
|
|
|
* later, the compare event will occur again, and
|
|
|
|
* us_ticker_irq_handler() will be called
|
|
|
|
*
|
|
|
|
* delta - 1 is used because the timer compare event happens on the
|
|
|
|
* counter incrementing to match the compare value, and it won't occur
|
|
|
|
* immediately when the compare value is set to the current counter
|
|
|
|
* value.
|
|
|
|
*/
|
|
|
|
uint32_t current_time = us_ticker_read();
|
|
|
|
uint32_t delta = timestamp - current_time;
|
2017-09-05 11:03:21 +00:00
|
|
|
/* Note: The case of delta <= 0 is handled in MBED upper layer */
|
Ticker: add fire interrupt now function
fire_interrupt function should be used for events in the past. As we have now
64bit timestamp, we can figure out what is in the past, and ask a target to invoke
an interrupt immediately. The previous attemps in the target HAL tickers were not ideal, as it can wrap around easily (16 or 32 bit counters). This new
functionality should solve this problem.
set_interrupt for tickers in HAL code should not handle anything but the next match interrupt. If it was in the past is handled by the upper layer.
It is possible that we are setting next event to the close future, so once it is set it is already in the past. Therefore we add a check after set interrupt to verify it is in future.
If it is not, we fire interrupt immediately. This results in
two events - first one immediate, correct one. The second one might be scheduled in far future (almost entire ticker range),
that should be discarded.
The specification for the fire_interrupts are:
- should set pending bit for the ticker interrupt (as soon as possible),
the event we are scheduling is already in the past, and we do not want to skip
any events
- no arguments are provided, neither return value, not needed
- ticker should be initialized prior calling this function (no need to check if it is already initialized)
All our targets provide this new functionality, removing old misleading if (timestamp is in the past) checks.
2017-06-27 11:18:59 +00:00
|
|
|
oc_int_part = (delta - 1) >> 16;
|
|
|
|
if ( ((delta - 1) & 0xFFFF) >= 0x8000 &&
|
|
|
|
__HAL_TIM_GET_FLAG(&TimMasterHandle, TIM_FLAG_CC1) == SET ) {
|
|
|
|
++oc_int_part;
|
|
|
|
/* NOTE: Instead of incrementing oc_int_part here, we could clear
|
|
|
|
* the CC1 flag, but then you'd have to wait to ensure the
|
|
|
|
* interrupt is knocked down before returning and reenabling
|
|
|
|
* interrupts. Since this is a rare case, it's not worth it
|
|
|
|
* to try and optimize it, and it keeps the code simpler and
|
|
|
|
* safer to just do this increment instead.
|
2017-05-31 07:19:01 +00:00
|
|
|
*/
|
2015-03-12 09:21:58 +00:00
|
|
|
}
|
Ticker: add fire interrupt now function
fire_interrupt function should be used for events in the past. As we have now
64bit timestamp, we can figure out what is in the past, and ask a target to invoke
an interrupt immediately. The previous attemps in the target HAL tickers were not ideal, as it can wrap around easily (16 or 32 bit counters). This new
functionality should solve this problem.
set_interrupt for tickers in HAL code should not handle anything but the next match interrupt. If it was in the past is handled by the upper layer.
It is possible that we are setting next event to the close future, so once it is set it is already in the past. Therefore we add a check after set interrupt to verify it is in future.
If it is not, we fire interrupt immediately. This results in
two events - first one immediate, correct one. The second one might be scheduled in far future (almost entire ticker range),
that should be discarded.
The specification for the fire_interrupts are:
- should set pending bit for the ticker interrupt (as soon as possible),
the event we are scheduling is already in the past, and we do not want to skip
any events
- no arguments are provided, neither return value, not needed
- ticker should be initialized prior calling this function (no need to check if it is already initialized)
All our targets provide this new functionality, removing old misleading if (timestamp is in the past) checks.
2017-06-27 11:18:59 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void us_ticker_fire_interrupt(void)
|
|
|
|
{
|
2017-09-05 11:03:21 +00:00
|
|
|
/* When firing the event, the number of 16 bits counter wrap-ups (oc_int)
|
|
|
|
* must be re-initialized */
|
|
|
|
oc_int_part = 0;
|
Ticker: add fire interrupt now function
fire_interrupt function should be used for events in the past. As we have now
64bit timestamp, we can figure out what is in the past, and ask a target to invoke
an interrupt immediately. The previous attemps in the target HAL tickers were not ideal, as it can wrap around easily (16 or 32 bit counters). This new
functionality should solve this problem.
set_interrupt for tickers in HAL code should not handle anything but the next match interrupt. If it was in the past is handled by the upper layer.
It is possible that we are setting next event to the close future, so once it is set it is already in the past. Therefore we add a check after set interrupt to verify it is in future.
If it is not, we fire interrupt immediately. This results in
two events - first one immediate, correct one. The second one might be scheduled in far future (almost entire ticker range),
that should be discarded.
The specification for the fire_interrupts are:
- should set pending bit for the ticker interrupt (as soon as possible),
the event we are scheduling is already in the past, and we do not want to skip
any events
- no arguments are provided, neither return value, not needed
- ticker should be initialized prior calling this function (no need to check if it is already initialized)
All our targets provide this new functionality, removing old misleading if (timestamp is in the past) checks.
2017-06-27 11:18:59 +00:00
|
|
|
HAL_TIM_GenerateEvent(&TimMasterHandle, TIM_EVENTSOURCE_CC1);
|
2015-03-12 09:21:58 +00:00
|
|
|
}
|
|
|
|
|
2017-07-25 09:50:50 +00:00
|
|
|
/* NOTE: must be called with interrupts disabled! */
|
2016-11-14 08:56:54 +00:00
|
|
|
void us_ticker_disable_interrupt(void)
|
|
|
|
{
|
2015-03-12 09:21:58 +00:00
|
|
|
__HAL_TIM_DISABLE_IT(&TimMasterHandle, TIM_IT_CC1);
|
|
|
|
}
|
|
|
|
|
2017-07-25 09:50:50 +00:00
|
|
|
/* NOTE: must be called with interrupts disabled! */
|
2016-11-14 08:56:54 +00:00
|
|
|
void us_ticker_clear_interrupt(void)
|
|
|
|
{
|
2017-06-02 07:30:31 +00:00
|
|
|
__HAL_TIM_CLEAR_FLAG(&TimMasterHandle, TIM_FLAG_CC1);
|
2015-03-12 09:21:58 +00:00
|
|
|
}
|
2016-10-25 13:33:40 +00:00
|
|
|
|
|
|
|
#endif // TIM_MST_16BIT
|