找回密码
 立即注册
楼主: 王昱顺

【AI助手】@AiCube-ISP-V6.96Z中,【自动生成程序】+【查错高手】

 火.. [复制链接]
  • 打卡等级:常住居民III
  • 打卡总天数:105
  • 最近打卡:2026-07-21 13:30:21

38

主题

496

回帖

3341

积分

版主

积分
3341
QQ
发表于 前天 11:36 | 显示全部楼层

好的,我已经获取了足够的信息。现在我来编写完整的电梯召唤盒演示程序。


下面是为 STC8H1K28 设计的电梯召唤盒(呼梯盒)完整演示程序。本程序使用 Keil C51 编译,采用 TSSOP20 封装引脚定义(也可适配其他封装,只需调整引脚映射即可)。

硬件功能说明

功能 引脚 说明
上行按钮 P3.2 (INT0) 按下请求上行,下降沿触发中断
下行按钮 P3.3 (INT1) 按下请求下行,下降沿触发中断
上行指示灯 P1.0 推挽输出,高电平点亮LED
下行指示灯 P1.1 推挽输出,高电平点亮LED
串口1 TXD P3.1 与电梯主控通信(RS485需外接转换芯片)
串口1 RXD P3.0 与电梯主控通信
蜂鸣器 P1.2 按键反馈提示音(PWM或IO翻转)

通信协议(MODBUS-RTU 简化版)

  • 波特率:9600, 8N1
  • 上行请求:发送 0x01 0x05 0x00 0x01 0xFF 0x00 CRC16(写单线圈)
  • 下行请求:发送 0x01 0x05 0x00 0x02 0xFF 0x00 CRC16
  • 取消请求:发送 0x01 0x05 0x00 0x01/0x02 0x00 0x00 CRC16

完整程序代码

文件1:main.c

/******************************************************************************
 * 文件名:main.c
 * 芯片型号:STC8H1K28 (TSSOP20/LQFP32/QFN32)
 * 功能描述:电梯召唤盒(呼梯盒)完整演示程序
 * 编译环境:Keil C51
 * 
 * 【硬件连接】
 *   P3.2 (INT0) —— 上行按钮(按下到GND,外部4.7K上拉到VCC)
 *   P3.3 (INT1) —— 下行按钮(按下到GND,外部4.7K上拉到VCC)
 *   P1.0        —— 上行指示灯LED(阳极串470Ω到VCC,阴极接P1.0)
 *   P1.1        —— 下行指示灯LED(阳极串470Ω到VCC,阴极接P1.1)
 *   P1.2        —— 蜂鸣器(有源蜂鸣器,NPN三极管驱动)
 *   P3.0 (RxD)  —— 串口1接收(接RS485芯片RO)
 *   P3.1 (TxD)  —— 串口1发送(接RS485芯片DI)
 *   P3.6        —— RS485收发控制(DE/RE,高电平发送,低电平接收)
 *   Vcc         —— 5V/3.3V
 *   Gnd         —— 地
 *
 * 【功能说明】
 *   1. 按下上行按钮,上行指示灯点亮,通过串口发送上行请求
 *   2. 按下下行按钮,下行指示灯点亮,通过串口发送下行请求
 *   3. 收到电梯主控的应答后,指示灯熄灭
 *   4. 按键按下时蜂鸣器短鸣一声作为反馈
 *   5. 使用定时器0实现10ms系统心跳,用于按键消抖和超时管理
 *   6. 使用定时器2作为串口1波特率发生器(9600bps)
 *   7. 通信协议采用MODBUS-RTU简化版
 *
 * 【ISP下载设置】
 *   内部IRC频率:22.1184MHz
 *   不需要外部晶振,不需要外部复位
 *   P5.4保持为I/O口
 *
 * 【注意事项】
 *   1. 除P3.0/P3.1外,所有I/O口上电后为高阻输入,使用前必须配置模式
 *   2. 本程序使用定时器2做波特率发生器,定时器0做系统心跳
 *   3. RS485控制脚P3.6在发送时置高,发送完成后置低
 ******************************************************************************/

#include "stc8h.h"
#include "intrins.h"
#include <string.h>

/* ========================== 宏定义 ========================== */

#define FOSC            22118400UL      /* 系统主时钟频率 22.1184MHz */
#define BRT             (65536 - (FOSC / 9600 + 2) / 4)  /* 定时器2重载值,9600bps */

#define LED_UP          P10             /* 上行指示灯 */
#define LED_DN          P11             /* 下行指示灯 */
#define KEY_UP          P32             /* 上行按键输入 */
#define KEY_DN          P33             /* 下行按键输入 */
#define BUZZER          P12             /* 蜂鸣器 */
#define RS485_CTRL      P36             /* RS485方向控制:1=发送,0=接收 */

/* 按键状态定义 */
#define KEY_NONE        0               /* 无按键 */
#define KEY_UP_PRESS    1               /* 上行按键按下 */
#define KEY_DN_PRESS    2               /* 下行按键按下 */

/* 系统状态定义 */
#define STATE_IDLE      0               /* 空闲状态 */
#define STATE_UP_REQ    1               /* 上行请求已发送,等待应答 */
#define STATE_DN_REQ    2               /* 下行请求已发送,等待应答 */

/* MODBUS地址定义 */
#define MODBUS_ADDR     0x01            /* 本机MODBUS地址 */
#define CMD_WRITE_COIL  0x05            /* MODBUS写单线圈命令 */
#define COIL_UP         0x0001          /* 上行线圈地址 */
#define COIL_DN         0x0002          /* 下行线圈地址 */

/* 超时时间定义 */
#define TIMEOUT_MS      500             /* 等待应答超时时间(ms) */
#define BUZZER_MS       80              /* 蜂鸣器鸣叫时长(ms) */
#define KEY_DEBOUNCE_MS 20              /* 按键消抖时间(ms) */

/* ========================== 全局变量 ========================== */

/* 系统心跳计数 */
volatile unsigned int sys_tick_ms;      /* 系统毫秒计数器,定时器0中断累加 */

/* 按键相关 */
volatile unsigned char key_state;       /* 当前按键状态 */
volatile unsigned int key_press_tick;   /* 按键按下时的系统tick记录 */
volatile unsigned char key_debounce_flag; /* 消抖标志 */

/* 通信相关 */
volatile unsigned char sys_state;       /* 系统状态 */
volatile unsigned int comm_timeout;     /* 通信超时计数器 */
volatile unsigned char comm_busy;       /* 通信忙标志 */

/* 串口接收缓冲(环形缓冲) */
#define RX_BUF_SIZE     16
volatile unsigned char rx_buffer[RX_BUF_SIZE];
volatile unsigned char rx_wptr;         /* 写指针 */
volatile unsigned char rx_rptr;         /* 读指针 */

/* 串口发送缓冲 */
volatile unsigned char tx_busy;         /* 发送忙标志 */

/* 蜂鸣器控制 */
volatile unsigned int buzzer_timer;     /* 蜂鸣器计时 */

/* MODBUS CRC16 计算表 */
static const unsigned char code crc_hi_table[] = {
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41
};

static const unsigned char code crc_lo_table[] = {
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2,
    0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04,
    0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
    0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8,
    0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
    0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
    0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6,
    0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10,
    0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
    0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
    0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE,
    0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
    0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA,
    0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C,
    0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0
};

/* ========================== 函数声明 ========================== */

/* 系统初始化 */
void System_Init(void);
void GPIO_Init(void);
void Timer0_Init(void);
void Timer2_Init(void);
void UART1_Init(void);
void Interrupt_Init(void);

/* 按键处理 */
unsigned char Key_Scan(void);
void Key_Process(unsigned char key);

/* 通信处理 */
unsigned int CRC16_Modbus(unsigned char *buf, unsigned char len);
void UART1_SendByte(unsigned char dat);
void UART1_SendString(unsigned char *p, unsigned char len);
void Send_Modbus_Request(unsigned char coil_addr, unsigned char on_off);
void Process_Received_Data(void);

/* 外设控制 */
void LED_Control(unsigned char up_state, unsigned char dn_state);
void Buzzer_On(void);
void Buzzer_Off(void);
void Buzzer_Process(void);

/* 延时 */
void Delay_Ms(unsigned int ms);

/* ========================== 中断服务函数 ========================== */

/* 定时器0中断:10ms系统心跳 */
void Timer0_ISR(void) interrupt 1
{
    /* TF0由硬件自动清零 */
    sys_tick_ms += 10;                  /* 每10ms累加一次 */
  
    /* 按键消抖计时 */
    if (key_debounce_flag)
    {
        key_debounce_flag = 0;          /* 消抖标志由主循环重新设置 */
    }
  
    /* 通信超时计时 */
    if (comm_timeout > 0)
    {
        comm_timeout--;
    }
  
    /* 蜂鸣器计时 */
    if (buzzer_timer > 0)
    {
        buzzer_timer--;
        if (buzzer_timer == 0)
        {
            Buzzer_Off();               /* 蜂鸣器计时到,关闭 */
        }
    }
}

/* 串口1中断服务 */
void UART1_ISR(void) interrupt 4
{
    unsigned char dat;
  
    if (TI)                             /* 发送中断 */
    {
        TI = 0;                         /* 软件清零发送中断标志 */
        tx_busy = 0;                    /* 发送完成 */
    }
  
    if (RI)                             /* 接收中断 */
    {
        RI = 0;                         /* 软件清零接收中断标志 */
        dat = SBUF;                     /* 读取接收数据 */
      
        /* 将数据存入环形缓冲区 */
        rx_buffer[rx_wptr] = dat;
        rx_wptr++;
        rx_wptr &= (RX_BUF_SIZE - 1);   /* 环形缓冲取模 */
      
        /* 如果写指针追上读指针,丢弃最旧的数据(覆盖) */
        if (rx_wptr == rx_rptr)
        {
            rx_rptr++;
            rx_rptr &= (RX_BUF_SIZE - 1);
        }
    }
}

/* ========================== 函数实现 ========================== */

/******************************************************************************
 * 函数名:System_Init
 * 功能:系统初始化总入口
 * 参数:无
 * 返回:无
 ******************************************************************************/
void System_Init(void)
{
    /* 使能访问XFR(扩展特殊功能寄存器),后续配置时钟、I/O等需要 */
    P_SW2 |= 0x80;
  
    GPIO_Init();                        /* I/O口初始化 */
    Timer0_Init();                      /* 定时器0初始化(10ms心跳) */
    Timer2_Init();                      /* 定时器2初始化(串口波特率) */
    UART1_Init();                       /* 串口1初始化 */
    Interrupt_Init();                   /* 中断初始化 */
  
    /* 初始化全局变量 */
    sys_tick_ms = 0;
    key_state = KEY_NONE;
    sys_state = STATE_IDLE;
    comm_timeout = 0;
    comm_busy = 0;
    tx_busy = 0;
    rx_wptr = 0;
    rx_rptr = 0;
    buzzer_timer = 0;
    key_debounce_flag = 0;
    key_press_tick = 0;
  
    /* 设置省电模式唤醒等待时钟数(如需省电模式) */
    IRCDB = 0x10;                       /* 唤醒后等待16个时钟再运行 */
}

/******************************************************************************
 * 函数名:GPIO_Init
 * 功能:I/O口初始化配置
 * 参数:无
 * 返回:无
 * 说明:除P3.0/P3.1上电为准双向口外,其余I/O上电为高阻输入
 ******************************************************************************/
void GPIO_Init(void)
{
    /* P1.0、P1.1、P1.2 —— 推挽输出(LED和蜂鸣器) */
    /* P1M1.0=0, P1M0.0=1 => 推挽输出 */
    /* P1M1.1=0, P1M0.1=1 => 推挽输出 */
    /* P1M1.2=0, P1M0.2=1 => 推挽输出 */
    P1M0 = 0x07;                        /* P1.0/P1.1/P1.2 推挽输出 */
    P1M1 = 0x00;
  
    /* P3.2、P3.3 —— 高阻输入(按键输入,外部已有上拉电阻) */
    /* P3M1.2=1, P3M0.2=0 => 高阻输入 */
    /* P3M1.3=1, P3M0.3=0 => 高阻输入 */
    P3M0 = 0x00;
    P3M1 = 0x0C;                        /* P3.2/P3.3 高阻输入 */
  
    /* P3.6 —— 推挽输出(RS485方向控制) */
    /* P3M1.6=0, P3M0.6=1 => 推挽输出 */
    P3M0 |= 0x40;
    P3M1 &= ~0x40;
  
    /* P3.0/P3.1 保持默认准双向口模式(串口通信) */
  
    /* 初始状态:所有输出置低 */
    LED_UP = 0;
    LED_DN = 0;
    BUZZER = 0;
    RS485_CTRL = 0;                     /* RS485初始为接收状态 */
}

/******************************************************************************
 * 函数名:Timer0_Init
 * 功能:定时器0初始化,产生10ms系统心跳
 * 参数:无
 * 返回:无
 * 说明:使用定时器0模式1(16位自动重载),1T模式
 *       重载值 = 65536 - FOSC / 100 / 1 = 65536 - 221184 = 负数(超出范围)
 *       所以需要分频:使用12T模式
 *       重载值 = 65536 - FOSC / 12 / 100 = 65536 - 18432 = 47104 = 0xB800
 ******************************************************************************/
void Timer0_Init(void)
{
    /* 定时器0配置为16位自动重载模式(模式1) */
    /* TMOD: GATE0=0, C/T0=0, M01=0, M00=1 */
    TMOD &= 0xF0;                       /* 清除T0相关位 */
    TMOD |= 0x01;                       /* T0模式1:16位自动重载 */
  
    /* 设置定时器0为12T模式(默认) */
    /* AUXR: T0x12=0 为12T模式 */
    AUXR &= ~0x80;                      /* T0x12 = 0 */
  
    /* 计算重载值:10ms = FOSC / 12 / 100 */
    /* 重载值 = 65536 - 22118400 / 12 / 100 = 65536 - 18432 = 47104 = 0xB800 */
    TH0 = 0xB8;                         /* 高8位 */
    TL0 = 0x00;                         /* 低8位 */
  
    TR0 = 1;                            /* 启动定时器0 */
}

/******************************************************************************
 * 函数名:Timer2_Init
 * 功能:定时器2初始化,作为串口1波特率发生器
 * 参数:无
 * 返回:无
 * 说明:使用定时器2的1T模式,16位自动重载
 *       波特率 = FOSC / 4 / (65536 - 重载值)
 *       重载值 = 65536 - FOSC / 4 / 9600 = 65536 - 576 = 64960 = 0xFDC0
 ******************************************************************************/
void Timer2_Init(void)
{
    /* 定时器2配置为波特率发生器 */
    /* AUXR: T2x12=1 (1T模式), T2R=1 (启动) */
    AUXR &= ~0x10;                      /* T2_C/T = 0,定时器模式 */
    AUXR |= 0x14;                       /* T2x12=1, T2R=1 */
  
    /* 设置重载值 */
    T2H = (unsigned char)(BRT >> 8);    /* 高8位 */
    T2L = (unsigned char)BRT;           /* 低8位 */
}

/******************************************************************************
 * 函数名:UART1_Init
 * 功能:串口1初始化,模式1(8位UART),波特率由定时器2提供
 * 参数:无
 * 返回:无
 ******************************************************************************/
void UART1_Init(void)
{
    /* 串口1模式1:8位UART,可变波特率 */
    /* SCON: SM0=0, SM1=1, SM2=0, REN=1, TB8=0, RB8=0, TI=0, RI=0 */
    SCON = 0x50;                        /* 模式1,允许接收 */
  
    /* 选择定时器2作为串口1波特率发生器 */
    /* AUXR: S1ST2=1 选择定时器2 */
    AUXR |= 0x01;                       /* S1ST2 = 1 */
  
    /* 清除发送和接收标志 */
    TI = 0;
    RI = 0;
}

/******************************************************************************
 * 函数名:Interrupt_Init
 * 功能:中断系统初始化
 * 参数:无
 * 返回:无
 ******************************************************************************/
void Interrupt_Init(void)
{
    ET0 = 1;                            /* 使能定时器0中断 */
    ES = 1;                             /* 使能串口1中断 */
    EA = 1;                             /* 打开总中断 */
}

/******************************************************************************
 * 函数名:CRC16_Modbus
 * 功能:计算MODBUS CRC16校验值
 * 参数:buf - 数据缓冲区指针
 *       len - 数据长度
 * 返回:16位CRC校验值
 ******************************************************************************/
unsigned int CRC16_Modbus(unsigned char *buf, unsigned char len)
{
    unsigned char crc_hi = 0xFF;        /* CRC高字节 */
    unsigned char crc_lo = 0xFF;        /* CRC低字节 */
    unsigned char index;
    unsigned char i;
  
    for (i = 0; i < len; i++)
    {
        index = crc_lo ^ buf[i];
        crc_lo = crc_hi ^ crc_hi_table[index];
        crc_hi = crc_lo_table[index];
    }
  
    return ((unsigned int)crc_hi << 8) | crc_lo;
}

/******************************************************************************
 * 函数名:UART1_SendByte
 * 功能:串口1发送一个字节(阻塞方式)
 * 参数:dat - 要发送的数据
 * 返回:无
 ******************************************************************************/
void UART1_SendByte(unsigned char dat)
{
    while (tx_busy);                    /* 等待上次发送完成 */
    tx_busy = 1;                        /* 设置发送忙标志 */
    SBUF = dat;                         /* 写入发送缓冲,启动发送 */
}

/******************************************************************************
 * 函数名:UART1_SendString
 * 功能:串口1发送指定长度的数据
 * 参数:p   - 数据缓冲区指针
 *       len - 发送长度
 * 返回:无
 ******************************************************************************/
void UART1_SendString(unsigned char *p, unsigned char len)
{
    unsigned char i;
  
    for (i = 0; i < len; i++)
    {
        UART1_SendByte(p[i]);
    }
}

/******************************************************************************
 * 函数名:Send_Modbus_Request
 * 功能:发送MODBUS写线圈命令
 * 参数:coil_addr - 线圈地址(0x0001=上行,0x0002=下行)
 *       on_off    - 0xFF=置位(请求),0x00=复位(取消)
 * 返回:无
 ******************************************************************************/
void Send_Modbus_Request(unsigned char coil_addr, unsigned char on_off)
{
    unsigned char buf[8];
    unsigned int crc;
  
    /* 构建MODBUS帧 */
    buf[0] = MODBUS_ADDR;               /* 从机地址 */
    buf[1] = CMD_WRITE_COIL;            /* 功能码:写单线圈 */
    buf[2] = 0x00;                      /* 线圈地址高字节 */
    buf[3] = coil_addr;                 /* 线圈地址低字节 */
    buf[4] = on_off;                    /* 数据:0xFF=ON, 0x00=OFF */
    buf[5] = 0x00;                      /* 固定为0x00 */
  
    /* 计算CRC16 */
    crc = CRC16_Modbus(buf, 6);
    buf[6] = (unsigned char)(crc & 0xFF);       /* CRC低字节 */
    buf[7] = (unsigned char)((crc >> 8) & 0xFF); /* CRC高字节 */
  
    /* RS485切换到发送模式 */
    RS485_CTRL = 1;
    _nop_();                            /* 等待一小段时间让RS485芯片切换 */
    _nop_();
  
    /* 发送数据 */
    UART1_SendString(buf, 8);
  
    /* 等待发送完成 */
    while (tx_busy);
  
    /* 延时确保最后一个字节发送完毕 */
    Delay_Ms(1);
  
    /* RS485切换回接收模式 */
    RS485_CTRL = 0;
}

/******************************************************************************
 * 函数名:Process_Received_Data
 * 功能:处理接收到的MODBUS应答数据
 * 参数:无
 * 返回:无
 * 说明:在主循环中调用,处理串口接收缓冲中的数据
 ******************************************************************************/
