STM32笔记归纳3:串口

串口

目录

串口

一、通信协议

1.1.串口的概念

1.2.串口的通信协议

1.2.1.通信协议的概念

1.2.2.传输流程

1.2.3.串口数据帧格式

1.2.4.校验位的使用方式

二、USART模块的使用方法

2.1.USART模块简介

2.2.USART的基本用法

2.2.1.发送数据

2.2.2.接收数据

2.3.移位寄存器和串并转换

2.3.1.并行传输

2.3.2.串行传输

2.3.3.并行转串行

2.3.4.串行转并行

2.4.数据帧格式的设置方法

2.4.1.数据帧格式

2.4.2.参数设置方法

2.5.波特率的设置方法

2.6.编程接口

2.6.1.USART编程接口

2.6.2.设置USART实验

2.6.2.1.开启USART1模块时钟

2.6.2.2.USART结构的前置声明

2.6.2.3.串口初始化

三、为串口初始化IO引脚

3.1.USART模块引脚

3.2.引脚分布表

3.3.重映射表

3.4.IO配置表

3.4.1.全双工模式

3.4.2.半双工模式

3.4.3.同步模式

3.4.4.硬件流量控制

[3.5.编写代码(默认PA9 PA10)](#3.5.编写代码(默认PA9 PA10))

3.5.1.GPIO结构前置声明

3.5.2.配置发送端Tx对应的PA9引脚

3.5.3.配置接收端Rx对应的PA10引脚

[3.6.编写代码(重映射PB6 PB7)](#3.6.编写代码(重映射PB6 PB7))

3.6.1.开启AFIO模块时钟

3.6.2.使能重映射

3.6.3.GPIO结构的前置声明

3.6.4.配置发送端Tx对应的PB6重映射引脚

3.6.5.配置接收端Rx对应的PB7重映射引脚

四、发送数据

4.1.回顾串口数据发送的过程

4.2.TxE标志位

4.3.TC标志位

4.4.编程接口

4.4.1.编程接口1

4.4.2.编程接口2

4.2.3.编程接口3

4.5.串口发送多字节数据函数

4.6.串口发送数据实验

4.6.1.接线方式

4.6.2.代码编写

4.6.3.配置串口助手

五、格式化打印字符串

5.1.给工程重命名

5.2.整理之前的代码

5.3.给代码添加注释

5.4.格式化字符串编程原理

5.5.重写fputc函数

5.6.格式化时间字符串

5.6.1.时间获取函数

5.6.2.代码编写

六、串口接收数据

6.1.回顾数据接收过程

6.2.RxNE标志位

6.3.接收代码的编写方法

6.3.1.编程接口

6.4.使用串口控制LED

6.5.错误标志位

6.5.1.PE标志位

6.5.2.FE标志位

6.5.3.NE标志位

6.5.4.ORE标志位

七、封装常用功能

7.1.为什么要封装串口的常用功能

7.2.复制串口的初始化代码

7.3.MyLib文件夹简介

7.4.串口发送相关的函数

7.4.1.发送一个字节

7.4.2.发送多个字节

7.4.3.发送一个字符

7.4.4.发送字符串

7.4.5.发送格式化字符串

7.5.串口接收相关的函数

7.5.1.接收一个字节

7.5.1.接收一行字符串


一、通信协议

1.1.串口的概念

通信接口,用来传输数据

Tx:Transmit 数据发送引脚

Rx:Receive 数据接收引脚

1.2.串口的通信协议

1.2.1.通信协议的概念

甲与乙两人之间交流需要使用相同的语言

串口发送方与接收方要使用相同数据格式

这种数据格式也被称为通信协议

1.2.2.传输流程

数据通过导线以高低电平变化的形式传输:

  • 空闲状态:串口未传送数据时,线路保持高电平
  • 起始信号:空闲状态下,发送方拉低电平启动数据传输
  • 接收数据:接收方检测到空闲状态下的低电平后开始接收数据
  • 数据传输:高电平表示1,低电平表示0,以字节为单位逐位传输
  • 终止信号:发送方恢复高电平结束数据传输

**注:**串口传输是低位先行(LSB First)

**示例1:**通过串口发送十进制数字27

27的原码:0001 1011

低位先行:发送时的顺序为1101 1000

**示例2:**发送字符串"Hello"

ASCII码值:H=0x48 e=0x65 l=0x6c o=0x6f

1.2.3.串口数据帧格式
  • 起始位:1位
  • 数据位:8~9位
  • 校验位:检测数据传输过程中是否出错(数据位的最后一位可以作为校验位)
  • 停止位:0.5,1,1.5,2位

八位无校验(常用):

数据位长度:1字节

八位有校验:

数据位长度:不足1字节

九位无校验:

数据位长度:多余1字节

九位有校验(常用):

数据位长度:1字节

1.2.4.校验位的使用方式

**奇校验:**要求数据位有奇数个1

**偶校验:**要求数据位有偶数个1

**示例:**假设采用九位有校验的数据帧格式,校验方式为奇校验

发送方发送85(0101 0101):

**注:**为了确保数据位有奇数个1,发送方需通过校验位补1

接收方:

**注:**检验数据位中是否有奇数个1

  • 有奇数个1:传输数据成功
  • 无奇数个1:传输数据出错

二、USART模块的使用方法

2.1.USART模块简介

USART模块类似于人的嘴巴与耳朵的结合体

既能通过嘴巴发送信息,又能通过耳朵接收信息

2.2.USART的基本用法

2.2.1.发送数据

向发送数据寄存器写入数据,USART模块将数据以数据帧格式从Tx引脚发送给对侧

**示例:**向发送数据寄存器写入100(0110 0100)

发送数据寄存器存放:

Tx引脚输出:

2.2.2.接收数据

Rx引脚接收对侧发送的数据帧,USART模块将数据解析出来,存入接收数据寄存器

**示例:**从接收数据寄存器读取88(0101 1000)

接收数据寄存器存放:

Rx引脚接收:

2.3.移位寄存器和串并转换

2.3.1.并行传输

多个bit位同时进行传输操作

示例:

  • 通过USART模块发送数据时,8位数据同时写入发送数据寄存器
  • 通过USART模块接收数据时,8位数据同时存入接收数据寄存器
2.3.2.串行传输

逐个bit位按序进行传输操作

示例:

  • 通过Tx引脚以数据帧格式传输时,每个bit位按顺序依次发送
  • 通过Rx引脚接收数据时,逐个bit位检测高低电平的变化
2.3.3.并行转串行
2.3.4.串行转并行

2.4.数据帧格式的设置方法

2.4.1.数据帧格式
2.4.2.参数设置方法

通过电路控制数据传输时波形(数据帧)的格式

参数设置:

  • 数据位:8、9
  • 停止位:0.5、1、1.5、2
  • 校验方式:奇、偶、无

2.5.波特率的设置方法

**波特率:**每秒钟最多传输多少位

**常用的波特率:**9600、115200、921600

**示例:**输入时钟频率为72MHz,通过电路,产生115200的波特率

将波特率寄存器的值设置为x,即分频器的分配系数为x

列出方程:72MHz ÷ x ÷ 16 = 115200,解得x的值等于39.0625

将x的值以二进制的形式(0000 0010 0111 0001)存入波特率寄存器

2.6.编程接口

2.6.1.USART编程接口
cpp 复制代码
void USART_Init(USARTTypeDef* USARTx, USART_InitTypeDef* USART_InistStruct);

解析:

  • 参数1:设置串口名称
  • 参数2:初始化的参数结构体地址

作用: 初始化串口,配置串口的各种参数

  • 串口的波特率
  • 串口的数据位长度
  • 串口的停止位长度
  • 串口的校验方式
  • 串口的数据收发方向

**补充:**USART_InitTypeDef结构(串口参数菜单)

cpp 复制代码
typedef struct USART_InitTypeDef
{
	uint32_t USART_BaudRate;
	uint16_t USART_WordLength;
	uint16_t USART_StopBits;
	uint16_t USART_Parity;
	uint16_t USART_Mode;
}USART_InitTypeDef;

分析:

**1.USART_BaudRate:**波特率

**2.USART_WordLength:**数据位长度

  • USART_WordLength_8b
  • USART_WordLength_9b

**3.USART_StopBits:**停止位长度

  • USART_StopBits_0_5
  • USART_StopBits_1
  • USART_StopBits_1_5
  • USART_StopBits_2

**4.USART_Parity:**校验方式

  • USART_Parity_No
  • USART_Parity_Even
  • USART_Parity_Odd

**5.USART_Mode:**数据收发方向

  • USART_Mode_Tx
  • USART_Mode_Rx
  • USART_Mode_Tx | USART_Mode_Rx
2.6.2.设置USART实验

**实验内容:**设置USART1 波特率115200 数据位8位 停止位1位 无校验

编码部分:

2.6.2.1.开启USART1模块时钟
cpp 复制代码
RCC_APB2PerihClockCmd(RCC_APB2Periph_USART1, ENABLE);
2.6.2.2.USART结构的前置声明
cpp 复制代码
USART_InitTypeDef USART_InitStruct;
2.6.2.3.串口初始化
cpp 复制代码
/*波特率为115200*/
USART_InitStruct.USART_BaudRate = 115200;
/*数据位为8位*/
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
/*停止位为1位*/
USART_InitStruct.USART_StopBits = USART_StopBits_1;
/*校验方式为无*/
USART_InitStruct.USART_Parity = USART_Parity_No;
/*收发方向为双向*/
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;

/*调用初始化函数*/
USART_Init(USART1,&USART_InitStruct);

三、为串口初始化IO引脚

3.1.USART模块引脚

  • Tx:数据发送引脚
  • Rx:数据接收引脚
  • CTS:硬件流控
  • RTS:硬件流控
  • CK:同步模式的时钟线

3.2.引脚分布表

数据手册3.Table 5 引脚分布表(封装为LQFP48)

|--------|-----------------|----------|----------------|-------------------------------------------------|--------------------------------|
| 编号 | 名称 | 电压范围 | 主功能(复位后默认) | 复用功能 ||
| 编号 | 名称 | 电压范围 | 主功能(复位后默认) | 默认 | 重映射 |
| 1 | VBAT | | VBAT | | |
| 2 | PC13-TEMPER-RTC | | PC13 | TAMPER-RTC | |
| 3 | PC14-OSC32_IN | | PC14 | OSC32_IN | |
| 4 | PC15- OSC32_OUT | | PC15 | OSC32_OUT | |
| 5 | OSC_IN | | OSC_IN | PD0 | |
| 6 | OSC_OUT | | OSC_OUT | PD1 | |
| 7 | NRST | | NRST | | |
| 8 | VSSA | | VSSA | | |
| 9 | VDDA | | VDDA | | |
| 10 | PA0-WKUP | | PA0 | WKUP USART2_CTS ADC12_IN0 TIM2_CH1_ ETR | |
| 11 | PA1 | | PA1 | USART2_RTS ADC12_IN1 TIM2_CH2 | |
| 12 | PA2 | | PA2 | USART2_TX ADC12_IN2 TIM2_CH3 | |
| 13 | PA3 | | PA3 | USART2_RX ADC12_IN3 TIM2_CH4 | |
| 14 | PA4 | | PA4 | SPI1_NSS USART2_CK ADC12_IN4 | |
| 15 | PA5 | | PA5 | SPI1_SCK ADC12_IN5 | |
| 16 | PA6 | | PA6 | SPI1_MISO ADC12_IN6 TIM3_CH1 | TIM1_BKIN |
| 17 | PA7 | | PA7 | SPI1_MOSI ADC12_IN7 TIM3_CH2 | TIM1_CH1N |
| 18 | PB0 | | PB0 | ADC12_IN8 TIM3_CH3 | TIM1_CH2N |
| 19 | PB1 | | PB1 | ADC12_IN9 TIM3_CH4 | TIM1_CH3N |
| 20 | PB2 | | PB2/BOOT1 | | |
| 21 | PB10 | FT | PB10 | I2C2_SCL USART3_TX | TIM2_CH3 |
| 22 | PB11 | FT | PB11 | I2C2_SDA USART3_RX | TIM2_CH4 |
| 23 | VSS_1 | | VSS_1 | | |
| 24 | VDD_1 | | VDD_1 | | |
| 25 | PB12 | FT | PB12 | SPI2_NSS I2C2_SMBAl USART3_CK TIM1_BKIN | |
| 26 | PB13 | FT | PB13 | SPI2_SCK USART3_CTS TIM1_CH1N | |
| 27 | PB14 | FT | PB14 | SPI2_MISO USART3_RTS TIM1_CH2N | |
| 28 | PB15 | FT | PB15 | SPI2_MOSI TIM1_CH3N | |
| 29 | PA8 | FT | PA8 | USART1_CK TIM1_CH1 MCO | |
| 30 | PA9 | FT | PA9 | USART1_TX TIM1_CH2 | |
| 31 | PA10 | FT | PA10 | USART1_RX TIM1_CH3 | |
| 32 | PA11 | FT | PA11 | USART1_CTS CANRX USBDM TIM1_CH4 | |
| 33 | PA12 | FT | PA12 | USART1_RTS CANTX USBDP TIM1_ETR | |
| 34 | PA13 | FT | JTMS SWDIO | | PA13 |
| 35 | VSS_2 | | VSS_2 | | |
| 36 | VDD_2 | | VDD_2 | | |
| 37 | PA14 | FT | JTCK SWCLK | | PA14 |
| 38 | PA15 | FT | JTDI | | TIM2_CH1_ ETR . SPI1_NSS |
| 39 | PB3 | FT | JTDO | | TIM2_CH2 PB3 TRACESWO SPI1_SCK |
| 40 | PB4 | FT | JNTRST | | TIM3_CH1 PB4 SPI1_MISO |
| 41 | PB5 | | PB5 | I2C1_SMBAl | TIM3_CH2 SPI1_MOSI |
| 42 | PB6 | FT | PB6 | I2C1_SCL TIM4_CH1 | USART1_TX |
| 43 | PB7 | FT | PB7 | I2C1_SDA TIM4_CH2 | USART1_RX |
| 44 | BOOT0 | | BOOT0 | | |
| 45 | PB8 | FT | PB8 | TIM4_CH3 | I2C1_SCL CANRX |
| 46 | PB9 | FT | PB9 | TIM4_CH4 | I2C1_SDA CANTX |
| 47 | VSS_3 | | VSS_3 | | |
| 48 | VDD_3 | | VDD_3 | | |

根据IO引脚分布表,找到USART1模块默认发送引脚为PA9,接收引脚为PA10

当这些引脚不可用时,芯片还为USART1模块提供了备用引脚

发送引脚PB6,接收引脚PB7,通过使能重映射,可以切换为备用的引脚

3.3.重映射表

参考手册8.3 复用功能I/O和调试配置(AFIO)

3.4.IO配置表

参考手册8.1.11 外设的GPIO配置

3.4.1.全双工模式

数据传输方向为双向,能同时进行

3.4.2.半双工模式

数据传输方向为双向,不能同时进行

3.4.3.同步模式

在标准的串口基础上添加CK线

在两个设备之间传输时钟信号,使两个设备同步

3.4.4.硬件流量控制

在标准的串口基础上添加CTS和RTS两个引脚

这两个引脚交叉连接

注:

Rx引脚配置全双工模式时,GPIO优先选择输入上拉模式

  • 当通信线路断开,Rx引脚会处于悬空状态,上拉电阻可以为Rx引脚提供稳定的高电平
  • 避免外界电磁波干扰,确保信号的完整性
  • 使串口引脚保持在空闲状态(高电平),降低断线对系统的影响

3.5.编写代码(默认PA9 PA10)

3.5.1.GPIO结构前置声明
cpp 复制代码
/*前置声明*/
GPIO_InitTypeDef GPIO_InitStruct;
3.5.2.配置发送端Tx对应的PA9引脚
cpp 复制代码
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*选择PA9引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
/*设置复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速度为10MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;

/*初始化引脚*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
3.5.3.配置接收端Rx对应的PA10引脚
cpp 复制代码
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/*选择PA10引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
/*设置输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;

/*初始化引脚*/
GPIO_Init(GPIOA, &GPIO_InitStruct);

3.6.编写代码(重映射PB6 PB7)

3.6.1.开启AFIO模块时钟
cpp 复制代码
/*使能AFIO模块的时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

**注:**AFIO作为芯片的片上外设,核心功能是实现引脚复用选择

3.6.2.使能重映射
cpp 复制代码
/*使能USART1的重映射*/
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
3.6.3.GPIO结构的前置声明
cpp 复制代码
/*前置声明*/
GPIO_InitTypeDef GPIO_InitStruct;
3.6.4.配置发送端Tx对应的PB6重映射引脚
cpp 复制代码
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/*选择PB6引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
/*设置复用输出推挽模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
/*最大输出速度为10MHz*/
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;

/*初始化引脚*/
GPIO_Init(GPIOB, &GPIO_InitStruct);
3.6.5.配置接收端Rx对应的PB7重映射引脚
cpp 复制代码
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/*选择PB7引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
/*设置输入上拉模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;

/*初始化引脚*/
GPIO_Init(GPIOB, &GPIO_InitStruct);

四、发送数据

4.1.回顾串口数据发送的过程

  • 通过CPU将要发送的数据写入发送数据寄存器
  • 发送数据寄存器将数据并行传输到移位寄存器
  • 移位寄存器将数据串行输出

4.2.TxE标志位

**TxE(Transmit Data Register Empty ):**发送数据寄存器空标志位

**功能:**检测发送数据寄存器(TDR)是否有数据,避免数据发送过程中出现覆盖

状态指示:

  • 当TDR为空时:TxE = 1
  • 当TDR非空时:TxE = 0

4.3.TC标志位

**TC(Transmit Complete):**发送完成标志位

**功能:**检测TDR和移位寄存器是否有数据,判断数据发送是否完成

状态指示:

  • 当TDR和移位寄存器都为空时:TC = 1
  • 当TDR或移位寄存器不为空时:TC = 0

4.4.编程接口

4.4.1.编程接口1
cpp 复制代码
void USART_Cmd(USARTTypeDef* USARTx, FunctiomalState NewState);

解析:

  • 参数1:串口名称
  • 参数2:使能标志 ENABLE(使能)DISABLE(禁止)

**作用:**控制USART模块的使能和禁止(总开关)

示例:

使能USART1:

cpp 复制代码
USART_Cmd(USART1,ENABLE);

禁止USART1:

cpp 复制代码
USART_Cmd(USART1,DISABLE);
4.4.2.编程接口2
cpp 复制代码
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

解析:

  • 参数1:串口名称
  • 参数2:要查询的标志位名称

**返回值:**RESET(表示0)SET(表示1)

作用: 查询USART标志位的值

标志位:

  • TXE:USART_FLAG_TXE
  • TC:USART_FLAG_TC
  • RXNE:USART_FLAG_RXNE
  • PE:USART_FLAG_PE

示例:

判断发送数据寄存器是否为空:

cpp 复制代码
if ((USART_GetFlagStatus(USART1, USART_FLAG_TXE)) == SET)
{
	//......
}

判断数据发送是否完成:

cpp 复制代码
if ((USART_GetFlagStatus(USART1, USART_FLAG_TC)) == SET)
{
	//......
}
4.4.3.编程接口3
cpp 复制代码
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);

解析:

  • 参数1:串口名称
  • 参数2:要发送的数据

**作用:**把要发送的数据写入到发送数据寄存器里

**注:**发送数据的类型为无符号16位的整型,因为发送的数据可能是9位,8位整型无法接收

示例:

发送字节0x01:

cpp 复制代码
USART_SendData(USART1, 0x01);

4.5.串口发送多字节数据函数

cpp 复制代码
void My_USART_SendBytes(USART_TypeDef* USARTx, uint8_t* pData, uint16_t Size);

|------------------------|--------------|
| 参数 | 含义 |
| USART_TypeDef* USARTx | 串口名称 |
| uint8_t* pData | 发送数据的数组首元素地址 |
| uint16_t Size | 发送数据的字节数量 |
| 返回值 | void |

代码实现:

cpp 复制代码
void My_USART_SendBytes(USART_TypeDef* USARTx, uint8_t* pData, uint16_t Size)
{
	for (uint32_t i = 0; i < Size; i++)
	{
		/*等待发送数据寄存器为空*/
		while ((USART_GetFlagStatus(USARTx, USART_FLAG_TXE)) == RESET);
		/*为空后将数据写入发送数据寄存器*/
		USART_SendData(USARTx, pData[i]);
	}

    /*等待数组中所有数据发送完成,移位寄存器和数据寄存器都为空*/
	while ((USART_GetFlagStatus(USART1, USART_FLAG_TC)) == RESET);
}

解析:

  • for循环将数据以字节为单位依次发送,循环次数为发送数据的字节数量
  • while循环等待发送数据寄存器为空,为空后将数据写入发送数据寄存器
  • while循环等待数组中所有数据发送完成,移位寄存器和数据寄存器都为空

4.6.串口发送数据实验

4.6.1.接线方式

**注:**串口USB的TXD和RXD与芯片上Tx端对应的PB6引脚和Rx端对应的PB7引脚交叉连接:

  • 串口USB的TXD接芯片的PB7引脚
  • 串口USB的RXD接芯片的PB6引脚
4.6.2.代码编写
cpp 复制代码
#include "stm32f10x.h"

//声明串口发送多字节数据函数
void My_USART_SendBytes(USART_TypeDef* USARTx, uint8_t* pData, uint16_t Size);

int main(void)
{
	//初始化GPIO
	
	/*开启AFIO模块的时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	/*使能USART1的重映射*/
	GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
	/*GPIO结构前置声明*/
	GPIO_InitTypeDef GPIO_InitStruct;
	
	//配置发送端Tx对应的PB6重映射引脚
	
	/*开启GPIOB时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/*选择PB6引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	/*设置复用输出推挽模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	/*最大输出速度为10MHz*/
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	/*初始化PB6引脚*/
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	//配置接收端Rx对应的PB7重映射引脚
	
	/*开启GPIOB时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/*选择PB7引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
	/*设置输入上拉模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	/*初始化PB7引脚*/
	GPIO_Init(GPIOB, &GPIO_InitStruct);

	//初始化串口
	
	/*开启USART1模块时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	/*USART结构前置声明*/
	USART_InitTypeDef USART_InitStruct;
	/*波特率为115200*/
	USART_InitStruct.USART_BaudRate = 115200;
	/*数据位为8位*/
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	/*停止位为1位*/
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	/*校验方式为无*/
	USART_InitStruct.USART_Parity = USART_Parity_No;
	/*收发方向为双向*/
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	/*初始化USART1*/
	USART_Init(USART1,&USART_InitStruct);
	/*闭合串口总开关*/
	USART_Cmd(USART1,ENABLE);
	
	//创建要发送数据的数组
	uint8_t bytesToSend[] = {1,2,3,4,5};
	
	//调用串口发送多字节数据函数
	My_USART_SendBytes(USART1,bytesToSend,5);
	
	while(1)
	{
	}
	
}

//创建串口发送多字节数据函数
void My_USART_SendBytes(USART_TypeDef* USARTx, uint8_t* pData, uint16_t Size)
{
	for (uint32_t i = 0; i < Size; i++)
	{
	    /*等待发送数据寄存器为空*/
		while ((USART_GetFlagStatus(USARTx, USART_FLAG_TXE)) == RESET);
		/*为空后将数据写入发送数据寄存器*/
		USART_SendData(USARTx, pData[i]);
	}

    /*等待数组中所有数据发送完成,移位寄存器和数据寄存器都为空*/
	while ((USART_GetFlagStatus(USART1, USART_FLAG_TC)) == RESET);
}
4.6.3.配置串口助手

五、格式化打印字符串

5.1.给工程重命名

将模板工程template拷贝一份,重命名为新的工程名字

5.2.整理之前的代码

将IO引脚初始化与串口初始化代码封装为一个函数

cpp 复制代码
#include "stm32f10x.h"

//声明串口发送多字节数据函数
void My_USART_SendBytes(USART_TypeDef* USARTx, uint8_t* pData, uint16_t Size);

//声明初始化函数
void My_USART_Init(void);

int main(void)
{

	//调用初始化函数
	My_USART_Init();
	
	//创建要发送数据的数组
	uint8_t bytesToSend[] = {1,2,3,4,5};
	
	//调用串口发送多字节数据函数
	My_USART_SendBytes(USART1,bytesToSend,5);
	
	while(1)
	{
	}
	
}

//创建串口发送多字节数据函数
void My_USART_SendBytes(USART_TypeDef* USARTx, uint8_t* pData, uint16_t Size)
{
	for (uint32_t i = 0; i < Size; i++)
	{
		/*等待发送数据寄存器为空*/
		while ((USART_GetFlagStatus(USARTx, USART_FLAG_TXE)) == RESET);
		/*为空后将数据写入发送数据寄存器*/
		USART_SendData(USARTx, pData[i]);
	}
	
	/*等待数组中所有数据发送完成,移位寄存器和数据寄存器都为空*/
	while ((USART_GetFlagStatus(USARTx, USART_FLAG_TC)) == RESET);
}

//创建初始化函数
void My_USART_Init(void)
{
	//初始化GPIO
	
	/*开启AFIO模块的时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	/*使能USART1的重映射*/
	GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
	/*GPIO结构前置声明*/
	GPIO_InitTypeDef GPIO_InitStruct;
	
	//配置发送端Tx对应的PB6重映射引脚
	
	/*开启GPIOB时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/*选择PB6引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	/*设置复用输出推挽模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	/*最大输出速度为10MHz*/
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	/*初始化PB6引脚*/
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	//配置接收端Rx对应的PB7重映射引脚
	
	/*开启GPIOB时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/*选择PB7引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
	/*设置输入上拉模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	/*初始化PB7引脚*/
	GPIO_Init(GPIOB, &GPIO_InitStruct);

	//初始化串口
	
	/*开启USART1模块时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	/*USART结构前置声明*/
	USART_InitTypeDef USART_InitStruct;
	/*波特率为115200*/
	USART_InitStruct.USART_BaudRate = 115200;
	/*数据位为8位*/
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	/*停止位为1位*/
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	/*校验方式为无*/
	USART_InitStruct.USART_Parity = USART_Parity_No;
	/*收发方向为双向*/
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	/*初始化USART1*/
	USART_Init(USART1,&USART_InitStruct);
	/*闭合串口总开关*/
	USART_Cmd(USART1,ENABLE);
}

5.3.给代码添加注释

cpp 复制代码
#include "stm32f10x.h"

//声明串口发送多字节数据函数
void My_USART_SendBytes(USART_TypeDef* USARTx, uint8_t* pData, uint16_t Size);

//声明初始化函数
void My_USART_Init(void);

int main(void)
{

	//调用初始化函数
	My_USART_Init();
	
	//创建要发送数据的数组
	uint8_t bytesToSend[] = {1,2,3,4,5};
	
	//调用串口发送多字节数据函数
	My_USART_SendBytes(USART1,bytesToSend,5);
	
	while(1)
	{
	}
	
}


//创建串口发送多字节数据函数
//
//@简介:通过串口发送多个字节
//@参数 USARTx:填写串口的名称
//@参数 pData:要发送的数据
//@参数 Size:要发送数据的数量,单位是字节
//
void My_USART_SendBytes(USART_TypeDef* USARTx, uint8_t* pData, uint16_t Size)
{
	for (uint32_t i = 0; i < Size; i++)
	{
		/*等待发送数据寄存器为空*/
		while ((USART_GetFlagStatus(USARTx, USART_FLAG_TXE)) == RESET);
		/*为空后将数据写入发送数据寄存器*/
		USART_SendData(USARTx, pData[i]);
	}
	
	/*等待数组中所有数据发送完成,移位寄存器和数据寄存器都为空*/
	while ((USART_GetFlagStatus(USARTx, USART_FLAG_TC)) == RESET);
}

//创建初始化函数
//
//@简介:对USART1初始化
//@格式:PB6 - Tx,PB7 - Rx
//      115200,8,1,None,双向
//
void My_USART_Init(void)
{
	//#1:初始化GPIO
	
	/*开启AFIO模块的时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	/*使能USART1的重映射*/
	GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
	/*GPIO结构前置声明*/
	GPIO_InitTypeDef GPIO_InitStruct;
	
	//配置发送端Tx对应的PB6重映射引脚
	
	/*开启GPIOB时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/*选择PB6引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	/*设置复用输出推挽模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	/*最大输出速度为10MHz*/
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	/*初始化PB6引脚*/
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	//配置接收端Rx对应的PB7重映射引脚
	
	/*开启GPIOB时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/*选择PB7引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
	/*设置输入上拉模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	/*初始化PB7引脚*/
	GPIO_Init(GPIOB, &GPIO_InitStruct);

	//#2:初始化串口
	
	/*开启USART1模块时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	/*USART结构前置声明*/
	USART_InitTypeDef USART_InitStruct;
	/*波特率为115200*/
	USART_InitStruct.USART_BaudRate = 115200;
	/*数据位为8位*/
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	/*停止位为1位*/
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	/*校验方式为无*/
	USART_InitStruct.USART_Parity = USART_Parity_No;
	/*收发方向为双向*/
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	/*初始化USART1*/
	USART_Init(USART1,&USART_InitStruct);
	/*闭合串口总开关*/
	USART_Cmd(USART1,ENABLE);
}

5.4.格式化字符串编程原理

生成格式化字符串

通过fputc输出到标准输出流

重写fputc把数据发送到串口

5.5.重写fputc函数

cpp 复制代码
#include <stdio.h>

int fputc(int ch, FILE* f)
{
	/*等待发送数据寄存器为空*/
	while ((USART_GetFlagStatus(USARTx, USART_FLAG_TXE)) == RESET);

	/*为空后将数据写入发送数据寄存器*/
	USART_SendData(USARTx, (uint8_t)ch);

	return ch;
}

注: fputc函数需包含<stdio.h>头文件,只需要在主函数中调用printf函数,写入要发送的字符串就可以将字符串发送至串口,串口的接收设置要改为ASCII

5.6.格式化时间字符串

5.6.1.时间获取函数
cpp 复制代码
/*delay初始化*/
Delay_Init();
uint32_t currentTick = GetTick();

**注:**GetTick函数需包含"delay.h"头文件,Delay函数需要初始化

cpp 复制代码
/*计算毫秒*/
uint32_t miliseconds = currentTick % 1000;
currentTick /= 1000;
/*计算秒钟*/
uint32_t seconds = currentTick % 60;
currentTick /= 60;
/*计算分钟*/
uint32_t minutes = currentTick % 60;
currentTick /= 60;
/*计算时钟*/
uint32_t hour = currentTick;

**解析:**时间获取函数会返回一个数值,需要经过计算才能获取具体时间,假设数值为29,430,125

5.6.2.代码编写
cpp 复制代码
#include "stm32f10x.h"
#include <stdio.h>
#include "delay.h"

//声明串口发送多字节数据函数
void My_USART_SendBytes(USART_TypeDef* USARTx, uint8_t* pData, uint16_t Size);

//声明初始化函数
void My_USART_Init(void);

int main(void)
{
	/*delay初始化*/
	Delay_Init();
	
	//调用初始化函数
	My_USART_Init();

	while(1)
	{
		uint32_t currentTick = GetTick();
		/*计算毫秒*/
		uint32_t miliseconds = currentTick % 1000;
		currentTick /= 1000;
		/*计算秒钟*/
		uint32_t seconds = currentTick % 60;
		currentTick /= 60;
		/*计算分钟*/
		uint32_t minutes = currentTick % 60;
		currentTick /= 60;
		/*计算时钟*/
		uint32_t hour = currentTick;
		
		printf("%02u:%02u:%02u.%03u:",hour,minutes,seconds,miliseconds);
		
		Delay(100);
	}
	
}


//创建串口发送多字节数据函数
//
//@简介:通过串口发送多个字节
//@参数 USARTx:填写串口的名称
//@参数 pData:要发送的数据
//@参数 Size:要发送数据的数量,单位是字节
//
void My_USART_SendBytes(USART_TypeDef* USARTx, uint8_t* pData, uint16_t Size)
{
	for (uint32_t i = 0; i < Size; i++)
	{
		/*等待发送数据寄存器为空*/
		while ((USART_GetFlagStatus(USARTx, USART_FLAG_TXE)) == RESET);
		/*为空后将数据写入发送数据寄存器*/
		USART_SendData(USARTx, pData[i]);
	}
	
	/*等待数组中所有数据发送完成,移位寄存器和数据寄存器都为空*/
	while ((USART_GetFlagStatus(USARTx, USART_FLAG_TC)) == RESET);
}

//创建初始化函数
//
//@简介:对USART1初始化
//@格式:PB6 - Tx,PB7 - Rx
//      115200,8,1,None,双向
//
void My_USART_Init(void)
{
	//#1:初始化GPIO
	
	/*开启AFIO模块的时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	/*使能USART1的重映射*/
	GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
	/*GPIO结构前置声明*/
	GPIO_InitTypeDef GPIO_InitStruct;
	
	//配置发送端Tx对应的PB6重映射引脚
	
	/*开启GPIOB时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/*选择PB6引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	/*设置复用输出推挽模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	/*最大输出速度为10MHz*/
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	/*初始化PB6引脚*/
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	//配置接收端Rx对应的PB7重映射引脚
	
	/*开启GPIOB时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/*选择PB7引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
	/*设置输入上拉模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	/*初始化PB7引脚*/
	GPIO_Init(GPIOB, &GPIO_InitStruct);

	//#2:初始化串口
	
	/*开启USART1模块时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	/*USART结构前置声明*/
	USART_InitTypeDef USART_InitStruct;
	/*波特率为115200*/
	USART_InitStruct.USART_BaudRate = 115200;
	/*数据位为8位*/
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	/*停止位为1位*/
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	/*校验方式为无*/
	USART_InitStruct.USART_Parity = USART_Parity_No;
	/*收发方向为双向*/
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	/*初始化USART1*/
	USART_Init(USART1,&USART_InitStruct);
	/*闭合串口总开关*/
	USART_Cmd(USART1,ENABLE);
}

int fputc(int ch, FILE* f)
{
	/*等待发送数据寄存器为空*/
	while ((USART_GetFlagStatus(USART1, USART_FLAG_TXE)) == RESET);

	/*为空后将数据写入发送数据寄存器*/
	USART_SendData(USART1, (uint8_t)ch);

	return ch;
}

六、串口接收数据

6.1.回顾数据接收过程

6.2.RxNE标志位

**RxNE(Receive Data Register Not Empty):**接收数据寄存器非空标志位

**功能:**检测接收数据寄存器(RDR)是否有数据,有数据时就可以读取数据

状态指示:

  • 当RDR为空时:RxNE = 0
  • 当RDR非空时:RxNE = 1

6.3.接收代码的编写方法

6.3.1.编程接口
cpp 复制代码
uint16_t USART_ReceiveData(USARTTypeDef* USARTx);

**注:**返回值为16位是为了方式数据位为9位且没有校验位时不够用

**作用:**从接收数据寄存器读取数据

示例:

cpp 复制代码
//#1:等待接收数据寄存器非空
while (USART_GetFlagStatus(USARTx,USART_FLAG_RXNE) == RESET);

//#2:接收数据
uint8_t byteRcvd = USART_ReceiveData(USARTx);

//#3:处理数据
//...

6.4.使用串口控制LED

**实验内容:**通过串口控制板载LED,发送0时点亮,发送1时熄灭

cpp 复制代码
#include "stm32f10x.h"


//声明初始化函数
void My_USART_Init(void);
//声明板载LED初始化函数
void My_OnBoardLED_Init(void);

int main(void)
{
	/*调用初始化函数*/
	My_USART_Init();
	/*调用板载LED初始化函数*/
	My_OnBoardLED_Init();
	
	while(1)
	{
		/*等待接收数据寄存器非空*/
		while (USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);

		/*接收数据*/
		uint8_t byteRcvd = USART_ReceiveData(USART1);
		
		/*处理数据*/
		if(byteRcvd == '0')
		{
			GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);//写0亮灯
		}
		else if(byteRcvd == '1')
		{
			GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);//写1灭灯
		}
	}
}

