好的,我已经获取了足够的信息。现在我来编写完整的电梯召唤盒演示程序。
下面是为 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. 编译与下载
- 在 Keil C51 中新建项目,芯片选择 STC8H1K28 系列
- 将上述代码保存为
main.c,添加到项目中
- 在项目选项中设置:
- Target 页:晶振频率设为 22.1184MHz
- Output 页:勾选 "Create HEX File"
- 编译生成 HEX 文件
- 使用 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. 工作流程
- 上电初始化:系统启动,所有LED熄灭,RS485处于接收状态
- 按下上行按钮:
- 蜂鸣器短鸣一声
- 上行指示灯点亮
- 通过RS485发送MODBUS写线圈命令(地址0x01,线圈0x0001,数据0xFF00)
- 等待电梯主控应答(超时500ms)
- 按下下行按钮:
- 蜂鸣器短鸣一声
- 下行指示灯点亮
- 通过RS485发送MODBUS写线圈命令(地址0x01,线圈0x0002,数据0xFF00)
- 等待电梯主控应答(超时500ms)
- 再次按下同一按钮:取消请求,发送复位命令
- 收到应答:根据应答内容更新指示灯状态
- 超时处理:500ms内未收到应答,自动取消请求并熄灭指示灯
4. 扩展建议
- RS485总线:如需多台召唤盒组网,每台设置不同的MODBUS地址
- 掉电保存:可使用IAP/EEPROM保存按键请求状态
- 省电模式:长时间无操作可进入掉电模式,由按键中断唤醒
- 看门狗:可开启看门狗防止程序跑飞
以上即为完整的电梯召唤盒演示程序。如需调整引脚分配、通信协议细节或增加其他功能(如楼层显示、语音播报等),请告知具体需求,我可以进一步修改完善。
🔍 搜索 "STC8H1K28 电梯召唤盒 演示程序 程序注解"
📌 相关论坛讨论
- 【AI助手】@AiCube-ISP-V6.96Z中,【自动生成程序】+【查错高手】
⏱️ 查找用时 34.7s | 总回答用时 112.0s