void Process_Received_Data(void)
{
    unsigned char len;
    unsigned char buf[8];
    unsigned int crc_calc, crc_recv;
    unsigned char i;
  
    /* 检查是否有数据可读 */
    if (rx_rptr == rx_wptr)
    {
        return;                         /* 缓冲区为空 */
    }
  
    /* 计算缓冲区中有效数据长度 */
    if (rx_wptr >= rx_rptr)
    {
        len = rx_wptr - rx_rptr;
    }
    else
    {
        len = (RX_BUF_SIZE - rx_rptr) + rx_wptr;
    }
  
    /* 需要至少8个字节才能构成一个完整的MODBUS应答帧 */
    if (len < 8)
    {
        return;
    }
  
    /* 从环形缓冲区读取一帧数据(最多8字节) */
    for (i = 0; i < 8; i++)
    {
        buf[i] = rx_buffer[rx_rptr];
        rx_rptr++;
        rx_rptr &= (RX_BUF_SIZE - 1);
    }
  
    /* 验证从机地址 */
    if (buf[0] != MODBUS_ADDR)
    {
        return;                         /* 地址不匹配,丢弃 */
    }
  
    /* 验证功能码(应答中功能码最高位为1表示异常) */
    if ((buf[1] & 0x7F) != CMD_WRITE_COIL)
    {
        return;                         /* 功能码不匹配 */
    }
  
    /* 验证CRC16 */
    crc_calc = CRC16_Modbus(buf, 6);
    crc_recv = ((unsigned int)buf[7] << 8) | buf[6];
    if (crc_calc != crc_recv)
    {
        return;                         /* CRC校验失败,丢弃 */
    }
  
    /* 检查是否为异常应答(功能码最高位为1) */
    if (buf[1] & 0x80)
    {
        /* 异常应答,异常码在buf[2]中 */
        /* 这里简单处理:超时后重试,由主循环处理 */
        return;
    }
  
    /* 正常应答,处理结果 */
    /* 应答格式:地址 + 0x05 + 线圈地址(2字节) + 数据(2字节) + CRC(2字节) */
    if (buf[3] == COIL_UP)              /* 上行线圈应答 */
    {
        if (buf[4] == 0xFF)             /* 置位成功 */
        {
            /* 上行请求已被电梯主控接收,保持指示灯 */
            /* 等待电梯到达后主控会发取消命令,或超时后自动取消 */
        }
        else if (buf[4] == 0x00)        /* 复位成功 */
        {
            LED_UP = 0;                 /* 熄灭上行指示灯 */
            sys_state = STATE_IDLE;     /* 回到空闲状态 */
        }
    }
    else if (buf[3] == COIL_DN)         /* 下行线圈应答 */
    {
        if (buf[4] == 0xFF)             /* 置位成功 */
        {
            /* 下行请求已被接收 */
        }
        else if (buf[4] == 0x00)        /* 复位成功 */
        {
            LED_DN = 0;                 /* 熄灭下行指示灯 */
            sys_state = STATE_IDLE;     /* 回到空闲状态 */
        }
    }
  
    comm_timeout = 0;                   /* 清除超时计数 */
}

/******************************************************************************
 * 函数名:Key_Scan
 * 功能:按键扫描,检测按键按下
 * 参数:无
 * 返回:按键值(KEY_NONE/KEY_UP_PRESS/KEY_DN_PRESS)
 * 说明:采用软件消抖,检测到低电平后延时消抖再确认
 ******************************************************************************/
unsigned char Key_Scan(void)
{
    static unsigned char last_key = KEY_NONE;
    static unsigned int last_tick = 0;
    unsigned char current_key = KEY_NONE;
    unsigned char ret = KEY_NONE;
  
    /* 读取按键电平(低电平有效) */
    if (KEY_UP == 0)
    {
        current_key = KEY_UP_PRESS;
    }
    else if (KEY_DN == 0)
    {
        current_key = KEY_DN_PRESS;
    }
  
    /* 按键状态变化检测 */
    if (current_key != last_key)
    {
        last_key = current_key;
        if (current_key != KEY_NONE)
        {
            last_tick = sys_tick_ms;    /* 记录按下时刻 */
        }
    }
    else
    {
        /* 状态稳定,检查消抖时间 */
        if (current_key != KEY_NONE)
        {
            if ((sys_tick_ms - last_tick) >= KEY_DEBOUNCE_MS)
            {
                ret = current_key;      /* 消抖通过,确认按键 */
            }
        }
    }
  
    return ret;
}

/******************************************************************************
 * 函数名:Key_Process
 * 功能:按键处理函数
 * 参数:key - 按键值
 * 返回:无
 ******************************************************************************/
void Key_Process(unsigned char key)
{
    if (key == KEY_NONE)
    {
        return;
    }
  
    /* 系统忙,不处理新按键 */
    if (comm_busy)
    {
        return;
    }
  
    /* 蜂鸣器反馈 */
    Buzzer_On();
    buzzer_timer = BUZZER_MS / 10;      /* 设置蜂鸣器计时(10ms单位) */
  
    if (key == KEY_UP_PRESS)
    {
        /* 上行按键 */
        if (sys_state == STATE_IDLE)
        {
            /* 空闲状态,发送上行请求 */
            LED_UP = 1;                 /* 点亮上行指示灯 */
            sys_state = STATE_UP_REQ;   /* 设置状态 */
            comm_timeout = TIMEOUT_MS / 10; /* 设置超时(10ms单位) */
            comm_busy = 1;
          
            /* 发送MODBUS写线圈命令:置位上行线圈 */
            Send_Modbus_Request(COIL_UP, 0xFF);
        }
        else if (sys_state == STATE_UP_REQ)
        {
            /* 已在上行请求状态,再次按下取消请求 */
            LED_UP = 0;                 /* 熄灭上行指示灯 */
            sys_state = STATE_IDLE;
            comm_timeout = 0;
          
            /* 发送MODBUS写线圈命令:复位上行线圈 */
            Send_Modbus_Request(COIL_UP, 0x00);
        }
        /* 如果在下行请求状态,忽略上行按键(或可根据需求处理) */
    }
    else if (key == KEY_DN_PRESS)
    {
        /* 下行按键 */
        if (sys_state == STATE_IDLE)
        {
            /* 空闲状态,发送下行请求 */
            LED_DN = 1;                 /* 点亮下行指示灯 */
            sys_state = STATE_DN_REQ;   /* 设置状态 */
            comm_timeout = TIMEOUT_MS / 10; /* 设置超时(10ms单位) */
            comm_busy = 1;
          
            /* 发送MODBUS写线圈命令:置位下行线圈 */
            Send_Modbus_Request(COIL_DN, 0xFF);
        }
        else if (sys_state == STATE_DN_REQ)
        {
            /* 已在下行请求状态,再次按下取消请求 */
            LED_DN = 0;                 /* 熄灭下行指示灯 */
            sys_state = STATE_IDLE;
            comm_timeout = 0;
          
            /* 发送MODBUS写线圈命令:复位下行线圈 */
            Send_Modbus_Request(COIL_DN, 0x00);
        }
    }
}

/******************************************************************************
 * 函数名:LED_Control
 * 功能:LED控制
 * 参数:up_state - 上行LED状态(0=灭,1=亮)
 *       dn_state - 下行LED状态(0=灭,1=亮)
 * 返回:无
 ******************************************************************************/
void LED_Control(unsigned char up_state, unsigned char dn_state)
{
    LED_UP = up_state ? 1 : 0;
    LED_DN = dn_state ? 1 : 0;
}

/******************************************************************************
 * 函数名:Buzzer_On
 * 功能:打开蜂鸣器
 * 参数:无
 * 返回:无
 ******************************************************************************/
void Buzzer_On(void)
{
    BUZZER = 1;
}

/******************************************************************************
 * 函数名:Buzzer_Off
 * 功能:关闭蜂鸣器
 * 参数:无
 * 返回:无
 ******************************************************************************/
void Buzzer_Off(void)
{
    BUZZER = 0;
}

/******************************************************************************
 * 函数名:Buzzer_Process
 * 功能:蜂鸣器处理(在主循环中调用)
 * 参数:无
 * 返回:无
 ******************************************************************************/
void Buzzer_Process(void)
{
    /* 蜂鸣器计时在定时器中断中处理 */
    /* 此函数保留供扩展使用 */
}

/******************************************************************************
 * 函数名:Delay_Ms
 * 功能:毫秒级延时(阻塞方式)
 * 参数:ms - 延时毫秒数
 * 返回:无
 * 说明:使用定时器0的sys_tick_ms实现精确延时
 ******************************************************************************/
void Delay_Ms(unsigned int ms)
{
    unsigned int start;
  
    start = sys_tick_ms;
    while ((sys_tick_ms - start) < ms);
}

/******************************************************************************
 * 函数名:main
 * 功能:主函数
 * 参数:无
 * 返回:无
 ******************************************************************************/
void main(void)
{
    unsigned char key;
    static unsigned int last_heartbeat = 0;
  
    /* 系统初始化 */
    System_Init();
  
    /* 主循环 */
    while (1)
    {
        /* ===== 1. 按键扫描与处理 ===== */
        key = Key_Scan();
        if (key != KEY_NONE)
        {
            Key_Process(key);
        }
      
        /* ===== 2. 通信数据处理 ===== */
        Process_Received_Data();
      
        /* ===== 3. 通信超时处理 ===== */
        if (comm_busy && (comm_timeout == 0))
        {
            /* 通信超时,取消当前请求 */
            if (sys_state == STATE_UP_REQ)
            {
                LED_UP = 0;             /* 熄灭上行指示灯 */
            }
            else if (sys_state == STATE_DN_REQ)
            {
                LED_DN = 0;             /* 熄灭下行指示灯 */
            }
          
            sys_state = STATE_IDLE;     /* 回到空闲状态 */
            comm_busy = 0;
        }
      
        /* ===== 4. 通信完成后清除忙标志 ===== */
        if (comm_busy && (sys_state == STATE_IDLE))
        {
            comm_busy = 0;
        }
      
        /* ===== 5. 蜂鸣器处理 ===== */
        Buzzer_Process();
      
        /* ===== 6. 系统心跳指示(可选,闪烁LED指示系统运行) ===== */
        /* 每1秒翻转一次P1.0(如果未使用上行请求时) */
        if ((sys_tick_ms - last_heartbeat) >= 100)
        {
            last_heartbeat = sys_tick_ms;
            /* 此处可添加系统运行指示,例如翻转一个调试LED */
        }
    }
}

使用说明

1. 编译与下载

  1. 在 Keil C51 中新建项目,芯片选择 STC8H1K28 系列
  2. 将上述代码保存为 main.c,添加到项目中
  3. 在项目选项中设置:
    • Target 页:晶振频率设为 22.1184MHz
    • Output 页:勾选 "Create HEX File"
  4. 编译生成 HEX 文件
  5. 使用 STC-ISP (AIapp-ISP) 下载软件:
    • 选择芯片型号:STC8H1K28
    • 设置内部IRC频率:22.1184MHz
    • 加载 HEX 文件
    • 点击下载/编程

2. 硬件接线

STC8H1K28 (TSSOP20封装)
┌─────────────┐
│ P1.0 ──── 上行LED (阳极→470Ω→VCC)    │
│ P1.1 ──── 下行LED (阳极→470Ω→VCC)    │
│ P1.2 ──── 蜂鸣器 (NPN三极管驱动)      │
│ P3.0 ──── RS485芯片RO (接收)          │
│ P3.1 ──── RS485芯片DI (发送)          │
│ P3.2 ──── 上行按钮 (→GND, 外部4.7K↑) │
│ P3.3 ──── 下行按钮 (→GND, 外部4.7K↑) │
│ P3.6 ──── RS485芯片DE/RE (方向控制)   │
│ Vcc ──── 5V/3.3V                      │
│ Gnd ──── GND                          │
└─────────────┘

3. 工作流程

  1. 上电初始化:系统启动,所有LED熄灭,RS485处于接收状态
  2. 按下上行按钮
    • 蜂鸣器短鸣一声
    • 上行指示灯点亮
    • 通过RS485发送MODBUS写线圈命令(地址0x01,线圈0x0001,数据0xFF00)
    • 等待电梯主控应答(超时500ms)
  3. 按下下行按钮
    • 蜂鸣器短鸣一声
    • 下行指示灯点亮
    • 通过RS485发送MODBUS写线圈命令(地址0x01,线圈0x0002,数据0xFF00)
    • 等待电梯主控应答(超时500ms)
  4. 再次按下同一按钮:取消请求,发送复位命令
  5. 收到应答:根据应答内容更新指示灯状态
  6. 超时处理:500ms内未收到应答,自动取消请求并熄灭指示灯

4. 扩展建议

  • RS485总线:如需多台召唤盒组网,每台设置不同的MODBUS地址
  • 掉电保存:可使用IAP/EEPROM保存按键请求状态
  • 省电模式:长时间无操作可进入掉电模式,由按键中断唤醒
  • 看门狗:可开启看门狗防止程序跑飞

以上即为完整的电梯召唤盒演示程序。如需调整引脚分配、通信协议细节或增加其他功能(如楼层显示、语音播报等),请告知具体需求,我可以进一步修改完善。


🔍 搜索 "STC8H1K28 电梯召唤盒 演示程序 程序注解"

📌 相关论坛讨论

  1. 【AI助手】@AiCube-ISP-V6.96Z中,【自动生成程序】+【查错高手】

⏱️ 查找用时 34.7s | 总回答用时 112.0s

QQ:1463035472,微信:19952583954
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:202
  • 最近打卡:2026-07-21 16:10:42

51

主题

1389

回帖

3694

积分

超级版主

积分
3694
发表于 前天 11:54 | 显示全部楼层
用STC8H1K08自动编写冷柜温控制的程序,生成完整演示程序,并含详细程序注解

截图202607201154545563.jpg
STC官网:https://www.stcai.com/
QQ:2593903262
微信号:18106296598
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:202
  • 最近打卡:2026-07-21 16:10:42

51

主题

1389

回帖

3694

积分

超级版主

积分
3694
发表于 前天 11:55 | 显示全部楼层

好的,我已经查阅了STC8H1K08的相关手册资料。现在我来编写完整的冷柜温控程序。

首先,我需要确认一下STC8H1K08的SOP16封装管脚分配,以便合理分配IO口。根据手册,SOP16封装的管脚如下:

  • P3.0/RxD, P3.1/TxD (串口)
  • P5.4/nRST (可做IO或复位)
  • P1.2~P1.7 (IO口)
  • Vcc, Gnd, ADC_VRef+

现在我来编写完整的冷柜温控程序。

/******************************************************************************
 * 文件名:Freezer_Temp_Control.c
 * 芯片型号:STC8H1K08 (SOP16封装)
 * 功能描述:冷柜温度控制系统完整演示程序
 * 
 * 【硬件连接说明】
 *   P1.2 (ADC通道2) —— NTC热敏电阻分压采样 (温度传感器)
 *   P1.3              —— 继电器驱动 (压缩机控制) 推挽输出
 *   P1.4              —— 蜂鸣器驱动 (报警) 推挽输出
 *   P1.5              —— 化霜加热丝控制 推挽输出
 *   P3.0/RxD          —— 串口接收 (USB转串口)
 *   P3.1/TxD          —— 串口发送 (USB转串口)
 *   P5.4              —— 按键输入 (温度设定切换/手动化霜)
 *   
 * 【功能说明】
 *   1. 温度采集:通过ADC读取NTC分压值,查表换算为实际温度
 *   2. 温度控制:设定目标温度,回差控制压缩机启停
 *   3. 化霜控制:定时化霜或手动化霜
 *   4. 超温报警:温度超出上下限时蜂鸣器报警
 *   5. 串口通信:通过串口输出实时温度、压缩机状态等信息
 *   6. 按键输入:P5.4按键切换显示模式/手动化霜
 *
 * 【参数默认值】
 *   目标温度:-18℃
 *   回差温度:2℃ (即-20℃停机,-16℃启动)
 *   化霜周期:6小时
 *   化霜时长:15分钟
 *   高温报警:-10℃
 *   低温报警:-25℃
 *
 * 【使用说明】
 *   1. 使用STC-ISP软件,选择STC8H1K08系列
 *   2. 内部IRC时钟设置为11.0592MHz
 *   3. 下载时硬件选项:选择内部IRC时钟,不需要外部晶振
 *   4. 打开串口助手,波特率9600,可查看实时数据
 *   5. 短按P5.4按键:切换显示模式
 *   6. 长按P5.4按键(>3秒):手动启动化霜
 *
 * 【编译环境】Keil C51
 ******************************************************************************/

#include "STC8H.h"          // STC8H系列头文件
#include <stdio.h>          // 标准输入输出,用于sprintf

/*============================================================================
 * 宏定义
 *============================================================================*/
#define FOSC            11059200L   // 系统时钟频率 11.0592MHz
#define BAUD            9600        // 串口波特率

/* 温度控制参数 */
#define TARGET_TEMP     (-18)       // 目标温度 -18℃
#define HYSTERESIS      2           // 回差温度 2℃
#define HIGH_ALARM      (-10)       // 高温报警阈值 -10℃
#define LOW_ALARM       (-25)       // 低温报警阈值 -25℃

/* 化霜参数 */
#define DEFROST_CYCLE   6           // 化霜周期 6小时
#define DEFROST_TIME    15          // 化霜时长 15分钟

/* 定时器时间常量 (定时器0 1T模式, 1ms中断) */
/* 定时周期 = (65536 - 重载值) / FOSC
 * 重载值 = 65536 - FOSC / 1000 = 65536 - 11059 = 54477 = 0xD4ED */
#define T0_1MS_RELOAD_L 0xED        // 定时器0重载值低8位 (1ms)
#define T0_1MS_RELOAD_H 0xD4        // 定时器0重载值高8位 (1ms)

/* ADC相关 */
#define ADC_CH_TEMP     2           // 温度传感器接在P1.2,ADC通道2

/* I/O口定义 */
#define COMPRESSOR      P13         // 压缩机控制 P1.3
#define BUZZER          P14         // 蜂鸣器 P1.4
#define DEFROST_HEAT    P15         // 化霜加热 P1.5
#define KEY             P54         // 按键 P5.4

/* 压缩机状态 */
#define COMP_ON         1           // 压缩机开启
#define COMP_OFF        0           // 压缩机关闭

/* 按键状态 */
#define KEY_NONE        0           // 无按键
#define KEY_SHORT       1           // 短按
#define KEY_LONG        2           // 长按

/*============================================================================
 * 全局变量
 *============================================================================*/
/* 系统时间相关 */
volatile unsigned int  g_ms_count;          // 毫秒计数器 (0~65535循环)
volatile unsigned char g_sec_count;         // 秒计数器
volatile unsigned char g_min_count;         // 分钟计数器
volatile unsigned int  g_hour_count;        // 小时计数器

/* 温度相关 */
volatile int           g_current_temp;      // 当前温度 (单位0.1℃, 如-180表示-18.0℃)
volatile unsigned char g_temp_updated;      // 温度更新标志

/* 压缩机控制 */
volatile unsigned char g_compressor_state;  // 压缩机当前状态 (COMP_ON/COMP_OFF)
volatile unsigned int  g_comp_run_time;     // 压缩机本次运行时间(秒)
volatile unsigned int  g_comp_stop_time;    // 压缩机关闭持续时间(秒)

/* 化霜控制 */
volatile unsigned char g_defrost_flag;      // 化霜进行标志
volatile unsigned int  g_defrost_timer;     // 化霜计时(秒)
volatile unsigned int  g_defrost_interval;  // 距上次化霜时间(分钟)

/* 报警 */
volatile unsigned char g_alarm_flag;        // 报警标志

/* 按键 */
volatile unsigned char g_key_value;         // 按键值
volatile unsigned int  g_key_press_time;    // 按键按下持续时间(ms)

/* 串口发送缓冲区 */
unsigned char g_uart_tx_buf[64];            // 串口发送缓冲区
unsigned char g_uart_tx_len;                // 待发送数据长度

