B.2 硬件移植代码
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "system_init.h"
#include "system_display.h"
#include "system_input.h"
#include "system_app.h"
#include "system_menu.h"
#include "buzzer.h"
#include "time_manager.h"
#include "uart_comm.h"
#include "pid_control.h"
#include "fuzzy_pid.h"
#include "env_simulator.h"
#include "cmd_handler.h"
#include "app_globals.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
I2C_HandleTypeDef hi2c2;
SPI_HandleTypeDef hspi1;
UART_HandleTypeDef huart1;
PCD_HandleTypeDef hpcd_USB_FS;
/* USER CODE BEGIN PV */
/* 全局句柄 — 通过 app_globals.h 供所有模块访问 */
PID_Handle_t g_pid;
EnvSim_Handle_t g_env;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
static void MX_USB_PCD_Init(void);
static void MX_I2C1_Init(void);
static void MX_I2C2_Init(void);
static void MX_SPI1_Init(void);
static void MX_USART1_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI1_Init();
MX_USB_PCD_Init();
MX_I2C1_Init();
MX_I2C2_Init();
MX_USART1_Init();
/* USER CODE BEGIN 2 */
// 系统初始化
if (System_Init_All() != HAL_OK) {
Error_Handler();
}
// 输入模块初始化
if (Input_Init() != HAL_OK) {
Error_Handler();
}
// 应用模块初始化(绑定g_env作为统一数据源)
if (App_Init_WithSim(&g_env) != HAL_OK) {
Error_Handler();
}
// 串口通信初始化
UART_Comm_Init();
/* PID控制器初始化 (论文5.5.1节初始参数: Kp0=3.0, Ki0=0.5, Kd0=1.0) */
PID_Init(&g_pid, 3.0f, 0.5f, 1.0f);
/* 设置初始输入参数:T_surface初始值与环境温度一致 */
PID_SetInput(&g_pid, -5.0f, -5.0f, 60.0f, 5.0f); /* T_env, T_surface, H, W */
/* 自动启动PID(仿真模式下无需手动发命令) */
PID_Start(&g_pid);
/* 环境模拟器初始化 */
EnvSim_Init(&g_env, -5.0f, 60.0f, 5.0f);
/* 命令处理器初始化 */
CmdHandler_Init(&g_pid, &g_env);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// 蜂鸣器定时处理
Buzzer_HandleTypeDef *buzzer = System_Get_Buzzer();
if (buzzer != NULL) {
Buzzer_Process(buzzer);
}
// 时间同步处理
TimeManager_Sync();
// 运行应用主循环
if (App_Run() != HAL_OK) {
Error_Handler();
}
// 串口接收处理 + 命令解析
CmdHandler_Process();
/* 环境模拟器更新 */
EnvSim_Process(&g_env);
/* 将模拟器当前参数同步到PID输入(环境温度/湿度/风速直接从env_sim读取) */
g_pid.input.t_env = g_env.t_env;
g_pid.input.humidity = g_env.humidity;
g_pid.input.wind_speed = g_env.wind_speed;
/* PID控制器计算 */
/* STOPPED状态下:若T_surface再次低于重启阈值(0°C),自动重启 */
if (g_pid.state == PID_STATE_STOPPED &&
g_pid.input.t_surface < PID_RESTART_TEMP) {
g_pid.state = PID_STATE_RUNNING;
g_pid.last_tick = HAL_GetTick();
}
PID_Process(&g_pid);
// 串口上报:数据变化时立即发送,无变化时5秒心跳一次
{
static uint32_t last_uart_report = 0;
static uint32_t last_pid_report = 0;
static int16_t last_temp = 0x7FFF;
static uint8_t last_humi = 0xFF;
static uint16_t last_wind = 0xFFFF;
static uint8_t last_sec = 0xFF;
uint32_t now_ms = HAL_GetTick();
int16_t cur_temp = App_Get_Temperature();
uint8_t cur_humi = App_Get_Humidity();
uint16_t cur_wind = App_Get_WindSpeed();
System_Config_t* cfg = Menu_Get_Config();
uint8_t cur_sec = (cfg != NULL) ? cfg->system_time.second : 0;
/* 基础数据:有变化或5秒超时发送 */
uint8_t changed = (cur_temp != last_temp ||
cur_humi != last_humi ||
cur_wind != last_wind ||
cur_sec != last_sec);
if (changed || (now_ms - last_uart_report) >= 5000) {
HAL_StatusTypeDef uart_ret = UART_Comm_Printf(
"T:%d H:%d W:%d Time:%02d:%02d:%02d\r\n",
cur_temp, cur_humi, cur_wind,
(cfg != NULL) ? cfg->system_time.hour : 0,
(cfg != NULL) ? cfg->system_time.minute : 0,
cur_sec);
App_Set_UartTxStatus(uart_ret == HAL_OK ? 1 : 0);
last_temp = cur_temp;
last_humi = cur_humi;
last_wind = cur_wind;
last_sec = cur_sec;
last_uart_report = now_ms;
}
/* PID数据:每1秒独立上报,不依赖基础数据变化 */
if ((now_ms - last_pid_report) >= 1000) {
/* 计算当前结冰速率和加热功率(n_ice 只依赖环境温度,t_surface 不影响结果) */
IcingParams_t ip;
Fuzzy_CalcIcingParams(g_pid.input.t_env,
g_pid.input.wind_speed,
g_pid.input.humidity,
g_pid.input.t_surface, &ip);
UART_Comm_Printf(
"PID state:%d Tenv:%.1f Tsurf:%.2f H:%.1f W:%.1f "
"mdot:%.5f Pff:%.1f Pdyn:%.1f "
"e:%.3f I:%.3f D:%.3f Kp:%.3f Ki:%.3f Kd:%.3f P:%.2f\r\n",
(int)g_pid.state,
g_pid.input.t_env,
g_pid.input.t_surface,
g_pid.input.humidity,
g_pid.input.wind_speed,
ip.m_dot, ip.p_ff, ip.p_dyn,
g_pid.process.error,
g_pid.process.integral,
g_pid.process.derivative,
g_pid.process.kp,
g_pid.process.ki,
g_pid.process.kd,
g_pid.process.output);
last_pid_report = now_ms;
}
}
HAL_Delay(10); /* 10ms延迟,减少CPU占用 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief I2C1 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_ENABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
/**
* @brief I2C2 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C2_Init(void)
{
/* USER CODE BEGIN I2C2_Init 0 */
/* USER CODE END I2C2_Init 0 */
/* USER CODE BEGIN I2C2_Init 1 */
/* USER CODE END I2C2_Init 1 */
hi2c2.Instance = I2C2;
hi2c2.Init.ClockSpeed = 100000;
hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c2.Init.OwnAddress1 = 0;
hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c2.Init.OwnAddress2 = 0;
hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_ENABLE;
if (HAL_I2C_Init(&hi2c2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C2_Init 2 */
/* USER CODE END I2C2_Init 2 */
}
/**
* @brief SPI1 Initialization Function
* @param None
* @retval None
*/
static void MX_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
/* SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI1_Init 2 */
/* USER CODE END SPI1_Init 2 */
}
/**
* @brief USB Initialization Function
* @param None
* @retval None
*/
static void MX_USB_PCD_Init(void)
{
/* USER CODE BEGIN USB_Init 0 */
/* USER CODE END USB_Init 0 */
/* USER CODE BEGIN USB_Init 1 */
/* USER CODE END USB_Init 1 */
hpcd_USB_FS.Instance = USB;
hpcd_USB_FS.Init.dev_endpoints = 8;
hpcd_USB_FS.Init.speed = PCD_SPEED_FULL;
hpcd_USB_FS.Init.low_power_enable = DISABLE;
hpcd_USB_FS.Init.lpm_enable = DISABLE;
hpcd_USB_FS.Init.battery_charging_enable = DISABLE;
if (HAL_PCD_Init(&hpcd_USB_FS) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USB_Init 2 */
/* USER CODE END USB_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|SEG_CLK_Pin|SEG_DIO1_Pin|SEG_DIO2_Pin|SEG_DIO3_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(BUZZER_GPIO_Port, BUZZER_Pin, GPIO_PIN_SET);
/*Configure GPIO pins : ENC1_S1_Pin ENC1_KEY_Pin */
GPIO_InitStruct.Pin = ENC1_S1_Pin|ENC1_KEY_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : ENC1_S2_Pin */
GPIO_InitStruct.Pin = ENC1_S2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(ENC1_S2_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : SD_CS_Pin */
GPIO_InitStruct.Pin = SD_CS_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(SD_CS_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : SEG_CLK_Pin SEG_DIO1_Pin SEG_DIO2_Pin SEG_DIO3_Pin */
GPIO_InitStruct.Pin = SEG_CLK_Pin|SEG_DIO1_Pin|SEG_DIO2_Pin|SEG_DIO3_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pins : ENC2_S1_Pin */
GPIO_InitStruct.Pin = ENC2_S1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(ENC2_S1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : ENC2_S2_Pin (PB8) */
GPIO_InitStruct.Pin = ENC2_S2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(ENC2_S2_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : ENC2_KEY_Pin (PB9) - 外部中断 */
GPIO_InitStruct.Pin = ENC2_KEY_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(ENC2_KEY_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : BUZZER_Pin */
GPIO_InitStruct.Pin = BUZZER_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(BUZZER_GPIO_Port, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
HAL_NVIC_SetPriority(EXTI2_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI2_IRQn);
HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
/* ENC2_KEY 移至 PB9,同属 EXTI9_5,已在上方启用 */
/* USER CODE BEGIN MX_GPIO_Init_2 */
// 设置SD卡CS引脚为高电平(不选中状态)
HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_SET);
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/**
* @brief USART1 初始化 (PA9=TX, PA10=RX, 115200 8N1)
* @retval None
*/
static void MX_USART1_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK) {
Error_Handler();
}
/* 启用 USART1 全局中断 */
HAL_NVIC_SetPriority(USART1_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
}
/**
* @brief GPIO外部中断回调函数
* @param GPIO_Pin: 触发中断的GPIO引脚
* @retval None
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
static uint32_t last_encoder_time = 0;
uint32_t current_time = HAL_GetTick();
// 获取蜂鸣器句柄
Buzzer_HandleTypeDef *buzzer = System_Get_Buzzer();
// 蜂鸣器反馈处理
if (buzzer != NULL) {
switch (GPIO_Pin) {
case ENC1_S1_Pin:
case ENC2_S1_Pin:
// 编码器旋转中断 - 防抖处理
if (current_time - last_encoder_time > 50) {
Buzzer_Encoder_Feedback(buzzer);
last_encoder_time = current_time;
}
break;
case ENC1_KEY_Pin:
case ENC2_KEY_Pin:
/* 按键中断:只在按下时(低电平)响一次,加防抖避免重复触发 */
if ((current_time - last_encoder_time) > 200) {
if (HAL_GPIO_ReadPin(
(GPIO_Pin == ENC1_KEY_Pin) ? ENC1_KEY_GPIO_Port : ENC2_KEY_GPIO_Port,
GPIO_Pin) == GPIO_PIN_RESET) {
Buzzer_Key_Feedback(buzzer);
last_encoder_time = current_time;
}
}
break;
default:
break;
}
}
// 委托给输入处理模块
Input_IRQ_Handler(GPIO_Pin);
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 LiXinrui.cn
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果