From 4c550c61ebc99122f4da83f5ecf3a1c69caf785e Mon Sep 17 00:00:00 2001 From: Kasion <540853397@qq.com> Date: Tue, 21 Apr 2026 00:44:14 +0800 Subject: [PATCH 1/2] feat: Refactor panda to use source + header file structure for driver modules --- board/drivers/bootkick.c | 68 +++++++++++++++++++++++++++ board/drivers/bootkick.h | 69 ++------------------------- board/drivers/clock_source.c | 66 ++++++++++++++++++++++++++ board/drivers/clock_source.h | 68 ++------------------------- board/drivers/fan.c | 48 +++++++++++++++++++ board/drivers/fan.h | 59 ++++++------------------ board/drivers/led.c | 27 +++++++++++ board/drivers/led.h | 30 ++---------- board/drivers/pwm.c | 58 +++++++++++++++++++++++ board/drivers/pwm.h | 56 ++-------------------- board/drivers/registers.c | 77 +++++++++++++++++++++++++++++++ board/drivers/registers.h | 82 +++------------------------------ board/drivers/simple_watchdog.c | 21 +++++++++ board/drivers/simple_watchdog.h | 27 ++++------- 14 files changed, 412 insertions(+), 344 deletions(-) create mode 100644 board/drivers/bootkick.c create mode 100644 board/drivers/clock_source.c create mode 100644 board/drivers/fan.c create mode 100644 board/drivers/led.c create mode 100644 board/drivers/pwm.c create mode 100644 board/drivers/registers.c create mode 100644 board/drivers/simple_watchdog.c diff --git a/board/drivers/bootkick.c b/board/drivers/bootkick.c new file mode 100644 index 00000000000..102b6e4da96 --- /dev/null +++ b/board/drivers/bootkick.c @@ -0,0 +1,68 @@ +#include "board/drivers/bootkick.h" + +bool bootkick_reset_triggered = false; + +void bootkick_tick(bool ignition, bool recent_heartbeat) { + static uint16_t bootkick_last_serial_ptr = 0; + static uint8_t waiting_to_boot_countdown = 0; + static uint8_t boot_reset_countdown = 0; + static uint8_t bootkick_harness_status_prev = HARNESS_STATUS_NC; + static bool bootkick_ign_prev = false; + static BootState boot_state = BOOT_BOOTKICK; + BootState boot_state_prev = boot_state; + const bool harness_inserted = (harness.status != bootkick_harness_status_prev) && (harness.status != HARNESS_STATUS_NC); + + if ((ignition && !bootkick_ign_prev) || harness_inserted) { + // bootkick on rising edge of ignition or harness insertion + boot_state = BOOT_BOOTKICK; + } else if (recent_heartbeat) { + // disable bootkick once openpilot is up + boot_state = BOOT_STANDBY; + } else { + + } + + /* + Ensure SOM boots in case it goes into QDL mode. Reset behavior: + * shouldn't trigger on the first boot after power-on + * only try reset once per bootkick, i.e. don't keep trying until booted + * only try once per panda boot, since openpilot will reset panda on startup + * once BOOT_RESET is triggered, it stays until countdown is finished + */ + if (!bootkick_reset_triggered && (boot_state == BOOT_BOOTKICK) && (boot_state_prev == BOOT_STANDBY)) { + waiting_to_boot_countdown = 20U; + } + if (waiting_to_boot_countdown > 0U) { + bool serial_activity = uart_ring_som_debug.w_ptr_tx != bootkick_last_serial_ptr; + if (serial_activity || current_board->read_som_gpio() || (boot_state != BOOT_BOOTKICK)) { + waiting_to_boot_countdown = 0U; + } else { + // try a reset + if (waiting_to_boot_countdown == 1U) { + boot_reset_countdown = 5U; + } + } + } + + // handle reset state + if (boot_reset_countdown > 0U) { + boot_state = BOOT_RESET; + bootkick_reset_triggered = true; + } else { + if (boot_state == BOOT_RESET) { + boot_state = BOOT_BOOTKICK; + } + } + + // update state + bootkick_ign_prev = ignition; + bootkick_harness_status_prev = harness.status; + bootkick_last_serial_ptr = uart_ring_som_debug.w_ptr_tx; + if (waiting_to_boot_countdown > 0U) { + waiting_to_boot_countdown--; + } + if (boot_reset_countdown > 0U) { + boot_reset_countdown--; + } + current_board->set_bootkick(boot_state); +} diff --git a/board/drivers/bootkick.h b/board/drivers/bootkick.h index 4bf990391b6..7a386659e46 100644 --- a/board/drivers/bootkick.h +++ b/board/drivers/bootkick.h @@ -1,68 +1,7 @@ -#include "board/drivers/drivers.h" - -bool bootkick_reset_triggered = false; - -void bootkick_tick(bool ignition, bool recent_heartbeat) { - static uint16_t bootkick_last_serial_ptr = 0; - static uint8_t waiting_to_boot_countdown = 0; - static uint8_t boot_reset_countdown = 0; - static uint8_t bootkick_harness_status_prev = HARNESS_STATUS_NC; - static bool bootkick_ign_prev = false; - static BootState boot_state = BOOT_BOOTKICK; - BootState boot_state_prev = boot_state; - const bool harness_inserted = (harness.status != bootkick_harness_status_prev) && (harness.status != HARNESS_STATUS_NC); +#pragma once - if ((ignition && !bootkick_ign_prev) || harness_inserted) { - // bootkick on rising edge of ignition or harness insertion - boot_state = BOOT_BOOTKICK; - } else if (recent_heartbeat) { - // disable bootkick once openpilot is up - boot_state = BOOT_STANDBY; - } else { - - } - - /* - Ensure SOM boots in case it goes into QDL mode. Reset behavior: - * shouldn't trigger on the first boot after power-on - * only try reset once per bootkick, i.e. don't keep trying until booted - * only try once per panda boot, since openpilot will reset panda on startup - * once BOOT_RESET is triggered, it stays until countdown is finished - */ - if (!bootkick_reset_triggered && (boot_state == BOOT_BOOTKICK) && (boot_state_prev == BOOT_STANDBY)) { - waiting_to_boot_countdown = 20U; - } - if (waiting_to_boot_countdown > 0U) { - bool serial_activity = uart_ring_som_debug.w_ptr_tx != bootkick_last_serial_ptr; - if (serial_activity || current_board->read_som_gpio() || (boot_state != BOOT_BOOTKICK)) { - waiting_to_boot_countdown = 0U; - } else { - // try a reset - if (waiting_to_boot_countdown == 1U) { - boot_reset_countdown = 5U; - } - } - } +#include "board/drivers/drivers.h" - // handle reset state - if (boot_reset_countdown > 0U) { - boot_state = BOOT_RESET; - bootkick_reset_triggered = true; - } else { - if (boot_state == BOOT_RESET) { - boot_state = BOOT_BOOTKICK; - } - } +extern bool bootkick_reset_triggered; - // update state - bootkick_ign_prev = ignition; - bootkick_harness_status_prev = harness.status; - bootkick_last_serial_ptr = uart_ring_som_debug.w_ptr_tx; - if (waiting_to_boot_countdown > 0U) { - waiting_to_boot_countdown--; - } - if (boot_reset_countdown > 0U) { - boot_reset_countdown--; - } - current_board->set_bootkick(boot_state); -} +void bootkick_tick(bool ignition, bool recent_heartbeat); diff --git a/board/drivers/clock_source.c b/board/drivers/clock_source.c new file mode 100644 index 00000000000..f1e18bbf8a6 --- /dev/null +++ b/board/drivers/clock_source.c @@ -0,0 +1,66 @@ +#include "board/drivers/clock_source.h" + +#define CLOCK_SOURCE_PERIOD_MS 50U +#define CLOCK_SOURCE_PULSE_LEN_MS 2U + +void clock_source_set_timer_params(uint16_t param1, uint16_t param2) { + // Pulse length of each channel + register_set(&(TIM1->CCR1), (((param1 & 0xFF00U) >> 8U)*10U), 0xFFFFU); + register_set(&(TIM1->CCR2), ((param1 & 0x00FFU)*10U), 0xFFFFU); + register_set(&(TIM8->CCR3), (((param2 & 0xFF00U) >> 8U)*10U), 0xFFFFU); + // Timer period + register_set(&(TIM1->ARR), (((param2 & 0x00FFU)*10U) - 1U), 0xFFFFU); + register_set(&(TIM1->CCR4), ((TIM1->ARR + 1U) / 2U), 0xFFFFU); +} + +void clock_source_init(bool enable_channel1) { + // Setup timer + register_set(&(TIM1->PSC), ((APB2_TIMER_FREQ*100U)-1U), 0xFFFFU); // Tick on 0.1 ms + register_set(&(TIM1->ARR), ((CLOCK_SOURCE_PERIOD_MS*10U) - 1U), 0xFFFFU); // Period + register_set(&(TIM1->CCMR1), 0U, 0xFFFFU); // No output on compare + register_set(&(TIM1->CCER), TIM_CCER_CC1E | TIM_CCER_CC2NE, 0xFFFFU); // Enable compares + register_set(&(TIM1->CCR1), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 1 value + register_set(&(TIM1->CCR2), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 2 value + register_set(&(TIM1->CCR4), (CLOCK_SOURCE_PERIOD_MS*5U), 0xFFFFU); // For slave timer + register_set_bits(&(TIM1->DIER), TIM_DIER_UIE | TIM_DIER_CC1IE); // Enable interrupts + + + // No interrupts + NVIC_DisableIRQ(TIM1_UP_TIM10_IRQn); + NVIC_DisableIRQ(TIM1_CC_IRQn); + + // Set GPIO as timer channels + if (enable_channel1) { + set_gpio_alternate(GPIOA, 8, GPIO_AF1_TIM1); + } + set_gpio_alternate(GPIOB, 14, GPIO_AF1_TIM1); + + // Set PWM mode + register_set(&(TIM1->CCMR1), (0b110UL << TIM_CCMR1_OC1M_Pos) | (0b110UL << TIM_CCMR1_OC2M_Pos), 0xFFFFU); + register_set(&(TIM1->CCMR2), (0b110UL << TIM_CCMR2_OC3M_Pos) | (0b111UL << TIM_CCMR2_OC4M_Pos), 0xFFFFU); + + // Enable output + register_set(&(TIM1->BDTR), TIM_BDTR_MOE, 0xFFFFU); + + // Sync with slave + register_set(&(TIM1->SMCR), TIM_SMCR_MSM , 0xFFFFU); + register_set(&(TIM1->CR2), (0b0111U << TIM_CR2_MMS_Pos), 0xFFFFU); + register_set(&(TIM8->SMCR), (0b0100U << TIM_SMCR_SMS_Pos) | (0b000U << TIM_SMCR_TS_Pos), 0xFFFFU); + + // Setup slave timer (TIM8) + register_set(&(TIM8->PSC), TIM1->PSC, 0xFFFFU); + register_set(&(TIM8->ARR), TIM1->ARR, 0xFFFFU); + register_set(&(TIM8->CCMR2), (0b110UL << TIM_CCMR2_OC3M_Pos), 0xFFFFU); + register_set(&(TIM8->CCR3), (CLOCK_SOURCE_PULSE_LEN_MS * 10U), 0xFFFFU); + register_set(&(TIM8->CCER), TIM_CCER_CC3NE, 0xFFFFU); + + // MOE for TIM8 as well + register_set(&(TIM8->BDTR), TIM_BDTR_MOE, 0xFFFFU); + + // Set GPIO + set_gpio_alternate(GPIOB, 15, GPIO_AF3_TIM8); + + // Enable timers + register_set(&(TIM1->CR1), TIM_CR1_CEN, 0x3FU); + register_set(&(TIM8->CR1), TIM_CR1_CEN, 0x3FU); +} \ No newline at end of file diff --git a/board/drivers/clock_source.h b/board/drivers/clock_source.h index bd05d414596..c67a69d6f08 100644 --- a/board/drivers/clock_source.h +++ b/board/drivers/clock_source.h @@ -1,66 +1,6 @@ -#include "board/drivers/drivers.h" - -#define CLOCK_SOURCE_PERIOD_MS 50U -#define CLOCK_SOURCE_PULSE_LEN_MS 2U - -void clock_source_set_timer_params(uint16_t param1, uint16_t param2) { - // Pulse length of each channel - register_set(&(TIM1->CCR1), (((param1 & 0xFF00U) >> 8U)*10U), 0xFFFFU); - register_set(&(TIM1->CCR2), ((param1 & 0x00FFU)*10U), 0xFFFFU); - register_set(&(TIM8->CCR3), (((param2 & 0xFF00U) >> 8U)*10U), 0xFFFFU); - // Timer period - register_set(&(TIM1->ARR), (((param2 & 0x00FFU)*10U) - 1U), 0xFFFFU); - register_set(&(TIM1->CCR4), ((TIM1->ARR + 1U) / 2U), 0xFFFFU); -} - -void clock_source_init(bool enable_channel1) { - // Setup timer - register_set(&(TIM1->PSC), ((APB2_TIMER_FREQ*100U)-1U), 0xFFFFU); // Tick on 0.1 ms - register_set(&(TIM1->ARR), ((CLOCK_SOURCE_PERIOD_MS*10U) - 1U), 0xFFFFU); // Period - register_set(&(TIM1->CCMR1), 0U, 0xFFFFU); // No output on compare - register_set(&(TIM1->CCER), TIM_CCER_CC1E | TIM_CCER_CC2NE, 0xFFFFU); // Enable compares - register_set(&(TIM1->CCR1), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 1 value - register_set(&(TIM1->CCR2), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 2 value - register_set(&(TIM1->CCR4), (CLOCK_SOURCE_PERIOD_MS*5U), 0xFFFFU); // For slave timer - register_set_bits(&(TIM1->DIER), TIM_DIER_UIE | TIM_DIER_CC1IE); // Enable interrupts - - - // No interrupts - NVIC_DisableIRQ(TIM1_UP_TIM10_IRQn); - NVIC_DisableIRQ(TIM1_CC_IRQn); - - // Set GPIO as timer channels - if (enable_channel1) { - set_gpio_alternate(GPIOA, 8, GPIO_AF1_TIM1); - } - set_gpio_alternate(GPIOB, 14, GPIO_AF1_TIM1); +#pragma once - // Set PWM mode - register_set(&(TIM1->CCMR1), (0b110UL << TIM_CCMR1_OC1M_Pos) | (0b110UL << TIM_CCMR1_OC2M_Pos), 0xFFFFU); - register_set(&(TIM1->CCMR2), (0b110UL << TIM_CCMR2_OC3M_Pos) | (0b111UL << TIM_CCMR2_OC4M_Pos), 0xFFFFU); - - // Enable output - register_set(&(TIM1->BDTR), TIM_BDTR_MOE, 0xFFFFU); - - // Sync with slave - register_set(&(TIM1->SMCR), TIM_SMCR_MSM , 0xFFFFU); - register_set(&(TIM1->CR2), (0b0111U << TIM_CR2_MMS_Pos), 0xFFFFU); - register_set(&(TIM8->SMCR), (0b0100U << TIM_SMCR_SMS_Pos) | (0b000U << TIM_SMCR_TS_Pos), 0xFFFFU); - - // Setup slave timer (TIM8) - register_set(&(TIM8->PSC), TIM1->PSC, 0xFFFFU); - register_set(&(TIM8->ARR), TIM1->ARR, 0xFFFFU); - register_set(&(TIM8->CCMR2), (0b110UL << TIM_CCMR2_OC3M_Pos), 0xFFFFU); - register_set(&(TIM8->CCR3), (CLOCK_SOURCE_PULSE_LEN_MS * 10U), 0xFFFFU); - register_set(&(TIM8->CCER), TIM_CCER_CC3NE, 0xFFFFU); - - // MOE for TIM8 as well - register_set(&(TIM8->BDTR), TIM_BDTR_MOE, 0xFFFFU); - - // Set GPIO - set_gpio_alternate(GPIOB, 15, GPIO_AF3_TIM8); +#include "board/drivers/drivers.h" - // Enable timers - register_set(&(TIM1->CR1), TIM_CR1_CEN, 0x3FU); - register_set(&(TIM8->CR1), TIM_CR1_CEN, 0x3FU); -} +void clock_source_set_timer_params(uint16_t param1, uint16_t param2); +void clock_source_init(bool enable_channel1); \ No newline at end of file diff --git a/board/drivers/fan.c b/board/drivers/fan.c new file mode 100644 index 00000000000..eec4969be19 --- /dev/null +++ b/board/drivers/fan.c @@ -0,0 +1,48 @@ +#include "board/drivers/fan.h" + +struct fan_state_t fan_state; + +static const uint8_t FAN_TICK_FREQ = 8U; + +void fan_set_power(uint8_t percentage) { + if (percentage > 0U) { + fan_state.power = CLAMP(percentage, 20U, 100U); + } else { + fan_state.power = 0U; + } +} + +void fan_init(void) { + fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; + llfan_init(); +} + +// Call this at FAN_TICK_FREQ +void fan_tick(void) { + if (current_board->has_fan) { + // Measure fan RPM + uint16_t fan_rpm_fast = fan_state.tach_counter * (60U * FAN_TICK_FREQ / 4U); // 4 interrupts per rotation + fan_state.tach_counter = 0U; + fan_state.rpm = (fan_rpm_fast + (3U * fan_state.rpm)) / 4U; + + #ifdef DEBUG_FAN + puth(fan_state.target_rpm); + print(" "); puth(fan_rpm_fast); + print(" "); puth(fan_state.power); + print("\n"); + #endif + + // Cooldown counter to prevent noise on tachometer line. + if (fan_state.power > 0U) { + fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; + } else { + if (fan_state.cooldown_counter > 0U) { + fan_state.cooldown_counter--; + } + } + + // Set PWM and enable line + pwm_set(TIM3, 3, fan_state.power); + current_board->set_fan_enabled((fan_state.power > 0U) || (fan_state.cooldown_counter > 0U)); + } +} \ No newline at end of file diff --git a/board/drivers/fan.h b/board/drivers/fan.h index 389fdd3f409..a73f9c99d97 100644 --- a/board/drivers/fan.h +++ b/board/drivers/fan.h @@ -1,48 +1,17 @@ -#include "board/drivers/drivers.h" - -struct fan_state_t fan_state; - -static const uint8_t FAN_TICK_FREQ = 8U; - -void fan_set_power(uint8_t percentage) { - if (percentage > 0U) { - fan_state.power = CLAMP(percentage, 20U, 100U); - } else { - fan_state.power = 0U; - } -} +#pragma once -void fan_init(void) { - fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; - llfan_init(); -} +#include "board/drivers/drivers.h" +struct fan_state_t { + uint16_t tach_counter; + uint16_t rpm; + uint8_t power; + float error_integral; + uint8_t cooldown_counter; +}; +extern struct fan_state_t fan_state; + +void fan_set_power(uint8_t percentage); +void fan_init(void); // Call this at FAN_TICK_FREQ -void fan_tick(void) { - if (current_board->has_fan) { - // Measure fan RPM - uint16_t fan_rpm_fast = fan_state.tach_counter * (60U * FAN_TICK_FREQ / 4U); // 4 interrupts per rotation - fan_state.tach_counter = 0U; - fan_state.rpm = (fan_rpm_fast + (3U * fan_state.rpm)) / 4U; - - #ifdef DEBUG_FAN - puth(fan_state.target_rpm); - print(" "); puth(fan_rpm_fast); - print(" "); puth(fan_state.power); - print("\n"); - #endif - - // Cooldown counter to prevent noise on tachometer line. - if (fan_state.power > 0U) { - fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; - } else { - if (fan_state.cooldown_counter > 0U) { - fan_state.cooldown_counter--; - } - } - - // Set PWM and enable line - pwm_set(TIM3, 3, fan_state.power); - current_board->set_fan_enabled((fan_state.power > 0U) || (fan_state.cooldown_counter > 0U)); - } -} +void fan_tick(void); \ No newline at end of file diff --git a/board/drivers/led.c b/board/drivers/led.c new file mode 100644 index 00000000000..8cb51dc33d6 --- /dev/null +++ b/board/drivers/led.c @@ -0,0 +1,27 @@ +#include "board/drivers/led.h" + +void led_set(uint8_t color, bool enabled) { + if (color < 3U) { + if (current_board->led_pwm_channels[color] != 0U) { + pwm_set(TIM3, current_board->led_pwm_channels[color], 100U - (enabled ? LED_PWM_POWER : 0U)); + } else { + set_gpio_output(current_board->led_GPIO[color], current_board->led_pin[color], !enabled); + } + } +} + +void led_init(void) { + for (uint8_t i = 0U; i<3U; i++){ + set_gpio_pullup(current_board->led_GPIO[i], current_board->led_pin[i], PULL_NONE); + set_gpio_output_type(current_board->led_GPIO[i], current_board->led_pin[i], OUTPUT_TYPE_OPEN_DRAIN); + + if (current_board->led_pwm_channels[i] != 0U) { + set_gpio_alternate(current_board->led_GPIO[i], current_board->led_pin[i], GPIO_AF2_TIM3); + pwm_init(TIM3, current_board->led_pwm_channels[i]); + } else { + set_gpio_mode(current_board->led_GPIO[i], current_board->led_pin[i], MODE_OUTPUT); + } + + led_set(i, false); + } +} diff --git a/board/drivers/led.h b/board/drivers/led.h index 7cea94e854f..67192bc5f4a 100644 --- a/board/drivers/led.h +++ b/board/drivers/led.h @@ -1,3 +1,6 @@ +#pragma once + +#include "board/drivers/drivers.h" #define LED_RED 0U #define LED_GREEN 1U @@ -5,28 +8,5 @@ #define LED_PWM_POWER 2U -void led_set(uint8_t color, bool enabled) { - if (color < 3U) { - if (current_board->led_pwm_channels[color] != 0U) { - pwm_set(TIM3, current_board->led_pwm_channels[color], 100U - (enabled ? LED_PWM_POWER : 0U)); - } else { - set_gpio_output(current_board->led_GPIO[color], current_board->led_pin[color], !enabled); - } - } -} - -void led_init(void) { - for (uint8_t i = 0U; i<3U; i++){ - set_gpio_pullup(current_board->led_GPIO[i], current_board->led_pin[i], PULL_NONE); - set_gpio_output_type(current_board->led_GPIO[i], current_board->led_pin[i], OUTPUT_TYPE_OPEN_DRAIN); - - if (current_board->led_pwm_channels[i] != 0U) { - set_gpio_alternate(current_board->led_GPIO[i], current_board->led_pin[i], GPIO_AF2_TIM3); - pwm_init(TIM3, current_board->led_pwm_channels[i]); - } else { - set_gpio_mode(current_board->led_GPIO[i], current_board->led_pin[i], MODE_OUTPUT); - } - - led_set(i, false); - } -} +void led_set(uint8_t color, bool enabled); +void led_init(void); diff --git a/board/drivers/pwm.c b/board/drivers/pwm.c new file mode 100644 index 00000000000..5faca9752ff --- /dev/null +++ b/board/drivers/pwm.c @@ -0,0 +1,58 @@ +#include "board/drivers/pwm.h" + +#define PWM_COUNTER_OVERFLOW 4800U // To get ~25kHz + +// TODO: Implement for 32-bit timers + +void pwm_init(TIM_TypeDef *TIM, uint8_t channel){ + // Enable timer and auto-reload + register_set(&(TIM->CR1), TIM_CR1_CEN | TIM_CR1_ARPE, 0x3FU); + + // Set channel as PWM mode 1 and enable output + switch(channel){ + case 1U: + register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC1E); + break; + case 2U: + register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC1PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC2E); + break; + case 3U: + register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC1PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC3E); + break; + case 4U: + register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC1PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC4E); + break; + default: + break; + } + + // Set max counter value + register_set(&(TIM->ARR), PWM_COUNTER_OVERFLOW, 0xFFFFU); + + // Update registers and clear counter + TIM->EGR |= TIM_EGR_UG; +} + +void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage){ + uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U); + switch(channel){ + case 1U: + register_set(&(TIM->CCR1), comp_value, 0xFFFFU); + break; + case 2U: + register_set(&(TIM->CCR2), comp_value, 0xFFFFU); + break; + case 3U: + register_set(&(TIM->CCR3), comp_value, 0xFFFFU); + break; + case 4U: + register_set(&(TIM->CCR4), comp_value, 0xFFFFU); + break; + default: + break; + } +} \ No newline at end of file diff --git a/board/drivers/pwm.h b/board/drivers/pwm.h index 3d0d73efdc6..27be0bbf43f 100644 --- a/board/drivers/pwm.h +++ b/board/drivers/pwm.h @@ -1,56 +1,8 @@ +#pragma once + #define PWM_COUNTER_OVERFLOW 4800U // To get ~25kHz // TODO: Implement for 32-bit timers -void pwm_init(TIM_TypeDef *TIM, uint8_t channel){ - // Enable timer and auto-reload - register_set(&(TIM->CR1), TIM_CR1_CEN | TIM_CR1_ARPE, 0x3FU); - - // Set channel as PWM mode 1 and enable output - switch(channel){ - case 1U: - register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE)); - register_set_bits(&(TIM->CCER), TIM_CCER_CC1E); - break; - case 2U: - register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE)); - register_set_bits(&(TIM->CCER), TIM_CCER_CC2E); - break; - case 3U: - register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE)); - register_set_bits(&(TIM->CCER), TIM_CCER_CC3E); - break; - case 4U: - register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE)); - register_set_bits(&(TIM->CCER), TIM_CCER_CC4E); - break; - default: - break; - } - - // Set max counter value - register_set(&(TIM->ARR), PWM_COUNTER_OVERFLOW, 0xFFFFU); - - // Update registers and clear counter - TIM->EGR |= TIM_EGR_UG; -} - -void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage){ - uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U); - switch(channel){ - case 1U: - register_set(&(TIM->CCR1), comp_value, 0xFFFFU); - break; - case 2U: - register_set(&(TIM->CCR2), comp_value, 0xFFFFU); - break; - case 3U: - register_set(&(TIM->CCR3), comp_value, 0xFFFFU); - break; - case 4U: - register_set(&(TIM->CCR4), comp_value, 0xFFFFU); - break; - default: - break; - } -} +void pwm_init(TIM_TypeDef *TIM, uint8_t channel); +void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage); \ No newline at end of file diff --git a/board/drivers/registers.c b/board/drivers/registers.c new file mode 100644 index 00000000000..08170d0c3ab --- /dev/null +++ b/board/drivers/registers.c @@ -0,0 +1,77 @@ +#include "board/drivers/registers.h" + +typedef struct reg { + volatile uint32_t *address; + uint32_t value; + uint32_t check_mask; + bool logged_fault; +} reg; + +#define CHECK_COLLISION(hash, addr) (((uint32_t) register_map[hash].address != 0U) && (register_map[hash].address != (addr))) + +static reg register_map[REGISTER_MAP_SIZE]; + +// Hash spread in first and second iterations seems to be reasonable. +// See: tests/development/register_hashmap_spread.py +// Also, check the collision warnings in the debug output, and minimize those. +static uint16_t hash_addr(uint32_t input){ + return (((input >> 16U) ^ ((((input + 1U) & 0xFFFFU) * HASHING_PRIME) & 0xFFFFU)) & REGISTER_MAP_SIZE); +} + +// Do not put bits in the check mask that get changed by the hardware +void register_set(volatile uint32_t *addr, uint32_t val, uint32_t mask){ + ENTER_CRITICAL() + // Set bits in register that are also in the mask + (*addr) = ((*addr) & (~mask)) | (val & mask); + + // Add these values to the map + uint16_t hash = hash_addr((uint32_t) addr); + uint16_t tries = REGISTER_MAP_SIZE; + while(CHECK_COLLISION(hash, addr) && (tries > 0U)) { hash = hash_addr((uint32_t) hash); tries--;} + if (tries != 0U){ + register_map[hash].address = addr; + register_map[hash].value = (register_map[hash].value & (~mask)) | (val & mask); + register_map[hash].check_mask |= mask; + } else { + #ifdef DEBUG_FAULTS + print("Hash collision: address 0x"); puth((uint32_t) addr); print("!\n"); + #endif + } + EXIT_CRITICAL() +} + +// Set individual bits. Also add them to the check_mask. +// Do not use this to change bits that get reset by the hardware +void register_set_bits(volatile uint32_t *addr, uint32_t val) { + register_set(addr, val, val); +} + +// Clear individual bits. Also add them to the check_mask. +// Do not use this to clear bits that get set by the hardware +void register_clear_bits(volatile uint32_t *addr, uint32_t val) { + register_set(addr, (~val), val); +} + +// To be called periodically +void check_registers(void){ + for(uint16_t i=0U; i> 16U) ^ ((((input + 1U) & 0xFFFFU) * HASHING_PRIME) & 0xFFFFU)) & REGISTER_MAP_SIZE); -} +#pragma once -// Do not put bits in the check mask that get changed by the hardware -void register_set(volatile uint32_t *addr, uint32_t val, uint32_t mask){ - ENTER_CRITICAL() - // Set bits in register that are also in the mask - (*addr) = ((*addr) & (~mask)) | (val & mask); - - // Add these values to the map - uint16_t hash = hash_addr((uint32_t) addr); - uint16_t tries = REGISTER_MAP_SIZE; - while(CHECK_COLLISION(hash, addr) && (tries > 0U)) { hash = hash_addr((uint32_t) hash); tries--;} - if (tries != 0U){ - register_map[hash].address = addr; - register_map[hash].value = (register_map[hash].value & (~mask)) | (val & mask); - register_map[hash].check_mask |= mask; - } else { - #ifdef DEBUG_FAULTS - print("Hash collision: address 0x"); puth((uint32_t) addr); print("!\n"); - #endif - } - EXIT_CRITICAL() -} - -// Set individual bits. Also add them to the check_mask. -// Do not use this to change bits that get reset by the hardware -void register_set_bits(volatile uint32_t *addr, uint32_t val) { - register_set(addr, val, val); -} - -// Clear individual bits. Also add them to the check_mask. -// Do not use this to clear bits that get set by the hardware -void register_clear_bits(volatile uint32_t *addr, uint32_t val) { - register_set(addr, (~val), val); -} - -// To be called periodically -void check_registers(void){ - for(uint16_t i=0U; i wd_state.threshold) { + print("WD timeout 0x"); puth(et); print("\n"); + fault_occurred(wd_state.fault); + } + + wd_state.last_ts = ts; +} + +void simple_watchdog_init(uint32_t fault, uint32_t threshold) { + wd_state.fault = fault; + wd_state.threshold = threshold; + wd_state.last_ts = microsecond_timer_get(); +} diff --git a/board/drivers/simple_watchdog.h b/board/drivers/simple_watchdog.h index 7668402fec4..b4befb1e96e 100644 --- a/board/drivers/simple_watchdog.h +++ b/board/drivers/simple_watchdog.h @@ -1,21 +1,12 @@ -#include "board/drivers/drivers.h" - -static simple_watchdog_state_t wd_state; +#pragma once -void simple_watchdog_kick(void) { - uint32_t ts = microsecond_timer_get(); - - uint32_t et = get_ts_elapsed(ts, wd_state.last_ts); - if (et > wd_state.threshold) { - print("WD timeout 0x"); puth(et); print("\n"); - fault_occurred(wd_state.fault); - } +#include "board/drivers/drivers.h" - wd_state.last_ts = ts; -} +typedef struct simple_watchdog_state_t { + uint32_t fault; + uint32_t last_ts; + uint32_t threshold; +} simple_watchdog_state_t; -void simple_watchdog_init(uint32_t fault, uint32_t threshold) { - wd_state.fault = fault; - wd_state.threshold = threshold; - wd_state.last_ts = microsecond_timer_get(); -} +void simple_watchdog_kick(void); +void simple_watchdog_init(uint32_t fault, uint32_t threshold); From c05519703850aa61f4628cf3dd5a5112b2f661d0 Mon Sep 17 00:00:00 2001 From: Kasion <540853397@qq.com> Date: Sat, 9 May 2026 09:02:37 +0800 Subject: [PATCH 2/2] Fix CI build: add new driver .c files to SConscript build The PR #2389 refactored driver modules from header-only to .c+.h structure. New .c files in board/drivers/ were not added to the SConscript build system, causing undefined reference errors during linking. --- SConscript | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/SConscript b/SConscript index de770920385..552e123cad2 100644 --- a/SConscript +++ b/SConscript @@ -115,7 +115,14 @@ def build_project(project_name, project, main, extra_flags): # Build + sign main (aka app) main_elf = env.Program(f"{project_dir}/main.elf", [ startup, - main + main, + "./board/drivers/bootkick.c", + "./board/drivers/clock_source.c", + "./board/drivers/fan.c", + "./board/drivers/led.c", + "./board/drivers/pwm.c", + "./board/drivers/registers.c", + "./board/drivers/simple_watchdog.c", ], LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags) main_bin = env.Objcopy(f"{project_dir}/main.bin", main_elf) sign_py = File(f"./board/crypto/sign.py").srcnode().relpath