一、引言:从单机到互联的跨越

在掌握了中断技术后,我们的单片机已经具备了"感知环境,及时响应"的能力。然而,真正的智能系统不仅需要感知,更需要沟通。本次课程将带您进入单片机与外部世界通信的新领域:

通过异步串行通信实现单片机与PC的数据交换

运用串口中断实现高效数据收发

构建双向控制系统:既接收指令又上传状态

"串口是单片机的'嘴巴'和'耳朵',让机器学会与人类和其他设备交流。"

二、串口通信:异步传输的精妙设计

1. 串行 vs. 并行:效率与成本的平衡

特性

并行通信

串行通信

数据线数量

8根或更多

2根(TXD/RXD)

传输速率

高(短距离)

适中(任意距离)

抗干扰能力

弱(易受干扰)

强(差分信号可选)

成本

高(线材多)

低(线材少)

适用场景

内部模块短距通信

远距离设备间通信

                 并行通信示意图                                        串行通信的示意图

2. 异步串行通信协议解析

[起始位] [数据位(5-8)] [校验位(可选)] [停止位(1-2)]
  • 波特率:通信速度(如9600bps表示每秒9600位)
  • 数据格式:通常采用8N1(8数据位、无校验、1停止位)
  • 电平标准:TTL电平(0-5V)或RS-232(±12V)

3. 串行通信的传输模式

 串行通信按照按照数据传输的方向及时间关系可分为单工、半双工和全双工。

1.单工

    数据传输仅能按一个固定方向传输,不能反向传输

2.半双工

    数据传输可双向传输,但不能同时传输

3.全双工

    数据传输可同时进行双向传输

三、硬件升级:添加串口通信电路

1. 串口通信硬件连接

[单片机TXD] --- [电平转换芯片] --- [PC RXD]
[单片机RXD] --- [电平转换芯片] --- [PC TXD]
[GND]        --- [电平转换芯片] --- [PC GND]

2. Proteus中的串口组件

  • VIRTUAL TERMINAL (虚拟终端,用于仿真调试)
  • COMPIM (串口组件,连接实际硬件)
  • MAX232 (电平转换芯片,TTL↔RS232)

3. 完整串口控制流水灯电路

为确保速率计算的准确性,我们将单片机主频设置为11.0592MHz,并将RX引脚连接至虚拟终端的TXD,TX引脚连接至RXD。

四、51单片机串口机制原理

1. 串口相关寄存器

SCON寄存器(串口控制)

符号

功能说明

7

SM0

工作模式选择位0

6

SM1

工作模式选择位1

5

SM2

多机通信控制位

4

REN

接收使能位

3

TB8

发送第9数据位

2

RB8

接收第9数据位

1

TI

发送中断标志

0

RI

接收中断标志

PCON寄存器(电源控制)

  • SMOD位:波特率加倍控制位

2. 串口工作模式

  • 模式0:同步移位寄存器(常用于扩展I/O)
  • 模式1:8位UART,可变波特率(最常用)
  • 模式2:9位UART,固定波特率
  • 模式3:9位UART,可变波特率

3. 波特率计算

定时器1作为波特率发生器时:

波特率 = (2^SMOD / 32) × (定时器1溢出率)

常用波特率设置(12MHz晶振):

// 9600bps @12MHz
TH1 = 0xFD;   // 重装值
TL1 = 0xFD;
PCON &= 0x7F; // SMOD=0

用定时器T1产生的常用波特率

五、串口通信基础实现

1. 串口初始化配置

串行口初始化设置具体步骤如下:

  1. 设置波特率:
  2. ¨a.设置T1工作在计数方式2
  3. ¨b.设置T1计数初值
  4. ¨c.开启T1计数(不能开启T1中断)
  5. 设置串口工作方式SCON:工作方式1
  6. 开启串口中断(或查询方式)
  7. 等待中断,或发送后等待中断(或查询)
  8. 处理中断,接收或发送数据
void UART_Init(void)
{
    SCON = 0x50;      // 模式1,8位UART,允许接收
    TMOD &= 0x0F;     // 清除T1控制位
    TMOD |= 0x20;     // T1模式2:8位自动重装
    TH1 = 0xFD;       // 9600bps @12MHz
    TL1 = 0xFD;
    PCON &= 0x7F;     // SMOD=0
    ES = 1;           // 允许串口中断
    EA = 1;           // 全局中断允许
    TR1 = 1;          // 启动定时器1
}

