当前位置: 首页 > news >正文

STM32F103C8T6_UART串口通信完整教程

STM32F103C8T6 UART串口通信完整教程:从入门到精通

适用芯片: STM32F103C8T6(Blue Pill)
难度等级: 入门到进阶


📋 目录

  1. UART协议基础理论
  2. STM32F103 UART硬件资源
  3. UART寄存器详解
  4. UART配置与初始化
  5. 硬件电路连接
  6. 标准库开发实战
  7. HAL库开发实战
  8. 高级应用与优化
  9. 常见问题与调试技巧

1. UART协议基础理论

1.1 什么是UART?

UART(Universal Asynchronous Receiver/Transmitter,通用异步收发传输器)是一种串行、异步、全双工的通信协议。与SPI、I2C等同步协议不同,UART不需要时钟信号,通过约定的波特率进行数据传输。

1.2 UART协议特点

  • 异步通信: 不需要时钟信号,只需要波特率一致
  • 全双工通信: 可以同时发送和接收数据
  • 简单协议: 硬件实现简单,软件易于控制
  • 广泛应用: 广泛用于串口通信、调试、设备间通信
  • 长距离传输: 可以使用RS232/RS485等标准延长传输距离

1.3 UART信号线定义

UART通信最少只需要2根线:

信号线名称全称方向说明
TXTransmitter主机→从机发送数据线
RXReceiver主机←从机接收数据线
GNDGround-地线(必需)

注意事项:

  • TX连接对方的RX,RX连接对方的TX(交叉连接)
  • 必须共地才能正常工作

1.4 UART数据帧格式

UART数据帧由以下几部分构成:

┌─────┬──────┬─────┬──────┬─────┐
│起始位│数据位│校验位│停止位│空闲位│
│  0  │ 5-9  │ 0-1  │  1-2 │ 高  │
└─────┴──────┴─────┴──────┴─────┘

各部分详细说明

  1. 起始位(Start Bit):

    • 固定为低电平(逻辑0)
    • 表示数据帧的开始
    • 持续1个波特率周期
  2. 数据位(Data Bits):

    • 可以是5、6、7、8或9位
    • 最常用的是8位
    • 先发送最低位(LSB),后发送最高位(MSB)
  3. 校验位(Parity Bit)(可选):

    • 无校验(None): 不使用校验位
    • 奇校验(Odd): 使数据位+校验位的"1"的总数为奇数
    • 偶校验(Even): 使数据位+校验位的"1"的总数为偶数
  4. 停止位(Stop Bit):

    • 固定为高电平(逻辑1)
    • 持续1个或2个波特率周期
    • 表示数据帧的结束
  5. 空闲位(Idle):

    • 传输间隔期间保持高电平
    • 确保下次起始位能被正确识别

1.5 波特率(Baud Rate)

波特率定义

  • 波特率表示每秒传输的符号(位)数
  • 单位:bps(bits per second)
  • 常用的波特率:9600, 19200, 38400, 57600, 115200 等

波特率计算公式

STM32F103的UART波特率通过以下公式配置:

波特率 = fCK / (USARTDIV × 16)其中:
- fCK: USART时钟频率
- USARTDIV: 分频因子

1.6 UART工作流程

发送流程

  1. 发送器将数据写入发送数据寄存器(TDR/DR)
  2. 数据从TDR传送到发送移位寄存器
  3. 按照LSB先发的顺序,逐位发送数据
  4. 添加起始位、停止位、校验位(如果启用)
  5. 通过TX引脚输出数据

接收流程

  1. 检测到RX引脚的下降沿(起始位)
  2. 按照约定的波特率采样数据位
  3. 数据从接收移位寄存器传送到接收数据寄存器(RDR/DR)
  4. 触发接收中断或标志位
  5. CPU读取接收到的数据

2. STM32F103 UART硬件资源

2.1 STM32F103C8T6 UART外设概述

STM32F103C8T6共有3个UART外设:

  • USART1: 全功能UART/USART(最高4.5Mbps)
  • USART2: 全功能UART/USART(最高2.25Mbps)
  • USART3: 全功能UART/USART(最高2.25Mbps)

UART vs USART

  • UART:只支持异步通信
  • USART:支持异步和同步通信(可以使用外部时钟)
  • STM32F103的USART可以通过配置只使用异步模式

2.2 UART引脚资源表

USART1引脚:

模式引脚功能复用重映射
标准PA9TX已启用
标准PA10RX已启用
重映射PB6TX需要重映射
重映射PB7RX需要重映射

