2 Commits

Author SHA1 Message Date
d32db245fa Stabilize USB control firmware 2026-04-26 18:39:55 +03:00
ea1fbb071d big refactoring and features added 2026-04-24 16:51:15 +03:00
184 changed files with 35440 additions and 75480 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
build/

File diff suppressed because one or more lines are too long

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>RadioPhotonic_PCB_software</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
</buildSpec>
<natures>
</natures>
</projectDescription>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>For_stm32_2023_12_08</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
</buildSpec>
<natures>
</natures>
</projectDescription>

1475
App/Core/app_core.c Normal file

File diff suppressed because it is too large Load Diff

61
App/Core/app_core.h Normal file
View File

@ -0,0 +1,61 @@
/**
* @file app_core.h
* @brief Top-level application runtime and IRQ entry points.
*/
#ifndef APP_CORE_H
#define APP_CORE_H
#include <stdint.h>
#include "app_types.h"
/**
* @brief Initialise the modular application layer after CubeMX peripherals are ready.
*/
void app_init(void);
/**
* @brief Execute one non-blocking iteration of the top-level application state machine.
*/
void app_run_once(void);
/**
* @brief Post a deferred application event from an ISR, button handler, or future UI backend.
*
* The event is latched and consumed inside `app_run_once()` so hardware-facing
* interrupt adapters stay thin and the main runtime keeps ownership of mode
* transitions and safe-stop sequencing.
*
* @param event Event to enqueue for the application core.
*/
void app_post_event(app_event_t event);
/**
* @brief Forward one raw UART byte into the transport-agnostic protocol parser.
*
* @param byte Newly received UART byte.
*/
void app_on_uart_byte(uint8_t byte);
/**
* @brief Notify the application core about a UART framing/parity/overrun error.
*/
void app_on_uart_error(void);
/**
* @brief Notify the application core about the 10 ms housekeeping tick.
*/
void app_on_tim6_tick(void);
/**
* @brief Notify the application core about the 1 ms timebase tick.
*/
void app_on_tim7_tick(void);
/**
* @brief Notify the application core that the USART DMA transmit finished.
*/
void app_on_dma_tx_complete(void);
#endif /* APP_CORE_H */

716
App/Devices/ad9102_device.c Normal file
View File

