This commit is contained in:
2026-05-23 18:02:41 +03:00
commit b85718ccf2
42 changed files with 9913 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
#ifndef APP_CONFIG_H
#define APP_CONFIG_H
#include <stdint.h>
/*
* B-G474E-DPOW1 quick configuration.
*
* SAFETY:
* - The project configures the buck half-bridge on PB12/PB13.
* - Keep DCDC_POWER_STAGE_ENABLE at 0 until VIN, load and oscilloscope
* probes are ready. With 0 the firmware prints ADC values and prepares
* HRTIM, but does not enable HRTIM outputs.
* - If VIN comes from USB-C/USB-PD through JP1, set
* DCDC_CONNECT_USBPD_INPUT to 1 only after checking the board jumpers.
*/
#define DCDC_POWER_STAGE_ENABLE 0U
#define DCDC_CONNECT_USBPD_INPUT 0U
/*
* Clock startup.
* 0 = safest rescue mode: run directly from HSI16, no PLL switch.
* 1 = normal performance mode: HSI16 -> PLL -> 170 MHz.
*
* If the debugger hangs inside SystemClock_Config(), keep this at 0 until
* the board is recovered and ST-LINK connects reliably.
*/
#define CLOCK_USE_PLL_170MHZ 1U
#define SYSCLK_HZ 170000000UL
#define HSI16_HZ 16000000UL
#define USART3_BAUDRATE 115200UL
#define DEBUG_ATTACH_DELAY_LOOPS 8000000UL
/*
* HRTIM Timer C drives the onboard buck leg:
* PB12 = BUCKBOOST_P1_DRIVE = HRTIM1_CHC1, high-side buck MOSFET
* PB13 = BUCKBOOST_N1_DRIVE = HRTIM1_CHC2, low-side buck MOSFET
*
* fHRCK = fHRTIM * 32. The firmware calculates the actual period from
* SystemCoreClock, so rescue HSI16 mode still works.
*/
#define DCDC_HRTIM_PERIOD_TICKS 27200U
#define DCDC_PWM_FREQUENCY_HZ 200000UL
#define DCDC_MIN_DUTY_TICKS 0U
#define DCDC_MAX_DUTY_TICKS 24000U
#define DCDC_START_DUTY_TICKS 400U
/*
* Dead-time values are expressed in HRTIM dead-time ticks.
* They are intentionally conservative starter values; verify PB12/PB13
* with an oscilloscope before enabling the power stage.
*/
#define DCDC_DEADTIME_RISING_TICKS 110U
#define DCDC_DEADTIME_FALLING_TICKS 350U
/*
* ADC measurement pins on B-G474E-DPOW1:
* PA1 = BUCKBOOST_VIN (ADC1_IN2)
* PA2 = BUCKBOOST_I_IN_AVG (ADC1_IN3)
* PA3 = BUCKBOOST_VOUT (ADC1_IN4)
*
* ST X-CUBE-DPower reference values for this board use:
* Vout_Scaling = 0.198795180 -> ADC pin voltage = VOUT * 0.198795180
* Iin_Scaling = 0.721543408 -> ADC pin voltage = IIN * 0.721543408 V/A
*
* VIN uses the same divider value by default here. If your hardware revision
* or solder bridge setup differs, adjust DCDC_VIN_SCALE_PPM after measuring.
*/
#define ADC_REFERENCE_MV 3300U
#define ADC_FULL_SCALE_COUNTS 4095U
#define DCDC_VOUT_SCALE_PPM 198795UL
#define DCDC_VIN_SCALE_PPM 198795UL
#define DCDC_IIN_UV_PER_MA 721UL
/* Starter setpoints and protection thresholds. */
#define DCDC_TARGET_VOUT_MV 3300U
#define DCDC_MIN_VIN_MV 4500U
#define DCDC_MAX_VOUT_MV 3800U
#define DCDC_MAX_INPUT_CURRENT_MA 300U
#define DCDC_HARD_INPUT_CURRENT_MA 450U
/*
* Simple voltage loop gains for the starter firmware.
* Duty command = feed-forward + proportional + integral - current limiting.
* Tune these gently with a current-limited supply and a scope.
*/
#define DCDC_KP_TICKS_PER_100MV 90
#define DCDC_KI_TICKS_PER_100MV 3
#define DCDC_CURRENT_LIMIT_KP 60
#endif

View File

@@ -0,0 +1,11 @@
#ifndef BOARD_H
#define BOARD_H
#include <stdint.h>
void Board_Init(void);
uint32_t Board_Millis(void);
void Board_DelayMs(uint32_t delay_ms);
void Board_FatalError(void);
#endif

