User | Revision | Line number | New contents of line |
ganlikun |
0:06036f8bee2d
|
1
|
/* mbed Microcontroller Library
|
ganlikun |
0:06036f8bee2d
|
2
|
* Copyright (c) 2006-2016 ARM Limited
|
ganlikun |
0:06036f8bee2d
|
3
|
*
|
ganlikun |
0:06036f8bee2d
|
4
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
ganlikun |
0:06036f8bee2d
|
5
|
* you may not use this file except in compliance with the License.
|
ganlikun |
0:06036f8bee2d
|
6
|
* You may obtain a copy of the License at
|
ganlikun |
0:06036f8bee2d
|
7
|
*
|
ganlikun |
0:06036f8bee2d
|
8
|
* http://www.apache.org/licenses/LICENSE-2.0
|
ganlikun |
0:06036f8bee2d
|
9
|
*
|
ganlikun |
0:06036f8bee2d
|
10
|
* Unless required by applicable law or agreed to in writing, software
|
ganlikun |
0:06036f8bee2d
|
11
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
ganlikun |
0:06036f8bee2d
|
12
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
ganlikun |
0:06036f8bee2d
|
13
|
* See the License for the specific language governing permissions and
|
ganlikun |
0:06036f8bee2d
|
14
|
* limitations under the License.
|
ganlikun |
0:06036f8bee2d
|
15
|
*/
|
ganlikun |
0:06036f8bee2d
|
16
|
#include <stddef.h>
|
ganlikun |
0:06036f8bee2d
|
17
|
#include "us_ticker_api.h"
|
ganlikun |
0:06036f8bee2d
|
18
|
#include "PeripheralNames.h"
|
ganlikun |
0:06036f8bee2d
|
19
|
#include "hal_tick.h"
|
ganlikun |
0:06036f8bee2d
|
20
|
|
ganlikun |
0:06036f8bee2d
|
21
|
// A 16-bit timer is used
|
ganlikun |
0:06036f8bee2d
|
22
|
#if TIM_MST_16BIT
|
ganlikun |
0:06036f8bee2d
|
23
|
|
ganlikun |
0:06036f8bee2d
|
24
|
TIM_HandleTypeDef TimMasterHandle;
|
ganlikun |
0:06036f8bee2d
|
25
|
|
ganlikun |
0:06036f8bee2d
|
26
|
volatile uint32_t SlaveCounter = 0;
|
ganlikun |
0:06036f8bee2d
|
27
|
volatile uint32_t oc_int_part = 0;
|
ganlikun |
0:06036f8bee2d
|
28
|
|
ganlikun |
0:06036f8bee2d
|
29
|
void us_ticker_init(void)
|
ganlikun |
0:06036f8bee2d
|
30
|
{
|
ganlikun |
0:06036f8bee2d
|
31
|
/* NOTE: assuming that HAL tick has already been initialized! */
|
ganlikun |
0:06036f8bee2d
|
32
|
}
|
ganlikun |
0:06036f8bee2d
|
33
|
|
ganlikun |
0:06036f8bee2d
|
34
|
uint32_t us_ticker_read()
|
ganlikun |
0:06036f8bee2d
|
35
|
{
|
ganlikun |
0:06036f8bee2d
|
36
|
uint16_t cntH_old, cntH, cntL;
|
ganlikun |
0:06036f8bee2d
|
37
|
do {
|
ganlikun |
0:06036f8bee2d
|
38
|
cntH_old = SlaveCounter;
|
ganlikun |
0:06036f8bee2d
|
39
|
/* SlaveCounter needs to be checked before AND after we read the
|
ganlikun |
0:06036f8bee2d
|
40
|
* current counter TIM_MST->CNT, in case it wraps around.
|
ganlikun |
0:06036f8bee2d
|
41
|
* there are 2 possible cases of wrap around
|
ganlikun |
0:06036f8bee2d
|
42
|
* 1) in case this function is interrupted by timer_irq_handler and
|
ganlikun |
0:06036f8bee2d
|
43
|
* the SlaveCounter is updated. In that case we will loop again.
|
ganlikun |
0:06036f8bee2d
|
44
|
* 2) in case this function is called from interrupt context during
|
ganlikun |
0:06036f8bee2d
|
45
|
* wrap-around condtion. That would prevent/delay the timer_irq_handler
|
ganlikun |
0:06036f8bee2d
|
46
|
* from being called so we need to locally check the FLAG_UPDATE and
|
ganlikun |
0:06036f8bee2d
|
47
|
* update the cntH accordingly. The SlaveCounter variable itself will
|
ganlikun |
0:06036f8bee2d
|
48
|
* be updated in the interrupt handler just after ...
|
ganlikun |
0:06036f8bee2d
|
49
|
*/
|
ganlikun |
0:06036f8bee2d
|
50
|
if (__HAL_TIM_GET_FLAG(&TimMasterHandle, TIM_FLAG_UPDATE) == SET) {
|
ganlikun |
0:06036f8bee2d
|
51
|
cntH_old += 1;
|
ganlikun |
0:06036f8bee2d
|
52
|
}
|
ganlikun |
0:06036f8bee2d
|
53
|
cntL = TIM_MST->CNT;
|
ganlikun |
0:06036f8bee2d
|
54
|
cntH = SlaveCounter;
|
ganlikun |
0:06036f8bee2d
|
55
|
if (__HAL_TIM_GET_FLAG(&TimMasterHandle, TIM_FLAG_UPDATE) == SET) {
|
ganlikun |
0:06036f8bee2d
|
56
|
cntH += 1;
|
ganlikun |
0:06036f8bee2d
|
57
|
}
|
ganlikun |
0:06036f8bee2d
|
58
|
} while(cntH_old != cntH);
|
ganlikun |
0:06036f8bee2d
|
59
|
// Glue the upper and lower part together to get a 32 bit timer
|
ganlikun |
0:06036f8bee2d
|
60
|
return (uint32_t)(cntH << 16 | cntL);
|
ganlikun |
0:06036f8bee2d
|
61
|
}
|
ganlikun |
0:06036f8bee2d
|
62
|
|
ganlikun |
0:06036f8bee2d
|
63
|
void us_ticker_set_interrupt(timestamp_t timestamp)
|
ganlikun |
0:06036f8bee2d
|
64
|
{
|
ganlikun |
0:06036f8bee2d
|
65
|
// NOTE: This function must be called with interrupts disabled to keep our
|
ganlikun |
0:06036f8bee2d
|
66
|
// timer interrupt setup atomic
|
ganlikun |
0:06036f8bee2d
|
67
|
|
ganlikun |
0:06036f8bee2d
|
68
|
// Set new output compare value
|
ganlikun |
0:06036f8bee2d
|
69
|
__HAL_TIM_SET_COMPARE(&TimMasterHandle, TIM_CHANNEL_1, timestamp & 0xFFFF);
|
ganlikun |
0:06036f8bee2d
|
70
|
// Ensure the compare event starts clear
|
ganlikun |
0:06036f8bee2d
|
71
|
__HAL_TIM_CLEAR_FLAG(&TimMasterHandle, TIM_FLAG_CC1);
|
ganlikun |
0:06036f8bee2d
|
72
|
// Enable IT
|
ganlikun |
0:06036f8bee2d
|
73
|
__HAL_TIM_ENABLE_IT(&TimMasterHandle, TIM_IT_CC1);
|
ganlikun |
0:06036f8bee2d
|
74
|
|
ganlikun |
0:06036f8bee2d
|
75
|
/* Set the number of timer wrap-around loops before the actual timestamp
|
ganlikun |
0:06036f8bee2d
|
76
|
* is reached. If the calculated delta time is more than halfway to the
|
ganlikun |
0:06036f8bee2d
|
77
|
* next compare event, check to see if a compare event has already been
|
ganlikun |
0:06036f8bee2d
|
78
|
* set, and if so, add one to the wrap-around count. This is done to
|
ganlikun |
0:06036f8bee2d
|
79
|
* ensure the correct wrap count is used in the corner cases where the
|
ganlikun |
0:06036f8bee2d
|
80
|
* 16 bit counter passes the compare value during the process of
|
ganlikun |
0:06036f8bee2d
|
81
|
* configuring this interrupt.
|
ganlikun |
0:06036f8bee2d
|
82
|
*
|
ganlikun |
0:06036f8bee2d
|
83
|
* Assumption: The time to execute this function is less than 32ms
|
ganlikun |
0:06036f8bee2d
|
84
|
* (otherwise incorrect behaviour could result)
|
ganlikun |
0:06036f8bee2d
|
85
|
*
|
ganlikun |
0:06036f8bee2d
|
86
|
* Consider the following corner cases:
|
ganlikun |
0:06036f8bee2d
|
87
|
* 1) timestamp is 1 us in the future:
|
ganlikun |
0:06036f8bee2d
|
88
|
* oc_int_part = 0 initially
|
ganlikun |
0:06036f8bee2d
|
89
|
* oc_int_part left at 0 because ((delta - 1) & 0xFFFF) < 0x8000
|
ganlikun |
0:06036f8bee2d
|
90
|
* Compare event should happen in 1 us and us_ticker_irq_handler()
|
ganlikun |
0:06036f8bee2d
|
91
|
* called
|
ganlikun |
0:06036f8bee2d
|
92
|
* 2) timestamp is 0x8000 us in the future:
|
ganlikun |
0:06036f8bee2d
|
93
|
* oc_int_part = 0 initially
|
ganlikun |
0:06036f8bee2d
|
94
|
* oc_int_part left at 0 because ((delta - 1) & 0xFFFF) < 0x8000
|
ganlikun |
0:06036f8bee2d
|
95
|
* There should be no possibility of the CC1 flag being set yet
|
ganlikun |
0:06036f8bee2d
|
96
|
* (see assumption above). When the compare event does occur in
|
ganlikun |
0:06036f8bee2d
|
97
|
* 32768 us, us_ticker_irq_handler() will be called
|
ganlikun |
0:06036f8bee2d
|
98
|
* 3) timestamp is 0x8001 us in the future:
|
ganlikun |
0:06036f8bee2d
|
99
|
* oc_int_part = 0 initially
|
ganlikun |
0:06036f8bee2d
|
100
|
* ((delta - 1) & 0xFFFF) >= 0x8000 but there should be no
|
ganlikun |
0:06036f8bee2d
|
101
|
* possibility of the CC1 flag being set yet (see assumption above),
|
ganlikun |
0:06036f8bee2d
|
102
|
* so oc_int_part will be left at 0, and when the compare event
|
ganlikun |
0:06036f8bee2d
|
103
|
* does occur in 32769 us, us_ticker_irq_handler() will be called
|
ganlikun |
0:06036f8bee2d
|
104
|
* 4) timestamp is 0x10000 us in the future:
|
ganlikun |
0:06036f8bee2d
|
105
|
* oc_int_part = 0 initially
|
ganlikun |
0:06036f8bee2d
|
106
|
* ((delta - 1) & 0xFFFF) >= 0x8000
|
ganlikun |
0:06036f8bee2d
|
107
|
* There are two subcases:
|
ganlikun |
0:06036f8bee2d
|
108
|
* a) The timer counter has not incremented past the compare
|
ganlikun |
0:06036f8bee2d
|
109
|
* value while setting up the interrupt. In this case, the
|
ganlikun |
0:06036f8bee2d
|
110
|
* CC1 flag will not be set, so oc_int_part will be
|
ganlikun |
0:06036f8bee2d
|
111
|
* left at 0, and when the compare event occurs in 65536 us,
|
ganlikun |
0:06036f8bee2d
|
112
|
* us_ticker_irq_handler() will be called
|
ganlikun |
0:06036f8bee2d
|
113
|
* b) The timer counter has JUST incremented past the compare
|
ganlikun |
0:06036f8bee2d
|
114
|
* value. In this case, the CC1 flag will be set, so
|
ganlikun |
0:06036f8bee2d
|
115
|
* oc_int_part will be incremented to 1, and the interrupt will
|
ganlikun |
0:06036f8bee2d
|
116
|
* occur immediately after this function returns, where
|
ganlikun |
0:06036f8bee2d
|
117
|
* oc_int_part will decrement to 0 without calling
|
ganlikun |
0:06036f8bee2d
|
118
|
* us_ticker_irq_handler(). Then about 65536 us later, the
|
ganlikun |
0:06036f8bee2d
|
119
|
* compare event will occur again, and us_ticker_irq_handler()
|
ganlikun |
0:06036f8bee2d
|
120
|
* will be called
|
ganlikun |
0:06036f8bee2d
|
121
|
* 5) timestamp is 0x10001 us in the future:
|
ganlikun |
0:06036f8bee2d
|
122
|
* oc_int_part = 1 initially
|
ganlikun |
0:06036f8bee2d
|
123
|
* oc_int_part left at 1 because ((delta - 1) & 0xFFFF) < 0x8000
|
ganlikun |
0:06036f8bee2d
|
124
|
* CC1 flag will not be set (see assumption above). In 1 us the
|
ganlikun |
0:06036f8bee2d
|
125
|
* compare event will cause an interrupt, where oc_int_part will be
|
ganlikun |
0:06036f8bee2d
|
126
|
* decremented to 0 without calling us_ticker_irq_handler(). Then
|
ganlikun |
0:06036f8bee2d
|
127
|
* about 65536 us later, the compare event will occur again, and
|
ganlikun |
0:06036f8bee2d
|
128
|
* us_ticker_irq_handler() will be called
|
ganlikun |
0:06036f8bee2d
|
129
|
* 6) timestamp is 0x18000 us in the future:
|
ganlikun |
0:06036f8bee2d
|
130
|
* oc_int_part = 1 initially
|
ganlikun |
0:06036f8bee2d
|
131
|
* oc_int_part left at 1 because ((delta - 1) & 0xFFFF) < 0x8000
|
ganlikun |
0:06036f8bee2d
|
132
|
* There should be no possibility of the CC1 flag being set yet
|
ganlikun |
0:06036f8bee2d
|
133
|
* (see assumption above). When the compare event does occur in
|
ganlikun |
0:06036f8bee2d
|
134
|
* 32768 us, oc_int_part will be decremented to 0 without calling
|
ganlikun |
0:06036f8bee2d
|
135
|
* us_ticker_irq_handler(). Then about 65536 us later, the
|
ganlikun |
0:06036f8bee2d
|
136
|
* compare event will occur again, and us_ticker_irq_handler() will
|
ganlikun |
0:06036f8bee2d
|
137
|
* be called
|
ganlikun |
0:06036f8bee2d
|
138
|
* 7) timestamp is 0x18001 us in the future:
|
ganlikun |
0:06036f8bee2d
|
139
|
* oc_int_part = 1 initially
|
ganlikun |
0:06036f8bee2d
|
140
|
* ((delta - 1) & 0xFFFF) >= 0x8000 but there should be no
|
ganlikun |
0:06036f8bee2d
|
141
|
* possibility of the CC1 flag being set yet (see assumption above),
|
ganlikun |
0:06036f8bee2d
|
142
|
* so oc_int_part will be left at 1, and when the compare event
|
ganlikun |
0:06036f8bee2d
|
143
|
* does occur in 32769 us, oc_int_part will be decremented to 0
|
ganlikun |
0:06036f8bee2d
|
144
|
* without calling us_ticker_irq_handler(). Then about 65536 us
|
ganlikun |
0:06036f8bee2d
|
145
|
* later, the compare event will occur again, and
|
ganlikun |
0:06036f8bee2d
|
146
|
* us_ticker_irq_handler() will be called
|
ganlikun |
0:06036f8bee2d
|
147
|
*
|
ganlikun |
0:06036f8bee2d
|
148
|
* delta - 1 is used because the timer compare event happens on the
|
ganlikun |
0:06036f8bee2d
|
149
|
* counter incrementing to match the compare value, and it won't occur
|
ganlikun |
0:06036f8bee2d
|
150
|
* immediately when the compare value is set to the current counter
|
ganlikun |
0:06036f8bee2d
|
151
|
* value.
|
ganlikun |
0:06036f8bee2d
|
152
|
*/
|
ganlikun |
0:06036f8bee2d
|
153
|
uint32_t current_time = us_ticker_read();
|
ganlikun |
0:06036f8bee2d
|
154
|
uint32_t delta = timestamp - current_time;
|
ganlikun |
0:06036f8bee2d
|
155
|
/* Note: The case of delta <= 0 is handled in MBED upper layer */
|
ganlikun |
0:06036f8bee2d
|
156
|
oc_int_part = (delta - 1) >> 16;
|
ganlikun |
0:06036f8bee2d
|
157
|
if ( ((delta - 1) & 0xFFFF) >= 0x8000 &&
|
ganlikun |
0:06036f8bee2d
|
158
|
__HAL_TIM_GET_FLAG(&TimMasterHandle, TIM_FLAG_CC1) == SET ) {
|
ganlikun |
0:06036f8bee2d
|
159
|
++oc_int_part;
|
ganlikun |
0:06036f8bee2d
|
160
|
/* NOTE: Instead of incrementing oc_int_part here, we could clear
|
ganlikun |
0:06036f8bee2d
|
161
|
* the CC1 flag, but then you'd have to wait to ensure the
|
ganlikun |
0:06036f8bee2d
|
162
|
* interrupt is knocked down before returning and reenabling
|
ganlikun |
0:06036f8bee2d
|
163
|
* interrupts. Since this is a rare case, it's not worth it
|
ganlikun |
0:06036f8bee2d
|
164
|
* to try and optimize it, and it keeps the code simpler and
|
ganlikun |
0:06036f8bee2d
|
165
|
* safer to just do this increment instead.
|
ganlikun |
0:06036f8bee2d
|
166
|
*/
|
ganlikun |
0:06036f8bee2d
|
167
|
}
|
ganlikun |
0:06036f8bee2d
|
168
|
|
ganlikun |
0:06036f8bee2d
|
169
|
}
|
ganlikun |
0:06036f8bee2d
|
170
|
|
ganlikun |
0:06036f8bee2d
|
171
|
void us_ticker_fire_interrupt(void)
|
ganlikun |
0:06036f8bee2d
|
172
|
{
|
ganlikun |
0:06036f8bee2d
|
173
|
/* When firing the event, the number of 16 bits counter wrap-ups (oc_int)
|
ganlikun |
0:06036f8bee2d
|
174
|
* must be re-initialized */
|
ganlikun |
0:06036f8bee2d
|
175
|
oc_int_part = 0;
|
ganlikun |
0:06036f8bee2d
|
176
|
HAL_TIM_GenerateEvent(&TimMasterHandle, TIM_EVENTSOURCE_CC1);
|
ganlikun |
0:06036f8bee2d
|
177
|
}
|
ganlikun |
0:06036f8bee2d
|
178
|
|
ganlikun |
0:06036f8bee2d
|
179
|
/* NOTE: must be called with interrupts disabled! */
|
ganlikun |
0:06036f8bee2d
|
180
|
void us_ticker_disable_interrupt(void)
|
ganlikun |
0:06036f8bee2d
|
181
|
{
|
ganlikun |
0:06036f8bee2d
|
182
|
__HAL_TIM_DISABLE_IT(&TimMasterHandle, TIM_IT_CC1);
|
ganlikun |
0:06036f8bee2d
|
183
|
}
|
ganlikun |
0:06036f8bee2d
|
184
|
|
ganlikun |
0:06036f8bee2d
|
185
|
/* NOTE: must be called with interrupts disabled! */
|
ganlikun |
0:06036f8bee2d
|
186
|
void us_ticker_clear_interrupt(void)
|
ganlikun |
0:06036f8bee2d
|
187
|
{
|
ganlikun |
0:06036f8bee2d
|
188
|
__HAL_TIM_CLEAR_FLAG(&TimMasterHandle, TIM_FLAG_CC1);
|
ganlikun |
0:06036f8bee2d
|
189
|
}
|
ganlikun |
0:06036f8bee2d
|
190
|
|
ganlikun |
0:06036f8bee2d
|
191
|
#endif // TIM_MST_16BIT
|
ganlikun |
0:06036f8bee2d
|
192
|
|