@ -0,0 +1,716 @@
/**
* @file ad9102_device.c
* @brief AD9102 waveform-generation device driver.
*/
#include "ad9102_device.h"
#include "board_io.h"
#include "main.h"
/* AD9102 register map used by the existing firmware. See ad9102.pdf. */
#define AD9102_REG_SPICONFIG 0x0000u
#define AD9102_REG_POWERCONFIG 0x0001u
#define AD9102_REG_CLOCKCONFIG 0x0002u
#define AD9102_REG_RAMUPDATE 0x001Du
#define AD9102_REG_PAT_STATUS 0x001Eu
#define AD9102_REG_PAT_TYPE 0x001Fu
#define AD9102_REG_WAV_CONFIG 0x0027u
#define AD9102_REG_PAT_TIMEBASE 0x0028u
#define AD9102_REG_PAT_PERIOD 0x0029u
#define AD9102_REG_DAC_PAT 0x002Bu
#define AD9102_REG_SAW_CONFIG 0x0037u
#define AD9102_REG_START_DLY 0x005Cu
#define AD9102_REG_START_ADDR 0x005Du
#define AD9102_REG_STOP_ADDR 0x005Eu
#define AD9102_REG_CFG_ERROR 0x0060u
#define AD9102_REG_SRAM_DATA_BASE 0x6000u
#define AD9102_PAT_STATUS_RUN (1u << 0)
#define AD9102_SAW_TYPE_UP 0u
#define AD9102_SAW_TYPE_TRIANGLE 2u
#define AD9102_SAW_STEP_DEFAULT 1u
#define AD9102_PAT_PERIOD_BASE_DEFAULT 0x2u
#define AD9102_START_DELAY_BASE_DEFAULT 0x1u
#define AD9102_PAT_TIMEBASE_HOLD_DEFAULT 0x1u
#define AD9102_PAT_PERIOD_DEFAULT 0xFFFFu
#define AD9102_EX4_WAV_CONFIG 0x3212u
#define AD9102_EX4_SAW_CONFIG 0x0606u
#define AD9102_EX2_WAV_CONFIG 0x3030u
#define AD9102_EX2_DAC_PAT 0x0101u
#define AD9102_EX2_SAW_CONFIG 0x0200u
#define AD9102_SRAM_PAT_PERIOD_BASE_DEFAULT 0x1u
#define AD9102_SRAM_START_DELAY_BASE_DEFAULT 0x1u
#define AD9102_SRAM_START_DLY_DEFAULT 0x0000u
#define AD9102_SRAM_RAMP_MIN (-8192)
#define AD9102_SRAM_RAMP_MAX (8191)
#define AD9102_REG_COUNT 66u
#define AD9102_WAVE_MAX_CHUNK_SAMPLES 12u
static const uint16_t g_ad9102_reg_addr[AD9102_REG_COUNT] = {
0x0000u, 0x0001u, 0x0002u, 0x0003u, 0x0004u, 0x0005u, 0x0006u, 0x0007u,
0x0008u, 0x0009u, 0x000Au, 0x000Bu, 0x000Cu, 0x000Du, 0x000Eu, 0x001Fu,
0x0020u, 0x0022u, 0x0023u, 0x0024u, 0x0025u, 0x0026u, 0x0027u, 0x0028u,
0x0029u, 0x002Au, 0x002Bu, 0x002Cu, 0x002Du, 0x002Eu, 0x002Fu, 0x0030u,
0x0031u, 0x0032u, 0x0033u, 0x0034u, 0x0035u, 0x0036u, 0x0037u, 0x003Eu,
0x003Fu, 0x0040u, 0x0041u, 0x0042u, 0x0043u, 0x0044u, 0x0045u, 0x0047u,
0x0050u, 0x0051u, 0x0052u, 0x0053u, 0x0054u, 0x0055u, 0x0056u, 0x0057u,
0x0058u, 0x0059u, 0x005Au, 0x005Bu, 0x005Cu, 0x005Du, 0x005Eu, 0x005Fu,
0x001Eu, 0x001Du
};
static const uint16_t g_ad9102_example4_regval[AD9102_REG_COUNT] = {
0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x4000u,
0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x1F00u, 0x0000u, 0x0000u, 0x0000u,
0x000Eu, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x3212u, 0x0121u,
0xFFFFu, 0x0000u, 0x0101u, 0x0003u, 0x0000u, 0x0000u, 0x0000u, 0x0000u,
0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x4000u, 0x0000u, 0x0606u, 0x1999u,
0x9A00u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u,
0x0FA0u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u,
0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x16FFu,
0x0001u, 0x0001u
};
static const uint16_t g_ad9102_example2_regval[AD9102_REG_COUNT] = {
0x0000u, 0x0E00u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x4000u,
0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x1F00u, 0x0000u, 0x0000u, 0x0000u,
0x000Eu, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x3030u, 0x0111u,
0xFFFFu, 0x0000u, 0x0101u, 0x0003u, 0x0000u, 0x0000u, 0x0000u, 0x0000u,
0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x4000u, 0x0000u, 0x0200u, 0x0000u,
0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u,
0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0000u,
0x0000u, 0x0000u, 0x0000u, 0x0000u, 0x0FA0u, 0x0000u, 0x3FF0u, 0x0100u,
0x0001u, 0x0001u
};
typedef struct ad9102_upload_state_t {
uint8_t active;
uint16_t expected_samples;
uint16_t written_samples;
} ad9102_upload_state_t;
static ad9102_upload_state_t g_upload_state;
static void ad9102_write_reg(uint16_t address, uint16_t value)
{
uint16_t command = (uint16_t)(address & 0x7FFFu);
uint32_t timeout = 0u;
board_io_configure_spi2_mode(LL_SPI_POLARITY_LOW, LL_SPI_PHASE_1EDGE);
HAL_GPIO_WritePin(DAC_LD1_CS_GPIO_Port, DAC_LD1_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(DAC_TEC1_CS_GPIO_Port, DAC_TEC1_CS_Pin, GPIO_PIN_SET);
if (!LL_SPI_IsEnabled(SPI2))
{
LL_SPI_Enable(SPI2);
}
HAL_GPIO_WritePin(AD9102_CS_GPIO_Port, AD9102_CS_Pin, GPIO_PIN_RESET);
while ((!LL_SPI_IsActiveFlag_TXE(SPI2)) && (timeout++ < 1000u))
{
}
LL_SPI_TransmitData16(SPI2, command);
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_RXNE(SPI2)) && (timeout++ < 1000u))
{
}
(void)SPI2->DR;
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_TXE(SPI2)) && (timeout++ < 1000u))
{
}
LL_SPI_TransmitData16(SPI2, value);
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_RXNE(SPI2)) && (timeout++ < 1000u))
{
}
(void)SPI2->DR;
HAL_GPIO_WritePin(AD9102_CS_GPIO_Port, AD9102_CS_Pin, GPIO_PIN_SET);
}
static uint16_t ad9102_read_reg(uint16_t address)
{
uint16_t command = (uint16_t)(0x8000u | (address & 0x7FFFu));
uint16_t value;
uint32_t timeout = 0u;
board_io_configure_spi2_mode(LL_SPI_POLARITY_LOW, LL_SPI_PHASE_1EDGE);
HAL_GPIO_WritePin(DAC_LD1_CS_GPIO_Port, DAC_LD1_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(DAC_TEC1_CS_GPIO_Port, DAC_TEC1_CS_Pin, GPIO_PIN_SET);
if (!LL_SPI_IsEnabled(SPI2))
{
LL_SPI_Enable(SPI2);
}
HAL_GPIO_WritePin(AD9102_CS_GPIO_Port, AD9102_CS_Pin, GPIO_PIN_RESET);
while ((!LL_SPI_IsActiveFlag_TXE(SPI2)) && (timeout++ < 1000u))
{
}
LL_SPI_TransmitData16(SPI2, command);
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_RXNE(SPI2)) && (timeout++ < 1000u))
{
}
(void)SPI2->DR;
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_TXE(SPI2)) && (timeout++ < 1000u))
{
}
LL_SPI_TransmitData16(SPI2, 0x0000u);
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_RXNE(SPI2)) && (timeout++ < 1000u))
{
}
value = LL_SPI_ReceiveData16(SPI2);
HAL_GPIO_WritePin(AD9102_CS_GPIO_Port, AD9102_CS_Pin, GPIO_PIN_SET);
return value;
}
static void ad9102_write_reg_table(const uint16_t *values, uint16_t count)
{
uint16_t index;
for (index = 0u; index < count; ++index)
{
ad9102_write_reg(g_ad9102_reg_addr[index], values[index]);
}
}
static void ad9102_reset_upload_state(void)
{
g_upload_state.active = 0u;
g_upload_state.expected_samples = 0u;
g_upload_state.written_samples = 0u;
}
static void ad9102_start_output(void)
{
HAL_GPIO_WritePin(AD9102_TRIG_GPIO_Port, AD9102_TRIG_Pin, GPIO_PIN_SET);
ad9102_write_reg(AD9102_REG_PAT_STATUS, AD9102_PAT_STATUS_RUN);
ad9102_write_reg(AD9102_REG_RAMUPDATE, 0x0001u);
for (volatile uint32_t delay_counter = 0u; delay_counter < 1000u; ++delay_counter)
{
}
HAL_GPIO_WritePin(AD9102_TRIG_GPIO_Port, AD9102_TRIG_Pin, GPIO_PIN_RESET);
}
static void ad9102_configure_sram_playback(uint16_t sample_count, uint8_t hold_cycles)
{
uint16_t pat_timebase;
uint32_t pat_period;
if (sample_count < 2u)
{
sample_count = 2u;
}
if (sample_count > AD9102_SRAM_MAX_SAMPLE_COUNT)
{
sample_count = AD9102_SRAM_MAX_SAMPLE_COUNT;
}
if (hold_cycles == 0u)
{
hold_cycles = AD9102_SRAM_DEFAULT_HOLD;
}
if (hold_cycles > 0x0Fu)
{
hold_cycles = 0x0Fu;
}
pat_timebase = (uint16_t)(((uint16_t)(hold_cycles & 0x0Fu) << 8) |
((AD9102_SRAM_PAT_PERIOD_BASE_DEFAULT & 0x0Fu) << 4) |
(AD9102_SRAM_START_DELAY_BASE_DEFAULT & 0x0Fu));
pat_period = (uint32_t)sample_count * (uint32_t)(hold_cycles & 0x0Fu);
if (pat_period == 0u)
{
pat_period = sample_count;
}
if (pat_period > 0xFFFFu)
{
pat_period = 0xFFFFu;
}
ad9102_write_reg_table(g_ad9102_example2_regval, AD9102_REG_COUNT);
ad9102_stop_output();
ad9102_write_reg(AD9102_REG_WAV_CONFIG, AD9102_EX2_WAV_CONFIG);
ad9102_write_reg(AD9102_REG_SAW_CONFIG, AD9102_EX2_SAW_CONFIG);
ad9102_write_reg(AD9102_REG_DAC_PAT, AD9102_EX2_DAC_PAT);
ad9102_write_reg(AD9102_REG_PAT_TIMEBASE, pat_timebase);
ad9102_write_reg(AD9102_REG_PAT_PERIOD, (uint16_t)pat_period);
ad9102_write_reg(AD9102_REG_PAT_TYPE, 0x0000u);
ad9102_write_reg(AD9102_REG_START_DLY, AD9102_SRAM_START_DLY_DEFAULT);
ad9102_write_reg(AD9102_REG_START_ADDR, 0x0000u);
ad9102_write_reg(AD9102_REG_STOP_ADDR, (uint16_t)((sample_count - 1u) << 4));
ad9102_write_reg(AD9102_REG_RAMUPDATE, 0x0001u);
}
static void ad9102_load_sram_ramp(uint16_t sample_count, uint8_t triangle_mode, uint16_t amplitude)
{
uint16_t sample_index;
if (sample_count < 2u)
{
sample_count = 2u;
}
if (sample_count > AD9102_SRAM_MAX_SAMPLE_COUNT)
{
sample_count = AD9102_SRAM_MAX_SAMPLE_COUNT;
}
if (amplitude > AD9102_SRAM_DEFAULT_AMPLITUDE)
{
amplitude = AD9102_SRAM_DEFAULT_AMPLITUDE;
}
ad9102_write_reg(AD9102_REG_PAT_STATUS, 0x0004u);
for (sample_index = 0u; sample_index < sample_count; ++sample_index)
{
int32_t value;
int32_t min_value = -(int32_t)amplitude;
int32_t max_value = (int32_t)amplitude;
int32_t span = max_value - min_value;
if (triangle_mode != 0u)
{
uint16_t half = sample_count / 2u;
if (half == 0u)
{
half = 1u;
}
if (sample_index < half)
{
uint16_t denominator = (half > 1u) ? (uint16_t)(half - 1u) : 1u;
value = min_value + (span * (int32_t)sample_index) / (int32_t)denominator;
}
else
{
uint16_t tail = (uint16_t)(sample_count - half);
uint16_t denominator = (tail > 1u) ? (uint16_t)(tail - 1u) : 1u;
value = max_value - (span * (int32_t)(sample_index - half)) / (int32_t)denominator;
}
}
else
{
uint16_t denominator = (sample_count > 1u) ? (uint16_t)(sample_count - 1u) : 1u;
value = min_value + (span * (int32_t)sample_index) / (int32_t)denominator;
}
if (value < AD9102_SRAM_RAMP_MIN)
{
value = AD9102_SRAM_RAMP_MIN;
}
else if (value > AD9102_SRAM_RAMP_MAX)
{
value = AD9102_SRAM_RAMP_MAX;
}
ad9102_write_reg((uint16_t)(AD9102_REG_SRAM_DATA_BASE + sample_index),
(uint16_t)(((uint16_t)((int16_t)value) & 0x3FFFu) << 2));
}
ad9102_write_reg(AD9102_REG_PAT_STATUS, 0x0000u);
}
void ad9102_init(void)
{
HAL_GPIO_WritePin(AD9102_CS_GPIO_Port, AD9102_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(AD9102_RESET_GPIO_Port, AD9102_RESET_Pin, GPIO_PIN_RESET);
for (volatile uint32_t delay_counter = 0u; delay_counter < 1000u; ++delay_counter)
{
}
HAL_GPIO_WritePin(AD9102_RESET_GPIO_Port, AD9102_RESET_Pin, GPIO_PIN_SET);
ad9102_write_reg_table(g_ad9102_example4_regval, AD9102_REG_COUNT);
ad9102_write_reg(AD9102_REG_PAT_STATUS, 0x0000u);
ad9102_write_reg(AD9102_REG_RAMUPDATE, 0x0001u);
HAL_GPIO_WritePin(AD9102_TRIG_GPIO_Port, AD9102_TRIG_Pin, GPIO_PIN_SET);
ad9102_reset_upload_state();
}
void ad9102_stop_output(void)
{
ad9102_write_reg(AD9102_REG_PAT_STATUS, 0x0000u);
HAL_GPIO_WritePin(AD9102_TRIG_GPIO_Port, AD9102_TRIG_Pin, GPIO_PIN_SET);
}
uint16_t ad9102_apply_saw(uint8_t saw_type,
uint8_t enabled,
uint8_t saw_step,
uint8_t pat_period_base,
uint16_t pat_period)
{
uint16_t saw_config;
uint16_t pat_timebase;
ad9102_reset_upload_state();
if (enabled == 0u)
{
ad9102_stop_output();
return ad9102_read_reg(AD9102_REG_PAT_STATUS);
}
if (saw_step == 0u)
{
saw_step = AD9102_SAW_STEP_DEFAULT;
}
else if (saw_step > 63u)
{
saw_step = 63u;
}
if (pat_period == 0u)
{
pat_period = AD9102_PAT_PERIOD_DEFAULT;
}
saw_config = (uint16_t)(((uint16_t)(saw_step & 0x3Fu) << 2) | ((uint16_t)(saw_type & 0x3u)));
pat_timebase = (uint16_t)(((AD9102_PAT_TIMEBASE_HOLD_DEFAULT & 0x0Fu) << 8) |
((pat_period_base & 0x0Fu) << 4) |
(AD9102_START_DELAY_BASE_DEFAULT & 0x0Fu));
ad9102_write_reg(AD9102_REG_WAV_CONFIG, AD9102_EX4_WAV_CONFIG);
ad9102_write_reg(AD9102_REG_SAW_CONFIG, saw_config);
ad9102_write_reg(AD9102_REG_PAT_TIMEBASE, pat_timebase);
ad9102_write_reg(AD9102_REG_PAT_PERIOD, pat_period);
ad9102_write_reg(AD9102_REG_PAT_TYPE, 0x0000u);
ad9102_start_output();
return ad9102_read_reg(AD9102_REG_PAT_STATUS);
}
uint16_t ad9102_apply_generated_sram(uint8_t enabled,
uint16_t sample_count,
uint8_t hold_cycles,
uint8_t triangle_mode,
uint16_t amplitude)
{
ad9102_reset_upload_state();
if (sample_count == 0u)
{
sample_count = AD9102_SRAM_DEFAULT_SAMPLE_COUNT;
}
if (sample_count < 2u)
{
sample_count = 2u;
}
if (sample_count > AD9102_SRAM_MAX_SAMPLE_COUNT)
{
sample_count = AD9102_SRAM_MAX_SAMPLE_COUNT;
}
if (hold_cycles == 0u)
{
hold_cycles = AD9102_SRAM_DEFAULT_HOLD;
}
if (hold_cycles > 0x0Fu)
{
hold_cycles = 0x0Fu;
}
if (amplitude > AD9102_SRAM_DEFAULT_AMPLITUDE)
{
amplitude = AD9102_SRAM_DEFAULT_AMPLITUDE;
}
ad9102_configure_sram_playback(sample_count, hold_cycles);
ad9102_load_sram_ramp(sample_count, triangle_mode, amplitude);
if (enabled != 0u)
{
ad9102_start_output();
}
else
{
ad9102_stop_output();
}
return ad9102_read_reg(AD9102_REG_PAT_STATUS);
}
bool ad9102_begin_custom_upload(uint16_t sample_count)
{
if ((sample_count < 2u) || (sample_count > AD9102_SRAM_MAX_SAMPLE_COUNT))
{
return false;
}
ad9102_stop_output();
ad9102_reset_upload_state();
ad9102_configure_sram_playback(sample_count, AD9102_SRAM_DEFAULT_HOLD);
ad9102_write_reg(AD9102_REG_PAT_STATUS, 0x0004u);
g_upload_state.expected_samples = sample_count;
g_upload_state.written_samples = 0u;
g_upload_state.active = 1u;
return true;
}
bool ad9102_write_custom_chunk(const uint16_t *samples, uint16_t chunk_count)
{
uint16_t index;
if ((samples == NULL) || (g_upload_state.active == 0u))
{
return false;
}
if ((chunk_count == 0u) || (chunk_count > AD9102_WAVE_MAX_CHUNK_SAMPLES))
{
return false;
}
if (((uint32_t)g_upload_state.written_samples + (uint32_t)chunk_count) >
(uint32_t)g_upload_state.expected_samples)
{
return false;
}
for (index = 0u; index < chunk_count; ++index)
{
int16_t sample = (int16_t)samples[index];
if ((sample < AD9102_SRAM_RAMP_MIN) || (sample > AD9102_SRAM_RAMP_MAX))
{
return false;
}
ad9102_write_reg((uint16_t)(AD9102_REG_SRAM_DATA_BASE + g_upload_state.written_samples + index),
(uint16_t)(((uint16_t)sample & 0x3FFFu) << 2));
}
g_upload_state.written_samples = (uint16_t)(g_upload_state.written_samples + chunk_count);
return true;
}
uint16_t ad9102_commit_custom_upload(bool *out_ok)
{
uint16_t pat_status;
if (out_ok != NULL)
{
*out_ok = false;
}
if ((g_upload_state.active == 0u) ||
(g_upload_state.expected_samples < 2u) ||
(g_upload_state.written_samples != g_upload_state.expected_samples))
{
ad9102_cancel_custom_upload();
return ad9102_read_reg(AD9102_REG_PAT_STATUS);
}
ad9102_write_reg(AD9102_REG_PAT_STATUS, 0x0000u);
ad9102_write_reg(AD9102_REG_START_ADDR, 0x0000u);
ad9102_write_reg(AD9102_REG_STOP_ADDR, (uint16_t)((g_upload_state.expected_samples - 1u) << 4));
ad9102_write_reg(AD9102_REG_RAMUPDATE, 0x0001u);
ad9102_start_output();
pat_status = ad9102_read_reg(AD9102_REG_PAT_STATUS);
ad9102_reset_upload_state();
if (out_ok != NULL)
{
*out_ok = true;
}
return pat_status;
}
void ad9102_cancel_custom_upload(void)
{
if (g_upload_state.active != 0u)
{
ad9102_stop_output();
}
ad9102_reset_upload_state();
}
uint8_t ad9102_check_saw_configuration(uint16_t pat_status,
uint8_t expect_run,
uint8_t saw_type,
uint8_t saw_step,
uint8_t pat_period_base,
uint16_t pat_period)
{
uint16_t spiconfig = ad9102_read_reg(AD9102_REG_SPICONFIG);
uint16_t power_config = ad9102_read_reg(AD9102_REG_POWERCONFIG);
uint16_t clock_config = ad9102_read_reg(AD9102_REG_CLOCKCONFIG);
uint16_t config_error = ad9102_read_reg(AD9102_REG_CFG_ERROR);
uint16_t pat_timebase = (uint16_t)(((AD9102_PAT_TIMEBASE_HOLD_DEFAULT & 0x0Fu) << 8) |
((pat_period_base & 0x0Fu) << 4) |
(AD9102_START_DELAY_BASE_DEFAULT & 0x0Fu));
uint16_t expected_saw;
uint8_t ok = 1u;
if (saw_step == 0u)
{
saw_step = AD9102_SAW_STEP_DEFAULT;
}
if (saw_step > 63u)
{
saw_step = 63u;
}
if (pat_period == 0u)
{
pat_period = AD9102_PAT_PERIOD_DEFAULT;
}
expected_saw = (uint16_t)(((uint16_t)(saw_step & 0x3Fu) << 2) | ((uint16_t)(saw_type & 0x3u)));
if (spiconfig != 0x0000u)
{
ok = 0u;
}
if (power_config & ((1u << 8) | (1u << 7) | (1u << 6) | (1u << 5) | (1u << 3)))
{
ok = 0u;
}
if (clock_config & ((1u << 11) | (1u << 7) | (1u << 6) | (1u << 5)))
{
ok = 0u;
}
if (config_error & 0x003Fu)
{
ok = 0u;
}
if ((expect_run != 0u) && ((pat_status & AD9102_PAT_STATUS_RUN) == 0u))
{
ok = 0u;
}
if (ad9102_read_reg(AD9102_REG_WAV_CONFIG) != AD9102_EX4_WAV_CONFIG)
{
ok = 0u;
}
if (ad9102_read_reg(AD9102_REG_PAT_TIMEBASE) != pat_timebase)
{
ok = 0u;
}
if (ad9102_read_reg(AD9102_REG_PAT_PERIOD) != pat_period)
{
ok = 0u;
}
if (ad9102_read_reg(AD9102_REG_PAT_TYPE) != 0x0000u)
{
ok = 0u;
}
if (ad9102_read_reg(AD9102_REG_SAW_CONFIG) != expected_saw)
{
ok = 0u;
}
return (ok != 0u) ? 0u : 1u;
}
uint8_t ad9102_check_sram_configuration(uint16_t pat_status,
uint8_t expect_run,
uint16_t sample_count,
uint8_t hold_cycles)
{
uint16_t spiconfig = ad9102_read_reg(AD9102_REG_SPICONFIG);
uint16_t power_config = ad9102_read_reg(AD9102_REG_POWERCONFIG);
uint16_t clock_config = ad9102_read_reg(AD9102_REG_CLOCKCONFIG);
uint16_t config_error = ad9102_read_reg(AD9102_REG_CFG_ERROR);
uint16_t pat_timebase;
uint32_t pat_period;
uint16_t stop_address;
uint8_t ok = 1u;
if (sample_count == 0u)
{
sample_count = AD9102_SRAM_DEFAULT_SAMPLE_COUNT;
}
if (sample_count < 2u)
{
sample_count = 2u;
}
if (sample_count > AD9102_SRAM_MAX_SAMPLE_COUNT)
{
sample_count = AD9102_SRAM_MAX_SAMPLE_COUNT;
}
if (hold_cycles == 0u)
{
hold_cycles = AD9102_SRAM_DEFAULT_HOLD;
}
if (hold_cycles > 0x0Fu)
{
hold_cycles = 0x0Fu;
}
pat_timebase = (uint16_t)(((uint16_t)(hold_cycles & 0x0Fu) << 8) |
((AD9102_SRAM_PAT_PERIOD_BASE_DEFAULT & 0x0Fu) << 4) |
(AD9102_SRAM_START_DELAY_BASE_DEFAULT & 0x0Fu));
pat_period = (uint32_t)sample_count * (uint32_t)(hold_cycles & 0x0Fu);
if (pat_period == 0u)
{
pat_period = sample_count;
}
if (pat_period > 0xFFFFu)
{
pat_period = 0xFFFFu;
}
stop_address = (uint16_t)((sample_count - 1u) << 4);
if (spiconfig != 0x0000u)
{
ok = 0u;
}
if (power_config & ((1u << 8) | (1u << 7) | (1u << 6) | (1u << 5) | (1u << 3)))
{
ok = 0u;
}
if (clock_config & ((1u << 11) | (1u << 7) | (1u << 6) | (1u << 5)))
{
ok = 0u;
}
if (config_error & 0x003Fu)
{
ok = 0u;
}
if ((expect_run != 0u) && ((pat_status & AD9102_PAT_STATUS_RUN) == 0u))
{
ok = 0u;
}
if (ad9102_read_reg(AD9102_REG_WAV_CONFIG) != AD9102_EX2_WAV_CONFIG)
{
ok = 0u;
}
if (ad9102_read_reg(AD9102_REG_PAT_TIMEBASE) != pat_timebase)
{
ok = 0u;
}
if (ad9102_read_reg(AD9102_REG_PAT_PERIOD) != (uint16_t)pat_period)
{
ok = 0u;
}
if (ad9102_read_reg(AD9102_REG_PAT_TYPE) != 0x0000u)
{
ok = 0u;
}
if (ad9102_read_reg(AD9102_REG_START_ADDR) != 0x0000u)
{
ok = 0u;
}
if (ad9102_read_reg(AD9102_REG_STOP_ADDR) != stop_address)
{
ok = 0u;
}
if (ad9102_read_reg(AD9102_REG_DAC_PAT) != AD9102_EX2_DAC_PAT)
{
ok = 0u;
}
return (ok != 0u) ? 0u : 1u;
}

View File

@ -0,0 +1,52 @@
/**
* @file ad9102_device.h
* @brief AD9102 waveform-generation device driver.
*
* Architectural note:
* The driver owns all AD9102 register tables and safe mode switching rules.
* High-level services describe *what* waveform to apply, while this module
* owns the register-level details of *how* to enter, verify, and leave each
* mode without cross-coupling to UART or storage logic.
*/
#ifndef AD9102_DEVICE_H
#define AD9102_DEVICE_H
#include <stdbool.h>
#include <stdint.h>
#include "app_types.h"
#define AD9102_SRAM_DEFAULT_HOLD 1u
#define AD9102_SRAM_DEFAULT_AMPLITUDE 8191u
#define AD9102_SRAM_DEFAULT_SAMPLE_COUNT 16u
#define AD9102_SRAM_MAX_SAMPLE_COUNT 4096u
void ad9102_init(void);
void ad9102_stop_output(void);
uint16_t ad9102_apply_saw(uint8_t saw_type,
uint8_t enabled,
uint8_t saw_step,
uint8_t pat_period_base,
uint16_t pat_period);
uint16_t ad9102_apply_generated_sram(uint8_t enabled,
uint16_t sample_count,
uint8_t hold_cycles,
uint8_t triangle_mode,
uint16_t amplitude);
bool ad9102_begin_custom_upload(uint16_t sample_count);
bool ad9102_write_custom_chunk(const uint16_t *samples, uint16_t chunk_count);
uint16_t ad9102_commit_custom_upload(bool *out_ok);
void ad9102_cancel_custom_upload(void);
uint8_t ad9102_check_saw_configuration(uint16_t pat_status,
uint8_t expect_run,
uint8_t saw_type,
uint8_t saw_step,
uint8_t pat_period_base,
uint16_t pat_period);
uint8_t ad9102_check_sram_configuration(uint16_t pat_status,
uint8_t expect_run,
uint16_t sample_count,
uint8_t hold_cycles);
#endif /* AD9102_DEVICE_H */

View File

@ -0,0 +1,64 @@
/**
* @file ad9833_device.c
* @brief AD9833 waveform-generator control helpers.
*/
#include "ad9833_device.h"
#include "board_io.h"
#include "main.h"
static void ad9833_write_word(uint16_t word)
{
uint32_t timeout = 0u;
board_io_configure_spi2_mode(LL_SPI_POLARITY_HIGH, LL_SPI_PHASE_1EDGE);
HAL_GPIO_WritePin(AD9102_CS_GPIO_Port, AD9102_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(DAC_LD1_CS_GPIO_Port, DAC_LD1_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(DAC_TEC1_CS_GPIO_Port, DAC_TEC1_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(AD9833_CS_GPIO_Port, AD9833_CS_Pin, GPIO_PIN_RESET);
while ((!LL_SPI_IsActiveFlag_TXE(SPI2)) && (timeout++ < 1000u))
{
}
LL_SPI_TransmitData16(SPI2, word);
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_RXNE(SPI2)) && (timeout++ < 1000u))
{
}
(void)SPI2->DR;
HAL_GPIO_WritePin(AD9833_CS_GPIO_Port, AD9833_CS_Pin, GPIO_PIN_SET);
}
void ad9833_apply(uint8_t enabled, uint8_t triangle_mode, uint32_t frequency_word)
{
uint16_t control = 0x2000u;
uint16_t lsw;
uint16_t msw;
if (triangle_mode != 0u)
{
control |= 0x0002u;
}
control |= 0x0100u;
frequency_word &= 0x0FFFFFFFu;
lsw = (uint16_t)(0x4000u | (frequency_word & 0x3FFFu));
msw = (uint16_t)(0x4000u | ((frequency_word >> 14) & 0x3FFFu));
ad9833_write_word(control);
ad9833_write_word(lsw);
ad9833_write_word(msw);
ad9833_write_word(0xC000u);
if (enabled != 0u)
{
control &= (uint16_t)(~0x0100u);
}
ad9833_write_word(control);
}

View File

@ -0,0 +1,13 @@
/**
* @file ad9833_device.h
* @brief AD9833 waveform generator control helpers.
*/
#ifndef AD9833_DEVICE_H
#define AD9833_DEVICE_H
#include <stdint.h>
void ad9833_apply(uint8_t enabled, uint8_t triangle_mode, uint32_t frequency_word);
#endif /* AD9833_DEVICE_H */

151
App/Devices/adc_mux.c Normal file
View File

@ -0,0 +1,151 @@
/**
* @file adc_mux.c
* @brief External photodiode ADC and internal STM32 ADC helpers.
*/
#include "adc_mux.h"
#include "board_handles.h"
#include "main.h"
uint16_t adc_mux_read_external_channel(uint8_t channel_index)
{
uint16_t result = 0u;
uint32_t delay_counter;
HAL_GPIO_WritePin(SPI4_CNV_GPIO_Port, SPI4_CNV_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(SPI5_CNV_GPIO_Port, SPI5_CNV_Pin, GPIO_PIN_RESET);
for (delay_counter = 0u; delay_counter < 500u; ++delay_counter)
{
}
HAL_GPIO_WritePin(SPI4_CNV_GPIO_Port, SPI4_CNV_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(SPI5_CNV_GPIO_Port, SPI5_CNV_Pin, GPIO_PIN_SET);
for (delay_counter = 0u; delay_counter < 500u; ++delay_counter)
{
}
if (channel_index == 1u)
{
HAL_GPIO_WritePin(ADC_ThrLD1_CS_GPIO_Port, ADC_ThrLD1_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(ADC_MPD1_CS_GPIO_Port, ADC_MPD1_CS_Pin, GPIO_PIN_RESET);
for (delay_counter = 0u; delay_counter < 500u; ++delay_counter)
{
}
LL_SPI_Enable(SPI4);
delay_counter = 0u;
while ((!LL_SPI_IsActiveFlag_RXNE(SPI4)) && (delay_counter <= 1000u))
{
++delay_counter;
}
LL_SPI_Disable(SPI4);
while (delay_counter < 500u)
{
++delay_counter;
}
HAL_GPIO_WritePin(ADC_MPD1_CS_GPIO_Port, ADC_MPD1_CS_Pin, GPIO_PIN_SET);
result = LL_SPI_ReceiveData16(SPI4);
}
else if (channel_index == 2u)
{
HAL_GPIO_WritePin(ADC_ThrLD2_CS_GPIO_Port, ADC_ThrLD2_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(ADC_MPD2_CS_GPIO_Port, ADC_MPD2_CS_Pin, GPIO_PIN_RESET);
for (delay_counter = 0u; delay_counter < 500u; ++delay_counter)
{
}
LL_SPI_Enable(SPI5);
delay_counter = 0u;
while ((!LL_SPI_IsActiveFlag_RXNE(SPI5)) && (delay_counter <= 1000u))
{
++delay_counter;
}
LL_SPI_Disable(SPI5);
while (delay_counter < 500u)
{
++delay_counter;
}
HAL_GPIO_WritePin(ADC_MPD2_CS_GPIO_Port, ADC_MPD2_CS_Pin, GPIO_PIN_SET);
result = LL_SPI_ReceiveData16(SPI5);
}
else if (channel_index == 3u)
{
HAL_GPIO_WritePin(ADC_MPD1_CS_GPIO_Port, ADC_MPD1_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(ADC_ThrLD1_CS_GPIO_Port, ADC_ThrLD1_CS_Pin, GPIO_PIN_RESET);
for (delay_counter = 0u; delay_counter < 500u; ++delay_counter)
{
}
LL_SPI_Enable(SPI4);
delay_counter = 0u;
while ((!LL_SPI_IsActiveFlag_RXNE(SPI4)) && (delay_counter <= 1000u))
{
++delay_counter;
}
LL_SPI_Disable(SPI4);
while (delay_counter < 500u)
{
++delay_counter;
}
HAL_GPIO_WritePin(ADC_ThrLD1_CS_GPIO_Port, ADC_ThrLD1_CS_Pin, GPIO_PIN_SET);
result = LL_SPI_ReceiveData16(SPI4);
}
else if (channel_index == 4u)
{
HAL_GPIO_WritePin(ADC_MPD2_CS_GPIO_Port, ADC_MPD2_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(ADC_ThrLD2_CS_GPIO_Port, ADC_ThrLD2_CS_Pin, GPIO_PIN_RESET);
for (delay_counter = 0u; delay_counter < 500u; ++delay_counter)
{
}
LL_SPI_Enable(SPI5);
delay_counter = 0u;
while ((!LL_SPI_IsActiveFlag_RXNE(SPI5)) && (delay_counter <= 1000u))
{
++delay_counter;
}
LL_SPI_Disable(SPI5);
while (delay_counter < 500u)
{
++delay_counter;
}
HAL_GPIO_WritePin(ADC_ThrLD2_CS_GPIO_Port, ADC_ThrLD2_CS_Pin, GPIO_PIN_SET);
result = LL_SPI_ReceiveData16(SPI5);
}
return result;
}
uint16_t adc_mux_process_internal_adc_step(uint8_t step)
{
uint16_t output = 0u;
switch (step)
{
case 0u:
HAL_ADC_Start(&hadc1);
break;
case 1u:
HAL_ADC_PollForConversion(&hadc1, 100u);
output = HAL_ADC_GetValue(&hadc1);
break;
case 2u:
HAL_ADC_Stop(&hadc1);
break;
case 3u:
HAL_ADC_Start(&hadc3);
break;
case 4u:
HAL_ADC_PollForConversion(&hadc3, 100u);
output = HAL_ADC_GetValue(&hadc3);
break;
case 5u:
HAL_ADC_Stop(&hadc3);
break;
default:
break;
}
return output;
}

14
App/Devices/adc_mux.h Normal file
View File

@ -0,0 +1,14 @@
/**
* @file adc_mux.h
* @brief Helpers for external photodiode ADCs and internal STM32 ADC channels.
*/
#ifndef ADC_MUX_H
#define ADC_MUX_H
#include <stdint.h>
uint16_t adc_mux_read_external_channel(uint8_t channel_index);
uint16_t adc_mux_process_internal_adc_step(uint8_t step);
#endif /* ADC_MUX_H */

View File

@ -0,0 +1,22 @@
/**
* @file board_handles.h
* @brief Extern declarations for CubeMX-generated peripheral handles.
*
* Architectural note:
* Device and service modules depend on hardware handles owned by `Src/main.c`.
* This header isolates those extern declarations so the App layer never has to
* pull application types back into the CubeMX-generated file.
*/
#ifndef BOARD_HANDLES_H
#define BOARD_HANDLES_H
#include "main.h"
extern ADC_HandleTypeDef hadc1;
extern ADC_HandleTypeDef hadc3;
extern SD_HandleTypeDef hsd1;
extern TIM_HandleTypeDef htim1;
extern UART_HandleTypeDef huart8;
#endif /* BOARD_HANDLES_H */

257
App/Devices/board_io.c Normal file
View File

@ -0,0 +1,257 @@
/**
* @file board_io.c
* @brief Board-specific GPIO and shared low-level control helpers.
*/
#include "board_io.h"
#define UI_LCD_CONTRAST_PWM_FREQUENCY_HZ 20000u
#define UI_LCD_CONTRAST_PWM_PERIOD_COUNTS 1000u
#define UI_LCD_CONTRAST_PWM_DUTY_PERMILLE 300u
static TIM_HandleTypeDef g_ui_lcd_contrast_pwm_timer;
static uint32_t board_io_get_apb1_timer_clock_hz(void);
static void board_io_init_lcd_contrast_pwm(void);
void board_io_enable_uart_rx_irq(void)
{
LL_USART_EnableIT_PE(USART1);
LL_USART_EnableIT_RXNE(USART1);
LL_USART_EnableIT_ERROR(USART1);
NVIC_SetPriority(USART1_IRQn, 0);
NVIC_EnableIRQ(USART1_IRQn);
}
void board_io_init_standalone_ui(void)
{
GPIO_InitTypeDef gpio_init = {0};
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
HAL_GPIO_WritePin(UI_LCD_RS_GPIO_Port,
UI_LCD_RS_Pin | UI_LCD_E_Pin | UI_LCD_D4_Pin | UI_LCD_D5_Pin | UI_LCD_D6_Pin | UI_LCD_D7_Pin,
GPIO_PIN_RESET);
/*
* A fixed contrast pin is only a compromise until a potentiometer is
* added. Driving V0 low is the safest default for HD44780/SPLC780D-style
* modules because it makes characters visible instead of appearing blank.
*/
HAL_GPIO_WritePin(UI_LCD_V0_REF_GPIO_Port, UI_LCD_V0_REF_Pin, GPIO_PIN_RESET);
gpio_init.Pin = UI_LCD_RS_Pin | UI_LCD_E_Pin | UI_LCD_D4_Pin | UI_LCD_D5_Pin | UI_LCD_D6_Pin | UI_LCD_D7_Pin;
gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init.Pull = GPIO_NOPULL;
gpio_init.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOG, &gpio_init);
gpio_init.Pin = UI_LCD_V0_REF_Pin;
gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init.Pull = GPIO_NOPULL;
gpio_init.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(UI_LCD_V0_REF_GPIO_Port, &gpio_init);
gpio_init.Pin = UI_BUTTON_Pin;
gpio_init.Mode = GPIO_MODE_INPUT;
gpio_init.Pull = GPIO_PULLUP;
HAL_GPIO_Init(UI_BUTTON_GPIO_Port, &gpio_init);
board_io_init_lcd_contrast_pwm();
}
static uint32_t board_io_get_apb1_timer_clock_hz(void)
{
uint32_t pclk1_hz = HAL_RCC_GetPCLK1Freq();
if ((RCC->CFGR & RCC_CFGR_PPRE1) == RCC_CFGR_PPRE1_DIV1)
{
return pclk1_hz;
}
return pclk1_hz * 2u;
}
static void board_io_init_lcd_contrast_pwm(void)
{
GPIO_InitTypeDef gpio_init = {0};
TIM_OC_InitTypeDef pwm_channel = {0};
uint32_t timer_clock_hz = board_io_get_apb1_timer_clock_hz();
uint32_t prescaler_divisor = timer_clock_hz / (UI_LCD_CONTRAST_PWM_FREQUENCY_HZ * UI_LCD_CONTRAST_PWM_PERIOD_COUNTS);
uint32_t pulse_counts = ((UI_LCD_CONTRAST_PWM_PERIOD_COUNTS * UI_LCD_CONTRAST_PWM_DUTY_PERMILLE) + 999u) / 1000u;
if (prescaler_divisor == 0u)
{
prescaler_divisor = 1u;
}
if (pulse_counts >= UI_LCD_CONTRAST_PWM_PERIOD_COUNTS)
{
pulse_counts = UI_LCD_CONTRAST_PWM_PERIOD_COUNTS - 1u;
}
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_TIM4_CLK_ENABLE();
/*
* This PWM output is intended for the LCD contrast input (V0) through a
* simple RC low-pass filter. Keeping it local to board_io.c avoids
* spreading board wiring assumptions into higher-level UI code.
*/
gpio_init.Pin = UI_LCD_CONTRAST_PWM_Pin;
gpio_init.Mode = GPIO_MODE_AF_PP;
gpio_init.Pull = GPIO_NOPULL;
gpio_init.Speed = GPIO_SPEED_FREQ_LOW;
gpio_init.Alternate = GPIO_AF2_TIM4;
HAL_GPIO_Init(UI_LCD_CONTRAST_PWM_GPIO_Port, &gpio_init);
g_ui_lcd_contrast_pwm_timer.Instance = TIM4;
g_ui_lcd_contrast_pwm_timer.Init.Prescaler = (uint32_t)(prescaler_divisor - 1u);
g_ui_lcd_contrast_pwm_timer.Init.CounterMode = TIM_COUNTERMODE_UP;
g_ui_lcd_contrast_pwm_timer.Init.Period = UI_LCD_CONTRAST_PWM_PERIOD_COUNTS - 1u;
g_ui_lcd_contrast_pwm_timer.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
g_ui_lcd_contrast_pwm_timer.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_PWM_Init(&g_ui_lcd_contrast_pwm_timer) != HAL_OK)
{
return;
}
pwm_channel.OCMode = TIM_OCMODE_PWM1;
pwm_channel.Pulse = pulse_counts;
pwm_channel.OCPolarity = TIM_OCPOLARITY_HIGH;
pwm_channel.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&g_ui_lcd_contrast_pwm_timer, &pwm_channel, TIM_CHANNEL_3) != HAL_OK)
{
return;
}
(void)HAL_TIM_PWM_Start(&g_ui_lcd_contrast_pwm_timer, TIM_CHANNEL_3);
}
void board_io_reset_runtime_outputs(void)
{
HAL_GPIO_WritePin(EN_5V1_GPIO_Port, EN_5V1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(EN_5V2_GPIO_Port, EN_5V2_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LD1_EN_GPIO_Port, LD1_EN_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LD2_EN_GPIO_Port, LD2_EN_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(REF0_EN_GPIO_Port, REF0_EN_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(REF2_ON_GPIO_Port, REF2_ON_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(TECEN1_GPIO_Port, TECEN1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(TECEN2_GPIO_Port, TECEN2_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(TEC1_PD_GPIO_Port, TEC1_PD_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(TEC2_PD_GPIO_Port, TEC2_PD_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(ADC_MPD1_CS_GPIO_Port, ADC_MPD1_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(ADC_MPD2_CS_GPIO_Port, ADC_MPD2_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(SPI4_CNV_GPIO_Port, SPI4_CNV_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(SPI5_CNV_GPIO_Port, SPI5_CNV_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(DAC_LD1_CS_GPIO_Port, DAC_LD1_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(DAC_LD2_CS_GPIO_Port, DAC_LD2_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(DAC_TEC1_CS_GPIO_Port, DAC_TEC1_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(DAC_TEC2_CS_GPIO_Port, DAC_TEC2_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(AD9102_CS_GPIO_Port, AD9102_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(AD9833_CS_GPIO_Port, AD9833_CS_Pin, GPIO_PIN_SET);
}
void board_io_configure_spi2_mode(uint32_t polarity, uint32_t phase)
{
if (LL_SPI_IsEnabled(SPI2))
{
LL_SPI_Disable(SPI2);
}
LL_SPI_SetClockPolarity(SPI2, polarity);
LL_SPI_SetClockPhase(SPI2, phase);
if (!LL_SPI_IsEnabled(SPI2))
{
LL_SPI_Enable(SPI2);
}
}
bool board_io_is_usb_connected(void)
{
return HAL_GPIO_ReadPin(USB_FLAG_GPIO_Port, USB_FLAG_Pin) == GPIO_PIN_SET;
}
bool board_io_is_sd_card_present(void)
{
return HAL_GPIO_ReadPin(SDMMC1_EN_GPIO_Port, SDMMC1_EN_Pin) == GPIO_PIN_RESET;
}
bool board_io_is_standalone_ui_button_pressed(void)
{
return HAL_GPIO_ReadPin(UI_BUTTON_GPIO_Port, UI_BUTTON_Pin) == GPIO_PIN_RESET;
}
void board_io_set_supply_enabled(uint8_t supply_index, bool enabled)
{
GPIO_PinState pin_state = enabled ? GPIO_PIN_SET : GPIO_PIN_RESET;
if (supply_index == 1u)
{
HAL_GPIO_WritePin(EN_5V1_GPIO_Port, EN_5V1_Pin, pin_state);
}
else if (supply_index == 2u)
{
HAL_GPIO_WritePin(EN_5V2_GPIO_Port, EN_5V2_Pin, pin_state);
}
}
void board_io_set_laser_enabled(uint8_t laser_index, bool enabled)
{
GPIO_PinState pin_state = enabled ? GPIO_PIN_SET : GPIO_PIN_RESET;
if (laser_index == 1u)
{
HAL_GPIO_WritePin(LD1_EN_GPIO_Port, LD1_EN_Pin, pin_state);
}
else if (laser_index == 2u)
{
HAL_GPIO_WritePin(LD2_EN_GPIO_Port, LD2_EN_Pin, pin_state);
}
}
void board_io_set_reference_enabled(uint8_t reference_index, bool enabled)
{
GPIO_PinState pin_state = enabled ? GPIO_PIN_SET : GPIO_PIN_RESET;
if (reference_index == 1u)
{
HAL_GPIO_WritePin(REF0_EN_GPIO_Port, REF0_EN_Pin, pin_state);
}
else if (reference_index == 2u)
{
HAL_GPIO_WritePin(REF2_ON_GPIO_Port, REF2_ON_Pin, pin_state);
}
}
void board_io_set_tec_channel_enabled(uint8_t tec_index, bool enabled)
{
GPIO_PinState pin_state = enabled ? GPIO_PIN_SET : GPIO_PIN_RESET;
if (tec_index == 1u)
{
HAL_GPIO_WritePin(TEC1_PD_GPIO_Port, TEC1_PD_Pin, pin_state);
HAL_GPIO_WritePin(TECEN1_GPIO_Port, TECEN1_Pin, pin_state);
}
else if (tec_index == 2u)
{
HAL_GPIO_WritePin(TEC2_PD_GPIO_Port, TEC2_PD_Pin, pin_state);
HAL_GPIO_WritePin(TECEN2_GPIO_Port, TECEN2_Pin, pin_state);
}
}
void board_io_write_signal(GPIO_TypeDef *port, uint16_t pin, bool level_high)
{
HAL_GPIO_WritePin(port, pin, level_high ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
void board_io_toggle_debug_pin(void)
{
HAL_GPIO_TogglePin(TEST_01_GPIO_Port, TEST_01_Pin);
}

33
App/Devices/board_io.h Normal file
View File

@ -0,0 +1,33 @@
/**
* @file board_io.h
* @brief Board-specific GPIO and low-level control helpers.
*
* Architectural note:
* This module groups direct GPIO manipulations that are shared across several
* devices and services. Doing so keeps register-driving code localised and
* prevents high-level services from depending on scattered pin knowledge.
*/
#ifndef BOARD_IO_H
#define BOARD_IO_H
#include <stdbool.h>
#include <stdint.h>
#include "main.h"
void board_io_enable_uart_rx_irq(void);
void board_io_init_standalone_ui(void);
void board_io_reset_runtime_outputs(void);
void board_io_configure_spi2_mode(uint32_t polarity, uint32_t phase);
bool board_io_is_usb_connected(void);
bool board_io_is_sd_card_present(void);
bool board_io_is_standalone_ui_button_pressed(void);
void board_io_set_supply_enabled(uint8_t supply_index, bool enabled);
void board_io_set_laser_enabled(uint8_t laser_index, bool enabled);
void board_io_set_reference_enabled(uint8_t reference_index, bool enabled);
void board_io_set_tec_channel_enabled(uint8_t tec_index, bool enabled);
void board_io_toggle_debug_pin(void);
void board_io_write_signal(GPIO_TypeDef *port, uint16_t pin, bool level_high);
#endif /* BOARD_IO_H */

View File

@ -0,0 +1,83 @@
/**
* @file ds1809_device.c
* @brief DS1809 pulse-based control helper.
*/
#include "ds1809_device.h"
#include "main.h"
#define DS1809_POSITION_COUNT 64u
#define DS1809_CPU_PULSE_WIDTH_MS 2u
#define DS1809_CPU_INTER_PULSE_HIGH_MS 2u
#define DS1809_CONTROL_PORT_READY_DELAY_MS 10u
static void ds1809_drive_idle_high(void)
{
HAL_GPIO_WritePin(DS1809_UC_GPIO_Port, DS1809_UC_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(DS1809_DC_GPIO_Port, DS1809_DC_Pin, GPIO_PIN_SET);
}
static void ds1809_pulse_direction(uint8_t increment,
uint8_t decrement,
uint16_t count,
uint16_t low_time_ms,
uint16_t high_time_ms)
{
uint16_t pulse_index;
if ((count == 0u) || ((increment == 0u) && (decrement == 0u)))
{
ds1809_drive_idle_high();
return;
}
ds1809_drive_idle_high();
for (pulse_index = 0u; pulse_index < count; ++pulse_index)
{
if (increment != 0u)
{
HAL_GPIO_WritePin(DS1809_UC_GPIO_Port, DS1809_UC_Pin, GPIO_PIN_RESET);
}
if (decrement != 0u)
{
HAL_GPIO_WritePin(DS1809_DC_GPIO_Port, DS1809_DC_Pin, GPIO_PIN_RESET);
}
HAL_Delay(low_time_ms);
ds1809_drive_idle_high();
HAL_Delay(high_time_ms);
}
}
void ds1809_pulse(uint8_t increment, uint8_t decrement, uint16_t count, uint16_t pulse_ms)
{
if (pulse_ms < DS1809_CPU_PULSE_WIDTH_MS)
{
pulse_ms = DS1809_CPU_PULSE_WIDTH_MS;
}
ds1809_pulse_direction(increment, decrement, count, pulse_ms, pulse_ms);
}
void ds1809_apply_position_from_min(uint8_t position_from_min)
{
/*
* DS1809 CPU-driven timing requirements from the local datasheet:
* - a pulse must stay low for longer than 1 ms to count as a step;
* - repetitive pulses need at least 1 ms of high time between steps;
* - the control inputs are locked out for at least 10 ms after power-up.
*
* Use 2 ms low/high timing for margin, always drive to the RL end first,
* then walk back up to the requested absolute position.
*/
if (position_from_min >= DS1809_POSITION_COUNT)
{
position_from_min = (uint8_t)(DS1809_POSITION_COUNT - 1u);
}
HAL_Delay(DS1809_CONTROL_PORT_READY_DELAY_MS);
ds1809_pulse_direction(0u, 1u, DS1809_POSITION_COUNT, DS1809_CPU_PULSE_WIDTH_MS, DS1809_CPU_INTER_PULSE_HIGH_MS);
ds1809_pulse_direction(1u, 0u, position_from_min, DS1809_CPU_PULSE_WIDTH_MS, DS1809_CPU_INTER_PULSE_HIGH_MS);
}

View File

@ -0,0 +1,32 @@
/**
* @file ds1809_device.h
* @brief DS1809 pulse-based control helper.
*/
#ifndef DS1809_DEVICE_H
#define DS1809_DEVICE_H
#include <stdint.h>
/**
* @brief Send relative UC/DC pulses using the legacy serial-command semantics.
*
* @param increment Non-zero to pulse the UC pin.
* @param decrement Non-zero to pulse the DC pin.
* @param count Number of pulses to issue.
* @param pulse_ms Low time and inter-pulse high time, in milliseconds.
*/
void ds1809_pulse(uint8_t increment, uint8_t decrement, uint16_t count, uint16_t pulse_ms);
/**
* @brief Force the DS1809 to a known absolute position above the minimum tap.
*
* The helper first overdrives the wiper all the way down to the RL terminal and
* then steps back up to the requested tap. This makes the standalone profile
* path deterministic even though the DS1809 serial command itself is relative.
*
* @param position_from_min Number of upward steps above the minimum tap.
*/
void ds1809_apply_position_from_min(uint8_t position_from_min);
#endif /* DS1809_DEVICE_H */

103
App/Devices/laser_dac.c Normal file
View File

@ -0,0 +1,103 @@
/**
* @file laser_dac.c
* @brief External DAC access for laser-current and TEC channels.
*/
#include "laser_dac.h"
#include "board_io.h"
#include "main.h"
void laser_dac_write_channel(uint8_t channel, uint16_t value)
{
uint32_t timeout;
if ((channel == 1u) || (channel == 3u))
{
board_io_configure_spi2_mode(LL_SPI_POLARITY_HIGH, LL_SPI_PHASE_2EDGE);
HAL_GPIO_WritePin(AD9102_CS_GPIO_Port, AD9102_CS_Pin, GPIO_PIN_SET);
}
switch (channel)
{
case 1u:
HAL_GPIO_WritePin(DAC_LD1_CS_GPIO_Port, DAC_LD1_CS_Pin, GPIO_PIN_RESET);
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_TXE(SPI2)) && (timeout <= 500u))
{
++timeout;
}
LL_SPI_TransmitData16(SPI2, value);
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_RXNE(SPI2)) && (timeout <= 500u))
{
++timeout;
}
(void)SPI2->DR;
break;
case 2u:
if (!LL_SPI_IsEnabled(SPI6))
{
LL_SPI_Enable(SPI6);
}
HAL_GPIO_WritePin(DAC_LD2_CS_GPIO_Port, DAC_LD2_CS_Pin, GPIO_PIN_RESET);
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_TXE(SPI6)) && (timeout <= 500u))
{
++timeout;
}
LL_SPI_TransmitData16(SPI6, value);
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_RXNE(SPI6)) && (timeout <= 500u))
{
++timeout;
}
(void)SPI6->DR;
break;
case 3u:
HAL_GPIO_WritePin(DAC_TEC1_CS_GPIO_Port, DAC_TEC1_CS_Pin, GPIO_PIN_RESET);
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_TXE(SPI2)) && (timeout <= 500u))
{
++timeout;
}
LL_SPI_TransmitData16(SPI2, value);
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_RXNE(SPI2)) && (timeout <= 500u))
{
++timeout;
}
(void)SPI2->DR;
break;
case 4u:
if (!LL_SPI_IsEnabled(SPI6))
{
LL_SPI_Enable(SPI6);
}
HAL_GPIO_WritePin(DAC_TEC2_CS_GPIO_Port, DAC_TEC2_CS_Pin, GPIO_PIN_RESET);
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_TXE(SPI6)) && (timeout <= 500u))
{
++timeout;
}
LL_SPI_TransmitData16(SPI6, value);
timeout = 0u;
while ((!LL_SPI_IsActiveFlag_RXNE(SPI6)) && (timeout <= 500u))
{
++timeout;
}
(void)SPI6->DR;
break;
default:
break;
}
HAL_GPIO_WritePin(DAC_LD1_CS_GPIO_Port, DAC_LD1_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(DAC_LD2_CS_GPIO_Port, DAC_LD2_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(DAC_TEC1_CS_GPIO_Port, DAC_TEC1_CS_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(DAC_TEC2_CS_GPIO_Port, DAC_TEC2_CS_Pin, GPIO_PIN_SET);
}

13
App/Devices/laser_dac.h Normal file
View File

@ -0,0 +1,13 @@
/**
* @file laser_dac.h
* @brief Drivers for the external laser-current and TEC DAC channels.
*/
#ifndef LASER_DAC_H
#define LASER_DAC_H
#include <stdint.h>
void laser_dac_write_channel(uint8_t channel, uint16_t value);
#endif /* LASER_DAC_H */

View File

@ -0,0 +1,199 @@
/**
* @file lcd1602_display.c
* @brief Minimal SPLC780D/HD44780-compatible LCD1602 driver in 4-bit mode.
*
* Architectural note:
* The chosen wiring keeps the whole standalone UI on GPIOG. This driver uses
* write-only transfers with fixed delays, which keeps the implementation
* compact and avoids a bidirectional R/W pin or extra state.
*/
#include "lcd1602_display.h"
#include <stdbool.h>
#include <stddef.h>
#include "main.h"
#define LCD1602_COLUMNS 16u
#define LCD1602_LINE_1_ADDRESS 0x00u
#define LCD1602_LINE_2_ADDRESS 0x40u
#define LCD1602_COMMAND_DELAY_US 50u
#define LCD1602_CLEAR_DELAY_US 2000u
#define LCD1602_ENABLE_PULSE_US 1u
#define LCD1602_POWER_ON_DELAY_US 50000u
#define LCD1602_DATA_MASK (UI_LCD_D4_Pin | UI_LCD_D5_Pin | UI_LCD_D6_Pin | UI_LCD_D7_Pin)
static uint8_t g_cycle_counter_available = 0u;
static void lcd1602_enable_cycle_counter(void);
static void lcd1602_delay_us(uint32_t delay_us);
static void lcd1602_write_nibble(uint8_t nibble);
static void lcd1602_pulse_enable(void);
static void lcd1602_write_byte(bool is_data, uint8_t value);
static void lcd1602_write_command(uint8_t command);
static void lcd1602_write_data(uint8_t value);
static void lcd1602_set_cursor(uint8_t address);
static void lcd1602_write_padded_line(const char *text);
void lcd1602_display_init(void)
{
lcd1602_enable_cycle_counter();
HAL_GPIO_WritePin(UI_LCD_RS_GPIO_Port, UI_LCD_RS_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(UI_LCD_E_GPIO_Port, UI_LCD_E_Pin, GPIO_PIN_RESET);
UI_LCD_D4_GPIO_Port->BSRR = ((uint32_t)LCD1602_DATA_MASK) << 16u;
lcd1602_delay_us(LCD1602_POWER_ON_DELAY_US);
lcd1602_write_nibble(0x03u);
lcd1602_delay_us(5000u);
lcd1602_write_nibble(0x03u);
lcd1602_delay_us(150u);
lcd1602_write_nibble(0x03u);
lcd1602_delay_us(150u);
lcd1602_write_nibble(0x02u);
lcd1602_delay_us(LCD1602_COMMAND_DELAY_US);
lcd1602_write_command(0x28u);
lcd1602_write_command(0x08u);
lcd1602_write_command(0x01u);
lcd1602_write_command(0x06u);
lcd1602_write_command(0x0Cu);
}
void lcd1602_display_set_lines(const char *line1, const char *line2)
{
lcd1602_set_cursor(LCD1602_LINE_1_ADDRESS);
lcd1602_write_padded_line(line1);
lcd1602_set_cursor(LCD1602_LINE_2_ADDRESS);
lcd1602_write_padded_line(line2);
}
static void lcd1602_enable_cycle_counter(void)
{
uint32_t start_cycles;
uint32_t spin;
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0u;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
start_cycles = DWT->CYCCNT;
for (spin = 0u; spin < 64u; ++spin)
{
__NOP();
}
/*
* DWT-based microsecond delays are convenient, but they are not
* guaranteed to be available in every production boot/debug state.
* Falling back to HAL_Delay() keeps the LCD optional rather than
* letting the standalone UI block the whole firmware startup.
*/
g_cycle_counter_available = (DWT->CYCCNT != start_cycles) ? 1u : 0u;
}
static void lcd1602_delay_us(uint32_t delay_us)
{
if (delay_us == 0u)
{
return;
}
if (g_cycle_counter_available != 0u)
{
uint32_t start_cycles = DWT->CYCCNT;
uint32_t target_cycles = delay_us * (SystemCoreClock / 1000000u);
while ((DWT->CYCCNT - start_cycles) < target_cycles)
{
}
return;
}
while (delay_us > 1000u)
{
HAL_Delay(1u);
delay_us -= 1000u;
}
HAL_Delay(1u);
}
static void lcd1602_write_nibble(uint8_t nibble)
{
uint32_t bsrr = ((uint32_t)LCD1602_DATA_MASK) << 16u;
if ((nibble & 0x01u) != 0u)
{
bsrr |= UI_LCD_D4_Pin;
}
if ((nibble & 0x02u) != 0u)
{
bsrr |= UI_LCD_D5_Pin;
}
if ((nibble & 0x04u) != 0u)
{
bsrr |= UI_LCD_D6_Pin;
}
if ((nibble & 0x08u) != 0u)
{
bsrr |= UI_LCD_D7_Pin;
}
UI_LCD_D4_GPIO_Port->BSRR = bsrr;
lcd1602_pulse_enable();
}
static void lcd1602_pulse_enable(void)
{
HAL_GPIO_WritePin(UI_LCD_E_GPIO_Port, UI_LCD_E_Pin, GPIO_PIN_SET);
lcd1602_delay_us(LCD1602_ENABLE_PULSE_US);
HAL_GPIO_WritePin(UI_LCD_E_GPIO_Port, UI_LCD_E_Pin, GPIO_PIN_RESET);
lcd1602_delay_us(LCD1602_COMMAND_DELAY_US);
}
static void lcd1602_write_byte(bool is_data, uint8_t value)
{
HAL_GPIO_WritePin(UI_LCD_RS_GPIO_Port, UI_LCD_RS_Pin, is_data ? GPIO_PIN_SET : GPIO_PIN_RESET);
lcd1602_write_nibble((uint8_t)(value >> 4u));
lcd1602_write_nibble((uint8_t)(value & 0x0Fu));
}
static void lcd1602_write_command(uint8_t command)
{
lcd1602_write_byte(false, command);
if ((command == 0x01u) || (command == 0x02u))
{
lcd1602_delay_us(LCD1602_CLEAR_DELAY_US);
}
}
static void lcd1602_write_data(uint8_t value)
{
lcd1602_write_byte(true, value);
}
static void lcd1602_set_cursor(uint8_t address)
{
lcd1602_write_command((uint8_t)(0x80u | address));
}
static void lcd1602_write_padded_line(const char *text)
{
uint8_t index;
uint8_t value;
for (index = 0u; index < LCD1602_COLUMNS; ++index)
{
value = ' ';
if ((text != NULL) && (text[index] != '\0'))
{
value = (uint8_t)text[index];
}
lcd1602_write_data(value);
}
}

View File

@ -0,0 +1,19 @@
/**
* @file lcd1602_display.h
* @brief Minimal SPLC780D/HD44780-compatible LCD1602 driver in 4-bit mode.
*
* Architectural note:
* This module owns the LCD bus timing and character writes for the standalone
* front panel. High-level services format text elsewhere and call this driver
* only with already-prepared 16-character lines.
*/
#ifndef LCD1602_DISPLAY_H
#define LCD1602_DISPLAY_H
#include <stdint.h>
void lcd1602_display_init(void);
void lcd1602_display_set_lines(const char *line1, const char *line2);
#endif /* LCD1602_DISPLAY_H */

View File

@ -0,0 +1,45 @@
/**
* @file stm32_dac_output.c
* @brief STM32 internal DAC helper for the PA4 analogue output.
*/
#include "stm32_dac_output.h"
#include "main.h"
#define STM32_DAC_MAX_CODE 4095u
void stm32_dac_output_init(void)
{
GPIO_InitTypeDef gpio_init = {0};
__HAL_RCC_DAC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
gpio_init.Pin = GPIO_PIN_4;
gpio_init.Mode = GPIO_MODE_ANALOG;
gpio_init.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &gpio_init);
DAC->CR &= ~(DAC_CR_EN1 | DAC_CR_TEN1 | DAC_CR_DMAEN1);
DAC->DHR12R1 = 0u;
}
void stm32_dac_output_set(uint16_t dac_code, uint8_t enabled)
{
if (dac_code > STM32_DAC_MAX_CODE)
{
dac_code = STM32_DAC_MAX_CODE;
}
DAC->DHR12R1 = dac_code;
if (enabled != 0u)
{
DAC->CR |= DAC_CR_EN1;
}
else
{
DAC->CR &= ~DAC_CR_EN1;
}
}

View File

@ -0,0 +1,14 @@
/**
* @file stm32_dac_output.h
* @brief STM32 internal DAC helper for PA4 output generation.
*/
#ifndef STM32_DAC_OUTPUT_H
#define STM32_DAC_OUTPUT_H
#include <stdint.h>
void stm32_dac_output_init(void);
void stm32_dac_output_set(uint16_t dac_code, uint8_t enabled);
#endif /* STM32_DAC_OUTPUT_H */

View File

@ -0,0 +1,68 @@
/**
* @file uart_transport.c
* @brief USART1 transmit helpers used by the application core.
*/
#include "uart_transport.h"
#include "main.h"
static volatile uint8_t g_dma_busy = 0u;
void uart_transport_init(void)
{
LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_7);
LL_DMA_ClearFlag_TC7(DMA2);
LL_DMA_ClearFlag_TE7(DMA2);
LL_USART_EnableDMAReq_TX(USART1);
LL_DMA_EnableIT_TC(DMA2, LL_DMA_STREAM_7);
LL_DMA_EnableIT_TE(DMA2, LL_DMA_STREAM_7);
g_dma_busy = 0u;
}
void uart_transport_reset(void)
{
g_dma_busy = 0u;
}
void uart_transport_send_blocking(const uint8_t *data, uint16_t size)
{
uint16_t index;
for (index = 0u; index < size; ++index)
{
while (!LL_USART_IsActiveFlag_TXE(USART1))
{
}
LL_USART_TransmitData8(USART1, data[index]);
}
}
void uart_transport_send_dma(const uint8_t *data, uint16_t size)
{
while (g_dma_busy != 0u)
{
}
LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_7);
LL_DMA_ClearFlag_TC7(DMA2);
LL_DMA_ClearFlag_TE7(DMA2);
LL_DMA_ConfigAddresses(DMA2,
LL_DMA_STREAM_7,
(uint32_t)data,
LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_TRANSMIT),
LL_DMA_GetDataTransferDirection(DMA2, LL_DMA_STREAM_7));
LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_7, size);
g_dma_busy = 1u;
LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_7);
}
void uart_transport_mark_dma_complete(void)
{
g_dma_busy = 0u;
}
bool uart_transport_is_dma_busy(void)
{
return g_dma_busy != 0u;
}

View File

@ -0,0 +1,19 @@
/**
* @file uart_transport.h
* @brief USART1 transmit helpers used by the application core.
*/
#ifndef UART_TRANSPORT_H
#define UART_TRANSPORT_H
#include <stdbool.h>
#include <stdint.h>
void uart_transport_init(void);
void uart_transport_reset(void);
void uart_transport_send_blocking(const uint8_t *data, uint16_t size);
void uart_transport_send_dma(const uint8_t *data, uint16_t size);
void uart_transport_mark_dma_complete(void);
bool uart_transport_is_dma_busy(void);
#endif /* UART_TRANSPORT_H */

344
App/Models/app_types.h Normal file
View File

@ -0,0 +1,344 @@
/**
* @file app_types.h
* @brief Shared application-level types, constants, and protocol identifiers.
*
* Architectural note:
* This header owns the firmware vocabulary used by the modular App layer.
* CubeMX-generated headers keep only board-level definitions, while every
* application module includes these types to communicate through explicit
* interfaces instead of ad-hoc globals and scattered macros.
*/
#ifndef APP_TYPES_H
#define APP_TYPES_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/** @brief Total number of 16-bit words in a telemetry frame, including header. */
#define APP_TELEMETRY_WORD_COUNT 15u
/** @brief Total number of bytes in a telemetry frame. */
#define APP_TELEMETRY_FRAME_BYTES 30u
/** @brief Maximum number of payload words stored by the UART protocol parser. */
#define APP_PROTOCOL_MAX_PAYLOAD_WORDS 15u
/** @brief Maximum packet size handled by the UART protocol parser. */
#define APP_PROTOCOL_MAX_PACKET_BYTES 32u
/** @brief Maximum number of visible characters allowed in a saved profile name. */
#define APP_PROFILE_NAME_MAX_CHARACTERS 16u
/** @brief Maximum profile display-name length, including the terminating NUL. */
#define APP_PROFILE_NAME_LENGTH (APP_PROFILE_NAME_MAX_CHARACTERS + 1u)
/** @brief Maximum stored path length for profile and waveform files. */
#define APP_PROFILE_PATH_LENGTH 96u
/** @brief Future profile list file for SD-card based standalone operation. */
#define APP_STORAGE_PROFILE_INDEX_FILE "profiles.csv"
/** @brief UART header for the host work-configuration packet. */
#define APP_PACKET_HEADER_WORK_CONFIG 0x1111u
/** @brief UART header for the "return to defaults" command. */
#define APP_PACKET_HEADER_DEFAULTS 0x2222u
/** @brief UART header for the "transmit live telemetry" command. */
#define APP_PACKET_HEADER_TX_CURRENT 0x4444u
/** @brief UART header for the "query state" command. */
#define APP_PACKET_HEADER_QUERY_STATE 0x6666u
/** @brief UART header for profile-save control packets. */
#define APP_PACKET_HEADER_PROFILE_SAVE_CONTROL 0x7777u
/** @brief UART header for the AD9102 control packet. */
#define APP_PACKET_HEADER_AD9102_CONTROL 0x8888u
/** @brief UART header for the AD9833 control packet. */
#define APP_PACKET_HEADER_AD9833_CONTROL 0x9999u
/** @brief UART header for the DS1809 control packet. */
#define APP_PACKET_HEADER_DS1809_CONTROL 0xAAAAu
/** @brief UART header for the STM32 internal DAC control packet. */
#define APP_PACKET_HEADER_STM32_DAC_CONTROL 0xBBBBu
/** @brief UART header for the AD9102 waveform upload control packet. */
#define APP_PACKET_HEADER_AD9102_WAVE_CONTROL 0xCCCCu
/** @brief UART header for the AD9102 waveform upload data packet. */
#define APP_PACKET_HEADER_AD9102_WAVE_DATA 0xDDDDu
/** @brief UART header for profile-save data packets. */
#define APP_PACKET_HEADER_PROFILE_SAVE_DATA 0xEEEEu
/** @brief Packet size of the host work-configuration message, in bytes. */
#define APP_PACKET_BYTES_WORK_CONFIG 30u
/** @brief Packet size of short control messages, in bytes. */
#define APP_PACKET_BYTES_SHORT_CONTROL 10u
/** @brief Packet size of AD9102 waveform-data messages, in bytes. */
#define APP_PACKET_BYTES_AD9102_WAVE_DATA 30u
/** @brief Packet size of profile-save control messages, in bytes. */
#define APP_PACKET_BYTES_PROFILE_SAVE_CONTROL 30u
/** @brief Packet size of profile-save data messages, in bytes. */
#define APP_PACKET_BYTES_PROFILE_SAVE_DATA 30u
/** @brief Number of payload words carried by the host work packet. */
#define APP_PACKET_WORDS_WORK_CONFIG 14u
/** @brief Number of payload words carried by short control packets. */
#define APP_PACKET_WORDS_SHORT_CONTROL 4u
/** @brief Number of payload words carried by AD9102 waveform-data packets. */
#define APP_PACKET_WORDS_AD9102_WAVE_DATA 14u
/** @brief Number of payload words carried by profile-save control packets. */
#define APP_PACKET_WORDS_PROFILE_SAVE_CONTROL 14u
/** @brief Number of payload words carried by profile-save data packets. */
#define APP_PACKET_WORDS_PROFILE_SAVE_DATA 14u
/** @brief First status byte flag for SD-card related errors. */
#define APP_STATUS_FLAG_SD_ERROR 0x01u
/** @brief First status byte flag for UART framing/header errors. */
#define APP_STATUS_FLAG_UART_ERROR 0x02u
/** @brief First status byte flag for checksum or payload validation errors. */
#define APP_STATUS_FLAG_UART_DECODE_ERROR 0x04u
/** @brief First status byte flag for TEC1-related runtime errors. */
#define APP_STATUS_FLAG_TEC1_ERROR 0x08u
/** @brief First status byte flag for TEC2-related runtime errors. */
#define APP_STATUS_FLAG_TEC2_ERROR 0x10u
/** @brief First status byte flag for default/reset handling errors. */
#define APP_STATUS_FLAG_DEFAULT_ERROR 0x20u
/** @brief First status byte flag for AD9102 configuration/upload errors. */
#define APP_STATUS_FLAG_AD9102_ERROR 0x80u
/** @brief AD9102 control flag: enable or disable output. */
#define APP_AD9102_FLAG_ENABLE 0x0001u
/** @brief AD9102 control flag: select triangle instead of saw ramp. */
#define APP_AD9102_FLAG_TRIANGLE 0x0002u
/** @brief AD9102 control flag: use SRAM-backed waveform path. */
#define APP_AD9102_FLAG_SRAM 0x0004u
/** @brief AD9102 control flag: use amplitude/sample-count short format. */
#define APP_AD9102_FLAG_SRAM_FORMAT_ALT 0x0008u
/** @brief AD9833 control flag: enable or disable output. */
#define APP_AD9833_FLAG_ENABLE 0x0001u
/** @brief AD9833 control flag: select triangle mode. */
#define APP_AD9833_FLAG_TRIANGLE 0x0002u
/** @brief DS1809 control flag: pulse the "up" control line. */
#define APP_DS1809_FLAG_INCREMENT 0x0001u
/** @brief DS1809 control flag: pulse the "down" control line. */
#define APP_DS1809_FLAG_DECREMENT 0x0002u
/** @brief STM32 DAC control flag: enable output buffer/channel. */
#define APP_STM32_DAC_FLAG_ENABLE 0x0001u
/** @brief Op-code used to begin a custom AD9102 waveform upload. */
#define APP_AD9102_WAVE_OPCODE_BEGIN 0x0001u
/** @brief Op-code used to commit a custom AD9102 waveform upload. */
#define APP_AD9102_WAVE_OPCODE_COMMIT 0x0002u
/** @brief Op-code used to cancel a custom AD9102 waveform upload. */
#define APP_AD9102_WAVE_OPCODE_CANCEL 0x0003u
/** @brief Op-code used to begin a streamed profile-save session. */
#define APP_PROFILE_SAVE_OPCODE_BEGIN 0x0001u
/** @brief Op-code used to commit a streamed profile-save session. */
#define APP_PROFILE_SAVE_OPCODE_COMMIT 0x0002u
/** @brief Op-code used to cancel a streamed profile-save session. */
#define APP_PROFILE_SAVE_OPCODE_CANCEL 0x0003u
/** @brief Section id used for profile INI text chunks during profile save. */
#define APP_PROFILE_SAVE_SECTION_PROFILE_TEXT 0x0001u
/** @brief Section id used for optional waveform CSV chunks during profile save. */
#define APP_PROFILE_SAVE_SECTION_WAVEFORM_TEXT 0x0002u
/** @brief Maximum number of raw data bytes carried by one profile-save data packet. */
#define APP_PROFILE_SAVE_MAX_DATA_BYTES_PER_PACKET 22u
/**
* @brief High-level runtime modes executed by the application core.
*/
typedef enum app_mode_t {
APP_MODE_IDLE = 0,
APP_MODE_WORK,
APP_MODE_TX_CURRENT,
APP_MODE_ERROR
} app_mode_t;
/**
* @brief Deferred application events injected by future UI/GPIO frontends.
*/
typedef enum app_event_t {
APP_EVENT_NONE = 0,
APP_EVENT_PROFILE_NEXT
} app_event_t;
/**
* @brief Internal transmission slots queued by the application core.
*/
typedef enum app_tx_request_t {
APP_TX_REQUEST_NONE = 0,
APP_TX_REQUEST_STATUS,
APP_TX_REQUEST_CURRENT_FRAME
} app_tx_request_t;
/**
* @brief Packet kinds emitted by the UART protocol parser.
*/
typedef enum app_packet_kind_t {
APP_PACKET_KIND_NONE = 0,
APP_PACKET_KIND_WORK_CONFIG,
APP_PACKET_KIND_DEFAULTS,
APP_PACKET_KIND_TX_CURRENT,
APP_PACKET_KIND_QUERY_STATE,
APP_PACKET_KIND_PROFILE_SAVE_CONTROL,
APP_PACKET_KIND_AD9102_CONTROL,
APP_PACKET_KIND_AD9833_CONTROL,
APP_PACKET_KIND_DS1809_CONTROL,
APP_PACKET_KIND_STM32_DAC_CONTROL,
APP_PACKET_KIND_AD9102_WAVE_CONTROL,
APP_PACKET_KIND_AD9102_WAVE_DATA,
APP_PACKET_KIND_PROFILE_SAVE_DATA
} app_packet_kind_t;
/**
* @brief User-visible signal-generation mode for the AD9102 path.
*/
typedef enum waveform_mode_t {
WAVEFORM_MODE_SAW = 0,
WAVEFORM_MODE_SRAM_GENERATED,
WAVEFORM_MODE_SRAM_CUSTOM
} waveform_mode_t;
/**
* @brief Packed enable flags and misc runtime settings for the board.
*/
typedef struct work_config_t {
uint8_t work_enabled;
uint8_t supply_5v1_enabled;
uint8_t supply_5v2_enabled;
uint8_t laser1_enabled;
uint8_t laser2_enabled;
uint8_t reference1_enabled;
uint8_t reference2_enabled;
uint8_t tec1_enabled;
uint8_t tec2_enabled;
uint8_t temp_sensor1_enabled;
uint8_t temp_sensor2_enabled;
uint8_t pid1_from_host;
uint8_t pid2_from_host;
uint16_t averages;
uint16_t message_id;
} work_config_t;
/**
* @brief Runtime configuration of one laser channel.
*/
typedef struct laser_channel_config_t {
uint16_t target_temperature_raw;
float pid_p;
float pid_i;
uint16_t current_raw;
} laser_channel_config_t;
/**
* @brief Mutable per-channel runtime state derived from sensor feedback.
*/
typedef struct laser_runtime_t {
uint16_t current_temperature_raw;
float integral_error;
uint16_t power_raw;
} laser_runtime_t;
/**
* @brief Normalised configuration for AD9102 waveform control.
*/
typedef struct waveform_config_t {
waveform_mode_t mode;
uint8_t enabled;
uint8_t triangle;
uint8_t saw_step;
uint8_t pat_period_base;
uint16_t pat_period;
uint16_t sample_count;
uint8_t hold_cycles;
uint16_t amplitude;
char source_path[APP_PROFILE_PATH_LENGTH];
} waveform_config_t;
/**
* @brief AD9833 generator configuration mirrored from the serial-control path.
*/
typedef struct ad9833_config_t {
uint8_t enabled;
uint8_t triangle;
uint32_t frequency_word;
} ad9833_config_t;
/**
* @brief STM32 internal DAC configuration mirrored from the serial-control path.
*/
typedef struct stm32_dac_config_t {
uint8_t enabled;
uint16_t code;
} stm32_dac_config_t;
/**
* @brief Absolute DS1809 target expressed as a number of steps above the minimum tap.
*/
typedef struct ds1809_config_t {
bool apply_position;
uint8_t position_from_min;
} ds1809_config_t;
/**
* @brief Standalone profile model loaded from SD-card configuration.
*/
typedef struct profile_t {
char display_name[APP_PROFILE_NAME_LENGTH];
char profile_path[APP_PROFILE_PATH_LENGTH];
char waveform_path[APP_PROFILE_PATH_LENGTH];
bool auto_run;
bool boot_enabled;
work_config_t work_config;
laser_channel_config_t laser_channels[2];
waveform_config_t waveform;
ad9833_config_t ad9833;
stm32_dac_config_t stm32_dac;
ds1809_config_t ds1809;
} profile_t;
/**
* @brief Telemetry frame sent over UART.
*/
typedef struct telemetry_frame_t {
uint16_t words[APP_TELEMETRY_WORD_COUNT];
} telemetry_frame_t;
#endif /* APP_TYPES_H */

View File

@ -0,0 +1,162 @@
/**
* @file app_uart_protocol.c
* @brief Table-driven incremental UART packet parser and checksum helpers.
*/
#include "app_uart_protocol.h"
#include <string.h>
static const app_packet_descriptor_t g_packet_descriptors[] = {
{APP_PACKET_HEADER_WORK_CONFIG, APP_PACKET_BYTES_WORK_CONFIG, APP_PACKET_WORDS_WORK_CONFIG, 13, APP_PACKET_KIND_WORK_CONFIG},
{APP_PACKET_HEADER_DEFAULTS, 2u, 0u, -1, APP_PACKET_KIND_DEFAULTS},
{APP_PACKET_HEADER_TX_CURRENT, 2u, 0u, -1, APP_PACKET_KIND_TX_CURRENT},
{APP_PACKET_HEADER_QUERY_STATE, 2u, 0u, -1, APP_PACKET_KIND_QUERY_STATE},
{APP_PACKET_HEADER_PROFILE_SAVE_CONTROL, APP_PACKET_BYTES_PROFILE_SAVE_CONTROL, APP_PACKET_WORDS_PROFILE_SAVE_CONTROL, 13, APP_PACKET_KIND_PROFILE_SAVE_CONTROL},
{APP_PACKET_HEADER_AD9102_CONTROL, APP_PACKET_BYTES_SHORT_CONTROL, APP_PACKET_WORDS_SHORT_CONTROL, 3, APP_PACKET_KIND_AD9102_CONTROL},
{APP_PACKET_HEADER_AD9833_CONTROL, APP_PACKET_BYTES_SHORT_CONTROL, APP_PACKET_WORDS_SHORT_CONTROL, 3, APP_PACKET_KIND_AD9833_CONTROL},
{APP_PACKET_HEADER_DS1809_CONTROL, APP_PACKET_BYTES_SHORT_CONTROL, APP_PACKET_WORDS_SHORT_CONTROL, 3, APP_PACKET_KIND_DS1809_CONTROL},
{APP_PACKET_HEADER_STM32_DAC_CONTROL, APP_PACKET_BYTES_SHORT_CONTROL, APP_PACKET_WORDS_SHORT_CONTROL, 3, APP_PACKET_KIND_STM32_DAC_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_PROFILE_SAVE_DATA, APP_PACKET_BYTES_PROFILE_SAVE_DATA, APP_PACKET_WORDS_PROFILE_SAVE_DATA, 13, APP_PACKET_KIND_PROFILE_SAVE_DATA}
};
static const app_packet_descriptor_t *app_uart_protocol_find_descriptor(uint16_t header)
{
size_t index;
for (index = 0u; index < (sizeof(g_packet_descriptors) / sizeof(g_packet_descriptors[0])); ++index)
{
if (g_packet_descriptors[index].header == header)
{
return &g_packet_descriptors[index];
}
}
return NULL;
}
uint16_t app_protocol_calculate_checksum(const uint16_t *words, uint16_t word_count)
{
uint16_t checksum;
uint16_t index;
if ((words == NULL) || (word_count == 0u))
{
return 0u;
}
checksum = words[0];
for (index = 1u; index < word_count; ++index)
{
checksum ^= words[index];
}
return checksum;
}
void app_uart_protocol_init(app_uart_protocol_parser_t *parser)
{
if (parser != NULL)
{
memset(parser, 0, sizeof(*parser));
}
}
void app_uart_protocol_reset(app_uart_protocol_parser_t *parser)
{
app_uart_protocol_init(parser);
}
app_protocol_feed_result_t app_uart_protocol_feed_byte(app_uart_protocol_parser_t *parser,
uint8_t byte,
app_packet_t *out_packet)
{
uint8_t word_index;
if ((parser == NULL) || (out_packet == NULL))
{
return APP_PROTOCOL_FEED_IN_PROGRESS;
}
if (parser->bytes_received == 0u)
{
parser->buffer[0] = byte;
parser->partial_header = byte;
parser->bytes_received = 1u;
return APP_PROTOCOL_FEED_IN_PROGRESS;
}
if (parser->bytes_received == 1u)
{
parser->buffer[1] = byte;
parser->partial_header = (uint16_t)(parser->partial_header | ((uint16_t)byte << 8));
parser->descriptor = app_uart_protocol_find_descriptor(parser->partial_header);
if (parser->descriptor == NULL)
{
app_uart_protocol_reset(parser);
return APP_PROTOCOL_FEED_INVALID_HEADER;
}
parser->bytes_received = 2u;
if (parser->descriptor->total_bytes == 2u)
{
memset(out_packet, 0, sizeof(*out_packet));
out_packet->kind = parser->descriptor->kind;
out_packet->header = parser->descriptor->header;
out_packet->checksum_valid = true;
app_uart_protocol_reset(parser);
return APP_PROTOCOL_FEED_PACKET_READY;
}
return APP_PROTOCOL_FEED_IN_PROGRESS;
}
if (parser->descriptor == NULL)
{
app_uart_protocol_reset(parser);
return APP_PROTOCOL_FEED_INVALID_HEADER;
}
if (parser->bytes_received >= parser->descriptor->total_bytes)
{
app_uart_protocol_reset(parser);
return APP_PROTOCOL_FEED_INVALID_HEADER;
}
parser->buffer[parser->bytes_received] = byte;
++parser->bytes_received;
if (parser->bytes_received < parser->descriptor->total_bytes)
{
return APP_PROTOCOL_FEED_IN_PROGRESS;
}
memset(out_packet, 0, sizeof(*out_packet));
out_packet->kind = parser->descriptor->kind;
out_packet->header = parser->descriptor->header;
out_packet->payload_words = parser->descriptor->payload_words;
for (word_index = 0u; word_index < parser->descriptor->payload_words; ++word_index)
{
uint8_t byte_offset = (uint8_t)(2u + (word_index * 2u));
out_packet->words[word_index] = (uint16_t)(parser->buffer[byte_offset] |
((uint16_t)parser->buffer[byte_offset + 1u] << 8));
}
if (parser->descriptor->checksum_word_index >= 0)
{
uint8_t checksum_index = (uint8_t)parser->descriptor->checksum_word_index;
out_packet->checksum_valid =
app_protocol_calculate_checksum(out_packet->words, checksum_index) == out_packet->words[checksum_index];
}
else
{
out_packet->checksum_valid = true;
}
app_uart_protocol_reset(parser);
return APP_PROTOCOL_FEED_PACKET_READY;
}

View File

@ -0,0 +1,68 @@
/**
* @file app_uart_protocol.h
* @brief Table-driven incremental UART packet parser and checksum helpers.
*
* Architectural note:
* The parser is intentionally transport-agnostic. It receives one byte at a
* time from the IRQ adapter, reconstructs packets according to a descriptor
* table, validates checksums when applicable, and emits fully decoded packet
* objects for the application core.
*/
#ifndef APP_UART_PROTOCOL_H
#define APP_UART_PROTOCOL_H
#include <stdbool.h>
#include <stdint.h>
#include "app_types.h"
/**
* @brief Descriptor describing one supported UART packet shape.
*/
typedef struct app_packet_descriptor_t {
uint16_t header;
uint8_t total_bytes;
uint8_t payload_words;
int8_t checksum_word_index;
app_packet_kind_t kind;
} app_packet_descriptor_t;
/**
* @brief Parsed UART packet emitted by the protocol layer.
*/
typedef struct app_packet_t {
app_packet_kind_t kind;
uint16_t header;
uint8_t payload_words;
bool checksum_valid;
uint16_t words[APP_PROTOCOL_MAX_PAYLOAD_WORDS];
} app_packet_t;
/**
* @brief Mutable state of the incremental UART parser.
*/
typedef struct app_uart_protocol_parser_t {
const app_packet_descriptor_t *descriptor;
uint16_t partial_header;
uint8_t bytes_received;
uint8_t buffer[APP_PROTOCOL_MAX_PACKET_BYTES];
} app_uart_protocol_parser_t;
/**
* @brief Result returned after feeding one byte into the parser.
*/
typedef enum app_protocol_feed_result_t {
APP_PROTOCOL_FEED_IN_PROGRESS = 0,
APP_PROTOCOL_FEED_PACKET_READY,
APP_PROTOCOL_FEED_INVALID_HEADER
} app_protocol_feed_result_t;
void app_uart_protocol_init(app_uart_protocol_parser_t *parser);
void app_uart_protocol_reset(app_uart_protocol_parser_t *parser);
app_protocol_feed_result_t app_uart_protocol_feed_byte(app_uart_protocol_parser_t *parser,
uint8_t byte,
app_packet_t *out_packet);
uint16_t app_protocol_calculate_checksum(const uint16_t *words, uint16_t word_count);
#endif /* APP_UART_PROTOCOL_H */

View File

@ -0,0 +1,525 @@
/**
* @file profile_repository.c
* @brief Future SD-card profile repository with minimal standalone parsing.
*/
#include "profile_repository.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "ad9102_device.h"
#include "storage_sd.h"
#define PROFILE_REPOSITORY_BUFFER_SIZE 1024u
static char *profile_repository_trim(char *text)
{
char *end;
while ((*text != '\0') && isspace((unsigned char)*text))
{
++text;
}
if (*text == '\0')
{
return text;
}
end = text + strlen(text) - 1u;
while ((end > text) && isspace((unsigned char)*end))
{
*end = '\0';
--end;
}
return text;
}
static void profile_repository_copy_string(char *destination, size_t destination_size, const char *source)
{
if ((destination == NULL) || (destination_size == 0u))
{
return;
}
if (source == NULL)
{
destination[0] = '\0';
return;
}
strncpy(destination, source, destination_size - 1u);
destination[destination_size - 1u] = '\0';
}
static bool profile_repository_parse_bool(const char *value, bool *out_value)
{
if ((value == NULL) || (out_value == NULL))
{
return false;
}
if ((strcmp(value, "1") == 0) || (strcasecmp(value, "true") == 0) || (strcasecmp(value, "yes") == 0))
{
*out_value = true;
return true;
}
if ((strcmp(value, "0") == 0) || (strcasecmp(value, "false") == 0) || (strcasecmp(value, "no") == 0))
{
*out_value = false;
return true;
}
return false;
}
static uint16_t profile_repository_parse_u16(const char *value)
{
return (uint16_t)strtoul(value, NULL, 0);
}
static uint32_t profile_repository_parse_u32(const char *value)
{
return (uint32_t)strtoul(value, NULL, 0);
}
static float profile_repository_parse_float(const char *value)
{
return strtof(value, NULL);
}
static void profile_repository_reset_profile(profile_t *profile)
{
memset(profile, 0, sizeof(*profile));
profile->auto_run = true;
profile->boot_enabled = true;
profile->waveform.mode = WAVEFORM_MODE_SAW;
profile->waveform.enabled = 1u;
profile->waveform.saw_step = 1u;
profile->waveform.pat_period_base = 2u;
profile->waveform.pat_period = 0xFFFFu;
profile->waveform.sample_count = AD9102_SRAM_DEFAULT_SAMPLE_COUNT;
profile->waveform.hold_cycles = AD9102_SRAM_DEFAULT_HOLD;
profile->waveform.amplitude = AD9102_SRAM_DEFAULT_AMPLITUDE;
profile->ds1809.apply_position = false;
}
static void profile_repository_apply_key_value(profile_t *profile, const char *key, const char *value)
{
bool bool_value;
if ((profile == NULL) || (key == NULL) || (value == NULL))
{
return;
}
if (strcmp(key, "profile_name") == 0)
{
profile_repository_copy_string(profile->display_name, sizeof(profile->display_name), value);
}
else if (strcmp(key, "auto_run") == 0)
{
if (profile_repository_parse_bool(value, &bool_value))
{
profile->auto_run = bool_value;
}
}
else if (strcmp(key, "boot_enabled") == 0)
{
if (profile_repository_parse_bool(value, &bool_value))
{
profile->boot_enabled = bool_value;
}
}
else if (strcmp(key, "work_enable") == 0)
{
profile->work_config.work_enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "u5v1_enable") == 0)
{
profile->work_config.supply_5v1_enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "u5v2_enable") == 0)
{
profile->work_config.supply_5v2_enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "ld1_enable") == 0)
{
profile->work_config.laser1_enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "ld2_enable") == 0)
{
profile->work_config.laser2_enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "ref1_enable") == 0)
{
profile->work_config.reference1_enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "ref2_enable") == 0)
{
profile->work_config.reference2_enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "tec1_enable") == 0)
{
profile->work_config.tec1_enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "tec2_enable") == 0)
{
profile->work_config.tec2_enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "ts1_enable") == 0)
{
profile->work_config.temp_sensor1_enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "ts2_enable") == 0)
{
profile->work_config.temp_sensor2_enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "pid1_from_host") == 0)
{
profile->work_config.pid1_from_host = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "pid2_from_host") == 0)
{
profile->work_config.pid2_from_host = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "averages") == 0)
{
profile->work_config.averages = profile_repository_parse_u16(value);
}
else if (strcmp(key, "message_id") == 0)
{
profile->work_config.message_id = profile_repository_parse_u16(value);
}
else if (strcmp(key, "laser1_target_temp") == 0)
{
profile->laser_channels[0].target_temperature_raw = profile_repository_parse_u16(value);
}
else if (strcmp(key, "laser2_target_temp") == 0)
{
profile->laser_channels[1].target_temperature_raw = profile_repository_parse_u16(value);
}
else if (strcmp(key, "laser1_current") == 0)
{
profile->laser_channels[0].current_raw = profile_repository_parse_u16(value);
}
else if (strcmp(key, "laser2_current") == 0)
{
profile->laser_channels[1].current_raw = profile_repository_parse_u16(value);
}
else if (strcmp(key, "laser1_pid_p") == 0)
{
profile->laser_channels[0].pid_p = profile_repository_parse_float(value);
}
else if (strcmp(key, "laser1_pid_i") == 0)
{
profile->laser_channels[0].pid_i = profile_repository_parse_float(value);
}
else if (strcmp(key, "laser2_pid_p") == 0)
{
profile->laser_channels[1].pid_p = profile_repository_parse_float(value);
}
else if (strcmp(key, "laser2_pid_i") == 0)
{
profile->laser_channels[1].pid_i = profile_repository_parse_float(value);
}
else if (strcmp(key, "waveform_mode") == 0)
{
if (strcasecmp(value, "saw") == 0)
{
profile->waveform.mode = WAVEFORM_MODE_SAW;
}
else if (strcasecmp(value, "generated_sram") == 0)
{
profile->waveform.mode = WAVEFORM_MODE_SRAM_GENERATED;
}
else if (strcasecmp(value, "custom_sram") == 0)
{
profile->waveform.mode = WAVEFORM_MODE_SRAM_CUSTOM;
}
}
else if (strcmp(key, "waveform_enable") == 0)
{
profile->waveform.enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "waveform_triangle") == 0)
{
profile->waveform.triangle = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "waveform_saw_step") == 0)
{
profile->waveform.saw_step = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "waveform_pat_base") == 0)
{
profile->waveform.pat_period_base = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "waveform_pat_period") == 0)
{
profile->waveform.pat_period = profile_repository_parse_u16(value);
}
else if (strcmp(key, "waveform_sample_count") == 0)
{
profile->waveform.sample_count = profile_repository_parse_u16(value);
}
else if (strcmp(key, "waveform_hold_cycles") == 0)
{
profile->waveform.hold_cycles = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "waveform_amplitude") == 0)
{
profile->waveform.amplitude = profile_repository_parse_u16(value);
}
else if (strcmp(key, "waveform_source") == 0)
{
profile_repository_copy_string(profile->waveform.source_path, sizeof(profile->waveform.source_path), value);
}
else if (strcmp(key, "ad9833_enable") == 0)
{
profile->ad9833.enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "ad9833_triangle") == 0)
{
profile->ad9833.triangle = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "ad9833_frequency_word") == 0)
{
profile->ad9833.frequency_word = profile_repository_parse_u32(value);
}
else if (strcmp(key, "stm32_dac_enable") == 0)
{
profile->stm32_dac.enabled = (uint8_t)profile_repository_parse_u16(value);
}
else if (strcmp(key, "stm32_dac_code") == 0)
{
profile->stm32_dac.code = profile_repository_parse_u16(value);
}
else if (strcmp(key, "ds1809_apply") == 0)
{
if (profile_repository_parse_bool(value, &bool_value))
{
profile->ds1809.apply_position = bool_value;
}
}
else if (strcmp(key, "ds1809_position_from_min") == 0)
{
profile->ds1809.position_from_min = (uint8_t)profile_repository_parse_u16(value);
}
}
static bool profile_repository_load_ini_file(const char *profile_path, profile_t *profile)
{
char buffer[PROFILE_REPOSITORY_BUFFER_SIZE];
UINT bytes_read = 0u;
char *cursor;
FRESULT result;
result = storage_sd_read_bytes(profile_path, 0u, buffer, sizeof(buffer) - 1u, &bytes_read);
if (result != FR_OK)
{
return false;
}
buffer[bytes_read] = '\0';
cursor = buffer;
while (*cursor != '\0')
{
char *line_start = cursor;
char *line_end = strpbrk(cursor, "\r\n");
char *separator;
char *key;
char *value;
if (line_end != NULL)
{
*line_end = '\0';
cursor = line_end + 1;
while ((*cursor == '\r') || (*cursor == '\n'))
{
++cursor;
}
}
else
{
cursor += strlen(cursor);
}
line_start = profile_repository_trim(line_start);
if ((*line_start == '\0') || (*line_start == '#') || (*line_start == ';'))
{
continue;
}
separator = strchr(line_start, '=');
if (separator == NULL)
{
continue;
}
*separator = '\0';
key = profile_repository_trim(line_start);
value = profile_repository_trim(separator + 1);
profile_repository_apply_key_value(profile, key, value);
}
return true;
}
static bool profile_repository_load_by_valid_line_index(uint16_t target_index,
profile_t *out_profile,
uint16_t *out_total_valid_lines)
{
char buffer[PROFILE_REPOSITORY_BUFFER_SIZE];
UINT bytes_read = 0u;
char *cursor;
uint16_t valid_line_index = 0u;
FRESULT result;
if ((out_profile == NULL) || !storage_sd_file_exists(APP_STORAGE_PROFILE_INDEX_FILE))
{
return false;
}
result = storage_sd_read_bytes(APP_STORAGE_PROFILE_INDEX_FILE, 0u, buffer, sizeof(buffer) - 1u, &bytes_read);
if (result != FR_OK)
{
return false;
}
buffer[bytes_read] = '\0';
cursor = buffer;
while (*cursor != '\0')
{
char *line_start = cursor;
char *line_end = strpbrk(cursor, "\r\n");
char *display_name;
char *profile_path;
char *waveform_path;
char *separator_1;
char *separator_2;
if (line_end != NULL)
{
*line_end = '\0';
cursor = line_end + 1;
while ((*cursor == '\r') || (*cursor == '\n'))
{
++cursor;
}
}
else
{
cursor += strlen(cursor);
}
line_start = profile_repository_trim(line_start);
if ((*line_start == '\0') || (*line_start == '#') || (*line_start == ';'))
{
continue;
}
separator_1 = strchr(line_start, ',');
if (separator_1 == NULL)
{
continue;
}
*separator_1 = '\0';
separator_2 = strchr(separator_1 + 1, ',');
if (separator_2 != NULL)
{
*separator_2 = '\0';
}
display_name = profile_repository_trim(line_start);
profile_path = profile_repository_trim(separator_1 + 1);
waveform_path = (separator_2 != NULL) ? profile_repository_trim(separator_2 + 1) : "";
if (*profile_path == '\0')
{
continue;
}
if (valid_line_index == target_index)
{
profile_repository_reset_profile(out_profile);
profile_repository_copy_string(out_profile->display_name, sizeof(out_profile->display_name), display_name);
profile_repository_copy_string(out_profile->profile_path, sizeof(out_profile->profile_path), profile_path);
profile_repository_copy_string(out_profile->waveform_path, sizeof(out_profile->waveform_path), waveform_path);
profile_repository_copy_string(out_profile->waveform.source_path,
sizeof(out_profile->waveform.source_path),
waveform_path);
if (!profile_repository_load_ini_file(profile_path, out_profile))
{
return false;
}
if (out_total_valid_lines != NULL)
{
*out_total_valid_lines = (uint16_t)(valid_line_index + 1u);
}
return true;
}
++valid_line_index;
}
if (out_total_valid_lines != NULL)
{
*out_total_valid_lines = valid_line_index;
}
return false;
}
bool profile_repository_load_first(profile_t *out_profile, uint16_t *out_index)
{
uint16_t total_lines = 0u;
if (!profile_repository_load_by_valid_line_index(0u, out_profile, &total_lines))
{
return false;
}
if (out_index != NULL)
{
*out_index = 0u;
}
return true;
}
bool profile_repository_load_next(uint16_t *in_out_index, profile_t *out_profile)
{
uint16_t requested_index;
uint16_t total_lines = 0u;
if ((in_out_index == NULL) || (out_profile == NULL))
{
return false;
}
requested_index = (uint16_t)(*in_out_index + 1u);
if (profile_repository_load_by_valid_line_index(requested_index, out_profile, &total_lines))
{
*in_out_index = requested_index;
return true;
}
if ((total_lines == 0u) || !profile_repository_load_by_valid_line_index(0u, out_profile, NULL))
{
return false;
}
*in_out_index = 0u;
return true;
}

