-
Notifications
You must be signed in to change notification settings - Fork 28
Support for STM32 #302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Support for STM32 #302
Changes from all commits
6de22f6
256abcc
92fcfbe
dca5be8
89986a9
436b36b
bcbbf43
48be057
31d62b2
73aea2e
7899e6b
d11f3ff
58d2323
5ae3be6
4bb9231
9527537
8a97bcd
c46fbcc
b3097a8
f92307f
f2554dd
03124a5
6d2964c
13ca0d8
cd0c9c1
6d8d4db
bb75db6
6a66f47
a285e52
6ba0f42
a22a4fd
42fc271
895a8fb
0d3af6d
50603d7
cba9193
cf36f59
6e9e3c7
6974d8e
a2e1e4b
68ec2d7
2d23659
128e9a4
22b871d
68bd26a
335d52b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
fix-concurrency | ||
master |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* STM32 API support for the C target of Lingua Franca. */ | ||
|
||
#ifndef LF_STM32F4_SUPPORT_H | ||
#define LF_STM32F4_SUPPORT_H | ||
|
||
#define NO_TTY | ||
|
||
#include <stm32f4xx_hal.h> | ||
|
||
#define PRINTF_TAG "(" PRINTF_TIME ", " PRINTF_MICROSTEP ")" | ||
#define PRINTF_TIME "%lld" | ||
#define PRINTF_MICROSTEP "%d" | ||
|
||
#define _LF_TIMEOUT 1 | ||
|
||
#ifdef LF_THREADED | ||
#error "Threaded runtime not supported on STM32" | ||
#endif // LF_THREADED | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
#if defined(PLATFORM_STM32F4) | ||
|
||
#include "platform/lf_STM32f4_support.h" | ||
#include "low_level_platform.h" | ||
#include "tag.h" | ||
|
||
static volatile bool _lf_sleep_interrupted = false; | ||
static volatile bool _lf_async_event = false; | ||
|
||
// nested critical section counter | ||
static uint32_t _lf_num_nested_crit_sec = 0; | ||
|
||
// Timer upper half (for overflow) | ||
static uint32_t _lf_time_us_high = 0; | ||
|
||
#define LF_MIN_SLEEP_NS 10 | ||
|
||
// Combine 2 32bit works to a 64 bit word (Takes from nrf52 support) | ||
#define COMBINE_HI_LO(hi, lo) ((((uint64_t)hi) << 32) | ((uint64_t)lo)) | ||
|
||
void lf_SystemClock_Config(); | ||
void Error_Handler(); | ||
|
||
TIM_HandleTypeDef htim5; | ||
|
||
void _lf_initialize_clock(void) { | ||
// Standard initializations from generated code | ||
HAL_Init(); | ||
lf_SystemClock_Config(); | ||
|
||
// Configure TIM5 as our 32-bit clock timer | ||
__HAL_RCC_TIM5_CLK_ENABLE(); // initialize counter | ||
TIM5->CR1 = TIM_CR1_CEN; // enable counter | ||
|
||
// set prescaler to (16 - 1) = 15 | ||
// CPU runs a 16MHz so timer ticks at 1MHz | ||
// Which means period of 1 microsecond | ||
TIM5->PSC = 15; | ||
|
||
// Setup ISR to increment upper bits | ||
TIM5->DIER |= TIM_DIER_CC1IE; | ||
NVIC_EnableIRQ(TIM5_IRQn); | ||
|
||
/* This is to make the Prescaler actually work | ||
* For some reason, the Prescaler only kicks in once the timer has reset once. | ||
* Thus, the current solution is to manually ret the value to a really large | ||
* and force it to reset once. Its really jank but its the only way I could | ||
* find to make it work | ||
*/ | ||
TIM5->CNT = 0xFFFFFFFE; | ||
} | ||
|
||
// ISR for handling timer overflow -> We increment the upper timer | ||
void TIM5_IRQHandler(void) { | ||
if (TIM5->SR & TIM_FLAG_CC1) { | ||
TIM5->SR &= ~TIM_FLAG_CC1; | ||
_lf_time_us_high += 1; | ||
} | ||
} | ||
|
||
/** | ||
* Write the time since boot into time variable | ||
*/ | ||
int _lf_clock_gettime(instant_t* t) { | ||
// Timer is cooked | ||
if (!t) { | ||
return 1; | ||
} | ||
|
||
// Get the current microseconds from TIM5 | ||
uint32_t now_us_hi_pre = _lf_time_us_high; | ||
uint32_t now_us_low = TIM5->CNT; | ||
uint32_t now_us_hi_post = _lf_time_us_high; | ||
|
||
// Check if we read the time during a wrap | ||
if (now_us_hi_pre != now_us_hi_post) { | ||
// There was a wrap. read again and return | ||
now_us_low = TIM5->CNT; | ||
} | ||
|
||
// Combine upper and lower timers | ||
uint64_t now_us = COMBINE_HI_LO((now_us_hi_post - 1), now_us_low); | ||
*t = ((instant_t)now_us) * 1000; | ||
return 0; | ||
} | ||
|
||
/** | ||
* Make the STM32 sleep for set nanoseconds | ||
* Based on the lf_nrf52 support implementation | ||
*/ | ||
int lf_sleep(interval_t sleep_duration) { | ||
instant_t target_time; | ||
instant_t current_time; | ||
|
||
_lf_clock_gettime(¤t_time); | ||
target_time = current_time + sleep_duration; | ||
// HAL_Delay only supports miliseconds. We try to use that for as long as possible | ||
// before switching to another meothd for finer tuned delay times | ||
long delaytime_ms = sleep_duration / 1000000; | ||
HAL_Delay(delaytime_ms); | ||
|
||
while (current_time <= target_time) | ||
_lf_clock_gettime(¤t_time); | ||
|
||
return 0; | ||
} | ||
|
||
/* sleep until wakeup time but wake up if there is an async event | ||
*/ | ||
int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_time) { | ||
// Get the current time and sleep time | ||
instant_t now; | ||
_lf_clock_gettime(&now); | ||
interval_t duration = wakeup_time - now; | ||
|
||
// Edge case handling for super small duration | ||
if (duration <= 0) { | ||
return 0; | ||
} else if (duration < LF_MIN_SLEEP_NS) { | ||
instant_t current_time; | ||
_lf_clock_gettime(¤t_time); | ||
|
||
// We repurpose the lf_sleep function here, just to better streamline the code | ||
interval_t sleep_duration = wakeup_time - current_time; | ||
lf_sleep(sleep_duration); | ||
return 0; | ||
} | ||
|
||
// Enable interrupts and prepare to wait | ||
_lf_async_event = false; | ||
lf_enable_interrupts_nested(); | ||
|
||
do { | ||
_lf_clock_gettime(&now); | ||
// Exit when the timer is up or there is an exception | ||
} while (!_lf_async_event && (now < wakeup_time)); | ||
Comment on lines
+133
to
+136
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be low-power sleep, not busy-waiting. Is there anything in the HAL for doing low-power sleep? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There seems to be a low power sleep mode, but its only interrupt based. It seems most timed delay implementations just use a while loop. Im not sure it itll be worth it trying to create some workaround now? or just merge now and maybe improve the implementation later. |
||
|
||
// Disable interrupts again on exit | ||
lf_disable_interrupts_nested(); | ||
|
||
if (!_lf_async_event) { | ||
return 0; | ||
} else { | ||
return 1; | ||
} | ||
} | ||
|
||
// disables the IRQ with support for nested disabling | ||
int lf_disable_interrupts_nested() { | ||
// Disable interrupts if they are currently enabled | ||
if (_lf_num_nested_crit_sec == 0) { | ||
__disable_irq(); | ||
} | ||
|
||
// update the depth of disabling interrupts | ||
_lf_num_nested_crit_sec++; | ||
return 0; | ||
} | ||
|
||
// enables the IRQ (checks if other processes still want it disabled first) | ||
int lf_enable_interrupts_nested() { | ||
// Left the critical section more often than it was entered. | ||
|
||
if (_lf_num_nested_crit_sec <= 0) { | ||
return 1; | ||
} | ||
|
||
// update the depth of disabling interrupts | ||
_lf_num_nested_crit_sec--; | ||
|
||
// We enable interrupts again | ||
if (_lf_num_nested_crit_sec == 0) { | ||
__enable_irq(); | ||
} | ||
return 0; | ||
} | ||
|
||
int _lf_single_threaded_notify_of_event() { | ||
_lf_async_event = true; | ||
return 0; | ||
} | ||
|
||
// The following functions are copied from the STM32F4 HAL library | ||
void lf_SystemClock_Config(void) { | ||
RCC_OscInitTypeDef RCC_OscInitStruct = {0}; | ||
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; | ||
|
||
/** Configure the main internal regulator output voltage | ||
*/ | ||
__HAL_RCC_PWR_CLK_ENABLE(); | ||
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3); | ||
|
||
/** Initializes the RCC Oscillators according to the specified parameters | ||
* in the RCC_OscInitTypeDef structure. | ||
*/ | ||
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; | ||
RCC_OscInitStruct.HSIState = RCC_HSI_ON; | ||
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; | ||
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; | ||
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { | ||
Error_Handler(); | ||
} | ||
|
||
/** Initializes the CPU, AHB and APB buses clocks | ||
*/ | ||
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; | ||
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; | ||
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; | ||
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; | ||
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; | ||
|
||
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { | ||
Error_Handler(); | ||
} | ||
} | ||
|
||
// Called upon error like a hardfault. | ||
void Error_Handler(void) { | ||
__disable_irq(); | ||
while (1) { | ||
} | ||
} | ||
|
||
#endif |
Uh oh!
There was an error while loading. Please reload this page.