View File

@@ -0,0 +1,48 @@
#ifndef DCDC_H
#define DCDC_H
#include <stdbool.h>
#include <stdint.h>
typedef struct
{
uint16_t vin_raw;
uint16_t iin_raw;
uint16_t vout_raw;
uint32_t vin_mv;
uint32_t iin_ma;
uint32_t vout_mv;
} DCDC_Measurements;
typedef enum
{
DCDC_STATE_STOPPED = 0,
DCDC_STATE_READY,
DCDC_STATE_RUNNING,
DCDC_STATE_FAULT
} DCDC_State;
typedef enum
{
DCDC_FAULT_NONE = 0,
DCDC_FAULT_UNDERVOLTAGE,
DCDC_FAULT_OVERVOLTAGE,
DCDC_FAULT_OVERCURRENT,
DCDC_FAULT_HRTIM
} DCDC_Fault;
void DCDC_Init(void);
void DCDC_Start(void);
void DCDC_Stop(void);
void DCDC_ControlStep(void);
void DCDC_ReadMeasurements(DCDC_Measurements *out);
DCDC_State DCDC_GetState(void);
DCDC_Fault DCDC_GetFault(void);
bool DCDC_IsHrtimReady(void);
uint32_t DCDC_GetDutyTicks(void);
uint32_t DCDC_GetPeriodTicks(void);
const char *DCDC_StateText(DCDC_State state);
const char *DCDC_FaultText(DCDC_Fault fault);
#endif

View File

@@ -0,0 +1,9 @@
#ifndef RETARGET_H
#define RETARGET_H
#include <stdint.h>
void Retarget_Init(uint32_t baudrate);
void Retarget_PutChar(char ch);
#endif

View File

@@ -0,0 +1,189 @@
#include "board.h"
#include "app_config.h"
#include <stdbool.h>
#include "stm32g474xx.h"
#define CLOCK_STARTUP_TIMEOUT 1000000UL
static volatile uint32_t s_ms_ticks;
static void SystemClock_Config(void);
static bool wait_mask_set(volatile uint32_t *reg, uint32_t mask);
static bool wait_mask_clear(volatile uint32_t *reg, uint32_t mask);
static bool wait_mask_value(volatile uint32_t *reg, uint32_t mask, uint32_t value);
static bool Flash_SetLatency(uint32_t latency);
static void Clock_UseHsi16(void);
void Board_Init(void)
{
SystemClock_Config();
if (SysTick_Config(SystemCoreClock / 1000U) != 0U)
{
Board_FatalError();
}
}
uint32_t Board_Millis(void)
{
return s_ms_ticks;
}
void Board_DelayMs(uint32_t delay_ms)
{
uint32_t start = Board_Millis();
while ((Board_Millis() - start) < delay_ms)
{
__NOP();
}
}
void Board_FatalError(void)
{
__disable_irq();
while (1)
{
__NOP();
}
}
void SysTick_Handler(void)
{
s_ms_ticks++;
}
static void SystemClock_Config(void)
{
Clock_UseHsi16();
#if CLOCK_USE_PLL_170MHZ
/* Enable PWR and switch the STM32G474 regulator to Range 1 boost. */
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;
(void)RCC->APB1ENR1;
PWR->CR1 = (PWR->CR1 & ~PWR_CR1_VOS) | PWR_CR1_VOS_0;
PWR->CR5 &= ~PWR_CR5_R1MODE;
if (!wait_mask_clear(&PWR->SR2, PWR_SR2_VOSF))
{
Clock_UseHsi16();
return;
}
/*
* ST G474 power examples use FLASH_LATENCY_4 for 170 MHz.
* Set only the latency field first, then switch SYSCLK, then enable cache.
*/
if (!Flash_SetLatency(FLASH_ACR_LATENCY_4WS))
{
Clock_UseHsi16();
return;
}
RCC->CR |= RCC_CR_HSION;
if (!wait_mask_set(&RCC->CR, RCC_CR_HSIRDY))
{
Clock_UseHsi16();
return;
}
/* HSI16 / 4 * 85 / 2 = 170 MHz. */
RCC->CR &= ~RCC_CR_PLLON;
if (!wait_mask_clear(&RCC->CR, RCC_CR_PLLRDY))
{
Clock_UseHsi16();
return;
}
RCC->PLLCFGR =
RCC_PLLCFGR_PLLSRC_HSI |
(3UL << RCC_PLLCFGR_PLLM_Pos) |
(85UL << RCC_PLLCFGR_PLLN_Pos) |
RCC_PLLCFGR_PLLREN;
RCC->CR |= RCC_CR_PLLON;
if (!wait_mask_set(&RCC->CR, RCC_CR_PLLRDY))
{
Clock_UseHsi16();
return;
}
RCC->CFGR =
RCC_CFGR_HPRE_DIV1 |
RCC_CFGR_PPRE1_DIV1 |
RCC_CFGR_PPRE2_DIV1 |
RCC_CFGR_SW_PLL;
if (!wait_mask_value(&RCC->CFGR, RCC_CFGR_SWS, RCC_CFGR_SWS_PLL))
{
Clock_UseHsi16();
return;
}
FLASH->ACR |= FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_PRFTEN;
SystemCoreClockUpdate();
#endif
}
static bool wait_mask_set(volatile uint32_t *reg, uint32_t mask)
{
uint32_t timeout = CLOCK_STARTUP_TIMEOUT;
while (((*reg & mask) != mask) && (timeout > 0U))
{
timeout--;
}
return timeout > 0U;
}
static bool wait_mask_clear(volatile uint32_t *reg, uint32_t mask)
{
uint32_t timeout = CLOCK_STARTUP_TIMEOUT;
while (((*reg & mask) != 0U) && (timeout > 0U))
{
timeout--;
}
return timeout > 0U;
}
static bool wait_mask_value(volatile uint32_t *reg, uint32_t mask, uint32_t value)
{
uint32_t timeout = CLOCK_STARTUP_TIMEOUT;
while (((*reg & mask) != value) && (timeout > 0U))
{
timeout--;
}
return timeout > 0U;
}
static bool Flash_SetLatency(uint32_t latency)
{
uint32_t timeout = CLOCK_STARTUP_TIMEOUT;
FLASH->ACR = (FLASH->ACR & ~FLASH_ACR_LATENCY) | latency;
while (((FLASH->ACR & FLASH_ACR_LATENCY) != latency) && (timeout > 0U))
{
timeout--;
}
return timeout > 0U;
}
static void Clock_UseHsi16(void)
{
RCC->CR |= RCC_CR_HSION;
(void)wait_mask_set(&RCC->CR, RCC_CR_HSIRDY);
(void)Flash_SetLatency(FLASH_ACR_LATENCY_0WS);
RCC->CFGR =
RCC_CFGR_HPRE_DIV1 |
RCC_CFGR_PPRE1_DIV1 |
RCC_CFGR_PPRE2_DIV1 |
RCC_CFGR_SW_HSI;
(void)wait_mask_value(&RCC->CFGR, RCC_CFGR_SWS, RCC_CFGR_SWS_HSI);
SystemCoreClockUpdate();
}

