From 4548c1f2a08cca50c0da0c799a8857abc1cef8be Mon Sep 17 00:00:00 2001 From: Ayzen Date: Fri, 9 Oct 2026 19:45:27 +0300 Subject: [PATCH] sweep feature added --- App/Core/app_core.c | 179 ++++++++++++++++++++++++++----- App/Core/app_core.h | 5 + App/Devices/stm32_dac_output.c | 2 - App/Devices/stm32_dac_output.h | 2 + App/Models/app_types.h | 9 +- App/Protocol/app_uart_protocol.c | 3 +- App/Services/sweep.c | 66 ++++++++++++ App/Services/sweep.h | 57 ++++++++++ For_stm32.ioc | 4 +- Makefile | 1 + Src/main.c | 4 +- Src/stm32f7xx_it.c | 6 +- 12 files changed, 299 insertions(+), 39 deletions(-) create mode 100644 App/Services/sweep.c create mode 100644 App/Services/sweep.h diff --git a/App/Core/app_core.c b/App/Core/app_core.c index b3d653a..3da9196 100644 --- a/App/Core/app_core.c +++ b/App/Core/app_core.c @@ -26,6 +26,7 @@ #include "profile_storage.h" #include "storage_sd.h" #include "stm32_dac_output.h" +#include "sweep.h" #include "telemetry.h" #include "temperature_control.h" #include "uart_transport.h" @@ -40,12 +41,16 @@ #define APP_TEC_CHANNEL_COUNT 2u #define APP_TEC_DRIVE_MIN_CODE 1000u #define APP_TEC_DRIVE_MAX_CODE 56800u +/* Largest swing that fits the drive range; the actual amplitude is further limited by headroom around the PID output. */ +#define APP_TEC_MODULATION_MAX_AMPLITUDE_CODE ((APP_TEC_DRIVE_MAX_CODE - APP_TEC_DRIVE_MIN_CODE) / 2u) #define APP_TEC_MODULATION_UPDATE_RATE_HZ 16000u #define APP_TEC_MODULATION_LUT_BITS 5u #define APP_TEC_MODULATION_LUT_SIZE (1u << APP_TEC_MODULATION_LUT_BITS) #define APP_TEC_MODULATION_PHASE_SHIFT (32u - APP_TEC_MODULATION_LUT_BITS) #define APP_TEC_MODULATION_ALLOWED_FLAGS \ (APP_TEC_MODULATION_FLAG_ENABLE | APP_TEC_MODULATION_FLAG_CHANNEL_2) +#define APP_SWEEP_TICK_US 10u +#define APP_SWEEP_ALLOWED_FLAGS (APP_SWEEP_FLAG_ENABLE | APP_SWEEP_FLAG_TARGET_STM32_DAC) /* * Boot-diagnostic codes exposed through QUERY_STATE (0x6666). @@ -151,8 +156,17 @@ typedef struct app_context_t { /* Written by PID loop. Read by TIM5. Purpose: latest slow TEC drive centers for both channels. */ uint16_t tec_base_code[APP_TEC_CHANNEL_COUNT]; - /* Written by main-loop SPI sections. Read by TIM5. Purpose: skip modulation writes while shared SPI is busy. */ - volatile uint8_t tec_modulation_spi_suspended; + /* Written by main-loop SPI sections. Read by TIM5 and TIM2. Purpose: defer IRQ-driven DAC writes while shared SPI is busy. */ + volatile uint8_t irq_dac_writes_suspended; + + /* Written by sweep command and TIM2. Read by TIM2 and the WORK loop. Purpose: laser 2 current staircase sweep. */ + sweep_t laser2_current_sweep; + + /* Written by sweep command and TIM2. Read by TIM2 and STM32 DAC setters. Purpose: STM32 DAC staircase sweep. */ + sweep_t stm32_dac_sweep; + + /* Written by STM32 DAC command, profiles and idle reset. Read when the DAC sweep stops. Purpose: manual DAC output setting. */ + stm32_dac_config_t stm32_dac; /* Written when a profile is activated. Read mainly for future profile cycling/debug visibility. Purpose: snapshot of the currently selected standalone profile. */ profile_t active_profile; @@ -180,11 +194,14 @@ static uint16_t app_clamp_tec_drive_code(int32_t value); static bool app_normalize_tec_modulation_config(const tec_modulation_config_t *input, tec_modulation_config_t *output); static void app_store_tec_modulation_config(const tec_modulation_config_t *config); -static void app_suspend_tec_modulation_spi(void); -static void app_resume_tec_modulation_spi(void); +static void app_suspend_irq_dac_writes(void); +static void app_resume_irq_dac_writes(void); static void app_write_laser_dac_channel(uint8_t channel, uint16_t value); static void app_write_tec_pid_output(uint8_t channel_index, uint16_t pid_code); static void app_write_tec_modulation_sample(void); +static void app_set_stm32_dac(uint16_t code, uint8_t enabled); +static void app_update_sweep_timer(void); +static void app_stop_sweeps(void); static void app_prepare_status_response(void); static void app_capture_live_frame(bool update_temperature_loops, bool drive_laser_currents); static void app_decode_work_packet(const uint16_t *packet_words); @@ -204,6 +221,7 @@ static void app_handle_ad9833_control_packet(const app_packet_t *packet); static void app_handle_ds1809_control_packet(const app_packet_t *packet); static void app_handle_stm32_dac_control_packet(const app_packet_t *packet); static void app_handle_tec_modulation_control_packet(const app_packet_t *packet); +static void app_handle_sweep_control_packet(const app_packet_t *packet); static void app_handle_ad9102_wave_control_packet(const app_packet_t *packet); static void app_handle_ad9102_wave_data_packet(const app_packet_t *packet); static void app_handle_profile_save_data_packet(const app_packet_t *packet); @@ -335,6 +353,7 @@ static void app_apply_work_config_to_board(void) static void app_enter_idle_state(bool reset_ticks) { + app_stop_sweeps(); g_app.work_config = g_app.default_work_config; g_app.laser_config[0] = g_app.default_laser_config[0]; g_app.laser_config[1] = g_app.default_laser_config[1]; @@ -346,7 +365,7 @@ static void app_enter_idle_state(bool reset_ticks) ad9102_stop_output(); ad9833_apply(0u, 0u, 0u); board_io_reset_runtime_outputs(); - stm32_dac_output_set(0u, 0u); + app_set_stm32_dac(0u, 0u); if (LL_SPI_IsEnabled(SPI2)) { @@ -367,7 +386,7 @@ static void app_reset_tec_modulation(void) g_app.tec_modulation_phase_step = 0u; g_app.tec_base_code[0] = APP_TEC_PRECHARGE_CODE; g_app.tec_base_code[1] = APP_TEC_PRECHARGE_CODE; - g_app.tec_modulation_spi_suspended = 0u; + g_app.irq_dac_writes_suspended = 0u; } static bool app_is_tec_channel_active(uint8_t channel_index) @@ -452,31 +471,31 @@ static void app_store_tec_modulation_config(const tec_modulation_config_t *confi __enable_irq(); } -static void app_suspend_tec_modulation_spi(void) +static void app_suspend_irq_dac_writes(void) { __disable_irq(); - if (g_app.tec_modulation_spi_suspended < 0xFFu) + if (g_app.irq_dac_writes_suspended < 0xFFu) { - ++g_app.tec_modulation_spi_suspended; + ++g_app.irq_dac_writes_suspended; } __enable_irq(); } -static void app_resume_tec_modulation_spi(void) +static void app_resume_irq_dac_writes(void) { __disable_irq(); - if (g_app.tec_modulation_spi_suspended > 0u) + if (g_app.irq_dac_writes_suspended > 0u) { - --g_app.tec_modulation_spi_suspended; + --g_app.irq_dac_writes_suspended; } __enable_irq(); } static void app_write_laser_dac_channel(uint8_t channel, uint16_t value) { - app_suspend_tec_modulation_spi(); + app_suspend_irq_dac_writes(); laser_dac_write_channel(channel, value); - app_resume_tec_modulation_spi(); + app_resume_irq_dac_writes(); } static void app_write_tec_pid_output(uint8_t channel_index, uint16_t pid_code) @@ -515,7 +534,7 @@ static void app_write_tec_modulation_sample(void) return; } - if (g_app.tec_modulation_spi_suspended != 0u) + if (g_app.irq_dac_writes_suspended != 0u) { g_app.tec_modulation_phase += g_app.tec_modulation_phase_step; return; @@ -544,6 +563,37 @@ static void app_write_tec_modulation_sample(void) g_app.tec_modulation_phase += g_app.tec_modulation_phase_step; } +static void app_set_stm32_dac(uint16_t code, uint8_t enabled) +{ + g_app.stm32_dac.code = code; + g_app.stm32_dac.enabled = enabled; + + /* A running sweep owns the output; the manual setting is restored when it stops. */ + if (g_app.stm32_dac_sweep.enabled == 0u) + { + stm32_dac_output_set(code, enabled); + } +} + +static void app_update_sweep_timer(void) +{ + if ((g_app.laser2_current_sweep.enabled != 0u) || (g_app.stm32_dac_sweep.enabled != 0u)) + { + LL_TIM_EnableCounter(TIM2); + } + else + { + LL_TIM_DisableCounter(TIM2); + } +} + +static void app_stop_sweeps(void) +{ + sweep_stop(&g_app.laser2_current_sweep); + sweep_stop(&g_app.stm32_dac_sweep); + app_update_sweep_timer(); +} + static void app_apply_profile_auxiliary_outputs(const profile_t *profile) { if (profile == NULL) @@ -552,7 +602,7 @@ static void app_apply_profile_auxiliary_outputs(const profile_t *profile) } ad9833_apply(profile->ad9833.enabled, profile->ad9833.triangle, profile->ad9833.frequency_word); - stm32_dac_output_set(profile->stm32_dac.code, profile->stm32_dac.enabled); + app_set_stm32_dac(profile->stm32_dac.code, profile->stm32_dac.enabled); } static void app_prepare_status_response(void) @@ -630,9 +680,9 @@ static bool app_activate_profile(const profile_t *profile, uint16_t index, bool if (profile->ds1809.apply_position) { - app_suspend_tec_modulation_spi(); + app_suspend_irq_dac_writes(); ds1809_apply_position_from_min(profile->ds1809.position_from_min); - app_resume_tec_modulation_spi(); + app_resume_irq_dac_writes(); } return true; @@ -673,7 +723,10 @@ static void app_capture_live_frame(bool update_temperature_loops, bool drive_las if (drive_laser_currents) { app_write_laser_dac_channel(1u, g_app.laser_config[0].current_raw); - app_write_laser_dac_channel(2u, g_app.laser_config[1].current_raw); + if (g_app.laser2_current_sweep.enabled == 0u) + { + app_write_laser_dac_channel(2u, g_app.laser_config[1].current_raw); + } } (void)adc_mux_capture_internal_telemetry(&internal_telemetry); @@ -1112,7 +1165,7 @@ static void app_handle_ad9102_control_packet(const app_packet_t *packet) triangle = (flags & APP_AD9102_FLAG_TRIANGLE) ? 1u : 0u; sram_mode = (flags & APP_AD9102_FLAG_SRAM) ? 1u : 0u; - app_suspend_tec_modulation_spi(); + app_suspend_irq_dac_writes(); if (sram_mode != 0u) { uint16_t sample_count; @@ -1185,7 +1238,7 @@ static void app_handle_ad9102_control_packet(const app_packet_t *packet) app_set_error(APP_STATUS_FLAG_AD9102_ERROR); } } - app_resume_tec_modulation_spi(); + app_resume_irq_dac_writes(); g_app.status_bytes[1] = (uint8_t)(pat_status & 0x00FFu); app_prepare_status_response(); @@ -1209,11 +1262,11 @@ static void app_handle_ad9833_control_packet(const app_packet_t *packet) lsw = (uint16_t)(packet->words[1] & 0x3FFFu); msw = (uint16_t)(packet->words[2] & 0x3FFFu); frequency_word = ((uint32_t)msw << 14) | (uint32_t)lsw; - app_suspend_tec_modulation_spi(); + app_suspend_irq_dac_writes(); ad9833_apply((flags & APP_AD9833_FLAG_ENABLE) ? 1u : 0u, (flags & APP_AD9833_FLAG_TRIANGLE) ? 1u : 0u, frequency_word); - app_resume_tec_modulation_spi(); + app_resume_irq_dac_writes(); app_prepare_status_response(); } @@ -1275,8 +1328,8 @@ static void app_handle_stm32_dac_control_packet(const app_packet_t *packet) return; } - stm32_dac_output_set((uint16_t)(packet->words[1] & 0x0FFFu), - (packet->words[0] & APP_STM32_DAC_FLAG_ENABLE) ? 1u : 0u); + app_set_stm32_dac((uint16_t)(packet->words[1] & 0x0FFFu), + (packet->words[0] & APP_STM32_DAC_FLAG_ENABLE) ? 1u : 0u); app_prepare_status_response(); } @@ -1329,6 +1382,53 @@ static void app_handle_tec_modulation_control_packet(const app_packet_t *packet) app_prepare_status_response(); } +static void app_handle_sweep_control_packet(const app_packet_t *packet) +{ + sweep_config_t config; + sweep_t *sweep; + bool dac_target; + bool started = true; + + if ((packet == NULL) || !packet->checksum_valid || + ((packet->words[0] & ~APP_SWEEP_ALLOWED_FLAGS) != 0u)) + { + app_set_error(APP_STATUS_FLAG_UART_DECODE_ERROR); + app_prepare_status_response(); + return; + } + + dac_target = (packet->words[0] & APP_SWEEP_FLAG_TARGET_STM32_DAC) != 0u; + sweep = dac_target ? &g_app.stm32_dac_sweep : &g_app.laser2_current_sweep; + sweep_stop(sweep); + + if ((packet->words[0] & APP_SWEEP_FLAG_ENABLE) != 0u) + { + config.min_code = packet->words[1]; + config.max_code = packet->words[2]; + config.step_code = packet->words[3]; + config.step_period_us = packet->words[4]; + config.pause_ms = packet->words[5]; + + /* The laser current is only driven in WORK mode; idle entry stops the sweep again. */ + started = dac_target ? (config.max_code <= STM32_DAC_MAX_CODE) : (g_app.mode == APP_MODE_WORK); + __disable_irq(); + started = started && sweep_start(sweep, &config, APP_SWEEP_TICK_US); + __enable_irq(); + } + + if (dac_target && (sweep->enabled == 0u)) + { + stm32_dac_output_set(g_app.stm32_dac.code, g_app.stm32_dac.enabled); + } + app_update_sweep_timer(); + + if (!started) + { + app_set_error(APP_STATUS_FLAG_UART_DECODE_ERROR); + } + app_prepare_status_response(); +} + static void app_handle_ad9102_wave_control_packet(const app_packet_t *packet) { uint16_t opcode; @@ -1349,7 +1449,7 @@ static void app_handle_ad9102_wave_control_packet(const app_packet_t *packet) param0 = packet->words[1]; param1 = packet->words[2]; - app_suspend_tec_modulation_spi(); + app_suspend_irq_dac_writes(); switch (opcode) { case APP_AD9102_WAVE_OPCODE_BEGIN: @@ -1391,7 +1491,7 @@ static void app_handle_ad9102_wave_control_packet(const app_packet_t *packet) app_set_error(APP_STATUS_FLAG_AD9102_ERROR); break; } - app_resume_tec_modulation_spi(); + app_resume_irq_dac_writes(); app_prepare_status_response(); } @@ -1406,13 +1506,13 @@ static void app_handle_ad9102_wave_data_packet(const app_packet_t *packet) return; } - app_suspend_tec_modulation_spi(); + app_suspend_irq_dac_writes(); if (!ad9102_write_custom_chunk(&packet->words[1], packet->words[0])) { ad9102_cancel_custom_upload(); app_set_error(APP_STATUS_FLAG_AD9102_ERROR); } - app_resume_tec_modulation_spi(); + app_resume_irq_dac_writes(); app_prepare_status_response(); } @@ -1506,6 +1606,10 @@ static void app_handle_packet(const app_packet_t *packet) app_handle_tec_modulation_control_packet(packet); break; + case APP_PACKET_KIND_SWEEP_CONTROL: + app_handle_sweep_control_packet(packet); + break; + case APP_PACKET_KIND_AD9102_WAVE_CONTROL: app_handle_ad9102_wave_control_packet(packet); break; @@ -1652,6 +1756,7 @@ void app_init(void) board_io_reset_runtime_outputs(); HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); + LL_TIM_EnableIT_UPDATE(TIM2); LL_TIM_EnableIT_UPDATE(TIM5); LL_TIM_EnableCounter(TIM5); LL_TIM_EnableIT_UPDATE(TIM6); @@ -1764,6 +1869,22 @@ void app_on_tim7_tick(void) ++g_app.tick_1ms; } +void app_on_tim2_tick(void) +{ + uint16_t code; + + /* The laser step is postponed, not dropped, while the main loop owns the shared DAC SPI bus. */ + if ((g_app.irq_dac_writes_suspended == 0u) && + sweep_tick(&g_app.laser2_current_sweep, &code)) + { + laser_dac_write_channel(2u, code); + } + if (sweep_tick(&g_app.stm32_dac_sweep, &code)) + { + stm32_dac_output_set(code, 1u); + } +} + void app_on_tim5_tick(void) { app_write_tec_modulation_sample(); diff --git a/App/Core/app_core.h b/App/Core/app_core.h index d2fdeb7..22f4c44 100644 --- a/App/Core/app_core.h +++ b/App/Core/app_core.h @@ -58,6 +58,11 @@ void app_on_tim7_tick(void); */ void app_on_tim5_tick(void); +/** + * @brief Notify the application core about the 10 us sweep tick. + */ +void app_on_tim2_tick(void); + /** * @brief Notify the application core that the USART DMA transmit finished. */ diff --git a/App/Devices/stm32_dac_output.c b/App/Devices/stm32_dac_output.c index d5a3dab..86a25d5 100644 --- a/App/Devices/stm32_dac_output.c +++ b/App/Devices/stm32_dac_output.c @@ -7,8 +7,6 @@ #include "main.h" -#define STM32_DAC_MAX_CODE 4095u - void stm32_dac_output_init(void) { GPIO_InitTypeDef gpio_init = {0}; diff --git a/App/Devices/stm32_dac_output.h b/App/Devices/stm32_dac_output.h index 2c98d97..d0fa254 100644 --- a/App/Devices/stm32_dac_output.h +++ b/App/Devices/stm32_dac_output.h @@ -8,6 +8,8 @@ #include +#define STM32_DAC_MAX_CODE 4095u + void stm32_dac_output_init(void); void stm32_dac_output_set(uint16_t dac_code, uint8_t enabled); diff --git a/App/Models/app_types.h b/App/Models/app_types.h index d48908d..9434f0f 100644 --- a/App/Models/app_types.h +++ b/App/Models/app_types.h @@ -78,6 +78,7 @@ /** @brief UART header for TEC drive modulation control packets. */ #define APP_PACKET_HEADER_TEC_MODULATION_CONTROL 0xF0F0u +#define APP_PACKET_HEADER_SWEEP_CONTROL 0xF1F1u /** @brief Packet size of the host work-configuration message, in bytes. */ #define APP_PACKET_BYTES_WORK_CONFIG 30u @@ -93,6 +94,7 @@ /** @brief Packet size of profile-save data messages, in bytes. */ #define APP_PACKET_BYTES_PROFILE_SAVE_DATA 30u +#define APP_PACKET_BYTES_SWEEP_CONTROL 16u /** @brief Number of payload words carried by the host work packet. */ #define APP_PACKET_WORDS_WORK_CONFIG 14u @@ -108,6 +110,7 @@ /** @brief Number of payload words carried by profile-save data packets. */ #define APP_PACKET_WORDS_PROFILE_SAVE_DATA 14u +#define APP_PACKET_WORDS_SWEEP_CONTROL 7u /** @brief First status byte flag for SD-card related errors. */ #define APP_STATUS_FLAG_SD_ERROR 0x01u @@ -170,7 +173,8 @@ #define APP_TEC_MODULATION_MAX_FREQUENCY_HZ 2000u /** @brief Highest accepted peak modulation amplitude in external TEC DAC codes. */ -#define APP_TEC_MODULATION_MAX_AMPLITUDE_CODE 4096u +#define APP_SWEEP_FLAG_ENABLE 0x0001u +#define APP_SWEEP_FLAG_TARGET_STM32_DAC 0x0002u /** @brief Op-code used to begin a custom AD9102 waveform upload. */ #define APP_AD9102_WAVE_OPCODE_BEGIN 0x0001u @@ -243,7 +247,8 @@ typedef enum app_packet_kind_t { APP_PACKET_KIND_AD9102_WAVE_CONTROL, APP_PACKET_KIND_AD9102_WAVE_DATA, APP_PACKET_KIND_PROFILE_SAVE_DATA, - APP_PACKET_KIND_TEC_MODULATION_CONTROL + APP_PACKET_KIND_TEC_MODULATION_CONTROL, + APP_PACKET_KIND_SWEEP_CONTROL } app_packet_kind_t; /** diff --git a/App/Protocol/app_uart_protocol.c b/App/Protocol/app_uart_protocol.c index afc0c15..db01d95 100644 --- a/App/Protocol/app_uart_protocol.c +++ b/App/Protocol/app_uart_protocol.c @@ -20,7 +20,8 @@ static const app_packet_descriptor_t g_packet_descriptors[] = { {APP_PACKET_HEADER_AD9102_WAVE_CONTROL, APP_PACKET_BYTES_SHORT_CONTROL, APP_PACKET_WORDS_SHORT_CONTROL, 3, APP_PACKET_KIND_AD9102_WAVE_CONTROL}, {APP_PACKET_HEADER_AD9102_WAVE_DATA, APP_PACKET_BYTES_AD9102_WAVE_DATA, APP_PACKET_WORDS_AD9102_WAVE_DATA, 13, APP_PACKET_KIND_AD9102_WAVE_DATA}, {APP_PACKET_HEADER_PROFILE_SAVE_DATA, APP_PACKET_BYTES_PROFILE_SAVE_DATA, APP_PACKET_WORDS_PROFILE_SAVE_DATA, 13, APP_PACKET_KIND_PROFILE_SAVE_DATA}, - {APP_PACKET_HEADER_TEC_MODULATION_CONTROL, APP_PACKET_BYTES_SHORT_CONTROL, APP_PACKET_WORDS_SHORT_CONTROL, 3, APP_PACKET_KIND_TEC_MODULATION_CONTROL} + {APP_PACKET_HEADER_TEC_MODULATION_CONTROL, APP_PACKET_BYTES_SHORT_CONTROL, APP_PACKET_WORDS_SHORT_CONTROL, 3, APP_PACKET_KIND_TEC_MODULATION_CONTROL}, + {APP_PACKET_HEADER_SWEEP_CONTROL, APP_PACKET_BYTES_SWEEP_CONTROL, APP_PACKET_WORDS_SWEEP_CONTROL, 6, APP_PACKET_KIND_SWEEP_CONTROL} }; static const app_packet_descriptor_t *app_uart_protocol_find_descriptor(uint16_t header) diff --git a/App/Services/sweep.c b/App/Services/sweep.c new file mode 100644 index 0000000..e3e1bde --- /dev/null +++ b/App/Services/sweep.c @@ -0,0 +1,66 @@ +/** + * @file sweep.c + * @brief Periodic staircase sweep state machine. + */ + +#include "sweep.h" + +#include + +bool sweep_start(sweep_t *sweep, const sweep_config_t *config, uint32_t tick_us) +{ + if ((sweep == NULL) || (config == NULL) || (tick_us == 0u) || + (config->min_code > config->max_code) || (config->step_code == 0u) || + (config->step_period_us < tick_us)) + { + return false; + } + + sweep->min_code = config->min_code; + sweep->max_code = config->max_code; + sweep->step_code = config->step_code; + sweep->step_ticks = config->step_period_us / tick_us; + sweep->pause_ticks = ((uint32_t)config->pause_ms * 1000u) / tick_us; + sweep->next_code = config->min_code; + sweep->countdown_ticks = 1u; + sweep->enabled = 1u; + return true; +} + +void sweep_stop(sweep_t *sweep) +{ + if (sweep != NULL) + { + sweep->enabled = 0u; + } +} + +bool sweep_tick(sweep_t *sweep, uint16_t *out_code) +{ + if ((sweep->enabled == 0u) || (--sweep->countdown_ticks != 0u)) + { + return false; + } + + *out_code = (uint16_t)sweep->next_code; + sweep->countdown_ticks = sweep->step_ticks; + if (sweep->next_code == sweep->min_code) + { + /* The lower bound is held for the inter-sweep pause. */ + sweep->countdown_ticks += sweep->pause_ticks; + } + + if (sweep->next_code >= sweep->max_code) + { + sweep->next_code = sweep->min_code; + } + else + { + sweep->next_code += sweep->step_code; + if (sweep->next_code > sweep->max_code) + { + sweep->next_code = sweep->max_code; + } + } + return true; +} diff --git a/App/Services/sweep.h b/App/Services/sweep.h new file mode 100644 index 0000000..6ed64c7 --- /dev/null +++ b/App/Services/sweep.h @@ -0,0 +1,57 @@ +/** + * @file sweep.h + * @brief Hardware-agnostic periodic staircase sweep between two output codes. + * + * The output steps from `min_code` up to `max_code` (inclusive) every step + * period, then returns to `min_code` and holds it for an extra pause before + * the next sweep starts. + */ + +#ifndef SWEEP_H +#define SWEEP_H + +#include +#include + +/** + * @brief Host-supplied sweep parameters. + */ +typedef struct sweep_config_t { + uint16_t min_code; + uint16_t max_code; + uint16_t step_code; + uint16_t step_period_us; + uint16_t pause_ms; +} sweep_config_t; + +/** + * @brief Sweep state advanced from the periodic tick interrupt. + */ +typedef struct sweep_t { + uint8_t enabled; + uint16_t min_code; + uint16_t max_code; + uint16_t step_code; + uint32_t step_ticks; + uint32_t pause_ticks; + uint32_t next_code; + uint32_t countdown_ticks; +} sweep_t; + +/** + * @brief Validate the configuration and start sweeping from `min_code` on the next tick. + * + * @return false (sweep left stopped) if bounds, step or period are invalid. + */ +bool sweep_start(sweep_t *sweep, const sweep_config_t *config, uint32_t tick_us); + +void sweep_stop(sweep_t *sweep); + +/** + * @brief Advance the sweep by one tick. + * + * @return true when `*out_code` holds a new value that must be written to the output. + */ +bool sweep_tick(sweep_t *sweep, uint16_t *out_code); + +#endif /* SWEEP_H */ diff --git a/For_stm32.ioc b/For_stm32.ioc index c780cc4..51e90bb 100644 --- a/For_stm32.ioc +++ b/For_stm32.ioc @@ -629,8 +629,8 @@ TIM11.Prescaler=1 TIM11.Pulse=91 TIM2.ClockDivision=TIM_CLOCKDIVISION_DIV1 TIM2.IPParameters=Period,ClockDivision,Prescaler -TIM2.Period=840000 -TIM2.Prescaler=1000 +TIM2.Period=9 +TIM2.Prescaler=91 TIM4.Channel-PWM\ Generation3\ CH3=TIM_CHANNEL_3 TIM4.IPParameters=Channel-PWM Generation3 CH3,Period,Pulse-PWM Generation3 CH3 TIM4.Period=45 diff --git a/Makefile b/Makefile index 97a6541..96503d5 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,7 @@ App/Services/storage_sd.c \ App/Services/profile_repository.c \ App/Services/profile_storage.c \ App/Services/ui_status.c \ +App/Services/sweep.c \ Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_adc.c \ Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_adc_ex.c \ Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_rcc.c \ diff --git a/Src/main.c b/Src/main.c index cdbfc23..79a78de 100644 --- a/Src/main.c +++ b/Src/main.c @@ -658,9 +658,9 @@ static void MX_TIM2_Init(void) /* USER CODE BEGIN TIM2_Init 1 */ /* USER CODE END TIM2_Init 1 */ - TIM_InitStruct.Prescaler = 1000; + TIM_InitStruct.Prescaler = 91; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; - TIM_InitStruct.Autoreload = 840000; + TIM_InitStruct.Autoreload = 9; TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; LL_TIM_Init(TIM2, &TIM_InitStruct); LL_TIM_DisableARRPreload(TIM2); diff --git a/Src/stm32f7xx_it.c b/Src/stm32f7xx_it.c index c61d7ec..1323abb 100644 --- a/Src/stm32f7xx_it.c +++ b/Src/stm32f7xx_it.c @@ -246,7 +246,11 @@ void TIM2_IRQHandler(void) /* USER CODE END TIM2_IRQn 0 */ /* USER CODE BEGIN TIM2_IRQn 1 */ - + if(LL_TIM_IsActiveFlag_UPDATE(TIM2)) + { + LL_TIM_ClearFlag_UPDATE(TIM2); + app_on_tim2_tick(); + } /* USER CODE END TIM2_IRQn 1 */ }