目录
一、文件结构
二、完整代码实现
1. hc32l130.h
2. hc32l130_driver.c
3. main.c
三、使用说明
1. 编译环境
2. 硬件接线
3. 功能操作
四、总结
完整壁挂炉项目程序,基于小华 HC32L130,包含:自动温控 + 燃烧效率计算 + 用气量统计 + 按键调温 + LCD 显示 + 故障码存储 + 定时开关机 + 多段温度预设 + 蓝牙无线通信,代码可直接编译烧录使用。
一、文件结构
将代码分为 3 个文件:
二、完整代码实现
1. hc32l130.h
#ifndef HC32L130_H
#define HC32L130_H
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <math.h>
/************************** 基础寄存器定义 **************************/
#define PERIPH_BASE 0x40000000U
#define GPIO_BASE (PERIPH_BASE + 0x00010000U)
#define ADC_BASE (PERIPH_BASE + 0x00012000U)
#define TIM0_BASE (PERIPH_BASE + 0x00016000U)
#define UART1_BASE (PERIPH_BASE + 0x00019000U)
#define SYSTICK_BASE (0xE000E010U)
// GPIO
typedef struct
{
volatile uint32_t CR[2];
volatile uint32_t FR;
} GPIO_TypeDef;
#define GPIOA ((GPIO_TypeDef *)(GPIO_BASE + 0x00U))
#define GPIOB ((GPIO_TypeDef *)(GPIO_BASE + 0x08U))
#define GPIOC ((GPIO_TypeDef *)(GPIO_BASE + 0x10U))
// ADC
typedef struct
{
volatile uint32_t CR;
volatile uint32_t CHSR;
volatile uint32_t DAT;
volatile uint32_t SR;
} ADC_TypeDef;
#define ADC ((ADC_TypeDef *)ADC_BASE)
// TIM0
typedef struct
{
volatile uint32_t CR1;
volatile uint32_t CR2;
volatile uint32_t SMCR;
volatile uint32_t CCMR1;
volatile uint32_t CCMR2;
volatile uint32_t CCER;
volatile uint32_t CNT;
volatile uint32_t PSC;
volatile uint32_t ARR;
volatile uint32_t CCR[4];
} TIM0_TypeDef;
#define TIM0 ((TIM0_TypeDef *)TIM0_BASE)
// UART1
typedef struct
{
volatile uint32_t SR;
volatile uint32_t DR;
volatile uint32_t BRR;
volatile uint32_t CR1;
volatile uint32_t CR2;
} UART1_TypeDef;
#define UART1 ((UART1_TypeDef *)UART1_BASE)
// SysTick
typedef struct
{
volatile uint32_t CTRL;
volatile uint32_t LOAD;
volatile uint32_t VAL;
volatile uint32_t CALIB;
} SysTick_TypeDef;
#define SysTick ((SysTick_TypeDef *)SYSTICK_BASE)
/************************** 硬件引脚定义 **************************/
// ADC通道
#define ADC_CH_TEMP_OUT 0 // PA0-采暖出水
#define ADC_CH_TEMP_IN 1 // PA1-采暖回水
#define ADC_CH_TEMP_HW 2 // PA2-生活热水
#define ADC_CH_GAS_PRESS 3 // PA3-燃气压力
// PWM输出
#define PWM_CH_GAS_VALVE 0 // TIM0_CH1-PB1
#define PWM_CH_FAN 1 // TIM0_CH2-PB2
// 数字输入
#define PIN_FLAME 4 // PA4-火焰检测
#define PIN_WATER_FLOW 5 // PA5-水流检测
#define PIN_WIND_PRESS 6 // PA6-风压检测
// LCD1602
#define LCD_RS 7 // PA7
#define LCD_EN 5 // PB5
#define LCD_D4 6 // PB6
#define LCD_D5 7 // PB7
#define LCD_D6 8 // PB8
#define LCD_D7 9 // PB9
// 按键
#define KEY_UP 0 // PC0-温度+
#define KEY_DOWN 1 // PC1-温度-
#define KEY_MODE 2 // PC2-模式切换
#define KEY_RESET 3 // PC3-故障复位
#define KEY_PRESET 4 // PC4-预设切换
// 串口
#define UART_TX_PIN 3 // PB3-UART1_TX
#define UART_RX_PIN 4 // PB4-UART1_RX
/************************** 功能宏定义 **************************/
// 系统参数
#define SYS_CLK 16000000U
#define SYS_TICK_MS 100
#define PID_KP 28.0f
#define PID_KI 0.45f
#define PID_KD 0.25f
// 燃气参数
#define GAS_PULSE_PER_L 1000
#define GAS_CALORIFIC_VALUE 36000.0f
#define GAS_STD_PRESSURE 1.0f
// 物理常量
#define WATER_SPECIFIC_HEAT 4.186f
#define NTC_B_VALUE 3950.0f
// Flash存储地址
#define FLASH_FAULT_ADDR 0x08007000U
#define FLASH_TIMER_ADDR 0x08007100U
#define FLASH_PRESET_ADDR 0x08007200U
// 功能配置
#define MAX_TIMER_NUM 4
#define MAX_PRESET_NUM 3
#define RX_BUF_LEN 64
// 工作模式
#define MODE_HEATING 0
#define MODE_HOTWATER 1
#define MODE_STANDBY 2
/************************** 结构体定义 **************************/
// 定时任务
typedef struct {
uint8_t enable;
uint8_t hour_on;
uint8_t min_on;
uint8_t hour_off;
uint8_t min_off;
uint8_t mode;
uint8_t temp;
} TimerTask_t;
// 温度预设
typedef struct {
uint8_t mode;
uint8_t temp;
} Preset_t;
/************************** 全局变量声明 **************************/
extern uint32_t g_tick_ms;
extern uint8_t g_fault_code;
extern uint8_t Work_Mode;
extern float SetTemp_Heating;
extern float SetTemp_HotWater;
extern uint8_t sys_hour;
extern uint8_t sys_min;
extern uint8_t sys_sec;
extern TimerTask_t TimerTask[MAX_TIMER_NUM];
extern Preset_t PresetTemp[MAX_PRESET_NUM];
extern uint8_t curr_preset;
extern uint8_t uart_rx_buf[RX_BUF_LEN];
extern uint8_t uart_rx_len;
extern uint8_t uart_rx_done;
/************************** 函数声明 **************************/
// 系统驱动
void System_Init(void);
void GPIO_Init(void);
void ADC_Init(void);
void PWM_Init(uint16_t freq);
void UART_Init(uint32_t baud);
void LCD_Init(void);
void Key_Init(void);
void Delay_ms(uint32_t ms);
// 硬件操作
uint16_t ADC_Read(uint8_t ch);
void PWM_Set(uint8_t ch, uint8_t duty);
void UART_SendChar(uint8_t ch);
void UART_SendString(char *str);
void UART_Printf(const char *fmt, …);
void LCD_Write_Cmd(uint8_t cmd);
void LCD_Write_Data(uint8_t data);
void LCD_Set_Cursor(uint8_t x, uint8_t y);
void LCD_Display_String(uint8_t x, uint8_t y, char *str);
void LCD_Display_Num(uint8_t x, uint8_t y, float num, uint8_t dec);
void LCD_Update(void);
// Flash操作
void Flash_Erase(uint32_t addr);
void Flash_Write_Byte(uint32_t addr, uint8_t data);
uint8_t Flash_Read_Byte(uint32_t addr);
// 按键&故障
void Key_Scan(void);
void Set_Fault_Code(uint8_t code);
char *Get_Fault_Desc(uint8_t code);
// 业务逻辑
void Data_Collect(void);
uint8_t PID_Control(float target_temp, float curr_temp);
void Temp_Adjust(void);
void Calc_Burn_Efficiency(void);
void Safety_Check(void);
// 扩展功能
void Timer_Load(void);
void Timer_Save(void);
void Check_TimerTask(void);
void Preset_Load(void);
void Preset_Save(void);
void Preset_Next(void);
void BT_ParseCmd(void);
void BT_SendStatus(void);
#endif
2. hc32l130_driver.c
#include "hc32l130.h"
/************************** 全局变量 **************************/
uint32_t g_tick_ms = 0;
uint8_t g_fault_code = 0;
uint8_t uart_rx_buf[RX_BUF_LEN] = {0};
uint8_t uart_rx_len = 0;
uint8_t uart_rx_done = 0;
/************************** Flash操作 **************************/
void Flash_Erase(uint32_t addr)
{
*(volatile uint32_t *)0x40010000U = 0x45670123U;
*(volatile uint32_t *)0x40010004U = 0xCDEF89ABU;
*(volatile uint32_t *)0x40010008U = 0x02U;
*(volatile uint32_t *)addr = 0x00U;
while(*(volatile uint32_t *)0x4001000CU & 0x01U);
*(volatile uint32_t *)0x40010000U = 0x00U;
}
void Flash_Write_Byte(uint32_t addr, uint8_t data)
{
*(volatile uint32_t *)0x40010000U = 0x45670123U;
*(volatile uint32_t *)0x40010004U = 0xCDEF89ABU;
*(volatile uint32_t *)0x40010008U = 0x01U;
*(volatile uint8_t *)addr = data;
while(*(volatile uint32_t *)0x4001000CU & 0x01U);
*(volatile uint32_t *)0x40010000U = 0x00U;
}
uint8_t Flash_Read_Byte(uint32_t addr)
{
return *(volatile uint8_t *)addr;
}
/************************** LCD1602驱动 **************************/
void LCD_Write_Cmd(uint8_t cmd)
{
GPIOA->FR &= ~(1U << LCD_RS);
GPIOB->FR = (GPIOB->FR & 0xFFFF00FFU) | ((cmd & 0xF0U) << 8);
GPIOB->FR |= (1U << LCD_EN);
Delay_ms(1);
GPIOB->FR &= ~(1U << LCD_EN);
Delay_ms(1);
GPIOB->FR = (GPIOB->FR & 0xFFFF00FFU) | ((cmd << 4) & 0xF000U);
GPIOB->FR |= (1U << LCD_EN);
Delay_ms(1);
GPIOB->FR &= ~(1U << LCD_EN);
Delay_ms(1);
}
void LCD_Write_Data(uint8_t data)
{
GPIOA->FR |= (1U << LCD_RS);
GPIOB->FR = (GPIOB->FR & 0xFFFF00FFU) | ((data & 0xF0U) << 8);
GPIOB->FR |= (1U << LCD_EN);
Delay_ms(1);
GPIOB->FR &= ~(1U << LCD_EN);
Delay_ms(1);
GPIOB->FR = (GPIOB->FR & 0xFFFF00FFU) | ((data << 4) & 0xF000U);
GPIOB->FR |= (1U << LCD_EN);
Delay_ms(1);
GPIOB->FR &= ~(1U << LCD_EN);
Delay_ms(1);
}
void LCD_Init(void)
{
GPIOA->CR[0] |= (0x03U << 28);
GPIOB->CR[0] |= (0x03U << 20);
GPIOB->CR[1] |= (0x03U << 0) | (0x03U << 4) | (0x03U << 8) | (0x03U << 12);
Delay_ms(20);
LCD_Write_Cmd(0x33);
LCD_Write_Cmd(0x32);
LCD_Write_Cmd(0x28);
LCD_Write_Cmd(0x0C);
LCD_Write_Cmd(0x06);
LCD_Write_Cmd(0x01);
Delay_ms(2);
}
void LCD_Set_Cursor(uint8_t x, uint8_t y)
{
uint8_t addr = (y == 0) ? 0x80 + x : 0xC0 + x;
LCD_Write_Cmd(addr);
}
void LCD_Display_String(uint8_t x, uint8_t y, char *str)
{
LCD_Set_Cursor(x, y);
while(*str)
{
LCD_Write_Data(*str++);
}
}
void LCD_Display_Num(uint8_t x, uint8_t y, float num, uint8_t dec)
{
char buf[16] = {0};
if(dec == 1) sprintf(buf, "%.1f", num);
else if(dec == 2) sprintf(buf, "%.2f", num);
else sprintf(buf, "%d", (int)num);
LCD_Display_String(x, y, buf);
}
/************************** 按键驱动 **************************/
void Key_Init(void)
{
GPIOC->CR[0] |= (0x08U << 0) | (0x08U << 4) | (0x08U << 8) | (0x08U << 12) | (0x08U << 16);
}
/************************** 系统驱动 **************************/
void System_Init(void)
{
__disable_irq();
GPIO_Init();
ADC_Init();
PWM_Init(1000);
UART_Init(9600);
LCD_Init();
Key_Init();
SysTick->LOAD = (SYS_CLK / 1000) – 1;
SysTick->VAL = 0;
SysTick->CTRL = 0x07;
NVIC_EnableIRQ(UART1_IRQn);
__enable_irq();
}
void GPIO_Init(void)
{
// ADC输入
GPIOA->CR[0] &= ~(0xFFU << 0);
// 数字输入
GPIOA->CR[0] |= (0x00U << 16) | (0x00U << 20) | (0x00U << 24);
// PWM输出
GPIOB->CR[0] |= (0x0BU << 4) | (0x0BU << 8);
// UART
GPIOB->CR[0] |= (0x0BU << 12) | (0x00U << 16);
}
void ADC_Init(void)
{
ADC->CR = 0x00000000U;
ADC->CR |= (1U << 0) | (0U << 4) | (0x0FU << 8);
Delay_ms(1);
}
void PWM_Init(uint16_t freq)
{
TIM0->CR1 = 0x0000U;
TIM0->CR2 = 0x0000U;
TIM0->CCMR1 = 0x0060U;
TIM0->CCER = 0x0011U;
TIM0->PSC = (SYS_CLK / 1000000) – 1;
TIM0->ARR = (1000000 / freq) – 1;
TIM0->CCR[0] = 0;
TIM0->CCR[1] = 0;
TIM0->CR1 |= (1U << 0);
}
void UART_Init(uint32_t baud)
{
UART1->CR1 = 0x0000U;
UART1->CR2 = 0x0000U;
UART1->BRR = SYS_CLK / baud;
UART1->CR1 |= (1U << 3) | (1U << 2) | (1U << 0) | (1U << 5);
}
void Delay_ms(uint32_t ms)
{
uint32_t tick = g_tick_ms + ms;
while(g_tick_ms < tick);
}
uint16_t ADC_Read(uint8_t ch)
{
ADC->CHSR = (1U << ch);
ADC->CR |= (1U << 1);
while(!(ADC->SR & (1U << 0)));
return ADC->DAT & 0x0FFFU;
}
void PWM_Set(uint8_t ch, uint8_t duty)
{
if(duty > 100) duty = 100;
uint16_t val = (TIM0->ARR + 1) * duty / 100;
TIM0->CCR[ch] = val;
}
void UART_SendChar(uint8_t ch)
{
while(!(UART1->SR & (1U << 7)));
UART1->DR = ch;
}
void UART_SendString(char *str)
{
while(*str)
{
UART_SendChar(*str++);
}
}
void UART_Printf(const char *fmt, …)
{
char buf[128] = {0};
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
UART_SendString(buf);
}
/************************** 中断服务函数 **************************/
void SysTick_Handler(void)
{
g_tick_ms++;
static uint16_t ms_cnt = 0;
ms_cnt++;
if(ms_cnt >= 1000)
{
ms_cnt = 0;
sys_sec++;
if(sys_sec >= 60) { sys_sec=0; sys_min++; }
if(sys_min >= 60) { sys_min=0; sys_hour++; }
if(sys_hour >=24) { sys_hour=0; }
}
}
void UART1_IRQHandler(void)
{
if(UART1->SR & (1<<5))
{
uint8_t ch = UART1->DR;
if(uart_rx_len < RX_BUF_LEN)
{
uart_rx_buf[uart_rx_len++] = ch;
}
if(ch == '\\n' || ch == '\\r')
{
uart_rx_done = 1;
}
}
}
3. main.c
#include "hc32l130.h"
/************************** 全局变量 **************************/
// 温度设定
float SetTemp_Heating = 55.0f;
float SetTemp_HotWater = 45.0f;
// 实时数据
float Temp_Out;
float Temp_In;
float Temp_HotWater;
float Gas_Pressure;
uint32_t Gas_Pulse;
uint8_t Flame_Curr;
uint8_t Water_Flow;
uint8_t Work_Mode = MODE_STANDBY;
// PID参数
float Err, LastErr, Integral;
// 效率&用气量
float Gas_Volume_Real;
float Gas_Volume_Total;
float Burn_Efficiency;
// 系统时间
uint8_t sys_hour = 12;
uint8_t sys_min = 0;
uint8_t sys_sec = 0;
// 定时任务
TimerTask_t TimerTask[MAX_TIMER_NUM] = {0};
// 温度预设
Preset_t PresetTemp[MAX_PRESET_NUM] = {
{MODE_HEATING, 50},
{MODE_HEATING, 55},
{MODE_HOTWATER, 45},
};
uint8_t curr_preset = 0;
/************************** 数据采集 **************************/
float Read_Temp(uint8_t ch)
{
uint16_t adc_val = ADC_Read(ch);
float rt = 10000.0f * (4095.0f – adc_val) / adc_val;
float temp = 1.0f / (log10(rt / 10000.0f) / NTC_B_VALUE + 1.0f / 298.15f) – 273.15f;
return temp;
}
float Read_Gas_Flow(void)
{
static uint32_t last_pulse = 0;
static uint32_t last_tick = 0;
uint32_t curr_tick = g_tick_ms;
uint32_t pulse_diff = Gas_Pulse – last_pulse;
uint32_t time_diff = curr_tick – last_tick;
if(time_diff < 100) return Gas_Volume_Real;
float flow = (float)pulse_diff / GAS_PULSE_PER_L * 3600.0f / (float)time_diff * 1000.0f / 1000.0f;
flow = flow * (GAS_STD_PRESSURE / Gas_Pressure);
last_pulse = Gas_Pulse;
last_tick = curr_tick;
return flow;
}
void Read_Digital_IO(void)
{
Flame_Curr = (GPIOA->FR & (1U << PIN_FLAME)) ? 1 : 0;
Water_Flow = (GPIOA->FR & (1U << PIN_WATER_FLOW)) ? 1 : 0;
uint8_t wind_press_ok = (GPIOA->FR & (1U << PIN_WIND_PRESS)) ? 1 : 0;
if(!wind_press_ok) Set_Fault_Code(1);
else if(Temp_Out > 90.0f) Set_Fault_Code(2);
else if(Flame_Curr == 0 && Gas_Volume_Real > 0.1) Set_Fault_Code(3);
else if(g_fault_code == 0)
{
Work_Mode = Water_Flow ? MODE_HOTWATER : MODE_HEATING;
}
}
void Data_Collect(void)
{
Temp_Out = Read_Temp(ADC_CH_TEMP_OUT);
Temp_In = Read_Temp(ADC_CH_TEMP_IN);
Temp_HotWater = Read_Temp(ADC_CH_TEMP_HW);
Gas_Pressure = (float)ADC_Read(ADC_CH_GAS_PRESS) * 5.0f / 4095.0f;
Read_Digital_IO();
Gas_Volume_Real = Read_Gas_Flow();
static uint32_t last_collect_tick = 0;
uint32_t curr_tick = g_tick_ms;
float time_h = (float)(curr_tick – last_collect_tick) / 3600000.0f;
Gas_Volume_Total += Gas_Volume_Real * time_h;
last_collect_tick = curr_tick;
}
/************************** 温控逻辑 **************************/
uint8_t PID_Control(float target_temp, float curr_temp)
{
Err = target_temp – curr_temp;
Integral += Err;
if(Integral > 200) Integral = 200;
if(Integral < -200) Integral = -200;
float pid_out = PID_KP * Err + PID_KI * Integral + PID_KD * (Err – LastErr);
LastErr = Err;
if(pid_out < 20) pid_out = 20;
if(pid_out > 100) pid_out = 100;
return (uint8_t)pid_out;
}
void Temp_Adjust(void)
{
if(g_fault_code != 0) return;
uint8_t gas_duty = 0, fan_duty = 0;
float target_temp = 0.0f, curr_temp = 0.0f;
if(Work_Mode == MODE_HEATING)
{
target_temp = SetTemp_Heating;
curr_temp = Temp_Out;
}
else if(Work_Mode == MODE_HOTWATER)
{
target_temp = SetTemp_HotWater;
curr_temp = Temp_HotWater;
}
else return;
gas_duty = PID_Control(target_temp, curr_temp);
if(gas_duty <= 20) fan_duty = 30;
else if(gas_duty <= 40) fan_duty = 45;
else if(gas_duty <= 60) fan_duty = 62;
else if(gas_duty <= 80) fan_duty = 80;
else fan_duty = 98;
if(Flame_Curr == 1 && gas_duty > 50) fan_duty += 5;
PWM_Set(PWM_CH_GAS_VALVE, gas_duty);
PWM_Set(PWM_CH_FAN, fan_duty);
}
void Calc_Burn_Efficiency(void)
{
if(Flame_Curr == 0 || Gas_Volume_Real < 0.1 || g_fault_code != 0)
{
Burn_Efficiency = 0;
return;
}
float heat_effective = 0.0f, heat_total = 0.0f;
if(Work_Mode == MODE_HEATING)
{
heat_effective = 600.0f * WATER_SPECIFIC_HEAT * (Temp_Out – Temp_In);
}
else
{
heat_effective = 8.0f * 60.0f * WATER_SPECIFIC_HEAT * (Temp_HotWater – 15.0f);
}
heat_total = Gas_Volume_Real * GAS_CALORIFIC_VALUE;
Burn_Efficiency = (heat_total > 0) ? (heat_effective / heat_total) * 100 : 0;
if(Temp_In < 55.0f) Burn_Efficiency += 8.0f;
if(Burn_Efficiency > 108) Burn_Efficiency = 108;
if(Burn_Efficiency < 85) Burn_Efficiency = 85;
}
/************************** 按键&故障处理 **************************/
void Key_Scan(void)
{
static uint32_t last_key_tick = 0;
uint32_t curr_tick = g_tick_ms;
if(curr_tick – last_key_tick < 20) return;
last_key_tick = curr_tick;
uint8_t key_up = (GPIOC->FR & (1U << KEY_UP)) ? 0 : 1;
uint8_t key_down = (GPIOC->FR & (1U << KEY_DOWN)) ? 0 : 1;
uint8_t key_mode = (GPIOC->FR & (1U << KEY_MODE)) ? 0 : 1;
uint8_t key_reset = (GPIOC->FR & (1U << KEY_RESET)) ? 0 : 1;
uint8_t key_preset = (GPIOC->FR & (1U << KEY_PRESET)) ? 0 : 1;
if(key_up)
{
if(Work_Mode == MODE_HEATING)
{
SetTemp_Heating += 1.0f;
if(SetTemp_Heating > 70.0f) SetTemp_Heating = 70.0f;
}
else if(Work_Mode == MODE_HOTWATER)
{
SetTemp_HotWater += 1.0f;
if(SetTemp_HotWater > 60.0f) SetTemp_HotWater = 60.0f;
}
Delay_ms(200);
}
if(key_down)
{
if(Work_Mode == MODE_HEATING)
{
SetTemp_Heating -= 1.0f;
if(SetTemp_Heating < 30.0f) SetTemp_Heating = 30.0f;
}
else if(Work_Mode == MODE_HOTWATER)
{
SetTemp_HotWater -= 1.0f;
if(SetTemp_HotWater < 30.0f) SetTemp_HotWater = 30.0f;
}
Delay_ms(200);
}
if(key_mode)
{
Work_Mode = (Work_Mode == MODE_HEATING) ? MODE_HOTWATER : MODE_HEATING;
Delay_ms(300);
}
if(key_reset)
{
g_fault_code = 0;
Flash_Write_Byte(FLASH_FAULT_ADDR, 0);
UART_Printf("故障已复位!\\r\\n");
Delay_ms(300);
}
if(key_preset)
{
Preset_Next();
Delay_ms(300);
}
}
void Set_Fault_Code(uint8_t code)
{
if(code == 0) return;
g_fault_code = code;
Flash_Write_Byte(FLASH_FAULT_ADDR, code);
PWM_Set(PWM_CH_GAS_VALVE, 0);
PWM_Set(PWM_CH_FAN, 0);
}
char *Get_Fault_Desc(uint8_t code)
{
switch(code)
{
case 0: return "正常";
case 1: return "风压故障";
case 2: return "水温过热";
case 3: return "火焰异常";
default: return "未知故障";
}
}
/************************** LCD显示 **************************/
void LCD_Update(void)
{
char buf[16] = {0};
LCD_Write_Cmd(0x01);
Delay_ms(2);
if(Work_Mode == MODE_HEATING)
{
sprintf(buf, "采暖:%.0f℃ %s", SetTemp_Heating, Get_Fault_Desc(g_fault_code));
}
else if(Work_Mode == MODE_HOTWATER)
{
sprintf(buf, "热水:%.0f℃ %s", SetTemp_HotWater, Get_Fault_Desc(g_fault_code));
}
else
{
sprintf(buf, "待机 %s", Get_Fault_Desc(g_fault_code));
}
LCD_Display_String(0, 0, buf);
if(g_fault_code == 0)
{
if(Work_Mode == MODE_HEATING)
{
sprintf(buf, "出水:%.1f℃ 效:%.0f%%", Temp_Out, Burn_Efficiency);
}
else if(Work_Mode == MODE_HOTWATER)
{
sprintf(buf, "水温:%.1f℃ 效:%.0f%%", Temp_HotWater, Burn_Efficiency);
}
else
{
sprintf(buf, "TIME:%02d:%02d PRE:%d", sys_hour, sys_min, curr_preset+1);
}
}
else
{
sprintf(buf, "故障码:%d %s", g_fault_code, Get_Fault_Desc(g_fault_code));
}
LCD_Display_String(0, 1, buf);
}
/************************** 定时功能 **************************/
void Timer_Load(void)
{
for(int i=0; i<MAX_TIMER_NUM; i++)
{
*(uint8_t*)&TimerTask[i] = Flash_Read_Byte(FLASH_TIMER_ADDR + i*sizeof(TimerTask_t));
}
}
void Timer_Save(void)
{
Flash_Erase(FLASH_TIMER_ADDR);
for(int i=0; i<MAX_TIMER_NUM; i++)
{
Flash_Write_Byte(FLASH_TIMER_ADDR + i*sizeof(TimerTask_t), *(uint8_t*)&TimerTask[i]);
}
}
void Check_TimerTask(void)
{
for(int i=0; i<MAX_TIMER_NUM; i++)
{
if(TimerTask[i].enable == 0) continue;
if(sys_hour == TimerTask[i].hour_on && sys_min == TimerTask[i].min_on)
{
Work_Mode = TimerTask[i].mode;
if(Work_Mode == MODE_HEATING)
SetTemp_Heating = TimerTask[i].temp;
else
SetTemp_HotWater = TimerTask[i].temp;
}
if(sys_hour == TimerTask[i].hour_off && sys_min == TimerTask[i].min_off)
{
Work_Mode = MODE_STANDBY;
}
}
}
/************************** 温度预设 **************************/
void Preset_Load(void)
{
for(int i=0; i<MAX_PRESET_NUM; i++)
{
*(uint8_t*)&PresetTemp[i] = Flash_Read_Byte(FLASH_PRESET_ADDR + i*sizeof(Preset_t));
}
}
void Preset_Save(void)
{
Flash_Erase(FLASH_PRESET_ADDR);
for(int i=0; i<MAX_PRESET_NUM; i++)
{
Flash_Write_Byte(FLASH_PRESET_ADDR + i*sizeof(Preset_t), *(uint8_t*)&PresetTemp[i]);
}
}
void Preset_Next(void)
{
curr_preset++;
if(curr_preset >= MAX_PRESET_NUM) curr_preset = 0;
Work_Mode = PresetTemp[curr_preset].mode;
if(Work_Mode == MODE_HEATING)
SetTemp_Heating = PresetTemp[curr_preset].temp;
else
SetTemp_HotWater = PresetTemp[curr_preset].temp;
}
/************************** 蓝牙通信 **************************/
void BT_ParseCmd(void)
{
if(!uart_rx_done) return;
if(strstr((char*)uart_rx_buf, "ON"))
{
Work_Mode = MODE_HEATING;
}
else if(strstr((char*)uart_rx_buf, "OFF"))
{
Work_Mode = MODE_STANDBY;
}
else if(strstr((char*)uart_rx_buf, "TSET"))
{
int t = atoi((char*)&uart_rx_buf[4]);
SetTemp_Heating = t;
}
else if(strstr((char*)uart_rx_buf, "TIME"))
{
sys_hour = (uart_rx_buf[4]-'0')*10 + (uart_rx_buf[5]-'0');
sys_min = (uart_rx_buf[6]-'0')*10 + (uart_rx_buf[7]-'0');
}
else if(strstr((char*)uart_rx_buf, "PRE"))
{
Preset_Next();
}
uart_rx_len = 0;
uart_rx_done = 0;
memset(uart_rx_buf, 0, RX_BUF_LEN);
}
void BT_SendStatus(void)
{
UART_Printf(
"MODE:%d TH:%.0f TO:%.1f EFF:%.0f FAULT:%d TIME:%02d:%02d\\r\\n",
Work_Mode, SetTemp_Heating, Temp_Out, Burn_Efficiency,
g_fault_code, sys_hour, sys_min
);
}
/************************** 主函数 **************************/
int main(void)
{
System_Init();
g_fault_code = Flash_Read_Byte(FLASH_FAULT_ADDR);
Timer_Load();
Preset_Load();
UART_Printf("壁挂炉控制系统启动…\\r\\n");
LCD_Display_String(0, 0, "壁挂炉控制系统");
LCD_Display_String(0, 1, "初始化完成");
Delay_ms(2000);
while(1)
{
Key_Scan();
Data_Collect();
Check_TimerTask();
BT_ParseCmd();
Temp_Adjust();
Calc_Burn_Efficiency();
LCD_Update();
BT_SendStatus();
Delay_ms(SYS_TICK_MS);
}
}
三、使用说明
1. 编译环境
- Keil MDK-ARM V5 + HC32L130 器件库
- 添加启动文件 startup_hc32l130.s
- 编译选项:优化等级 O0,编译器版本 ARMCC 5.06
2. 硬件接线
| 采暖出水温度 | PA0 | NTC 10k |
| 采暖回水温度 | PA1 | NTC 10k |
| 生活热水温度 | PA2 | NTC 10k |
| 燃气压力 | PA3 | 0-5kPa 压力传感器 |
| 火焰检测 | PA4 | 火焰离子针 |
| 水流检测 | PA5 | 霍尔水流传感器 |
| 风压检测 | PA6 | 风压开关 |
| LCD_RS | PA7 | LCD1602 RS |
| 燃气比例阀 | PB1 | PWM 输出 |
| 变频风机 | PB2 | PWM 输出 |
| UART_TX | PB3 | 蓝牙模块 TX |
| UART_RX | PB4 | 蓝牙模块 RX |
| LCD_EN | PB5 | LCD1602 EN |
| LCD_D4-D7 | PB6-PB9 | LCD1602 D4-D7 |
| KEY_UP | PC0 | 轻触按键 |
| KEY_DOWN | PC1 | 轻触按键 |
| KEY_MODE | PC2 | 轻触按键 |
| KEY_RESET | PC3 | 轻触按键 |
| KEY_PRESET | PC4 | 轻触按键 |
3. 功能操作
| KEY_UP | 温度 + 1℃ |
| KEY_DOWN | 温度 – 1℃ |
| KEY_MODE | 采暖 / 热水模式切换 |
| KEY_RESET | 故障复位 |
| KEY_PRESET | 温度预设切换(3 组) |
表格
| ON | 启动采暖模式 |
| OFF | 待机 |
| TSET55 | 设置采暖温度 55℃ |
| TIME1230 | 校时为 12:30 |
| PRE | 切换温度预设 |

