Skip to content

Commit 2bf0444

Browse files
dhylandsdpgeorge
authored andcommitted
Add support for pyb.micros() by using the systick timer.
I also removed trailing spaces from modpyb.c which affected a couple of lines technically not part of this patch. Tested using: https://github.com/dhylands/upy-examples/blob/master/micros_test.py which eventually fails due to wraparound issues (I could fix the test to compensate but didn't bother)
1 parent 8c0add4 commit 2bf0444

5 files changed

Lines changed: 82 additions & 3 deletions

File tree

stmhal/modpyb.c

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ STATIC mp_obj_t pyb_info(uint n_args, const mp_obj_t *args) {
9999
// get and print clock speeds
100100
// SYSCLK=168MHz, HCLK=168MHz, PCLK1=42MHz, PCLK2=84MHz
101101
{
102-
printf("S=%lu\nH=%lu\nP1=%lu\nP2=%lu\n",
102+
printf("S=%lu\nH=%lu\nP1=%lu\nP2=%lu\n",
103103
HAL_RCC_GetSysClockFreq(),
104104
HAL_RCC_GetHCLKFreq(),
105105
HAL_RCC_GetPCLK1Freq(),
@@ -187,11 +187,46 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_sync_obj, pyb_sync);
187187

188188
/// \function millis()
189189
/// Returns the number of milliseconds since the board was last reset.
190+
///
191+
/// Note that this may return a negative number. This allows you to always
192+
/// do:
193+
/// start = pyb.millis()
194+
/// ...do some operation...
195+
/// elapsed = pyb.millis() - start
196+
///
197+
/// and as long as the time of your operation is less than 24 days, you'll
198+
/// always get the right answer and not have to worry about whether pyb.millis()
199+
/// wraps around.
190200
STATIC mp_obj_t pyb_millis(void) {
191-
return mp_obj_new_int(HAL_GetTick());
201+
// We want to "cast" the 32 bit unsigned into a small-int. So we shift it
202+
// left by 1 to throw away the top bit, and then shift it right by one
203+
// to sign extend.
204+
mp_int_t val = HAL_GetTick() << 1;
205+
return mp_obj_new_int(val >> 1);
192206
}
193207
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_millis_obj, pyb_millis);
194208

209+
/// \function micros()
210+
/// Returns the number of microseconds since the board was last reset.
211+
///
212+
/// Note that this may return a negative number. This allows you to always
213+
/// do:
214+
/// start = pyb.micros()
215+
/// ...do some operation...
216+
/// elapsed = pyb.micros() - start
217+
///
218+
/// and as long as the time of your operation is less than 35 minutes, you'll
219+
/// always get the right answer and not have to worry about whether pyb.micros()
220+
/// wraps around.
221+
STATIC mp_obj_t pyb_micros(void) {
222+
// We want to "cast" the 32 bit unsigned into a small-int. So we shift it
223+
// left by 1 to throw away the top bit, and then shift it right by one
224+
// to sign extend.
225+
mp_int_t val = sys_tick_get_microseconds() << 1;
226+
return mp_obj_new_int(val >> 1);
227+
}
228+
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_micros_obj, pyb_micros);
229+
195230
/// \function delay(ms)
196231
/// Delay for the given number of milliseconds.
197232
STATIC mp_obj_t pyb_delay(mp_obj_t ms_in) {
@@ -251,7 +286,7 @@ STATIC mp_obj_t pyb_stop(void) {
251286
/* Enter Stop Mode */
252287
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
253288

254-
/* Configures system clock after wake-up from STOP: enable HSE, PLL and select
289+
/* Configures system clock after wake-up from STOP: enable HSE, PLL and select
255290
* PLL as system clock source (HSE and PLL are disabled in STOP mode) */
256291
SYSCLKConfig_STOP();
257292

@@ -343,6 +378,7 @@ STATIC const mp_map_elem_t pyb_module_globals_table[] = {
343378
{ MP_OBJ_NEW_QSTR(MP_QSTR_USB_VCP), (mp_obj_t)&pyb_usb_vcp_type },
344379

345380
{ MP_OBJ_NEW_QSTR(MP_QSTR_millis), (mp_obj_t)&pyb_millis_obj },
381+
{ MP_OBJ_NEW_QSTR(MP_QSTR_micros), (mp_obj_t)&pyb_micros_obj },
346382
{ MP_OBJ_NEW_QSTR(MP_QSTR_delay), (mp_obj_t)&pyb_delay_obj },
347383
{ MP_OBJ_NEW_QSTR(MP_QSTR_udelay), (mp_obj_t)&pyb_udelay_obj },
348384
{ MP_OBJ_NEW_QSTR(MP_QSTR_sync), (mp_obj_t)&pyb_sync_obj },

stmhal/qstrdefsport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Q(/flash/lib)
6767
Q(/sd)
6868
Q(/sd/lib)
6969
Q(millis)
70+
Q(micros)
7071

7172
// for file class
7273
Q(seek)

stmhal/stm32f4xx_it.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ void PendSV_Handler(void) {
174174
*/
175175
void SysTick_Handler(void) {
176176
HAL_IncTick();
177+
178+
// Read the systick control regster. This has the side effect of clearing
179+
// the COUNTFLAG bit, which makes the logic in sys_tick_get_microseconds
180+
// work properly.
181+
SysTick->CTRL;
177182
}
178183

179184
/******************************************************************************/

stmhal/systick.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
#include <stm32f4xx_hal.h>
2828
#include "mpconfig.h"
2929
#include "misc.h"
30+
#include "nlr.h"
31+
#include "qstr.h"
32+
#include "obj.h"
33+
#include "irq.h"
3034
#include "systick.h"
3135

3236
bool sys_tick_has_passed(uint32_t start_tick, uint32_t delay_ms) {
@@ -41,3 +45,35 @@ void sys_tick_wait_at_least(uint32_t start_tick, uint32_t delay_ms) {
4145
__WFI(); // enter sleep mode, waiting for interrupt
4246
}
4347
}
48+
49+
// The SysTick timer counts down at 168 MHz, so we can use that knowledge
50+
// to grab a microsecond counter.
51+
//
52+
// We assume that HAL_GetTickis returns milliseconds.
53+
uint32_t sys_tick_get_microseconds(void) {
54+
mp_int_t enabled = disable_irq();
55+
uint32_t counter = SysTick->VAL;
56+
uint32_t milliseconds = HAL_GetTick();
57+
uint32_t status = SysTick->CTRL;
58+
enable_irq(enabled);
59+
60+
// It's still possible for the countflag bit to get set if the counter was
61+
// reloaded between reading VAL and reading CTRL. With interrupts disabled
62+
// it definitely takes less than 50 HCLK cycles between reading VAL and
63+
// reading CTRL, so the test (counter > 50) is to cover the case where VAL
64+
// is +ve and very close to zero, and the COUNTFLAG bit is also set.
65+
if ((status & SysTick_CTRL_COUNTFLAG_Msk) && counter > 50) {
66+
// This means that the HW reloaded VAL between the time we read VAL and the
67+
// time we read CTRL, which implies that there is an interrupt pending
68+
// to increment the tick counter.
69+
milliseconds++;
70+
}
71+
uint32_t load = SysTick->LOAD;
72+
counter = load - counter; // Convert from decrementing to incrementing
73+
74+
// ((load + 1) / 1000) is the number of counts per microsecond.
75+
//
76+
// counter / ((load + 1) / 1000) scales from the systick clock to microseconds
77+
// and is the same thing as (counter * 1000) / (load + 1)
78+
return milliseconds * 1000 + (counter * 1000) / (load + 1);
79+
}

stmhal/systick.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@
2626

2727
void sys_tick_wait_at_least(uint32_t stc, uint32_t delay_ms);
2828
bool sys_tick_has_passed(uint32_t stc, uint32_t delay_ms);
29+
uint32_t sys_tick_get_microseconds(void);

0 commit comments

Comments
 (0)