View File

@@ -0,0 +1,462 @@
#include "dcdc.h"
#include "app_config.h"
#include "board.h"
#include "stm32g474xx.h"
#define HRTIM_TIMER_C_INDEX 2U
#define ADC_SAMPLE_TIME_47CYCLES 5U
#define HRTIM_DLL_READY_TIMEOUT 1000000UL
static DCDC_State s_state = DCDC_STATE_STOPPED;
static DCDC_Fault s_fault = DCDC_FAULT_NONE;
static uint32_t s_duty_ticks = DCDC_START_DUTY_TICKS;
static uint32_t s_period_ticks = DCDC_HRTIM_PERIOD_TICKS;
static int32_t s_integrator_ticks;
static bool s_hrtim_ready;
static void gpio_init_for_dcdc(void);
static void adc1_init(void);
static uint16_t adc1_read_channel(uint32_t channel);
static bool hrtim1_timer_c_init(void);
static bool hrtim1_wait_dll_ready(void);
static void hrtim1_outputs_enable(bool enable);
static void hrtim1_set_duty(uint32_t duty_ticks);
static uint32_t hrtim_period_from_clock(void);
static uint32_t hrtim_max_duty_ticks(void);
static uint32_t adc_raw_to_mv(uint16_t raw);
static uint32_t sense_mv_to_voltage_mv(uint32_t sense_mv, uint32_t scale_ppm);
static uint32_t sense_mv_to_current_ma(uint32_t sense_mv);
static void set_usbpd_input_switch(bool enable);
static void set_loads_off(void);
static void latch_fault(DCDC_Fault fault);
void DCDC_Init(void)
{
gpio_init_for_dcdc();
set_usbpd_input_switch(false);
set_loads_off();
adc1_init();
s_hrtim_ready = hrtim1_timer_c_init();
if (s_hrtim_ready)
{
hrtim1_outputs_enable(false);
}
s_state = DCDC_STATE_READY;
s_fault = DCDC_FAULT_NONE;
}
void DCDC_Start(void)
{
if (s_fault != DCDC_FAULT_NONE)
{
return;
}
if (!s_hrtim_ready)
{
latch_fault(DCDC_FAULT_HRTIM);
return;
}
#if DCDC_CONNECT_USBPD_INPUT
set_usbpd_input_switch(true);
#endif
s_integrator_ticks = 0;
hrtim1_set_duty(DCDC_START_DUTY_TICKS);
HRTIM1->sMasterRegs.MCR |= HRTIM_MCR_TCCEN;
hrtim1_outputs_enable(true);
s_state = DCDC_STATE_RUNNING;
}
void DCDC_Stop(void)
{
hrtim1_outputs_enable(false);
hrtim1_set_duty(DCDC_MIN_DUTY_TICKS);
HRTIM1->sMasterRegs.MCR &= ~HRTIM_MCR_TCCEN;
set_usbpd_input_switch(false);
s_state = DCDC_STATE_STOPPED;
}
void DCDC_ControlStep(void)
{
DCDC_Measurements m;
int32_t error_mv;
int32_t feed_forward;
int32_t duty;
DCDC_ReadMeasurements(&m);
if (s_state != DCDC_STATE_RUNNING)
{
return;
}
if (m.vin_mv < DCDC_MIN_VIN_MV)
{
latch_fault(DCDC_FAULT_UNDERVOLTAGE);
return;
}
if (m.vout_mv > DCDC_MAX_VOUT_MV)
{
latch_fault(DCDC_FAULT_OVERVOLTAGE);
return;
}
if (m.iin_ma > DCDC_HARD_INPUT_CURRENT_MA)
{
latch_fault(DCDC_FAULT_OVERCURRENT);
return;
}
/*
* Starter buck controller:
* 1. feed-forward estimates duty from Vout/Vin,
* 2. PI term removes static voltage error,
* 3. current term pulls duty down before hard over-current trips.
*
* This is deliberately readable, not a final compensated SMPS loop.
*/
feed_forward = (int32_t)(((uint64_t)DCDC_TARGET_VOUT_MV * s_period_ticks) / m.vin_mv);
error_mv = (int32_t)DCDC_TARGET_VOUT_MV - (int32_t)m.vout_mv;
s_integrator_ticks += (error_mv * DCDC_KI_TICKS_PER_100MV) / 100;
if (s_integrator_ticks > 3000)
{
s_integrator_ticks = 3000;
}
else if (s_integrator_ticks < -3000)
{
s_integrator_ticks = -3000;
}
duty = feed_forward +
((error_mv * DCDC_KP_TICKS_PER_100MV) / 100) +
s_integrator_ticks;
if (m.iin_ma > DCDC_MAX_INPUT_CURRENT_MA)
{
duty -= (int32_t)((m.iin_ma - DCDC_MAX_INPUT_CURRENT_MA) * DCDC_CURRENT_LIMIT_KP);
}
if (duty < (int32_t)DCDC_MIN_DUTY_TICKS)
{
duty = (int32_t)DCDC_MIN_DUTY_TICKS;
}
else if (duty > (int32_t)hrtim_max_duty_ticks())
{
duty = (int32_t)hrtim_max_duty_ticks();
}
hrtim1_set_duty((uint32_t)duty);
}
void DCDC_ReadMeasurements(DCDC_Measurements *out)
{
uint32_t vin_sense_mv;
uint32_t iin_sense_mv;
uint32_t vout_sense_mv;
out->vin_raw = adc1_read_channel(2U);
out->iin_raw = adc1_read_channel(3U);
out->vout_raw = adc1_read_channel(4U);
vin_sense_mv = adc_raw_to_mv(out->vin_raw);
iin_sense_mv = adc_raw_to_mv(out->iin_raw);
vout_sense_mv = adc_raw_to_mv(out->vout_raw);
out->vin_mv = sense_mv_to_voltage_mv(vin_sense_mv, DCDC_VIN_SCALE_PPM);
out->iin_ma = sense_mv_to_current_ma(iin_sense_mv);
out->vout_mv = sense_mv_to_voltage_mv(vout_sense_mv, DCDC_VOUT_SCALE_PPM);
}
DCDC_State DCDC_GetState(void)
{
return s_state;
}
DCDC_Fault DCDC_GetFault(void)
{
return s_fault;
}
bool DCDC_IsHrtimReady(void)
{
return s_hrtim_ready;
}
uint32_t DCDC_GetDutyTicks(void)
{
return s_duty_ticks;
}
uint32_t DCDC_GetPeriodTicks(void)
{
return s_period_ticks;
}
const char *DCDC_StateText(DCDC_State state)
{
switch (state)
{
case DCDC_STATE_STOPPED: return "stopped";
case DCDC_STATE_READY: return "ready";
case DCDC_STATE_RUNNING: return "running";
case DCDC_STATE_FAULT: return "fault";
default: return "unknown";
}
}
const char *DCDC_FaultText(DCDC_Fault fault)
{
switch (fault)
{
case DCDC_FAULT_NONE: return "none";
case DCDC_FAULT_UNDERVOLTAGE: return "vin undervoltage";
case DCDC_FAULT_OVERVOLTAGE: return "vout overvoltage";
case DCDC_FAULT_OVERCURRENT: return "input overcurrent";
case DCDC_FAULT_HRTIM: return "hrtim dll timeout";
default: return "unknown";
}
}
static void gpio_init_for_dcdc(void)
{
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN | RCC_AHB2ENR_GPIOBEN | RCC_AHB2ENR_GPIOCEN;
(void)RCC->AHB2ENR;
/* PA1/PA2/PA3 are analog feedback signals: VIN, input current, VOUT. */
GPIOA->MODER |= (3UL << (1U * 2U)) | (3UL << (2U * 2U)) | (3UL << (3U * 2U));
GPIOA->PUPDR &= ~((3UL << (1U * 2U)) | (3UL << (2U * 2U)) | (3UL << (3U * 2U)));
/* PB12/PB13 are HRTIM1 Timer C outputs for the synchronous buck leg. */
GPIOB->MODER &= ~((3UL << (12U * 2U)) | (3UL << (13U * 2U)));
GPIOB->MODER |= ((2UL << (12U * 2U)) | (2UL << (13U * 2U)));
GPIOB->OTYPER &= ~((1UL << 12U) | (1UL << 13U));
GPIOB->OSPEEDR |= ((3UL << (12U * 2U)) | (3UL << (13U * 2U)));
GPIOB->PUPDR &= ~((3UL << (12U * 2U)) | (3UL << (13U * 2U)));
GPIOB->AFR[1] &= ~((0xFUL << ((12U - 8U) * 4U)) | (0xFUL << ((13U - 8U) * 4U)));
GPIOB->AFR[1] |= ((13UL << ((12U - 8U) * 4U)) | (13UL << ((13U - 8U) * 4U)));
/* PC3 controls USBPD_VBUS to VIN switch. PC14/PC15 switch onboard loads. */
GPIOC->MODER &= ~((3UL << (3U * 2U)) | (3UL << (14U * 2U)) | (3UL << (15U * 2U)));
GPIOC->MODER |= ((1UL << (3U * 2U)) | (1UL << (14U * 2U)) | (1UL << (15U * 2U)));
GPIOC->OTYPER &= ~((1UL << 3U) | (1UL << 14U) | (1UL << 15U));
GPIOC->OSPEEDR |= ((2UL << (3U * 2U)) | (2UL << (14U * 2U)) | (2UL << (15U * 2U)));
GPIOC->PUPDR &= ~((3UL << (3U * 2U)) | (3UL << (14U * 2U)) | (3UL << (15U * 2U)));
}
static void adc1_init(void)
{
RCC->AHB2ENR |= RCC_AHB2ENR_ADC12EN;
(void)RCC->AHB2ENR;
/* Synchronous ADC clock HCLK/4 = 42.5 MHz. */
ADC12_COMMON->CCR = (ADC12_COMMON->CCR & ~ADC_CCR_CKMODE) |
(ADC_CCR_CKMODE_1 | ADC_CCR_CKMODE_0);
ADC1->CR &= ~ADC_CR_DEEPPWD;
ADC1->CR |= ADC_CR_ADVREGEN;
Board_DelayMs(1U);
ADC1->CR |= ADC_CR_ADCAL;
while ((ADC1->CR & ADC_CR_ADCAL) != 0U)
{
__NOP();
}
ADC1->CFGR = ADC_CFGR_OVRMOD;
ADC1->SMPR1 =
(ADC_SAMPLE_TIME_47CYCLES << ADC_SMPR1_SMP2_Pos) |
(ADC_SAMPLE_TIME_47CYCLES << ADC_SMPR1_SMP3_Pos) |
(ADC_SAMPLE_TIME_47CYCLES << ADC_SMPR1_SMP4_Pos);
ADC1->ISR = ADC_ISR_ADRDY;
ADC1->CR |= ADC_CR_ADEN;
while ((ADC1->ISR & ADC_ISR_ADRDY) == 0U)
{
__NOP();
}
}
static uint16_t adc1_read_channel(uint32_t channel)
{
ADC1->SQR1 = (channel << ADC_SQR1_SQ1_Pos);
ADC1->ISR = ADC_ISR_EOC | ADC_ISR_EOS;
ADC1->CR |= ADC_CR_ADSTART;
while ((ADC1->ISR & ADC_ISR_EOC) == 0U)
{
__NOP();
}
return (uint16_t)(ADC1->DR & 0x0FFFU);
}
static bool hrtim1_timer_c_init(void)
{
HRTIM_Timerx_TypeDef *timer = &HRTIM1->sTimerxRegs[HRTIM_TIMER_C_INDEX];
RCC->APB2ENR |= RCC_APB2ENR_HRTIM1EN;
(void)RCC->APB2ENR;
HRTIM1->sCommonRegs.ODISR = HRTIM_ODISR_TC1ODIS | HRTIM_ODISR_TC2ODIS;
s_period_ticks = hrtim_period_from_clock();
HRTIM1->sCommonRegs.ICR = HRTIM_ICR_DLLRDYC;
HRTIM1->sCommonRegs.DLLCR = HRTIM_DLLCR_CALEN |
HRTIM_DLLCR_CALRTE_1 |
HRTIM_DLLCR_CAL;
/* In rescue clock mode DLLRDY may never rise; keep diagnostics alive. */
if (!hrtim1_wait_dll_ready())
{
HRTIM1->sCommonRegs.ODISR = HRTIM_ODISR_TC1ODIS | HRTIM_ODISR_TC2ODIS;
HRTIM1->sMasterRegs.MCR &= ~HRTIM_MCR_TCCEN;
return false;
}
timer->TIMxCR = HRTIM_TIMCR_CONT;
timer->PERxR = s_period_ticks;
timer->REPxR = 0U;
timer->CMP1xR = DCDC_MIN_DUTY_TICKS;
timer->CMP2xR = hrtim_max_duty_ticks();
timer->CMP3xR = 1000U;
timer->DTxR =
((DCDC_DEADTIME_RISING_TICKS & 0x1FFUL) << HRTIM_DTR_DTR_Pos) |
((DCDC_DEADTIME_FALLING_TICKS & 0x1FFUL) << HRTIM_DTR_DTF_Pos) |
(HRTIM_DTR_DTPRSC_1 | HRTIM_DTR_DTPRSC_0);
/*
* Complementary buck PWM:
* CHC1 goes active at period event and inactive at CMP1.
* CHC2 goes active at CMP1 and inactive at period event.
* HRTIM dead-time block delays transitions to avoid shoot-through.
*/
timer->SETx1R = HRTIM_SET1R_PER;
timer->RSTx1R = HRTIM_RST1R_CMP1;
timer->SETx2R = HRTIM_SET2R_CMP1;
timer->RSTx2R = HRTIM_RST2R_PER;
timer->OUTxR = HRTIM_OUTR_DTEN | HRTIM_OUTR_FAULT1_1 | HRTIM_OUTR_FAULT2_1;
/* Trigger point for future synchronized ADC sampling. */
HRTIM1->sCommonRegs.ADC1R = HRTIM_ADC1R_AD1TCC3;
HRTIM1->sMasterRegs.MCR &= ~HRTIM_MCR_TCCEN;
return true;
}
static bool hrtim1_wait_dll_ready(void)
{
uint32_t timeout = HRTIM_DLL_READY_TIMEOUT;
while (((HRTIM1->sCommonRegs.ISR & HRTIM_ISR_DLLRDY) == 0U) && (timeout > 0U))
{
timeout--;
}
return timeout > 0U;
}
static void hrtim1_outputs_enable(bool enable)
{
if (enable)
{
HRTIM1->sCommonRegs.OENR = HRTIM_OENR_TC1OEN | HRTIM_OENR_TC2OEN;
}
else
{
HRTIM1->sCommonRegs.ODISR = HRTIM_ODISR_TC1ODIS | HRTIM_ODISR_TC2ODIS;
}
}
static void hrtim1_set_duty(uint32_t duty_ticks)
{
if (duty_ticks < DCDC_MIN_DUTY_TICKS)
{
duty_ticks = DCDC_MIN_DUTY_TICKS;
}
else if (duty_ticks > hrtim_max_duty_ticks())
{
duty_ticks = hrtim_max_duty_ticks();
}
HRTIM1->sTimerxRegs[HRTIM_TIMER_C_INDEX].CMP1xR = duty_ticks;
s_duty_ticks = duty_ticks;
}
static uint32_t hrtim_period_from_clock(void)
{
uint64_t ticks = (((uint64_t)SystemCoreClock * 32ULL) + (DCDC_PWM_FREQUENCY_HZ / 2ULL)) /
DCDC_PWM_FREQUENCY_HZ;
if (ticks < 100ULL)
{
ticks = 100ULL;
}
else if (ticks > 0xFFFFULL)
{
ticks = 0xFFFFULL;
}
return (uint32_t)ticks;
}
static uint32_t hrtim_max_duty_ticks(void)
{
uint32_t max_ticks = DCDC_MAX_DUTY_TICKS;
if (s_period_ticks > 10U && max_ticks >= (s_period_ticks - 1U))
{
max_ticks = s_period_ticks - 1U;
}
return max_ticks;
}
static uint32_t adc_raw_to_mv(uint16_t raw)
{
return ((uint32_t)raw * ADC_REFERENCE_MV) / ADC_FULL_SCALE_COUNTS;
}
static uint32_t sense_mv_to_voltage_mv(uint32_t sense_mv, uint32_t scale_ppm)
{
return (uint32_t)(((uint64_t)sense_mv * 1000000ULL) / scale_ppm);
}
static uint32_t sense_mv_to_current_ma(uint32_t sense_mv)
{
return (sense_mv * 1000UL) / DCDC_IIN_UV_PER_MA;
}
static void set_usbpd_input_switch(bool enable)
{
if (enable)
{
GPIOC->BSRR = (1UL << 3U);
}
else
{
GPIOC->BRR = (1UL << 3U);
}
}
static void set_loads_off(void)
{
GPIOC->BRR = (1UL << 14U) | (1UL << 15U);
}
static void latch_fault(DCDC_Fault fault)
{
hrtim1_outputs_enable(false);
hrtim1_set_duty(DCDC_MIN_DUTY_TICKS);
HRTIM1->sMasterRegs.MCR &= ~HRTIM_MCR_TCCEN;
set_usbpd_input_switch(false);
s_fault = fault;
s_state = DCDC_STATE_FAULT;
}