//创建初始化函数
//
//@简介:对USART1初始化
//@格式:PB6 - Tx,PB7 - Rx
//      115200,8,1,None,双向
//
void My_USART_Init(void)
{
	//#1:初始化GPIO
	
	/*开启AFIO模块的时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	/*使能USART1的重映射*/
	GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
	/*GPIO结构前置声明*/
	GPIO_InitTypeDef GPIO_InitStruct;
	
	//配置发送端Tx对应的PB6重映射引脚
	
	/*开启GPIOB时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/*选择PB6引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	/*设置复用输出推挽模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	/*最大输出速度为10MHz*/
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	/*初始化PB6引脚*/
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	//配置接收端Rx对应的PB7重映射引脚
	
	/*开启GPIOB时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/*选择PB7引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
	/*设置输入上拉模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	/*初始化PB7引脚*/
	GPIO_Init(GPIOB, &GPIO_InitStruct);

	//#2:初始化串口
	
	/*开启USART1模块时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	/*USART结构前置声明*/
	USART_InitTypeDef USART_InitStruct;
	/*波特率为115200*/
	USART_InitStruct.USART_BaudRate = 115200;
	/*数据位为8位*/
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	/*停止位为1位*/
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	/*校验方式为无*/
	USART_InitStruct.USART_Parity = USART_Parity_No;
	/*收发方向为双向*/
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	/*初始化USART1*/
	USART_Init(USART1,&USART_InitStruct);
	/*闭合串口总开关*/
	USART_Cmd(USART1,ENABLE);
}

