目录

 

一.串口简介

二.串口通讯协议

2.1 串口通信方式

2.2 串口通信硬件电路

2.3 串口参数及时序

2.3.1 串口相关参数

2.3.2 串口通信时序

2.3.2.1 数据帧

2.3.2.2 停止位

三.串口的基本结构

四. 代码示例

4.1 串口配置流程

4.2 串口单字节收发(不带校验位)

4.3 串口单数据收发(带偶校验位)

五. 附录

5.1 ASCII码表

六. 结语


一.串口简介

串口(Serial Port)是一种全双工数据通信接口,广泛应用于嵌入式系统和计算机外设之间的数据交换。它通常用于两台设备之间的点对点通信,并且可以通过简单的线缆连接,极大地方便了设备间的连接和数据传输。串口通信的最大特点是通过单根数据线顺序传输数据,即每次传输一个比特(bit),因此被称为“串行通信”。

串口通信是一种高效、简单的点对点通信方式,其设计思想简单,适合低速、长距离的数据传输。通过配置不同的通信参数(如波特率、数据位、校验位、停止位等),可以满足多种不同的通信需求。对于嵌入式开发人员来说,掌握串口通信的基本原理和使用方法,对于设备间的交互和调试具有重要意义。尽管如今有更高速的通信方式(如USB、以太网等),串口依然在许多低速、低功耗设备中保持着广泛的应用。

二.串口通讯协议

2.1 串口通信方式

  • 同步通信:发送方和接收方共享时钟信号,数据传输是同步进行的,数据帧之间不需要间隔时间。
  • 异步通信:数据发送方和接收方之间不共享时钟信号,接收方根据固定的波特率(数据传输速度)来估算数据的时序。每个数据帧之间需要有间隔时间,接收方通过起始位来确定数据的开始。
  • USART:支持同步通信,异步通信。
  • UART:只支持异步通信。

2.2 串口通信硬件电路

  • VCC:供电端,需按照实际要求接入电源。
  • GND:接地端。
  • TX:数据发送端,接另一模块的RX端。
  • RX:数据接收端,接另一模块的TX端。

注意事项:

  • 若单片机和其他模块都有单独供电,则可不接VCC。
  • 如果只需要单向的数据传输,则可以只接一根数据通信线(RX或TX)。
  • TX,RX在两个模块之间交叉连接。
  • TX,RX的电平都是相对于GND而言的。
  • 当通信双方电平标准不一致时,需要添加电平转换芯片。例:单片机与电脑通信。

以下是STM32中常见的电平标准,在单片机中,最常见的是TTL电平,之后我们都以TTL进行讲解。

2.3 串口参数及时序

2.3.1 串口相关参数

  1. 波特率:串口通信的速率,常见的波特率有9600 Baud,19200 Baud,115200 Baud等。
  2. 起始位:标志着一个数据帧的开始,固定为低电平。
  3. 数据位:低位先行(LSB),数据位的有效载荷,1为高电平,0为低电平,可配置数据位长度(8/9)。通常以8居多。
  4. 校验位:用于数据验证,分为无校验/奇校验/偶校验。
  5. 停止位:用于两个数据帧之间的间隔,固定位高电平。长度可配置0.5/1/1.5/2。通常使用1居多。
  6. 支持同步模式,硬件流控制,DMA,智能卡,lrDA,LIN。

2.3.2 串口通信时序

USART/UART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里,自带波特率发生器,最高达4.5Mbits/s。

2.3.2.1 数据帧

校验位可根据需求进行设置,若不进行设置,可能的奇偶校验位就是有效的数据位之一,用来承载数据,这里推荐的配置为

  • 9位字长:8位数据位 + 1位校验位
  • 8位字长:8位数据位,无校验位
2.3.2.2 停止位

可以配置成 0.5/1/1.5/2 位停止位,一般情况下,如无特殊需求,一般配置成1位。

三.串口的基本结构

核心结构

  • 发送数据寄存器(TDR) + 发送移位寄存器

        通过写操作,将数据写入TDR,写入完成后,硬件就会自动检查发送移位寄存器中是否有数据正在进行移位,有则等待,若没有,则数据就会立刻转入发送移位寄存器,此时会将标志位TXE(发送寄存器空)置1,代表TDR为空,新转入的数据进入准备发送状态。此时发送移位寄存器就会在下面的发送器控制下,将数据输出到TX引脚。这样可以保证连续发送数据的时候,数据帧之间不会有空闲,提高了工作效率。

  • 接收数据寄存器(RDR) + 接收移位寄存器

        通过RX引脚接收到的数据会进入接收移位寄存器,数据由高位向低位逐渐移位,然后全部保存到接收数据寄存器(RDR),当完成一个字节的移位后,会将标志位RXNE(接收寄存器非空)置1,通过标志位,就可以对数据进行读取处理。

  • 硬件数据流控(了解)

        什么是硬件数据流控?

        如果数据发送的太快来不及接收,就会出现丢弃或数据覆盖现象,当使用流控时,就可以避免这个问题,nRTS为输出引脚,输出低电平0有效,对方的nCTS接收到低电平后,就可以继续发送数据,反之1为无效,就会暂停发送;nCTS为输入引脚,用于接收对方发来的nRTS信号。其中两者名字前的n代表低电平有效。

