串口通讯模块
串口通讯模块
- 串口通讯模块
- 一、并行通讯(Parallel Communication)
- **1. 定义**
- **2. 工作原理**
- **3. 优缺点**
- **优点**
- **缺点**
- **4. 应用场景**
- 二、串行通讯(Serial Communication)
- **1. 定义**
- **2. 工作原理**
- **3. 优缺点**
- **优点**
- **缺点**
- **4. 应用场景**
- 三、并行通讯与串行通讯的对比
- 四、选择并行通讯还是串行通讯
- **选择标准**
- **实际应用案例**
- 五、总结
- 串口通讯底层逻辑:
- 格式化发送
- **1. 头文件包含**
- **2. 串口初始化函数**
- **功能概述**
- **详细解释**
- **3. 字节发送函数**
- **功能概述**
- **详细解释**
- **4. 字符串发送函数**
- **功能概述**
- **详细解释**
- **5. 代码总结与注意事项**
- **总体流程**
- **关键点**
- **6. 波特率计算与验证**
- **步骤**
- **结论**
- **结论**
并行通讯与串行通讯详解
在电子通信领域,并行通讯(Parallel Communication)和串行通讯(Serial Communication)是两种基本的数据传输方式。它们在数据传输的方式、速度、距离、成本等方面各有优缺点,广泛应用于不同的场景和设备中。以下将详细介绍这两种通讯方式的定义、工作原理、优缺点及其应用。
一、并行通讯(Parallel Communication)
1. 定义
并行通讯是指在同一时间内通过多条数据线同时传输多个比特的数据。例如,一个8位并行通讯系统会使用8条数据线,每条线传输1位数据,从而在一个时钟周期内传输8位数据。
2. 工作原理
在并行通讯中,数据通过多条独立的数据线同时传输,每条线对应数据的一位。除了数据线,还通常需要额外的控制线用于时钟同步、信号确认等。例如,常见的并行接口包括:
- 并行打印接口(如旧式打印机的并行端口)
- 内部总线(如计算机主板上的数据总线)
- 微控制器的并行I/O端口
3. 优缺点
优点
- 高速传输:由于多个比特同时传输,并行通讯在短距离内可以实现较高的数据传输速率。
- 低延迟:由于数据同时传输,数据传输的延迟较低,适用于需要实时响应的应用。
缺点
- 成本高:需要多条数据线和复杂的布线,增加了成本和系统复杂性。
- 受距离限制:并行传输容易受到信号干扰和时序偏差的影响,适用于短距离传输。
- 同步问题:多条数据线需要严格的同步,时钟偏差可能导致数据错误。
4. 应用场景
- 内部总线:计算机主板上的数据总线、地址总线和控制总线。
- 打印机接口:早期的并行打印接口(如IEEE 1284标准)。
- 高速数据传输:在需要高速传输且传输距离较短的内部设备间通信。
二、串行通讯(Serial Communication)
1. 定义
串行通讯是指数据按位(bit)依次通过单一的数据线传输。每个时钟周期只传输1位数据,但通过时钟同步或其他协议,可以实现连续高速的数据传输。
2. 工作原理
在串行通讯中,数据位按顺序依次通过一条数据线传输。串行通讯可以分为以下几种类型:
- 同步串行通讯:发送方和接收方共享一个时钟信号,确保数据位的同步传输。例如,SPI(Serial Peripheral Interface)、I²C(Inter-Integrated Circuit)。
- 异步串行通讯:发送方和接收方各自使用独立的时钟,通过起始位和停止位来同步数据传输。例如,UART(Universal Asynchronous Receiver/Transmitter)、RS-232。
- 半双工与全双工:串行通讯可以是单向(单工)、双向但不能同时传输(半双工)或双向同时传输(全双工)。
3. 优缺点
优点
- 成本低:只需要一条或少量的数据线,降低了布线复杂性和成本。
- 适用长距离:由于信号线少,串行通讯更适合长距离传输,抗干扰能力更强。
- 简化设计:布线简单,易于实现和维护,适用于多种设备间的通信。
缺点
- 传输速度较低:相比并行通讯,单条数据线的传输速率较低,虽然通过高级协议和高速串行接口可以弥补这一点。
- 时序复杂:需要复杂的协议和时钟同步机制,确保数据的正确传输。
4. 应用场景
- 计算机与外围设备通信:如USB(Universal Serial Bus)、串口(COM端口)。
- 微控制器间通信:如UART、SPI、I²C,用于传感器、显示器、存储器等外设的连接。
- 网络通讯:如以太网、光纤通信,利用高速串行传输实现数据网络。
- 消费电子设备:如蓝牙、Wi-Fi等无线串行通信协议。
三、并行通讯与串行通讯的对比
特性 | 并行通讯 | 串行通讯 |
---|---|---|
数据线数量 | 多条(每位一条) | 一条或少量 |
传输速率 | 高(适用于短距离) | 适中至高(适用于长距离,通过高级协议) |
成本 | 高(多条数据线和复杂布线) | 低(少量数据线,简化布线) |
适用距离 | 短距离 | 长距离 |
抗干扰能力 | 较差(多条线易受干扰) | 较强(少量线,信号更易管理) |
复杂性 | 高(需要精确同步和多线布线) | 低至中(依赖协议和时钟同步) |
应用实例 | 计算机内部总线、并行打印机接口 | USB、UART、SPI、I²C、以太网、无线通信协议 |
四、选择并行通讯还是串行通讯
选择并行通讯还是串行通讯主要取决于应用需求,包括数据传输速率、传输距离、成本限制、系统复杂性等因素。
选择标准
-
数据传输速率需求
- 高速度且短距离:并行通讯更为合适,如计算机内部总线。
- 中高速度且可接受较长距离:串行通讯,通过高级串行协议如USB 3.0、Thunderbolt等也能实现高速传输。
-
传输距离
- 短距离:并行通讯适用,如板级通信。
- 长距离:串行通讯更佳,如网络通信、工业控制。
-
成本与复杂性
- 预算有限或设计需简化:串行通讯更具成本效益和设计简便性。
- 无需考虑成本且可处理复杂布线:并行通讯可提供更高的传输速率。
-
设备兼容性
- 已有标准协议:如USB、UART等,通常采用串行通讯。
- 特定应用需求:某些工业或高性能计算需求可能倾向于并行通讯。
实际应用案例
-
并行通讯:
- 内部总线:如计算机的PCIe(Peripheral Component Interconnect Express)总线,尽管现代PCIe使用的是高速串行通信,但其初衷来源于并行总线的发展。
- 并行打印机接口:早期的打印机通常使用并行端口连接计算机。
-
串行通讯:
- USB:广泛用于连接计算机与各种外设,如鼠标、键盘、存储设备等。
- UART:常用于微控制器间的通信,如开发板之间的数据传输。
- SPI与I²C:用于嵌入式系统中连接传感器、显示器、存储器等外设。
- 以太网与光纤通信:实现计算机网络和远距离数据传输。
- 无线通信协议:如Bluetooth、Wi-Fi,采用串行数据传输方式。
五、总结
并行通讯和串行通讯各有其独特的优势和适用场景。并行通讯以其高传输速率和低延迟在短距离内表现出色,适用于计算机内部总线和高速设备间的通信。然而,其高成本、复杂布线和对同步的严格要求限制了其在更广泛应用中的使用。
相反,串行通讯以其低成本、简化布线和较强的抗干扰能力在长距离传输和多种设备间的通信中占据主导地位。通过先进的串行协议和高速串行接口,串行通讯在现代电子设备中得到了广泛应用,成为连接各种外设和实现网络通信的首选方式。
在实际设计和应用中,选择并行通讯还是串行通讯应根据具体需求、系统限制和成本考量进行权衡。随着技术的发展,串行通讯因其灵活性和扩展性,逐渐在更多领域取代了传统的并行通讯方式。
串口通讯底层逻辑:
#include <Uart.h>
/* 串口初始化函数 */
void UartInit(void) //9600bps@12.000MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x01; //串口1选择定时器2为波特率发生器
AUXR |= 0x04; //定时器2时钟为Fosc,即1T
T2L = 0xC7; //设定定时初值
T2H = 0xFE; //设定定时初值
AUXR |= 0x10; //启动定时器2
ES=1;
EA=1;
}
/* 字节发送函数 */
void SendByte(dat)
{
SBUF=dat;
while(TI==0);
TI=0;
}
/* 字符串发送函数 */
void Uart_Send_String(unsigned char *dat)
{
while(*dat!='\0')
{
SendByte(*dat++);
}
}
#include <Uart.h>
unsigned char Uart_Send[10];//串口接收数据储存数组 默认10个字节
unsigned char Uart_Recv[10];//串口接收数据储存数组 默认10个字节
/* 串口1中断服务函数 */
void UartServer() interrupt 4
{
if(RI==1)
{
Uart_Recv[Uart_Recv_Index]==SBUF;
Uart_Recv_Index++;
RI=0;
}
}
格式化发送
#include <stdio.h>
sprint(Uart_Send,"格式=%2.f",变量);
Uart_Send_String(Uart_Send);
详解 UART 通讯代码
以下是您提供的用于 UART(通用异步收发传输器) 通讯的代码片段。该代码主要包括串口初始化函数、字节发送函数和字符串发送函数。以下将逐行详细解释代码的功能、工作原理及其背后的硬件概念。
1. 头文件包含
#include <Uart.h>
- 作用:包含名为
Uart.h
的头文件,通常用于声明与 UART 通讯相关的宏、常量、函数原型等。 - 注意:确保
Uart.h
文件存在于项目目录中,并包含必要的定义。如果未包含,编译器会报错。
2. 串口初始化函数
/* 串口初始化函数 */
void UartInit(void) //9600bps@12.000MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x01; //串口1选择定时器2为波特率发生器
AUXR |= 0x04; //定时器2时钟为Fosc,即1T
T2L = 0xC7; //设定定时初值
T2H = 0xFE; //设定定时初值
AUXR |= 0x10; //启动定时器2
ES = 1;
EA = 1;
}
功能概述
UartInit
函数用于初始化微控制器的串口(UART)通讯设置,使其能够以 9600 bps 的速率进行数据传输。该函数配置了串口控制寄存器、辅助寄存器以及定时器,以生成所需的波特率。
详细解释
-
函数声明
void UartInit(void) //9600bps@12.000MHz
void UartInit(void)
:定义一个不接受参数且不返回值的函数,用于初始化 UART 设置。- 注释:指明初始化后的波特率为 9600 bps,并假设系统时钟频率为 12.000 MHz。
-
设置串口控制寄存器
SCON = 0x50; //8位数据,可变波特率
SCON
(Serial Control Register):控制串口的工作模式和接收发送状态。0x50
的二进制表示:0101 0000
- SM0 (位7):0
- SM1 (位6):1
- SM2 (位5):0
- REN (位4):1
- TB8 (位3):0
- RB8 (位2):0
- TI (位1):0
- RI (位0):0
- 解释:
- SM0 和 SM1:设置串口工作在 模式1,即 8位数据,可变波特率。
- 模式1:8位数据,波特率由定时器控制,可支持多种波特率。
- REN(Receive Enable):使能接收功能。
- 其余位:初始化为默认状态,发送和接收标志位清零。
- SM0 和 SM1:设置串口工作在 模式1,即 8位数据,可变波特率。
-
选择波特率发生器
AUXR |= 0x01; //串口1选择定时器2为波特率发生器
AUXR
(Auxiliary Register):辅助寄存器,用于扩展微控制器的功能。0x01
:二进制0000 0001
- bit0:设置串口 1 使用 定时器2 作为波特率发生器。
- 操作:
AUXR |= 0x01;
:将 AUXR 的 bit0 设置为 1,选择 定时器2 作为串口的波特率发生器。
- 原因:定时器2 通常能够提供更灵活和精确的波特率控制,适合需要多样波特率设置的应用。
-
配置定时器2的时钟源
AUXR |= 0x04; //定时器2时钟为Fosc,即1T
0x04
:二进制0000 0100
- bit2:设置定时器2的时钟源。
- 操作:
AUXR |= 0x04;
:将 AUXR 的 bit2 设置为 1,选择 Fosc(系统时钟) 作为定时器2的时钟源,即 1T(一个时钟周期)。
- 解释:
- Fosc:系统主时钟频率。
- 1T:定时器在每个时钟周期内递增一次计数。
-
设置定时器2的初值
T2L = 0xC7; //设定定时初值 T2H = 0xFE; //设定定时初值
T2L
和T2H
:定时器2的低8位和高8位寄存器。- 目的:设定定时器2的初值,以生成所需的波特率。
- 计算波特率:
-
系统时钟:12 MHz,即一个时钟周期 1/12,000,000 ≈ 83.33 ns。
-
定时器2在1T模式下的计数频率:12 MHz。
-
波特率计算公式:
波 特 率 = F osc ( 256 − TH2 ⋅ 256 − TL2 ) × 2 {波特率} = \frac{F_{\text{osc}}}{(256 - \text{TH2} \cdot 256 - \text{TL2}) \times 2} 波特率=(256−TH2⋅256−TL2)×2Fosc -
设定值:
TH2 = 0 x F E = 254 {\text{TH2} = 0xFE = 254 } TH2=0xFE=254KaTeX parse error: Undefined control sequence: \ at position 30: …= 0xC7 = 199 \̲ ̲
KaTeX parse error: Undefined control sequence: \ at position 48: … 199 = 65023 \̲ ̲
KaTeX parse error: Undefined control sequence: \ at position 69: …2 \text{ ms} \̲ ̲
KaTeX parse error: Undefined control sequence: \ at position 4: \̲ ̲ \text{波特率} = …
-
问题:上述计算与注释中的 9600 bps 不符。可能的原因包括:
- 波特率计算公式:不同芯片对定时器溢出后的操作可能不同。
- 芯片特定:此定时器2的配置可能与标准8051不同,或有额外的分频。
- 代码片段不完整:可能需要更多的上下文来准确计算波特率。
-
解决方案:
- 根据具体的微控制器数据手册,确认定时器2的波特率计算方式。
- 确认定时器2的重装载值是否为
0xFE C7
。 - 若定时器2的配置确实生成了9600 bps,则可能存在内部倍频或其他寄存器的设置影响。
-
-
启动定时器2
AUXR |= 0x10; //启动定时器2
0x10
:二进制0001 0000
- bit4:启动定时器2。
- 操作:
AUXR |= 0x10;
:将 AUXR 的 bit4 设置为 1,启动 定时器2,使其开始计数。
- 解释:启动定时器2后,定时器将根据设定的初值开始递增,并在溢出时生成中断或执行其他操作。
-
使能中断
ES = 1; EA = 1;
ES
(Serial Interrupt Enable):串口中断使能位。ES = 1;
:使能串口中断,允许串口中断请求被处理。
EA
(Global Interrupt Enable):全局中断使能位。EA = 1;
:使能全局中断,允许所有可用的中断被响应。
- 解释:
- 串口中断:在数据接收或发送完成时触发中断,以便微控制器处理相关事件。
- 全局中断:必须使能全局中断才能使局部中断(如串口中断)生效。
3. 字节发送函数
/* 字节发送函数 */
void SendByte(dat)
{
SBUF = dat;
while(TI == 0);
TI = 0;
}
功能概述
SendByte
函数用于通过串口发送单个字节的数据。该函数将数据写入串口缓冲寄存器,并等待发送完成。
详细解释
-
函数声明
void SendByte(dat)
- 函数名:
SendByte
- 参数:
dat
,表示要发送的字节数据。由于类型未明确指定,通常假设为unsigned char
或char
。 - 返回类型:
void
,不返回任何值。
- 函数名:
-
写入数据到串口缓冲寄存器
SBUF = dat;
SBUF
(Serial Buffer Register):串口数据缓冲寄存器。- 作用:将要发送的数据字节写入
SBUF
寄存器,触发串口发送硬件开始发送数据。 - 解释:当数据写入
SBUF
后,硬件自动将数据逐位通过串口发送线发送出去。
-
等待发送完成
while(TI == 0);
TI
(Transmit Interrupt Flag):发送完成标志位。- 作用:循环等待,直到
TI
被置为 1,表示数据发送完成。 - 解释:发送完成后,硬件自动将
TI
置为 1,跳出循环。
-
清除发送完成标志位
TI = 0;
- 作用:清除
TI
标志位,为下次发送做准备。 - 解释:需要在每次发送完成后手动清除
TI
,否则后续发送可能无法正确检测到发送完成。
- 作用:清除
4. 字符串发送函数
/* 字符串发送函数 */
void Uart_Send_String(unsigned char *dat)
{
while(*dat != '\0')
{
SendByte(*dat++);
}
}
功能概述
Uart_Send_String
函数用于通过串口发送一个以 空字符 ('\0'
) 结尾的字符串。它依次调用 SendByte
函数,将字符串中的每个字符发送出去。
详细解释
-
函数声明
void Uart_Send_String(unsigned char *dat)
- 函数名:
Uart_Send_String
- 参数:
unsigned char *dat
,指向要发送的字符串的指针。 - 返回类型:
void
,不返回任何值。
- 函数名:
-
循环发送字符串
while(*dat != '\0') { SendByte(*dat++); }
while(*dat != '\0')
:- 条件:检查当前指针指向的字符是否为 空字符(字符串结束标志)。
- 作用:持续循环,直到遇到字符串的结束标志。
SendByte(*dat++);
:*dat
:解引用指针,获取当前字符。SendByte(*dat++)
:调用SendByte
函数发送当前字符,然后将指针dat
增加 1,指向下一个字符。
- 解释:通过逐字符发送,实现整个字符串的串口发送。
5. 代码总结与注意事项
总体流程
-
初始化 UART:
- 配置串口控制寄存器
SCON
,设置串口工作模式。 - 选择定时器2作为波特率发生器,并配置定时器2的时钟源和初值。
- 启动定时器2并使能串口中断。
- 波特率:根据设定的定时器2初值,实现 9600 bps 的串口通讯。
- 配置串口控制寄存器
-
发送数据:
- 发送单个字节:
- 将数据写入
SBUF
寄存器。 - 等待发送完成标志位
TI
被置位。 - 清除
TI
标志位。
- 将数据写入
- 发送字符串:
- 遍历字符串,逐个发送字符,直到遇到空字符。
- 发送单个字节:
关键点
-
定时器与波特率:
- 波特率的生成依赖于定时器的配置。正确设置定时器2的初值对于实现准确的波特率至关重要。
- 定时器2的初值
T2H
和T2L
需要根据系统时钟和目标波特率进行精确计算。波特率误差可能导致通讯不稳定或数据错误。
-
中断使能:
- 虽然代码中使能了串口中断 (
ES = 1;
) 和全局中断 (EA = 1;
),但并未在此代码片段中提供中断服务程序(ISR)。若使用中断处理数据接收或发送,则需要定义相应的 ISR。 - 在当前发送函数中,发送是通过轮询方式完成的(等待
TI
标志位),未使用中断方式。
- 虽然代码中使能了串口中断 (
-
数据类型:
SendByte
函数中的参数dat
类型未明确声明。为避免潜在问题,建议明确参数类型,如:void SendByte(unsigned char dat)
-
函数命名一致性:
- 在
UartInit
函数中,调用了Delay12us()
,而在Wave_Init
函数中调用了Delay_12us()
。确保函数名的一致性,避免编译错误。
- 在
-
宏定义与常量:
0xC7
和0xFE
作为定时器2的初值,最好通过宏定义或注释详细说明其来源或计算依据,便于维护和理解。
6. 波特率计算与验证
为了确保串口通讯达到 9600 bps,需要验证定时器2的初值是否正确配置。以下是基于 12 MHz 系统时钟的波特率计算过程:
步骤
-
系统时钟频率:12 MHz
- 周期时间: { ( T = 1 12 , 000 , 000 ≈ 83.33 ns } \{( T = \frac{1}{12,000,000} \approx 83.33 \text{ ns} \} {(T=12,000,0001≈83.33 ns})
-
定时器2的时钟源:Fosc,1T,即每个时钟周期为 83.33 ns。
-
定时器2的初值设置:
- T2H = 0xFE = 254
- T2L = 0xC7 = 199
- 定时器2的总计数: { ( 254 × 256 + 199 = 65023 } \{( 254 \times 256 + 199 = 65023 \} {(254×256+199=65023})
- 定时器溢出时间:
- { ( 65024 12 , 000 , 000 ≈ 5.418 ms ) } \{( \frac{65024}{12,000,000} \approx 5.418 \text{ ms} )\} {(12,000,00065024≈5.418 ms)}
-
波特率计算:
- 波特率公式:
KaTeX parse error: Undefined control sequence: \ at position 1: \̲ ̲ \text{波特率} = … - 替换数值:
KaTeX parse error: Undefined control sequence: \ at position 1: \̲ ̲ \text{波特率} = … - 结果:约 10,568 bps,高于目标 9600 bps。
- 波特率公式:
-
调整初值以实现9600 bps
-
目标波特率:9600 bps
-
波特率公式重排:
[ 波特率 = F osc ( 256 × ( 256 − T H 2 ) + ( 256 − T L 2 ) ) × 2 ] [ \text{波特率} = \frac{F_{\text{osc}}}{(256 \times (256 - TH2) + (256 - TL2)) \times 2} ] [波特率=(256×(256−TH2)+(256−TL2))×2Fosc]
KaTeX parse error: Undefined control sequence: \ at position 1: \̲ ̲ (256 \times (… -
设定:
KaTeX parse error: Undefined control sequence: \ at position 1: \̲ ̲ 256 \times (2…- 选择 TH2 和 TL2:
- 假设 TH2 = 255:
KaTeX parse error: Undefined control sequence: \ at position 1: \̲ ̲ 256 \times (2…- 不合理:TL2 为负数,超出8位范围。
- 假设 TH2 = 254:
KaTeX parse error: Undefined control sequence: \ at position 1: \̲ ̲ 256 \times 2 … - 结果:
- T2H = 254 = 0xFE
- T2L = 143 = 0x8F
- 假设 TH2 = 255:
- 选择 TH2 和 TL2:
-
更新定时器2的初值:
T2L = 0x8F; //设定定时初值为143 T2H = 0xFE; //设定定时初值为254
-
验证波特率:
[
波特率 = 12 , 000 , 000 ( 256 × 2 + ( 256 − 143 ) ) × 2 = 12 , 000 , 000 ( 512 + 113 ) × 2 = 12 , 000 , 000 625 × 2 = 9600 bps \text{波特率} = \frac{12,000,000}{(256 \times 2 + (256 - 143)) \times 2} = \frac{12,000,000}{(512 + 113) \times 2} = \frac{12,000,000}{625 \times 2} = 9600 \text{ bps} 波特率=(256×2+(256−143))×212,000,000=(512+113)×212,000,000=625×212,000,000=9600 bps
]
-
结论
为了实现 9600 bps 的波特率,定时器2的初值应设置为 T2H = 0xFE 和 T2L = 0x8F。因此,您提供的代码中的 T2L = 0xC7 和 T2H = 0xFE 生成的波特率约为 10,568 bps,需要调整 T2L 的值以达到准确的 9600 bps。
54 = 0xFE**
- T2L = 143 = 0x8F
- **更新定时器2的初值**:
```c
T2L = 0x8F; //设定定时初值为143
T2H = 0xFE; //设定定时初值为254
```
- **验证波特率**:
\[
$\text{波特率} = \frac{12,000,000}{(256 \times 2 + (256 - 143)) \times 2} = \frac{12,000,000}{(512 + 113) \times 2} = \frac{12,000,000}{625 \times 2} = 9600 \text{ bps}$
\]
结论
为了实现 9600 bps 的波特率,定时器2的初值应设置为 T2H = 0xFE 和 T2L = 0x8F。因此,您提供的代码中的 T2L = 0xC7 和 T2H = 0xFE 生成的波特率约为 10,568 bps,需要调整 T2L 的值以达到准确的 9600 bps。