sweep feature added
This commit is contained in:
+148
-27
@@ -26,6 +26,7 @@
|
|||||||
#include "profile_storage.h"
|
#include "profile_storage.h"
|
||||||
#include "storage_sd.h"
|
#include "storage_sd.h"
|
||||||
#include "stm32_dac_output.h"
|
#include "stm32_dac_output.h"
|
||||||
|
#include "sweep.h"
|
||||||
#include "telemetry.h"
|
#include "telemetry.h"
|
||||||
#include "temperature_control.h"
|
#include "temperature_control.h"
|
||||||
#include "uart_transport.h"
|
#include "uart_transport.h"
|
||||||
@@ -40,12 +41,16 @@
|
|||||||
#define APP_TEC_CHANNEL_COUNT 2u
|
#define APP_TEC_CHANNEL_COUNT 2u
|
||||||
#define APP_TEC_DRIVE_MIN_CODE 1000u
|
#define APP_TEC_DRIVE_MIN_CODE 1000u
|
||||||
#define APP_TEC_DRIVE_MAX_CODE 56800u
|
#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_UPDATE_RATE_HZ 16000u
|
||||||
#define APP_TEC_MODULATION_LUT_BITS 5u
|
#define APP_TEC_MODULATION_LUT_BITS 5u
|
||||||
#define APP_TEC_MODULATION_LUT_SIZE (1u << APP_TEC_MODULATION_LUT_BITS)
|
#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_PHASE_SHIFT (32u - APP_TEC_MODULATION_LUT_BITS)
|
||||||
#define APP_TEC_MODULATION_ALLOWED_FLAGS \
|
#define APP_TEC_MODULATION_ALLOWED_FLAGS \
|
||||||
(APP_TEC_MODULATION_FLAG_ENABLE | APP_TEC_MODULATION_FLAG_CHANNEL_2)
|
(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).
|
* 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. */
|
/* 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];
|
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. */
|
/* 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 tec_modulation_spi_suspended;
|
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. */
|
/* 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;
|
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,
|
static bool app_normalize_tec_modulation_config(const tec_modulation_config_t *input,
|
||||||
tec_modulation_config_t *output);
|
tec_modulation_config_t *output);
|
||||||
static void app_store_tec_modulation_config(const tec_modulation_config_t *config);
|
static void app_store_tec_modulation_config(const tec_modulation_config_t *config);
|
||||||
static void app_suspend_tec_modulation_spi(void);
|
static void app_suspend_irq_dac_writes(void);
|
||||||
static void app_resume_tec_modulation_spi(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_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_pid_output(uint8_t channel_index, uint16_t pid_code);
|
||||||
static void app_write_tec_modulation_sample(void);
|
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_prepare_status_response(void);
|
||||||
static void app_capture_live_frame(bool update_temperature_loops, bool drive_laser_currents);
|
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);
|
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_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_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_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_control_packet(const app_packet_t *packet);
|
||||||
static void app_handle_ad9102_wave_data_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);
|
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)
|
static void app_enter_idle_state(bool reset_ticks)
|
||||||
{
|
{
|
||||||
|
app_stop_sweeps();
|
||||||
g_app.work_config = g_app.default_work_config;
|
g_app.work_config = g_app.default_work_config;
|
||||||
g_app.laser_config[0] = g_app.default_laser_config[0];
|
g_app.laser_config[0] = g_app.default_laser_config[0];
|
||||||
g_app.laser_config[1] = g_app.default_laser_config[1];
|
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();
|
ad9102_stop_output();
|
||||||
ad9833_apply(0u, 0u, 0u);
|
ad9833_apply(0u, 0u, 0u);
|
||||||
board_io_reset_runtime_outputs();
|
board_io_reset_runtime_outputs();
|
||||||
stm32_dac_output_set(0u, 0u);
|
app_set_stm32_dac(0u, 0u);
|
||||||
|
|
||||||
if (LL_SPI_IsEnabled(SPI2))
|
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_modulation_phase_step = 0u;
|
||||||
g_app.tec_base_code[0] = APP_TEC_PRECHARGE_CODE;
|
g_app.tec_base_code[0] = APP_TEC_PRECHARGE_CODE;
|
||||||
g_app.tec_base_code[1] = 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)
|
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();
|
__enable_irq();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void app_suspend_tec_modulation_spi(void)
|
static void app_suspend_irq_dac_writes(void)
|
||||||
{
|
{
|
||||||
__disable_irq();
|
__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();
|
__enable_irq();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void app_resume_tec_modulation_spi(void)
|
static void app_resume_irq_dac_writes(void)
|
||||||
{
|
{
|
||||||
__disable_irq();
|
__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();
|
__enable_irq();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void app_write_laser_dac_channel(uint8_t channel, uint16_t value)
|
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);
|
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)
|
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;
|
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;
|
g_app.tec_modulation_phase += g_app.tec_modulation_phase_step;
|
||||||
return;
|
return;
|
||||||
@@ -544,6 +563,37 @@ static void app_write_tec_modulation_sample(void)
|
|||||||
g_app.tec_modulation_phase += g_app.tec_modulation_phase_step;
|
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)
|
static void app_apply_profile_auxiliary_outputs(const profile_t *profile)
|
||||||
{
|
{
|
||||||
if (profile == NULL)
|
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);
|
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)
|
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)
|
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);
|
ds1809_apply_position_from_min(profile->ds1809.position_from_min);
|
||||||
app_resume_tec_modulation_spi();
|
app_resume_irq_dac_writes();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -673,8 +723,11 @@ static void app_capture_live_frame(bool update_temperature_loops, bool drive_las
|
|||||||
if (drive_laser_currents)
|
if (drive_laser_currents)
|
||||||
{
|
{
|
||||||
app_write_laser_dac_channel(1u, g_app.laser_config[0].current_raw);
|
app_write_laser_dac_channel(1u, g_app.laser_config[0].current_raw);
|
||||||
|
if (g_app.laser2_current_sweep.enabled == 0u)
|
||||||
|
{
|
||||||
app_write_laser_dac_channel(2u, g_app.laser_config[1].current_raw);
|
app_write_laser_dac_channel(2u, g_app.laser_config[1].current_raw);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(void)adc_mux_capture_internal_telemetry(&internal_telemetry);
|
(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;
|
triangle = (flags & APP_AD9102_FLAG_TRIANGLE) ? 1u : 0u;
|
||||||
sram_mode = (flags & APP_AD9102_FLAG_SRAM) ? 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)
|
if (sram_mode != 0u)
|
||||||
{
|
{
|
||||||
uint16_t sample_count;
|
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_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);
|
g_app.status_bytes[1] = (uint8_t)(pat_status & 0x00FFu);
|
||||||
app_prepare_status_response();
|
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);
|
lsw = (uint16_t)(packet->words[1] & 0x3FFFu);
|
||||||
msw = (uint16_t)(packet->words[2] & 0x3FFFu);
|
msw = (uint16_t)(packet->words[2] & 0x3FFFu);
|
||||||
frequency_word = ((uint32_t)msw << 14) | (uint32_t)lsw;
|
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,
|
ad9833_apply((flags & APP_AD9833_FLAG_ENABLE) ? 1u : 0u,
|
||||||
(flags & APP_AD9833_FLAG_TRIANGLE) ? 1u : 0u,
|
(flags & APP_AD9833_FLAG_TRIANGLE) ? 1u : 0u,
|
||||||
frequency_word);
|
frequency_word);
|
||||||
app_resume_tec_modulation_spi();
|
app_resume_irq_dac_writes();
|
||||||
app_prepare_status_response();
|
app_prepare_status_response();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1275,7 +1328,7 @@ static void app_handle_stm32_dac_control_packet(const app_packet_t *packet)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
stm32_dac_output_set((uint16_t)(packet->words[1] & 0x0FFFu),
|
app_set_stm32_dac((uint16_t)(packet->words[1] & 0x0FFFu),
|
||||||
(packet->words[0] & APP_STM32_DAC_FLAG_ENABLE) ? 1u : 0u);
|
(packet->words[0] & APP_STM32_DAC_FLAG_ENABLE) ? 1u : 0u);
|
||||||
app_prepare_status_response();
|
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();
|
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)
|
static void app_handle_ad9102_wave_control_packet(const app_packet_t *packet)
|
||||||
{
|
{
|
||||||
uint16_t opcode;
|
uint16_t opcode;
|
||||||
@@ -1349,7 +1449,7 @@ static void app_handle_ad9102_wave_control_packet(const app_packet_t *packet)
|
|||||||
param0 = packet->words[1];
|
param0 = packet->words[1];
|
||||||
param1 = packet->words[2];
|
param1 = packet->words[2];
|
||||||
|
|
||||||
app_suspend_tec_modulation_spi();
|
app_suspend_irq_dac_writes();
|
||||||
switch (opcode)
|
switch (opcode)
|
||||||
{
|
{
|
||||||
case APP_AD9102_WAVE_OPCODE_BEGIN:
|
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);
|
app_set_error(APP_STATUS_FLAG_AD9102_ERROR);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
app_resume_tec_modulation_spi();
|
app_resume_irq_dac_writes();
|
||||||
|
|
||||||
app_prepare_status_response();
|
app_prepare_status_response();
|
||||||
}
|
}
|
||||||
@@ -1406,13 +1506,13 @@ static void app_handle_ad9102_wave_data_packet(const app_packet_t *packet)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
app_suspend_tec_modulation_spi();
|
app_suspend_irq_dac_writes();
|
||||||
if (!ad9102_write_custom_chunk(&packet->words[1], packet->words[0]))
|
if (!ad9102_write_custom_chunk(&packet->words[1], packet->words[0]))
|
||||||
{
|
{
|
||||||
ad9102_cancel_custom_upload();
|
ad9102_cancel_custom_upload();
|
||||||
app_set_error(APP_STATUS_FLAG_AD9102_ERROR);
|
app_set_error(APP_STATUS_FLAG_AD9102_ERROR);
|
||||||
}
|
}
|
||||||
app_resume_tec_modulation_spi();
|
app_resume_irq_dac_writes();
|
||||||
|
|
||||||
app_prepare_status_response();
|
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);
|
app_handle_tec_modulation_control_packet(packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case APP_PACKET_KIND_SWEEP_CONTROL:
|
||||||
|
app_handle_sweep_control_packet(packet);
|
||||||
|
break;
|
||||||
|
|
||||||
case APP_PACKET_KIND_AD9102_WAVE_CONTROL:
|
case APP_PACKET_KIND_AD9102_WAVE_CONTROL:
|
||||||
app_handle_ad9102_wave_control_packet(packet);
|
app_handle_ad9102_wave_control_packet(packet);
|
||||||
break;
|
break;
|
||||||
@@ -1652,6 +1756,7 @@ void app_init(void)
|
|||||||
board_io_reset_runtime_outputs();
|
board_io_reset_runtime_outputs();
|
||||||
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
|
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
|
||||||
|
|
||||||
|
LL_TIM_EnableIT_UPDATE(TIM2);
|
||||||
LL_TIM_EnableIT_UPDATE(TIM5);
|
LL_TIM_EnableIT_UPDATE(TIM5);
|
||||||
LL_TIM_EnableCounter(TIM5);
|
LL_TIM_EnableCounter(TIM5);
|
||||||
LL_TIM_EnableIT_UPDATE(TIM6);
|
LL_TIM_EnableIT_UPDATE(TIM6);
|
||||||
@@ -1764,6 +1869,22 @@ void app_on_tim7_tick(void)
|
|||||||
++g_app.tick_1ms;
|
++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)
|
void app_on_tim5_tick(void)
|
||||||
{
|
{
|
||||||
app_write_tec_modulation_sample();
|
app_write_tec_modulation_sample();
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ void app_on_tim7_tick(void);
|
|||||||
*/
|
*/
|
||||||
void app_on_tim5_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.
|
* @brief Notify the application core that the USART DMA transmit finished.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
#define STM32_DAC_MAX_CODE 4095u
|
|
||||||
|
|
||||||
void stm32_dac_output_init(void)
|
void stm32_dac_output_init(void)
|
||||||
{
|
{
|
||||||
GPIO_InitTypeDef gpio_init = {0};
|
GPIO_InitTypeDef gpio_init = {0};
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define STM32_DAC_MAX_CODE 4095u
|
||||||
|
|
||||||
void stm32_dac_output_init(void);
|
void stm32_dac_output_init(void);
|
||||||
void stm32_dac_output_set(uint16_t dac_code, uint8_t enabled);
|
void stm32_dac_output_set(uint16_t dac_code, uint8_t enabled);
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,7 @@
|
|||||||
|
|
||||||
/** @brief UART header for TEC drive modulation control packets. */
|
/** @brief UART header for TEC drive modulation control packets. */
|
||||||
#define APP_PACKET_HEADER_TEC_MODULATION_CONTROL 0xF0F0u
|
#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. */
|
/** @brief Packet size of the host work-configuration message, in bytes. */
|
||||||
#define APP_PACKET_BYTES_WORK_CONFIG 30u
|
#define APP_PACKET_BYTES_WORK_CONFIG 30u
|
||||||
@@ -93,6 +94,7 @@
|
|||||||
|
|
||||||
/** @brief Packet size of profile-save data messages, in bytes. */
|
/** @brief Packet size of profile-save data messages, in bytes. */
|
||||||
#define APP_PACKET_BYTES_PROFILE_SAVE_DATA 30u
|
#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. */
|
/** @brief Number of payload words carried by the host work packet. */
|
||||||
#define APP_PACKET_WORDS_WORK_CONFIG 14u
|
#define APP_PACKET_WORDS_WORK_CONFIG 14u
|
||||||
@@ -108,6 +110,7 @@
|
|||||||
|
|
||||||
/** @brief Number of payload words carried by profile-save data packets. */
|
/** @brief Number of payload words carried by profile-save data packets. */
|
||||||
#define APP_PACKET_WORDS_PROFILE_SAVE_DATA 14u
|
#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. */
|
/** @brief First status byte flag for SD-card related errors. */
|
||||||
#define APP_STATUS_FLAG_SD_ERROR 0x01u
|
#define APP_STATUS_FLAG_SD_ERROR 0x01u
|
||||||
@@ -170,7 +173,8 @@
|
|||||||
#define APP_TEC_MODULATION_MAX_FREQUENCY_HZ 2000u
|
#define APP_TEC_MODULATION_MAX_FREQUENCY_HZ 2000u
|
||||||
|
|
||||||
/** @brief Highest accepted peak modulation amplitude in external TEC DAC codes. */
|
/** @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. */
|
/** @brief Op-code used to begin a custom AD9102 waveform upload. */
|
||||||
#define APP_AD9102_WAVE_OPCODE_BEGIN 0x0001u
|
#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_CONTROL,
|
||||||
APP_PACKET_KIND_AD9102_WAVE_DATA,
|
APP_PACKET_KIND_AD9102_WAVE_DATA,
|
||||||
APP_PACKET_KIND_PROFILE_SAVE_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;
|
} app_packet_kind_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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_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_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_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)
|
static const app_packet_descriptor_t *app_uart_protocol_find_descriptor(uint16_t header)
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* @file sweep.c
|
||||||
|
* @brief Periodic staircase sweep state machine.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sweep.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -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 <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 */
|
||||||
+2
-2
@@ -629,8 +629,8 @@ TIM11.Prescaler=1
|
|||||||
TIM11.Pulse=91
|
TIM11.Pulse=91
|
||||||
TIM2.ClockDivision=TIM_CLOCKDIVISION_DIV1
|
TIM2.ClockDivision=TIM_CLOCKDIVISION_DIV1
|
||||||
TIM2.IPParameters=Period,ClockDivision,Prescaler
|
TIM2.IPParameters=Period,ClockDivision,Prescaler
|
||||||
TIM2.Period=840000
|
TIM2.Period=9
|
||||||
TIM2.Prescaler=1000
|
TIM2.Prescaler=91
|
||||||
TIM4.Channel-PWM\ Generation3\ CH3=TIM_CHANNEL_3
|
TIM4.Channel-PWM\ Generation3\ CH3=TIM_CHANNEL_3
|
||||||
TIM4.IPParameters=Channel-PWM Generation3 CH3,Period,Pulse-PWM Generation3 CH3
|
TIM4.IPParameters=Channel-PWM Generation3 CH3,Period,Pulse-PWM Generation3 CH3
|
||||||
TIM4.Period=45
|
TIM4.Period=45
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ App/Services/storage_sd.c \
|
|||||||
App/Services/profile_repository.c \
|
App/Services/profile_repository.c \
|
||||||
App/Services/profile_storage.c \
|
App/Services/profile_storage.c \
|
||||||
App/Services/ui_status.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.c \
|
||||||
Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_adc_ex.c \
|
Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_adc_ex.c \
|
||||||
Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_rcc.c \
|
Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_rcc.c \
|
||||||
|
|||||||
+2
-2
@@ -658,9 +658,9 @@ static void MX_TIM2_Init(void)
|
|||||||
/* USER CODE BEGIN TIM2_Init 1 */
|
/* USER CODE BEGIN TIM2_Init 1 */
|
||||||
|
|
||||||
/* USER CODE END 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.CounterMode = LL_TIM_COUNTERMODE_UP;
|
||||||
TIM_InitStruct.Autoreload = 840000;
|
TIM_InitStruct.Autoreload = 9;
|
||||||
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
|
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
|
||||||
LL_TIM_Init(TIM2, &TIM_InitStruct);
|
LL_TIM_Init(TIM2, &TIM_InitStruct);
|
||||||
LL_TIM_DisableARRPreload(TIM2);
|
LL_TIM_DisableARRPreload(TIM2);
|
||||||
|
|||||||
+5
-1
@@ -246,7 +246,11 @@ void TIM2_IRQHandler(void)
|
|||||||
|
|
||||||
/* USER CODE END TIM2_IRQn 0 */
|
/* USER CODE END TIM2_IRQn 0 */
|
||||||
/* USER CODE BEGIN TIM2_IRQn 1 */
|
/* 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 */
|
/* USER CODE END TIM2_IRQn 1 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user