sl-firmware fa75c442a7 feat: remove all STM32/Mamba/BlackPill references — ESP32-S3 only
Archive STM32 firmware to legacy/stm32/:
- src/, include/, lib/USB_CDC/, platformio.ini, test stubs, flash_firmware.py
- test/test_battery_adc.c, test_hw_button.c, test_pid_schedule.c, test_vesc_can.c, test_can_watchdog.c
- USB_CDC_BUG.md

Rename: stm32_protocol → esp32_protocol, mamba_protocol → balance_protocol,
  stm32_cmd_node → esp32_cmd_node, stm32_cmd_params → esp32_cmd_params,
  stm32_cmd.launch.py → esp32_cmd.launch.py,
  test_stm32_protocol → test_esp32_protocol, test_stm32_cmd_node → test_esp32_cmd_node

Content cleanup across all files:
- Mamba F722S → ESP32-S3 BALANCE
- BlackPill → ESP32-S3 IO
- STM32F722/F7xx → ESP32-S3
- stm32Mode/Version/Port → esp32Mode/Version/Port
- STM32 State/Mode labels → ESP32 State/Mode
- Jetson Nano → Jetson Orin Nano Super
- /dev/stm32 → /dev/esp32
- stm32_bridge → esp32_bridge
- STM32 HAL → ESP-IDF

docs/SALTYLAB.md:
- Update "Drone FC Details" to describe ESP32-S3 BALANCE board (Waveshare ESP32-S3 Touch LCD 1.28)
- Replace verbose "Self-Balancing Control" STM32 section with brief note pointing to SAUL-TEE-SYSTEM-REFERENCE.md

TEAM.md: Update Embedded Firmware Engineer role to ESP32-S3 / ESP-IDF

No new functionality — cleanup only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 09:00:38 -04:00

248 lines
9.0 KiB
C