View File

@@ -0,0 +1,70 @@
#include <stdio.h>
#include "app_config.h"
#include "board.h"
#include "dcdc.h"
#include "retarget.h"
#include "stm32g474xx.h"
int main(void)
{
uint32_t last_control_ms = 0U;
uint32_t last_print_ms = 0U;
volatile uint32_t debug_attach_delay;
/*
* Short rescue window after reset. It lets ST-LINK attach before the
* firmware reconfigures clocks and power peripherals.
*/
for (debug_attach_delay = 0U; debug_attach_delay < DEBUG_ATTACH_DELAY_LOOPS; debug_attach_delay++)
{
__NOP();
}
Board_Init();
Retarget_Init(USART3_BAUDRATE);
DCDC_Init();
printf("\nB-G474E-DPOW1 DCDC starter project\n");
printf("USART3 retarget printf: %lu baud\n", (unsigned long)USART3_BAUDRATE);
printf("SystemCoreClock: %lu Hz\n", (unsigned long)SystemCoreClock);
printf("PWM: %lu Hz, period=%lu HRTIM ticks\n",
(unsigned long)DCDC_PWM_FREQUENCY_HZ,
(unsigned long)DCDC_GetPeriodTicks());
printf("HRTIM DLL: %s\n", DCDC_IsHrtimReady() ? "ready" : "not ready");
#if DCDC_POWER_STAGE_ENABLE
printf("Power stage: ENABLED. Check VIN/current limit before flashing.\n");
DCDC_Start();
#else
printf("Power stage: disabled in app_config.h; ADC monitor only.\n");
#endif
while (1)
{
uint32_t now = Board_Millis();
if ((now - last_control_ms) >= 1U)
{
last_control_ms = now;
DCDC_ControlStep();
}
if ((now - last_print_ms) >= 500U)
{
DCDC_Measurements m;
last_print_ms = now;
DCDC_ReadMeasurements(&m);
printf("state=%s fault=%s hrtim=%s vin=%lumV iin=%lumA vout=%lumV duty=%lu/%lu\n",
DCDC_StateText(DCDC_GetState()),
DCDC_FaultText(DCDC_GetFault()),
DCDC_IsHrtimReady() ? "ready" : "not-ready",
(unsigned long)m.vin_mv,
(unsigned long)m.iin_ma,
(unsigned long)m.vout_mv,
(unsigned long)DCDC_GetDutyTicks(),
(unsigned long)DCDC_GetPeriodTicks());
}
}
}

