串口接收代码

Tutorial: 电子电路 Category: 嵌入式 Published: 2026-10-08 01:26:37 Views: 1 Likes: 0 Comments: 0

050.6.6 串口接收代码

/*
 * 整体目的:在上一节UART查询发送程序上增加中断接收,根据电脑发来的命令控制板载外设。
 * 整体设计:UART以9600 8N1持续发送测试文本;接收完一帧后硬件置RI并进入串口中断,
 *           中断函数读取SBUF,再用switch把0x01~0x09映射为LED、蜂鸣器和继电器动作。
 * 硬件依据:LED1~LED4接P1.0~P1.3且低电平点亮;蜂鸣器由P1.6经PNP管低电平驱动;
 *           外接低电平触发继电器模块的IN接P2.0,并与开发板共用5V和GND。
 * 通信前提:电脑串口助手使用9600、8N1和HEX发送;输入01才能匹配case 0x1,文本'1'是0x31。
 * 课程上下文:代码来自6.3串口接收工程,保留主动发送用于观察串口,同时演示RI中断接收。
 */

#include "reg52.h"   /* 引入8052特殊功能寄存器声明,使程序能使用P1、SCON、SBUF、RI等名称。 */
#include <intrins.h>  /* 引入Keil C51内部函数声明,软件延时需要调用_nop_()。 */

typedef unsigned char uchar;  /* 为8位无符号字符型建立简写,用于UART字节和接收变量。 */
typedef unsigned int uint;    /* 为C51的16位无符号整型建立简写,用于循环和毫秒参数。 */

uint count = 0;  /* 定义a~z发送循环使用的全局计数器,取值范围为0~25。 */
uchar recv;      /* 定义8位全局变量,中断中把接收SBUF的数据转存到这里。 */

sbit LED1 = P1^0;  /* 将LED1绑定到P1.0;输出0时通过灌电流点亮第一只LED。 */
sbit LED2 = P1^1;  /* 将LED2绑定到P1.1;输出0时点亮第二只LED。 */
sbit LED3 = P1^2;  /* 将LED3绑定到P1.2;输出0时点亮第三只LED。 */
sbit LED4 = P1^3;  /* 将LED4绑定到P1.3;输出0时点亮第四只LED。 */
sbit BEEP = P1^6;  /* 将蜂鸣器控制信号绑定到P1.6;输出0使PNP驱动管导通并发声。 */
sbit JDQ1 = P2^0;  /* 将外接继电器模块IN绑定到P2.0;本实验模块以低电平触发吸合。 */

/*
 * 函数目的:执行由xms指定的毫秒级软件忙等待,降低主循环重复发送的频率。
 * 时间前提:计数值按11.0592MHz和当前Keil C51指令耗时生成;改变时钟或优化会影响精度。
 */
void Delay_xms(uint xms)                  /* 定义软件延时函数,xms是计划等待的毫秒数。 */
{                                        /* 延时函数体开始,忙等期间CPU不能执行其他前台任务。 */
    uchar data i, j;                      /* 定义两个8位内层计数器,并放入片内直接寻址data RAM。 */

    while (xms)                           /* 每完成一轮约1ms等待,就把剩余毫秒数减1。 */
    {                                     /* 一毫秒延时循环开始。 */
        _nop_();                           /* 执行一条空操作指令,用来补齐延时生成器计算的时间。 */
        i = 2;                             /* 初始化外层8位循环计数器。 */
        j = 199;                           /* 初始化内层计数器,其递减指令构成主要延时。 */
        do                                /* 使用后判断循环,保证循环体至少执行一次。 */
        {                                 /* do循环体开始,此处只消耗CPU执行时间。 */
            while (--j);                  /* 先递减j,非0便继续空转;分号表示循环体为空。 */
        } while (--i);                    /* 一组j循环完成后递减i,非0时继续下一组。 */
        xms--;                             /* 一轮约1ms结束,将剩余等待次数减1。 */
    }                                     /* xms归零后退出延时循环。 */
}                                         /* 延时函数结束并返回调用处。 */