/*============================================================================
 * 函数声明
 *============================================================================*/
void System_Init(void);
void GPIO_Init(void);
void Timer0_Init(void);
void UART1_Init(void);
void ADC_Init(void);
void Delay_ms(unsigned int ms);

unsigned int ADC_Read(unsigned char channel);
int  Calc_Temperature(unsigned int adc_value);
void Temp_Control(void);
void Defrost_Control(void);
void Alarm_Check(void);
void Key_Scan(void);
void Key_Process(void);
void UART1_SendString(unsigned char *str);
void Report_Status(void);

/*============================================================================
 * NTC温度-ADC值对照表 (10K NTC, 上拉10K, Vcc=5V)
 * 温度范围 -30℃ ~ +10℃,步进1℃
 * 实际项目中建议使用更精确的查表或Steinhart-Hart公式计算
 *============================================================================*/
/* ADC值 = 1024 * R_NTC / (R_NTC + 10K) */
/* 这里给出简化对照表,实际应用需根据具体NTC型号精确标定 */
code unsigned int code_adc_table[] = {
    920,    /* -30℃ */
    910,    /* -29℃ */
    898,    /* -28℃ */
    885,    /* -27℃ */
    870,    /* -26℃ */
    853,    /* -25℃ */
    835,    /* -24℃ */
    815,    /* -23℃ */
    794,    /* -22℃ */
    771,    /* -21℃ */
    747,    /* -20℃ */
    722,    /* -19℃ */
    696,    /* -18℃ */
    669,    /* -17℃ */
    642,    /* -16℃ */
    614,    /* -15℃ */
    586,    /* -14℃ */
    558,    /* -13℃ */
    530,    /* -12℃ */
    502,    /* -11℃ */
    475,    /* -10℃ */
    448,    /* -9℃  */
    422,    /* -8℃  */
    397,    /* -7℃  */
    373,    /* -6℃  */
    350,    /* -5℃  */
    328,    /* -4℃  */
    307,    /* -3℃  */
    287,    /* -2℃  */
    268,    /* -1℃  */
    250,    /* 0℃   */
    233,    /* 1℃   */
    217,    /* 2℃   */
    202,    /* 3℃   */
    188,    /* 4℃   */
    175,    /* 5℃   */
    163,    /* 6℃   */
    152,    /* 7℃   */
    142,    /* 8℃   */
    132,    /* 9℃   */
    123     /* 10℃  */
};

/* 温度表起始温度 */
#define TEMP_TABLE_START    (-30)
#define TEMP_TABLE_END      10

/*============================================================================
 * 函数实现
 *============================================================================*/

/******************************************************************************
 * 函数名:Delay_ms
 * 功能  :毫秒延时 (软件延时,用于初始化阶段)
 * 输入  :ms - 延时毫秒数
 * 输出  :无
 ******************************************************************************/
void Delay_ms(unsigned int ms)
{
    unsigned int i, j;
    for (i = 0; i < ms; i++)
    {
        /* 11.0592MHz下,约延时1ms */
        for (j = 0; j < 2760; j++);
    }
}

/******************************************************************************
 * 函数名:GPIO_Init
 * 功能  :初始化I/O口
 *         上电后除P3.0/P3.1为准双向口外,其余均为高阻输入
 *         需要根据用途配置各IO口模式
 * 输入  :无
 * 输出  :无
 ******************************************************************************/
void GPIO_Init(void)
{
    /* P1口配置:
     * P1.2 - ADC输入 (温度传感器) -> 高阻输入 (M1=1, M0=0)
     * P1.3 - 压缩机控制 -> 推挽输出 (M1=0, M0=1)
     * P1.4 - 蜂鸣器 -> 推挽输出 (M1=0, M0=1)
     * P1.5 - 化霜加热 -> 推挽输出 (M1=0, M0=1)
     * P1.6, P1.7 - 未使用,保持高阻输入
     */
    P1M1 = 0x04;    /* P1.2 = 1 (高阻输入), 其余位=0 */
    P1M0 = 0x38;    /* P1.3/P1.4/P1.5 = 1 (推挽输出), P1.2=0 */
  
    /* P3口配置:
     * P3.0/RxD - 串口接收,由串口模块自动配置
     * P3.1/TxD - 串口发送,由串口模块自动配置
     * 保持默认准双向口即可
     */
    P3M0 = 0x00;
    P3M1 = 0x00;
  
    /* P5口配置:
     * P5.4 - 按键输入 -> 高阻输入 (M1=1, M0=0)
     * 注意:P5.4上电后内部4K上拉会打开约6.5ms后关闭
     * 作为按键输入,需要外部上拉或使用内部上拉
     * 这里配置为准双向口(内部弱上拉)
     */
    P5M1 = 0x00;    /* P5.4 = 0 */
    P5M0 = 0x00;    /* P5.4 = 0 -> 准双向口 */
  
    /* 初始状态:所有输出关闭 */
    COMPRESSOR = 0;     /* 压缩机关闭 */
    BUZZER = 0;         /* 蜂鸣器关闭 */
    DEFROST_HEAT = 0;   /* 化霜加热关闭 */
}

/******************************************************************************
 * 函数名:Timer0_Init
 * 功能  :初始化定时器0,产生1ms定时中断
 *         使用模式0 (16位自动重装载),1T模式
 *         定时周期 = (65536 - 重载值) / FOSC
 *         重载值 = 65536 - 11059200/1000 = 54477 = 0xD4ED
 * 输入  :无
 * 输出  :无
 ******************************************************************************/
void Timer0_Init(void)
{
    /* 设置定时器0为1T模式 (AUXR.7 = T0x12 = 1) */
    AUXR |= 0x80;   /* T0x12 = 1, 1T模式 */
  
    /* 设置定时器0为16位自动重装载模式 (TMOD[1:0] = 00) */
    /* TMOD低4位控制T0: GATE=0, C/T=0, M1=0, M0=0 */
    TMOD &= 0xF0;   /* 清空T0控制位 */
    TMOD |= 0x00;   /* 模式0: 16位自动重装载 */
  
    /* 装载定时器初值 */
    TL0 = T0_1MS_RELOAD_L;  /* 低8位 */
    TH0 = T0_1MS_RELOAD_H;  /* 高8位 */
  
    /* 清除定时器0中断标志 */
    TF0 = 0;
  
    /* 使能定时器0中断 */
    ET0 = 1;        /* IE.1 = 1, 允许T0中断 */
  
    /* 启动定时器0 */
    TR0 = 1;        /* TCON.4 = 1, 启动T0 */
}

/******************************************************************************
 * 函数名:UART1_Init
 * 功能  :初始化串口1,模式1 (8位UART),波特率9600
 *         使用定时器2作为波特率发生器 (1T模式)
 *         定时器2重载值 = 65536 - FOSC / 4 / BAUD
 *                       = 65536 - 11059200 / 4 / 9600
 *                       = 65536 - 288 = 65248 = 0xFEE0
 * 输入  :无
 * 输出  :无
 ******************************************************************************/
void UART1_Init(void)
{
    /* 设置串口1为模式1 (8位UART, 可变波特率) */
    SCON = 0x50;    /* SM0=0, SM1=1, SM2=0, REN=1, TB8=0 */
  
    /* 选择定时器2作为串口1的波特率发生器 */
    AUXR &= ~0x01;  /* S1ST2 = 0, 选择定时器2作为波特率发生器 */
    /* 注意:手册中S1ST2=0选择定时器2,S1ST2=1选择定时器1 */
    /* 实际上STC8H系列中,AUXR.0 = S1ST2, 0:定时器2, 1:定时器1 */
  
    /* 配置定时器2为波特率发生器 */
    /* 定时器2固定为16位自动重装载模式 */
    /* 设置定时器2为1T模式 */
    AUXR |= 0x04;   /* T2x12 = 1, 1T模式 */
  
    /* 设置定时器2为定时器模式 (C/T=0) */
    AUXR &= ~0x08;  /* T2_C/T = 0, 定时器模式 */
  
    /* 装载定时器2重载值 (9600波特率 @ 11.0592MHz) */
    T2L = 0xE0;     /* 低8位: 0xFEE0的低8位 */
    T2H = 0xFE;     /* 高8位: 0xFEE0的高8位 */
  
    /* 启动定时器2 */
    AUXR |= 0x10;   /* T2R = 1, 启动定时器2 */
  
    /* 使能串口1中断 */
    ES = 1;         /* IE.4 = 1 */
}

/******************************************************************************
 * 函数名:ADC_Init
 * 功能  :初始化ADC模块
 *         STC8H1K08为10位ADC,右对齐格式
 *         ADC时钟 = SYSclk / 2 / (SPEED+1)
 *         设置SPEED=15,ADC时钟 ≈ 345KHz (在推荐范围内)
 * 输入  :无
 * 输出  :无
 ******************************************************************************/
void ADC_Init(void)
{
    /* 必须先开启扩展寄存器访问使能 */
    P_SW2 |= 0x80;  /* EAXFR = 1, 允许访问XFR */
  
    /* 配置ADC时序 */
    /* ADCTIM寄存器地址 FEA8H (属于XFR) */
    /* CSSETUP=0 (通道选择时间1个ADC时钟) */
    /* CSHOLD[1:0]=01 (通道保持时间2个ADC时钟) */
    /* SMPDUTY[4:0]=01010 (采样时间11个ADC时钟,最小值) */
    ADCTIM = 0x2A;  /* 0b00101010 */
  
    /* 配置ADC结果格式:右对齐 (RESFMT=1) */
    /* ADC时钟速度:SPEED[3:0]=1111, ADC时钟 = FOSC/2/16 ≈ 345KHz */
    ADCCFG = 0x2F;  /* RESFMT=1, SPEED=1111 */
  
    /* 打开ADC电源 */
    ADC_CONTR = 0x80;   /* ADC_POWER=1, 其余位=0 */
  
    /* 等待ADC电源稳定 (约1ms) */
    Delay_ms(2);
  
    /* 使能ADC中断 */
    EADC = 1;       /* IE2.0 = 1, 允许ADC中断 */
}

/******************************************************************************
 * 函数名:ADC_Read
 * 功能  :读取指定通道的ADC值 (阻塞方式)
 * 输入  :channel - ADC通道号 (0~7)
 * 输出  :10位ADC转换结果 (0~1023)
 ******************************************************************************/
unsigned int ADC_Read(unsigned char channel)
{
    unsigned int result;
  
    /* 选择ADC通道 */
    ADC_CONTR = (ADC_CONTR & 0xF0) | channel;  /* 保留高4位,设置通道 */
  
    /* 启动ADC转换 */
    ADC_CONTR |= 0x40;  /* ADC_START = 1 */
  
    /* 等待转换完成 */
    while (!(ADC_CONTR & 0x20));    /* 等待ADC_FLAG置1 */
  
    /* 清除转换完成标志 (软件清零) */
    ADC_CONTR &= ~0x20; /* ADC_FLAG = 0 */
  
    /* 读取转换结果 (10位右对齐) */
    /* RESFMT=1时: ADC_RES[1:0]为高2位, ADC_RESL[7:0]为低8位 */
    result = ((unsigned int)ADC_RES << 8) | ADC_RESL;
  
    return result;
}

/******************************************************************************
 * 函数名:Calc_Temperature
 * 功能  :根据ADC值查表计算温度值
 *         使用线性插值法提高精度
 * 输入  :adc_value - ADC采样值 (0~1023)
 * 输出  :温度值 (单位0.1℃, 如-180表示-18.0℃)
 ******************************************************************************/
int Calc_Temperature(unsigned int adc_value)
{
    unsigned char i;
    int temp;
  
    /* 超出表格范围处理 */
    if (adc_value >= code_adc_table[0])
    {
        /* 温度低于-30℃ */
        return TEMP_TABLE_START * 10;
    }
    if (adc_value <= code_adc_table[TEMP_TABLE_END - TEMP_TABLE_START])
    {
        /* 温度高于10℃ */
        return TEMP_TABLE_END * 10;
    }
  
    /* 查表找到对应的温度区间 */
    for (i = 0; i < (TEMP_TABLE_END - TEMP_TABLE_START); i++)
    {
        if (adc_value >= code_adc_table[i + 1])
        {
            /* 温度在 (TEMP_TABLE_START + i) 和 (TEMP_TABLE_START + i + 1) 之间 */
            /* 线性插值计算 */
            /* temp = T_low + (ADC_high - ADC_val) * (T_high - T_low) / (ADC_high - ADC_low) */
            unsigned int adc_high = code_adc_table[i];
            unsigned int adc_low  = code_adc_table[i + 1];
            int temp_low = (TEMP_TABLE_START + i) * 10;
            int temp_high = (TEMP_TABLE_START + i + 1) * 10;
          
            if (adc_high != adc_low)
            {
                temp = temp_low + (int)((unsigned long)(adc_high - adc_value) * 
                       (temp_high - temp_low) / (adc_high - adc_low));
            }
            else
            {
                temp = temp_low;
            }
            return temp;
        }
    }
  
    /* 默认返回0℃ */
    return 0;
}

/******************************************************************************
 * 函数名:Temp_Control
 * 功能  :温度控制逻辑
 *         根据当前温度与目标温度的差值,控制压缩机启停
 *         采用回差控制方式,防止压缩机频繁启停
 *       
 *         控制逻辑:
 *         当前温度 >= 目标温度 + 回差/2  -> 启动压缩机
 *         当前温度 <= 目标温度 - 回差/2  -> 停止压缩机
 *       
 *         例如:目标-18℃,回差2℃
 *         温度 >= -16℃ 启动压缩机
 *         温度 <= -20℃ 停止压缩机
 * 输入  :无
 * 输出  :无
 ******************************************************************************/
void Temp_Control(void)
{
    int temp_x10 = g_current_temp;  /* 当前温度 (0.1℃单位) */
    int target_x10 = TARGET_TEMP * 10;  /* 目标温度 */
    int hyst_x10 = HYSTERESIS * 10;     /* 回差 */
  
    /* 压缩机启动温度 = 目标温度 + 回差/2 */
    int start_temp = target_x10 + hyst_x10 / 2;
    /* 压缩机关闭温度 = 目标温度 - 回差/2 */
    int stop_temp = target_x10 - hyst_x10 / 2;
  
    /* 化霜期间不控制压缩机 (化霜时压缩机关闭) */
    if (g_defrost_flag)
    {
        if (g_compressor_state == COMP_ON)
        {
            g_compressor_state = COMP_OFF;
            COMPRESSOR = 0;     /* 关闭压缩机 */
        }
        return;
    }
  
    /* 回差控制 */
    if (g_compressor_state == COMP_OFF)
    {
        /* 压缩机关闭状态:温度高于启动温度则启动 */
        if (temp_x10 >= start_temp)
        {
            g_compressor_state = COMP_ON;
            COMPRESSOR = 1;     /* 启动压缩机 */
            g_comp_run_time = 0;    /* 重置运行计时 */
        }
    }
    else
    {
        /* 压缩机运行状态:温度低于停止温度则停止 */
        if (temp_x10 <= stop_temp)
        {
            g_compressor_state = COMP_OFF;
            COMPRESSOR = 0;     /* 关闭压缩机 */
            g_comp_stop_time = 0;   /* 重置停止计时 */
        }
    }
}

/******************************************************************************
 * 函数名:Defrost_Control
 * 功能  :化霜控制逻辑
 *         定时化霜:每隔DEFROST_CYCLE小时启动一次化霜
 *         化霜时长:DEFROST_TIME分钟
 *         化霜期间关闭压缩机,开启加热丝
 * 输入  :无
 * 输出  :无
 ******************************************************************************/
void Defrost_Control(void)
{
    /* 化霜间隔计时 (每分钟调用一次) */
    if (!g_defrost_flag)
    {
        /* 非化霜状态,累计间隔时间 */
        g_defrost_interval++;
      
        /* 达到化霜周期,启动化霜 */
        if (g_defrost_interval >= (DEFROST_CYCLE * 60))
        {
            g_defrost_flag = 1;         /* 设置化霜标志 */
            g_defrost_timer = 0;        /* 重置化霜计时 */
            DEFROST_HEAT = 1;           /* 开启化霜加热 */
            /* 压缩机已在Temp_Control中关闭 */
        }
    }
    else
    {
        /* 化霜状态,累计化霜时间 */
        g_defrost_timer++;
      
        /* 达到化霜时长,结束化霜 */
        if (g_defrost_timer >= (DEFROST_TIME * 60))
        {
            g_defrost_flag = 0;         /* 清除化霜标志 */
            g_defrost_interval = 0;     /* 重置间隔计时 */
            DEFROST_HEAT = 0;           /* 关闭化霜加热 */
            /* 压缩机由Temp_Control根据温度决定是否启动 */
        }
    }
}

/******************************************************************************
 * 函数名:Alarm_Check
 * 功能  :超温报警检测
 *         温度高于HIGH_ALARM或低于LOW_ALARM时触发报警
 *         蜂鸣器以1Hz频率鸣叫
 * 输入  :无
 * 输出  :无
 ******************************************************************************/
void Alarm_Check(void)
{
    int temp_x10 = g_current_temp;
    int high_alarm_x10 = HIGH_ALARM * 10;
    int low_alarm_x10 = LOW_ALARM * 10;
  
    /* 检查是否超温 */
    if ((temp_x10 > high_alarm_x10) || (temp_x10 < low_alarm_x10))
    {
        g_alarm_flag = 1;
    }
    else
    {
        g_alarm_flag = 0;
        BUZZER = 0;     /* 关闭蜂鸣器 */
    }
  
    /* 报警时蜂鸣器控制 (在1秒中断中处理) */
    if (g_alarm_flag)
    {
        /* 蜂鸣器以1Hz频率闪烁 (500ms开, 500ms关) */
        /* 在定时器中断中处理,这里不做具体控制 */
    }
}

/******************************************************************************
 * 函数名:Key_Scan
 * 功能  :按键扫描
 *         P5.4按键检测,支持短按和长按
 *         短按:切换显示模式
 *         长按(>3秒):手动启动化霜
 * 输入  :无
 * 输出  :无
 ******************************************************************************/
void Key_Scan(void)
{
    /* 按键按下检测 (低电平有效) */
    if (KEY == 0)
    {
        /* 按键按下,累计按下时间 */
        g_key_press_time++;
      
        /* 防抖处理:按下时间超过20ms才认为是有效按键 */
        if (g_key_press_time == 20)
        {
            /* 20ms防抖完成,标记按键按下 */
            /* 继续累计时间以区分短按和长按 */
        }
      
        /* 长按检测 (>3秒 = 3000ms) */
        if (g_key_press_time >= 3000)
        {
            g_key_value = KEY_LONG;
            g_key_press_time = 0;   /* 防止重复触发 */
        }
    }
    else
    {
        /* 按键释放 */
        if ((g_key_press_time >= 20) && (g_key_press_time < 3000))
        {
            /* 按下时间在20ms~3000ms之间,判定为短按 */
            g_key_value = KEY_SHORT;
        }
        g_key_press_time = 0;   /* 重置按下时间 */
    }
}

/******************************************************************************
 * 函数名:Key_Process
 * 功能  :按键处理
 *         根据按键值执行相应操作
 * 输入  :无
 * 输出  :无
 ******************************************************************************/
void Key_Process(void)
{
    if (g_key_value == KEY_SHORT)
    {
        /* 短按:切换显示模式 (通过串口输出当前状态) */
        /* 这里触发一次完整的状态报告 */
        Report_Status();
        g_key_value = KEY_NONE;
    }
    else if (g_key_value == KEY_LONG)
    {
        /* 长按:手动启动化霜 */
        if (!g_defrost_flag)
        {
            g_defrost_flag = 1;         /* 设置化霜标志 */
            g_defrost_timer = 0;        /* 重置化霜计时 */
            DEFROST_HEAT = 1;           /* 开启化霜加热 */
          
            /* 通过串口输出化霜启动信息 */
            sprintf((char *)g_uart_tx_buf, "\r\n[手动化霜] 启动化霜\r\n");
            UART1_SendString(g_uart_tx_buf);
        }
        else
        {
            /* 如果正在化霜,长按则强制结束化霜 */
            g_defrost_flag = 0;
            g_defrost_interval = 0;
            DEFROST_HEAT = 0;
          
            sprintf((char *)g_uart_tx_buf, "\r\n[手动化霜] 强制结束化霜\r\n");
            UART1_SendString(g_uart_tx_buf);
        }
        g_key_value = KEY_NONE;
    }
}