View File

@@ -0,0 +1,101 @@
#include "retarget.h"
#include <stdio.h>
#include "app_config.h"
#include "stm32g474xx.h"
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 6000000)
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
#elif defined(__ARMCC_VERSION)
__asm(".global __use_no_semihosting\n");
#endif
FILE __stdout;
FILE __stdin;
static void gpio_pc10_pc11_to_usart3(void)
{
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN;
(void)RCC->AHB2ENR;
/* PC10 = USART3_TX, PC11 = USART3_RX, AF7: ST-LINK VCP on CN3. */
GPIOC->MODER &= ~((3UL << (10U * 2U)) | (3UL << (11U * 2U)));
GPIOC->MODER |= ((2UL << (10U * 2U)) | (2UL << (11U * 2U)));
GPIOC->OTYPER &= ~((1UL << 10U) | (1UL << 11U));
GPIOC->OSPEEDR |= ((3UL << (10U * 2U)) | (3UL << (11U * 2U)));
GPIOC->PUPDR &= ~((3UL << (10U * 2U)) | (3UL << (11U * 2U)));
GPIOC->AFR[1] &= ~((0xFUL << ((10U - 8U) * 4U)) | (0xFUL << ((11U - 8U) * 4U)));
GPIOC->AFR[1] |= ((7UL << ((10U - 8U) * 4U)) | (7UL << ((11U - 8U) * 4U)));
}
void Retarget_Init(uint32_t baudrate)
{
gpio_pc10_pc11_to_usart3();
RCC->APB1ENR1 |= RCC_APB1ENR1_USART3EN;
(void)RCC->APB1ENR1;
USART3->CR1 = 0U;
USART3->CR2 = 0U;
USART3->CR3 = 0U;
/* APB1 is not prescaled; use the clock that really started. */
USART3->BRR = (SystemCoreClock + (baudrate / 2U)) / baudrate;
USART3->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}
void Retarget_PutChar(char ch)
{
while ((USART3->ISR & USART_ISR_TXE_TXFNF) == 0U)
{
__NOP();
}
USART3->TDR = (uint8_t)ch;
}
int fputc(int ch, FILE *f)
{
(void)f;
if (ch == '\n')
{
Retarget_PutChar('\r');
}
Retarget_PutChar((char)ch);
return ch;
}
int fgetc(FILE *f)
{
(void)f;
while ((USART3->ISR & USART_ISR_RXNE_RXFNE) == 0U)
{
__NOP();
}
return (int)(USART3->RDR & 0xFFU);
}
void _ttywrch(int ch)
{
Retarget_PutChar((char)ch);
}
void _sys_exit(int return_code)
{
(void)return_code;
while (1)
{
__NOP();
}
}