/* 函数目的:通过查询TI可靠发送一个字节,避免连续写SBUF造成数据丢失。 */
void UART_Send_Byte(uchar send_byte)       /* send_byte保存本次需要发送的8位数据。 */
{                                         /* 单字节发送流程开始。 */
    SBUF = send_byte;                      /* 写发送SBUF,启动UART硬件发送一帧。 */
    while (TI == 0);                       /* TI为0时原地等待;末尾分号表示空循环。 */
    TI = 0;                                /* 硬件置位TI后由软件清0,为下一帧做准备。 */
}                                         /* 当前字节发送到规定时刻,函数返回。 */

/* 函数目的:遍历以'\0'结尾的C字符串,并复用单字节函数逐字符发送。 */
void UART_Send_Str(uchar *send_str)        /* send_str接收待发送字符串的首地址。 */
{                                         /* 字符串发送函数体开始。 */
    while (*send_str != '\0')              /* 当前字符不是NUL结束标志时继续循环。 */
    {                                     /* 当前正文字符的发送分支开始。 */
        UART_Send_Byte(*send_str++);       /* 发送当前字符,再把指针移动到下一个字符。 */
    }                                     /* 遇到'\0'时退出,不把结束标志发到串口。 */
}                                         /* 字符串发送完成并返回。 */

/* 函数目的:初始化UART和波特率时基,循环发送测试内容并等待中断接收命令。 */
void main()                                /* 定义Keil C51程序入口。 */
{                                         /* 主函数体开始。 */
    SCON = 0x50;                           /* 选择UART方式1、允许接收,并将TI和RI初始化为0。 */
    PCON &= 0x7F;                          /* 清SMOD选择波特率不加倍,同时保留PCON其他位。 */
    TMOD &= 0x0F;                          /* 清除定时器1的高4位配置,保留定时器0低4位。 */
    TMOD |= 0x20;                          /* 设置定时器1为内部定时、模式2自动重装。 */
    TH1 = 0xFD;                            /* 保存自动重装值253,用于生成9600波特时基。 */
    TL1 = 0xFD;                            /* 设置第一次计数初值,使首轮周期与后续一致。 */
    TR1 = 1;                               /* 启动定时器1,开始给UART提供波特率时钟。 */
    ES = 1;                                /* 打开串口中断分开关,使RI或TI能请求中断。 */
    EA = 1;                                /* 打开CPU总中断门控,使已允许的串口请求能被响应。 */

    while (1)                              /* 无限循环,持续发送数据并等待接收中断随时打断。 */
    {                                     /* 一轮主动发送开始。 */
        for (count = 0; count < 26; count++)  /* count取0~25,依次产生26个小写字母。 */
        {                                  /* 当前字母发送循环体开始。 */
            UART_Send_Byte('a' + count);   /* 生成'a'~'z'并逐字节等待TI后发送。 */
        }                                  /* 26个字母发送完毕。 */

        UART_Send_Byte('\n');               /* 发送LF换行符,使后续文本换到下一行。 */
        UART_Send_Str("i am wfeng!\r\n");   /* 发送测试字符串及CR+LF行结束序列。 */
        Delay_xms(1000);                   /* 忙等待约1秒,降低整段测试数据的重复频率。 */
    }                                      /* 一轮结束后回到while顶部继续发送。 */
}                                          /* 主函数语法边界;无限循环使其不会正常返回。 */

/*
 * 函数目的:响应串口接收完成事件,把命令字节转存后控制相应外设。
 * 中断来源:TI与RI共用中断号4;本程序的实际命令处理只在RI=1时进行。
 * 执行原则:中断内只做快速寄存器赋值,不加入软件延时或长时间阻塞操作。
 */