/******************************************************************************
 * 函数名:UART1_SendByte
 * 功能  :串口1发送一个字节 (查询方式)
 * 输入  :dat - 要发送的数据
 * 输出  :无
 ******************************************************************************/
void UART1_SendByte(unsigned char dat)
{
    SBUF = dat;                 /* 写入发送缓冲区 */
    while (!TI);                /* 等待发送完成 */
    TI = 0;                     /* 清除发送中断标志 */
}

/******************************************************************************
 * 函数名:UART1_SendString
 * 功能  :串口1发送字符串
 * 输入  :str - 要发送的字符串指针
 * 输出  :无
 ******************************************************************************/
void UART1_SendString(unsigned char *str)
{
    while (*str)
    {
        UART1_SendByte(*str++);
    }
}

/******************************************************************************
 * 函数名:Report_Status
 * 功能  :通过串口输出当前系统状态
 * 输入  :无
 * 输出  :无
 ******************************************************************************/
void Report_Status(void)
{
    unsigned char buf[80];
    int temp_int, temp_dec;
  
    /* 将温度拆分为整数和小数部分 */
    temp_int = g_current_temp / 10;     /* 整数部分 */
    temp_dec = g_current_temp % 10;     /* 小数部分 */
    if (temp_dec < 0) temp_dec = -temp_dec;
  
    /* 格式化输出 */
    sprintf((char *)buf, 
        "\r\n========== 冷柜温控系统状态 ==========\r\n"
        "当前温度: %d.%d℃\r\n"
        "目标温度: %d℃\r\n"
        "压缩机: %s\r\n"
        "化霜状态: %s\r\n"
        "运行时间: %d小时%02d分\r\n"
        "压缩机运行: %d秒\r\n"
        "报警状态: %s\r\n"
        "========================================\r\n",
        temp_int, temp_dec,
        TARGET_TEMP,
        g_compressor_state ? "运行中" : "已停止",
        g_defrost_flag ? "化霜中" : "正常",
        g_hour_count, g_min_count,
        g_comp_run_time,
        g_alarm_flag ? "报警!!!" : "正常"
    );
  
    UART1_SendString(buf);
}

/******************************************************************************
 * 函数名:System_Init
 * 功能  :系统初始化,依次初始化各模块
 * 输入  :无
 * 输出  :无
 ******************************************************************************/
void System_Init(void)
{
    /* 关闭全局中断,初始化过程中不响应中断 */
    EA = 0;
  
    /* 初始化I/O口 */
    GPIO_Init();
  
    /* 初始化定时器0 (1ms中断) */
    Timer0_Init();
  
    /* 初始化串口1 (9600波特率) */
    UART1_Init();
  
    /* 初始化ADC */
    ADC_Init();
  
    /* 初始化全局变量 */
    g_ms_count = 0;
    g_sec_count = 0;
    g_min_count = 0;
    g_hour_count = 0;
    g_current_temp = 0;
    g_temp_updated = 0;
    g_compressor_state = COMP_OFF;
    g_comp_run_time = 0;
    g_comp_stop_time = 0;
    g_defrost_flag = 0;
    g_defrost_timer = 0;
    g_defrost_interval = 0;
    g_alarm_flag = 0;
    g_key_value = KEY_NONE;
    g_key_press_time = 0;
  
    /* 打开全局中断 */
    EA = 1;
  
    /* 输出启动信息 */
    sprintf((char *)g_uart_tx_buf, "\r\n\r\n=== 冷柜温控系统启动 ===\r\n");
    UART1_SendString(g_uart_tx_buf);
    sprintf((char *)g_uart_tx_buf, "系统时钟: %dMHz\r\n", (int)(FOSC / 1000000));
    UART1_SendString(g_uart_tx_buf);
    sprintf((char *)g_uart_tx_buf, "目标温度: %d℃\r\n", TARGET_TEMP);
    UART1_SendString(g_uart_tx_buf);
    sprintf((char *)g_uart_tx_buf, "回差温度: %d℃\r\n", HYSTERESIS);
    UART1_SendString(g_uart_tx_buf);
    sprintf((char *)g_uart_tx_buf, "化霜周期: %d小时\r\n", DEFROST_CYCLE);
    UART1_SendString(g_uart_tx_buf);
    sprintf((char *)g_uart_tx_buf, "化霜时长: %d分钟\r\n", DEFROST_TIME);
    UART1_SendString(g_uart_tx_buf);
    sprintf((char *)g_uart_tx_buf, "系统初始化完成,开始运行...\r\n\r\n");
    UART1_SendString(g_uart_tx_buf);
}

/******************************************************************************
 * 函数名:main
 * 功能  :主函数
 *         系统初始化后进入主循环
 *         主循环中执行温度采集、温度控制、化霜控制、报警检测等任务
 * 输入  :无
 * 输出  :无
 ******************************************************************************/
void main(void)
{
    unsigned int adc_val;
    unsigned char report_counter = 0;   /* 状态报告计数器 */
  
    /* 系统初始化 */
    System_Init();
  
    /* 主循环 */
    while (1)
    {
        /* ========== 温度采集 (每1秒执行一次) ========== */
        if (g_temp_updated)
        {
            g_temp_updated = 0;     /* 清除更新标志 */
          
            /* 读取ADC值 (温度通道) */
            adc_val = ADC_Read(ADC_CH_TEMP);
          
            /* 查表计算温度 */
            g_current_temp = Calc_Temperature(adc_val);
          
            /* ========== 温度控制 ========== */
            Temp_Control();
          
            /* ========== 报警检测 ========== */
            Alarm_Check();
          
            /* ========== 状态报告 (每10秒输出一次) ========== */
            report_counter++;
            if (report_counter >= 10)
            {
                report_counter = 0;
                Report_Status();
            }
        }
      
        /* ========== 按键处理 ========== */
        if (g_key_value != KEY_NONE)
        {
            Key_Process();
        }
    }
}

/******************************************************************************
 * 定时器0中断服务函数
 * 功能:1ms定时中断,用于系统计时
 *       每1ms中断一次,累计毫秒、秒、分钟、小时
 *       同时处理按键扫描、蜂鸣器控制等需要精确时基的任务
 * 中断向量:000BH
 * 优先级:默认 (可被更高优先级中断打断)
 ******************************************************************************/
void Timer0_ISR(void) interrupt 1
{
    /* 定时器0为16位自动重装载模式,无需重新赋值 */
  
    /* 毫秒计数器递增 */
    g_ms_count++;
  
    /* 按键扫描 (每1ms执行一次) */
    Key_Scan();
  
    /* 每1000ms = 1秒 */
    if (g_ms_count >= 1000)
    {
        g_ms_count = 0;
        g_sec_count++;
      
        /* 温度更新标志 (每秒更新一次温度) */
        g_temp_updated = 1;
      
        /* 压缩机运行/停止计时 */
        if (g_compressor_state == COMP_ON)
        {
            g_comp_run_time++;
        }
        else
        {
            g_comp_stop_time++;
        }
      
        /* 报警蜂鸣器控制 (1Hz闪烁) */
        if (g_alarm_flag)
        {
            /* 每500ms翻转一次蜂鸣器状态 */
            if (g_sec_count & 0x01)  /* 奇数次秒 */
            {
                BUZZER = 1;     /* 蜂鸣器开 */
            }
            else
            {
                BUZZER = 0;     /* 蜂鸣器关 */
            }
        }
      
        /* 每60秒 = 1分钟 */
        if (g_sec_count >= 60)
        {
            g_sec_count = 0;
            g_min_count++;
          
            /* 化霜控制 (每分钟执行一次) */
            Defrost_Control();
          
            /* 每60分钟 = 1小时 */
            if (g_min_count >= 60)
            {
                g_min_count = 0;
                g_hour_count++;
            }
        }
    }
}

/******************************************************************************
 * 串口1中断服务函数
 * 功能:处理串口接收数据
 *       本演示程序中,串口接收主要用于接收上位机指令
 *       可扩展实现远程设定温度、查询状态等功能
 * 中断向量:0023H
 ******************************************************************************/
void UART1_ISR(void) interrupt 4
{
    unsigned char rx_data;
  
    if (RI)     /* 接收中断 */
    {
        RI = 0;                     /* 清除接收中断标志 */
        rx_data = SBUF;             /* 读取接收数据 */
      
        /* 此处可扩展串口命令解析功能 */
        /* 例如:'R' 查询状态, 'S' 设定温度等 */
        /* 本演示程序暂不实现复杂命令解析 */
    }
  
    if (TI)     /* 发送中断 (本程序使用查询方式发送,此处不清除) */
    {
        /* 使用查询方式发送,TI在发送函数中清除 */
        /* 如果使用中断方式发送,可在此处理 */
    }
}

/******************************************************************************
 * ADC中断服务函数
 * 功能:ADC转换完成中断
 *       本演示程序使用查询方式读取ADC,此中断暂不使用
 *       如需使用DMA或自动连续转换,可在此处理
 * 中断向量:002BH
 ******************************************************************************/
void ADC_ISR(void) interrupt 5
{
    /* 本程序使用查询方式读取ADC,中断未使能 */
    /* 如果使能了ADC中断,需要在此清除ADC_FLAG */
    /* ADC_CONTR &= ~0x20; */   /* 清除ADC_FLAG */
}

程序说明

一、硬件资源分配

管脚 功能 配置
P1.2 NTC温度传感器输入 (ADC通道2) 高阻输入
P1.3 压缩机继电器控制 推挽输出
P1.4 蜂鸣器报警输出 推挽输出
P1.5 化霜加热丝控制 推挽输出
P3.0/RxD 串口接收 (USB转串口) 准双向口
P3.1/TxD 串口发送 (USB转串口) 准双向口
P5.4 按键输入 (短按/长按) 准双向口

二、软件架构

程序采用前后台系统架构:

  • 前台(中断):定时器0每1ms中断,负责系统计时、按键扫描、蜂鸣器控制
  • 后台(主循环):温度采集、温度控制逻辑、化霜控制、报警检测、状态报告

三、核心控制逻辑

温度控制:采用回差控制方式

  • 目标温度 -18℃,回差 2℃
  • 温度 ≥ -16℃ → 启动压缩机
  • 温度 ≤ -20℃ → 停止压缩机

化霜控制

  • 每6小时自动启动一次化霜
  • 化霜持续15分钟
  • 长按P5.4按键可手动启动/强制结束化霜

四、使用步骤

  1. 硬件连接:按上述管脚分配连接NTC、继电器、蜂鸣器、加热丝和按键
  2. 编译下载:使用Keil C51编译,STC-ISP软件下载
  3. 串口调试:打开串口助手,波特率9600,可看到实时温度、压缩机状态等信息
  4. 按键操作
    • 短按P5.4:立即输出一次完整状态报告
    • 长按(>3秒)P5.4:手动启动化霜(再次长按可强制结束)

五、注意事项

  1. NTC参数:程序中使用的ADC查表数据基于10K NTC + 10K上拉电阻,实际应用需根据具体NTC型号重新标定
  2. 压缩机保护:程序中未实现压缩机启动延时保护,实际产品建议增加压缩机停机后3~5分钟延时再启动
  3. ADC参考电压:ADC_VRef+引脚需接稳定的参考电压,简单应用可直接接Vcc
  4. P5.4上电特性:P5.4上电后内部4K上拉会打开约6.5ms,设计按键电路时需注意

🔍 搜索 "用 STC8H1K08自动编写冷柜温控制的程序 生成完整演示程序 并含详细程序注解"

📌 相关论坛讨论

  1. 【AI助手】@AiCube-ISP-V6.96Z中,【自动生成程序】+【查错高手】

⏱️ 查找用时 14.4s | 总回答用时 81.8s

STC官网:https://www.stcai.com/
QQ:2593903262
微信号:18106296598
回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:104
  • 最近打卡:2026-07-21 08:50:20

50

主题

353

回帖

2848

积分

超级版主

积分
2848
发表于 前天 13:15 | 显示全部楼层
AI8H1K28-36I-QFN32用于耳机充电盒,用到功能:一组I2C, 一组串口 。出程序 和详细注解。
截图202607201315415113.jpg




QQ:3398500488
微信号:18106296592(小刘)
回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:104
  • 最近打卡:2026-07-21 08:50:20

50

主题

353

回帖

2848

积分

超级版主

积分
2848
发表于 前天 13:16 | 显示全部楼层

好的,信息已经全部获取完毕。现在我来为您编写完整的程序。

根据手册信息,STC8H1K28-36I-QFN32 具有:

  • 串口1:默认在 P3.0(RxD)/P3.1(TxD),也可切换到其他引脚
  • 串口2:默认在 P1.0(RxD2)/P1.1(TxD2)
  • I2C:默认在 P1.5(SCL)/P1.4(SDA),也可切换到其他引脚

为了不与串口下载冲突(P3.0/P3.1用于ISP下载),我选择:

  • 串口1 使用默认引脚 P3.0(RxD)/P3.1(TxD) — 用于与外部蓝牙/充电盒主控通信
  • I2C 使用 P1.5(SCL)/P1.4(SDA) — 用于与充电盒内的电池管理芯片(如IP系列)通信

以下是完整的程序代码:

/*
 * ============================================================
 *  文件名: main.c
 *  芯片  : STC8H1K28-36I-QFN32
 *  功能  : 耳机充电盒控制程序
 *          1. I2C主机模式 — 与充电管理芯片通信(如IP系列)
 *          2. 串口1       — 与外部主控/蓝牙模块通信
 *  时钟  : 内部IRC 11.0592MHz
 *  串口  : 115200,8,n,1,使用定时器2做波特率发生器
 *          P3.0(RxD) / P3.1(TxD)
 *  I2C   : 主机模式,400kHz
 *          P1.5(SCL) / P1.4(SDA)
 *
 *  使用说明:
 *  1. 在 Keil C51 中新建项目,选择 STC8H1K28 系列
 *  2. 将本文件加入项目
 *  3. 在 Target Options -> Output 中勾选 "Create HEX File"
 *  4. 使用 STC-ISP 下载软件,选择芯片 STC8H1K28
 *  5. 下载时选择内部IRC频率 11.0592MHz
 *  6. 下载完成后上电运行
 *
 *  引脚分配(QFN32):
 *    Pin 13 (P3.0) — RxD (串口1接收)
 *    Pin 14 (P3.1) — TxD (串口1发送)
 *    Pin  5 (P1.4) — SDA (I2C数据线)
 *    Pin  6 (P1.5) — SCL (I2C时钟线)
 * ============================================================
 */

#include "stc8h.h"
#include "intrins.h"

/* ======================== 宏定义 ======================== */

#define FOSC            11059200UL      // 系统时钟频率 11.0592MHz
#define BAUD_RATE       115200UL        // 串口波特率 115200

/* 定时器2重装值:65536 - FOSC / 4 / BAUD_RATE */
/* 加2实现四舍五入 */
#define T2_RELOAD       (65536UL - (FOSC / 4 / BAUD_RATE + 2) / 4)

/* I2C速度设置:MSSPEED = (FOSC / 2 / I2C_SPEED - 4) / 2 */
/* 目标400kHz,MSSPEED = (11059200/2/400000 - 4)/2 ≈ 5.9,取6 */
#define I2C_SPEED       6               // MSSPEED值,实际速度约 11059200/2/(6*2+4) ≈ 345kHz

/* I2C设备地址(示例:充电管理芯片,需根据实际芯片修改) */
#define CHARGER_ADDR_W  0x6C            // 充电芯片写地址(7位地址0x36左移1位)
#define CHARGER_ADDR_R  0x6D            // 充电芯片读地址

/* 串口接收缓冲区大小 */
#define UART_BUF_SIZE   16

/* ======================== 全局变量 ======================== */

bit   uart_busy;                        // 串口发送忙标志
unsigned char uart_wptr;                // 串口接收写入指针
unsigned char uart_rptr;                // 串口接收读取指针
unsigned char uart_buffer[UART_BUF_SIZE]; // 串口接收环形缓冲区

/* ======================== 函数声明 ======================== */

/* 系统初始化 */
void System_Init(void);

/* 串口1初始化 — 使用定时器2做波特率发生器 */
void UART1_Init(void);

/* 串口1发送单字节 */
void UART1_SendByte(unsigned char dat);

/* 串口1发送字符串 */
void UART1_SendString(unsigned char *p);

/* I2C初始化 — 主机模式 */
void I2C_Init(void);

/* I2C等待命令完成(查询方式) */
void I2C_Wait(void);

/* I2C发送起始信号 */
void I2C_Start(void);

/* I2C发送停止信号 */
void I2C_Stop(void);

/* I2C发送一字节数据 */
void I2C_SendData(unsigned char dat);

/* I2C接收ACK */
void I2C_RecvACK(void);

/* I2C接收一字节数据 */
unsigned char I2C_RecvData(void);

/* I2C发送ACK */
void I2C_SendACK(void);

/* I2C发送NAK */
void I2C_SendNAK(void);

/* I2C向充电芯片写一字节(寄存器地址+数据) */
bit I2C_WriteReg(unsigned char dev_addr, unsigned char reg_addr, unsigned char dat);

/* I2C从充电芯片读一字节 */
unsigned char I2C_ReadReg(unsigned char dev_addr, unsigned char reg_addr);

/* 延时约1ms */
void Delay1ms(void);

/* 延时n毫秒 */
void DelayNms(unsigned int n);

/* ======================== 主函数 ======================== */

void main(void)
{
    unsigned char i;
    unsigned char bat_voltage;           // 电池电压寄存器值(示例)
    unsigned char charge_status;         // 充电状态寄存器值(示例)

    /* 第一步:系统初始化 */
    System_Init();

    /* 第二步:串口1初始化 */
    UART1_Init();

    /* 第三步:I2C初始化 */
    I2C_Init();

    /* 第四步:使能串口1中断和总中断 */
    ES = 1;                             // 使能串口1中断
    EA = 1;                             // 打开总中断

    /* 发送启动信息 */
    UART1_SendString("STC8H1K28 Charger Box Start!\r\n");

    /* ==================== 主循环 ==================== */
    while (1)
    {
        /* ---- 串口接收数据处理(回显) ---- */
        if (uart_rptr != uart_wptr)
        {
            /* 从环形缓冲区取出一个字节并发送回去(回显) */
            UART1_SendByte(uart_buffer[uart_rptr]);
            uart_rptr++;
            uart_rptr &= (UART_BUF_SIZE - 1);   // 环形指针回绕
        }

        /* ---- 周期性读取充电芯片状态(示例:每500ms) ---- */
        for (i = 0; i < 500; i++)
        {
            Delay1ms();
        }

        /* 读取充电芯片寄存器(示例寄存器地址,需根据实际芯片修改) */
        bat_voltage = I2C_ReadReg(CHARGER_ADDR_W, 0x00);    // 示例:读寄存器0x00
        charge_status = I2C_ReadReg(CHARGER_ADDR_W, 0x01);  // 示例:读寄存器0x01

        /* 通过串口输出状态信息 */
        UART1_SendString("BatVolt:0x");
        /* 简单十六进制输出 */
        {
            unsigned char nibble;
            nibble = (bat_voltage >> 4) & 0x0F;
            if (nibble < 10)
                UART1_SendByte('0' + nibble);
            else
                UART1_SendByte('A' + nibble - 10);
            nibble = bat_voltage & 0x0F;
            if (nibble < 10)
                UART1_SendByte('0' + nibble);
            else
                UART1_SendByte('A' + nibble - 10);
        }
        UART1_SendString(" Status:0x");
        {
            unsigned char nibble;
            nibble = (charge_status >> 4) & 0x0F;
            if (nibble < 10)
                UART1_SendByte('0' + nibble);
            else
                UART1_SendByte('A' + nibble - 10);
            nibble = charge_status & 0x0F;
            if (nibble < 10)
                UART1_SendByte('0' + nibble);
            else
                UART1_SendByte('A' + nibble - 10);
        }
        UART1_SendString("\r\n");
    }
}

