/** * @file board.c * @author xxx * @date 2023-09-12 13:52:37 * @brief * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. */ #include #include #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)

* > Ov=[(Osh-Osl)*(Iv-Isl)/(Ish-Isl)]+Osl

*/ 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

*> 10% - 5.6mA

*> 20% - 7.2mA

*> 30% - 8.8mA

*> 40% - 10.4mA

*> 50% - 12.0mA

*> 55% - 12.8mA

*> 60% - 13.6mA

*> 70% - 15.2mA

*> 80% - 16.8mA

*> 90% - 18.4mA

*> 100% - 20.0mA

*/ 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)

* > m = y2 - k * x2

*/ 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

* > 简化后:adc*VREF_VALUE/409600 = ma

*/ 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; }