polarimeter_software/User/app/tec_control.c
2025-09-30 10:37:23 +08:00

337 lines
9.3 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @Date: 2025-06-23 13:04:20
* @Author: mypx
* @LastEditors: mypx mypx_coder@163.com
* @LastEditTime: 2025-09-25 16:49:59
* @FilePath: tec_control.c
* @Description:
* Copyright (c) 2025 by mypx, All Rights Reserved.
*/
#include "tec_control.h"
#include "ad7793.h"
#include "et_log.h"
#include "etk_utils.h"
#include "mb_interface.h"
#include "pm_board.h"
#include "pm_common.h"
#include "pt100x.h"
#include <stdint.h>
#include <stdio.h>
#if TEC_CONTROL_PERIOD < ADC_SAMPLE_PERIOD
#error "TEC_CONTROL_PERIOD must be greater than ADC_SAMPLE_PERIOD"
#endif
/* Define AD7793 device structure */
ad7793_dev_t ad7793_dev;
extern ad7793_hw_if_t ad7793_hw_if;
#if (USING_SOFT_SPI == 1)
extern soft_spi_t soft_spi;
#endif
tec_control_t *tec_control = NULL;
/* Shared data structure for temperature sampling and TEC control */
static struct
{
float current_temperature;
bool temp_updated;
rt_mutex_t data_mutex;
} temp_shared_data;
float tec_get_target_voltage(float r)
{
double numerator = 135 - 675 * r;
double denominator = 390 * r + 1343;
if (denominator == 0)
{
ET_ERR("Error: Denominator is zero for r = %.2f", r);
return 0.0;
}
return numerator / denominator;
}
/**
* @param voltage: Voltage value in volts
* @return double: Resistance in ohms
*/
static double solve_resistance(double voltage)
{
double numerator = 135 - 1343 * voltage;
double denominator = 390 * voltage + 675;
if (denominator == 0)
{
ET_ERR("Error: Denominator is zero for v = %.2f", voltage);
return 0.0;
}
return numerator * 1000 / denominator;
}
void tec_pid_init(tec_control_t *tec)
{
float kp = 15.0f;
float ki = 0.2f;
float kd = 0.05f;
float threshold = 0.5f; // 积分分离阈值,可根据实际情况调整
et_integral_separation_pid_init(&tec->pid, kp, ki, kd, threshold);
}
float tec_pid_update(tec_control_t *tec, float set_point, float current_temp, float dt)
{
tec->pid.set_point = set_point;
return et_integral_separation_pid_update(&tec->pid, current_temp, dt);
}
void tec_control_reset(void)
{
if (tec_control != NULL)
{
tec_control->target_temper = 0.0f;
}
}
int tec_control_start(void)
{
if (tec_control == NULL)
{
ET_ERR("TEC control not initialized");
return -1;
}
if (tec_control->cur_temper > tec_control->exception_temper)
{
ET_WARN("current temperature is too high, cannot start TEC");
return -2;
}
tec_control->is_control = true;
return 0;
}
int tec_control_stop(void)
{
if (tec_control == RT_NULL)
{
ET_ERR("TEC control not initialized");
return -1;
}
tec_control->is_control = false;
return 0;
}
void tec_calculate_temperature(uint32_t adc_value)
{
float mv = 0;
double resistance;
mv = ad7793_convert_to_voltage(&ad7793_dev, adc_value) * 1000;
ET_DBG("voltage:%.2f", mv);
resistance = solve_resistance(mv / 1000);
//ET_PRINTF_FLOAT("resistance:", resistance);
tec_control->cur_temper = pt_precise_temperature(resistance, PT100);
tec_control->cur_temper = et_ema_filter_process(&tec_control->ema_filter, tec_control->cur_temper);
if (isnan(tec_control->cur_temper))
{
rt_kprintf("Invalid resistance value, cannot calculate temperature.\n");
}
else
{
ET_DBG("temperature:%.2f", tec_control->cur_temper);
/* Update shared data */
rt_mutex_take(temp_shared_data.data_mutex, RT_WAITING_FOREVER);
temp_shared_data.current_temperature = tec_control->cur_temper;
temp_shared_data.temp_updated = true;
rt_mutex_release(temp_shared_data.data_mutex);
}
}
float generate_random_temperature(void)
{
// 生成0到RAND_MAX之间的随机整数
int randomInt = rand();
// 转换为0到1之间的浮点数再映射到0到55范围
float randomFloat = (float)randomInt / RAND_MAX * 55.0f;
return randomFloat;
}
/* Temperature sampling thread entry function */
static void temp_sampling_thread_entry(void *parameter)
{
ETK_UNUSED(parameter);
//uint32_t adc_value = 0;
ET_INFO("Temperature sampling thread started");
while (1)
{
/* Wait for ADC ready and read data */
// if (ad7793_wait_ready(&ad7793_dev, 1000) == true)
// {
// if (ad7793_read_data(&ad7793_dev, &adc_value))
// {
// /* Calculate temperature from ADC value */
// tec_calculate_temperature(adc_value);
// }
// else
// {
// ET_ERR("Failed to read ADC data.\n");
// }
// }
//mb_write_current_temp(tec_control->cur_temper);
mb_write_current_temp(generate_random_temperature()); // test only
mb_write_current_rotation(generate_random_temperature()); // test only
/* Delay for next sampling period */
rt_thread_mdelay(ADC_SAMPLE_PERIOD);
}
}
void tec_control_temperature(tec_control_t *tec)
{
float pid_out = 0;
float current_temp;
if (tec == NULL)
{
ET_ERR("tec is NULL");
return;
}
if (tec->is_control == false)
return;
/* Get current temperature from shared data */
rt_mutex_take(temp_shared_data.data_mutex, RT_WAITING_FOREVER);
current_temp = temp_shared_data.current_temperature;
temp_shared_data.temp_updated = false;
rt_mutex_release(temp_shared_data.data_mutex);
/* Update tec control current temperature */
tec->cur_temper = current_temp;
// dt convert to seconds
pid_out = tec_pid_update(tec, tec->target_temper, current_temp, TEC_CONTROL_PERIOD / 1000.0f);
ET_INFO("pid_out:%.2f", pid_out);
pm_tec_set_duty(pid_out);
}
static void tec_control_thread_entry(void *parameter)
{
ETK_UNUSED(parameter);
rt_tick_t last_ms = rt_tick_get_millisecond();
rt_uint32_t received;
et_ema_filter_init(&tec_control->ema_filter, EMA_FILTER_ALPHA);
tec_pid_init(tec_control);
tec_control->is_control = false; // control disabled by default
pm_tec_start();
pm_fan_start();
tec_control->target_temper = 25.0f;
ET_INFO("TEC control thread started");
while (1)
{
// wait for control events with timeout
rt_event_recv(tec_control->control_event,
TEC_CONTROL_START_EVENT | TEC_CONTROL_STOP_EVENT | TEC_TEMP_UPDATE_EVENT,
RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, TEC_CONTROL_PERIOD / 2, &received);
// handle received events
if (received & TEC_CONTROL_START_EVENT)
{
tec_control_start();
ET_INFO("TEC control started by event");
}
if (received & TEC_CONTROL_STOP_EVENT)
{
tec_control_stop();
ET_INFO("TEC control stopped by event");
}
if (received & TEC_TEMP_UPDATE_EVENT)
{
ET_INFO("Target temperature updated to: %.2f°C", tec_control->target_temper);
}
// perform temperature control at specified period
if (rt_tick_get_millisecond() - last_ms > TEC_CONTROL_PERIOD)
{
last_ms = rt_tick_get_millisecond();
tec_control_temperature(tec_control);
}
}
}
int tec_control_init(tec_control_t *tec)
{
rt_thread_t tid;
rt_thread_t temp_tid;
if (tec == NULL)
{
ET_ERR("tec is NULL");
return -1;
}
pm_tec_init();
tec_control = tec;
// create event object for temperature control
tec_control->control_event = rt_event_create("tec_event", RT_IPC_FLAG_FIFO);
if (tec_control->control_event == RT_NULL)
{
ET_ERR("Failed to create TEC control event");
return -3;
}
// create mutex for shared data protection
temp_shared_data.data_mutex = rt_mutex_create("temp_mutex", RT_IPC_FLAG_FIFO);
if (temp_shared_data.data_mutex == RT_NULL)
{
ET_ERR("Failed to create temperature data mutex");
return -5;
}
// initialize shared data
temp_shared_data.current_temperature = 25.0f;
temp_shared_data.temp_updated = false;
// #if (USING_SOFT_SPI == 1)
// if (ad7793_init(&ad7793_dev, &ad7793_hw_if, soft_spi.cs_pin))
// #else
// if (ad7793_init(&ad7793_dev, &ad7793_hw_if, SPI3_CS_AD7793_Pin))
// #endif
// {
// rt_kprintf("AD7793 device initialized successfully.\n");
// }
// else
// {
// rt_kprintf("Failed to initialize AD7793 device.\n");
// return -2;
// }
// create temperature sampling thread
temp_tid = rt_thread_create("temp-sample", temp_sampling_thread_entry, RT_NULL, TEMP_SAMPLE_THREAD_STACK_SIZE,
TEMP_SAMPLE_THREAD_PRIORITY, TEMP_SAMPLE_THREAD_TIMESLICE);
if (temp_tid == RT_NULL)
{
ET_ERR("Failed to create temperature sampling thread");
return -6;
}
rt_thread_startup(temp_tid);
// create TEC control thread
// tid = rt_thread_create("tec-control", tec_control_thread_entry, RT_NULL, TEC_CONTROL_THREAD_STACK_SIZE,
// TEC_CONTROL_THREAD_PRIORITY, TEC_CONTROL_THREAD_TIMESLICE);
// if (tid != RT_NULL)
// {
// rt_thread_startup(tid);
// return 0;
// }
return -4;
}