/* ======================== 系统初始化 ======================== */

void System_Init(void)
{
    /* 使能访问扩展寄存器区(XFR),I2C寄存器在XFR区 */
    P_SW2 |= 0x80;

    /* 将所有I/O口设置为准双向口(弱上拉模式) */
    P0M0 = 0x00;    P0M1 = 0x00;
    P1M0 = 0x00;    P1M1 = 0x00;
    P2M0 = 0x00;    P2M1 = 0x00;
    P3M0 = 0x00;    P3M1 = 0x00;
    P4M0 = 0x00;    P4M1 = 0x00;
    P5M0 = 0x00;    P5M1 = 0x00;
}

/* ======================== 串口1初始化 ======================== */

void UART1_Init(void)
{
    /* 串口1配置:模式1(8位数据,可变波特率),允许接收 */
    SCON = 0x50;                        // SCON = 0101 0000B
                                        // SM0=0, SM1=1 → 模式1
                                        // SM2=0, REN=1 → 允许接收
                                        // TB8=0, RB8=0
                                        // TI=0, RI=0

    /* 设置定时器2作为串口1的波特率发生器 */
    /* 定时器2工作在1T模式(AUXR.2=1),启动定时器2(AUXR.4=1) */
    /* S1ST2=1(AUXR.0=1)选择定时器2作为串口1波特率发生器 */
    T2L = (unsigned char)(T2_RELOAD);           // 定时器2低字节
    T2H = (unsigned char)(T2_RELOAD >> 8);      // 定时器2高字节
    AUXR = 0x15;                        // AUXR = 0001 0101B
                                        // B7(T0x12)=0, B6(T1x12)=0
                                        // B5(UART_M0x6)=0
                                        // B4(T2R)=1 → 启动定时器2
                                        // B3(T2_C/T)=0 → 定时器模式
                                        // B2(T2x12)=1 → 1T模式
                                        // B1(EXTRAM)=0
                                        // B0(S1ST2)=1 → 串口1使用定时器2

    /* 初始化环形缓冲区指针 */
    uart_wptr = 0x00;
    uart_rptr = 0x00;
    uart_busy = 0;
}

/* ======================== 串口1发送单字节 ======================== */

void UART1_SendByte(unsigned char dat)
{
    /* 等待上一次发送完成 */
    while (uart_busy);

    /* 设置忙标志,写入数据到SBUF触发发送 */
    uart_busy = 1;
    SBUF = dat;
}

/* ======================== 串口1发送字符串 ======================== */

void UART1_SendString(unsigned char *p)
{
    /* 逐字节发送,直到遇到字符串结束符 '\0' */
    while (*p != '\0')
    {
        UART1_SendByte(*p);
        p++;
    }
}

/* ======================== 串口1中断服务函数 ======================== */

void UART1_ISR(void) interrupt 4        // 串口1中断入口地址
{
    /* 发送中断处理 */
    if (TI)
    {
        TI = 0;                         // 清除发送中断标志
        uart_busy = 0;                  // 清除发送忙标志
    }

    /* 接收中断处理 */
    if (RI)
    {
        RI = 0;                         // 清除接收中断标志

        /* 将接收到的数据存入环形缓冲区 */
        uart_buffer[uart_wptr] = SBUF;
        uart_wptr++;
        uart_wptr &= (UART_BUF_SIZE - 1);   // 环形指针回绕
    }
}

/* ======================== I2C初始化 ======================== */

void I2C_Init(void)
{
    /* I2C配置寄存器 I2CCFG (地址 FE80H) */
    /* ENI2C=1 使能I2C功能 */
    /* MSSL=1  选择主机模式 */
    /* MSSPEED[5:0] = I2C_SPEED 设置总线速度 */
    I2CCFG = 0x80 | 0x40 | I2C_SPEED;  // 0xC0 | I2C_SPEED
                                        // B7(ENI2C)=1, B6(MSSL)=1
                                        // B5-B0(MSSPEED)=I2C_SPEED

    /* 清除主机状态寄存器 */
    I2CMSST = 0x00;                     // 清MSIF等标志位
}

/* ======================== I2C等待命令完成 ======================== */

void I2C_Wait(void)
{
    /* 等待主机中断标志MSIF置1,表示当前命令执行完成 */
    while (!(I2CMSST & 0x40));          // 等待MSIF=1

    /* 软件清除MSIF标志位 */
    I2CMSST &= ~0x40;
}

/* ======================== I2C发送起始信号 ======================== */

void I2C_Start(void)
{
    /* 写命令 0001 (0x01) 到 I2CMSCR 的 MSCMD[3:0] */
    /* 发送START信号 */
    I2CMSCR = 0x01;
    I2C_Wait();                         // 等待命令完成
}

/* ======================== I2C发送停止信号 ======================== */

void I2C_Stop(void)
{
    /* 写命令 0110 (0x06) 到 I2CMSCR 的 MSCMD[3:0] */
    /* 发送STOP信号 */
    I2CMSCR = 0x06;
    I2C_Wait();                         // 等待命令完成
}

/* ======================== I2C发送一字节数据 ======================== */

void I2C_SendData(unsigned char dat)
{
    /* 将要发送的数据写入I2CTXD寄存器 */
    I2CTXD = dat;

    /* 写命令 0010 (0x02) 到 I2CMSCR 的 MSCMD[3:0] */
    /* 发送8位数据 */
    I2CMSCR = 0x02;
    I2C_Wait();                         // 等待命令完成
}

/* ======================== I2C接收ACK ======================== */

void I2C_RecvACK(void)
{
    /* 写命令 0011 (0x03) 到 I2CMSCR 的 MSCMD[3:0] */
    /* 接收从机返回的ACK/NAK信号 */
    I2CMSCR = 0x03;
    I2C_Wait();                         // 等待命令完成

    /* 读取到的ACK/NAK保存在I2CMSST.1 (MSACKI) */
    /* 如果MSACKI=0表示从机应答ACK,=1表示NAK */
}

/* ======================== I2C接收一字节数据 ======================== */

unsigned char I2C_RecvData(void)
{
    /* 写命令 0100 (0x04) 到 I2CMSCR 的 MSCMD[3:0] */
    /* 从SDA上接收8位数据 */
    I2CMSCR = 0x04;
    I2C_Wait();                         // 等待命令完成

    /* 从I2CRXD寄存器读取接收到的数据 */
    return I2CRXD;
}

/* ======================== I2C发送ACK ======================== */

void I2C_SendACK(void)
{
    /* 设置MSACKO=0,表示要发送ACK应答 */
    I2CMSST &= ~0x01;                   // MSACKO = 0

    /* 写命令 0101 (0x05) 到 I2CMSCR 的 MSCMD[3:0] */
    /* 发送ACK信号 */
    I2CMSCR = 0x05;
    I2C_Wait();                         // 等待命令完成
}

/* ======================== I2C发送NAK ======================== */

void I2C_SendNAK(void)
{
    /* 设置MSACKO=1,表示要发送NAK非应答 */
    I2CMSST |= 0x01;                    // MSACKO = 1

    /* 写命令 0101 (0x05) 到 I2CMSCR 的 MSCMD[3:0] */
    /* 发送NAK信号 */
    I2CMSCR = 0x05;
    I2C_Wait();                         // 等待命令完成
}

/* ======================== I2C写寄存器 ======================== */

bit I2C_WriteReg(unsigned char dev_addr, unsigned char reg_addr, unsigned char dat)
{
    /* I2C写操作时序:
     * START + 设备地址(W) + ACK + 寄存器地址 + ACK + 数据 + ACK + STOP
     */

    I2C_Start();                        // 发送起始信号

    I2C_SendData(dev_addr);             // 发送设备地址+写方向
    I2C_RecvACK();                      // 接收ACK
    if (I2CMSST & 0x02)                 // 检查MSACKI,若为1表示NAK
    {
        I2C_Stop();
        return 1;                       // 返回失败
    }

    I2C_SendData(reg_addr);             // 发送寄存器地址
    I2C_RecvACK();                      // 接收ACK
    if (I2CMSST & 0x02)
    {
        I2C_Stop();
        return 1;
    }

    I2C_SendData(dat);                  // 发送要写入的数据
    I2C_RecvACK();                      // 接收ACK
    if (I2CMSST & 0x02)
    {
        I2C_Stop();
        return 1;
    }

    I2C_Stop();                         // 发送停止信号
    return 0;                           // 返回成功
}

/* ======================== I2C读寄存器 ======================== */

unsigned char I2C_ReadReg(unsigned char dev_addr, unsigned char reg_addr)
{
    unsigned char dat;

    /* I2C读操作时序:
     * START + 设备地址(W) + ACK + 寄存器地址 + ACK
     * + 重复START + 设备地址(R) + ACK + 读取数据 + NAK + STOP
     */

    I2C_Start();                        // 发送起始信号

    I2C_SendData(dev_addr);             // 发送设备地址+写方向
    I2C_RecvACK();                      // 接收ACK
    if (I2CMSST & 0x02)                 // 若NAK则退出
    {
        I2C_Stop();
        return 0xFF;
    }

    I2C_SendData(reg_addr);             // 发送寄存器地址
    I2C_RecvACK();                      // 接收ACK
    if (I2CMSST & 0x02)
    {
        I2C_Stop();
        return 0xFF;
    }

    I2C_Start();                        // 发送重复起始信号

    I2C_SendData(dev_addr | 0x01);      // 发送设备地址+读方向
    I2C_RecvACK();                      // 接收ACK
    if (I2CMSST & 0x02)
    {
        I2C_Stop();
        return 0xFF;
    }

    dat = I2C_RecvData();               // 读取一字节数据
    I2C_SendNAK();                      // 发送NAK(最后一字节)

    I2C_Stop();                         // 发送停止信号

    return dat;                         // 返回读取到的数据
}

/* ======================== 延时函数 ======================== */

void Delay1ms(void)
{
    unsigned int i;

    /* 11.0592MHz下,约1ms延时 */
    /* 每个循环约10个时钟周期 */
    i = FOSC / 10000;                   // 约1105次循环
    while (i)
    {
        i--;
    }
}

void DelayNms(unsigned int n)
{
    unsigned int i;
    for (i = 0; i < n; i++)
    {
        Delay1ms();
    }
}

程序说明

1. 引脚分配(QFN32封装)

引脚号 引脚名 功能 说明
13 P3.0 RxD 串口1接收 — 接外部蓝牙/主控的Tx
14 P3.1 TxD 串口1发送 — 接外部蓝牙/主控的Rx
5 P1.4 SDA I2C数据线 — 接充电管理芯片SDA
6 P1.5 SCL I2C时钟线 — 接充电管理芯片SCL
10 Vcc 电源 1.9V~5.5V
12 Gnd 电源地

注意:P3.0/P3.1也是ISP下载口,下载时需通过USB转串口工具连接这两个引脚。下载完成后正常运行。

2. 串口1配置

  • 模式:模式1(8位数据,1位起始,1位停止,无校验)
  • 波特率:115200bps
  • 波特率发生器:定时器2(1T模式)
  • 中断方式:接收中断将数据存入环形缓冲区,主循环中轮询处理

3. I2C配置

  • 模式:主机模式
  • 速度:约345kHz(MSSPEED=6)
  • 操作方式:查询方式(非中断),提供 I2C_WriteReg()I2C_ReadReg() 两个高层函数

4. 主循环工作流程

  1. 串口接收数据回显(收到什么发回什么)
  2. 每500ms通过I2C读取充电芯片的两个寄存器
  3. 将读取到的电压和状态信息通过串口输出

5. 需要您根据实际硬件修改的参数

/* 充电芯片的I2C设备地址 — 请根据实际芯片手册修改 */
#define CHARGER_ADDR_W  0x6C    // 写地址
#define CHARGER_ADDR_R  0x6D    // 读地址

/* 示例中读取的寄存器地址 0x00 和 0x01 */
/* 请根据您使用的充电管理芯片(如IP系列、TI BQ系列等)的数据手册修改 */

6. 编译与下载

  1. 在 Keil C51 中新建项目,选择芯片型号为 STC8H1K28
  2. 将上述代码保存为 main.c 加入项目
  3. 编译生成 HEX 文件
  4. 使用 STC-ISP 下载软件,选择芯片 STC8H1K28,IRC频率选 11.0592MHz
  5. 通过 USB转串口工具连接 P3.0/P3.1 下载程序

如果您需要调整I2C速度、串口波特率或增加更多功能(如ADC检测电池电压、GPIO控制LED指示灯等),请告诉我,我可以为您扩展代码。


🔍 搜索 "STC8H1K28 耳机充电盒 I2C 串口 程序注解"

📌 相关论坛讨论

  1. STC单片机在TWS耳机充电盒中的应用
  2. 【AI助手】@AiCube-ISP-V6.96Z中,【自动生成程序】+【查错高手】
  3. STC单片机用于超声波清洗设备应用

⏱️ 查找用时 6.9s | 总回答用时 49.1s

QQ:3398500488
微信号:18106296592(小刘)
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:54
  • 最近打卡:2026-07-21 09:52:21
已绑定手机

4

主题

65

回帖

250

积分

中级会员

积分
250
发表于 前天 13:18 | 显示全部楼层
STC【AI 助手】帮 STC32G12K128-24A-LQFP32 写一个完整演示程序,并含详细程序注解,要包含下面这些:
1.6 路互补对称 PWM 输出,带死区控制,适配直流无刷电机调速;
2.ADC 采集电机电流做过流保护,电流超标自动关断 PWM 输出;
3.串口下发转速指令,动态调节 PWM 频率与占空比实现调速。
截图202607201318184476.jpg



回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:54
  • 最近打卡:2026-07-21 09:52:21
已绑定手机

4

主题

65

回帖

250

积分

中级会员

积分
250
发表于 前天 13:19 | 显示全部楼层

现在我已经掌握了所有必要的信息。让我整理一下LQFP32封装的引脚分配情况,然后编写完整的演示程序。

从管脚图可以看到,STC32G12K128的LQFP32封装中:

  • PWM1P/PWM1N 在 P1.0/P1.1(引脚9/10),但也可切换到 P6.0/P6.1(但LQFP32没有引出P6口)
  • 实际上LQFP32只有32个引脚,PWM输出需要看哪些引脚被引出

让我确认一下LQFP32的引脚编号和功能:

LQFP32引脚:
1: P1.0 (ADC0, PWM1P, RxD2)
2: P1.1 (ADC1, PWM1N, TxD2)
3: P1.4 (ADC4, PWM3P, MISO, SDA)
4: P1.5 (ADC5, PWM3N, SCLK, SCL)
5: P1.6 (ADC6, RxD_3, PWM4P)
6: P1.7 (ADC7, TxD_3, PWM4N)
7: P1.3 (ADC3, MOSI, PWM2N)
8: UCAP
9: P5.4 (nRST, ADC2, PWM2P)
10: Vcc
11: Vref+
12: Gnd
13: P3.0 (RxD, D-)
14: P3.1 (TxD, D+)
15: P3.2 (INT0)
16: P3.3 (INT1)
17: P3.4 (T0, T1CLKO)
18: P3.5 (T1, T0CLKO, PWMFLT)
19: P3.6 (INT2, RxD_2)
20: P3.7 (INT3, TxD_2)
21: P2.0 (PWM1P_2, PWM5)
22: P2.1 (PWM1N_2, PWM6)
23: P2.2 (PWM2P_2, PWM7)
24: P2.3 (PWM2N_2, PWM8)
25: P2.4 (PWM3P_2, SDA_2)
26: P2.5 (PWM3N_2, SCL_2)
27: P2.6 (PWM4P_2)
28: P2.7 (PWM4N_2)
29: P0.0 (ADC8, RxD3, PWM5_3)
30: P0.1 (ADC9, TxD3, PWM6_3)
31: P0.2 (ADC10, RxD4, PWM7_3)
32: P0.3 (ADC11, TxD4, PWM8_3)

好的,现在开始编写完整的演示程序。

/*****************************************************************************
 * 文件名: main.c
 * 描  述: STC32G12K128-LQFP32 直流无刷电机(BLDC)控制演示程序
 *         功能:
 *           1. 6路互补对称PWM输出(PWMA),带死区控制,适配BLDC六步换相
 *           2. ADC采集电机电流做过流保护,电流超标自动关断PWM输出
 *           3. 串口1下发转速指令,动态调节PWM频率与占空比实现调速
 *       
 * 硬件连接(LQFP32封装):
 *   PWM1P  - P2.0 (引脚21) -> 上桥臂U+
 *   PWM1N  - P2.1 (引脚22) -> 下桥臂U-
 *   PWM2P  - P2.2 (引脚23) -> 上桥臂V+
 *   PWM2N  - P2.3 (引脚24) -> 下桥臂V-
 *   PWM3P  - P2.4 (引脚25) -> 上桥臂W+
 *   PWM3N  - P2.5 (引脚26) -> 下桥臂W-
 *   ADC电流采样 - P1.0 (引脚1, ADC通道0)
 *   串口1 TXD - P3.1 (引脚14)
 *   串口1 RXD - P3.0 (引脚13)
 *
 * 串口协议(115200,8,N,1):
 *   上位机发送: "SPEED:xxx" 设置目标转速(0~1000 RPM)
 *   上位机发送: "DUTY:xxx"  设置占空比(0~1000, 对应0.0%~100.0%)
 *   单片机回传: "RPM:%u, DUTY:%u, CURRENT:%umA\r\n"
 *
 * 下载时选择内部IRC时钟: 24MHz (用户可自行修改)
 * 编译器: Keil C251, Memory Model 推荐 XSmall
 *
 * 使用说明:
 *   1. 用STC-ISP下载软件将本程序烧录到STC32G12K128-LQFP32
 *   2. 打开串口助手(115200,8,N,1),连接P3.0/P3.1
 *   3. 发送 "SPEED:500" 设置目标转速为500RPM
 *   4. 发送 "DUTY:500" 设置占空比为50%
 *   5. 观察PWM输出和串口返回数据
 ****************************************************************************/

#include "stc32g.h"         // STC32G系列头文件
#include "intrins.h"        // 内含_nop_()等 intrinsic 函数
#include "stdio.h"          // 标准输入输出,用于sprintf

/**************************** 用户配置宏定义 *********************************/
#define MAIN_Fosc           24000000UL      // 主时钟频率 24MHz
#define PWM_FREQ            20000UL         // PWM频率 20kHz (适合BLDC)
#define PWM_DEADTIME        48              // 死区时间 = 48个PWM时钟周期
                                            // 当PSC=0时,tCK_PSC=1/24MHz≈41.67ns
                                            // 死区时间 = 48 * 41.67ns ≈ 2us
#define ADC_CH_CURRENT      0               // ADC通道0(P1.0)采集电流
#define OVERCURRENT_THRESH  3000            // 过流阈值(ADC值),对应约3.0V/3.3V*4096≈3723
                                            // 实际根据采样电阻和运放增益计算
#define UART_BAUD           115200          // 串口波特率
#define UART_BUF_SIZE       32              // 串口接收缓冲区大小

/**************************** 类型定义 ***************************************/
typedef unsigned char   u8;
typedef unsigned int    u16;
typedef unsigned long   u32;

/**************************** 全局变量 ***************************************/
u8  xdata Uart1_RxBuf[UART_BUF_SIZE];   // 串口接收缓冲区
u8  xdata Uart1_RxIndex;                // 接收缓冲区索引
bit xdata Uart1_RxFlag;                 // 接收到完整一帧标志
bit xdata Uart1_Busy;                   // 串口发送忙标志