//创建板载LED初始化函数
void My_OnBoardLED_Init(void)
{
	/*开启GPIOC的时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	/*GPIO结构前置声明*/
	GPIO_InitTypeDef GPIO_InitStruct;
	/*选择PC13引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
	/*设置通用输出开漏模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
	/*最大输出速度为2MHz*/
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	/*初始化PC13引脚*/
	GPIO_Init(GPIOC,&GPIO_InitStruct);
	
	/*写1熄灭*/
	GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}

6.5.错误标志位

6.5.1.PE标志位

**PE(Parity Error):**奇偶校验错标志位

**功能:**检测接收到的数据是否有校验错误

状态指示:

  • 无校验错误:PE = 0
  • 有校验错误:PE = 1
6.5.2.FE标志位

**FE(Frame Error):**帧格式错误标志位

**功能:**检测接收到的数据帧是否有效

状态指示:

  • 数据帧有效:FE = 0
  • 数据帧无效:FE = 1
6.5.3.NE标志位

**NE(Noise Error):**噪声错标志位

**功能:**检测接收数据中是否接收到噪声

状态指示:

  • 无噪声:NE = 0
  • 有噪声:NE = 1

多次采样,如果三次都为高电平则为高电平

如果三次都为低电平则为低电平,当三次中

既出现高电平有出现高电平时,就有噪声错