View File

@ -0,0 +1,36 @@
/**
* @file profile_repository.h
* @brief Future SD-card profile repository with minimal standalone parsing.
*/
#ifndef PROFILE_REPOSITORY_H
#define PROFILE_REPOSITORY_H
#include <stdbool.h>
#include <stdint.h>
#include "app_types.h"
/**
* @brief Load the first valid profile listed in `profiles.csv`.
*
* @param out_profile Destination profile object.
* @param out_index Receives the zero-based valid profile index.
*
* @retval true A valid profile was loaded.
* @retval false No valid profile entry was available.
*/
bool profile_repository_load_first(profile_t *out_profile, uint16_t *out_index);
/**
* @brief Load the next valid profile, wrapping around to the first entry.
*
* @param in_out_index Current profile index on input, next index on success.
* @param out_profile Destination profile object.
*
* @retval true The next valid profile was loaded.
* @retval false No valid profile entry was available.
*/
bool profile_repository_load_next(uint16_t *in_out_index, profile_t *out_profile);
#endif /* PROFILE_REPOSITORY_H */

View File

@ -0,0 +1,497 @@
/**
* @file profile_storage.c
* @brief Streamed SD-card writer for GUI-initiated profile saves.
*/
#include "profile_storage.h"
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "app_types.h"
#include "storage_sd.h"
#define PROFILE_STORAGE_PROFILES_DIR "profiles"
#define PROFILE_STORAGE_WAVES_DIR "waves"
#define PROFILE_STORAGE_PROFILE_FILE_TEMPLATE "profiles/PROF%04u.INI"
#define PROFILE_STORAGE_WAVE_FILE_TEMPLATE "waves/WAVE%04u.CSV"
#define PROFILE_STORAGE_MAX_FILE_INDEX 9999u
#define PROFILE_STORAGE_INDEX_LINE_BUFFER_SIZE 192u
typedef struct profile_storage_context_t {
bool active;
bool profile_file_open;
bool waveform_file_open;
char display_name[APP_PROFILE_NAME_LENGTH];
char profile_path[APP_PROFILE_PATH_LENGTH];
char waveform_path[APP_PROFILE_PATH_LENGTH];
uint16_t profile_expected_bytes;
uint16_t waveform_expected_bytes;
uint16_t profile_received_bytes;
uint16_t waveform_received_bytes;
FIL profile_file;
FIL waveform_file;
profile_storage_status_t last_status;
} profile_storage_context_t;
static profile_storage_context_t g_profile_storage;
static profile_storage_status_t profile_storage_set_status(profile_storage_status_t status)
{
g_profile_storage.last_status = status;
return status;
}
static void profile_storage_reset_context(void)
{
memset(&g_profile_storage, 0, sizeof(g_profile_storage));
g_profile_storage.last_status = PROFILE_STORAGE_STATUS_OK;
}
static bool profile_storage_is_name_character_allowed(char character)
{
return (isalnum((unsigned char)character) != 0) ||
(character == ' ') ||
(character == '-') ||
(character == '_');
}
static bool profile_storage_copy_validated_name(char *destination, size_t destination_size, const char *source)
{
const char *start;
const char *end;
size_t length;
size_t index;
if ((destination == NULL) || (destination_size < APP_PROFILE_NAME_LENGTH) || (source == NULL))
{
return false;
}
start = source;
while ((*start != '\0') && isspace((unsigned char)*start))
{
++start;
}
end = start + strlen(start);
while ((end > start) && isspace((unsigned char)end[-1]))
{
--end;
}
length = (size_t)(end - start);
if ((length == 0u) || (length > APP_PROFILE_NAME_MAX_CHARACTERS))
{
return false;
}
for (index = 0u; index < length; ++index)
{
if (!profile_storage_is_name_character_allowed(start[index]))
{
return false;
}
}
memcpy(destination, start, length);
destination[length] = '\0';
return true;
}
static void profile_storage_clear_session_paths(void)
{
g_profile_storage.display_name[0] = '\0';
g_profile_storage.profile_path[0] = '\0';
g_profile_storage.waveform_path[0] = '\0';
}
static void profile_storage_close_open_files(void)
{
if (g_profile_storage.waveform_file_open)
{
(void)storage_sd_close_file(&g_profile_storage.waveform_file);
g_profile_storage.waveform_file_open = false;
}
if (g_profile_storage.profile_file_open)
{
(void)storage_sd_close_file(&g_profile_storage.profile_file);
g_profile_storage.profile_file_open = false;
}
}
static void profile_storage_remove_partial_files(void)
{
if (g_profile_storage.waveform_path[0] != '\0')
{
(void)storage_sd_remove_file(g_profile_storage.waveform_path);
}
if (g_profile_storage.profile_path[0] != '\0')
{
(void)storage_sd_remove_file(g_profile_storage.profile_path);
}
}
static void profile_storage_abort_session(void)
{
profile_storage_close_open_files();
profile_storage_remove_partial_files();
g_profile_storage.active = false;
g_profile_storage.profile_expected_bytes = 0u;
g_profile_storage.waveform_expected_bytes = 0u;
g_profile_storage.profile_received_bytes = 0u;
g_profile_storage.waveform_received_bytes = 0u;
profile_storage_clear_session_paths();
}
static profile_storage_status_t profile_storage_format_target_path(char *destination,
size_t destination_size,
const char *format_string,
uint16_t file_index)
{
int written;
written = snprintf(destination, destination_size, format_string, (unsigned int)file_index);
if ((written <= 0) || ((size_t)written >= destination_size))
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_FILE_NAME_EXHAUSTED);
}
return PROFILE_STORAGE_STATUS_OK;
}
static profile_storage_status_t profile_storage_find_free_paths(uint16_t waveform_text_bytes)
{
uint16_t file_index;
for (file_index = 1u; file_index <= PROFILE_STORAGE_MAX_FILE_INDEX; ++file_index)
{
FILINFO file_info;
FRESULT result;
profile_storage_status_t status;
status = profile_storage_format_target_path(g_profile_storage.profile_path,
sizeof(g_profile_storage.profile_path),
PROFILE_STORAGE_PROFILE_FILE_TEMPLATE,
file_index);
if (status != PROFILE_STORAGE_STATUS_OK)
{
return status;
}
result = storage_sd_stat(g_profile_storage.profile_path, &file_info);
if (result == FR_OK)
{
continue;
}
if ((result != FR_NO_FILE) && (result != FR_NO_PATH))
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_STORAGE_UNAVAILABLE);
}
if (waveform_text_bytes > 0u)
{
status = profile_storage_format_target_path(g_profile_storage.waveform_path,
sizeof(g_profile_storage.waveform_path),
PROFILE_STORAGE_WAVE_FILE_TEMPLATE,
file_index);
if (status != PROFILE_STORAGE_STATUS_OK)
{
return status;
}
result = storage_sd_stat(g_profile_storage.waveform_path, &file_info);
if (result == FR_OK)
{
continue;
}
if ((result != FR_NO_FILE) && (result != FR_NO_PATH))
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_STORAGE_UNAVAILABLE);
}
}
else
{
g_profile_storage.waveform_path[0] = '\0';
}
return profile_storage_set_status(PROFILE_STORAGE_STATUS_OK);
}
return profile_storage_set_status(PROFILE_STORAGE_STATUS_FILE_NAME_EXHAUSTED);
}
static profile_storage_status_t profile_storage_open_target_files(void)
{
FRESULT result;
result = storage_sd_make_directory(PROFILE_STORAGE_PROFILES_DIR);
if (result != FR_OK)
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_DIRECTORY_ERROR);
}
if (g_profile_storage.waveform_expected_bytes > 0u)
{
result = storage_sd_make_directory(PROFILE_STORAGE_WAVES_DIR);
if (result != FR_OK)
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_DIRECTORY_ERROR);
}
}
result = storage_sd_open_file(&g_profile_storage.profile_file,
g_profile_storage.profile_path,
FA_CREATE_ALWAYS | FA_WRITE);
if (result != FR_OK)
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_FILE_OPEN_ERROR);
}
g_profile_storage.profile_file_open = true;
if (g_profile_storage.waveform_expected_bytes > 0u)
{
result = storage_sd_open_file(&g_profile_storage.waveform_file,
g_profile_storage.waveform_path,
FA_CREATE_ALWAYS | FA_WRITE);
if (result != FR_OK)
{
profile_storage_abort_session();
return profile_storage_set_status(PROFILE_STORAGE_STATUS_FILE_OPEN_ERROR);
}
g_profile_storage.waveform_file_open = true;
}
return profile_storage_set_status(PROFILE_STORAGE_STATUS_OK);
}
static profile_storage_status_t profile_storage_write_to_file(FIL *file,
uint16_t *received_bytes,
uint16_t expected_bytes,
const uint8_t *data,
uint16_t data_size)
{
UINT bytes_written = 0u;
FRESULT result;
if ((file == NULL) || (received_bytes == NULL) || (data == NULL) || (data_size == 0u))
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_INVALID_ARGUMENT);
}
if ((uint32_t)(*received_bytes) + (uint32_t)data_size > (uint32_t)expected_bytes)
{
profile_storage_abort_session();
return profile_storage_set_status(PROFILE_STORAGE_STATUS_SIZE_MISMATCH);
}
result = f_write(file, data, data_size, &bytes_written);
if ((result != FR_OK) || (bytes_written != data_size))
{
profile_storage_abort_session();
return profile_storage_set_status(PROFILE_STORAGE_STATUS_WRITE_ERROR);
}
*received_bytes = (uint16_t)(*received_bytes + data_size);
return profile_storage_set_status(PROFILE_STORAGE_STATUS_OK);
}
static profile_storage_status_t profile_storage_append_index_entry(void)
{
char line_buffer[PROFILE_STORAGE_INDEX_LINE_BUFFER_SIZE];
FIL index_file;
UINT bytes_written = 0u;
FRESULT result;
int line_length;
line_length = snprintf(line_buffer,
sizeof(line_buffer),
"%s,%s,%s\r\n",
g_profile_storage.display_name,
g_profile_storage.profile_path,
g_profile_storage.waveform_path);
if ((line_length <= 0) || ((size_t)line_length >= sizeof(line_buffer)))
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_INDEX_UPDATE_ERROR);
}
result = storage_sd_open_file(&index_file,
APP_STORAGE_PROFILE_INDEX_FILE,
FA_OPEN_ALWAYS | FA_WRITE);
if (result != FR_OK)
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_INDEX_UPDATE_ERROR);
}
result = f_lseek(&index_file, f_size(&index_file));
if (result == FR_OK)
{
result = f_write(&index_file, line_buffer, (UINT)line_length, &bytes_written);
}
if (result == FR_OK)
{
result = f_sync(&index_file);
}
(void)storage_sd_close_file(&index_file);
if ((result != FR_OK) || (bytes_written != (UINT)line_length))
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_INDEX_UPDATE_ERROR);
}
return profile_storage_set_status(PROFILE_STORAGE_STATUS_OK);
}
void profile_storage_init(void)
{
profile_storage_reset_context();
}
bool profile_storage_is_active(void)
{
return g_profile_storage.active;
}
profile_storage_status_t profile_storage_get_last_status(void)
{
return g_profile_storage.last_status;
}
profile_storage_status_t profile_storage_begin(const char *display_name,
uint16_t profile_text_bytes,
uint16_t waveform_text_bytes)
{
if (profile_storage_is_active())
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_SESSION_ACTIVE);
}
profile_storage_reset_context();
if (!storage_sd_is_available())
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_STORAGE_UNAVAILABLE);
}
if ((profile_text_bytes == 0u) || !profile_storage_copy_validated_name(g_profile_storage.display_name,
sizeof(g_profile_storage.display_name),
display_name))
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_NAME_INVALID);
}
g_profile_storage.profile_expected_bytes = profile_text_bytes;
g_profile_storage.waveform_expected_bytes = waveform_text_bytes;
g_profile_storage.active = true;
if (profile_storage_find_free_paths(waveform_text_bytes) != PROFILE_STORAGE_STATUS_OK)
{
g_profile_storage.active = false;
return g_profile_storage.last_status;
}
if (profile_storage_open_target_files() != PROFILE_STORAGE_STATUS_OK)
{
g_profile_storage.active = false;
profile_storage_clear_session_paths();
return g_profile_storage.last_status;
}
return profile_storage_set_status(PROFILE_STORAGE_STATUS_OK);
}
profile_storage_status_t profile_storage_write_chunk(uint16_t section_id,
const uint8_t *data,
uint16_t data_size)
{
if (!g_profile_storage.active)
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_NO_ACTIVE_SESSION);
}
if ((data == NULL) || (data_size == 0u) || (data_size > APP_PROFILE_SAVE_MAX_DATA_BYTES_PER_PACKET))
{
profile_storage_abort_session();
return profile_storage_set_status(PROFILE_STORAGE_STATUS_INVALID_ARGUMENT);
}
if (section_id == APP_PROFILE_SAVE_SECTION_PROFILE_TEXT)
{
return profile_storage_write_to_file(&g_profile_storage.profile_file,
&g_profile_storage.profile_received_bytes,
g_profile_storage.profile_expected_bytes,
data,
data_size);
}
if ((section_id == APP_PROFILE_SAVE_SECTION_WAVEFORM_TEXT) && g_profile_storage.waveform_file_open)
{
return profile_storage_write_to_file(&g_profile_storage.waveform_file,
&g_profile_storage.waveform_received_bytes,
g_profile_storage.waveform_expected_bytes,
data,
data_size);
}
profile_storage_abort_session();
return profile_storage_set_status(PROFILE_STORAGE_STATUS_SECTION_ERROR);
}
profile_storage_status_t profile_storage_commit(void)
{
if (!g_profile_storage.active)
{
return profile_storage_set_status(PROFILE_STORAGE_STATUS_NO_ACTIVE_SESSION);
}
if ((g_profile_storage.profile_received_bytes != g_profile_storage.profile_expected_bytes) ||
(g_profile_storage.waveform_received_bytes != g_profile_storage.waveform_expected_bytes))
{
profile_storage_abort_session();
return profile_storage_set_status(PROFILE_STORAGE_STATUS_SIZE_MISMATCH);
}
if (g_profile_storage.profile_file_open && (f_sync(&g_profile_storage.profile_file) != FR_OK))
{
profile_storage_abort_session();
return profile_storage_set_status(PROFILE_STORAGE_STATUS_WRITE_ERROR);
}
if (g_profile_storage.waveform_file_open && (f_sync(&g_profile_storage.waveform_file) != FR_OK))
{
profile_storage_abort_session();
return profile_storage_set_status(PROFILE_STORAGE_STATUS_WRITE_ERROR);
}
profile_storage_close_open_files();
if (profile_storage_append_index_entry() != PROFILE_STORAGE_STATUS_OK)
{
profile_storage_remove_partial_files();
g_profile_storage.active = false;
profile_storage_clear_session_paths();
return g_profile_storage.last_status;
}
g_profile_storage.active = false;
g_profile_storage.profile_expected_bytes = 0u;
g_profile_storage.waveform_expected_bytes = 0u;
g_profile_storage.profile_received_bytes = 0u;
g_profile_storage.waveform_received_bytes = 0u;
profile_storage_clear_session_paths();
return profile_storage_set_status(PROFILE_STORAGE_STATUS_OK);
}
void profile_storage_cancel(void)
{
if (g_profile_storage.active)
{
profile_storage_abort_session();
}
g_profile_storage.last_status = PROFILE_STORAGE_STATUS_OK;
}