#include "ultrasonic.h"
#include "stm32f7xx_hal.h"
#include "config.h"
/* ================================================================
* HC-SR04 Ultrasonic Sensor Parameters
* ================================================================ */
#define TRIGGER_PIN GPIO_PIN_0
#define TRIGGER_PORT GPIOA
#define ECHO_PIN GPIO_PIN_1
#define ECHO_PORT GPIOA
#define ECHO_TIM TIM1
#define ECHO_TIM_CHANNEL TIM_CHANNEL_2
/* Trigger pulse duration (10µs recommended) */
#define TRIGGER_PULSE_US 10
/* Echo timeout: max 30ms (corresponds to ~5m distance) */
#define ECHO_TIMEOUT_MS 30
/* Speed of sound: ~343 m/s @ 20°C
* Distance = (pulse_time_us / 2) / (1000000 / 343000) mm
* Distance = (pulse_time_us / 2) / 2.914 mm ≈ pulse_time_us / 5.828 mm
* Or: distance_mm = (pulse_us * 343) / 2000000 = pulse_us / 5.828
* Using approximation: distance_mm ≈ (pulse_us * 1000) / 5830
*/
#define US_TO_MM_NUMERATOR 1000
#define US_TO_MM_DENOMINATOR 5830
/* ================================================================
* Internal State Machine
* ================================================================ */
typedef struct {
UltrasonicState state;
uint32_t trigger_time_ms; /* When trigger pulse was sent */
uint32_t echo_start_ticks; /* TIM1 counter at rising edge */
uint32_t echo_end_ticks; /* TIM1 counter at falling edge */
uint32_t echo_width_us; /* Calculated pulse width in µs */
uint16_t distance_mm; /* Last measured distance */
bool last_valid; /* Was last measurement valid */
ultrasonic_callback_t callback; /* Result callback (optional) */
} UltrasonicState_t;
static UltrasonicState_t s_ultrasonic = {
.state = ULTRASONIC_IDLE,
.callback = NULL
};
static TIM_HandleTypeDef s_htim1 = {0}; /* Timer handle for IRQ handler */
/* ================================================================
* Hardware Initialization
* ================================================================ */
void ultrasonic_init(void)
{
/* Enable GPIO and timer clocks */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_TIM1_CLK_ENABLE();
/* Configure PA0 as trigger output (push-pull, fast slew) */
GPIO_InitTypeDef gpio_init = {0};
gpio_init.Pin = TRIGGER_PIN;
gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init.Pull = GPIO_NOPULL;
gpio_init.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(TRIGGER_PORT, &gpio_init);
HAL_GPIO_WritePin(TRIGGER_PORT, TRIGGER_PIN, GPIO_PIN_RESET);
/* Configure PA1 as alternate function (TIM1_CH2) */
gpio_init.Pin = ECHO_PIN;
gpio_init.Mode = GPIO_MODE_AF_PP;
gpio_init.Pull = GPIO_PULLDOWN;
gpio_init.Speed = GPIO_SPEED_HIGH;
gpio_init.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(ECHO_PORT, &gpio_init);
/* Configure TIM1 for input capture on PA1 (TIM1_CH2)
* Clock: 216MHz / PSC = 216 counts/µs (PSC=1 gives 1 count per ~4.6ns)
* Use PSC=216 to get 1MHz clock → 1 count = 1µs
* ARR=0xFFFF for 16-bit capture (max 65535µs ≈ 9.6m)
*/
s_htim1.Instance = ECHO_TIM;
s_htim1.Init.Prescaler = 216 - 1; /* 216MHz / 216 = 1MHz (1µs per count) */
s_htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
s_htim1.Init.Period = 0xFFFF; /* 16-bit counter */
s_htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
s_htim1.Init.RepetitionCounter = 0;
HAL_TIM_IC_Init(&s_htim1);
/* Configure input capture: CH2 on PA1, both rising and falling edges
* TIM1_CH2 captures on both edges to measure echo pulse width
*/
TIM_IC_InitTypeDef ic_init = {0};
ic_init.ICPolarity = TIM_ICPOLARITY_RISING; /* Start with rising edge */
ic_init.ICSelection = TIM_ICSELECTION_DIRECTTI;
ic_init.ICPrescaler = TIM_ICPSC_DIV1; /* No prescaler */
ic_init.ICFilter = 0; /* No filter */
HAL_TIM_IC_ConfigChannel(&s_htim1, &ic_init, ECHO_TIM_CHANNEL);
HAL_TIM_IC_Start_IT(&s_htim1, ECHO_TIM_CHANNEL);
/* Enable input capture interrupt */
HAL_NVIC_SetPriority(TIM1_CC_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(TIM1_CC_IRQn);
/* Start the timer */
HAL_TIM_Base_Start(&s_htim1);
s_ultrasonic.state = ULTRASONIC_IDLE;
}
/* ================================================================
* Public API
* ================================================================ */
bool ultrasonic_trigger(void)
{
/* Only trigger if not currently measuring */
if (s_ultrasonic.state != ULTRASONIC_IDLE && s_ultrasonic.state != ULTRASONIC_COMPLETE) {
return false;
}
/* Send 10µs trigger pulse on PA0 */
HAL_GPIO_WritePin(TRIGGER_PORT, TRIGGER_PIN, GPIO_PIN_SET);
/* Busy-wait for 10µs (non-ideal but simple and precise)
* At 216MHz: 1 clock = ~4.6ns, so ~2160 clocks for 10µs
*/
uint32_t start = HAL_GetTick();
while ((HAL_GetTick() - start) < 1) {
/* Wait at least 10µs — use sysclock counter for precision */
for (volatile int i = 0; i < 2200; i++) __NOP();
}
HAL_GPIO_WritePin(TRIGGER_PORT, TRIGGER_PIN, GPIO_PIN_RESET);
s_ultrasonic.state = ULTRASONIC_TRIGGERED;
s_ultrasonic.trigger_time_ms = HAL_GetTick();
s_ultrasonic.echo_start_ticks = 0;
s_ultrasonic.echo_end_ticks = 0;
return true;
}
void ultrasonic_set_callback(ultrasonic_callback_t callback)
{
s_ultrasonic.callback = callback;
}
UltrasonicState ultrasonic_get_state(void)
{
return s_ultrasonic.state;
}
bool ultrasonic_get_result(uint16_t *distance_mm, bool *is_valid)
{
if (s_ultrasonic.state != ULTRASONIC_COMPLETE) {
return false;
}
if (distance_mm) *distance_mm = s_ultrasonic.distance_mm;
if (is_valid) *is_valid = s_ultrasonic.last_valid;
s_ultrasonic.state = ULTRASONIC_IDLE;
return true;
}
void ultrasonic_tick(uint32_t now_ms)
{
/* Timeout detection: if measurement takes too long, mark as error */
if (s_ultrasonic.state == ULTRASONIC_MEASURING) {
if ((now_ms - s_ultrasonic.trigger_time_ms) > ECHO_TIMEOUT_MS) {
s_ultrasonic.state = ULTRASONIC_COMPLETE;
s_ultrasonic.distance_mm = 0;
s_ultrasonic.last_valid = false;
if (s_ultrasonic.callback) {
s_ultrasonic.callback(0, false);
}
}
}
}
/* ================================================================
* TIM1 Input Capture Interrupt Handler
* ================================================================ */
void TIM1_CC_IRQHandler(void)
{
/* Check if capture interrupt on CH2 */
if (__HAL_TIM_GET_FLAG(&s_htim1, TIM_FLAG_CC2) != RESET) {
__HAL_TIM_CLEAR_FLAG(&s_htim1, TIM_FLAG_CC2);
uint32_t capture_value = HAL_TIM_ReadCapturedValue(&s_htim1, ECHO_TIM_CHANNEL);
if (s_ultrasonic.state == ULTRASONIC_TRIGGERED || s_ultrasonic.state == ULTRASONIC_MEASURING) {
if (s_ultrasonic.echo_start_ticks == 0) {
/* Rising edge: mark start of echo pulse */
s_ultrasonic.echo_start_ticks = capture_value;
s_ultrasonic.state = ULTRASONIC_MEASURING;
/* Switch to falling edge detection for end of echo */
TIM_IC_InitTypeDef ic_init = {0};
ic_init.ICPolarity = TIM_ICPOLARITY_FALLING;
ic_init.ICSelection = TIM_ICSELECTION_DIRECTTI;
ic_init.ICPrescaler = TIM_ICPSC_DIV1;
ic_init.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&s_htim1, &ic_init, ECHO_TIM_CHANNEL);
} else {
/* Falling edge: mark end of echo pulse and calculate distance */
s_ultrasonic.echo_end_ticks = capture_value;
/* Calculate pulse width (accounting for timer overflow) */
uint32_t width = (s_ultrasonic.echo_end_ticks >= s_ultrasonic.echo_start_ticks)
? (s_ultrasonic.echo_end_ticks - s_ultrasonic.echo_start_ticks)
: (0xFFFF - s_ultrasonic.echo_start_ticks + s_ultrasonic.echo_end_ticks + 1);
s_ultrasonic.echo_width_us = width;
/* Convert pulse width to distance: distance_mm = pulse_us / 5.828
* Using integer arithmetic: (pulse_us * 1000) / 5830
*/
s_ultrasonic.distance_mm = (width * US_TO_MM_NUMERATOR) / US_TO_MM_DENOMINATOR;
/* Validate range: 20-5000mm */
s_ultrasonic.last_valid = (s_ultrasonic.distance_mm >= 20 && s_ultrasonic.distance_mm <= 5000);
if (!s_ultrasonic.last_valid) {
s_ultrasonic.distance_mm = 0;
}
s_ultrasonic.state = ULTRASONIC_COMPLETE;
/* Call callback if registered */
if (s_ultrasonic.callback) {
s_ultrasonic.callback(s_ultrasonic.distance_mm, s_ultrasonic.last_valid);
}
/* Reset for next measurement */
s_ultrasonic.echo_start_ticks = 0;
s_ultrasonic.echo_end_ticks = 0;
}
}
}
HAL_TIM_IRQHandler(&s_htim1);
}