void UART_Routine(void) interrupt 4        /* 声明串口中断服务函数,对应向量地址0x0023。 */
{                                          /* 中断服务程序开始,先判断是否由接收标志触发。 */
    if (1 == RI)                           /* RI为1表示UART已接收完一帧并把数据装入接收SBUF。 */
    {                                      /* 接收事件处理分支开始。 */
        RI = 0;                            /* 按配套源码顺序先由软件清RI,为下一帧接收做准备。 */
        recv = SBUF;                       /* 读取接收SBUF,把本次8位命令保存到全局变量recv。 */

        switch (recv)                      /* 按收到的原始字节值选择一个外设控制分支。 */
        {                                  /* switch分支表开始;每个case末尾都用break退出。 */
            case 0x1:                      /* 收到HEX字节01时进入LED1控制分支。 */
                LED1 = 0;                  /* 将P1.0拉低,形成灌电流通路并点亮LED1。 */
                break;                     /* 结束本分支,防止继续执行case 0x2。 */

            case 0x2:                      /* 收到HEX字节02时进入LED2控制分支。 */
                LED2 = 0;                  /* 将P1.1拉低,点亮低电平有效的LED2。 */
                break;                     /* 退出switch,不向下贯穿。 */

            case 0x3:                      /* 收到HEX字节03时进入LED3控制分支。 */
                LED3 = 0;                  /* 将P1.2拉低,点亮LED3。 */
                break;                     /* 完成LED3命令后退出switch。 */

            case 0x4:                      /* 收到HEX字节04时进入LED4控制分支。 */
                LED4 = 0;                  /* 将P1.3拉低,点亮LED4。 */
                break;                     /* 完成LED4命令后退出switch。 */

            case 0x5:                      /* 收到HEX字节05时执行四灯全灭命令。 */
                P1 |= 0x0F;                /* 将P1低4位置1而保留高4位,使LED1~LED4全部熄灭。 */
                break;                     /* 完成四灯全灭后退出switch。 */

            case 0x6:                      /* 收到HEX字节06时执行蜂鸣器开启命令。 */
                BEEP = 0;                  /* P1.6输出低电平使S9012 PNP管导通,蜂鸣器得电发声。 */
                break;                     /* 完成蜂鸣器开启命令后退出switch。 */

            case 0x7:                      /* 收到HEX字节07时执行蜂鸣器关闭命令。 */
                BEEP = 1;                  /* P1.6输出高电平使PNP管截止,蜂鸣器停止。 */
                break;                     /* 完成蜂鸣器关闭命令后退出switch。 */

            case 0x8:                      /* 收到HEX字节08时执行继电器吸合命令。 */
                JDQ1 = 0;                  /* P2.0向低电平触发模块的IN输出有效电平,使继电器吸合。 */
                break;                     /* 完成继电器吸合命令后退出switch。 */

            case 0x9:                      /* 收到HEX字节09时执行继电器释放命令。 */
                JDQ1 = 1;                  /* P2.0输出高电平撤销触发,使实验所用继电器模块释放。 */
                break;                     /* 完成继电器释放命令后退出switch。 */

            default:                       /* 收到01~09以外的字节时进入默认分支。 */
                break;                     /* 不改变任何外设状态,直接离开switch。 */
        }                                  /* 命令分支选择结束。 */
    }                                      /* RI接收事件处理结束;RI为0时不会读取SBUF。 */
}                                          /* 中断服务程序结束,执行中断返回。 */

一、串口数据的接收

本节在串口发送程序的基础上增加接收功能:电脑通过串口助手发送一个命令字节,STC89C52在串口中断中读取该字节,再根据数值控制LED、蜂鸣器和外接继电器模块。

完整数据链路如下:

串口助手选择HEX发送命令
          │
          ▼
USB数据 ─→ CH340N ─→ P3.0/RXD ─→ UART接收器 ─→ 接收SBUF
                                                   │
                                                   ├→ 硬件置RI=1
                                                   ▼
                                      ES=1且EA=1,进入中断号4
                                                   │
                                                   ▼
                                      recv=SBUF,switch解析命令
                                                   │
                        ┌──────────────────────────┼─────────────────────┐
                        ▼                          ▼                     ▼
                   P1.0~P1.3                  P1.6                  P2.0
                    控制LED                 控制蜂鸣器           控制继电器模块

串口接收和发送共用名为SBUF的特殊功能寄存器地址,但芯片内部实际上是两个不同的缓冲器:

CPU写SBUF ─→ 发送缓冲器 ─→ 发送移位寄存器 ─→ P3.1/TXD

CPU读SBUF ←─ 接收缓冲器 ←─ 接收移位寄存器 ←─ P3.0/RXD

因此SBUF=data表示发送,data=SBUF表示读取已经接收的数据。读写方向由硬件地址译码自动区分。

1.1 中断处理函数

接收中断从哪里产生

串口方式1的接收器先在RXD上检测空闲高电平到起始低电平的跳变,再用定时器1提供的16倍采样节拍对数据位进行采样。接收一帧有效数据后,硬件把8位数据装入接收SBUF,并把SCON.RI置1。

