STC15单片机双串口通信实战:手把手教你配置串口2(附完整代码)
2026/6/4 1:02:16 网站建设 项目流程

STC15单片机双串口通信实战:手把手教你配置串口2(附完整代码)

在嵌入式开发中,串口通信是最基础也最常用的功能之一。STC15系列单片机因其高性价比和丰富的外设资源,成为许多初学者和工程师的首选。当项目需要同时连接多个设备时,比如一个串口接传感器采集数据,另一个串口连接蓝牙模块传输数据,单串口就显得捉襟见肘了。这时,STC15系列提供的串口2功能就能大显身手。

与常见的串口1不同,串口2在使用上有几个关键区别:它使用定时器2作为波特率发生器,中断号为8,且相关寄存器配置方式也有所不同。这些差异常常成为初学者踩坑的重灾区。本文将带你从零开始,一步步完成串口2的配置与使用,避开那些常见的"雷区"。

1. 硬件准备与基础概念

在开始编码之前,我们需要确认几个硬件相关的重要事项。首先,并非所有STC15系列单片机都支持串口2功能,常见的支持型号包括STC15F2K60S2、STC15W4K56S4等。在选型时,务必查阅官方手册确认芯片是否具备该功能。

串口2与串口1的主要区别体现在以下几个方面:

  • 波特率发生器:串口1通常使用定时器1,而串口2使用定时器2
  • 中断号:串口1中断号为4,串口2为8
  • 寄存器配置:串口2的相关寄存器(如S2CON)大多不可位寻址,需要特别注意操作方式

下表对比了串口1和串口2的关键参数差异:

特性串口1串口2
波特率发生器定时器1定时器2
中断号48
数据寄存器SBUFS2BUF
控制寄存器SCON(可位寻址)S2CON(不可位寻址)

提示:在操作不可位寻址的寄存器时,必须使用位操作(与、或)来设置或清除特定位,而不能直接赋值。

2. 串口2初始化配置

串口2的初始化是整个通信功能的基础,正确的配置才能保证后续通信的稳定性。下面我们详细拆解初始化过程的每个步骤。

首先,我们需要配置波特率。以常用的9600bps为例,假设使用22.1184MHz的晶振,定时器2的初值计算如下:

// 定时器2初值计算 #define FOSC 22118400L // 系统频率 #define BAUD 9600 // 波特率 #define T2_TIMER (65536 - (FOSC/4/BAUD)) // 定时器2重装值

实际初始化函数如下:

