-
Notifications
You must be signed in to change notification settings - Fork 101
/
Copy pathfreertos_tickless.c
257 lines (214 loc) · 8.12 KB
/
freertos_tickless.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/******************************************************************************
*
* Copyright (C) 2022-2023 Maxim Integrated Products, Inc. (now owned by
* Analog Devices, Inc.),
* Copyright (C) 2023-2024 Analog Devices, Inc.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*
******************************************************************************/
/* Maxim CMSIS */
#include "mxc_device.h"
#include "board.h"
#include "mxc_assert.h"
#include "lp.h"
#include "pwrseq_regs.h"
#include "wut.h"
#include "mcr_regs.h"
#include "icc.h"
#include "pb.h"
#include "led.h"
#include "uart.h"
/* FreeRTOS includes */
#include "FreeRTOS.h"
#include "FreeRTOSConfig.h"
#include "task.h"
/* Bluetooth Cordio library */
#include "pal_timer.h"
#include "pal_uart.h"
#include "pal_bb.h"
#define MAX_WUT_TICKS (configRTC_TICK_RATE_HZ) /* Maximum deep sleep time, units of 32 kHz ticks */
#define MIN_WUT_TICKS 100 /* Minimum deep sleep time, units of 32 kHz ticks */
#define WAKEUP_US 500 /* Deep sleep recovery time, units of us */
/* Minimum ticks before SysTick interrupt, units of system clock ticks.
* Convert CPU_CLOCK_HZ to units of ticks per us
*/
#define MIN_SYSTICK (configCPU_CLOCK_HZ / 1000000 /* ticks / us */ * 10 /* us */)
/*
* Sleep-check function
*
* Your code should over-ride this weak function and return E_NO_ERROR if
* tickless sleep is permissible (ie. no UART/SPI/I2C activity). Any other
* return code will prevent FreeRTOS from entering tickless idle.
*/
int freertos_permit_tickless(void)
{
/* Can not disable BLE DBB and 32 MHz clock while trim procedure is ongoing */
if (MXC_WUT_TrimPending(MXC_WUT0) != E_NO_ERROR) {
return E_BUSY;
}
/* Figure out if the UART is active */
if (PalUartGetState(PAL_UART_ID_TERMINAL) == PAL_UART_STATE_BUSY) {
return E_BUSY;
}
/* Prevent characters from being corrupted if still transmitting,
UART will shutdown in deep sleep */
if (MXC_UART_GetActive(MXC_UART_GET_UART(CONSOLE_UART)) != E_NO_ERROR) {
return E_BUSY;
}
return E_NO_ERROR;
}
/*
* This function overrides vPortSuppressTicksAndSleep in portable/.../ARM_CM4F/port.c
*
* DEEPSLEEP mode will stop SysTick from counting, so that can't be
* used to wake up. Instead, calculate a wake-up period for the WUT to
* interrupt the WFI and continue execution.
*
*/
void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime)
{
uint32_t preCapture, postCapture, schUsec, dsTicks, dsWutTicks;
uint64_t bleSleepTicks, idleTicks, dsSysTickPeriods, schUsecElapsed;
bool_t schTimerActive;
/* We do not currently handle to case where the WUT is slower than the RTOS tick */
MXC_ASSERT(configRTC_TICK_RATE_HZ >= configTICK_RATE_HZ);
if (SysTick->VAL < MIN_SYSTICK) {
/* Avoid sleeping too close to a systick interrupt */
return;
}
/* Calculate the number of WUT ticks, but we need one to synchronize */
idleTicks = (uint64_t)(xExpectedIdleTime - 1) * (uint64_t)configRTC_TICK_RATE_HZ /
(uint64_t)configTICK_RATE_HZ;
if (idleTicks > MAX_WUT_TICKS) {
idleTicks = MAX_WUT_TICKS;
}
/* Check to see if we meet the minimum requirements for deep sleep */
if (idleTicks < (MIN_WUT_TICKS + WAKEUP_US)) {
return;
}
/* Enter a critical section but don't use the taskENTER_CRITICAL()
method as that will mask interrupts that should exit sleep mode. */
__asm volatile("cpsid i");
/* If a context switch is pending or a task is waiting for the scheduler
to be unsuspended then abandon the low power entry. */
/* Also check the MXC drivers for any in-progress activity */
if ((eTaskConfirmSleepModeStatus() == eAbortSleep) ||
(freertos_permit_tickless() != E_NO_ERROR)) {
/* Re-enable interrupts - see comments above the cpsid instruction()
above. */
__asm volatile("cpsie i");
return;
}
/* Determine if the Bluetooth scheduler is running */
if (PalTimerGetState() == PAL_TIMER_STATE_BUSY) {
schTimerActive = TRUE;
} else {
schTimerActive = FALSE;
}
if (!schTimerActive) {
uint32_t ts;
if (PalBbGetTimestamp(&ts)) {
/*Determine if PalBb is active, return if we get a valid time stamp indicating
* that the scheduler is waiting for a PalBb event */
/* Re-enable interrupts - see comments above the cpsid instruction()
above. */
__asm volatile("cpsie i");
return;
}
}
/* Disable SysTick */
SysTick->CTRL &= ~(SysTick_CTRL_ENABLE_Msk);
/* Enable wakeup from WUT */
NVIC_EnableIRQ(WUT0_IRQn);
MXC_LP_EnableWUTAlarmWakeup();
/* Determine if we need to snapshot the PalBb clock */
if (schTimerActive) {
/* Snapshot the current WUT value with the PalBb clock */
MXC_WUT_StoreCount(MXC_WUT0);
preCapture = MXC_WUT_GetCount(MXC_WUT0);
schUsec = PalTimerGetExpTime();
/* Adjust idleTicks for the time it takes to restart the BLE hardware */
idleTicks -= ((WAKEUP_US)*configRTC_TICK_RATE_HZ / 1000000);
/* Calculate the time to the next BLE scheduler event */
if (schUsec < WAKEUP_US) {
bleSleepTicks = 0;
} else {
bleSleepTicks = ((uint64_t)schUsec - (uint64_t)WAKEUP_US) *
(uint64_t)configRTC_TICK_RATE_HZ / (uint64_t)BB_CLK_RATE_HZ;
}
} else {
/* Snapshot the current WUT value */
MXC_WUT_WaitForEdge(MXC_WUT0);
preCapture = MXC_WUT_GetCount(MXC_WUT0);
bleSleepTicks = 0;
schUsec = 0;
}
/* Sleep for the shortest tick duration */
if ((schTimerActive) && (bleSleepTicks < idleTicks)) {
dsTicks = bleSleepTicks;
} else {
dsTicks = idleTicks;
}
/* Bound the deep sleep time */
if (dsTicks > MAX_WUT_TICKS) {
dsTicks = MAX_WUT_TICKS;
}
/* Don't deep sleep if we don't have time */
if (dsTicks >= MIN_WUT_TICKS) {
/* Arm the WUT interrupt */
MXC_WUT->cmp = preCapture + dsTicks;
if (schTimerActive) {
/* Stop the BLE scheduler timer */
PalTimerStop();
}
/* Shutdown BB hardware */
PalBbDisable();
LED_Off(SLEEP_LED);
LED_Off(DEEPSLEEP_LED);
MXC_LP_EnterStandbyMode();
LED_On(DEEPSLEEP_LED);
LED_On(SLEEP_LED);
/* Enable and restore the BB hardware */
PalBbEnable();
PalBbRestore();
if (schTimerActive) {
/* Restore the BB counter */
MXC_WUT_RestoreBBClock(MXC_WUT0, BB_CLK_RATE_HZ);
/* Restart the BLE scheduler timer */
dsWutTicks = MXC_WUT->cnt - preCapture;
schUsecElapsed =
(uint64_t)dsWutTicks * (uint64_t)1000000 / (uint64_t)configRTC_TICK_RATE_HZ;
int palTimerStartTicks = schUsec - schUsecElapsed;
if (palTimerStartTicks < 1) {
palTimerStartTicks = 1;
}
PalTimerStart(palTimerStartTicks);
}
}
/* Recalculate dsWutTicks for the FreeRTOS tick counter update */
MXC_WUT_WaitForEdge(MXC_WUT0);
postCapture = MXC_WUT_GetCount(MXC_WUT0);
dsWutTicks = postCapture - preCapture;
/*
* Advance ticks by # actually elapsed
*/
dsSysTickPeriods =
(uint64_t)dsWutTicks * (uint64_t)configTICK_RATE_HZ / (uint64_t)configRTC_TICK_RATE_HZ;
vTaskStepTick(dsSysTickPeriods);
/* Re-enable SysTick */
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
/* Re-enable interrupts - see comments above the cpsid instruction()
above. */
__asm volatile("cpsie i");
}