RXD波形:空闲1 │ START0 │ D0 │ D1 │ D2 │ D3 │ D4 │ D5 │ D6 │ D7 │ STOP1
                   │                                                │
                   └─ 检测起始跳变                   在停止位中部完成判定
                                                                    │
                                          数据装入接收SBUF,RI=1 ───┘

RI是SCON的B0位,是接收中断请求标志,不是独立寄存器。硬件负责置1,软件负责清0:

/* 片段目的:在确认接收完成后清除RI,并把接收SBUF中的一个字节保存到recv。 */
if (RI == 1)       /* 查询SCON.0;为1说明UART已经接收完一帧有效数据。 */
{                  /* 接收处理分支开始,RI为0时整个分支被跳过。 */
    RI = 0;        /* 按配套程序顺序先软件清除RI,为下一次硬件置位做准备。 */
    recv = SBUF;   /* 读地址0x99会选择内部接收缓冲器,并把8位数据复制到recv。 */
}                  /* 本字节转存完成,后续逻辑可以安全判断recv。 */

配套程序先清RI再立即读取SBUF。在9600波特下,一帧约需1.04ms,而这两条指令的执行时间远短于下一帧到达时间,因此能够正常工作。也常见先读取SBUF再清RI的写法;关键是进入中断后立即保存数据并及时清除标志,不能让RI长期保持为1。

ES=1和EA=1构成串口中断的两级允许开关:

$ \text{进入串口中断的条件}=(RI\lor TI)\land ES\land EA $

串口发送标志TI和接收标志RI共用中断号4、向量地址0x0023。所以进入UART_Routine后必须判断标志来源,不能认为每次进入都一定是接收完成。当前代码只在RI=1时执行接收分支;发送部分采用查询TI并在主程序中清零。

更完整的中断框架可以写成:

/*
 * 函数目的:展示同时兼顾接收与发送标志的完整串口中断骨架。
 * 结构依据:TI和RI共用中断号4,因此使用两个独立if,不能只凭进入中断判断事件类型。
 */
void UART_Routine(void) interrupt 4  /* 声明经典8051串口中断服务函数。 */
{                                    /* 中断处理开始;两个标志可能有一个或同时置位。 */
    if (RI == 1)                     /* 检查是否发生了接收完成事件。 */
    {                                /* 接收分支开始。 */
        RI = 0;                      /* 软件清除RI,为下一帧接收准备。 */
        recv = SBUF;                 /* 读取接收缓冲器并立即保存本次数据。 */
        /* 在这里快速解析recv,或只设置标志让主循环稍后处理。 */
    }                                /* 接收分支结束,继续检查TI。 */

    if (TI == 1)                     /* 检查是否发生了发送完成事件。 */
    {                                /* 发送分支开始。 */
        TI = 0;                      /* 软件清除TI,撤销本次发送请求。 */
        /* 中断发送方案可在这里装入下一个字节;查询发送方案则通常由发送函数清TI。 */
    }                                /* 发送分支结束。 */
}                                    /* 两类标志处理完毕,退出中断服务程序。 */

如果发送仍完全采用UART_Send_Byte中的查询方式,TI由发送函数清除即可;但正式工程要避免让TI在已开启ES的情况下长期为1,否则CPU可能反复响应同一个串口中断请求。

读取数据并保存到变量

/* 片段目的:提供独立于硬件SBUF的8位存储位置,供后续程序判断接收命令。 */
uchar recv;  /* uchar等价于unsigned char,可完整保存UART方式1接收的一个8位数据字节。 */

recv用于保存一个8位接收字节。recv=SBUF执行后,即使接收硬件继续工作,程序也可以使用recv进行判断。经典8051串口通常没有多字节接收FIFO,如果中断处理不及时且RI没有清除,后续帧可能无法装入接收SBUF而丢失。

中断服务程序应尽量短,不适合在里面执行长延时、等待按键或阻塞发送大量字符串。当前switch只进行寄存器赋值,执行时间很短,适合作为中断内的简单控制;复杂任务可以在中断中只保存命令并设置标志,再交给主循环处理。

HEX发送与文本发送不是同一个字节

switch中的分支使用0x01到0x09,因此串口助手应选择HEX方式发送。例如在HEX输入框发送01,单片机收到的才是数值0x01。

串口助手操作实际发送字节能否匹配case 0x1
HEX方式输入010x01能
文本方式输入字符10x31不能
HEX方式输入310x31不能

字符'1'的ASCII码是0x31,与数值1不同。如果希望同时支持文本命令,可以增加case '1':,或者在接收后把ASCII数字转换为数值:

/* 片段目的:把文本方式收到的ASCII数字字符'0'~'9'转换成数值0~9。 */
if ((recv >= '0') && (recv <= '9'))  /* 同时检查下界0x30和上界0x39,确保recv确为数字字符。 */
{                                    /* 只有ASCII数字进入转换分支,其他字节保持原值。 */
    recv = recv - '0';               /* 利用ASCII连续性减去0x30,例如'7'转换为数值7。 */
}                                    /* 一次可选的文本命令转换结束。 */

使用switch解析命令

switch(recv)根据接收字节跳转到相应case。每个分支末尾使用break,防止继续执行后面的分支:

命令字节执行操作控制电平
0x01点亮LED1P1.0=0
0x02点亮LED2P1.1=0
0x03点亮LED3P1.2=0
0x04点亮LED4P1.3=0
0x05熄灭4个LEDP1.0~P1.3=1
0x06蜂鸣器响P1.6=0
0x07蜂鸣器停止P1.6=1
0x08继电器模块吸合P2.0=0
0x09继电器模块释放P2.0=1
其他值不执行动作进入default

Keil的View → Templates Window可以打开代码模板窗口,再插入switch基本结构。模板只提供语法骨架,switch表达式、各个case值和实际控制语句仍需按功能填写。

LED引脚与低电平点亮原理

程序把4个板载LED定义在P1.0~P1.3:

/* 片段目的:为四个可位寻址的LED控制引脚建立易读的软件名称。 */
sbit LED1 = P1^0;  /* LED1连接P1.0,赋0点亮、赋1熄灭。 */
sbit LED2 = P1^1;  /* LED2连接P1.1,采用相同的低电平有效接法。 */
sbit LED3 = P1^2;  /* LED3连接P1.2,名称赋值会直接改变该端口位。 */
sbit LED4 = P1^3;  /* LED4连接P1.3,与P1低4位中的最高一位对应。 */

开发板上的每一路LED均从+5V经LED和10kΩ限流电阻连接到单片机端口,因此单片机输出低电平时形成电流通路:

+5V ─→ LED ─→ 10kΩ限流电阻 ─→ P1.x输出低电平 ─→ 芯片内部下拉 ─→ GND
         │
         └─ 有电流通过,LED点亮

P1.x输出高电平:电阻两端电压很小,几乎无电流,LED熄灭

所以代码采用LED1=0点亮,而不是LED1=1。限流电阻用于限制LED电流,避免LED和单片机端口过流;电阻越大,电流越小、亮度通常越低。

命令0x05使用:

/* 片段目的:一次把P1.0~P1.3全部置1,从而熄灭四个低电平有效LED。 */
P1 |= 0x0F;  /* 与0000 1111B按位或只强制低4位为1,P1.4~P1.7保持原状态。 */

0x0F的二进制为0000 1111B,按位或会把P1寄存器的低4位全部置1,同时保持高4位当前值:

P1原值: b7 b6 b5 b4 b3 b2 b1 b0
0x0F:     0  0  0  0  1  1  1  1
按位或: b7 b6 b5 b4  1  1  1  1
                      └P1.0~P1.3全部输出高电平,4灯熄灭

它设置的是低4位B0~B3,不是单独设置“第4位”B4。与逐句执行LED1=LED2=LED3=LED4=1相比,按位操作更紧凑,并且不会改变P1.4~P1.7。

蜂鸣器电路组成与低电平有效原理

开发板蜂鸣器并不是由P1.6直接供电,而是通过S9012 PNP三极管控制。电路包含:

元件连接与作用
Q1 S9012PNP三极管,作为高边开关并提供电流放大
R3 1kΩ串联在P1.6与三极管基极之间,限制基极电流
BELL1板载蜂鸣器,导通供电后发声
C6 100nF连接在+5V和GND之间,抑制蜂鸣器开关造成的高频电源扰动
                         +5V
                          │
                    E ┌───┴───┐
P1.6 ── R3 1kΩ ───── B│ Q1    │  S9012 PNP高边开关
                      └───┬───┘
                          C
                          │
                       BELL1
                          │
                         GND

                  C6 100nF
              +5V ──||── GND

