Skip to content
Snippets Groups Projects
Commit 2bf04444 authored by Dave Hylands's avatar Dave Hylands Committed by Damien George
Browse files

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)
parent 8c0add4e
No related branches found
No related tags found
No related merge requests found
......@@ -187,11 +187,46 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_sync_obj, pyb_sync);
/// \function millis()
/// Returns the number of milliseconds since the board was last reset.
///
/// Note that this may return a negative number. This allows you to always
/// do:
/// start = pyb.millis()
/// ...do some operation...
/// elapsed = pyb.millis() - start
///
/// and as long as the time of your operation is less than 24 days, you'll
/// always get the right answer and not have to worry about whether pyb.millis()
/// wraps around.
STATIC mp_obj_t pyb_millis(void) {
return mp_obj_new_int(HAL_GetTick());
// We want to "cast" the 32 bit unsigned into a small-int. So we shift it
// left by 1 to throw away the top bit, and then shift it right by one
// to sign extend.
mp_int_t val = HAL_GetTick() << 1;
return mp_obj_new_int(val >> 1);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_millis_obj, pyb_millis);
/// \function micros()
/// Returns the number of microseconds since the board was last reset.
///
/// Note that this may return a negative number. This allows you to always
/// do:
/// start = pyb.micros()
/// ...do some operation...
/// elapsed = pyb.micros() - start
///
/// and as long as the time of your operation is less than 35 minutes, you'll
/// always get the right answer and not have to worry about whether pyb.micros()
/// wraps around.
STATIC mp_obj_t pyb_micros(void) {
// We want to "cast" the 32 bit unsigned into a small-int. So we shift it
// left by 1 to throw away the top bit, and then shift it right by one
// to sign extend.
mp_int_t val = sys_tick_get_microseconds() << 1;
return mp_obj_new_int(val >> 1);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_micros_obj, pyb_micros);
/// \function delay(ms)
/// Delay for the given number of milliseconds.
STATIC mp_obj_t pyb_delay(mp_obj_t ms_in) {
......@@ -343,6 +378,7 @@ STATIC const mp_map_elem_t pyb_module_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_USB_VCP), (mp_obj_t)&pyb_usb_vcp_type },
{ MP_OBJ_NEW_QSTR(MP_QSTR_millis), (mp_obj_t)&pyb_millis_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_micros), (mp_obj_t)&pyb_micros_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_delay), (mp_obj_t)&pyb_delay_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_udelay), (mp_obj_t)&pyb_udelay_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_sync), (mp_obj_t)&pyb_sync_obj },
......
......@@ -67,6 +67,7 @@ Q(/flash/lib)
Q(/sd)
Q(/sd/lib)
Q(millis)
Q(micros)
// for file class
Q(seek)
......
......@@ -174,6 +174,11 @@ void PendSV_Handler(void) {
*/
void SysTick_Handler(void) {
HAL_IncTick();
// Read the systick control regster. This has the side effect of clearing
// the COUNTFLAG bit, which makes the logic in sys_tick_get_microseconds
// work properly.
SysTick->CTRL;
}
/******************************************************************************/
......
......@@ -27,6 +27,10 @@
#include <stm32f4xx_hal.h>
#include "mpconfig.h"
#include "misc.h"
#include "nlr.h"
#include "qstr.h"
#include "obj.h"
#include "irq.h"
#include "systick.h"
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) {
__WFI(); // enter sleep mode, waiting for interrupt
}
}
// The SysTick timer counts down at 168 MHz, so we can use that knowledge
// to grab a microsecond counter.
//
// We assume that HAL_GetTickis returns milliseconds.
uint32_t sys_tick_get_microseconds(void) {
mp_int_t enabled = disable_irq();
uint32_t counter = SysTick->VAL;
uint32_t milliseconds = HAL_GetTick();
uint32_t status = SysTick->CTRL;
enable_irq(enabled);
// It's still possible for the countflag bit to get set if the counter was
// reloaded between reading VAL and reading CTRL. With interrupts disabled
// it definitely takes less than 50 HCLK cycles between reading VAL and
// reading CTRL, so the test (counter > 50) is to cover the case where VAL
// is +ve and very close to zero, and the COUNTFLAG bit is also set.
if ((status & SysTick_CTRL_COUNTFLAG_Msk) && counter > 50) {
// This means that the HW reloaded VAL between the time we read VAL and the
// time we read CTRL, which implies that there is an interrupt pending
// to increment the tick counter.
milliseconds++;
}
uint32_t load = SysTick->LOAD;
counter = load - counter; // Convert from decrementing to incrementing
// ((load + 1) / 1000) is the number of counts per microsecond.
//
// counter / ((load + 1) / 1000) scales from the systick clock to microseconds
// and is the same thing as (counter * 1000) / (load + 1)
return milliseconds * 1000 + (counter * 1000) / (load + 1);
}
......@@ -26,3 +26,4 @@
void sys_tick_wait_at_least(uint32_t stc, uint32_t delay_ms);
bool sys_tick_has_passed(uint32_t stc, uint32_t delay_ms);
uint32_t sys_tick_get_microseconds(void);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment