Add STM32F103 port

Add a fully functional STM32F1 port, currently mostly targeting STM32F103
microcontrollers. This requires an 8 MHz XTAL. The maximum possible step rate
is around 282K steps per second.

This uses stm32flash to burn the firmware. The bootloader needs to be started
by setting BOOT0 to 1 and resetting the MCU. There is no automatic bootloader,
unlike on Arduino.

Signed-off-by: Grigori Goronzy <greg@kinoho.net>
This commit is contained in:
Grigori Goronzy
2018-03-31 15:34:59 +02:00
committed by KevinOConnor
parent e097b08520
commit 75d5737211
173 changed files with 267435 additions and 0 deletions

188
src/stm32f1/timer.c Normal file
View File

@@ -0,0 +1,188 @@
// STM32F1 timer interrupt scheduling code.
//
// Copyright (C) 2018 Grigori Goronzy <greg@kinoho.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "autoconf.h"
#include "board/misc.h" // timer_from_us
#include "stm32f1xx.h"
#include "stm32f1xx.h"
#include "stm32f1xx_ll_bus.h"
#include "stm32f1xx_ll_tim.h"
#include "command.h" // shutdown
#include "board/irq.h" // irq_save
#include "sched.h" // sched_timer_dispatch
/****************************************************************
* Low level timer code
****************************************************************/
DECL_CONSTANT(CLOCK_FREQ, CONFIG_CLOCK_FREQ);
union u32_u {
struct { uint8_t b0, b1, b2, b3; };
struct { uint16_t lo, hi; };
uint32_t val;
};
static inline uint16_t
timer_get(void)
{
return LL_TIM_GetCounter(TIM2);
}
static inline void
timer_set(uint16_t next)
{
LL_TIM_OC_SetCompareCH1(TIM2, next);
}
static inline void
timer_repeat_set(uint16_t next)
{
LL_TIM_OC_SetCompareCH2(TIM2, next);
LL_TIM_ClearFlag_CC2(TIM2);
}
// Activate timer dispatch as soon as possible
void
timer_kick(void)
{
timer_set(timer_get() + 50);
LL_TIM_ClearFlag_CC1(TIM2);
}
static struct timer wrap_timer;
void
timer_reset(void)
{
sched_add_timer(&wrap_timer);
}
DECL_SHUTDOWN(timer_reset);
void
timer_init(void)
{
irqstatus_t flag = irq_save();
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
LL_TIM_SetPrescaler(TIM2, __LL_TIM_CALC_PSC(SystemCoreClock, CONFIG_CLOCK_FREQ));
LL_TIM_SetCounter(TIM2, 0);
LL_TIM_SetAutoReload(TIM2, 0xFFFF);
LL_TIM_EnableIT_CC1(TIM2);
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1);
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2);
NVIC_EnableIRQ(TIM2_IRQn);
NVIC_SetPriority(TIM2_IRQn, 0);
timer_kick();
timer_repeat_set(timer_get() + 50);
timer_reset();
LL_TIM_EnableCounter(TIM2);
irq_restore(flag);
}
DECL_INIT(timer_init);
/****************************************************************
* 32bit timer wrappers
****************************************************************/
static uint16_t timer_high;
// Return the current time (in absolute clock ticks).
uint32_t
timer_read_time(void)
{
irqstatus_t flag = irq_save();
union u32_u calc = { .val = timer_get() };
calc.hi = timer_high;
if (unlikely(LL_TIM_IsActiveFlag_UPDATE(TIM2))) {
irq_restore(flag);
if (calc.b1 < 0xff)
calc.hi++;
return calc.val;
}
irq_restore(flag);
return calc.val;
}
// Timer that runs every cycle - allows 16bit comparison optimizations
static uint_fast8_t
timer_event(struct timer *t)
{
union u32_u *nextwake = (void*)&wrap_timer.waketime;
if (LL_TIM_IsActiveFlag_UPDATE(TIM2)) {
// Hardware timer has overflowed - update overflow counter
LL_TIM_ClearFlag_UPDATE(TIM2);
timer_high++;
*nextwake = (union u32_u){ .hi = timer_high, .lo = 0x8000 };
} else {
*nextwake = (union u32_u){ .hi = timer_high + 1, .lo = 0x0000 };
}
return SF_RESCHEDULE;
}
static struct timer wrap_timer = {
.func = timer_event,
.waketime = 0x8000,
};
#define TIMER_IDLE_REPEAT_TICKS timer_from_us(500)
#define TIMER_REPEAT_TICKS timer_from_us(100)
#define TIMER_MIN_TRY_TICKS timer_from_us(1)
#define TIMER_DEFER_REPEAT_TICKS timer_from_us(5)
// Hardware timer IRQ handler - dispatch software timers
void __visible __aligned(16)
TIM2_IRQHandler(void)
{
irq_disable();
LL_TIM_ClearFlag_CC1(TIM2);
uint16_t next;
for (;;) {
// Run the next software timer
next = sched_timer_dispatch();
for (;;) {
int16_t diff = timer_get() - next;
if (likely(diff >= 0)) {
// Another timer is pending - briefly allow irqs and then run it
irq_enable();
if (unlikely(LL_TIM_IsActiveFlag_CC2(TIM2)))
goto check_defer;
irq_disable();
break;
}
if (likely(diff <= -TIMER_MIN_TRY_TICKS))
// Schedule next timer normally
goto done;
irq_enable();
if (unlikely(LL_TIM_IsActiveFlag_CC2(TIM2)))
goto check_defer;
irq_disable();
continue;
check_defer:
// Check if there are too many repeat timers
irq_disable();
uint16_t now = timer_get();
if ((int16_t)(next - now) < (int16_t)(-timer_from_us(1000)))
try_shutdown("Rescheduled timer in the past");
if (sched_tasks_busy()) {
timer_repeat_set(now + TIMER_REPEAT_TICKS);
next = now + TIMER_DEFER_REPEAT_TICKS;
goto done;
}
timer_repeat_set(now + TIMER_IDLE_REPEAT_TICKS);
timer_set(now);
}
}
done:
timer_set(next);
irq_enable();
}