当P1.6=1时,三极管基极与发射极都接近+5V,基极-发射极电压不足,Q1截止,蜂鸣器无供电而停止。设置P1.6=0后,基极被R3拉低,PNP三极管导通,电流路径为:

+5V → Q1发射极 → Q1集电极 → BELL1 → GND

因此:

/* 片段目的:展示P1.6通过PNP高边开关控制蜂鸣器时的低电平有效关系。 */
BEEP = 0;  /* P1.6拉低Q1基极,S9012导通并给蜂鸣器供电,因此蜂鸣器响。 */
BEEP = 1;  /* P1.6接近Q1发射极电位,S9012截止,蜂鸣器失去供电而停止。 */

使用三极管的原因是蜂鸣器工作电流可能超过单片机IO适合直接承担的电流。IO只需提供较小的基极控制电流,就能控制较大的蜂鸣器电流,也减少蜂鸣器对芯片引脚的影响。

继电器模块的三根控制线

本实验使用外接继电器模块,控制端连接:

STC89C52/开发板                 继电器模块控制端

P2.0  ───────────────────────→ IN(控制信号)
+5V   ───────────────────────→ VCC(模块供电)
GND   ───────────────────────→ GND(共同参考地)

sbit JDQ1=P2^0;必须与实际杜邦线连接一致。若把信号线接到P2.4,软件也必须定义为P2^4;软件写的是哪个端口位,硬件信号线就必须接到哪个引脚。

视频使用的模块表现为低电平触发:

/* 片段目的:展示课程所用低电平触发继电器模块的输入控制关系。 */
JDQ1 = 0;  /* P2.0向模块IN输出低电平有效信号,使驱动电路给线圈通电并吸合。 */
JDQ1 = 1;  /* P2.0撤销低电平触发,使线圈断电并让机械触点释放。 */

低电平触发继电器模块的典型内部结构如下,具体元件型号和是否带光耦以模块实物为准:

控制侧
P2.0/IN ─→ 输入限流电阻 ─→ 三极管或光耦驱动 ─→ 继电器线圈 ─→ +5V
                                            │
                                  线圈两端并联续流二极管
                                  吸收断电时的反向感应电压

触点侧
公共端COM ──机械触点── 常开端NO
          └─机械触点── 常闭端NC

各部分作用如下:

部分作用
输入电阻限制控制端电流,保护IO和驱动器件
三极管/光耦用小控制电流驱动较大的线圈电流;光耦型模块还能提供一定信号隔离
继电器线圈通电后产生磁场,拉动机械触点
续流二极管在线圈断电时为感应电流提供通路,抑制高压尖峰
COM/NO/NC构成与单片机控制侧分开的被控回路触点

控制端GND必须与开发板GND建立共同参考,否则模块无法可靠判断P2.0是高还是低。继电器触点侧可能用于切换更高电压,但入门实验应只操作安全低电压负载;市电接线必须由具备资质和安全条件的人员完成,不能在通电状态下触摸或临时插拔。

完整接收控制流程

电脑发送HEX命令01~09
           │
           ▼
RXD收到一帧 ─→ UART硬件装入SBUF并置RI
           │
           ▼
CPU进入interrupt 4
           │
           ▼
判断RI=1 ─→ 软件清RI ─→ recv=SBUF
           │
           ▼
switch(recv)
   ├─ 01~04:拉低对应LED引脚
   ├─ 05:P1低4位置1,熄灭全部LED
   ├─ 06/07:拉低/拉高P1.6,控制蜂鸣器
   ├─ 08/09:拉低/拉高P2.0,控制继电器
   └─ 其他值:default,不执行动作

编译前应确认变量名为RI而不是相似的误拼写,并检查每个sbit定义与原理图、杜邦线位置一致。Delay_xms(1000)放在主循环中用于降低主动发送数据的频率,也使此前“函数定义但未调用”的warning消失;它不能放进串口中断服务程序,否则会长时间阻塞其他中断和接收任务。

完成代码后,先关闭串口助手并烧写6.3串口接收工程,再打开串口助手,选择9600、8N1和HEX发送。依次发送01到09,观察LED、蜂鸣器和继电器是否与命令表一致。若某个外设无响应,应按“收到的字节值→switch分支→端口定义→实际连线→低电平有效电路”的顺序排查。