updates
This commit is contained in:
344
Core/Src/main.c
344
Core/Src/main.c
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user