2. 查询方式发送数据

void UART_SendByte(unsigned char dat)
{
    SBUF = dat;           // 数据写入发送缓冲区
    while(!TI);           // 等待发送完成
    TI = 0;               // 清除发送中断标志
}

void UART_S极String(unsigned char *str)
{
    while(*str != '\0')   // 遍历字符串
    {
        UART_SendByte(*str++); // 发送每个字符
    }
}

3. 中断方式接收数据

unsigned char uart_buf[32];    // 接收缓冲区
unsigned char uart_index = 0;  // 缓冲区索引

void UART_ISR(void) interrupt 4
{
    if(RI)                  // 接收中断
    {
        RI = 0;            // 清除接收中断标志
        uart_buf[uart_index] = SBUF; // 保存接收数据
        
        if(uart_buf[uart_index] == '\n' || uart_index >= 31)
        {
            uart_buf[uart_index] = '\0'; // 字符串终止
            uart_index = 0;
            // 设置数据接收完成标志
        }
        else
        {
            uart_index++;
        }
    }
    
    if(TI)                  // 发送中断
    {
        TI = 0;            // 清除发送中断标志
        // 可在此添加发送完成处理
    }
}

六、PC与单片机双向通信系统

1. 通信协议设计

设计简单的指令协议:

  • 'L':查询LED状态
  • 'S':设置流水灯速度(后跟数字)
  • 'D':设置流水灯方向(0左移/1右移)
  • 'R':复位系统

2. 完整串口控制程序

#include <reg51.h>
#include <stdio.h>

/* 全局变量声明 */
volatile unsigned int time_count = 0;
volatile bit key_pressed = 0;
volatile bit uart_cmd_ready = 0;

unsigned char led = 0x01;        // 初始LED状态:最低位亮
bit direction = 0;               // 0: 左移, 1: 右移
unsigned int speed = 100;        // LED移动速度(单位:ms)
unsigned char uart_buffer[32];   // 串口接收缓冲区

/* 函数声明 */
void Timer0_Init(void);
void ExtInt_Init(void);
void UART_Init(void);
void Process_UART_Command(void);

/******************************
 * 主函数
 ******************************/
void main()
{
    static unsigned int last_update = 0;

    // 初始化外设
    Timer0_Init();
    ExtInt_Init();
    UART_Init();

    // 启动提示
    printf("System Ready!\r\n");
    printf("Commands: L-Status, S-Speed, D-Direction, R-Reset\r\n");

    while (1)
    {
        // 检测按键中断(外部中断0)
        if (key_pressed)
        {
            direction = !direction;  // 按键切换方向
            key_pressed = 0;
            printf("Direction changed to: %s\r\n", direction ? "Right" : "Left");
        }

        // 处理串口命令
        if (uart_cmd_ready)
        {
            Process_UART_Command();
            uart_cmd_ready = 0;
        }

        // 定时更新LED状态
        if ((time_count - last_update) >= speed)
        {
            if (direction)
            {
                // 向右移动(从高位到低位)
                led = (led == 0x01) ? 0x80 : (led >> 1);
            }
            else
            {
                // 向左移动(从低位到高位)
                led = (led == 0x80) ? 0x01 : (led << 1);
            }
            P1 = ~led;  // P1接LED,低电平点亮,故取反
            last_update = time_count;
        }
    }
}

/******************************
 * 串口命令处理函数
 ******************************/
void Process_UART_Command(void)
{
    switch (uart_buffer[0])
    {
        case 'L': // 查询当前状态
            printf("LED: 0x%02X, Direction: %s, Speed: %dms\r\n",
                   led, direction ? "Right" : "Left", speed);
            break;

        case 'S': // 设置速度,例如:S200
            sscanf(uart_buffer + 1, "%d", &speed);
            printf("Speed set to: %dms\r\n", speed);
            break;

        case 'D': // 设置方向:D1=右移,D0=左移
            direction = (uart_buffer[1] == '1');
            printf("Direction set to: %s\r\n", direction ? "Right" : "Left");
            break;

        case 'R': // 复位系统
            led = 0x01;
            direction = 0;
            speed = 100;
            P1 = ~led;
            printf("System reset\r\n");
            break;

        default:
            printf("Unknown command: %s\r\n", uart_buffer);
            break;
    }
}