View File

@@ -0,0 +1,56 @@
#include "stm32g474xx.h"
#include "app_config.h"
uint32_t SystemCoreClock = 16000000UL;
const uint8_t AHBPrescTable[16] =
{
0, 0, 0, 0, 0, 0, 0, 0,
1, 2, 3, 4, 6, 7, 8, 9
};
const uint8_t APBPrescTable[8] =
{
0, 0, 0, 0, 1, 2, 3, 4
};
void SystemInit(void)
{
/* Enable FPU access before C runtime starts. */
SCB->CPACR |= (0xFU << 20);
#if defined(VECT_TAB_SRAM)
SCB->VTOR = SRAM_BASE;
#else
SCB->VTOR = FLASH_BASE;
#endif
}
void SystemCoreClockUpdate(void)
{
uint32_t sysclk = 16000000UL;
uint32_t sws = RCC->CFGR & RCC_CFGR_SWS;
if (sws == RCC_CFGR_SWS_PLL)
{
uint32_t pllsrc_hz = 16000000UL;
uint32_t pllcfgr = RCC->PLLCFGR;
uint32_t pllm = ((pllcfgr & RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1U;
uint32_t plln = ((pllcfgr & RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos);
uint32_t pllr_bits = ((pllcfgr & RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos);
uint32_t pllr = (pllr_bits + 1U) * 2U;
if ((pllcfgr & RCC_PLLCFGR_PLLSRC) == RCC_PLLCFGR_PLLSRC_HSE)
{
pllsrc_hz = 8000000UL;
}
sysclk = (pllsrc_hz / pllm) * plln / pllr;
}
else if (sws == RCC_CFGR_SWS_HSE)
{
sysclk = 8000000UL;
}
SystemCoreClock = sysclk >> AHBPrescTable[(RCC->CFGR & RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos];
}