View File

@ -0,0 +1,50 @@
/**
* @file profile_storage.h
* @brief Streamed SD-card writer for GUI-initiated profile saves.
*
* Architectural note:
* The desktop GUI already knows the current human-facing configuration values.
* It serialises them into the same INI/CSV text format that standalone boot
* understands, while this service owns the SD-card side: validating the
* profile name, generating 8.3-compatible file names, streaming incoming text
* into files, and updating `profiles.csv` only after the full upload succeeds.
*/
#ifndef PROFILE_STORAGE_H
#define PROFILE_STORAGE_H
#include <stdbool.h>
#include <stdint.h>
/**
* @brief Result codes returned by the streamed profile-save service.
*/
typedef enum profile_storage_status_t {
PROFILE_STORAGE_STATUS_OK = 0,
PROFILE_STORAGE_STATUS_INVALID_ARGUMENT,
PROFILE_STORAGE_STATUS_NAME_INVALID,
PROFILE_STORAGE_STATUS_STORAGE_UNAVAILABLE,
PROFILE_STORAGE_STATUS_SESSION_ACTIVE,
PROFILE_STORAGE_STATUS_NO_ACTIVE_SESSION,
PROFILE_STORAGE_STATUS_DIRECTORY_ERROR,
PROFILE_STORAGE_STATUS_FILE_NAME_EXHAUSTED,
PROFILE_STORAGE_STATUS_FILE_OPEN_ERROR,
PROFILE_STORAGE_STATUS_WRITE_ERROR,
PROFILE_STORAGE_STATUS_INDEX_UPDATE_ERROR,
PROFILE_STORAGE_STATUS_SIZE_MISMATCH,
PROFILE_STORAGE_STATUS_SECTION_ERROR
} profile_storage_status_t;
void profile_storage_init(void);
bool profile_storage_is_active(void);
profile_storage_status_t profile_storage_get_last_status(void);
profile_storage_status_t profile_storage_begin(const char *display_name,
uint16_t profile_text_bytes,
uint16_t waveform_text_bytes);
profile_storage_status_t profile_storage_write_chunk(uint16_t section_id,
const uint8_t *data,
uint16_t data_size);
profile_storage_status_t profile_storage_commit(void);
void profile_storage_cancel(void);
#endif /* PROFILE_STORAGE_H */