u16 xdata g_TargetSpeed;                // 目标转速 (0~1000 RPM)
u16 xdata g_CurrentDuty;                // 当前占空比 (0~1000, 对应0.0%~100.0%)
u16 xdata g_AdcCurrentValue;            // ADC电流采样值
bit  xdata g_OverCurrentFlag;           // 过流标志
u16 xdata g_PwmPeriod;                  // PWM周期值 (ARR)
u16 xdata g_PwmDuty;                    // PWM占空比比较值 (CCR)

/**************************** 函数声明 ***************************************/
void SystemClock_Init(void);
void GPIO_Init(void);
void PWMA_Init(u16 period, u16 duty, u16 deadtime);
void PWMA_SetDuty(u16 duty);
void PWMA_SetPeriod(u16 period);
void PWMA_OutputEnable(void);
void PWMA_OutputDisable(void);
void ADC_Init(void);
u16  ADC_ReadChannel(u8 channel);
void Uart1_Init(void);
void Uart1_SendByte(u8 dat);
void Uart1_SendString(u8 *p);
void Uart1_ProcessCommand(void);
void Delay1ms(u16 ms);

/**************************** 主函数 *****************************************/
void main(void)
{
    u16 adc_val;
    u8  i;

    /* ---- 系统初始化 ---- */
    EAXFR = 1;                  // 使能访问XFR扩展寄存器
    CKCON = 0x00;               // 设置外部数据总线速度为最快
    WTST = 0x00;                // 设置程序代码等待参数为0,CPU执行速度最快

    SystemClock_Init();         // 系统时钟初始化(使用内部IRC 24MHz)
    GPIO_Init();                // GPIO初始化
    PWMA_Init(0, 0, PWM_DEADTIME); // PWM初始化(先给0周期和占空比,后面设置)
    ADC_Init();                 // ADC初始化
    Uart1_Init();               // 串口1初始化

    /* ---- 设置默认参数 ---- */
    g_TargetSpeed = 500;        // 默认目标转速 500RPM
    g_CurrentDuty = 500;        // 默认占空比 50.0%
    g_PwmPeriod = (MAIN_Fosc / PWM_FREQ) - 1;   // 计算ARR值
    g_PwmDuty = (u16)((u32)g_PwmPeriod * g_CurrentDuty / 1000);

    PWMA_SetPeriod(g_PwmPeriod);    // 设置PWM周期
    PWMA_SetDuty(g_PwmDuty);        // 设置PWM占空比
    PWMA_OutputEnable();            // 使能PWM输出

    /* ---- 打开总中断 ---- */
    EA = 1;                     // 打开总中断

    /* ---- 主循环 ---- */
    while(1)
    {
        /* 1. 采集电流 */
        adc_val = ADC_ReadChannel(ADC_CH_CURRENT);
        g_AdcCurrentValue = adc_val;

        /* 2. 过流判断 */
        if(adc_val >= OVERCURRENT_THRESH)
        {
            g_OverCurrentFlag = 1;
            PWMA_OutputDisable();       // 立即关断PWM输出,保护电机
        }
        else
        {
            if(g_OverCurrentFlag)
            {
                /* 如果之前过流,现在电流恢复正常,重新使能PWM */
                g_OverCurrentFlag = 0;
                PWMA_OutputEnable();
            }
        }

        /* 3. 处理串口命令 */
        if(Uart1_RxFlag)
        {
            Uart1_RxFlag = 0;
            Uart1_ProcessCommand();
        }

        /* 4. 串口回传状态信息(约每秒一次) */
        /* 这里简单演示,每循环一次发一次,实际应用可加定时器控制发送间隔 */
        {
            u8 buf[64];
            sprintf((char *)buf, "RPM:%u, DUTY:%u, CURRENT:%umA\r\n",
                    (u16)g_TargetSpeed, (u16)g_CurrentDuty,
                    (u16)((u32)adc_val * 3300 / 4096));  // 假设Vref=3.3V
            Uart1_SendString(buf);
        }

        /* 5. 简单延时,控制主循环速度 */
        for(i = 0; i < 50; i++)
        {
            Delay1ms(1);
        }
    }
}

/**************************** 系统时钟初始化 *********************************/
void SystemClock_Init(void)
{
    /* 使用内部24MHz IRC时钟,无需额外配置 */
    /* STC32G上电默认使用内部IRC高频时钟 */
}

/**************************** GPIO初始化 *************************************/
void GPIO_Init(void)
{
    /* PWM输出引脚配置为准双向模式(推挽也可以,但准双向更安全) */
    /* PWM1P - P2.0, PWM1N - P2.1 */
    /* PWM2P - P2.2, PWM2N - P2.3 */
    /* PWM3P - P2.4, PWM3N - P2.5 */
    P2M0 = 0x3F;        // P2.0~P2.5 推挽输出 (P2M0对应位=1)
    P2M1 = 0x00;        // P2M1对应位=0

    /* ADC输入引脚 P1.0 设置为高阻输入 */
    P1M0 = 0x00;        // P1.0: P1M0.0=0
    P1M1 = 0x01;        // P1.0: P1M1.0=1 -> 高阻输入

    /* 串口1引脚 P3.0(RxD), P3.1(TxD) */
    /* P3.0为准双向输入(默认),P3.1推挽输出 */
    P3M0 = 0x02;        // P3.1推挽输出
    P3M1 = 0x00;

    /* 其他未用引脚设为准双向 */
    P0M0 = 0x00; P0M1 = 0x00;
    P4M0 = 0x00; P4M1 = 0x00;
    P5M0 = 0x00; P5M1 = 0x00;
}

/**************************** PWMA高级PWM初始化 ******************************
 * 功能: 配置PWMA为边沿对齐模式,3组互补PWM输出(PWM1~PWM3)
 *       使用PWM1P/PWM1N, PWM2P/PWM2N, PWM3P/PWM3N
 *       引脚映射到第2组(PWMx_2): P2.0/P2.1, P2.2/P2.3, P2.4/P2.5
 * 参数: period - 自动重装载值(ARR),决定PWM频率
 *       duty   - 捕获比较值(CCR),决定PWM占空比
 *       deadtime - 死区时间(以PWM时钟周期为单位)
 ****************************************************************************/
void PWMA_Init(u16 period, u16 duty, u16 deadtime)
{
    /* ---- 1. 选择PWMA引脚映射到第2组 ---- */
    /* PWMA_PS寄存器: 
       [1:0] PWM1_PS: 00=P1.0/P1.1, 01=P2.0/P2.1, 10=P6.0/P6.1
       [3:2] PWM2_PS: 00=P1.2/P1.3, 01=P2.2/P2.3, 10=P6.2/P6.3
       [5:4] PWM3_PS: 00=P1.4/P1.5, 01=P2.4/P2.5, 10=P6.4/P6.5
       [7:6] PWM4_PS: 00=P1.6/P1.7, 01=P2.6/P2.7, 10=P6.6/P6.7
    */
    PWMA_PS = 0x15;     // 01 01 01: PWM1_PS=01(P2.0/P2.1)
                        //           PWM2_PS=01(P2.2/P2.3)
                        //           PWM3_PS=01(P2.4/P2.5)

    /* ---- 2. 配置PWMA_CR1: 边沿对齐模式,向上计数,使能自动预装载 ---- */
    /* CMS[1:0]=00 边沿对齐模式, DIR=0 向上计数, ARPE=1 自动预装载使能 */
    PWMA_CR1 = 0x80;    // ARPE=1, CMS=00, DIR=0, OPM=0, URS=0, UDIS=0, CEN=0

    /* ---- 3. 配置预分频器 ---- */
    /* PWM时钟 = SYSclk / (PSCR + 1) */
    /* 这里不分频: PSCR=0, PWM时钟 = 24MHz */
    PWMA_PSCRH = 0x00;
    PWMA_PSCRL = 0x00;

    /* ---- 4. 设置自动重装载值(ARR)决定PWM频率 ---- */
    /* PWM频率 = SYSclk / (PSCR+1) / (ARR+1) */
    /* 例如: 24MHz / 1 / (1200) = 20kHz */
    if(period > 0)
    {
        PWMA_ARRH = (u8)(period >> 8);
        PWMA_ARRL = (u8)(period);
    }

    /* ---- 5. 设置捕获比较值(CCR)决定占空比 ---- */
    /* 占空比 = CCR / (ARR+1) * 100% */
    if(duty > 0)
    {
        PWMA_CCR1H = (u8)(duty >> 8);
        PWMA_CCR1L = (u8)(duty);
        PWMA_CCR2H = (u8)(duty >> 8);
        PWMA_CCR2L = (u8)(duty);
        PWMA_CCR3H = (u8)(duty >> 8);
        PWMA_CCR3L = (u8)(duty);
    }

    /* ---- 6. 配置捕获/比较模式寄存器(CCMR)为PWM模式1 ---- */
    /* OC1M[2:0]=110 (PWM模式1): CNT<CCR时输出有效电平,否则无效 */
    /* OC1PE=1: 使能预装载功能 */
    /* CC1S[1:0]=00: 通道配置为输出 */
    PWMA_CCMR1 = 0x68;  // OC1CE=0, OC1M=110(PWM1), OC1PE=1, OC1FE=0, CC1S=00
    PWMA_CCMR2 = 0x68;  // 通道2同样配置
    PWMA_CCMR3 = 0x68;  // 通道3同样配置

    /* ---- 7. 配置捕获/比较使能寄存器(CCER) ---- */
    /* CC1E=1 使能PWM1P输出, CC1NE=1 使能PWM1N输出 */
    /* CC1P=0 高电平有效, CC1NP=0 高电平有效 */
    /* 同理配置通道2和通道3 */
    PWMA_CCER1 = 0x55;  // 0101 0101
                        // CC2NP=0, CC2NE=1, CC2P=0, CC2E=1
                        // CC1NP=0, CC1NE=1, CC1P=0, CC1E=1
    PWMA_CCER2 = 0x05;  // 0000 0101
                        // CC3NP=0, CC3NE=1, CC3P=0, CC3E=1

    /* ---- 8. 设置死区时间 ---- */
    /* DTG[7:0] = deadtime, 当DTG[7:5]=0xx时,死区时间 = DTG * tCK_PSC */
    /* tCK_PSC = 1/24MHz ≈ 41.67ns */
    /* deadtime=48时,死区≈2us */
    PWMA_DTR = (u8)deadtime;

    /* ---- 9. 配置刹车寄存器(BKR) ---- */
    /* MOE=1: 主输出使能 */
    /* AOE=0: MOE只能由软件置1 */
    /* BKP=0: 刹车输入低电平有效 */
    /* BKE=0: 禁止刹车输入 */
    /* OSSR=0, OSSI=0 */
    /* LOCK=00: 无写保护 */
    PWMA_BKR = 0x80;    // MOE=1, 其他默认

    /* ---- 10. 使能输出引脚 ---- */
    /* PWMA_ENO: 使能PWM1P/PWM1N, PWM2P/PWM2N, PWM3P/PWM3N */
    /* ENO1P=1, ENO1N=1, ENO2P=1, ENO2N=1, ENO3P=1, ENO3N=1 */
    PWMA_ENO = 0x3F;    // 0011 1111

    /* ---- 11. 使能计数器(CEN=1) ---- */
    PWMA_CR1 |= 0x01;   // CEN=1, 启动PWM计数器
}

/**************************** 设置PWM占空比 **********************************/
void PWMA_SetDuty(u16 duty)
{
    /* 限制占空比范围,防止超出ARR导致异常 */
    if(duty > g_PwmPeriod)
    {
        duty = g_PwmPeriod;     // 最大100%占空比
    }

    g_PwmDuty = duty;

    /* 更新三个通道的CCR值 */
    PWMA_CCR1H = (u8)(duty >> 8);
    PWMA_CCR1L = (u8)(duty);
    PWMA_CCR2H = (u8)(duty >> 8);
    PWMA_CCR2L = (u8)(duty);
    PWMA_CCR3H = (u8)(duty >> 8);
    PWMA_CCR3L = (u8)(duty);
}

/**************************** 设置PWM周期 ************************************/
void PWMA_SetPeriod(u16 period)
{
    if(period < 10) period = 10;    // 最小周期保护

    g_PwmPeriod = period;

    PWMA_ARRH = (u8)(period >> 8);
    PWMA_ARRL = (u8)(period);

    /* 更新周期后,占空比需要重新适配 */
    /* 如果当前占空比大于新周期,则限制 */
    if(g_PwmDuty > period)
    {
        PWMA_SetDuty(period);
    }
}

/**************************** 使能PWM输出 ************************************/
void PWMA_OutputEnable(void)
{
    PWMA_BKR |= 0x80;       // MOE=1, 使能主输出
    PWMA_ENO = 0x3F;        // 重新使能6路输出
}

/**************************** 关闭PWM输出 ************************************/
void PWMA_OutputDisable(void)
{
    PWMA_BKR &= ~0x80;      // MOE=0, 关闭主输出(所有PWM引脚输出无效电平)
}

/**************************** ADC初始化 **************************************/
void ADC_Init(void)
{
    /* 设置ADC时序 */
    /* ADCTIM: CSSETUP=0, CSHOLD=01(2个时钟), SMPDUTY=11111(32个时钟) */
    /* 建议SMPDUTY不小于15,这里设为31以获得更长的采样时间 */
    ADCTIM = 0x3F;          // 0x3F = 0011 1111
                            // CSSETUP=0, CSHOLD=11(4个时钟), SMPDUTY=11111(32个时钟)

    /* 设置ADC时钟和结果格式 */
    /* ADCCFG: RESFMT=0(左对齐), SPEED=15(最慢时钟) */
    /* ADC时钟 = SYSclk/2/(SPEED+1) = 24MHz/2/16 = 750kHz */
    ADCCFG = 0x0F;          // RESFMT=0, SPEED=1111

    /* 打开ADC电源 */
    ADC_POWER = 1;          // ADC_CONTR.7 = 1

    /* 等待ADC电源稳定(约1ms) */
    Delay1ms(2);
}

/**************************** 读取ADC通道值 **********************************
 * 功能: 读取指定ADC通道的转换结果
 * 参数: channel - ADC通道号(0~15)
 * 返回: 12位ADC转换结果(0~4095)
 ****************************************************************************/
u16 ADC_ReadChannel(u8 channel)
{
    u16 result;

    /* 选择ADC通道 */
    ADC_CONTR = (ADC_CONTR & 0xF0) | (channel & 0x0F);

    /* 清除ADC_FLAG标志 */
    ADC_FLAG = 0;

    /* 启动ADC转换 */
    ADC_START = 1;

    /* 插入短暂延时 */
    _nop_();
    _nop_();

    /* 等待转换完成 */
    while(!ADC_FLAG);

    /* 清除完成标志 */
    ADC_FLAG = 0;

    /* 读取转换结果(左对齐格式: ADC_RES高8位, ADC_RESL低4位) */
    result = ((u16)ADC_RES << 8) | ADC_RESL;

    return result;
}

/**************************** 串口1初始化 ************************************
 * 功能: 配置串口1为模式1(8位UART, 可变波特率)
 *       使用定时器1作为波特率发生器
 *       波特率 = SYSclk / 4 / (65536 - TH1)
 ****************************************************************************/
void Uart1_Init(void)
{
    u16 baud_tmp;

    /* 计算定时器1重装载值 */
    /* 波特率 = SYSclk / 4 / (65536 - TH1) */
    /* TH1 = 65536 - SYSclk / 4 / 波特率 */
    baud_tmp = 65536UL - (MAIN_Fosc / 4UL / UART_BAUD);

    /* 配置串口1模式1: 8位UART, 可变波特率 */
    SCON = 0x50;            // SM0=0, SM1=1(模式1), REN=1(允许接收)

    /* 配置定时器1为8位自动重装载模式 */
    TMOD = (TMOD & 0x0F) | 0x20;   // T1: 模式2(8位自动重装载)
    T1x12 = 1;              // 定时器1时钟选择1T模式(与系统时钟同步)

    /* 设置波特率 */
    TH1 = (u8)(baud_tmp >> 8);
    TL1 = (u8)(baud_tmp >> 8);

    /* 启动定时器1 */
    TR1 = 1;

    /* 清空接收缓冲区 */
    Uart1_RxIndex = 0;
    Uart1_RxFlag = 0;
    Uart1_Busy = 0;

    /* 使能串口1中断 */
    ES = 1;                 // 串口1中断使能
}

/**************************** 串口1发送单字节 ********************************/
void Uart1_SendByte(u8 dat)
{
    while(Uart1_Busy);      // 等待上次发送完成
    Uart1_Busy = 1;
    SBUF = dat;             // 写入发送缓冲区,启动发送
}

/**************************** 串口1发送字符串 ********************************/
void Uart1_SendString(u8 *p)
{
    while(*p != '\0')
    {
        Uart1_SendByte(*p++);
    }
}

/**************************** 串口1中断服务函数 ******************************/
void Uart1_Isr(void) interrupt 4
{
    u8 rx_data;

    if(TI)                  // 发送中断
    {
        TI = 0;
        Uart1_Busy = 0;
    }

    if(RI)                  // 接收中断
    {
        RI = 0;
        rx_data = SBUF;     // 读取接收数据

        /* 将接收到的数据存入缓冲区 */
        if(Uart1_RxIndex < UART_BUF_SIZE - 1)
        {
            Uart1_RxBuf[Uart1_RxIndex++] = rx_data;

            /* 判断是否接收到结束符(回车或换行) */
            if((rx_data == '\r') || (rx_data == '\n'))
            {
                Uart1_RxBuf[Uart1_RxIndex - 1] = '\0';  // 替换结束符为字符串结束符
                Uart1_RxFlag = 1;                       // 置位接收完成标志
                Uart1_RxIndex = 0;                      // 重置索引
            }
        }
        else
        {
            /* 缓冲区满,重置 */
            Uart1_RxIndex = 0;
        }
    }
}

/**************************** 串口命令处理 ************************************
 * 功能: 解析串口接收到的命令并执行相应操作
 *       支持命令:
 *         "SPEED:xxx" - 设置目标转速
 *         "DUTY:xxx"  - 设置占空比
 ****************************************************************************/
void Uart1_ProcessCommand(void)
{
    u8 *p;
    u16 value;
    u8 buf[64];

    p = Uart1_RxBuf;

    /* 解析 "SPEED:" 命令 */
    if((p[0] == 'S' || p[0] == 's') &&
       (p[1] == 'P' || p[1] == 'p') &&
       (p[2] == 'E' || p[2] == 'e') &&
       (p[3] == 'E' || p[3] == 'e') &&
       (p[4] == 'D' || p[4] == 'd') &&
       (p[5] == ':'))
    {
        /* 提取数值 */
        value = 0;
        p += 6;
        while((*p >= '0') && (*p <= '9'))
        {
            value = value * 10 + (*p - '0');
            p++;
        }

        /* 限制范围 */
        if(value > 10000) value = 10000;
        g_TargetSpeed = value;

        sprintf((char *)buf, "Speed set to %u RPM\r\n", (u16)g_TargetSpeed);
        Uart1_SendString(buf);
    }
    /* 解析 "DUTY:" 命令 */
    else if((p[0] == 'D' || p[0] == 'd') &&
            (p[1] == 'U' || p[1] == 'u') &&
            (p[2] == 'T' || p[2] == 't') &&
            (p[3] == 'Y' || p[3] == 'y') &&
            (p[4] == ':'))
    {
        /* 提取数值 */
        value = 0;
        p += 5;
        while((*p >= '0') && (*p <= '9'))
        {
            value = value * 10 + (*p - '0');
            p++;
        }

        /* 限制范围 0~1000 */
        if(value > 1000) value = 1000;
        g_CurrentDuty = value;

        /* 计算新的CCR值并更新PWM */
        g_PwmDuty = (u16)((u32)g_PwmPeriod * g_CurrentDuty / 1000);
        PWMA_SetDuty(g_PwmDuty);

        sprintf((char *)buf, "Duty set to %u.%u%%\r\n",
                (u16)(g_CurrentDuty / 10), (u16)(g_CurrentDuty % 10));
        Uart1_SendString(buf);
    }
    else
    {
        /* 未知命令,回显提示 */
        Uart1_SendString((u8 *)"Unknown cmd. Use SPEED:xxx or DUTY:xxx\r\n");
    }
}