每一帧帧头的起始位,帧尾的停止位等,硬件电路会自动进行剔除。

波特率发生器

总工作流程示意图(来源:江科大自化协)

四. 代码示例

4.1 串口配置流程

4.2 串口单字节收发(不带校验位)

#include "stm32f10x.h"

// 串口初始化函数
void USART1_Init(void) {
    // 使能USART1和GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    // 初始化GPIOA的PA9和PA10为USART1的TX和RX
    GPIO_InitTypeDef GPIO_InitStruct;
    
    // 配置PA9为USART1的TX (推挽输出)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; // TX
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 推挽输出
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 配置PA10为USART1的RX (浮空输入)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10; // RX
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 配置USART1的参数
    USART_InitTypeDef USART_InitStruct;
    USART_InitStruct.USART_BaudRate = 9600;  // 波特率9600
    USART_InitStruct.USART_WordLength = USART_WordLength_8b; // 8位数据位
    USART_InitStruct.USART_StopBits = USART_StopBits_1; // 1个停止位
    USART_InitStruct.USART_Parity = USART_Parity_No; // 不使用校验位
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 设置为收发模式
    USART_Init(USART1, &USART_InitStruct);
    
    // 使能USART1,准备开始通信
    USART_Cmd(USART1, ENABLE);
}

// 发送一个字节数据
void USART1_SendData(uint8_t data) {
    // 等待直到发送数据寄存器为空(TXE标志位清零表示发送缓冲区为空)
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    
    // 发送数据
    USART_SendData(USART1, data);
}

// 接收一个字节数据
uint8_t USART1_ReceiveData(void) {
    // 等待直到接收数据寄存器非空(RXNE标志位置位表示有接收数据)
    while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
    
    // 返回接收到的数据
    return USART_ReceiveData(USART1);
}

int main(void) {
    // 初始化USART1
    USART1_Init();
    
    uint8_t receivedData;
    
    while (1) {
        // 等待接收一个字节的数据
        receivedData = USART1_ReceiveData();
        
        // 将接收到的数据发送回去(即回显)
        USART1_SendData(receivedData);
    }
}

4.3 串口单数据收发(带偶校验位)

#include "stm32f10x.h"

// 串口初始化函数(带校验位)
void USART1_Init(void) {
    // 使能USART1和GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    // 配置GPIO
    GPIO_InitTypeDef GPIO_InitStruct;

    // 配置PA9为TX(推挽输出)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 配置PA10为RX(浮空输入)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 配置USART1(带校验位)
    USART_InitTypeDef USART_InitStruct;
    USART_InitStruct.USART_BaudRate = 9600;  // 波特率9600
    USART_InitStruct.USART_WordLength = USART_WordLength_9b; // 9位数据位(包括校验位)
    USART_InitStruct.USART_StopBits = USART_StopBits_1; // 1个停止位
    USART_InitStruct.USART_Parity = USART_Parity_Even;  // 偶校验(改为Odd可使用奇校验)
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 设置为收发模式
    USART_Init(USART1, &USART_InitStruct);

    // 使能USART1
    USART_Cmd(USART1, ENABLE);
}

// 发送一个字节(带校验位)
void USART1_SendData(uint8_t data) {
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 等待TXE标志位
    
    // 由于使用校验位,需要发送9位数据
    USART_SendData(USART1, (uint16_t)data);
}

// 接收一个字节(带校验位)
uint8_t USART1_ReceiveData(void) {
    while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET); // 等待RXNE标志位
    
    // 由于数据位是9位,接收时要去掉最高位
    return (uint8_t)(USART_ReceiveData(USART1) & 0xFF);
}

int main(void) {
    // 初始化USART1(带校验位)
    USART1_Init();
    
    uint8_t receivedData;
    
    while (1) {
        // 接收数据
        receivedData = USART1_ReceiveData();
        
        // 将接收到的数据发送回去(回显)
        USART1_SendData(receivedData);
    }
}

五. 附录

5.1 ASCII码表

六. 结语

串口多搭配DMA来一起使用,我们下节进行讲解。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