6.5.4.ORE标志位

**ORE(Over Run Error):**过载错标志位

**功能:**检测由于过载造成了数据丢失

状态指示:

  • 无过载错:ORE = 0
  • 有过载错:ORE = 1

Rx端对侧发送数据的速度比接收数据的速度快

数据读取太慢导致了中间字节数据的丢失

七、封装常用功能

7.1.为什么要封装串口的常用功能

将重复代码封装成函数

使用代码时可直接调用

极大地简化编程的过程

7.2.复制串口的初始化代码

cpp 复制代码
#include "stm32f10x.h"

void My_USART_Init(void);

int main(void)
{
	My_USART_Init();
	
	while(1)
	{
	}
}

void My_USART_Init(void)
{
	//#1:初始化GPIO
	
	/*开启AFIO模块的时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	/*使能USART1的重映射*/
	GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
	/*GPIO结构前置声明*/
	GPIO_InitTypeDef GPIO_InitStruct;
	
	//配置发送端Tx对应的PB6重映射引脚
	
	/*开启GPIOB时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/*选择PB6引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	/*设置复用输出推挽模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	/*最大输出速度为10MHz*/
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	/*初始化PB6引脚*/
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	//配置接收端Rx对应的PB7重映射引脚
	
	/*开启GPIOB时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/*选择PB7引脚*/
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
	/*设置输入上拉模式*/
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	/*初始化PB7引脚*/
	GPIO_Init(GPIOB, &GPIO_InitStruct);

	//#2:初始化串口
	
	/*开启USART1模块时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	/*USART结构前置声明*/
	USART_InitTypeDef USART_InitStruct;
	/*波特率为115200*/
	USART_InitStruct.USART_BaudRate = 115200;
	/*数据位为8位*/
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	/*停止位为1位*/
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	/*校验方式为无*/
	USART_InitStruct.USART_Parity = USART_Parity_No;
	/*收发方向为双向*/
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	/*初始化USART1*/
	USART_Init(USART1,&USART_InitStruct);
	/*闭合串口总开关*/
	USART_Cmd(USART1,ENABLE);
}