USART2引脚:

引脚功能说明
PA2TX串口2发送
PA3RX串口2接收

USART3引脚:

模式引脚功能
标准PB10TX
标准PB11RX
部分重映射PC10TX
部分重映射PC11RX
完全重映射PD8TX
完全重映射PD9RX

2.3 UART时钟源配置

STM32F103的UART时钟源:

USART1: 挂载在APB2总线上 (最高72MHz)
USART2: 挂载在APB1总线上 (最高36MHz)
USART3: 挂载在APB1总线上 (最高36MHz)

波特率分频器配置

通过配置BRR寄存器来设置波特率分频:

BRR寄存器 = 16位
- 高4位:小数部分
- 低12位:整数部分

常用波特率配置表(72MHz系统时钟,USART1):

波特率分频比BRR值(Hex)
96007500x1D4C
192003750xEA6
38400187.50x753
576001250x4E2
11520062.50x271
23040031.250x139
46080015.6250x9D
9216007.81250x4F

3. UART寄存器详解

3.1 状态寄存器(USART_SR)

USART状态寄存器用于反映当前UART的状态:

名称说明
7TXE发送数据寄存器空:可以写入新数据
6TC传输完成:数据已完全发送
5RXNE接收数据寄存器非空:有数据可读
4IDLE空闲总线检测
3ORE溢出错误
2NE噪声错误
1FE帧错误
0PE校验错误

3.2 数据寄存器(USART_DR)

  • 写入操作:数据写入发送数据寄存器(TDR)
  • 读取操作:从接收数据寄存器(RDR)读取数据
  • 该寄存器为8位,但在9位数据长度模式下可扩展到9位

3.3 波特率寄存器(USART_BRR)

16位寄存器,用于配置波特率:

位15-4:Mantissa(整数部分)
位3-0:Fraction(小数部分)波特率 = fCK / (16 × USARTDIV)
USARTDIV = Mantissa + (Fraction/16)

3.4 控制寄存器1(USART_CR1)

名称说明
13UEUSART使能
12M字长度:0=8位,1=9位
10PCE奇偶校验使能
9PS奇偶选择:0=偶校验,1=奇校验
7TXEIETXE中断使能
6TCIETC中断使能
5RXNEIERXNE中断使能
3TE发送使能
2RE接收使能
0SBK发送断开帧

3.5 控制寄存器2(USART_CR2)

名称说明
13-12STOP停止位长度
5CLKEN时钟使能(同步模式)
3CPHA时钟相位
2CPOL时钟极性

3.6 控制寄存器3(USART_CR3)

名称说明
7DMATDMA发送使能
6DMARDMA接收使能
5SCEN智能卡模式使能
1IREN红外模式使能

4. UART配置与初始化

4.1 UART初始化步骤

  1. 使能时钟: 使能UART和GPIO的时钟
  2. 配置GPIO: 配置TX为复用推挽输出,RX为浮空输入
  3. 配置UART参数:
    • 波特率
    • 数据位长度
    • 停止位
    • 校验位
    • 硬件流控(可选)
  4. 使能UART: 使能UART外设
  5. 配置中断(可选): 使能发送/接收中断

4.2 波特率计算

STM32提供的便捷计算公式:

// 波特率计算公式
uint16_t usartdiv = (uint16_t)(SystemCoreClock / (baudrate * 16));
uint8_t div_fraction = (uint8_t)(usartdiv % 16);
uint8_t div_mantissa = (uint8_t)(usartdiv / 16);BRR = (div_mantissa << 4) | div_fraction;

4.3 常用配置参数

常用波特率

  • 9600: 低速通信,稳定可靠
  • 115200: 高速通信,常用调试
  • 921600: 超高速通信

数据位配置

  • 8位: 最常用,一个字节
  • 9位: 可用于多机通信

停止位配置

  • 1位: 标准配置
  • 0.5位: 不需要
  • 2位: 用于噪声环境

校验位配置

  • None: 无校验(常用)
  • Even: 偶校验
  • Odd: 奇校验

5. 硬件电路连接

5.1 STM32与电脑连接

STM32F103C8T6                USB转TTL模块
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━PA9 (TX)  ────────────> RXPA10(RX)  <──────────── TXGND       ────────────> GND3.3V      ────────────> VCC (可选)

注意事项

  • STM32的UART是3.3V电平
  • 必须使用3.3V的USB转TTL模块
  • 不要连接5V模块,会损坏STM32