/******************************
 * 串口中断服务函数(接收中断)
 ******************************/
void UART_ISR(void) interrupt 4
{
    static unsigned char index = 0;
    unsigned char received;

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

        // 判断是否为结束符(回车或换行)或缓冲区满
        if (received == '\r' || received == '\n' || index >= 31)
        {
            if (index > 0)
            {
                uart_buffer[index] = '\0';     // 添加字符串结束符
                uart_cmd_ready = 1;            // 标记命令就绪
                index = 0;                     // 重置索引
            }
        }
        else
        {
            uart_buffer[index++] = received;   // 存入缓冲区
        }
    }

    if (TI)  // 发送中断(一般用于发送完成标志,此处仅清标志)
    {
        TI = 0;
    }
}

/******************************
 * Timer0 初始化(用于定时更新LED)
 * 晶振:11.0592MHz,定时1ms
 ******************************/
void Timer0_Init(void)
{
    TMOD &= 0xF0;      // 清除T0模式位
    TMOD |= 0x01;      // T0工作在模式1(16位定时器)
    TH0 = 0xFC;        // 1ms定时初值(11.0592MHz)
    TL0 = 0x18;
    ET0 = 1;           // 使能T0中断
    EA  = 1;           // 开总中断
    TR0 = 1;           // 启动T0
}

/******************************
 * 外部中断0 初始化(按键切换方向)
 ******************************/
void ExtInt_Init(void)
{
    IT0 = 1;           // 下降沿触发
    EX0 = 1;           // 使能外部中断0
    EA  = 1;
}

/******************************
 * 串口初始化(工作在模式1,8位UART,可变波特率)
 * 波特率:9600bps(11.0592MHz晶振)
 ******************************/
void UART_Init(void)
{
    SCON  = 0x50;      // 模式1,8位UART,允许接收
    TMOD &= 0x0F;      // 清除T1模式位
    TMOD |= 0x20;      // T1工作在模式2(8位自动重载)
    TH1   = 0xFD;      // 9600bps 波特率(11.0592MHz)
    TL1   = 0xFD;
    PCON &= 0x7F;      // 波特率不倍增
    ES    = 1;         // 使能串口中断
    EA    = 1;
    TR1   = 1;         // 启动T1
}

七、调试技巧与故障排除

1. 串口调试常见问题

问题现象

可能原因

解决方案

乱码

波特率不匹配

检查双方波特率设置

数据丢失

缓冲区溢出

增加接收缓冲区大小

无法通信

电平不匹配

使用MAX232进行电平转换

接收不完整

未处理帧错误

检查硬件连接和波特率

2. 使用虚拟终端调试

在Proteus中使用VIRTUAL TERMINAL组件:

  • 设置与程序相同的波特率
  • 可实时显示发送接收数据
  • 支持发送指令测试

3. 实际硬件调试要点

  • 确保共地连接(GND连接)
  • 使用示波器检查TXD/RXD信号
  • 通过LED指示灯显示通信状态

八、结语:开启物联的大门

通过本次串口通信实践,我们实现了单片机与外部世界的关键连接:

双向通信能力:既能接收指令又能上传状态

实时交互系统:PC端可实时监控和控制单片机

协议设计基础:为更复杂的通信协议打下基础

"串口通信是物联网的起点,从这里开始,单片机不再是信息孤岛,而是智能网络中的节点。"

学习拓展建议

  1. 实现数据日志功能:将系统状态保存并通过串口上传
  2. 添加图形界面:在PC端开发GUI控制程序
  3. 探索其他通信方式:I2C、SPI、CAN等总线协议
  4. 尝试无线通信:蓝牙、Wi-Fi、LoRa等无线模块

下篇预告

《LCD显示技术:从字符到菜单的视觉呈现》

探索内容:

  • 字符型LCD1602驱动原理
  • 自定义字符设计技巧
  • 多级菜单界面实现

更多推荐