214
App/Services/storage_sd.c Normal file
View File

@ -0,0 +1,214 @@
/**
* @file storage_sd.c
* @brief Narrow SD-card storage wrapper built directly on FatFs.
*/
#include "storage_sd.h"
#include "fatfs.h"
#include "board_io.h"
static uint8_t g_storage_mounted = 0u;
static uint16_t g_storage_mount_depth = 0u;
bool storage_sd_is_available(void)
{
return board_io_is_sd_card_present();
}
FRESULT storage_sd_begin(void)
{
FRESULT result;
if (!storage_sd_is_available())
{
return FR_NOT_READY;
}
if (g_storage_mount_depth > 0u)
{
++g_storage_mount_depth;
return FR_OK;
}
result = f_mount(&SDFatFS, SDPath, 1u);
if (result == FR_OK)
{
g_storage_mounted = 1u;
g_storage_mount_depth = 1u;
}
return result;
}
FRESULT storage_sd_end(void)
{
FRESULT result;
if (g_storage_mount_depth == 0u)
{
return FR_OK;
}
--g_storage_mount_depth;
if (g_storage_mount_depth > 0u)
{
return FR_OK;
}
result = f_mount(NULL, SDPath, 1u);
if (result == FR_OK)
{
g_storage_mounted = 0u;
}
else
{
g_storage_mount_depth = 1u;
}
return result;
}
FRESULT storage_sd_open_file(FIL *file, const char *path, BYTE mode)
{
FRESULT result;
if ((file == NULL) || (path == NULL))
{
return FR_INVALID_PARAMETER;
}
result = storage_sd_begin();
if (result != FR_OK)
{
return result;
}
result = f_open(file, path, mode);
if (result != FR_OK)
{
(void)storage_sd_end();
}
return result;
}
FRESULT storage_sd_close_file(FIL *file)
{
FRESULT close_result = FR_OK;
FRESULT end_result;
if (file != NULL)
{
close_result = f_close(file);
}
end_result = storage_sd_end();
if (close_result != FR_OK)
{
return close_result;
}
return end_result;
}
FRESULT storage_sd_read_bytes(const char *path, FSIZE_t offset, void *data, UINT size, UINT *bytes_read)
{
FIL file;
UINT local_bytes_read = 0u;
FRESULT result = storage_sd_open_file(&file, path, FA_READ);
if (result != FR_OK)
{
return result;
}
result = f_lseek(&file, offset);
if (result == FR_OK)
{
result = f_read(&file, data, size, &local_bytes_read);
}
(void)storage_sd_close_file(&file);
if (bytes_read != NULL)
{
*bytes_read = local_bytes_read;
}
return result;
}
FRESULT storage_sd_stat(const char *path, FILINFO *out_info)
{
FRESULT result;
if (path == NULL)
{
return FR_INVALID_PARAMETER;
}
result = storage_sd_begin();
if (result != FR_OK)
{
return result;
}
result = f_stat(path, out_info);
(void)storage_sd_end();
return result;
}
bool storage_sd_file_exists(const char *path)
{
FILINFO info;
return storage_sd_stat(path, &info) == FR_OK;
}
FRESULT storage_sd_make_directory(const char *path)
{
FRESULT result;
if (path == NULL)
{
return FR_INVALID_PARAMETER;
}
result = storage_sd_begin();
if (result != FR_OK)
{
return result;
}
result = f_mkdir(path);
if (result == FR_EXIST)
{
result = FR_OK;
}
(void)storage_sd_end();
return result;
}
FRESULT storage_sd_remove_file(const char *path)
{
FRESULT result;
if (path == NULL)
{
return FR_INVALID_PARAMETER;
}
result = storage_sd_begin();
if (result != FR_OK)
{
return result;
}
result = f_unlink(path);
if ((result == FR_NO_FILE) || (result == FR_NO_PATH))
{
result = FR_OK;
}
(void)storage_sd_end();
return result;
}