5.2 两个STM32互相通信

STM32#1                    STM32#2
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━PA9 (TX)  ────────────> PA10(RX)PA10(RX)  <──────────── PA9 (TX)GND       ────────────> GND

重要: TX接RX,RX接TX(交叉连接)

5.3 通过RS232模块连接

STM32                    MAX232模块          电脑串口
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━TX ──────> T1IN ────> T1OUT ────> RDRX <────── R1OUT <──── R1IN <──── TD3.3V ─────> VCCGND ──────> GND

6. 标准库开发实战

6.1 GPIO配置

/*** USART1 GPIO初始化配置* TX: PA9* RX: PA10*/
void USART1_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;// 使能GPIOA和USART1时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);// 配置PA9为复用推挽输出(TX)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);// 配置PA10为浮空输入(RX)GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);
}

6.2 UART配置

/*** USART1初始化配置* 波特率: 115200* 数据位: 8* 停止位: 1* 校验位: 无* 流控: 无*/
void USART1_Config(void)
{USART_InitTypeDef USART_InitStructure;// USART初始化结构体配置USART_InitStructure.USART_BaudRate = 115200;                           // 波特率115200USART_InitStructure.USART_WordLength = USART_WordLength_8b;           // 8位数据USART_InitStructure.USART_StopBits = USART_StopBits_1;                // 1个停止位USART_InitStructure.USART_Parity = USART_Parity_No;                   // 无校验USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无流控USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;       // 接收和发送模式// 初始化USART1USART_Init(USART1, &USART_InitStructure);// 使能USART1USART_Cmd(USART1, ENABLE);
}

6.3 发送函数实现

/*** USART1发送一个字节* @param data: 要发送的数据*/
void USART1_SendByte(uint8_t data)
{// 等待发送数据寄存器空while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);// 发送数据USART_SendData(USART1, data);
}/*** USART1发送字符串* @param str: 要发送的字符串*/
void USART1_SendString(char *str)
{while(*str){USART1_SendByte(*str);str++;}
}/*** USART1发送数组* @param buf: 要发送的数据缓冲区* @param len: 数据长度*/
void USART1_SendBuffer(uint8_t *buf, uint16_t len)
{uint16_t i;for(i = 0; i < len; i++){USART1_SendByte(buf[i]);}
}/*** USART1格式化输出(简单版)* 支持 %d, %x, %c, %s*/
void USART1_Printf(const char *fmt, ...)
{char buffer[256];va_list args;va_start(args, fmt);vsnprintf(buffer, sizeof(buffer), fmt, args);va_end(args);USART1_SendString(buffer);
}

6.4 接收函数实现

/*** USART1接收一个字节(阻塞方式)* @return: 接收到的数据*/
uint8_t USART1_ReceiveByte(void)
{// 等待接收数据就绪while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);// 读取接收到的数据return USART_ReceiveData(USART1);
}/*** USART1非阻塞接收* @param data: 接收数据指针* @return: 1-成功, 0-失败*/
uint8_t USART1_ReceiveByte_NonBlocking(uint8_t *data)
{if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET){*data = USART_ReceiveData(USART1);return 1;}return 0;
}

6.5 中断配置

/*** USART1中断配置*/
void USART1_NVIC_Config(void)
{NVIC_InitTypeDef NVIC_InitStructure;// 配置USART1中断优先级NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// 使能接收中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);// 使能发送完成中断(可选)USART_ITConfig(USART1, USART_IT_TC, ENABLE);
}/*** USART1中断服务函数*/
void USART1_IRQHandler(void)
{uint8_t recv_data;// 接收中断if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){// 清除中断标志USART_ClearITPendingBit(USART1, USART_IT_RXNE);// 读取接收到的数据recv_data = USART_ReceiveData(USART1);// 处理接收到的数据// 例如:回传数据或处理命令USART1_SendByte(recv_data);}// 发送完成中断if(USART_GetITStatus(USART1, USART_IT_TC) != RESET){USART_ClearITPendingBit(USART1, USART_IT_TC);// 可以在这里更新发送完成标志}
}

6.6 主函数示例

#include "stm32f10x.h"extern void USART1_GPIO_Config(void);
extern void USART1_Config(void);
extern void USART1_SendByte(uint8_t data);
extern void USART1_SendString(char *str);
extern uint8_t USART1_ReceiveByte(void);int main(void)
{uint8_t recv_data;// 系统初始化SystemInit();// USART1初始化USART1_GPIO_Config();USART1_Config();// 发送测试信息USART1_SendString("STM32F103 UART Test!\r\n");while(1){// 接收数据recv_data = USART1_ReceiveByte();// 回传数据USART1_SendByte(recv_data);}
}