/**************************** 毫秒延时函数 ************************************/
void Delay1ms(u16 ms)
{
    u16 i, j;

    for(i = 0; i < ms; i++)
    {
        /* 24MHz下,以下循环约1ms */
        for(j = 0; j < 4000; j++)
        {
            _nop_();
        }
    }
}

程序说明

一、功能概述

本程序为 STC32G12K128-LQFP32 量身定制的直流无刷电机(BLDC)控制演示,包含三大核心功能:

功能模块 实现方式 说明
6路互补PWM PWMA高级PWM定时器 PWM1P/N、PWM2P/N、PWM3P/N 三组互补输出,带死区控制
过流保护 ADC通道0(P1.0)采样 电流超标自动关断PWM(MOE清零),恢复后自动开启
串口调速 UART1(115200bps) 支持 SPEED:xxxDUTY:xxx 命令动态调节

二、引脚分配(LQFP32)

功能 引脚号 引脚名 说明
PWM1P (U+上桥) 21 P2.0 PWMA通道1正极
PWM1N (U-下桥) 22 P2.1 PWMA通道1负极
PWM2P (V+上桥) 23 P2.2 PWMA通道2正极
PWM2N (V-下桥) 24 P2.3 PWMA通道2负极
PWM3P (W+上桥) 25 P2.4 PWMA通道3正极
PWM3N (W-下桥) 26 P2.5 PWMA通道3负极
ADC电流采样 1 P1.0/ADC0 过流检测输入
UART1_TXD 14 P3.1 串口发送
UART1_RXD 13 P3.0 串口接收

三、关键参数

  • PWM频率:20kHz(适合BLDC电机驱动)
  • 死区时间:约2μs(48个PWM时钟周期 @ 24MHz)
  • ADC分辨率:12位,左对齐格式
  • 过流阈值:ADC值3000(对应约2.42V @ Vref=3.3V,实际需根据采样电阻计算)
  • 串口波特率:115200, 8, N, 1

四、串口命令格式

命令示例 功能 范围
SPEED:500 设置目标转速 0~10000 RPM
DUTY:500 设置占空比 0~1000 (对应0.0%~100.0%)

五、注意事项

  1. 下载时:在STC-ISP软件中选择内部IRC时钟为 24MHz
  2. 编译器:使用 Keil C251,Memory Model 推荐设置为 XSmall
  3. ADC_VRef+引脚(LQFP32的引脚11):不能浮空,必须接外部参考电源或直接连到VCC
  4. 过流阈值OVERCURRENT_THRESH 需根据实际采样电阻和运放增益计算调整
  5. BLDC六步换相:本程序为简化演示,三组PWM同时输出相同占空比。实际BLDC控制需结合霍尔传感器或反电动势检测实现六步换相逻辑,可在主循环中根据换相表切换PWM通道的使能状态

🔍 搜索 "STC32G12K128 互补对称PWM 6路 示例程序"

⏱️ 查找用时 35.3s | 总回答用时 96.3s

回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:72
  • 最近打卡:2026-07-21 14:34:43

33

主题

484

回帖

2584

积分

版主

积分
2584
发表于 前天 13:22 | 显示全部楼层
用STC32G12K128生成干洗机控制程序并含详细注解
截图202607201332116055.jpg
QQ:2269694595     微信:18106296594
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:72
  • 最近打卡:2026-07-21 14:34:43

33

主题

484

回帖

2584

积分

版主

积分
2584
发表于 前天 13:22 | 显示全部楼层

好的,资料已经查阅完毕。现在我来为您生成一个完整的基于STC32G12K128的干洗机控制程序。


干洗机控制程序 — 基于STC32G12K128

以下是一个完整的干洗机控制系统程序,包含详细的注解。程序使用Keil C251编译器,适用于STC32G12K128系列单片机。

文件1:main.c — 主程序文件

/******************************************************************************
 * 文件名:main.c
 * 功能:STC32G12K128 干洗机控制系统主程序
 * 硬件平台:STC32G12K128-LQFP64
 * 编译器:Keil C251
 * 
 * 【干洗机控制功能说明】
 * 1. 电机控制:正转、反转、停止(通过继电器控制交流电机)
 * 2. 加热控制:烘干加热器PWM控制(通过固态继电器/可控硅)
 * 3. 洗涤计时:可设置洗涤时间、脱水时间、烘干时间
 * 4. 门锁检测:门锁开关检测,门未关时禁止启动
 * 5. 水位检测:通过ADC检测水位传感器
 * 6. 温度检测:通过ADC检测温度传感器(NTC热敏电阻)
 * 7. 串口通信:通过UART1与外部显示面板通信(9600波特率)
 * 8. 状态指示:LED指示运行状态
 * 
 * 【引脚分配(LQFP64封装)】
 * P0.0 - RxD3 (串口3接收,接显示面板)
 * P0.1 - TxD3 (串口3发送,接显示面板)
 * P1.0 - ADC0 (水位传感器输入)
 * P1.1 - ADC1 (温度传感器输入/NTC)
 * P2.0 - 电机正转继电器控制 (推挽输出)
 * P2.1 - 电机反转继电器控制 (推挽输出)
 * P2.2 - 门锁检测输入 (高阻输入,低电平表示门关)
 * P2.3 - 启动/暂停按键输入 (高阻输入,低电平有效)
 * P2.4 - 运行状态LED (推挽输出)
 * P2.5 - 故障报警LED (推挽输出)
 * P3.0 - RxD (串口1,调试用)
 * P3.1 - TxD (串口1,调试用)
 * P5.4 - nRST (复位)
 * 
 * 【工作流程】
 * 上电初始化 -> 自检 -> 待机状态
 * 用户通过串口发送命令控制:
 *   "START"  - 启动洗涤
 *   "STOP"   - 停止
 *   "WASH"   - 洗涤模式
 *   "RINSE"  - 漂洗模式
 *   "SPIN"   - 脱水模式
 *   "DRY"    - 烘干模式
 *   "SET:T=30" - 设置洗涤时间30分钟
 *   "STATUS" - 查询状态
 * 
 * 使用说明:
 * 1. 使用STC-ISP软件下载程序,选择STC32G12K128
 * 2. 系统时钟使用内部IRC 11.0592MHz
 * 3. 串口1用于调试输出(115200bps)
 * 4. 串口3用于与显示面板通信(9600bps)
 ******************************************************************************/

#include "stc32g.h"      // STC32G系列头文件
#include "intrins.h"     // 内部函数(_nop_等)
#include <stdio.h>       // 标准输入输出(sprintf等)

//=============================================================================
// 系统参数定义
//=============================================================================
#define FOSC            11059200UL   // 系统时钟频率 11.0592MHz
#define BAUD1           115200UL     // 串口1波特率(调试用)
#define BAUD3           9600UL       // 串口3波特率(显示面板)

// 定时器0重载值计算(用于1ms系统滴答定时)
// 定时器0使用1T模式,每1us计数一次
// 1ms = 1000us,需要计数1000次
// 重载值 = 65536 - 1000 = 64536 = 0xFC18
#define T0_1MS_RELOAD   64536UL      // 1ms定时重载值

// 干洗机工作模式定义
#define MODE_IDLE       0            // 空闲模式
#define MODE_WASH       1            // 洗涤模式
#define MODE_RINSE      2            // 漂洗模式
#define MODE_SPIN       3            // 脱水模式
#define MODE_DRY        4            // 烘干模式
#define MODE_COMPLETE   5            // 完成模式
#define MODE_ERROR      6            // 故障模式

// 电机控制状态
#define MOTOR_STOP      0            // 电机停止
#define MOTOR_FORWARD   1            // 电机正转
#define MOTOR_REVERSE   2            // 电机反转

// 门锁状态
#define DOOR_OPEN       1            // 门打开
#define DOOR_CLOSED     0            // 门关闭

// 系统状态标志位
#define FLAG_DOOR       0x01         // 门状态标志位
#define FLAG_START      0x02         // 启动标志位
#define FLAG_PAUSE      0x04         // 暂停标志位
#define FLAG_ERROR      0x08         // 故障标志位
#define FLAG_HEAT       0x10         // 加热标志位

// 安全保护参数
#define MAX_TEMP        85           // 最高允许温度(摄氏度)
#define OVER_TEMP_ALARM 90           // 超温报警温度
#define WATER_LOW       100          // 低水位ADC阈值
#define WATER_HIGH      800          // 高水位ADC阈值

//=============================================================================
// 全局变量定义
//=============================================================================
unsigned char g_u8Mode;              // 当前工作模式
unsigned char g_u8MotorState;        // 电机状态
unsigned char g_u8DoorState;         // 门状态
unsigned char g_u8SystemFlags;       // 系统标志位
unsigned char g_u8ErrorCode;         // 故障代码

unsigned int  g_u16WashTime;         // 洗涤时间(秒)
unsigned int  g_u16RinseTime;        // 漂洗时间(秒)
unsigned int  g_u16SpinTime;         // 脱水时间(秒)
unsigned int  g_u16DryTime;          // 烘干时间(秒)
unsigned int  g_u16ElapsedTime;      // 已运行时间(秒)

unsigned int  g_u16WaterLevel;       // 水位ADC值
unsigned int  g_u16Temperature;      // 温度ADC值
unsigned char g_u8TempCelsius;       // 温度值(摄氏度)

unsigned char g_u8Tick1ms;           // 1ms滴答计数
unsigned int  g_u16Tick1s;           // 1秒滴答计数
unsigned char g_u8SecCounter;        // 秒计数器

// 串口接收缓冲区
unsigned char g_u8RxBuf[32];         // 串口接收缓冲区
unsigned char g_u8RxIndex;           // 接收索引
bit           g_bRxComplete;         // 接收完成标志

// 串口发送忙标志
bit           g_bUart1Busy;          // 串口1发送忙
bit           g_bUart3Busy;          // 串口3发送忙

//=============================================================================
// 函数声明
//=============================================================================
void SystemInit(void);
void GPIO_Init(void);
void Timer0_Init(void);
void UART1_Init(void);
void UART3_Init(void);
void ADC_Init(void);
void PWM_Init(void);

void Motor_Forward(void);
void Motor_Reverse(void);
void Motor_Stop(void);
void Heater_Control(unsigned char u8Duty);
void Door_Check(void);
void Temperature_Read(void);
void WaterLevel_Read(void);

void ProcessCommand(void);
void UpdateStatus(void);
void SafetyCheck(void);
void Delay1ms(unsigned int u16Ms);

//=============================================================================
// 系统初始化函数
//=============================================================================
void SystemInit(void)
{
    // 使能访问扩展RAM区(XFR),操作高级外设前必须设置
    P_SW2 |= 0x80;   // EAXFR = 1,允许访问XFR
  
    // 设置外部数据总线速度为最快
    CKCON = 0x00;
  
    // 设置程序代码等待参数,赋值为0可将CPU执行程序的速度设置为最快
    WTST = 0x00;
  
    // 初始化全局变量
    g_u8Mode = MODE_IDLE;
    g_u8MotorState = MOTOR_STOP;
    g_u8SystemFlags = 0;
    g_u8ErrorCode = 0;
  
    g_u16WashTime = 1800;    // 默认洗涤30分钟(1800秒)
    g_u16RinseTime = 600;    // 默认漂洗10分钟
    g_u16SpinTime = 300;     // 默认脱水5分钟
    g_u16DryTime = 1800;     // 默认烘干30分钟
    g_u16ElapsedTime = 0;
  
    g_u8Tick1ms = 0;
    g_u16Tick1s = 0;
    g_u8SecCounter = 0;
  
    g_u8RxIndex = 0;
    g_bRxComplete = 0;
    g_bUart1Busy = 0;
    g_bUart3Busy = 0;
}

//=============================================================================
// GPIO初始化函数
//=============================================================================
void GPIO_Init(void)
{
    // 将所有I/O口初始化为准双向口(默认模式)
    P0M0 = 0x00;  P0M1 = 0x00;
    P1M0 = 0x00;  P1M1 = 0x00;
    P2M0 = 0x00;  P2M1 = 0x00;
    P3M0 = 0x00;  P3M1 = 0x00;
    P4M0 = 0x00;  P4M1 = 0x00;
    P5M0 = 0x00;  P5M1 = 0x00;
  
    // P2.0 - 电机正转控制(推挽输出)
    P2M0 |= 0x01;  // P2M0.0 = 1
    P2M1 &= ~0x01; // P2M1.0 = 0
    P20 = 0;       // 初始为低电平,电机停止
  
    // P2.1 - 电机反转控制(推挽输出)
    P2M0 |= 0x02;  // P2M0.1 = 1
    P2M1 &= ~0x02; // P2M1.1 = 0
    P21 = 0;       // 初始为低电平,电机停止
  
    // P2.2 - 门锁检测输入(高阻输入)
    P2M0 &= ~0x04; // P2M0.2 = 0
    P2M1 |= 0x04;  // P2M1.2 = 1
  
    // P2.3 - 启动按键输入(高阻输入)
    P2M0 &= ~0x08; // P2M0.3 = 0
    P2M1 |= 0x08;  // P2M1.3 = 1
  
    // P2.4 - 运行状态LED(推挽输出)
    P2M0 |= 0x10;  // P2M0.4 = 1
    P2M1 &= ~0x10; // P2M1.4 = 0
    P24 = 0;       // 初始熄灭
  
    // P2.5 - 故障报警LED(推挽输出)
    P2M0 |= 0x20;  // P2M0.5 = 1
    P2M1 &= ~0x20; // P2M1.5 = 0
    P25 = 0;       // 初始熄灭
  
    // P1.0 - ADC0输入(高阻输入,水位传感器)
    P1M0 &= ~0x01;
    P1M1 |= 0x01;
  
    // P1.1 - ADC1输入(高阻输入,温度传感器)
    P1M0 &= ~0x02;
    P1M1 |= 0x02;
}

//=============================================================================
// 定时器0初始化函数
// 功能:产生1ms系统滴答定时中断
// 配置:16位自动重装载模式,1T模式
// 计算公式:重载值 = 65536 - FOSC / 1000
//          = 65536 - 11059200 / 1000 = 65536 - 11059 = 54477 = 0xD4ED
//=============================================================================
void Timer0_Init(void)
{
    // 注意:由于11059200/1000=11059.2不是整数,使用近似值
    // 实际重载值 = 65536 - 11059 = 54477 = 0xD4ED
    // 误差约0.2%,可接受
  
    TMOD &= ~0x0F;  // 清除T0模式设置
    TMOD |= 0x00;   // T0模式0:16位自动重装载
  
    // 设置1T模式(不分频)
    AUXR |= 0x80;   // T0x12 = 1,定时器0使用1T模式
  
    // 设置重载值
    TL0 = 0xED;     // 低8位
    TH0 = 0xD4;     // 高8位
  
    // 清中断标志并启动定时器
    TF0 = 0;        // 清定时器0中断标志
    TR0 = 1;        // 启动定时器0
  
    // 使能定时器0中断
    ET0 = 1;        // 使能定时器0中断
}

//=============================================================================
// 串口1初始化函数(调试用)
// 波特率:115200bps,8位数据,1位停止位,无校验
// 使用定时器1作为波特率发生器
// 计算公式:重载值 = 65536 - FOSC / 4 / BAUD
//          = 65536 - 11059200 / 4 / 115200
//          = 65536 - 24 = 65512 = 0xFFE8
//=============================================================================
void UART1_Init(void)
{
    // 配置串口1
    SCON = 0x50;    // 模式1:8位UART,允许接收
  
    // 使用定时器1作为波特率发生器
    TMOD &= ~0xF0;  // 清除T1模式设置
    TMOD |= 0x00;   // T1模式0:16位自动重装载
  
    AUXR &= ~0x40;  // T1x12 = 0,使用12T模式(传统8051兼容)
    // 注意:对于115200波特率,使用12T模式时:
    // 重载值 = 65536 - FOSC / 12 / 32 / BAUD
    // 但这里我们使用1T模式以获得更精确的波特率
    AUXR |= 0x40;   // T1x12 = 1,使用1T模式
  
    // 重载值 = 65536 - FOSC / 4 / BAUD
    //        = 65536 - 11059200 / 4 / 115200
    //        = 65536 - 24 = 65512 = 0xFFE8
    TL1 = 0xE8;
    TH1 = 0xFF;
  
    TR1 = 1;        // 启动定时器1
  
    // 使能串口1中断
    ES = 1;
}

//=============================================================================
// 串口3初始化函数(与显示面板通信)
// 波特率:9600bps,8位数据,1位停止位,无校验
// 使用定时器2作为波特率发生器
// 计算公式:重载值 = 65536 - FOSC / 4 / BAUD
//          = 65536 - 11059200 / 4 / 9600
//          = 65536 - 288 = 65248 = 0xFEE0
//=============================================================================
void UART3_Init(void)
{
    // 配置串口3
    S3CON = 0x10;   // S3SM0=0:模式0(8位UART),S3REN=1:允许接收
  
    // 选择定时器2作为波特率发生器(S3ST3=0)
    // S3CON的S3ST3位默认为0,选择定时器2
  
    // 配置定时器2
    T2L = 0xE0;     // 低8位
    T2H = 0xFE;     // 高8位
    T2x12 = 1;      // 定时器2使用1T模式
    T2R = 1;        // 启动定时器2
  
    // 使能串口3中断
    ES3 = 1;
}

//=============================================================================
// ADC初始化函数
// 功能:初始化ADC模块,用于水位和温度检测
// 配置:12位ADC,右对齐,时钟 = SYSclk/2/16
//=============================================================================
void ADC_Init(void)
{
    // 设置ADC内部时序
    ADCTIM = 0x3F;  // CSSETUP=0, CSHOLD=01, SMPDUTY=11111(31)
  
    // 配置ADC时钟
    // SPEED[3:0] = 1111,ADC时钟 = SYSclk/2/16 = 345.6KHz
    // RESFMT = 0:结果左对齐
    ADCCFG = 0x0F;  // 0x0F = 0000 1111,SPEED=1111
  
    // 打开ADC电源
    ADC_POWER = 1;
  
    // 等待ADC电源稳定(约1ms)
    Delay1ms(2);
}

//=============================================================================
// PWM初始化函数(用于加热器控制)
// 功能:使用PWMB(第二组PWM)的PWM5通道控制加热器
// 引脚:PWM5默认在P2.0,但我们用P2.0控制继电器,所以加热器用PWM5_2在P1.7
// 这里我们使用定时器模拟PWM来控制加热器
// 或者使用高级PWM的PWM5通道
//=============================================================================
void PWM_Init(void)
{
    // 使用PWMB的PWM5通道
    // PWM5默认输出引脚为P2.0,但P2.0已用于电机控制
    // 切换到PWM5_2(P1.7)
    // 通过PWMB_PS寄存器设置
    // PWMB_PS地址在XFR区域
  
    // 使能PWMB时钟
    PWMB_CR1 = 0x01;    // CEN=1,使能计数器
  
    // 配置PWM5输出
    PWMB_CCER1 = 0x00;  // 先禁止所有捕获/比较通道输出
    PWMB_CCMR1 = 0x60;  // CC5S=00(输出模式),OC5M=110(PWM模式1)
    PWMB_CCER1 |= 0x01; // CC5E=1,使能OC5输出
  
    // 设置PWM频率和占空比
    // 频率 = SYSclk / (PSC + 1) / (ARR + 1)
    // 设置PSC = 1105,ARR = 999,则频率 = 11059200 / 1106 / 1000 ≈ 10Hz
    // 对于加热器控制,10Hz的PWM频率是合适的
    PWMB_PSCRH = 0x04;  // 预分频器高8位:1105 = 0x0451
    PWMB_PSCRL = 0x51;
  
    PWMB_ARRH = 0x03;   // 自动重载值高8位:1000 = 0x03E8
    PWMB_ARRL = 0xE8;
  
    PWMB_CCR5H = 0x00;  // 初始占空比为0(关闭加热器)
    PWMB_CCR5L = 0x00;
  
    // 使能主输出
    PWMB_BKR = 0x80;    // MOE=1,使能PWM输出
}