30
App/Services/storage_sd.h Normal file
View File

@ -0,0 +1,30 @@
/**
* @file storage_sd.h
* @brief Narrow SD-card storage wrapper built directly on FatFs.
*
* Architectural note:
* An earlier storage layer mixed allocation-heavy tutorial helpers with
* application-specific policies. This service now exposes only the minimal
* file-oriented operations needed by profile loading and streamed profile
* saving, while keeping FatFs details out of higher-level services.
*/
#ifndef STORAGE_SD_H
#define STORAGE_SD_H
#include <stdbool.h>
#include "ff.h"
bool storage_sd_is_available(void);
FRESULT storage_sd_begin(void);
FRESULT storage_sd_end(void);
FRESULT storage_sd_read_bytes(const char *path, FSIZE_t offset, void *data, UINT size, UINT *bytes_read);
FRESULT storage_sd_open_file(FIL *file, const char *path, BYTE mode);
FRESULT storage_sd_close_file(FIL *file);
FRESULT storage_sd_stat(const char *path, FILINFO *out_info);
bool storage_sd_file_exists(const char *path);
FRESULT storage_sd_make_directory(const char *path);
FRESULT storage_sd_remove_file(const char *path);
#endif /* STORAGE_SD_H */

87
App/Services/telemetry.c Normal file
View File

@ -0,0 +1,87 @@
/**
* @file telemetry.c
* @brief Telemetry-frame creation, checksum finalisation, and serialisation.
*/
#include "telemetry.h"
#include <string.h>
#include "app_uart_protocol.h"
void telemetry_reset(telemetry_frame_t *frame)
{
if (frame == NULL)
{
return;
}
memset(frame->words, 0, sizeof(frame->words));
frame->words[0] = APP_PACKET_HEADER_WORK_CONFIG;
}
void telemetry_set_message_id(telemetry_frame_t *frame, uint16_t message_id)
{
if (frame != NULL)
{
frame->words[13] = message_id;
}
}
void telemetry_set_live_data(telemetry_frame_t *frame,
uint16_t laser1_power,
uint16_t laser2_power,
uint32_t tick_10ms,
uint16_t laser1_temperature,
uint16_t laser2_temperature,
uint16_t adc_slot_7,
uint16_t adc_slot_8,
uint16_t adc_slot_9,
uint16_t adc_slot_10,
uint16_t adc_slot_11,
uint16_t adc_slot_12)
{
if (frame == NULL)
{
return;
}
frame->words[1] = laser1_power;
frame->words[2] = laser2_power;
frame->words[3] = (uint16_t)(tick_10ms & 0xFFFFu);
frame->words[4] = (uint16_t)((tick_10ms >> 16) & 0xFFFFu);
frame->words[5] = laser1_temperature;
frame->words[6] = laser2_temperature;
frame->words[7] = adc_slot_7;
frame->words[8] = adc_slot_8;
frame->words[9] = adc_slot_9;
frame->words[10] = adc_slot_10;
frame->words[11] = adc_slot_11;
frame->words[12] = adc_slot_12;
}
void telemetry_finalize(telemetry_frame_t *frame)
{
if (frame == NULL)
{
return;
}
frame->words[14] = app_protocol_calculate_checksum(&frame->words[1], 13u);
}
void telemetry_to_bytes(const telemetry_frame_t *frame, uint8_t *out_bytes)
{
uint8_t index;
if ((frame == NULL) || (out_bytes == NULL))
{
return;
}
for (index = 0u; index < APP_TELEMETRY_WORD_COUNT; ++index)
{
out_bytes[index * 2u] = (uint8_t)(frame->words[index] & 0x00FFu);
out_bytes[(index * 2u) + 1u] = (uint8_t)((frame->words[index] >> 8) & 0x00FFu);
}
}

30
App/Services/telemetry.h Normal file
View File

@ -0,0 +1,30 @@
/**
* @file telemetry.h
* @brief Telemetry-frame creation, checksum finalisation, and serialisation.
*/
#ifndef TELEMETRY_H
#define TELEMETRY_H
#include <stdint.h>
#include "app_types.h"
void telemetry_reset(telemetry_frame_t *frame);
void telemetry_set_message_id(telemetry_frame_t *frame, uint16_t message_id);
void telemetry_set_live_data(telemetry_frame_t *frame,
uint16_t laser1_power,
uint16_t laser2_power,
uint32_t tick_10ms,
uint16_t laser1_temperature,
uint16_t laser2_temperature,
uint16_t adc_slot_7,
uint16_t adc_slot_8,
uint16_t adc_slot_9,
uint16_t adc_slot_10,
uint16_t adc_slot_11,
uint16_t adc_slot_12);
void telemetry_finalize(telemetry_frame_t *frame);
void telemetry_to_bytes(const telemetry_frame_t *frame, uint8_t *out_bytes);
#endif /* TELEMETRY_H */

View File

@ -0,0 +1,67 @@
/**
* @file temperature_control.c
* @brief Temperature-control services for the laser TEC loops.
*/
#include "temperature_control.h"
uint16_t temperature_control_compute_pid(const laser_channel_config_t *channel_config,
laser_runtime_t *runtime_state,
uint8_t channel_index,
uint32_t current_tick_1ms,
uint32_t *shared_pid_reference_tick)
{
int32_t error;
float proportional_gain;
float integral_term;
int32_t output;
if ((channel_config == NULL) || (runtime_state == NULL) || (shared_pid_reference_tick == NULL))
{
return 32768u;
}
error = (int32_t)runtime_state->current_temperature_raw - (int32_t)channel_config->target_temperature_raw;
integral_term = runtime_state->integral_error;
if ((error < 3000) && (error > -3000))
{
integral_term += channel_config->pid_i *
(float)error *
(float)(current_tick_1ms - *shared_pid_reference_tick) /
100.0f;
}
proportional_gain = channel_config->pid_p;
if (integral_term > 32000.0f)
{
integral_term = 32000.0f;
}
else if (integral_term < -32000.0f)
{
integral_term = -32000.0f;
}
runtime_state->integral_error = integral_term;
output = 32768 + (int32_t)(proportional_gain * (float)error) + (int32_t)integral_term;
if (output < 1000)
{
output = 8800;
}
else if (output > 56800)
{
output = 56800;
}
/* Both PID channels use a shared timing reference updated after the
* second TEC computation. This preserves the original controller timing. */
if (channel_index == 2u)
{
*shared_pid_reference_tick = current_tick_1ms;
}
return (uint16_t)output;
}

View File

