AnasSarkiz/usb-c-pd-brushed-motor
USB-C PD-powered motor controller with protected input, selectable 5/9/12 V buck supply, MCU qualification, PWM H-bridge drive, overvoltage sensing, regenerative energy-dump resistors, and safety interlocks.
- Version
- 0.0.1-a48-cloud-pipeline9-unrouted-c41d79a-305b1e7a
- License
- unset
- Stars
- 0
firmware/stm32_clock.c
#include "stm32_clock.h"
static volatile uint32_t timer_epoch_us;
#define CLOCK_POLLS 100000u
static bool wait_register(volatile uint32_t *reg, uint32_t mask) {
for (unsigned n = 0; n < CLOCK_POLLS; ++n)
if ((*reg & mask) == mask) return true;
return false;
}
bool motor_clock_valid(void) {
return (RCC->CR & (RCC_CR_HSION | RCC_CR_HSIRDY | RCC_CR_PLLON | RCC_CR_PLLRDY |
RCC_CR_HSIDIV)) ==
(RCC_CR_HSION | RCC_CR_HSIRDY | RCC_CR_PLLON | RCC_CR_PLLRDY) &&
RCC->PLLCFGR == MOTOR_PLL_CONFIG &&
(RCC->CFGR & (RCC_CFGR_SW | RCC_CFGR_SWS | RCC_CFGR_HPRE | RCC_CFGR_PPRE)) ==
(RCC_CFGR_SW_PLLRCLK | RCC_CFGR_SWS_PLLRCLK) &&
(FLASH->ACR & FLASH_ACR_LATENCY) == FLASH_ACR_LATENCY_0 &&
(PWR->CR1 & PWR_CR1_VOS) == PWR_CR1_VOS_0;
}
bool motor_clock_initialize(void) {
/* Reset owner must inhibit GPIO first. No live PLL reconfiguration. */
if ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSISYS ||
(RCC->CR & (RCC_CR_PLLON | RCC_CR_PLLRDY)) || (RCC->BDCR & RCC_BDCR_LSEON))
return false;
RCC->CR = (RCC->CR & ~RCC_CR_HSIDIV) | RCC_CR_HSION;
if (!wait_register(&RCC->CR, RCC_CR_HSIRDY)) return false;
RCC->APBENR1 |= RCC_APBENR1_PWREN;
if (!(RCC->APBENR1 & RCC_APBENR1_PWREN)) return false;
PWR->CR1 = (PWR->CR1 & ~PWR_CR1_VOS) | PWR_CR1_VOS_0;
for (unsigned n = 0; (PWR->SR2 & PWR_SR2_VOSF); ++n)
if (n >= CLOCK_POLLS) return false;
FLASH->ACR = (FLASH->ACR & ~FLASH_ACR_LATENCY) | FLASH_ACR_LATENCY_0;
if ((FLASH->ACR & FLASH_ACR_LATENCY) != FLASH_ACR_LATENCY_0) return false;
RCC->PLLCFGR = MOTOR_PLL_CONFIG;
if (RCC->PLLCFGR != MOTOR_PLL_CONFIG) return false;
RCC->CR |= RCC_CR_PLLON;
if (!wait_register(&RCC->CR, RCC_CR_PLLRDY)) return false;
RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_SW | RCC_CFGR_HPRE | RCC_CFGR_PPRE)) |
RCC_CFGR_SW_PLLRCLK;
for (unsigned n = 0; !motor_clock_valid(); ++n)
if (n >= CLOCK_POLLS) return false;
RCC->APBENR1 |= RCC_APBENR1_TIM3EN;
if (!(RCC->APBENR1 & RCC_APBENR1_TIM3EN)) return false;
TIM3->CR1 = 0; TIM3->DIER = 0;
TIM3->PSC = 31; TIM3->ARR = 65535; TIM3->EGR = TIM_EGR_UG;
TIM3->SR = 0; TIM3->CNT = 0; timer_epoch_us = 0;
TIM3->DIER = TIM_DIER_UIE;
NVIC_ClearPendingIRQ(TIM3_IRQn);
NVIC_SetPriority(TIM3_IRQn, 0);
NVIC_EnableIRQ(TIM3_IRQn);
TIM3->CR1 = TIM_CR1_CEN;
return TIM3->PSC == 31 && TIM3->ARR == 65535 &&
TIM3->DIER == TIM_DIER_UIE && TIM3->CR1 == TIM_CR1_CEN;
}
void TIM3_IRQHandler(void) {
if (TIM3->SR & TIM_SR_UIF) {
TIM3->SR = ~TIM_SR_UIF;
timer_epoch_us += 65536u;
}
}
uint32_t motor_time_us(void *context) {
(void)context;
uint32_t previous_mask = __get_PRIMASK();
__disable_irq();
uint32_t epoch = timer_epoch_us, count = TIM3->CNT;
/* An overflow after CNT was sampled belongs to the next epoch. It is
* consumed by the IRQ later; this reader never clears the pending event. */
if (TIM3->SR & TIM_SR_UIF) { epoch += 65536u; count = TIM3->CNT; }
__set_PRIMASK(previous_mask);
return epoch + count;
}
bool motor_watchdog_initialize(void) {
/* LSI29.5..34 kHz; DIV32*(127+1):120.5..138.9 ms. Deliberately not
* debug-frozen. Feed only after one bounded complete foreground iteration. */
IWDG->KR = 0xcccc; IWDG->KR = 0x5555;
IWDG->PR = 3; IWDG->RLR = 127;
for (unsigned n = 0; IWDG->SR; ++n)
if (n >= CLOCK_POLLS) return false;
IWDG->KR = 0xaaaa;
return IWDG->PR == 3 && IWDG->RLR == 127;
}
void motor_watchdog_refresh(void) { IWDG->KR = 0xaaaa; }