7.3.MyLib文件夹简介

存放底层驱动函数代码的头文件与源文件,方便调用

与串口功能相关的函数都存放在usart.h中,可以直接

在源文件中包含usart.h头文件进行函数调用

**注:**调用前要初始化串口

7.4.串口发送相关的函数

cpp 复制代码
void My_USART_SendByte(...);//发送一个字节
void My_USART_SendBytes(...);//发送多个字节
void My_USART_SendChar(...);//发送一个字符
void My_USART_SendString(...);//发送字符串
void My_USART_Printf(...);//发送格式化字符串
7.4.1.发送一个字节
cpp 复制代码
#include "stm32f10x.h"
#include "usart.h"

void My_USART_Init(void);

int main(void)
{
	My_USART_Init();
	
	My_USART_SendByte(USART1,0x5a);
	
	while(1)
	{
	}
}

**注:**调用函数时需包含头文件"usart.h",接收数据模式为HEX模式

7.4.2.发送多个字节
cpp 复制代码
#include "stm32f10x.h"
#include "usart.h"

void My_USART_Init(void);

int main(void)
{
	My_USART_Init();
		
	uint8_t byteArray[]={1,2,3,4,5};
	My_USART_SendBytes(USART1,byteArray,5);
	
	while(1)
	{
	}
}