//=============================================================================
// 电机控制函数
//=============================================================================
void Motor_Forward(void)
{
    // 正转:P2.0=1(正转继电器吸合),P2.1=0(反转继电器断开)
    P20 = 1;
    P21 = 0;
    g_u8MotorState = MOTOR_FORWARD;
}

void Motor_Reverse(void)
{
    // 反转:P2.0=0(正转继电器断开),P2.1=1(反转继电器吸合)
    P20 = 0;
    P21 = 1;
    g_u8MotorState = MOTOR_REVERSE;
}

void Motor_Stop(void)
{
    // 停止:两个继电器都断开
    P20 = 0;
    P21 = 0;
    g_u8MotorState = MOTOR_STOP;
}

//=============================================================================
// 加热器控制函数
// 功能:通过PWM控制加热器功率
// 参数:u8Duty - 占空比(0~100)
//=============================================================================
void Heater_Control(unsigned char u8Duty)
{
    unsigned int u16CCR;
  
    if (u8Duty > 100)
    {
        u8Duty = 100;
    }
  
    if (u8Duty == 0)
    {
        // 关闭加热器
        PWMB_CCR5H = 0x00;
        PWMB_CCR5L = 0x00;
        g_u8SystemFlags &= ~FLAG_HEAT;
    }
    else
    {
        // 设置占空比
        // CCR = ARR * Duty / 100
        // ARR = 1000
        u16CCR = (unsigned int)1000 * u8Duty / 100;
        PWMB_CCR5H = (unsigned char)(u16CCR >> 8);
        PWMB_CCR5L = (unsigned char)(u16CCR & 0xFF);
        g_u8SystemFlags |= FLAG_HEAT;
    }
}

//=============================================================================
// 门锁检测函数
//=============================================================================
void Door_Check(void)
{
    if (P22 == 0)
    {
        // 门关闭(低电平有效)
        g_u8DoorState = DOOR_CLOSED;
        g_u8SystemFlags &= ~FLAG_DOOR;
    }
    else
    {
        // 门打开
        g_u8DoorState = DOOR_OPEN;
        g_u8SystemFlags |= FLAG_DOOR;
      
        // 如果门打开且机器正在运行,立即停止并报警
        if (g_u8Mode != MODE_IDLE)
        {
            Motor_Stop();
            Heater_Control(0);
            g_u8Mode = MODE_ERROR;
            g_u8ErrorCode = 1;  // 门未关故障
            P25 = 1;            // 点亮报警LED
        }
    }
}

//=============================================================================
// 温度读取函数
// 功能:读取温度传感器(NTC热敏电阻)的ADC值并转换为摄氏度
// 使用ADC通道1(P1.1)
//=============================================================================
void Temperature_Read(void)
{
    unsigned int u16AdcValue;
    unsigned long u32Temp;
  
    // 选择ADC通道1(P1.1)
    ADC_CONTR = (ADC_CONTR & 0xF0) | 0x01;  // ADC_CHS[3:0] = 0001
  
    // 启动ADC转换
    ADC_START = 1;
    _nop_();
    _nop_();
  
    // 等待转换完成
    while (!ADC_FLAG);
    ADC_FLAG = 0;  // 清完成标志
  
    // 读取ADC结果(左对齐格式)
    // ADC_RES是高8位,ADC_RESL是低4位(高4位有效)
    u16AdcValue = ((unsigned int)ADC_RES << 8) | ADC_RESL;
  
    g_u16Temperature = u16AdcValue;
  
    // 将ADC值转换为温度(摄氏度)
    // 这里使用简化的NTC查表法,实际应用中需要根据具体NTC型号校准
    // 假设:ADC值越大,温度越低(NTC负温度系数)
    // 简化转换:Temp = 100 - ADC_value / 40
    // 实际应用中应使用查表法或Steinhart-Hart方程
    u32Temp = (unsigned long)u16AdcValue * 100 / 4096;
    if (u32Temp > 100)
    {
        u32Temp = 100;
    }
    g_u8TempCelsius = (unsigned char)(100 - u32Temp);
}

//=============================================================================
// 水位读取函数
// 功能:读取水位传感器的ADC值
// 使用ADC通道0(P1.0)
//=============================================================================
void WaterLevel_Read(void)
{
    // 选择ADC通道0(P1.0)
    ADC_CONTR = (ADC_CONTR & 0xF0) | 0x00;  // ADC_CHS[3:0] = 0000
  
    // 启动ADC转换
    ADC_START = 1;
    _nop_();
    _nop_();
  
    // 等待转换完成
    while (!ADC_FLAG);
    ADC_FLAG = 0;  // 清完成标志
  
    // 读取ADC结果
    g_u16WaterLevel = ((unsigned int)ADC_RES << 8) | ADC_RESL;
}

//=============================================================================
// 安全检测函数
// 功能:检测各种安全条件,如超温、门状态等
//=============================================================================
void SafetyCheck(void)
{
    // 读取温度
    Temperature_Read();
  
    // 超温检测
    if (g_u8TempCelsius >= OVER_TEMP_ALARM)
    {
        // 超温报警,立即停止加热和电机
        Heater_Control(0);
        Motor_Stop();
        g_u8Mode = MODE_ERROR;
        g_u8ErrorCode = 2;  // 超温故障
        P25 = 1;            // 点亮报警LED
    }
    else if (g_u8TempCelsius >= MAX_TEMP)
    {
        // 达到最高温度,关闭加热器但继续运行
        Heater_Control(0);
    }
  
    // 门状态检测
    Door_Check();
}

//=============================================================================
// 命令处理函数
// 功能:解析串口接收到的命令并执行相应操作
//=============================================================================
void ProcessCommand(void)
{
    unsigned char u8Cmd;
    unsigned int u16Value;
  
    if (!g_bRxComplete)
    {
        return;
    }
  
    g_bRxComplete = 0;
  
    // 比较接收到的命令
    // 使用简单的字符串比较
  
    // "START" - 启动
    if (g_u8RxBuf[0] == 'S' && g_u8RxBuf[1] == 'T' &&
        g_u8RxBuf[2] == 'A' && g_u8RxBuf[3] == 'R' &&
        g_u8RxBuf[4] == 'T' && g_u8RxIndex == 5)
    {
        if (g_u8DoorState == DOOR_CLOSED && g_u8Mode == MODE_IDLE)
        {
            g_u8Mode = MODE_WASH;
            g_u16ElapsedTime = 0;
            g_u8SystemFlags |= FLAG_START;
            P24 = 1;  // 点亮运行LED
        }
    }
    // "STOP" - 停止
    else if (g_u8RxBuf[0] == 'S' && g_u8RxBuf[1] == 'T' &&
             g_u8RxBuf[2] == 'O' && g_u8RxBuf[3] == 'P' &&
             g_u8RxIndex == 4)
    {
        Motor_Stop();
        Heater_Control(0);
        g_u8Mode = MODE_IDLE;
        g_u16ElapsedTime = 0;
        g_u8SystemFlags &= ~FLAG_START;
        P24 = 0;  // 熄灭运行LED
    }
    // "STATUS" - 查询状态
    else if (g_u8RxBuf[0] == 'S' && g_u8RxBuf[1] == 'T' &&
             g_u8RxBuf[2] == 'A' && g_u8RxBuf[3] == 'T' &&
             g_u8RxBuf[4] == 'U' && g_u8RxBuf[5] == 'S' &&
             g_u8RxIndex == 6)
    {
        UpdateStatus();
    }
    // "SET:T=xxx" - 设置时间
    else if (g_u8RxBuf[0] == 'S' && g_u8RxBuf[1] == 'E' &&
             g_u8RxBuf[2] == 'T' && g_u8RxBuf[3] == ':' &&
             g_u8RxBuf[4] == 'T' && g_u8RxBuf[5] == '=')
    {
        // 提取数值
        u16Value = 0;
        u8Cmd = 6;
        while (u8Cmd < g_u8RxIndex && g_u8RxBuf[u8Cmd] >= '0' && g_u8RxBuf[u8Cmd] <= '9')
        {
            u16Value = u16Value * 10 + (g_u8RxBuf[u8Cmd] - '0');
            u8Cmd++;
        }
        if (u16Value > 0 && u16Value <= 180)
        {
            g_u16WashTime = u16Value * 60;  // 转换为秒
        }
    }
  
    // 清空接收缓冲区
    for (u8Cmd = 0; u8Cmd < 32; u8Cmd++)
    {
        g_u8RxBuf[u8Cmd] = 0;
    }
    g_u8RxIndex = 0;
}

//=============================================================================
// 状态更新函数
// 功能:通过串口发送当前系统状态
//=============================================================================
void UpdateStatus(void)
{
    unsigned char u8Buf[64];
  
    // 格式化状态信息
    sprintf(u8Buf, "MODE:%d,MOTOR:%d,TEMP:%d,WATER:%d,DOOR:%d,ERR:%d,TIME:%d\r\n",
            (int)g_u8Mode,
            (int)g_u8MotorState,
            (int)g_u8TempCelsius,
            (int)g_u16WaterLevel,
            (int)g_u8DoorState,
            (int)g_u8ErrorCode,
            (int)g_u16ElapsedTime);
  
    // 通过串口3发送到显示面板
    // 这里简化处理,直接使用串口1输出
    {
        unsigned char *p = u8Buf;
        while (*p)
        {
            while (g_bUart1Busy);
            g_bUart1Busy = 1;
            SBUF = *p++;
        }
    }
}

//=============================================================================
// 毫秒延时函数
// 参数:u16Ms - 延时毫秒数
//=============================================================================
void Delay1ms(unsigned int u16Ms)
{
    unsigned int i, j;
    for (i = 0; i < u16Ms; i++)
    {
        // 11.0592MHz下,约1ms的软件延时
        for (j = 0; j < 2760; j++)
        {
            _nop_();
        }
    }
}

//=============================================================================
// 定时器0中断服务函数
// 功能:1ms系统滴答,用于计时和周期性任务调度
// 中断号:1
//=============================================================================
void Timer0_Isr(void) interrupt 1
{
    // 重载定时器值(16位自动重装载模式下,硬件自动重载)
    // 但为了确保精度,软件也重新加载
    TL0 = 0xED;
    TH0 = 0xD4;
  
    g_u8Tick1ms++;
  
    // 每1ms执行一次的任务
    if (g_u8Tick1ms >= 1)
    {
        // 每1ms检测一次按键(去抖)
        // 实际应用中按键检测需要更复杂的去抖逻辑
    }
  
    // 每10ms执行一次的任务
    if (g_u8Tick1ms >= 10)
    {
        g_u8Tick1ms = 0;
        g_u16Tick1s++;
      
        // 每10ms读取一次传感器
        WaterLevel_Read();
        Temperature_Read();
      
        // 每10ms进行一次安全检测
        SafetyCheck();
    }
  
    // 每1秒执行一次的任务
    if (g_u16Tick1s >= 100)  // 100 * 10ms = 1秒
    {
        g_u16Tick1s = 0;
        g_u8SecCounter++;
      
        // 如果机器正在运行,更新计时
        if (g_u8Mode != MODE_IDLE && g_u8Mode != MODE_ERROR)
        {
            g_u16ElapsedTime++;
        }
    }
}

//=============================================================================
// 串口1中断服务函数(调试用)
// 中断号:4
//=============================================================================
void UART1_Isr(void) interrupt 4
{
    if (TI)
    {
        TI = 0;
        g_bUart1Busy = 0;
    }
  
    if (RI)
    {
        RI = 0;
        // 串口1接收的数据(调试用,暂不处理)
    }
}

//=============================================================================
// 串口3中断服务函数(与显示面板通信)
// 中断号:17
//=============================================================================
void UART3_Isr(void) interrupt 17
{
    if (S3TI)
    {
        S3TI = 0;
        g_bUart3Busy = 0;
    }
  
    if (S3RI)
    {
        S3RI = 0;
      
        // 接收数据
        if (g_u8RxIndex < 31)
        {
            g_u8RxBuf[g_u8RxIndex++] = S3BUF;
          
            // 检查是否接收到结束符(回车或换行)
            if (g_u8RxBuf[g_u8RxIndex - 1] == '\r' ||
                g_u8RxBuf[g_u8RxIndex - 1] == '\n')
            {
                g_u8RxBuf[g_u8RxIndex - 1] = '\0';  // 替换结束符
                g_u8RxIndex--;
                g_bRxComplete = 1;
            }
        }
        else
        {
            // 缓冲区满,重置
            g_u8RxIndex = 0;
        }
    }
}

//=============================================================================
// 串口1发送字符串函数
//=============================================================================
void UART1_SendString(unsigned char *pStr)
{
    while (*pStr)
    {
        while (g_bUart1Busy);
        g_bUart1Busy = 1;
        SBUF = *pStr++;
    }
}

//=============================================================================
// 串口3发送字符串函数
//=============================================================================
void UART3_SendString(unsigned char *pStr)
{
    while (*pStr)
    {
        while (g_bUart3Busy);
        g_bUart3Busy = 1;
        S3BUF = *pStr++;
    }
}

//=============================================================================
// 干洗机工作模式处理函数
// 功能:根据当前模式执行相应的控制逻辑
//=============================================================================
void ModeProcess(void)
{
    unsigned char u8Buf[32];
  
    switch (g_u8Mode)
    {
        case MODE_IDLE:
            // 空闲模式:电机停止,加热关闭
            Motor_Stop();
            Heater_Control(0);
            P24 = 0;  // 运行LED熄灭
            break;
          
        case MODE_WASH:
            // 洗涤模式:电机正反转交替,不加热
            // 每30秒切换一次方向
            if ((g_u16ElapsedTime % 60) < 30)
            {
                Motor_Forward();
            }
            else
            {
                Motor_Reverse();
            }
            Heater_Control(0);
          
            // 检查洗涤时间是否到
            if (g_u16ElapsedTime >= g_u16WashTime)
            {
                g_u8Mode = MODE_RINSE;
                g_u16ElapsedTime = 0;
            }
            break;
          
        case MODE_RINSE:
            // 漂洗模式:电机正转,进水(通过外部电磁阀)
            Motor_Forward();
            Heater_Control(0);
          
            // 检查漂洗时间是否到
            if (g_u16ElapsedTime >= g_u16RinseTime)
            {
                g_u8Mode = MODE_SPIN;
                g_u16ElapsedTime = 0;
            }
            break;
          
        case MODE_SPIN:
            // 脱水模式:电机高速正转,不加热
            Motor_Forward();
            Heater_Control(0);
          
            // 检查脱水时间是否到
            if (g_u16ElapsedTime >= g_u16SpinTime)
            {
                g_u8Mode = MODE_DRY;
                g_u16ElapsedTime = 0;
            }
            break;
          
        case MODE_DRY:
            // 烘干模式:电机低速正转,加热器工作
            Motor_Forward();
          
            // 根据温度控制加热器功率
            if (g_u8TempCelsius < 60)
            {
                Heater_Control(80);   // 温度低于60度,全功率加热
            }
            else if (g_u8TempCelsius < 75)
            {
                Heater_Control(50);   // 60~75度,半功率加热
            }
            else
            {
                Heater_Control(20);   // 75度以上,低功率保温
            }
          
            // 检查烘干时间是否到
            if (g_u16ElapsedTime >= g_u16DryTime)
            {
                g_u8Mode = MODE_COMPLETE;
                g_u16ElapsedTime = 0;
                Heater_Control(0);
                Motor_Stop();
            }
            break;
          
        case MODE_COMPLETE:
            // 完成模式:所有动作停止,提示完成
            Motor_Stop();
            Heater_Control(0);
            P24 = 0;  // 运行LED熄灭
          
            // 发送完成信息
            sprintf(u8Buf, "COMPLETE\r\n");
            UART3_SendString(u8Buf);
          
            // 5秒后自动回到空闲模式
            if (g_u16ElapsedTime >= 5)
            {
                g_u8Mode = MODE_IDLE;
                g_u16ElapsedTime = 0;
            }
            break;
          
        case MODE_ERROR:
            // 故障模式:停止所有动作,报警
            Motor_Stop();
            Heater_Control(0);
            P25 = 1;  // 报警LED常亮
          
            // 发送故障信息
            sprintf(u8Buf, "ERROR:%d\r\n", (int)g_u8ErrorCode);
            UART3_SendString(u8Buf);
            break;
          
        default:
            g_u8Mode = MODE_IDLE;
            break;
    }
}

//=============================================================================
// 主函数
//=============================================================================
void main(void)
{
    // 系统初始化
    SystemInit();
    GPIO_Init();
    Timer0_Init();
    UART1_Init();
    UART3_Init();
    ADC_Init();
    PWM_Init();
  
    // 打开总中断
    EA = 1;
  
    // 发送启动信息
    UART1_SendString("Dry Cleaner Controller v1.0\r\n");
    UART1_SendString("System Initialized OK\r\n");
  
    // 主循环
    while (1)
    {
        // 处理接收到的命令
        ProcessCommand();
      
        // 处理工作模式
        ModeProcess();
      
        // 每100ms进行一次完整的状态检测
        // 实际检测在定时器中断中已经完成,这里只做模式处理
        Delay1ms(100);
    }
}

文件2:stc32g.h — 头文件(标准STC32G头文件)

此头文件为标准STC官方提供的 stc32g.h,请从STC-ISP下载软件中获取最新版本,路径为:
STC-ISP软件安装目录\STC32G\stc32g.h

使用说明

  1. 硬件连接

    • 将程序下载到STC32G12K128单片机
    • 按引脚分配表连接外部电路(继电器、传感器、显示面板等)
    • 注意:电机控制使用继电器隔离,加热器使用固态继电器(SSR)控制
  2. 编译环境

    • 使用Keil C251编译器
    • 创建项目时选择芯片型号:STC32G12K128
    • 添加 main.cstc32g.h 到项目
  3. 串口通信协议

    • 串口1(P3.0/P3.1):115200bps,调试输出
    • 串口3(P0.0/P0.1):9600bps,与显示面板通信
    • 命令格式:ASCII字符串,以回车或换行结束
  4. 安全保护

    • 门锁检测:门打开时自动停止所有操作
    • 超温保护:温度超过85°C关闭加热器,超过90°C报警停机
    • 故障代码:1-门未关,2-超温
  5. 扩展建议

    • 实际NTC温度传感器需要根据具体型号校准温度转换表
    • 水位传感器需要根据实际硬件调整ADC阈值
    • 可增加EEPROM存储用户设置参数
    • 可增加RTC模块实现定时启动功能

如需进一步调整某个功能模块或增加特定功能,请告诉我具体需求!


🔍 搜索 "用 STC32G12K128生成干洗机控制程序并含详细注解"

📌 相关论坛讨论

  1. 【AI助手】@AiCube-ISP-V6.96Z中,【自动生成程序】+【查错高手】

⏱️ 查找用时 19.8s | 总回答用时 99.8s

QQ:2269694595     微信:18106296594
回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:164
  • 最近打卡:2026-07-21 13:57:31

32

主题

187

回帖

1708

积分

版主

积分
1708
发表于 前天 13:26 | 显示全部楼层
用STC8H8K64U做工业风扇,生成完整演示程序,并含详细程序注解

截图202607201333549979.jpg
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-7-22 10:41 , Processed in 0.096080 second(s), 92 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表