This repository has been archived on 2025-04-02. You can view files and clone it, but cannot push or open issues or pull requests.
controller-hart/User/board/board.c

1627 lines
42 KiB
C
Raw 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.

/**
* @file board.c
* @author xxx
* @date 2023-09-12 13:52:37
* @brief
* @copyright Copyright (c) 2023 by xxx, All Rights Reserved.
*/
#include <stdlib.h>
#include <math.h>
#include "usart.h"
#include "board.h"
#include "entity.h"
#include "delay.h"
#include "filter.h"
#include "bootload.h"
#include "bsp.h"
#include "uarts.h"
#include "convert.h"
#include "params.h"
void SystemClock_Config(void);
board_data_t board_data; ///< 板卡数据
static BOOL driver_init_flag = FALSE;
lcd_t *lcd = NULL;
static kalman_t temperature_km; ///< 温度滤波器
static __IO BOOL _lcd_init = FALSE;
static __IO int8_t _uart1_idel_sec_count = 0;
// 注ARM架构要求所有的数据都必须按照4字节对齐也就是说数据的地址必须是4的倍数
__IO uint16_t dbg_assert_line __attribute__((at(APP_PRELOAD_AREA))); // 调试行号
__IO uint16_t app_preload_bootload_flag __attribute__((at(APP_PRELOAD_AREA + 2))); // 触发BOOTLOAD启动标志(在用户代码中接受代码文件)
__IO uint16_t app_preload_bootload_jump_flag __attribute__((at(APP_PRELOAD_AREA + 4))); // 触发BOOTLOAD跳转更新标志(在BOOTLOAD代码中更新用户代码)
/***************************************** 板卡初始化相关函数 *****************************************/
// dma发送回调函数
static void lcd_dma_tx_cb(spi_t *handle)
{
DBG_ASSERT(handle != NULL __DBG_LINE);
DMA_ClEAR_FLAG(handle->dma, 5, handle->tx_dma_ok);
}
/**
* @brief LCD设计
* @return {*}
* @note
*/
void lcd_design(void)
{
}
/**
* @brief LCD初始化
* @return {*}
* @note
*/
void lcd_init(void)
{
if (_lcd_init == TRUE)
{
return;
}
// 设置功能引脚
GPIO_SET_OUTPUT(LCD_PWR_GPIO_Port, LCD_PWR_Pin);
GPIO_SET_OUTPUT(LCD_CS_GPIO_Port, LCD_CS_Pin);
GPIO_SET_OUTPUT(LCD_DISP_GPIO_Port, LCD_DISP_Pin);
// 打开LCD电源
LCD_POWER_ON();
// 创建LCD对象
if (lcd == NULL)
{
// 初始化LCD相关的GPIO
spi_gpio_group_t gpios;
gpios.cs = gpio_create(LCD_CS_GPIO_Port, LCD_CS_Pin);
gpios.mosi = gpio_create(LCD_MOSI_GPIO_Port, LCD_MOSI_Pin);
gpios.sck = gpio_create(LCD_SCK_GPIO_Port, LCD_SCK_Pin);
gpios.rst = gpio_create(NULL, 0);
gpios.rdy = gpio_create(NULL, 0);
gpios.miso = gpio_create(NULL, 0);
// 初始化LCD相关的SPI
lcd_info_t info = {
.type = LCD_SHARP, // 设置LCD类型为ST7525
.spi = spi_create(SPI_TYPE_LCD, gpios, 0), // 创建SPI总线
.dir = W_LCD, // 设置显示方向为横向
.width = COL_DOT_MAX_400, // 设置LCD最大宽度为400列
.height = LIN_DOT_MAX_240, // 设置LCD最大高度为240行
.retransmission_count = 3, // 设置重传次数为3次
.disp = gpio_create(LCD_DISP_GPIO_Port, LCD_DISP_Pin),
};
info.spi->interface.hardware_enable(info.spi, LCD_SPI);
info.spi->interface.dma_enable(info.spi, LCD_DMA, 0xffffffff, NULL, LCD_DMA_TX_CHANNEL, lcd_dma_tx_cb);
// 创建LCD对象
lcd = lcd_create(info);
DBG_ASSERT(lcd != NULL __DBG_LINE);
}
// 初始化LCD驱动
lcd->driver.init(lcd);
_lcd_init = TRUE;
}
/**
* @brief LCD反初始化
* @return {*}
* @note
*/
void lcd_dinit(void)
{
if (_lcd_init == FALSE)
{
return;
}
// 关闭LCD显示
if (lcd != NULL)
{
gui_close();
}
// GPS3000 led 和 lcd 在一个屏幕上 不能关闭LCD电源
// LCD_POWER_OFF();
// 反初始化LCD相关引脚降低板卡功耗
// GPIO_SET_ANALOG(LCD_PWR_GPIO_Port, LCD_PWR_Pin);
GPIO_SET_ANALOG(LCD_CS_GPIO_Port, LCD_CS_Pin);
GPIO_SET_ANALOG(LCD_DISP_GPIO_Port, LCD_DISP_Pin);
_lcd_init = FALSE;
}
/**
* @brief 功耗引脚初始化
* @return {*}
* @note
*/
void driver_init(void)
{
// 标志位检查driver_init_flag = TRUE表示已经初始化
if (driver_init_flag == TRUE)
{
return;
}
driver_init_flag = TRUE;
pdctrl_run(); // 控制输出使能
}
/**
* @brief 功耗引脚反初始化
* @return {*}
* @note
*/
void driver_dinit(void)
{
// 标志位检查driver_init_flag = FALSE表示已经反初始化
if (driver_init_flag == FALSE)
{
return;
}
driver_init_flag = FALSE;
pdctrl_stop(); // 控制输出禁用
}
/**
* @brief 板卡初始化
* @return {*}
* @note
*/
void board_init(void)
{
VIP_H_EN_DISABLE(); // IP禁用满足条件时才使能
system_clock_read(); // 读取系统时钟配置
// 过采样提高精度
adc_init(ADCS_1, ADC1, DMA1, LL_DMA_CHANNEL_1, 400,
IN5 | IN6 | IN7 | IN8 | IN9 | IN11 | IN12 | IN13 | IN14 | IN16 | INTEMP | INVREF); // 初始化ADC1通道默认采集AD
leds_init(); // 初始化LED
rtc_init(TIME_1US); // 初始化RTC
eeprom_m95_init(M95_1); // 初始化SPI EEPROM1
eeprom_m95_init(M95_2); // 初始化SPI EEPROM2
eeprom_fm24_init(); // 初始化 IIC EEPROM
eeprom_lc02b_init(TIME_1US); // 初始化 IIC EEPROM
sht40_init(TIME_1US); // 初始化SHT40
dac161p997_init(TIME_1US); // 初始化DAC161
kalman_init(&temperature_km, 0.2f); // 温度突变的精度为0.2
}
void board_dinit(void)
{
adc_dinit(ADCS_1); // ADC反初始化
DISABLE_TIM(TASK_TIM);
DISABLE_TIM(MODE_TIM);
driver_dinit();
rtc_dinit(); // RTC反初始化
eeprom_m95_dinit(M95_1);
eeprom_m95_dinit(M95_2);
eeprom_fm24_dinit();
lcd_dinit(); // LCD screen Deinitialization
}
// 定时器周期
void timer_cycle_update(void)
{
// 定时器周期
rt_data.time_cycle.task = TIM_CYCLE(TASK_TIM);
rt_data.time_cycle.mode = TIM_CYCLE(MODE_TIM);
rt_data.time_cycle.mode_gathe = TIM_CYCLE(MODE_GATHE_TIM);
rt_data.time_cycle.hart = TIM_CYCLE(HART_TIM);
}
/**
* @brief 设置随机数种子
*
* 使用 get_seed() 函数获取的种子值来初始化随机数生成器的种子。
*
* @note 此函数应当在需要生成随机数之前被调用一次,以确保随机数的随机性。
*/
void board_srand(void)
{
srand(get_seed());
}
void dbg_assert_cb(uint16_t line)
{
dbg_assert_line = line;
sys_soft_reset(); // 复位行号保存在HART设备属性中的message
}
static uint32_t get_ahb_div(uint32_t sysclk)
{
if (sysclk == SYSTEM_CLOCK_CHANGE)
{
return LL_RCC_SYSCLK_DIV_1;
}
else
{
// 默认值或错误处理
return LL_RCC_SYSCLK_DIV_2;
}
}
/**
* @brief 设置TIM的Hart输出
*
* 本函数用于设置TIM的Hart输出相关配置。
*
* @return 无返回值
*/
void set_tim_hart_out(uint32_t sysclk)
{
uint8_t arr = sysclk / (1000 * SYSTEM_HART_CLOCK);
LL_TIM_SetPrescaler(HART_OUT_TIM, 0);
LL_TIM_SetAutoReload(HART_OUT_TIM, arr - 1);
PWM_SET_COMPARE(HART_OUT_TIM, HART_OUT_TIM_CHANNEL, arr * 0.5f);
}
/**
* @brief 更新定时器的预分频值和自动重装载值以保持中断时间不变
* @param tim 定时器实例
* @param cfg 时钟配置结构体,包含系统时钟频率和最后一次的系统时钟频率
*/
static void update_timer_prescaler(TIM_TypeDef *tim, const clock_config_t *const cfg)
{
uint32_t hclk = cfg->sysclk / 1000000;
uint32_t prescaler = (hclk * 100) - 1;
LL_TIM_SetPrescaler(tim, prescaler);
}
/**
* @brief 系统时钟配置低频
* @return {*}
* @note 4M 默认使用系统时钟配置低频
*/
void system_clock_config_low(void)
{
#if LCD_DESIGN == FALSE
// 如果系统核心时钟与原始时钟配置中的系统时钟相同,则直接返回
if (SystemCoreClock == get_original_clock_config()->sysclk)
{
return;
}
// 恢复系统时钟
restore_system_clock();
// 更新定时器的预分频值和自动重装载值
LL_TIM_SetPrescaler(TIME_1US, (get_original_clock_config()->sysclk / 1000000));
update_timer_prescaler(TASK_TIM, get_original_clock_config());
update_timer_prescaler(MODE_TIM, get_original_clock_config());
update_timer_prescaler(HART_TIM, get_original_clock_config());
update_timer_prescaler(MODE_GATHE_TIM, get_original_clock_config());
set_tim_hart_out(get_original_clock_config()->sysclk);
MX_USART1_UART_Init();
uart_set_baudrate(HART_UART1, get_original_clock_config()->sysclk, HART_UART1_BAUDRATE);
MX_UART5_Init();
uart_set_baudrate(HART_UART2, get_original_clock_config()->sysclk, HART_UART2_BAUDRATE);
#endif
}
/**
* @brief 系统时钟配置高频
* @return {*}
* @note
*/
void system_clock_config_high(void)
{
if (SystemCoreClock == SYSTEM_CLOCK_CHANGE)
{
return;
}
if (get_current_by_resistance() < SYSTEM_CLOCK_HIGHT_CURRENT_MIN)
{
return;
}
// 创建一个新的时钟配置
clock_config_t new_config = {
.pll_source = LL_RCC_PLLSOURCE_HSE,
.pll_m = LL_RCC_PLLM_DIV_2,
.pll_n = 12,
.pll_r = LL_RCC_PLLR_DIV_6,
.ahb_div = get_ahb_div(SYSTEM_CLOCK_CHANGE),
.apb1_div = LL_RCC_APB1_DIV_1,
.apb2_div = LL_RCC_APB2_DIV_1,
.sysclk = SYSTEM_CLOCK_CHANGE,
};
change_system_clock(&new_config);
// 更新定时器的预分频值和自动重装载值
LL_TIM_SetPrescaler(TIME_1US, (new_config.sysclk / 1000000));
update_timer_prescaler(TASK_TIM, &new_config);
update_timer_prescaler(MODE_TIM, &new_config);
update_timer_prescaler(HART_TIM, &new_config);
update_timer_prescaler(MODE_GATHE_TIM, &new_config);
set_tim_hart_out(new_config.sysclk);
MX_USART1_UART_Init();
uart_set_baudrate(HART_UART1, new_config.sysclk, HART_UART1_BAUDRATE);
MX_UART5_Init();
uart_set_baudrate(HART_UART2, new_config.sysclk, HART_UART2_BAUDRATE);
}
/***************************************** 板卡LCD操作相关函数 *****************************************/
/**
* @brief 判断GUI系统是否可以工作
*
* 根据当前电阻值判断GUI系统是否可以正常工作。
*
* @return 返回BOOL类型如果电阻值大于或等于系统正常工作所需的最小电流值则返回TRUE否则返回FALSE。
*/
BOOL gui_can_work(void)
{
if (get_current_by_resistance() < GUI_RUN_CURRENT)
{
return FALSE;
}
else
{
return TRUE;
}
}
/**
* @brief 判断 GUI 是否处于空闲状态
*
* 检查 GUI 是否处于空闲状态。如果 GUI 空闲,则返回 TRUE否则返回 FALSE。
*
* @return 如果 GUI 空闲,则返回 TRUE否则返回 FALSE
*/
BOOL gui_is_idle(void)
{
return lcd->driver.idel;
}
/**
* @brief LCD刷新
* @return {*}
* @note
*/
void gui_flush(void)
{
if (gui_is_idle() == TRUE)
{
lcd->driver.flush(lcd);
}
}
/**
* @brief LCD刷新并清除缓存
* @return {*}
* @note
*/
void gui_flush_Clear(void)
{
if (gui_can_work() == TRUE)
{
if (gui_is_idle() == TRUE)
{
lcd->driver.flush_clear(lcd);
}
}
}
/**
* @brief LCD全屏清除
* @return {*}
* @note
*/
void gui_clr(void)
{
lcd->driver.clear(lcd);
}
/**
* @brief LCD设置清屏标志
* @return {*}
* @note
*/
void gui_set_clear_flag(void)
{
lcd->driver.set_clear_flag(lcd);
}
/**
* @brief LCD获取清屏标志
* @return {*}
* @note
*/
BOOL gui_get_clear_flag(void)
{
return lcd->driver.get_clear_flag(lcd);
}
/**
* @brief LCD全屏填充
* @return {*}
* @note
*/
void gui_full(void)
{
lcd->driver.full_fill(lcd, BLACK);
}
/**
* @brief LCD启动
* @return {*}
* @note
*/
void gui_open()
{
lcd->driver.onoff(lcd, TRUE);
}
/**
* @brief LCD关闭
* @return {*}
* @note
*/
void gui_close()
{
lcd->driver.onoff(lcd, FALSE);
}
/**
* @brief 读取 Flash 存储器数据
*
* 从指定的 Flash 地址开始,读取指定长度的数据到提供的缓冲区中。
*
* @param address Flash 存储器的起始地址
* @param data 用于存储读取数据的缓冲区指针
* @param length 要读取的数据长度(以字节为单位)
*
* @return 如果读取成功,返回 TRUE否则返回 FALSE
*/
BOOL flash_read(uint32_t address, uint8_t *data, uint16_t length)
{
ErrorStatus rst = ERROR;
rst = LL_FLASH_Read(address, data, length);
return rst == SUCCESS ? TRUE : FALSE;
}
/**
* @brief 写入 Flash 存储器数据
*
* 将提供的数据写入 Flash 存储器的指定地址。
*
* @param address Flash 存储器的起始地址
* @param data 要写入的数据的缓冲区指针
* @param length 要写入的数据长度(以字节为单位)
*
* @return 如果写入成功,返回 TRUE否则返回 FALSE
*/
BOOL flash_write(uint32_t address, const uint8_t *data, uint16_t length)
{
ErrorStatus rst = ERROR;
LL_FLASH_Unlock(FLASH);
rst = LL_FLASH_Program(address, (uint8_t *)data, length);
LL_FLASH_Lock(FLASH);
return rst == SUCCESS ? TRUE : FALSE;
}
/**
* @brief 擦除指定页的 Flash
*
* 擦除 Flash 中的指定页。
*
* @param page 要擦除的 Flash 页号
*
* @return 如果擦除成功,返回 TRUE否则返回 FALSE
*/
BOOL flash_erase_page(uint32_t page)
{
ErrorStatus rst = ERROR;
LL_FLASH_Unlock(FLASH);
rst = LL_FLASH_ErasePage(page);
LL_FLASH_Lock(FLASH);
return rst == SUCCESS ? TRUE : FALSE;
}
/**
* @brief 设置扫描方向
* @return {*}
* @note
*/
void gui_set_scandir(uint8_t dir)
{
lcd->driver.set_dir(lcd, dir);
}
/***************************************** 板卡参数相关函数 *****************************************/
/**
* @brief 根据目标行程计算DAC输出理论值
* @param {float32} output - 目标行程(百分比)
* @return {uint16_t} DAC输出理论值
* @note 计算公式如下:
* > (Osh-Osl)/(Ish-Isl)=(Ov-Osl)/(Iv-Isl) <p>
* > Ov=[(Osh-Osl)*(Iv-Isl)/(Ish-Isl)]+Osl <p>
*/
uint16_t get_dac(float32 output)
{
return ((udevice.output_max - udevice.output_min) * (output - 0) / (100 - 0)) + udevice.output_min;
}
/**
* @brief PWM输出阀位百分比
* @param {float32} position_per-阀位百分比
* @return {*}
* @note 阀位反馈输出, 0%输出4.00mA, 100.0%输出20.00mA
*> 0% - 4.0mA <p>
*> 10% - 5.6mA <p>
*> 20% - 7.2mA <p>
*> 30% - 8.8mA <p>
*> 40% - 10.4mA <p>
*> 50% - 12.0mA <p>
*> 55% - 12.8mA <p>
*> 60% - 13.6mA <p>
*> 70% - 15.2mA <p>
*> 80% - 16.8mA <p>
*> 90% - 18.4mA <p>
*> 100% - 20.0mA <p>
*/
void pwm_output_position(float32 position_per)
{
uint16_t adc = 0;
static float32 f_span = 0xffff;
float32 f = 0.0f;
calib_param_t *p = (calib_param_t *)&calib_param[CALIBPARA_VIP];
DBG_ASSERT(p != NULL __DBG_LINE);
if (p->is_calibration == IS_CALIBRATION)
{
return;
}
if (p->offset != udevice.output_offset || p->span != udevice.output_span || f_span == 0xffff)
{
if (f_span != 0xffff)
{
// 重新计算斜率
calib_param_calculate(CALIBPARA_VIP, udevice.output_offset, udevice.output_span, p->min, p->max);
}
f_span = (float32)(udevice.output_span) * 3 / (float32)(ABS(p->original_adc_value[1] - p->original_adc_value[0]));
}
// 计算输出电流
{
adc = calib_param[CALIBPARA_VIP].original_adc_value[0] +
(calib_param[CALIBPARA_VIP].original_adc_value[1] - calib_param[CALIBPARA_VIP].original_adc_value[0]) * position_per / 100;
f = p->min + adc_linear_conversion(CALIBPARA_VIP, adc);
if (f < 0)
{
f = 0.0f;
}
else
{
float32 tmp = CURRENT_PERCENT(f) * f_span * (p->max - p->min);
tmp = floorf(tmp * 100.f) / 100.f; // 保留小数点后两位小数
f -= tmp;
}
}
rt_data.output_current = floorf(f * 1000.f) / 1000.f;
dac161p997_output_current(rt_data.output_current);
}
/**
* @brief 校准4-20mA输入电流
* @return {*}
* @note
*/
void calib_loop(void)
{
#define MA4 (LOOP_CURRENT_MIN * 1000)
#define MA20 (LOOP_CURRENT_MAX * 1000)
if (calib_param[CALIBPARA_LOOP].original_adc_value[0] == calib_param[CALIBPARA_LOOP].original_adc_value[1] ||
ABS(calib_param[CALIBPARA_LOOP].original_adc_value[1] - calib_param[CALIBPARA_LOOP].original_adc_value[0]) < 1000)
{
return;
}
}
/**
* @brief 校准压力表
* @return {*}
* @note
*/
void calib_kpa(void)
{
// uint16_t ma = 0, adc = 0;
// float32 f;
// // 计算校准参数
// ma = board_cache_set_value[BOARD_CACHE_2] - board_cache_set_value[BOARD_CACHE_1];
// adc = board_cache[BOARD_CACHE_2] - board_cache[BOARD_CACHE_1];
// f = ma;
// f = f / adc;
// calib_param[CALIBPARA_PS].value[0] = f;
// f = f * board_cache[BOARD_CACHE_2];
// calib_param[CALIBPARA_PS].value[1] = board_cache_set_value[BOARD_CACHE_2] - f;
// calib_param[CALIBPARA_PSB].value[0] = calib_param[CALIBPARA_PS].value[0];
// calib_param[CALIBPARA_PSB].value[1] = calib_param[CALIBPARA_PS].value[1];
// calib_param[CALIBPARA_PB].value[0] = calib_param[CALIBPARA_PS].value[0];
// calib_param[CALIBPARA_PB].value[1] = calib_param[CALIBPARA_PS].value[1];
}
/**
* @brief 校准4-20mA输出电流
* @return {*}
* @note
*/
void calib_pwm_out(void)
{
// 计算校准参数
// float32 mA4 = 400;
// float32 mA20 = 2000;
// float32 TDuty4 = board_cache[BOARD_CACHE_1];
// float32 TDuty20 = board_cache[BOARD_CACHE_2];
// calib_param[CALIBPARA_VIP].value[0] = (TDuty20 - TDuty4) / (mA20 - mA4);
// calib_param[CALIBPARA_VIP].value[1] = TDuty20 - calib_param[CALIBPARA_VIP].value[0] * mA20;
}
/**
* @brief 校准阀门位置参数,电压转换成%(放大10倍1位小数)
* @return {*}
* @note 计算公式如下:
* > k = (y2-y1) / (x2-x1) <p>
* > m = y2 - k * x2 <p>
*/
void calib_parapos_perent(void)
{
// 直行程位置反馈
{
calib_param[CALIBPARA_PSB].original_adc_value[0] = udevice.pos0_travel_vol;
calib_param[CALIBPARA_PSB].original_adc_value[1] = udevice.pos100_travel_vol;
calib_param_calculate(CALIBPARA_PSB, 0, 0, 0, 100);
}
// 小回路
{
calib_param[CALIBPARA_IPSB].original_adc_value[0] = udevice.pos0_minor_vol;
calib_param[CALIBPARA_IPSB].original_adc_value[1] = udevice.pos100_minor_vol;
calib_param_calculate(CALIBPARA_IPSB, 0, 0, 0, 100);
}
}
/**
* @brief 获取当前实际行程ADC值
* @return {*}
* @note
*/
uint16_t get_actual_travel_adc(void)
{
adc_raw[ADC_PSB_CHANNEL] = adc_result_average(ADCS_1, ADC_PSB_CHANNEL);
return adc_raw[ADC_PSB_CHANNEL];
}
/**
* @brief 获取当前实际行程百分比
* @param {uint16_t} adc
* @return {*}
* @note 保留小数点后一位
*/
float32 actual_adc_convert_percent(uint16_t adc)
{
float32 f = adc_linear_conversion(CALIBPARA_PSB, adc);
if (f < 0)
{
f = 0;
}
if (f > 100)
{
f = 100;
}
return floorf(f * 100.f) / 100.f;
}
/**
* @brief 获取当前实际行程
* @param {uint8_t} filter 1:平均 2中值
* @return {*}
* @note
*/
float32 get_actual_travel(filter_e filter)
{
uint16_t raw = 0;
// 读取位置反馈ADC值
if (filter == FILTER_AVERAGE)
{
raw = adc_result_average(ADCS_1, ADC_PSB_CHANNEL);
}
else if (filter == FILTER_MEDIAN)
{
raw = adc_result_median(ADCS_1, ADC_PSB_CHANNEL);
}
else if (filter == FILTER_MEDIAN_AVERAGE)
{
raw = adc_result_median_average(ADCS_1, ADC_PSB_CHANNEL);
}
else
{
raw = adc_result_average(ADCS_1, ADC_PSB_CHANNEL);
}
adc_raw[ADC_PSB_CHANNEL] = raw;
return actual_adc_convert_percent(raw);
}
/**
* @brief 获取电流,通过电阻计算
* @return {*}
* @note 获取当前电流通过AD采集值计算电流值
*/
float32 get_current_by_resistance(void)
{
float32 tmp = 0.0f;
tmp = loop_current_convert(adc_result_average(ADCS_1, ADC_LOOP_CHANNEL));
return tmp;
}
/**
* @brief 获取当前回路电流
* @return {float32} 回路电流值
* @note
*/
float32 get_current(filter_e filter)
{
static float32 f_span = 0xffff;
float32 f = 0.0f;
calib_param_t *p = (calib_param_t *)&calib_param[CALIBPARA_LOOP];
// 读取回路电流ADC值
if (filter == FILTER_AVERAGE)
{
adc_raw[ADC_LOOP_CHANNEL] = adc_result_average(ADCS_1, ADC_LOOP_CHANNEL);
}
else if (filter == FILTER_MEDIAN)
{
adc_raw[ADC_LOOP_CHANNEL] = adc_result_median(ADCS_1, ADC_LOOP_CHANNEL);
}
else if (filter == FILTER_MEDIAN_AVERAGE)
{
adc_raw[ADC_LOOP_CHANNEL] = adc_result_median_average(ADCS_1, ADC_LOOP_CHANNEL);
}
else
{
adc_raw[ADC_LOOP_CHANNEL] = adc_result_average(ADCS_1, ADC_LOOP_CHANNEL);
}
if (p->offset != udevice.input_offset || p->span != udevice.input_span || f_span == 0xffff)
{
if (f_span != 0xffff)
{
// 重新计算斜率
calib_param_calculate(CALIBPARA_LOOP, udevice.input_offset, udevice.input_span, p->min, p->max);
}
f_span = (float32)(udevice.input_span) * 3 / (float32)(ABS(p->original_adc_value[1] - p->original_adc_value[0]));
}
// 计算回路电流
{
f = p->min + adc_linear_conversion(CALIBPARA_LOOP, adc_raw[ADC_LOOP_CHANNEL]);
if (f < 0)
{
f = 0.0f;
}
else
{
float32 tmp = CURRENT_PERCENT(f) * f_span * (p->max - p->min);
tmp = floorf(tmp * 100.f) / 100.f; // 保留小数点后两位小数
f -= tmp;
}
}
return floorf(f * 1000.f) / 1000.f;
}
/**
* @brief 获取当前回路电流,保留小数点后1位
* @param {float32} current
* @return {*}
* @note
*/
float32 get_current_deal(float32 current)
{
// 判断current小数点后第二位是否大于5
if ((uint16_t)(current * 100) % 10 >= 5)
{
return (uint16_t)(current * 10) * 0.1f + 0.1f;
}
else
{
return (uint16_t)(current * 100) * 0.01f;
}
}
/**
* @brief 获取当前温度值
* @return {float32} 温度值
* @note
*/
float32 get_temperature(void)
{
float32 tmp = 0.0f;
// 采集ADC原始数据
adc_raw[ADC_NTC_CHANNEL] = adc_result_average(ADCS_1, ADC_NTC_CHANNEL);
// 使用NTC算法计算温度值
tmp = ntc_get_temp(adc_raw[ADC_NTC_CHANNEL]).f;
// tmp = kalman_update(&temperature_km, tmp);
tmp = temperature_c2unit(tmp, udevice.temp_unit);
return tmp;
}
/**
* @brief 获取当前CPU温度
* @return {float32} CPU温度
* @note
*/
float32 get_cpu_temperature(void)
{
float32 tmp = 0.0f;
adc_raw[ADC_TEMP_CHANNEL] = adc_result_average(ADCS_1, ADC_TEMP_CHANNEL);
tmp = adc_result_temperature(adc_raw[ADC_TEMP_CHANNEL]);
return tmp;
}
/**
* @brief 获取当前CPU电压
* @return {float32} CPU电压
* @note
*/
float32 get_cpu_volt(void)
{
float32 tmp = 0.0f;
adc_raw[ADC_INVREF_CHANNEL] = adc_result_average(ADCS_1, ADC_INVREF_CHANNEL);
tmp = adc_result_value_local(adc_raw[ADC_INVREF_CHANNEL]);
return tmp;
}
/**
* @brief 获取板载电流
*
* 该函数用于计算并返回板载电流值。 (DC_LOOP_7.5V - VDD_7.5V)*4/39
*
* @return 返回板载电流值,以 float32 类型表示 mA
*/
float32 get_board_current(void)
{
float32 tmp = 0.0f;
adc_raw[ADC_DCDC_CHANNEL] = adc_result_average(ADCS_1, ADC_DCDC_CHANNEL);
adc_raw[ADC_VDD_CHANNEL] = adc_result_average(ADCS_1, ADC_VDD_CHANNEL);
tmp = ((CPU_VREF * adc_raw[ADC_DCDC_CHANNEL] / ADC_MAX) - (CPU_VREF * adc_raw[ADC_VDD_CHANNEL] / ADC_MAX)) * 4 / 39;
return tmp * 1000;
}
/**
* @brief 获取死区
* @return {*}
* @note
*/
float32 get_dead_zone(void)
{
if (udevice.pid_index == 0)
{
return ABS(udevice.integral_db);
}
else
{
return ABS(udevice.spid_dead);
}
}
/**
* @brief 获取当前流量
* @return {*}
* @note
*/
float32 get_flow(void)
{
float32 v = 0.0f;
adc_raw[ADC_FLOW] = adc_result_average(ADCS_1, ADC_BPB_CHANNEL);
v = ((CPU_VREF * adc_raw[ADC_FLOW] / ADC_MAX) - 0.4f) * 600 / 1.6f;
return v < 0 ? 0 : v;
}
/**
* @brief 获取当前压力
* @return {float32} 压力值
* @note
*/
float32 get_pressure(pressure_type_e id)
{
calib_param_t *p = NULL;
float32 f = 0.0;
// 通过线性的方式计算压力值有问题
switch (id)
{
case PRESSURE_PARAM_S: // 读取气源压力ADC
p = (calib_param_t *)&calib_param[CALIBPARA_PS];
DBG_ASSERT(p != NULL __DBG_LINE);
adc_raw[ADC_BP_CHANNEL] = adc_result_average(ADCS_1, ADC_BP_CHANNEL);
if (p->is_calibration == IS_CALIBRATION)
{
f = adc_raw[ADC_BP_CHANNEL];
// 计算气源压力
f = adc_linear_conversion(CALIBPARA_PS, f);
}
else
{
f = pressure_adc2kpa(adc_raw[ADC_BP_CHANNEL]);
}
break;
case PRESSURE_PARAM_A: // 读取A路压力ADC
p = (calib_param_t *)&calib_param[CALIBPARA_PA];
DBG_ASSERT(p != NULL __DBG_LINE);
adc_raw[ADC_BPA_CHANNEL] = adc_result_average(ADCS_1, ADC_BPA_CHANNEL);
if (p->is_calibration == IS_CALIBRATION)
{
f = adc_raw[ADC_BPA_CHANNEL];
// 计算A路压力
f = adc_linear_conversion(CALIBPARA_PA, f);
}
else
{
f = pressure_adc2kpa(adc_raw[ADC_BPA_CHANNEL]);
}
break;
case PRESSURE_PARAM_B: // 读取B路压力ADC
p = (calib_param_t *)&calib_param[CALIBPARA_PB];
DBG_ASSERT(p != NULL __DBG_LINE);
adc_raw[ADC_BPB_CHANNEL] = adc_result_average(ADCS_1, ADC_BPB_CHANNEL);
if (p->is_calibration == IS_CALIBRATION)
{
f = adc_raw[ADC_BPB_CHANNEL];
// 计算B路压力
f = adc_linear_conversion(CALIBPARA_PB, f);
}
else
{
f = pressure_adc2kpa(adc_raw[ADC_BPB_CHANNEL]);
}
break;
default:
break;
}
return f < 0 ? 0 : f;
}
/**
* 获取实时数据的时间戳
*
* 该函数从实时数据结构体中获取当前的日期和时间并将它们转换为自1970年1月1日以来的时间戳秒数
*
* @return 返回的时间戳是自1970年1月1日以来的秒数。
*/
uint32_t rt_data_time_timestamp(void)
{
rtc_date_t date;
rtc_time_t time;
date.year = rt_save.real_time.date.year;
date.month = rt_save.real_time.date.month;
date.day = rt_save.real_time.date.day;
time.hour = rt_save.real_time.date.hour;
time.minute = rt_save.real_time.date.minute;
time.second = rt_save.real_time.date.second;
return time2stamp(&date, &time);
}
/***************************** 通过电阻测算,只在测试程序中使用 *****************************/
/**
* @brief 回路电流检测转换
* @param {uint16_t} adc
* @return {float32} 回路电流值
* @note 计算公式如下:
* > 压力和电流关系:(adc/4096)*VREF_VALUE/(5*20) = ma <p>
* > 简化后adc*VREF_VALUE/409600 = ma <p>
*/
float32 loop_current_convert(uint16_t adc)
{
float32 f;
f = ((float32)(adc * CPU_VREF * 1000)) / 409600;
return f;
}
/**
* @brief DAC:IP输出电流值转换
* @param {uint16_t} dac
* @return {float32} IP输出电流值
* @note
*/
float32 ip_dac2current(uint16_t dac)
{
float32 vol = 0, cur = 0;
// 计算电压值 mv
vol = (float32)dac / 4096 * VREF_VALUE;
// 计算电流值 ma
cur = vol / (40 * 40);
// 如果电流值大于2.5则设置为2.5
if (cur > 2.5f)
{
cur = 2.5;
}
return cur;
}
/**
* @brief IP输出电流值转换
* @return {*}
* @note
*/
float32 ip2current(void)
{
float32 cur = 0;
adc_raw[ADC_VIP_CHANNEL] = adc_result_average(ADCS_1, ADC_VIP_CHANNEL);
// 计算电流值 ma
cur = (float32)adc_raw[ADC_VIP_CHANNEL] * CPU_VREF * 1000 / (float32)(10 * 31 * ADC_MAX);
return cur;
}
/**
* @brief 压力输出kpa值转换
* @param {uint16_t} adc
* @return {*}
* @note
*/
float32 pressure_adc2kpa(uint16_t adc)
{
return ((CPU_VREF * adc / ADC_MAX) - 0.2755f) / 2.885f * 1000;
}
/**
* @brief PWM输出电流值转换
* @param {float32} current
* @return {*}
* @note: 计算公式如下:
* > I_OUT=3V*Duty/10K*100(mA)
*/
void pwm_duty2current(float32 cur_ma)
{
float32 current;
if (cur_ma < 4)
current = 4;
else if (cur_ma > 20)
current = 20;
else
current = cur_ma;
dac161p997_output_current(current);
}
/**
* @brief 获取RTC时间
* @param {date_time_t} *time
* @return {*}
* @note
*/
BOOL get_timestamp(date_time_t *time)
{
BOOL ret = FALSE;
uint8_t tmp[7];
ret = rtc_get_clock_time(tmp);
if (ret == FALSE)
{
return FALSE;
}
time->date.year = hex_format_dec(tmp[6]); // 将年存储在data结构体的year字段中
time->date.month = hex_format_dec(tmp[5]); // 将月存储在data结构体的month字段中
time->date.day = hex_format_dec(tmp[4]); // 将日存储在data结构体的day字段中
time->date.hour = hex_format_dec(tmp[2]); // 将时存储在data结构体的hour字段中
time->date.minute = hex_format_dec(tmp[1]); // 将分存储在data结构体的minute字段中
time->date.second = hex_format_dec(tmp[0]); // 将秒存储在data结构体的second字段中
return TRUE;
}
/**
* 从实时时钟RTC获取当前的日期和时间。
*
* @param date 指向rtc_date_t结构体的指针用于存储日期年、月、日
* @param time 指向rtc_time_t结构体的指针用于存储时间小时、分钟、秒
* @return 如果成功获取日期和时间则返回TRUE否则返回FALSE。
*
* 这个函数首先尝试更新实时时间如果成功则将日期和时间填充到传入的date和time结构体中。
* 如果更新实时时间失败则不会修改date和time指向的结构体并返回FALSE。
*/
BOOL rtc_get_datetime(rtc_date_t *const date, rtc_time_t *const time)
{
date_time_t real_time;
BOOL ret = FALSE;
ret = update_real_time(&real_time);
if (ret == TRUE)
{
date->year = real_time.date.year;
date->month = real_time.date.month;
date->day = real_time.date.day;
time->hour = real_time.date.hour;
time->minute = real_time.date.minute;
time->second = real_time.date.second;
}
return ret;
}
// 从RAM中获取当前的日期和时间。
void rtc_get_datetime_ram(rtc_date_t *const date, rtc_time_t *const time)
{
date_time_t *real_time = (date_time_t *)&rt_save.real_time;
date->year = real_time->date.year;
date->month = real_time->date.month;
date->day = real_time->date.day;
time->hour = real_time->date.hour;
time->minute = real_time->date.minute;
time->second = real_time->date.second;
}
/**
* @brief 更新实时时间
* @return {*}
* @note
*/
BOOL update_real_time(date_time_t *real_time)
{
return get_timestamp(real_time);
}
/**
* @brief 从实时数据结构中获取日期和时间。
*
* 此函数从`rt_save.real_time`结构中获取实时日期和时间,并将各个组成部分(年、月、日、小时、分钟、秒)存储在提供的指针中。
*
* @param year 存储年份的指针。
* @param month 存储月份的指针。
* @param day 存储日期的指针。
* @param hour 存储小时的指针。
* @param min 存储分钟的指针。
* @param sec 存储秒数的指针。
*/
BOOL get_real_time(uint8_t *year, uint8_t *month, uint8_t *day, uint8_t *hour, uint8_t *min, uint8_t *sec)
{
date_time_t real_time;
BOOL ret = FALSE;
ret = update_real_time(&real_time);
*year = real_time.date.year;
*month = real_time.date.month;
*day = real_time.date.day;
*hour = real_time.date.hour;
*min = real_time.date.minute;
*sec = real_time.date.second;
return ret;
}
/**
* @brief 设置UART1空闲处理函数
*
* 当UART1处于空闲状态时调用此函数进行空闲处理。
* 如果_uart1_idel_sec_count大于0则将其减1。
*/
void uart1_set_idel_handle_1_sec(void)
{
if (_uart1_idel_sec_count > 0)
{
_uart1_idel_sec_count--;
}
}
/**
* @brief 设置UART1空闲状态
*
* 根据传入的布尔值设置UART1的空闲状态。当idel为TRUE时将UART1的空闲秒数计数器重置为0
* 当idel为FALSE时将UART1的空闲秒数计数器设置为5。
*
* @param idel 空闲状态标志TRUE表示空闲FALSE表示非空闲
*/
void uart1_set_idel_status(BOOL idel)
{
if (idel == TRUE)
{
_uart1_idel_sec_count = 0;
}
else
{
_uart1_idel_sec_count = 30;
}
}
/**
* @brief 获取 UART1 空闲状态
*
* 获取 UART1 的空闲状态。
*
* @return UART1 的空闲状态
* 如果 UART1 处于空闲状态,则返回 TRUE否则返回 FALSE。
*/
BOOL uart1_get_idel_status(void)
{
return _uart1_idel_sec_count == 0 ? TRUE : FALSE;
}
/**
* @brief Sets the real-time clock (RTC) with the specified date and time.
*
* @param year The year value (0-99).
* @param month The month value (1-12).
* @param day The day value (1-31).
* @param hour The hour value (0-23).
* @param min The minute value (0-59).
* @param sec The second value (0-59).
*/
BOOL set_real_time(uint8_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec)
{
rtc_date date = {
.year = dec_format_hex(year),
.month = dec_format_hex(month),
.day = dec_format_hex(day),
.hour = dec_format_hex(hour),
.minute = dec_format_hex(min),
.second = dec_format_hex(sec),
.weekday = get_weekday(BASE_YEAR + year,
month,
day),
};
rtc_weekday_convert(&date.weekday);
if (rtc_set_clock_time(&date) == FALSE)
{
return FALSE;
}
return TRUE;
}
/**
* @brief 读取4mA输入时的ADC电压
* @return {*}
*/
void set_loop_4ma(void)
{
calib_param_t *p = (calib_param_t *)&calib_param[CALIBPARA_LOOP];
p->original_adc_value[0] = adc_result_average(ADCS_1, ADC_LOOP_CHANNEL); // 获取4mA输入时adc值
calib_param_calculate(CALIBPARA_LOOP, udevice.input_offset, udevice.input_span, p->min, p->max);
}
/**
* @brief 读取20mA输入时的ADC值
* @return {*}
*/
void set_loop_20ma(void)
{
calib_param_t *p = (calib_param_t *)&calib_param[CALIBPARA_LOOP];
p->original_adc_value[1] = adc_result_average(ADCS_1, ADC_LOOP_CHANNEL);
calib_param_calculate(CALIBPARA_LOOP, udevice.input_offset, udevice.input_span, p->min, p->max);
}
/**
* @brief 设置压力最小值
*
* 根据给定的值和索引设置压力的最小值。
*
* @param set_value 要设置的值
* @param index 压力索引
*/
void set_pressure_min(uint8_t unit, float32 set_value, pressure_type_e index)
{
pressure_calib_param_t *p = NULL;
uint8_t channel = 0;
switch (index)
{
case PRESSURE_PARAM_S:
channel = ADC_BP_CHANNEL;
p = (pressure_calib_param_t *)&pressure_calib_param[PRESSURE_PARAM_S];
break;
case PRESSURE_PARAM_A:
channel = ADC_BPA_CHANNEL;
p = (pressure_calib_param_t *)&pressure_calib_param[PRESSURE_PARAM_A];
break;
case PRESSURE_PARAM_B:
channel = ADC_BPB_CHANNEL;
p = (pressure_calib_param_t *)&pressure_calib_param[PRESSURE_PARAM_B];
break;
default:
return;
}
p->min_set = pressure_unit2kpa(set_value, unit);
p->min_ad_value = adc_raw[channel];
eeprom_lc02b_write(PRRESSURE_CALIBRATION_ADDRESS, (uint8_t *)pressure_calib_param, sizeof(pressure_calib_param_t) * PRESSURE_PARAM_MAX);
calib_pressure(index);
}
/**
* @brief 校准压力传感器
*
* 根据传入的压力传感器类型,对相应的校准参数进行初始化并计算校准值。
*
* @param index 压力传感器类型类型为pressure_type_e枚举
*/
void calib_pressure(pressure_type_e index)
{
calibration_e calib_index = CALIBPARA_MAX;
calib_param_t *p = NULL;
pressure_calib_param_t *pp = NULL;
if (index == PRESSURE_PARAM_S)
{
calib_index = CALIBPARA_PS;
p = (calib_param_t *)&calib_param[calib_index];
pp = &pressure_calib_param[PRESSURE_PARAM_S];
}
else if (index == PRESSURE_PARAM_A)
{
calib_index = CALIBPARA_PA;
p = (calib_param_t *)&calib_param[calib_index];
pp = &pressure_calib_param[PRESSURE_PARAM_A];
}
else if (index == PRESSURE_PARAM_B)
{
calib_index = CALIBPARA_PB;
p = (calib_param_t *)&calib_param[calib_index];
pp = &pressure_calib_param[PRESSURE_PARAM_B];
}
else
{
DBG_ASSERT(FALSE __DBG_LINE);
}
osel_memset((uint8_t *)p, 0, sizeof(calib_param_t));
p->original_adc_value[0] = pp->min_ad_value;
p->original_adc_value[1] = pp->max_ad_value;
calib_param_calculate(calib_index, 0, 0, pp->min_set, pp->max_set);
}
/**
* @brief 设置压力最大输入时的ADC电压
* @return {*}
*/
void set_pressure_max(uint8_t unit, float32 set_value, pressure_type_e index)
{
pressure_calib_param_t *p = NULL;
uint8_t channel = 0;
switch (index)
{
case PRESSURE_PARAM_S:
channel = ADC_BP_CHANNEL;
p = (pressure_calib_param_t *)&pressure_calib_param[PRESSURE_PARAM_S];
break;
case PRESSURE_PARAM_A:
channel = ADC_BPA_CHANNEL;
p = (pressure_calib_param_t *)&pressure_calib_param[PRESSURE_PARAM_A];
break;
case PRESSURE_PARAM_B:
channel = ADC_BPB_CHANNEL;
p = (pressure_calib_param_t *)&pressure_calib_param[PRESSURE_PARAM_B];
break;
default:
return;
}
p->max_set = pressure_unit2kpa(set_value, unit);
p->max_ad_value = adc_raw[channel];
eeprom_lc02b_write(PRRESSURE_CALIBRATION_ADDRESS, (uint8_t *)pressure_calib_param, sizeof(pressure_calib_param_t) * PRESSURE_PARAM_MAX);
calib_pressure(index);
}
/**
* @brief 记录输出4mA时的PWM值
* @return {*}
*/
void set_output_4ma_pwm(uint16_t value)
{
// board_cache[BOARD_CACHE_1] = value;
}
/**
* @brief 记录输出20mA时的PWM值
* @return {*}
*/
void set_output_20ma_pwm(uint16_t value)
{
// board_cache[BOARD_CACHE_2] = value;
}
/**
* @brief 设置语言预加载标志
* @param {uint8_t} language
* @return {*}
* @note
*/
void set_app_preload_language_flag(uint8_t flag)
{
if (board_data.app_preload_language_flag != flag)
{
board_data.app_preload_language_flag = flag;
}
}
/**
* @brief 设置工作页面索引
* @param {uint8_t} index
* @return {*}
* @note
*/
void set_work_page_index(uint8_t index)
{
rt_save.work_menu_index = index;
}
/**
* @brief 获取工作页面索引
* @return {*}
* @note
*/
uint8_t get_work_page_index(void)
{
return rt_save.work_menu_index;
}
uint8_t get_language(void)
{
return udevice.display_language;
}
void set_language(uint8_t language)
{
udevice.display_language = language;
}
/**
* @brief 设置预加载BOOTLOAD标志位
* @param {uint8_t} flag
* @return {*}
* @note
*/
void set_app_preload_bootload_flag(uint16_t flag)
{
if (app_preload_bootload_flag != flag)
{
app_preload_bootload_flag = flag;
}
}
/**
* @brief Get the application preload bootload flag
* @return The application preload bootload flag
*/
uint16_t get_app_preload_bootload_flag(void)
{
return app_preload_bootload_flag;
}
/**
* @brief Get the application preload language flag
* @return The application preload language flag
*/
uint8_t get_app_preload_language_flag(void)
{
return board_data.app_preload_language_flag;
}
/**
* @brief 设置预加载BOOTLOAD标志位
* @param {app_preload_bootload_jump_e} flag
* @return {*}
* @note
*/
void set_app_preload_bootload_jump_flag(app_preload_bootload_jump_e flag)
{
if (app_preload_bootload_jump_flag != flag)
{
app_preload_bootload_jump_flag = flag;
}
}
/**
* @brief Get the application preload bootload jump flag
* @return The application preload bootload jump flag
*/
app_preload_bootload_jump_e get_app_preload_bootload_jump_flag(void)
{
return (app_preload_bootload_jump_e)app_preload_bootload_jump_flag;
}
/**
* @brief 获取种子值
*
* 返回一个用于随机数生成的种子值。
*
* @return 返回一个 32 位无符号整数作为种子值
*/
uint32_t get_seed(void)
{
uint32_t seed = (uint32_t)(adc_result_only_one(ADCS_1, ADC_LOOP_CHANNEL) | adc_result_only_one(ADCS_1, ADC_BP_CHANNEL) << 8 | adc_result_only_one(ADCS_1, ADC_NTC_CHANNEL) << 16) +
(uint32_t)(adc_result_only_one(ADCS_1, ADC_NTC_CHANNEL));
return seed;
}
/**
* @brief 判断是否使用了外接LCD板
*
* 此函数用于判断系统是否使用了外接LCD板。
*
* @return BOOL 如果使用了外接LCD板则返回FALSE否则返回TRUE。
*/
BOOL is_lcd_ext_board(void)
{
if (LCD_DETECT() == TRUE)
{
return FALSE;
}
else
{
return TRUE;
}
}
// 更新设备最小最大温度
void update_device_temp_min_max(float32 temperature)
{
if (udevice.min_temp == TEMP_MIN_DEFAULT)
{
udevice.min_temp = temperature;
}
if (udevice.max_temp == TEMP_MAX_DEFAULT)
{
udevice.max_temp = temperature;
}
if (udevice.min_temp > temperature)
{
udevice.min_temp = temperature;
}
if (udevice.max_temp < temperature)
{
udevice.max_temp = temperature;
}
}
// 判断是否可以工作无线模块
BOOL wireless_can_work(float32 loop_current)
{
BOOL rst = TRUE;
if (udevice.wireless_enable == TRUE)
{
if (udevice.zero_power_condition == VALVE_CLOSE)
{
// 零功率条件下,执行机构位置在下面
// 蓝牙启动需要6.0mA
if (loop_current < GUI_HIGHT_CURRENT_MIN)
{
rst = FALSE;
}
}
else
{
// 零功率条件下,执行机构位置在上面
// 蓝牙启动需要8.0mA
if (loop_current < BLE_CURRENT_WORK)
{
rst = FALSE;
}
}
}
else
{
rst = FALSE;
}
return rst;
}
// 是否达到了运行的最大时间
BOOL is_run_max_time(void)
{
return rt_save.dev_run_time_h >= WORK_HOURS_MAX;
}