**注:**接收数据模式为HEX模式

7.4.3.发送一个字符
cpp 复制代码
#include "stm32f10x.h"
#include "usart.h"

void My_USART_Init(void);

int main(void)
{
	My_USART_Init();
		
	My_USART_SendChar(USART1,'a');
	
	while(1)
	{
	}
}

**注:**接收数据模式为ASCII模式

7.4.4.发送字符串
cpp 复制代码
#include "stm32f10x.h"
#include "usart.h"

void My_USART_Init(void);

int main(void)
{
	My_USART_Init();
		
	My_USART_SendString(USART1,"Hello World.\r\n");
	
	while(1)
	{
	}
}

**注:**接收数据模式为ASCII模式

7.4.5.发送格式化字符串
cpp 复制代码
#include "stm32f10x.h"
#include "usart.h"

void My_USART_Init(void);

int main(void)
{
	My_USART_Init();
		
	const char* strName = "Tom";
	
	My_USART_Printf(USART1,"Hi,%s!Nice to meet you!\r\n",strName);
	
	while(1)
	{
	}
}

**注:**接收数据模式为ASCII模式

7.5.串口接收相关的函数

cpp 复制代码
uint8_t My_USART_ReceiveByte(...);//接收一个字节
uint16_t My_USART_ReceiveBytes(...);//接收多个字节
int My_USART_ReceiveLine(...);//接收一行字符串
7.5.1.接收一个字节
cpp 复制代码
#include "stm32f10x.h"
#include "usart.h"

