This commit is contained in:
David Rice
2026-01-28 12:16:24 +00:00
parent c01006628f
commit 869aba188a
14 changed files with 14717 additions and 15385 deletions

View File

@@ -21,19 +21,22 @@
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stm32g4xx_hal.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define AVG_WINDOW 32
#define FILTER_SIZE 128 /* Must be a power of 2 for optimal bit-shifting (e.g., 8, 16, 32, 64) */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef struct
{
uint16_t buffer[FILTER_SIZE]; /* Circular buffer to hold N samples */
uint32_t sum; /* Running sum of all samples */
uint8_t index; /* Current position in the buffer */
} MovingAverageFilter;
uint32_t buffer[AVG_WINDOW];
uint8_t index;
uint32_t sum;
} ADC_Filter;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
@@ -41,10 +44,6 @@ typedef struct
#define IN_SYNC_BYTE_1 'A'
#define IN_SYNC_BYTE_2 'R'
#define MAX_PWM 63999
#define KP 0.15f /* Start small and increase slowly */
#define DEADBAND_MV 25 /* Ignore errors smaller than 25mV */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
@@ -80,20 +79,54 @@ uint16_t rx_checksum_hold = 0x0000;
uint8_t power_state_value = 0x00;
uint8_t command = 0x00;
uint8_t adc_task_flag = 0x00;
uint16_t vin_adc_val = 0x0000;
uint16_t vout_adc_val = 0x0000;
uint16_t vout_adc_val_av = 0x0000;
uint32_t vdd_ref = 0x00000000;
uint32_t vin_val = 0x00000000;
uint32_t vout_val = 0x00000000;
float vdd_ref = 0;
uint32_t vdd_ref_uint = 0x00000000;
uint32_t v_target = 0x00000000;
uint8_t vset_task_flag = 0x00;
uint8_t serial_number_flag = 0x00;
uint8_t serial_number[19] = "ARRIVE-POWERSIM-001";
uint16_t pwm_value = 0x0000;
uint32_t pwm_value_store = 0x00000000;
uint32_t vout_adc_val = 0x0000000;
uint32_t filtered_adc = 0x0000000;
uint32_t pwm_max = 20000;
float vout = 0;
float vout_adj = 0;
uint32_t vout_adj_uint = 0x00000000;
uint8_t buffer_count = 0x00;
uint32_t v_scale = 0x00000000;
/* Initialise MA filter */
MovingAverageFilter movavFilter;
/* Stored in RAM */
const uint16_t dataBuffer[25] =
{
0x0000,
0x000A,
0x00B5,
0x00C6,
0x00D5,
0x00E3,
0x00F2,
0x0100,
0x010F,
0x011E,
0x012D,
0x013D,
0x014E,
0x0163,
0x017B,
0x0195,
0x01B2,
0x01D0,
0x01F6,
0x021A,
0x0244,
0x0272,
0x02A2,
0x02D8,
0x0311
};
ADC_Filter v_out_filter;
/* USER CODE END PV */
@@ -108,14 +141,12 @@ static void MX_TIM16_Init(void);
/* USER CODE BEGIN PFP */
void power_switch (uint8_t state);
void adc_task(void);
uint32_t get_actual_vdda(ADC_HandleTypeDef *hadc);
float get_actual_vdda(ADC_HandleTypeDef *hadc);
void voltage_conversion_task(void);
void voltage_conversion_task_no_tx(void);
uint32_t get_divider_input_mv(uint32_t raw_adc_value, uint32_t vdda_mv);
void serial_number_task (void);
void MA_Init(MovingAverageFilter *filter);
uint16_t MA_Update(MovingAverageFilter *filter, uint16_t new_sample);
void Control_Loop_Update(uint32_t setpoint_mv, uint32_t measured_mv);
int32_t update_pwm(uint32_t measured_mv);
void ADC_Filter_Init(ADC_Filter *f);
uint32_t ADC_Filter_Update(ADC_Filter *f, uint32_t new_sample);
/* USER CODE END PFP */
@@ -173,34 +204,54 @@ int main(void)
rx_len = 0x00;
rx_len_counter = 0x00;
adc_task_flag = 0x00;
pwm_value = 0x0000;
HAL_UART_Receive_IT(&huart2, rx_hold_buffer, 1);
/* Get real VDDA value */
vdd_ref = get_actual_vdda(&hadc1);
/* Start output PWM at zero */
__HAL_TIM_SET_COMPARE(&htim16, TIM_CHANNEL_1, 0);
HAL_TIM_PWM_Start(&htim16, TIM_CHANNEL_1);
ADC_Filter_Init(&v_out_filter);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if (adc_task_flag == 0xff)
{
adc_task_flag = 0x00;
if (vset_task_flag != 0xff)
{
adc_task();
vout_adc_val_av = MA_Update (&movavFilter, vout_adc_val);
}
tx_len = 0x04;
voltage_conversion_task();
tx_buffer[0] = IN_SYNC_BYTE_1;
tx_buffer[1] = IN_SYNC_BYTE_2;
tx_buffer[2] = tx_len;
tx_buffer[3] = (uint8_t)((vout_adj_uint >> 24) & 0xFF);
tx_buffer[4] = (uint8_t)((vout_adj_uint >> 16) & 0xFF);
tx_buffer[5] = (uint8_t)((vout_adj_uint >> 8) & 0xFF);
tx_buffer[6] = (uint8_t)(vout_adj_uint & 0xFF);
/* Need to apply checksum to all data bits */
for (tx_len_counter = 0x00; tx_len_counter < tx_len; tx_len_counter++)
{
tx_checksum += tx_buffer[tx_len_counter + 3];
}
tx_checksum = ~tx_checksum;
tx_buffer[7] = (uint8_t)((tx_checksum >> 8) & 0xFF);
tx_buffer[8] = (uint8_t)(tx_checksum & 0xFF);
tx_len = 0x13;
HAL_UART_Transmit(&huart2, tx_buffer, tx_len, 100);
}
if (serial_number_flag == 0xff)
@@ -212,16 +263,29 @@ int main(void)
if (vset_task_flag == 0xff)
{
adc_task();
vout_adc_val_av = MA_Update (&movavFilter, vout_adc_val);
voltage_conversion_task_no_tx();
Control_Loop_Update(v_target, vout_val);
filtered_adc = ADC_Filter_Update(&v_out_filter, vout_adc_val);
vout = ((float)filtered_adc / 4095.0f) * vdd_ref;
vout_adj = vout * 10.9f;
vout_adj_uint = (uint32_t)vout_adj;
pwm_value_store = update_pwm(vout_adj_uint);
pwm_value = (uint16_t)pwm_value_store;
__HAL_TIM_SET_COMPARE(&htim16, TIM_CHANNEL_1, pwm_value);
}
else
{
__HAL_TIM_SET_COMPARE(&htim16, TIM_CHANNEL_1, 0);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
@@ -369,11 +433,11 @@ static void MX_ADC2_Init(void)
hadc2.Init.Resolution = ADC_RESOLUTION_12B;
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc2.Init.GainCompensation = 0;
hadc2.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc2.Init.LowPowerAutoWait = DISABLE;
hadc2.Init.ContinuousConvMode = DISABLE;
hadc2.Init.NbrOfConversion = 2;
hadc2.Init.NbrOfConversion = 1;
hadc2.Init.DiscontinuousConvMode = DISABLE;
hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
@@ -397,15 +461,6 @@ static void MX_ADC2_Init(void)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC2_Init 2 */
/* USER CODE END ADC2_Init 2 */
@@ -431,9 +486,9 @@ static void MX_TIM2_Init(void)
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.Prescaler = 12800-1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 128999;
htim2.Init.Period = 99;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
@@ -600,146 +655,104 @@ static void MX_GPIO_Init(void)
}
/* USER CODE BEGIN 4 */
void Control_Loop_Update(uint32_t setpoint_mv, uint32_t measured_mv)
void ADC_Filter_Init(ADC_Filter *f)
{
/* Positive error = need more power */
/* Negative error = need less power */
int32_t error = (int32_t)setpoint_mv - (int32_t)measured_mv;
memset(f->buffer, 0, sizeof(f->buffer));
f->sum = 0;
f->index = 0;
}
if (abs(error) < DEADBAND_MV)
uint32_t ADC_Filter_Update(ADC_Filter *f, uint32_t new_sample)
{
/* Remove the oldest sample from the running sum */
f->sum -= f->buffer[f->index];
/* Store the new sample in the buffer */
f->buffer[f->index] = new_sample;
/* Add the new sample to the sum */
f->sum += new_sample;
/* Move index to next position, wrap around if at end */
f->index++;
if (f->index >= AVG_WINDOW)
{
error = 0; /* Don't change PWM if we are "close enough" */
f->index = 0;
}
/* Proportional Calculation */
float p_term = KP * (float)error;
/* Adjust the Duty Cycle */
static float current_duty = 32000.0f; // Start at 50%
current_duty += p_term;
/* Anti-Windup / Saturation (Crucial for bidirectional) */
/* Prevents the PWM from trying to go to -50% or 200% */
if (current_duty > MAX_PWM) current_duty = (float)MAX_PWM;
if (current_duty < 0.0f) current_duty = 0.0f;
/* Update PWM */
__HAL_TIM_SET_COMPARE(&htim16, TIM_CHANNEL_1, (uint32_t)current_duty);
/* Return the average */
return f->sum / AVG_WINDOW;
}
void MA_Init(MovingAverageFilter *filter)
int32_t update_pwm (uint32_t measured_mv)
{
for (int i = 0; i < FILTER_SIZE; i++)
/* Calculate Error */
int32_t new_pwm = 0;
uint8_t direction_flag = 0x00;
if (v_target >= measured_mv)
{
filter->buffer[i] = 0;
direction_flag = 0x00;
}
filter->sum = 0;
filter->index = 0;
else
{
direction_flag = 0xFF;
}
if (direction_flag == 0xFF)
{
new_pwm = (uint32_t)pwm_value;
new_pwm--;
}
else
{
new_pwm = (uint32_t)pwm_value;
new_pwm++;
}
/* Output Saturation (Keep PWM within hardware limits) */
if (new_pwm > pwm_max)
{
new_pwm = pwm_max;
}
if (new_pwm <= 1)
{
new_pwm = 1;
}
return new_pwm;
}
uint16_t MA_Update(MovingAverageFilter *filter, uint16_t new_sample)
{
/* Subtract the oldest value from the running sum */
filter->sum -= filter->buffer[filter->index];
/* Add the new value to the running sum */
filter->sum += new_sample;
/* Store the new value in the buffer, overwriting the oldest one */
filter->buffer[filter->index] = new_sample;
/* Move the index to the next position (circular buffer wrap-around) */
filter->index++;
filter->index &= (FILTER_SIZE - 1); /* Equivalent to: if (filter->index >= FILTER_SIZE) filter->index = 0; */
/* Calculate the average using bit-shifting (faster than division by power of 2) */
/* For FILTER_SIZE = 16, this is a right shift by 4 bits (sum / 16) */
/* If used 32, it would be sum >> 5 */
return (uint16_t)(filter->sum >> 7);
}
uint32_t get_actual_vdda(ADC_HandleTypeDef *hadc)
float get_actual_vdda(ADC_HandleTypeDef *hadc)
{
uint32_t vrefint_raw = 0;
/* Perform ADC reading of the VREFINT channel */
HAL_ADC_Start(hadc);
if (HAL_ADC_PollForConversion(hadc, 10) == HAL_OK) {
vrefint_raw = HAL_ADC_GetValue(hadc);
if (HAL_ADC_PollForConversion(hadc, 10) == HAL_OK)
{
vrefint_raw = HAL_ADC_GetValue(hadc);
}
HAL_ADC_Stop(hadc);
if (vrefint_raw == 0) return 0; /* Avoid division by zero */
if (vrefint_raw == 0)
{
return 0; /* Avoid division by zero */
}
/* Use the standard ST formula to calculate VDDA */
/* VDDA = VREFINT_CAL_VREF * VREFINT_CAL / VREFINT_DATA */
uint32_t vdda_mv = (VREFINT_CAL_VREF * (uint32_t)(*VREFINT_CAL_ADDR)) / vrefint_raw;
float vdda_mv = (VREFINT_CAL_VREF * (uint32_t)(*VREFINT_CAL_ADDR)) / (float)vrefint_raw;
return vdda_mv;
}
/* Calculate original input voltage from a 22k/2.2k divider in mV */
uint32_t get_divider_input_mv(uint32_t raw_adc_value, uint32_t vdda_mv)
{
/* Calculate the voltage at the ADC pin (Vout of the divider) */
/* Using 64-bit for intermediate to avoid overflow: (Raw * VDDA) / 4095 */
uint64_t vout_mv = ((uint64_t)raw_adc_value * vdda_mv) / 4095;
/* Scale by the divider ratio: (22k + 2.2k) / 2.2k = 11 */
uint32_t vin_mv = (uint32_t)(vout_mv * 10.9);
return vin_mv;
}
/* Voltage Conversion Task */
void voltage_conversion_task(void)
{
/* Get Vin voltage */
vin_val = get_divider_input_mv(vin_adc_val, vdd_ref);
/* Get Vout voltage */
vout_val = get_divider_input_mv(vout_adc_val_av, vdd_ref);
tx_len = 0x08;
tx_buffer[0] = IN_SYNC_BYTE_1;
tx_buffer[1] = IN_SYNC_BYTE_2;
tx_buffer[2] = tx_len;
tx_buffer[3] = (uint8_t)((vin_val >> 24) & 0xFF);
tx_buffer[4] = (uint8_t)((vin_val >> 16) & 0xFF);
tx_buffer[5] = (uint8_t)((vin_val >> 8) & 0xFF);
tx_buffer[6] = (uint8_t)(vin_val & 0xFF);
tx_buffer[7] = (uint8_t)((vout_val >> 24) & 0xFF);
tx_buffer[8] = (uint8_t)((vout_val >> 16) & 0xFF);
tx_buffer[9] = (uint8_t)((vout_val >> 8) & 0xFF);
tx_buffer[10] = (uint8_t)(vout_val & 0xFF);
/* Need to apply checksum to all data bits */
for (tx_len_counter = 0x00; tx_len_counter < tx_len; tx_len_counter++)
{
tx_checksum += tx_buffer[tx_len_counter + 3];
}
tx_checksum = ~tx_checksum;
tx_buffer[11] = (uint8_t)((tx_checksum >> 8) & 0xFF);
tx_buffer[12] = (uint8_t)(tx_checksum & 0xFF);
tx_len = 0x0D;
HAL_UART_Transmit(&huart2, tx_buffer, tx_len, 100);
}
/* Voltage Conversion Task with No UART Tx */
void voltage_conversion_task_no_tx(void)
{
/* Get Vout voltage */
vout_val = get_divider_input_mv(vout_adc_val_av, vdd_ref);
}
void serial_number_task (void)
{
tx_len = 0x13;
@@ -783,11 +796,6 @@ void adc_task (void)
HAL_ADC_Start(&hadc2);
HAL_ADC_PollForConversion(&hadc2, 500);
vout_adc_val = HAL_ADC_GetValue(&hadc2);
HAL_ADC_Start(&hadc2);
HAL_ADC_PollForConversion(&hadc2, 500);
vin_adc_val = HAL_ADC_GetValue(&hadc2);
HAL_ADC_Stop(&hadc2);
}
@@ -798,12 +806,21 @@ void power_switch (uint8_t state)
{
vset_task_flag = 0xFF;
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
v_scale = v_target / 1000;
buffer_count = (uint8_t)v_scale;
pwm_value = dataBuffer[buffer_count];
__HAL_TIM_SET_COMPARE(&htim16, TIM_CHANNEL_1, pwm_value);
}
else
{
vset_task_flag = 0x00;
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
__HAL_TIM_SET_COMPARE(&htim16, TIM_CHANNEL_1, 0);
}
}
@@ -923,7 +940,6 @@ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
case 0x53:
power_state_value = rx_buffer[1];
v_target = ((uint32_t)rx_buffer[2] << 24) | ((uint32_t)rx_buffer[3] << 16) | ((uint32_t)rx_buffer[4] << 8) | ((uint32_t)rx_buffer[5]);
MA_Init(&movavFilter);
power_switch(power_state_value);
break;