big refactoring and features added
This commit is contained in:
716
App/Devices/ad9102_device.c
Normal file
716
App/Devices/ad9102_device.c
Normal 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;
|
||||
}
|
||||
52
App/Devices/ad9102_device.h
Normal file
52
App/Devices/ad9102_device.h
Normal 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 */
|
||||
64
App/Devices/ad9833_device.c
Normal file
64
App/Devices/ad9833_device.c
Normal 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);
|
||||
}
|
||||
13
App/Devices/ad9833_device.h
Normal file
13
App/Devices/ad9833_device.h
Normal 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 */
|
||||
135
App/Devices/adc_mux.c
Normal file
135
App/Devices/adc_mux.c
Normal file
@ -0,0 +1,135 @@
|
||||
/**
|
||||
* @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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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
14
App/Devices/adc_mux.h
Normal 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 */
|
||||
22
App/Devices/board_handles.h
Normal file
22
App/Devices/board_handles.h
Normal 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
257
App/Devices/board_io.c
Normal 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
33
App/Devices/board_io.h
Normal 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 */
|
||||
83
App/Devices/ds1809_device.c
Normal file
83
App/Devices/ds1809_device.c
Normal 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);
|
||||
}
|
||||
32
App/Devices/ds1809_device.h
Normal file
32
App/Devices/ds1809_device.h
Normal 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
103
App/Devices/laser_dac.c
Normal 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
13
App/Devices/laser_dac.h
Normal 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 */
|
||||
199
App/Devices/lcd1602_display.c
Normal file
199
App/Devices/lcd1602_display.c
Normal 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);
|
||||
}
|
||||
}
|
||||
19
App/Devices/lcd1602_display.h
Normal file
19
App/Devices/lcd1602_display.h
Normal 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 */
|
||||
45
App/Devices/stm32_dac_output.c
Normal file
45
App/Devices/stm32_dac_output.c
Normal 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;
|
||||
}
|
||||
}
|
||||
14
App/Devices/stm32_dac_output.h
Normal file
14
App/Devices/stm32_dac_output.h
Normal 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 */
|
||||
68
App/Devices/uart_transport.c
Normal file
68
App/Devices/uart_transport.c
Normal 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;
|
||||
}
|
||||
19
App/Devices/uart_transport.h
Normal file
19
App/Devices/uart_transport.h
Normal 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 */
|
||||
Reference in New Issue
Block a user