@ -0,0 +1,19 @@
/**
* @file temperature_control.h
* @brief Temperature-control services for the laser TEC loops.
*/
#ifndef TEMPERATURE_CONTROL_H
#define TEMPERATURE_CONTROL_H
#include <stdint.h>
#include "app_types.h"
uint16_t temperature_control_compute_pid(const laser_channel_config_t *channel_config,
laser_runtime_t *runtime_state,
uint8_t channel_index,
uint32_t current_tick_1ms,
uint32_t *shared_pid_reference_tick);
#endif /* TEMPERATURE_CONTROL_H */

244
App/Services/ui_status.c Normal file
View File

@ -0,0 +1,244 @@
/**
* @file ui_status.c
* @brief Standalone LCD/button frontend for profile-driven operation.
*
* Architectural note:
* This service owns all user-facing status formatting and button debouncing.
* The application core pushes state into this module, while this module
* returns high-level events such as "select next profile".
*/
#include "ui_status.h"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "board_io.h"
#include "lcd1602_display.h"
#define UI_STATUS_LCD_COLUMNS 16u
#define UI_STATUS_BUTTON_DEBOUNCE_TICKS_10MS 3u
typedef struct ui_button_state_t {
bool raw_pressed;
bool stable_pressed;
uint32_t last_change_tick_10ms;
} ui_button_state_t;
static char g_profile_name[APP_PROFILE_NAME_LENGTH];
static app_mode_t g_mode = APP_MODE_IDLE;
static uint8_t g_error_flags = 0u;
static uint8_t g_initialised = 0u;
static uint8_t g_display_dirty = 0u;
static ui_button_state_t g_button = {false, false, 0u};
static void ui_status_mark_dirty(void);
static void ui_status_copy_padded(char *destination, const char *source);
static const char *ui_status_mode_name(app_mode_t mode);
static void ui_status_build_line_1(char *line_buffer);
static void ui_status_build_line_2(char *line_buffer);
static void ui_status_refresh_display(void);
void ui_status_init(void)
{
bool raw_pressed;
memset(g_profile_name, 0, sizeof(g_profile_name));
g_mode = APP_MODE_IDLE;
g_error_flags = 0u;
board_io_init_standalone_ui();
raw_pressed = board_io_is_standalone_ui_button_pressed();
/*
* Synchronise the debounce state with the physical input level at boot.
* This prevents a stuck-low or already-held button from generating a
* synthetic "next profile" event immediately after startup.
*/
g_button.raw_pressed = raw_pressed;
g_button.stable_pressed = raw_pressed;
g_button.last_change_tick_10ms = 0u;
lcd1602_display_init();
g_initialised = 1u;
g_display_dirty = 1u;
ui_status_refresh_display();
}
void ui_status_set_profile_name(const char *profile_name)
{
char next_name[APP_PROFILE_NAME_LENGTH] = {0};
if (profile_name != NULL)
{
strncpy(next_name, profile_name, sizeof(next_name) - 1u);
}
if (strncmp(g_profile_name, next_name, sizeof(g_profile_name)) != 0)
{
memcpy(g_profile_name, next_name, sizeof(g_profile_name));
ui_status_mark_dirty();
}
}
void ui_status_set_mode(app_mode_t mode)
{
/*
* TX_CURRENT is a transient transport state rather than a user-visible
* operating mode. Ignoring it prevents needless LCD churn during polling.
*/
if (mode == APP_MODE_TX_CURRENT)
{
return;
}
if (g_mode != mode)
{
g_mode = mode;
ui_status_mark_dirty();
}
}
void ui_status_set_error(uint8_t error_flags)
{
if (g_error_flags != error_flags)
{
g_error_flags = error_flags;
ui_status_mark_dirty();
}
}
app_event_t ui_status_poll_event(uint32_t tick_10ms)
{
bool raw_pressed;
app_event_t event = APP_EVENT_NONE;
if (g_initialised == 0u)
{
return APP_EVENT_NONE;
}
raw_pressed = board_io_is_standalone_ui_button_pressed();
if (raw_pressed != g_button.raw_pressed)
{
g_button.raw_pressed = raw_pressed;
g_button.last_change_tick_10ms = tick_10ms;
}
else if ((tick_10ms - g_button.last_change_tick_10ms) >= UI_STATUS_BUTTON_DEBOUNCE_TICKS_10MS)
{
if (g_button.stable_pressed != g_button.raw_pressed)
{
g_button.stable_pressed = g_button.raw_pressed;
if (g_button.stable_pressed)
{
event = APP_EVENT_PROFILE_NEXT;
}
}
}
if (g_display_dirty != 0u)
{
ui_status_refresh_display();
}
return event;
}
static void ui_status_mark_dirty(void)
{
g_display_dirty = 1u;
}
static void ui_status_copy_padded(char *destination, const char *source)
{
size_t index;
for (index = 0u; index < UI_STATUS_LCD_COLUMNS; ++index)
{
destination[index] = ' ';
}
destination[UI_STATUS_LCD_COLUMNS] = '\0';
if (source == NULL)
{
return;
}
for (index = 0u; (index < UI_STATUS_LCD_COLUMNS) && (source[index] != '\0'); ++index)
{
destination[index] = source[index];
}
}
static const char *ui_status_mode_name(app_mode_t mode)
{
switch (mode)
{
case APP_MODE_WORK:
return "WORK";
case APP_MODE_ERROR:
return "ERROR";
case APP_MODE_IDLE:
default:
return "IDLE";
}
}
static void ui_status_build_line_1(char *line_buffer)
{
const char *label = g_profile_name;
if (line_buffer == NULL)
{
return;
}
if (label[0] == '\0')
{
label = (g_mode == APP_MODE_WORK) ? "Manual control" : "No profile";
}
ui_status_copy_padded(line_buffer, label);
}
static void ui_status_build_line_2(char *line_buffer)
{
char status_text[UI_STATUS_LCD_COLUMNS + 1u];
if (line_buffer == NULL)
{
return;
}
if (g_error_flags == 0u)
{
(void)snprintf(status_text, sizeof(status_text), "%s OK", ui_status_mode_name(g_mode));
}
else
{
(void)snprintf(status_text, sizeof(status_text), "%s ERR %02X", ui_status_mode_name(g_mode), g_error_flags);
}
ui_status_copy_padded(line_buffer, status_text);
}
static void ui_status_refresh_display(void)
{
char line_1[UI_STATUS_LCD_COLUMNS + 1u];
char line_2[UI_STATUS_LCD_COLUMNS + 1u];
if (g_initialised == 0u)
{
return;
}
ui_status_build_line_1(line_1);
ui_status_build_line_2(line_2);
lcd1602_display_set_lines(line_1, line_2);
g_display_dirty = 0u;
}

19
App/Services/ui_status.h Normal file
View File

@ -0,0 +1,19 @@
/**
* @file ui_status.h
* @brief Future LCD/UI abstraction used by standalone profile mode.
*/
#ifndef UI_STATUS_H
#define UI_STATUS_H
#include <stdint.h>
#include "app_types.h"
void ui_status_init(void);
void ui_status_set_profile_name(const char *profile_name);
void ui_status_set_mode(app_mode_t mode);
void ui_status_set_error(uint8_t error_flags);
app_event_t ui_status_poll_event(uint32_t tick_10ms);
#endif /* UI_STATUS_H */

View File

@ -0,0 +1,70 @@
SD card example layout
Firmware expects a plain FAT volume handled by FatFs. In this project `exFAT` is disabled in [ffconf.h](/home/europa/Desktop/RadioPhotonic_PCB_software/Inc/ffconf.h:213), so the safe choice is `FAT32` with a standard MBR partition.
Recommended card preparation:
1. Format the card as `FAT32`.
2. Copy the files from this directory to the root of the SD card, or keep the same relative folder structure.
3. Make sure `profiles.csv` is present at the root path expected by the firmware.
Expected file set:
- `profiles.csv`
- `profiles/profile_1_saw.ini`
- `profiles/profile_2_custom_wave.ini`
- `waves/wave_custom.csv`
`profiles.csv` format:
- One profile per line.
- Columns: `display_name, profile_ini_path, optional_waveform_csv_path`
- Empty lines are ignored.
- Lines starting with `#` or `;` are ignored.
Example:
```csv
Factory Saw,profiles/profile_1_saw.ini,
Custom SRAM,profiles/profile_2_custom_wave.ini,waves/wave_custom.csv
```
Profile `.ini` format:
- One `key=value` pair per line.
- Empty lines are ignored.
- Lines starting with `#` or `;` are ignored.
- Unknown keys are ignored.
Additional profile-controlled devices:
- `AD9102` waveform settings are stored through the `waveform_*` keys.
- `AD9833` settings are stored through `ad9833_enable`, `ad9833_triangle`, and `ad9833_frequency_word`.
- STM32 internal DAC settings are stored through `stm32_dac_enable` and `stm32_dac_code`.
- `DS1809` is stored as an absolute target above the minimum tap using `ds1809_apply` and `ds1809_position_from_min`.
Notes:
- `ad9833_frequency_word` uses the same raw 28-bit tuning word that the old serial command accepted.
- `ds1809_position_from_min` is clamped to the valid `0..63` range by the firmware.
- When `ds1809_apply=true`, the firmware first drives the potentiometer fully down and then steps it up to the requested absolute position.
Waveform file format:
- The firmware scans the file with `strtol()`.
- Any integer token found in the file becomes one AD9102 SRAM sample.
- Separators may be commas, spaces, tabs, or newlines.
- Do not put comments or stray digits into the waveform file, because any parsed integer will be treated as a sample.
Supported `waveform_mode` values:
- `saw`
- `generated_sram`
- `custom_sram`
Minimal boot behavior:
- On startup the firmware loads the first valid line from `profiles.csv`.
- It then loads the referenced profile `.ini`.
- If `boot_enabled=true`, the profile is activated.
- If `auto_run=true`, the board immediately enters `WORK` mode.

View File

@ -0,0 +1,3 @@
# display_name, profile_ini_path, optional_waveform_csv_path
Factory Saw,profiles/prof1.ini,
Custom SRAM,profiles/prof2.ini,waves/wave1.csv
1 # display_name profile_ini_path optional_waveform_csv_path
2 Factory Saw profiles/prof1.ini
3 Custom SRAM profiles/prof2.ini waves/wave1.csv

View File

@ -0,0 +1,47 @@
# Profile 1: standard saw waveform, immediate boot and auto-run.
profile_name=Factory Saw
boot_enabled=true
auto_run=true
work_enable=1
u5v1_enable=1
u5v2_enable=1
ld1_enable=1
ld2_enable=1
ref1_enable=1
ref2_enable=1
tec1_enable=1
tec2_enable=1
ts1_enable=1
ts2_enable=1
pid1_from_host=1
pid2_from_host=1
averages=16
message_id=100
laser1_target_temp=2500
laser2_target_temp=2520
laser1_current=1200
laser2_current=1180
laser1_pid_p=10.0
laser1_pid_i=0.01
laser2_pid_p=10.0
laser2_pid_i=0.01
waveform_mode=saw
waveform_enable=1
waveform_triangle=0
waveform_saw_step=4
waveform_pat_base=2
waveform_pat_period=65535
ad9833_enable=0
ad9833_triangle=0
ad9833_frequency_word=0
stm32_dac_enable=0
stm32_dac_code=0
ds1809_apply=true
ds1809_position_from_min=0

View File

@ -0,0 +1,51 @@
# Profile 2: custom waveform loaded from a separate CSV file.
profile_name=Custom SRAM
boot_enabled=true
auto_run=true
work_enable=1
u5v1_enable=1
u5v2_enable=1
ld1_enable=1
ld2_enable=0
ref1_enable=1
ref2_enable=0
tec1_enable=1
tec2_enable=0
ts1_enable=1
ts2_enable=0
pid1_from_host=1
pid2_from_host=1
averages=16
message_id=200
laser1_target_temp=2480
laser2_target_temp=0
laser1_current=1350
laser2_current=0
laser1_pid_p=9.5
laser1_pid_i=0.02
laser2_pid_p=0.0
laser2_pid_i=0.0
waveform_mode=custom_sram
waveform_enable=1
waveform_triangle=0
waveform_saw_step=1
waveform_pat_base=2
waveform_pat_period=65535
waveform_sample_count=16
waveform_hold_cycles=8
waveform_amplitude=4095
waveform_source=waves/wave1.csv
ad9833_enable=1
ad9833_triangle=0
ad9833_frequency_word=0x00123456
stm32_dac_enable=1
stm32_dac_code=2048
ds1809_apply=true
ds1809_position_from_min=18

View File

@ -0,0 +1,16 @@
0,
512,
1024,
1536,
2048,
3072,
4095,
3072,
2048,
1024,
0,
-1024,
-2048,
-1024,
0,
512
Can't render this file because it has a wrong number of fields in line 16.

View File

@ -1,69 +0,0 @@
/*
* File_Handling_RTOS.h
*
* Created on: 14-May-2020
* Author: Controllerstech
*/
#ifndef FILE_HANDLING_RTOS_H_
#define FILE_HANDLING_RTOS_H_
#include "fatfs.h"
#include <string.h>
#include "stdio.h"
#include "fatfs.h"
/* mounts the sd card*/
int Mount_SD (const TCHAR* path);
/* unmounts the sd card*/
int Unmount_SD (const TCHAR* path);
/* Start node to be scanned (***also used as work area***) */
FRESULT Scan_SD (char* pat);
/* Only supports removing files from home directory. Directory remover to be added soon */
FRESULT Format_SD (void);
/* write the data to the file
* @ name : is the path to the file*/
FRESULT Write_File (char *name, char *data);
/* write the data to the file
* @ name : is the path to the file tooooooooooooooooooooooooooooooooooooooooooooooooo*/
FRESULT Write_File_byte (char *name, uint8_t *data, unsigned int bytesize);
/* read data from the file
* @ name : is the path to the file*/
FRESULT Read_File (char *name);
FRESULT Seek_Read_File (char *name, uint8_t *data, unsigned int bytesize, unsigned long goto_label);
/* creates the file, if it does not exists
* @ name : is the path to the file*/
FRESULT Create_File (char *name);
/* Removes the file from the sd card
* @ name : is the path to the file*/
FRESULT Remove_File (char *name);
/* creates a directory
* @ name: is the path to the directory
*/
FRESULT Create_Dir (char *name);
/* checks the free space in the sd card*/
void Check_SD_Space (void);
/* updates the file. write pointer is set to the end of the file
* @ name : is the path to the file
*/
FRESULT Update_File (char *name, char *data);
FRESULT Update_File_float (char *name, float *data, unsigned int bytesize);
FRESULT Update_File_byte (char *name, uint8_t *data, unsigned int bytesize);
#endif /* FILE_HANDLING_RTOS_H_ */

View File

@ -62,6 +62,15 @@
#else
/* USER CODE BEGIN BSP_H_CODE */
typedef struct bsp_sd_debug_info_t
{
uint8_t last_init_status;
uint8_t last_detect_status;
uint8_t last_hal_init_status;
uint8_t last_wide_bus_status;
uint32_t last_hal_error;
} bsp_sd_debug_info_t;
/* Exported functions --------------------------------------------------------*/
uint8_t BSP_SD_Init(void);
uint8_t BSP_SD_ITConfig(void);
@ -79,6 +88,7 @@ uint8_t BSP_SD_IsDetected(void);
void BSP_SD_AbortCallback(void);
void BSP_SD_WriteCpltCallback(void);
void BSP_SD_ReadCpltCallback(void);
void BSP_SD_GetDebugInfo(bsp_sd_debug_info_t *out_info);
/* USER CODE END BSP_H_CODE */
#endif

View File

@ -68,10 +68,6 @@ void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);
/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);
/* USER CODE BEGIN EFP */
void Set_LTEC(uint8_t, uint16_t);
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
#define INP_0_Pin GPIO_PIN_3
#define INP_0_GPIO_Port GPIOF
@ -167,147 +163,25 @@ void Set_LTEC(uint8_t, uint16_t);
#define OUT_9_GPIO_Port GPIOB
/* USER CODE BEGIN Private defines */
#define CL_16 15
#define DL_16 15
#define CL_8 30
#define DL_8 30
#define TSK_8 32
#define TSK_16 16
// #define SD_Length 100
#define HALT 0
#define DECODE_ENABLE 1
#define DEFAULT_ENABLE 2
#define TRANS_S_ENABLE 3
#define TRANS_ENABLE 4
#define REMOVE_FILE 5
#define STATE 6
#define WORK_ENABLE 7
#define DECODE_TASK 8
#define RUN_TASK 9
#define AD9102_CMD 10
#define AD9833_CMD 11
#define DS1809_CMD 12
#define STM32_DAC_CMD 13
#define AD9102_WAVE_CTRL_CMD 14
#define AD9102_WAVE_DATA_CMD 15
#define SD_ERR 0x01
#define UART_ERR 0x02
#define UART_DECODE_ERR 0x04
#define TEC1_ERR 0x08
#define TEC2_ERR 0x10
#define DEFAUL_ERR 0x20
#define REMOVE_ERR 0x40
#define AD9102_ERR 0x80
#define NO_MESS 0
#define MESS_01 1
#define MESS_02 2
#define MESS_03 3
// AD9102 serial command (compatible header with PC tool)
#define AD9102_CMD_HEADER 0x8888
#define AD9102_CMD_8 10 // total bytes including header
#define AD9102_CMD_WORDS 4 // data words (flags, freq LSW, freq MSW, checksum)
#define AD9833_CMD_HEADER 0x9999
#define AD9833_CMD_8 10 // total bytes including header
#define AD9833_CMD_WORDS 4 // data words (flags, freq LSW, freq MSW, checksum)
#define DS1809_CMD_HEADER 0xAAAA
#define DS1809_CMD_8 10 // total bytes including header
#define DS1809_CMD_WORDS 4 // data words (flags, count, pulse_ms, checksum)
#define STM32_DAC_CMD_HEADER 0xBBBB
#define STM32_DAC_CMD_8 10 // total bytes including header
#define STM32_DAC_CMD_WORDS 4 // data words (flags, dac_code, reserved, checksum)
#define AD9102_WAVE_CTRL_HEADER 0xCCCC
#define AD9102_WAVE_CTRL_8 10 // total bytes including header
#define AD9102_WAVE_CTRL_WORDS 4 // data words (opcode, param0, param1, checksum)
#define AD9102_WAVE_DATA_HEADER 0xDDDD
#define AD9102_WAVE_DATA_8 30 // total bytes including header
#define AD9102_WAVE_DATA_WORDS 14 // data words (count, 12 samples, checksum)
#define AD9102_ON_SPI2 1
// AD9102 CS (chip select) uses AD9102_CS_* pin definitions above.
typedef struct{
uint8_t WORK_EN;
uint8_t U5V1_EN;
uint8_t U5V2_EN;
uint8_t LD1_EN;
uint8_t LD2_EN;
uint8_t REF1_EN;
uint8_t REF2_EN;
uint8_t TEC1_EN;
uint8_t TEC2_EN;
uint8_t TS1_EN;
uint8_t TS2_EN;
uint8_t SD_EN;
uint8_t PI1_RD;
uint8_t PI2_RD;
uint16_t AVERAGES;
uint16_t MES_ID;
}Work_SetupTypeDef;
typedef struct{
uint16_t LD_TEMP;
float P_coef_temp;
float I_coef_temp;
uint16_t CURRENT;
}LDx_SetupTypeDef;
typedef struct{
uint16_t LD_CURR_TEMP;
float e_integral;
uint16_t POWER;
}LDx_ParamTypeDef;
typedef enum _task_type_t
{
TT_CHANGE_CURR_1 = 0x1,
TT_CHANGE_CURR_2 = 0x2,
TT_CHANGE_TEMP_1 = 0x3,
TT_CHANGE_TEMP_2 = 0x4,
} task_type_t;
typedef struct _task_t
{
task_type_t task_type;
float min_param;
float max_param;
float delta_param; // change step between min_param and max_param
float current_param; // current_param is iterating by summary with delta_param every interrupt
uint8_t dt; // microseconds
uint16_t tau; // milliseconds or microseconds?
float sec_param;
float curr;
float temp;
float i_coef_1;
float p_coef_1;
float i_coef_2;
float p_coef_2;
} task_t;
typedef struct{
task_type_t task_type;
uint16_t signal_pin;
GPIO_TypeDef * signal_port;
uint16_t param;
uint8_t state; //0 -- disabled (do nothing);
//1 -- update LD current;
//2 -- blinking, set LD ON now;
//3 -- blinking, set LD OFF now
} LD_Blinker_StateTypeDef;
/* Board-level extensions only. Application-layer types live under App/. */
#define UI_LCD_RS_Pin GPIO_PIN_9
#define UI_LCD_RS_GPIO_Port GPIOG
#define UI_LCD_V0_REF_Pin GPIO_PIN_7
#define UI_LCD_V0_REF_GPIO_Port GPIOD
#define UI_LCD_CONTRAST_PWM_Pin GPIO_PIN_8
#define UI_LCD_CONTRAST_PWM_GPIO_Port GPIOB
#define UI_LCD_E_Pin OUT_0_Pin
#define UI_LCD_E_GPIO_Port OUT_0_GPIO_Port
#define UI_BUTTON_Pin OUT_1_Pin
#define UI_BUTTON_GPIO_Port OUT_1_GPIO_Port
#define UI_LCD_D4_Pin OUT_2_Pin
#define UI_LCD_D4_GPIO_Port OUT_2_GPIO_Port
#define UI_LCD_D5_Pin OUT_3_Pin
#define UI_LCD_D5_GPIO_Port OUT_3_GPIO_Port
#define UI_LCD_D6_Pin OUT_4_Pin
#define UI_LCD_D6_GPIO_Port OUT_4_GPIO_Port
#define UI_LCD_D7_Pin OUT_5_Pin
#define UI_LCD_D7_GPIO_Port OUT_5_GPIO_Port
/* USER CODE END Private defines */
#ifdef __cplusplus

View File

@ -28,6 +28,7 @@
/* USER CODE END firstSection */
/* Includes ------------------------------------------------------------------*/
#include "ff_gen_drv.h"
#include "bsp_driver_sd.h"
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
@ -36,6 +37,8 @@ extern const Diskio_drvTypeDef SD_Driver;
/* USER CODE BEGIN lastSection */
/* can be used to modify / undefine previous code or add new definitions */
DSTATUS sd_diskio_debug_get_last_initialize_status(void);
DSTATUS sd_diskio_debug_get_last_status_result(void);
/* USER CODE END lastSection */
#endif /* __SD_DISKIO_H */