7. HAL库开发实战

7.1 HAL库UART初始化

#include "main.h"
#include "stm32f1xx_hal.h"UART_HandleTypeDef huart1;/*** USART1初始化(HAL库版本)*/
void MX_USART1_UART_Init(void)
{huart1.Instance = USART1;huart1.Init.BaudRate = 115200;                          // 波特率huart1.Init.WordLength = UART_WORDLENGTH_8B;           // 8位数据huart1.Init.StopBits = UART_STOPBITS_1;                // 1个停止位huart1.Init.Parity = UART_PARITY_NONE;                 // 无校验huart1.Init.Mode = UART_MODE_TX_RX;                    // 收发模式huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;           // 无流控huart1.Init.OverSampling = UART_OVERSAMPLING_16;       // 过采样16倍if(HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}
}/*** UART底层初始化(MSP回调函数)*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(huart->Instance == USART1){// 使能时钟__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();// 配置PA9(TX)GPIO_InitStruct.Pin = GPIO_PIN_9;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 配置PA10(RX)GPIO_InitStruct.Pin = GPIO_PIN_10;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 使能UART中断(可选)HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);HAL_NVIC_EnableIRQ(USART1_IRQn);}
}

7.2 HAL库发送接收

/*** HAL库:UART发送数据* @param data: 数据指针* @param size: 数据大小* @param timeout: 超时时间(ms)*/
void UART1_Transmit(uint8_t *data, uint16_t size, uint32_t timeout)
{HAL_UART_Transmit(&huart1, data, size, timeout);
}/*** HAL库:UART接收数据* @param data: 数据缓冲区* @param size: 数据大小* @param timeout: 超时时间(ms)*/
void UART1_Receive(uint8_t *data, uint16_t size, uint32_t timeout)
{HAL_UART_Receive(&huart1, data, size, timeout);
}/*** HAL库:中断方式发送*/
void UART1_Transmit_IT(uint8_t *data, uint16_t size)
{HAL_UART_Transmit_IT(&huart1, data, size);
}/*** HAL库:中断方式接收*/
void UART1_Receive_IT(uint8_t *data, uint16_t size)
{HAL_UART_Receive_IT(&huart1, data, size);
}/*** HAL库:DMA方式发送*/
void UART1_Transmit_DMA(uint8_t *data, uint16_t size)
{HAL_UART_Transmit_DMA(&huart1, data, size);
}/*** HAL库:DMA方式接收*/
void UART1_Receive_DMA(uint8_t *data, uint16_t size)
{HAL_UART_Receive_DMA(&huart1, data, size);
}

7.3 HAL库回调函数

/*** 发送完成回调函数*/
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1){// 发送完成处理}
}/*** 接收完成回调函数*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1){// 接收完成处理// 可以重新启动接收HAL_UART_Receive_IT(&huart1, buffer, 1);}
}/*** 错误回调函数*/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1){// 错误处理}
}/*** USART1中断服务函数*/
void USART1_IRQHandler(void)
{HAL_UART_IRQHandler(&huart1);
}

8. 高级应用与优化

8.1 DMA方式传输

/*** UART DMA发送配置*/
void UART1_DMA_Config(void)
{// DMA配置...HAL_UART_Transmit_DMA(&huart1, tx_buffer, tx_len);
}

8.2 环形缓冲区实现

#define UART_BUFFER_SIZE 256typedef struct
{uint8_t buffer[UART_BUFFER_SIZE];volatile uint16_t head;volatile uint16_t tail;
} uart_ring_buffer_t;uart_ring_buffer_t rx_buffer;/*** 环形缓冲区初始化*/
void RingBuffer_Init(uart_ring_buffer_t *rbuf)
{rbuf->head = 0;rbuf->tail = 0;
}/*** 写入数据到环形缓冲区*/
uint8_t RingBuffer_Put(uart_ring_buffer_t *rbuf, uint8_t data)
{uint16_t next = (rbuf->head + 1) % UART_BUFFER_SIZE;if(next == rbuf->tail)return 0; // 缓冲区满rbuf->buffer[rbuf->head] = data;rbuf->head = next;return 1;
}/*** 从环形缓冲区读取数据*/
uint8_t RingBuffer_Get(uart_ring_buffer_t *rbuf, uint8_t *data)
{if(rbuf->head == rbuf->tail)return 0; // 缓冲区空*data = rbuf->buffer[rbuf->tail];rbuf->tail = (rbuf->tail + 1) % UART_BUFFER_SIZE;return 1;
}