void My_USART_Init(void);

int main(void)
{
	My_USART_Init();
	
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOC,&GPIO_InitStruct);
	
	while(1)
	{
		char c = My_USART_ReceiveByte(USART1);

		if(c == '0')
		{
			GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
		}
		else if(c == '1')
		{
			GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
		}
	}
}

**注:**接收数据模式为ASCII模式

7.5.2.接收一行字符串
cpp 复制代码
#include "stm32f10x.h"
#include "usart.h"

void My_USART_Init(void);

int main(void)
{
	
	My_USART_Init();
	
	while(1)
	{
		char buffer[100];
		
		if(My_USART_ReceiveLine(USART1,buffer,100,LINE_SEPERATOR_CRLF,-1) == 0)
		{
			My_USART_SendString(USART1,buffer);
		}
	}
}

**注:**接收数据模式为ASCII模式,发送数据也为ASCII模式

相关推荐
国科安芯2 小时前
抗辐照MCU在核电站交换机中的可靠性验证方法研究
单片机·嵌入式硬件·架构·安全性测试
九成宫2 小时前
计算机网络期末复习——第5章:链路层 Part One
网络·笔记·计算机网络·软件工程
松涛和鸣2 小时前
60、嵌入式定时器深度解析:EPIT与GPT
c语言·arm开发·单片机·嵌入式硬件·gpt·fpga开发
乐亦_Lee2 小时前
在Ubuntu下如何提升下载速度
linux·嵌入式硬件·ubuntu
俊俊谢2 小时前
HC32F460如何配置GPIO中断
单片机·嵌入式硬件·hc32f460
三佛科技-187366133972 小时前
SLD50N06T美浦森60V,50A N沟道MOS场效应管解析
单片机·嵌入式硬件
草丛中的蝈蝈2 小时前
单片机烧写新程序后,ST-LINK无法发现设备
单片机·嵌入式硬件
o(╥﹏╥)2 小时前
Learn how Gen AI 学习笔记
人工智能·笔记·学习
xqqxqxxq3 小时前
《智能仿真无人机平台(多线程V1.0)技术笔记》(初识线程,带你理解程序运行的基本流程)
java·笔记