View File

@ -56,11 +56,8 @@ void DebugMon_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
void ADC_IRQHandler(void);
void TIM1_UP_TIM10_IRQHandler(void);
void TIM1_TRG_COM_TIM11_IRQHandler(void);
void TIM2_IRQHandler(void);
void USART1_IRQHandler(void);
void TIM8_UP_TIM13_IRQHandler(void);
void TIM5_IRQHandler(void);
void TIM6_DAC_IRQHandler(void);
void TIM7_IRQHandler(void);

View File

@ -1,77 +0,0 @@
// File: STM32F76x_77x.dbgconf
// Version: 1.0.0
// Note: refer to STM32F76xxx STM32F77xxx reference manual (RM0410)
// refer to STM32F76xxx STM32F77xxx datasheets
// <<< Use Configuration Wizard in Context Menu >>>
// <h> Debug MCU configuration register (DBGMCU_CR)
// <o.2> DBG_STANDBY <i> Debug standby mode
// <o.1> DBG_STOP <i> Debug stop mode
// <o.0> DBG_SLEEP <i> Debug sleep mode
// </h>
DbgMCU_CR = 0x00000007;
// <h> Debug MCU APB1 freeze register (DBGMCU_APB1_FZ)
// <i> Reserved bits must be kept at reset value
// <o.26> DBG_CAN2_STOP <i> Debug CAN2 stopped when core is halted
// <o.25> DBG_CAN1_STOP <i> Debug CAN1 stopped when core is halted
// <o.24> DBG_I2C4_SMBUS_TIMEOUT <i> SMBUS timeout mode stopped when core is halted
// <o.23> DBG_I2C3_SMBUS_TIMEOUT <i> SMBUS timeout mode stopped when core is halted
// <o.22> DBG_I2C2_SMBUS_TIMEOUT <i> SMBUS timeout mode stopped when core is halted
// <o.21> DBG_I2C1_SMBUS_TIMEOUT <i> SMBUS timeout mode stopped when core is halted
// <o.13> DBG_CAN3_STOP <i> Debug CAN3 stopped when core is halted
// <o.12> DBG_IWDG_STOP <i> Debug independent watchdog stopped when core is halted
// <o.11> DBG_WWDG_STOP <i> Debug window watchdog stopped when core is halted
// <o.10> DBG_RTC_STOP <i> RTC stopped when core is halted
// <o.9> DBG_LPTIM1_STOP <i> LPTMI1 counter stopped when core is halted
// <o.8> DBG_TIM14_STOP <i> TIM14 counter stopped when core is halted
// <o.7> DBG_TIM13_STOP <i> TIM13 counter stopped when core is halted
// <o.6> DBG_TIM12_STOP <i> TIM12 counter stopped when core is halted
// <o.5> DBG_TIM7_STOP <i> TIM7 counter stopped when core is halted
// <o.4> DBG_TIM6_STOP <i> TIM6 counter stopped when core is halted
// <o.3> DBG_TIM5_STOP <i> TIM5 counter stopped when core is halted
// <o.2> DBG_TIM4_STOP <i> TIM4 counter stopped when core is halted
// <o.1> DBG_TIM3_STOP <i> TIM3 counter stopped when core is halted
// <o.0> DBG_TIM2_STOP <i> TIM2 counter stopped when core is halted
// </h>
DbgMCU_APB1_Fz = 0x00000000;
// <h> Debug MCU APB2 freeze register (DBGMCU_APB2_FZ)
// <i> Reserved bits must be kept at reset value
// <o.18> DBG_TIM11_STOP <i> TIM11 counter stopped when core is halted
// <o.17> DBG_TIM10_STOP <i> TIM10 counter stopped when core is halted
// <o.16> DBG_TIM9_STOP <i> TIM9 counter stopped when core is halted
// <o.1> DBG_TIM8_STOP <i> TIM8 counter stopped when core is halted
// <o.0> DBG_TIM1_STOP <i> TIM1 counter stopped when core is halted
// </h>
DbgMCU_APB2_Fz = 0x00000000;
// <h> TPIU Pin Routing (TRACECLK fixed on Pin PE2)
// <i> TRACECLK: Pin PE2
// <o1> TRACED0
// <i> ETM Trace Data 0
// <0x00040003=> Pin PE3
// <0x00020001=> Pin PC1
// <0x0006000D=> Pin PG13
// <o2> TRACED1
// <i> ETM Trace Data 1
// <0x00040004=> Pin PE4
// <0x00020008=> Pin PC8
// <0x0006000E=> Pin PG14
// <o3> TRACED2
// <i> ETM Trace Data 2
// <0x00040005=> Pin PE5
// <0x00030002=> Pin PD2
// <o4> TRACED3
// <i> ETM Trace Data 3
// <0x00040006=> Pin PE6
// <0x0002000C=> Pin PC12
// </h>
TraceClk_Pin = 0x00040002;
TraceD0_Pin = 0x00040003;
TraceD1_Pin = 0x00040004;
TraceD2_Pin = 0x00040005;
TraceD3_Pin = 0x00040006;
// <<< end of configuration section >>>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<component_viewer schemaVersion="0.1" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="Component_Viewer.xsd">
<component name="EventRecorderStub" version="1.0.0"/> <!--name and version of the component-->
<events>
</events>
</component_viewer>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,822 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<ProjectOpt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="project_optx.xsd">
<SchemaVersion>1.0</SchemaVersion>
<Header>### uVision Project, (C) Keil Software</Header>
<Extensions>
<cExt>*.c</cExt>
<aExt>*.s*; *.src; *.a*</aExt>
<oExt>*.obj; *.o</oExt>
<lExt>*.lib</lExt>
<tExt>*.txt; *.h; *.inc; *.md</tExt>
<pExt>*.plm</pExt>
<CppX>*.cpp</CppX>
<nMigrate>0</nMigrate>
</Extensions>
<DaveTm>
<dwLowDateTime>0</dwLowDateTime>
<dwHighDateTime>0</dwHighDateTime>
</DaveTm>
<Target>
<TargetName>For_stm32</TargetName>
<ToolsetNumber>0x4</ToolsetNumber>
<ToolsetName>ARM-ADS</ToolsetName>
<TargetOption>
<CLKADS>25000000</CLKADS>
<OPTTT>
<gFlags>1</gFlags>
<BeepAtEnd>1</BeepAtEnd>
<RunSim>0</RunSim>
<RunTarget>1</RunTarget>
<RunAbUc>0</RunAbUc>
</OPTTT>
<OPTHX>
<HexSelection>1</HexSelection>
<FlashByte>65535</FlashByte>
<HexRangeLowAddress>0</HexRangeLowAddress>
<HexRangeHighAddress>0</HexRangeHighAddress>
<HexOffset>0</HexOffset>
</OPTHX>
<OPTLEX>
<PageWidth>79</PageWidth>
<PageLength>66</PageLength>
<TabStop>8</TabStop>
<ListingPath></ListingPath>
</OPTLEX>
<ListingPage>
<CreateCListing>1</CreateCListing>
<CreateAListing>1</CreateAListing>
<CreateLListing>1</CreateLListing>
<CreateIListing>0</CreateIListing>
<AsmCond>1</AsmCond>
<AsmSymb>1</AsmSymb>
<AsmXref>0</AsmXref>
<CCond>1</CCond>
<CCode>0</CCode>
<CListInc>0</CListInc>
<CSymb>0</CSymb>
<LinkerCodeListing>0</LinkerCodeListing>
</ListingPage>
<OPTXL>
<LMap>1</LMap>
<LComments>1</LComments>
<LGenerateSymbols>1</LGenerateSymbols>
<LLibSym>1</LLibSym>
<LLines>1</LLines>
<LLocSym>1</LLocSym>
<LPubSym>1</LPubSym>
<LXref>0</LXref>
<LExpSel>0</LExpSel>
</OPTXL>
<OPTFL>
<tvExp>1</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<IsCurrentTarget>1</IsCurrentTarget>
</OPTFL>
<CpuCode>18</CpuCode>
<DebugOpt>
<uSim>0</uSim>
<uTrg>1</uTrg>
<sLdApp>1</sLdApp>
<sGomain>1</sGomain>
<sRbreak>1</sRbreak>
<sRwatch>1</sRwatch>
<sRmem>1</sRmem>
<sRfunc>1</sRfunc>
<sRbox>1</sRbox>
<tLdApp>1</tLdApp>
<tGomain>1</tGomain>
<tRbreak>1</tRbreak>
<tRwatch>1</tRwatch>
<tRmem>1</tRmem>
<tRfunc>1</tRfunc>
<tRbox>1</tRbox>
<tRtrace>1</tRtrace>
<sRSysVw>1</sRSysVw>
<tRSysVw>1</tRSysVw>
<sRunDeb>0</sRunDeb>
<sLrtime>0</sLrtime>
<bEvRecOn>1</bEvRecOn>
<bSchkAxf>0</bSchkAxf>
<bTchkAxf>0</bTchkAxf>
<nTsel>6</nTsel>
<sDll></sDll>
<sDllPa></sDllPa>
<sDlgDll></sDlgDll>
<sDlgPa></sDlgPa>
<sIfile></sIfile>
<tDll></tDll>
<tDllPa></tDllPa>
<tDlgDll></tDlgDll>
<tDlgPa></tDlgPa>
<tIfile></tIfile>
<pMon>STLink\ST-LINKIII-KEIL_SWO.dll</pMon>
</DebugOpt>
<TargetDriverDllRegistry>
<SetRegEntry>
<Number>0</Number>
<Key>ARMRTXEVENTFLAGS</Key>
<Name>-L70 -Z18 -C0 -M0 -T1</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>DLGTARM</Key>
<Name>(1010=-1,-1,-1,-1,0)(6017=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(6016=-1,-1,-1,-1,0)(1012=-1,-1,-1,-1,0)</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>ARMDBGFLAGS</Key>
<Name></Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>DLGUARM</Key>
<Name>(105=-1,-1,-1,-1,0)</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>UL2CM3</Key>
<Name>UL2CM3(-S0 -C0 -P0 -FD20020000 -FC1000 -FN2 -FF0STM32F7x_2048 -FS08000000 -FL0200000 -FF1STM32F7xTCM_2048 -FS1200000 -FL1200000 -FP0($$Device:STM32F767ZITx$CMSIS\Flash\STM32F7x_2048.FLM) -FP1($$Device:STM32F767ZITx$CMSIS\Flash\STM32F7xTCM_2048.FLM))</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>ST-LINKIII-KEIL_SWO</Key>
<Name>-U33FF6A064246313349321057 -O2254 -SF10000 -C0 -A0 -I0 -HNlocalhost -HP7184 -P1 -N00("ARM CoreSight SW-DP (ARM Core") -D00(5BA02477) -L00(0) -TO131090 -TC10000000 -TT10000000 -TP21 -TDS8000 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO15 -FD20000000 -FC1000 -FN2 -FF0STM32F7x_2048.FLM -FS08000000 -FL0200000 -FP0($$Device:STM32F767ZITx$CMSIS\Flash\STM32F7x_2048.FLM) -FF1STM32F7xTCM_2048.FLM -FS1200000 -FL1200000 -FP1($$Device:STM32F767ZITx$CMSIS\Flash\STM32F7xTCM_2048.FLM)</Name>
</SetRegEntry>
</TargetDriverDllRegistry>
<Breakpoint>
<Bp>
<Number>0</Number>
<Type>0</Type>
<LineNumber>1437</LineNumber>
<EnabledFlag>1</EnabledFlag>
<Address>134221116</Address>
<ByteObject>0</ByteObject>
<HtxType>0</HtxType>
<ManyObjects>0</ManyObjects>
<SizeOfObject>0</SizeOfObject>
<BreakByAccess>0</BreakByAccess>
<BreakIfRCount>1</BreakIfRCount>
<Filename>../Src/main.c</Filename>
<ExecCommand></ExecCommand>
<Expression>\\For_stm32\../Src/main.c\1437</Expression>
</Bp>
<Bp>
<Number>1</Number>
<Type>0</Type>
<LineNumber>1446</LineNumber>
<EnabledFlag>1</EnabledFlag>
<Address>0</Address>
<ByteObject>0</ByteObject>
<HtxType>0</HtxType>
<ManyObjects>0</ManyObjects>
<SizeOfObject>0</SizeOfObject>
<BreakByAccess>0</BreakByAccess>
<BreakIfRCount>0</BreakIfRCount>
<Filename>../Src/main.c</Filename>
<ExecCommand></ExecCommand>
<Expression></Expression>
</Bp>
</Breakpoint>
<WatchWindow1>
<Ww>
<count>0</count>
<WinNumber>1</WinNumber>
<ItemText>COMMAND</ItemText>
</Ww>
<Ww>
<count>1</count>
<WinNumber>1</WinNumber>
<ItemText>LD1_curr_setup</ItemText>
</Ww>
<Ww>
<count>2</count>
<WinNumber>1</WinNumber>
<ItemText>LD1_param</ItemText>
</Ww>
<Ww>
<count>3</count>
<WinNumber>1</WinNumber>
<ItemText>LD2_curr_setup</ItemText>
</Ww>
<Ww>
<count>4</count>
<WinNumber>1</WinNumber>
<ItemText>LD2_param</ItemText>
</Ww>
</WatchWindow1>
<Tracepoint>
<THDelay>0</THDelay>
</Tracepoint>
<DebugFlag>
<trace>0</trace>
<periodic>1</periodic>
<aLwin>1</aLwin>
<aCover>0</aCover>
<aSer1>0</aSer1>
<aSer2>0</aSer2>
<aPa>0</aPa>
<viewmode>1</viewmode>
<vrSel>0</vrSel>
<aSym>0</aSym>
<aTbox>0</aTbox>
<AscS1>0</AscS1>
<AscS2>0</AscS2>
<AscS3>0</AscS3>
<aSer3>0</aSer3>
<eProf>0</eProf>
<aLa>0</aLa>
<aPa1>0</aPa1>
<AscS4>0</AscS4>
<aSer4>0</aSer4>
<StkLoc>1</StkLoc>
<TrcWin>0</TrcWin>
<newCpu>0</newCpu>
<uProt>0</uProt>
</DebugFlag>
<LintExecutable></LintExecutable>
<LintConfigFile></LintConfigFile>
<bLintAuto>0</bLintAuto>
<bAutoGenD>0</bAutoGenD>
<LntExFlags>0</LntExFlags>
<pMisraName></pMisraName>
<pszMrule></pszMrule>
<pSingCmds></pSingCmds>
<pMultCmds></pMultCmds>
<pMisraNamep></pMisraNamep>
<pszMrulep></pszMrulep>
<pSingCmdsp></pSingCmdsp>
<pMultCmdsp></pMultCmdsp>
<SystemViewers>
<Entry>
<Name>System Viewer\USART1</Name>
<WinId>35905</WinId>
</Entry>
</SystemViewers>
<DebugDescription>
<Enable>1</Enable>
<EnableFlashSeq>0</EnableFlashSeq>
<EnableLog>0</EnableLog>
<Protocol>2</Protocol>
<DbgClock>10000000</DbgClock>
</DebugDescription>
</TargetOption>
</Target>
<Group>
<GroupName>Application/MDK-ARM</GroupName>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
<File>
<GroupNumber>1</GroupNumber>
<FileNumber>1</FileNumber>
<FileType>2</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>startup_stm32f767xx.s</PathWithFileName>
<FilenameWithoutPath>startup_stm32f767xx.s</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
<GroupName>Application/User</GroupName>
<tvExp>1</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
<File>
<GroupNumber>2</GroupNumber>
<FileNumber>2</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\Src\File_Handling.c</PathWithFileName>
<FilenameWithoutPath>File_Handling.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>2</GroupNumber>
<FileNumber>3</FileNumber>
<FileType>1</FileType>
<tvExp>1</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Src/main.c</PathWithFileName>
<FilenameWithoutPath>main.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>2</GroupNumber>
<FileNumber>4</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Src/bsp_driver_sd.c</PathWithFileName>
<FilenameWithoutPath>bsp_driver_sd.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>2</GroupNumber>
<FileNumber>5</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Src/sd_diskio.c</PathWithFileName>
<FilenameWithoutPath>sd_diskio.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>2</GroupNumber>
<FileNumber>6</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Src/fatfs.c</PathWithFileName>
<FilenameWithoutPath>fatfs.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>2</GroupNumber>
<FileNumber>7</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Src/fatfs_platform.c</PathWithFileName>
<FilenameWithoutPath>fatfs_platform.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>2</GroupNumber>
<FileNumber>8</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Src/stm32f7xx_it.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_it.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>2</GroupNumber>
<FileNumber>9</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Src/stm32f7xx_hal_msp.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_msp.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
<GroupName>Drivers/STM32F7xx_HAL_Driver</GroupName>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>10</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_adc.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_adc.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>11</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_adc_ex.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_adc_ex.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>12</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_rcc.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_rcc.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>13</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_rcc_ex.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_rcc_ex.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>14</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_flash.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_flash.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>15</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_flash_ex.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_flash_ex.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>16</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_gpio.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_gpio.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>17</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_dma.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_dma.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>18</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_dma_ex.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_dma_ex.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>19</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_pwr.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_pwr.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>20</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_pwr_ex.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_pwr_ex.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>21</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_cortex.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_cortex.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>22</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>23</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_i2c.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_i2c.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>24</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_i2c_ex.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_i2c_ex.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>25</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_exti.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_exti.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>26</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_rcc.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_ll_rcc.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>27</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_utils.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_ll_utils.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>28</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_exti.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_ll_exti.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>29</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_gpio.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_ll_gpio.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>30</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_dma.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_ll_dma.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>31</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_sdmmc.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_ll_sdmmc.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>32</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_sd.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_sd.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>33</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_spi.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_ll_spi.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>34</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_tim.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_tim.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>35</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_tim_ex.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_hal_tim_ex.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>36</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_tim.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_ll_tim.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>37</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_usart.c</PathWithFileName>
<FilenameWithoutPath>stm32f7xx_ll_usart.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
<GroupName>Drivers/CMSIS</GroupName>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>38</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Src/system_stm32f7xx.c</PathWithFileName>
<FilenameWithoutPath>system_stm32f7xx.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
<GroupName>Middlewares/FatFs</GroupName>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>39</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Middlewares/Third_Party/FatFs/src/diskio.c</PathWithFileName>
<FilenameWithoutPath>diskio.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>40</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Middlewares/Third_Party/FatFs/src/ff.c</PathWithFileName>
<FilenameWithoutPath>ff.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>41</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Middlewares/Third_Party/FatFs/src/ff_gen_drv.c</PathWithFileName>
<FilenameWithoutPath>ff_gen_drv.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>42</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Middlewares/Third_Party/FatFs/src/option/syscall.c</PathWithFileName>
<FilenameWithoutPath>syscall.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
<GroupName>::CMSIS</GroupName>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>1</RteFlg>
</Group>
</ProjectOpt>

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
[EXTDLL]
Count=0

View File

@ -1,47 +0,0 @@
--cpu Cortex-M7.fp.dp
"for_stm32\startup_stm32f767xx.o"
"for_stm32\file_handling.o"
"for_stm32\main.o"
"for_stm32\bsp_driver_sd.o"
"for_stm32\sd_diskio.o"
"for_stm32\fatfs.o"
"for_stm32\fatfs_platform.o"
"for_stm32\stm32f7xx_it.o"
"for_stm32\stm32f7xx_hal_msp.o"
"for_stm32\stm32f7xx_hal_adc.o"
"for_stm32\stm32f7xx_hal_adc_ex.o"
"for_stm32\stm32f7xx_hal_rcc.o"
"for_stm32\stm32f7xx_hal_rcc_ex.o"
"for_stm32\stm32f7xx_hal_flash.o"
"for_stm32\stm32f7xx_hal_flash_ex.o"
"for_stm32\stm32f7xx_hal_gpio.o"
"for_stm32\stm32f7xx_hal_dma.o"
"for_stm32\stm32f7xx_hal_dma_ex.o"
"for_stm32\stm32f7xx_hal_pwr.o"
"for_stm32\stm32f7xx_hal_pwr_ex.o"
"for_stm32\stm32f7xx_hal_cortex.o"
"for_stm32\stm32f7xx_hal.o"
"for_stm32\stm32f7xx_hal_i2c.o"
"for_stm32\stm32f7xx_hal_i2c_ex.o"
"for_stm32\stm32f7xx_hal_exti.o"
"for_stm32\stm32f7xx_ll_rcc.o"
"for_stm32\stm32f7xx_ll_utils.o"
"for_stm32\stm32f7xx_ll_exti.o"
"for_stm32\stm32f7xx_ll_gpio.o"
"for_stm32\stm32f7xx_ll_dma.o"
"for_stm32\stm32f7xx_ll_sdmmc.o"
"for_stm32\stm32f7xx_hal_sd.o"
"for_stm32\stm32f7xx_ll_spi.o"
"for_stm32\stm32f7xx_hal_tim.o"
"for_stm32\stm32f7xx_hal_tim_ex.o"
"for_stm32\stm32f7xx_ll_tim.o"
"for_stm32\stm32f7xx_ll_usart.o"
"for_stm32\system_stm32f7xx.o"
"for_stm32\diskio.o"
"for_stm32\ff.o"
"for_stm32\ff_gen_drv.o"
"for_stm32\syscall.o"
--strict --scatter "For_stm32\For_stm32.sct"
--summary_stderr --info summarysizes --map --load_addr_map_info --xref --callgraph --symbols
--info sizes --info totals --info unused --info veneers
--list "For_stm32.map" -o For_stm32\For_stm32.axf

View File

@ -1,16 +0,0 @@
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00200000 { ; load region size_region
ER_IROM1 0x08000000 0x00200000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00080000 { ; RW data
.ANY (+RW +ZI)
}
}

View File

@ -1,15 +0,0 @@
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00200000 { ; load region size_region
ER_IROM1 0x08000000 0x00200000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x20000000 0x00080000 { ; RW data
.ANY (+RW +ZI)
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More