8.3 printf重定向

#include <stdio.h>/*** 重定向fputc函数到UART*/
int fputc(int ch, FILE *f)
{USART1_SendByte((uint8_t)ch);return ch;
}// 现在可以使用printf了
printf("Hello World! Value = %d\r\n", value);

9. 常见问题与调试技巧

9.1 常见问题排查

问题1: 接收不到数据

可能原因:

  1. TX和RX接反
  2. 波特率不匹配
  3. 没有共地
  4. GPIO配置错误

解决方法:

  • 检查TX接对方RX,RX接对方TX
  • 确保波特率一致
  • 确保GND连接
  • 检查GPIO是否配置为复用功能
问题2: 数据乱码

可能原因:

  1. 波特率错误
  2. 时钟配置错误
  3. 数据位/停止位不匹配

解决方法:

  • 重新配置波特率
  • 检查系统时钟配置
  • 确保参数匹配

9.2 调试技巧

使用逻辑分析仪
  • 抓取TX/RX波形
  • 验证波特率
  • 检查数据格式
使用串口助手
  • 设置正确的参数
  • 十六进制显示
  • 发送/接收测试

9.3 实际应用案例

案例1: 串口命令解析
void UART1_Command_Process(uint8_t cmd)
{switch(cmd){case '1':USART1_SendString("LED1 ON\r\n");GPIO_SetBits(GPIOA, GPIO_Pin_0);break;case '0':USART1_SendString("LED1 OFF\r\n");GPIO_ResetBits(GPIOA, GPIO_Pin_0);break;default:USART1_SendString("Unknown Command\r\n");break;}
}

📚 总结

本文档详细介绍了STM32F103C8T6的UART串口通信,包括:

  1. 理论基础: UART协议原理、帧格式、波特率
  2. 硬件资源: STM32 UART外设和引脚资源
  3. 寄存器配置: 详细解释了关键寄存器
  4. 实战编程: 提供了标准库和HAL库的完整代码
  5. 高级应用: DMA、环形缓冲区、printf重定向
  6. 调试技巧: 常见问题排查方法

希望本文档能帮助大家快速掌握STM32F103的UART通信!


祝您学习愉快,开发顺利! 🎉

http://www.dtcms.com/a/533225.html

相关文章:

  • Gorm(一)查询方法
  • 网站管理工具wordpress中文版和英文版区别
  • 新网网站空间到期停了 咋续费北海哪里做网站建设
  • 百日挑战-单词篇(第四天)
  • 6.1 操作系统的启动流程
  • 英语学习 第四天
  • Compose笔记(五十四)--Card
  • 西宁电商网站制作公司北京广告设计招聘
  • 阿里巴巴网站建设销售软件商店下载最新版
  • 交流耦合和直流耦合
  • 印刷厂网站建设方案利用网上菜谱做网站
  • Flutter 中, Flame + flame_forge2d世界坐标和屏幕坐标对齐
  • 石家庄建站网页模板siteservercms做的网站在后台进行修改教程
  • 基于单片机的楼道声光人体红外智能控制灯设计
  • 做热处理工艺的网站有哪些苏州优化外包
  • 给网站怎么做tag标签网站优化公司免费咨询
  • 苏州专业做网站的公司有哪些杭州市优化服务
  • 5 Simplified LPDDR6 State Diagram(简化LPDDR6状态图)
  • 慈利做网站在哪里怎么做免费网站 视频
  • 怎么做赌钱网站网站备案现场
  • 通胀数据加强降息预期 金价周五收于4100美元之上
  • 高级机器学习作业(一)岭回归 + 信息熵 + Sigmoid + Softmax + PCA
  • 莱州网站建设关键字排名优化网络托管微信代运营wordpress 前台 插件
  • Python uv虚拟环境管理工具详解
  • 西安网站制作网站是如何设计配置方案的
  • 线程互斥量
  • 【瑞芯微】【rk3128】【03.编写音频测试程序】
  • 台湾精准医疗计划:GWAS-summary statistics完全公开可下载
  • 网站快速优化排名免费网络营销模式课
  • 【每日算法】 洛谷 P12341 【[蓝桥杯 2025 省 A/Python B 第二场] 消消乐】 2025.10.26