void Uart2_Init(void) { S2CON = 0x50; // 8位数据,可变波特率,允许接收 AUXR |= 0x04; // 定时器2时钟为Fosc,即1T模式 T2L = T2_TIMER; // 定时器2低8位 T2H = T2_TIMER >> 8; // 定时器2高8位 AUXR |= 0x10; // 启动定时器2 IE2 = 0x01; // 允许串口2中断 EA = 1; // 开启总中断 }

这段代码有几个关键点需要注意:

  1. S2CON寄存器:设置为0x50表示选择8位数据模式,可变波特率,并允许接收
  2. AUXR寄存器:第2位(0x04)设置定时器2为1T模式,第4位(0x10)启动定时器2
  3. 中断配置:IE2的最低位置1允许串口2中断,EA开启总中断

注意:STC15的寄存器操作需要特别注意时序。例如,定时器2的启动(AUXR |= 0x10)必须在初值设置完成后进行。

3. 数据发送与接收实现

串口通信的核心功能就是数据的发送和接收。与串口1相比,串口2在这方面的实现有一些特殊之处需要特别注意。

3.1 单字节发送

发送单个字节是串口通信最基本的操作。由于S2CON寄存器不可位寻址,我们需要使用位操作来管理发送标志位:

void UART2_Send_Byte(unsigned char dat) { IE2 &= ~0x01; // 临时关闭接收中断 S2CON &= ~S2TI; // 清除发送中断标志 S2BUF = dat; // 数据写入发送缓冲区 while((S2CON & S2TI) == 0); // 等待发送完成 S2CON &= ~S2TI; // 清除发送中断标志 IE2 |= 0x01; // 重新开启接收中断 }

这段代码中的几个关键操作:

  1. 临时关闭接收中断是为了防止在发送过程中被接收中断打断
  2. S2TI是发送中断标志位,需要先清除再等待发送完成
  3. 发送完成后必须再次清除标志位

3.2 字符串发送

基于单字节发送函数,我们可以轻松实现字符串发送功能:

void Uart2_Send_String(unsigned char *str) { while(*str != '\0') { UART2_Send_Byte(*str++); } }

3.3 中断接收实现

串口2的中断服务函数与串口1不同,其中断号为8。下面是一个基本的接收中断实现:

unsigned char UART2_Rx_Buf[64]; // 接收缓冲区 unsigned char UART2_Rx_Index = 0; // 缓冲区索引 void UART2_Interrupt() interrupt 8 { if(S2CON & S2RI) { // 检查接收中断标志 S2CON &= ~S2RI; // 清除接收中断标志 UART2_Rx_Buf[UART2_Rx_Index++] = S2BUF; // 读取接收数据 if(UART2_Rx_Index >= sizeof(UART2_Rx_Buf)) { UART2_Rx_Index = 0; // 防止缓冲区溢出 } } }

这个中断服务函数实现了基本的接收功能,将接收到的数据存入缓冲区。在实际项目中,你可能还需要添加以下功能:

  • 接收完成标志(如检测到回车符表示一帧数据结束)
  • 数据校验机制
  • 环形缓冲区实现以提高效率

4. 实战应用与常见问题排查

掌握了基本功能后,我们来看几个实际应用场景和常见问题的解决方法。

4.1 双串口协同工作示例

假设我们需要用串口1连接PC进行调试,串口2连接蓝牙模块,实现数据的转发功能:

void main() { Uart1_Init(); // 初始化串口1 Uart2_Init(); // 初始化串口2 Uart1_Send_String("System Ready\r\n"); while(1) { if(UART1_Rx_Flag) { // 串口1收到数据 UART1_Rx_Flag = 0; Uart2_Send_String(UART1_Rx_Buf); // 转发到串口2 } if(UART2_Rx_Flag) { // 串口2收到数据 UART2_Rx_Flag = 0; Uart1_Send_String(UART2_Rx_Buf); // 转发到串口1 } } }

4.2 常见问题与解决方案

在实际使用中,开发者常会遇到以下问题:

  1. 通信乱码

    • 检查晶振频率设置是否正确
    • 确认通信双方的波特率一致
    • 检查定时器2初值计算是否正确
  2. 无法进入中断

    • 确认中断号是否正确(串口2是8)
    • 检查IE2和EA是否已正确设置
    • 确保中断服务函数声明正确
  3. 数据丢失

    • 增加接收缓冲区大小
    • 提高中断优先级
    • 优化中断服务函数执行时间
  4. 寄存器操作无效

    • 确认寄存器是否可位寻址
    • 检查操作顺序是否符合要求
    • 查阅手册确认寄存器功能描述

调试技巧:在初期调试时,可以先用串口1打印调试信息,帮助定位串口2的问题所在。

5. 进阶应用与性能优化

当基本功能实现后,我们可以考虑进一步优化和扩展串口2的功能。

5.1 DMA方式数据传输

对于高性能应用,STC15的部分型号支持DMA方式传输数据,可以大幅提高传输效率:

// 假设使用STC15W4K系列支持DMA的功能 void UART2_DMA_Send(unsigned char *buf, unsigned int len) { DMA_UART2_CFG = 0x80; // 使能DMA DMA_UART2_BUF = buf; // 设置缓冲区地址 DMA_UART2_LEN = len; // 设置数据长度 DMA_UART2_CR = 0x01; // 启动传输 }

5.2 自定义通信协议

在实际项目中,我们通常需要定义自己的通信协议。以下是一个简单的帧结构示例:

| 帧头(0xAA) | 长度(1字节) | 数据(N字节) | 校验和(1字节) |

对应的解析代码:

void UART2_Protocol_Parse(unsigned char dat) { static unsigned char state = 0; static unsigned char len = 0; static unsigned char sum = 0; static unsigned char cnt = 0; static unsigned char buf[64]; switch(state) { case 0: // 等待帧头 if(dat == 0xAA) { state = 1; sum = 0; } break; case 1: // 获取长度 len = dat; sum += dat; cnt = 0; state = 2; break; case 2: // 接收数据 buf[cnt++] = dat; sum += dat; if(cnt >= len) { state = 3; } break; case 3: // 校验 if(sum == dat) { Process_Protocol(buf, len); // 处理有效数据 } state = 0; break; } }

5.3 低功耗设计

对于电池供电设备,合理的串口配置可以降低功耗:

void UART2_LowPower_Init() { S2CON = 0x50; // 基本配置 AUXR |= 0x04; // 定时器2 1T模式 T2L = 0xE8; // 低波特率配置 T2H = 0xFF; AUXR |= 0x10; // 启动定时器2 IE2 = 0x01; // 允许中断 EA = 1; // 配置唤醒功能 WAKE_CLKO |= 0x02; // 允许串口2唤醒 }

这种配置下,单片机可以在收到串口数据时自动唤醒,平时则保持低功耗状态。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询