一、理论基础
1.功能引脚
TX:发送数据输出引脚。
RX:接收数据输入引脚。
SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。
SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引脚。
nCTS:清除以发送(Clear To Send),n 表示低电平有效。如果使能 CTS 流控制,发送器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。
nRTS:请求以发送(Request To Send),n 表示低电平有效。如果使能 RTS 流控制,当USART 接收器准备好接收新数据时就会将 nRTS 变成低电平;当接收寄存器已满时,nRTS 将被设置为高电平。该引脚只适用于硬件流控制。
STM32F103RCT6 芯片的 串口引脚

STM32F103RCT6 系统控制器有三个 USART 和两个 UART,其中 USART1 和时钟来源于 APB2 总线时钟,其最大频率为 72MHz,其他四个的时钟来源于 APB1 总线时钟,其最大频率为 36MHz。UART 只是异步传输功能,所以没SCLK、nCTS 和 nRTS 功能引脚。
2.数据寄存器
USART 数据寄存器(USART_DR)只有低 9 位有效,并且第 9 位数据是否有效要取决于USART 控制寄存1(USART_CR1)的 M 位设置,当 M 位为 0 时表示 8 位数据字长,当 M位为 1 表示 9 位数据字长,我们一般使用 8 位数据字长。
USART_DR 包含了已发送的数据或者接收到的数据。USART_DR 实际是包含了两个寄存器,一个专门用于发送的可写 TDR,一个专门用于接收的可读 RDR。当进行发送操作时,往 USART_DR 写入数据会自动存储在 TDR 内;当进行读取操作时,向 USART_DR读取数据会自动提取 RDR 数据。
TDR 和 RDR 都是介于系统总线和移位寄存器之间。
串行通信是一个位一个位传输的,发送时把 TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接收时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到 RDR。
3.控制器
USART 有专门控制发送的发送器、控制接收的接收器,还有唤醒单元、中断控制等等。使用 USART 之前需要向 USART_CR1 寄存器的 UE 位置 1 使能 USART,UE 位用来开启供给给串口的时钟。发送或者接收数据字长可选 8 位或 9 位,由 USART_CR1 的 M 位控制。
发送器
当 USART_CR1 寄存器的发送使能位 TE 置 1 时,启动数据发送,发送移位寄存器的数据会在 TX 引脚输出,低位在前,高位在后。如果是同步模式 SCLK 也输出时钟信号。
一个字符帧发送需要三个部分:起始位+数据帧+停止位。起始位是一个位周期的低电平,位周期就是每一位占用的时间;数据帧就是我们要发送的 8 位或 9 位数据,数据是从最低位开始传输的;停止位是一定时间周期的高电平。
停止位时间长短是可以通过 USART 控制寄存器 2(USART_CR2)的 STOP[1:0]位控制,可选 0.5 个、1 个、1.5 个和 2 个停止位。默认使用 1 个停止位。2 个停止位适用于正常USART 模式、单线模式和调制解调器模式。0.5 个和 1.5 个停止位用于智能卡模式。
当发送使能位 TE 置 1 之后,发送器开始会先发送一个空闲帧(一个数据帧长度的高电平),接下来就可以往 USART_DR 寄存器写入要发送的数据。在写入最后一个数据后,需要等待 USART 状态寄存器(USART_SR)的 TC 位为 1,表示数据传输完成,如果USART_CR1 寄存器的 TCIE 位置1,将产生中断。
TE 发送使能
TXE 发送寄存器为空,发送单个字节的时候使用
TC 发送完成,发送多个字节数据的时候使用
TXIE 发送完成中断使能
接收器
如果将 USART_CR1 寄存器的 RE 位置 1,使能 USART 接收,使得接收器在 RX 线开始搜索起始位。在确定到起始位后就根据 RX 线电平状态把数据存放在接收移位寄存器内。接收完成后就把接收移位寄存器数据移到 RDR 内,并把 USART_SR 寄存器的 RXNE 位置1,同时如果USART_CR2 寄存器的 RXNEIE 置 1 的话可以产生中断。
RE 接收使能
RXNE 读数据寄存器非空
RXNEIE 发送完成中断使能
4.波特率
波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,单位为波特。比特率指单位时间内传输的比特数,单位 bit/s(bps)。对于 USART 波特率与比特率相等,以后不区分这两个概念。波特率越大,传输速率越快。
5.校验控制
STM32F103 系列控制器 USART 支持奇偶校验。当使用校验位时,串口传输的长度将是 8 位的数据帧加上 1 位的校验位总共 9 位,此时 USART_CR1 寄存器的 M 位需要设置为1,即 9 数据位。将 USART_CR1 寄存器的 PCE 位置 1 就可以启动奇偶校验控制,奇偶校验由硬件自动完成。启动了奇偶校验控制之后,在发送数据帧时会自动添加校验位,接收数据时自动验证校验位。接收数据时如果出现奇偶校验位验证失败,会见 USART_SR 寄存器的 PE 位置 1,并可以产生奇偶校验中断。使能了奇偶校验控制后,每个字符帧的格式将变成:起始位+数据帧+校验位+停止位。
6.中断控制
中断事件 事件标志 使能控制位
发送数据寄存器为空 TXE TXEIE
CTS 标志 CTS CTSIE
发送完成 TC TCIE
准备好读取接收到的数据 RXNE RXNEIE
检测到上溢错误 ORE RXNEIE
检测到空闲线路 IDLE IDLEIE
奇偶校验错误 PE PEIE
断路标志 LBD LBDIE
多缓冲通信中的噪声标志、
上溢错误和帧错误 NF/ORE/FE EIE
二、代码相关
stm32f10x_usart.h
typedef struct
{
uint32_t USART_BaudRate; //波特率
/*一般设置为 2400、9600、19200、115200。*/
uint16_t USART_WordLength; //字长
/*数据帧字长,可选 8 位或 9 位。它设定 USART_CR1 寄存器的 M 位的值。如果没有使能奇偶验控制,一般使用 8 数据位;如果使能了奇偶校验则一般设置为 9 数据位。*/
uint16_t USART_StopBits; //停止位
/*停止位设置,可选 0.5 个、1 个、1.5 个和 2 个停止位,它设定USART_CR2 寄存器的 STOP[1:0]位的值,一般我们选择 1 个停止位。*/
uint16_t USART_Parity; //效验位
/*奇 偶 校 验 控 制 选 择 , 可 选 USART_Parity_No( 无校验 ) 、USART_Parity_Even( 偶校验 ) 以 及 USART_Parity_Odd( 奇 校 验 ) , 它 设 定USART_CR1 寄存器的 PCE 位和 PS 位的值。*/
uint16_t USART_Mode; //USART模式
/*有 USART_Mode_Rx 和 USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定 USART_CR1 寄存器的 RE 位和 TE 位。*/
uint16_t USART_HardwareFlowControl; //硬件流控制
/*硬件流控制选择,只有在硬件流控制模式才有效,可选有⑴使能 RTS、⑵使能 CTS、⑶同时使能 RTS 和 CTS、⑷不使能硬件流。*/
} USART_InitTypeDef;//USART初始化结构体
typedef struct
{
uint16_t USART_Clock; //时钟使能控制
/*同步模式下 SCLK 引脚上时钟输出使能控制,可选禁止时钟输出(USART_Clock_Disable)或开启时钟输出(USART_Clock_Enable);如果使用同步模式发送,一般都需要开启时钟。它设定 USART_CR2 寄存器的 CLKEN 位的值。*/
uint16_t USART_CPOL; //时钟极性
/*同步模式下 SCLK 引脚上输出时钟极性设置,可设置在空闲时SCLK 引脚为低(USART_CPOL_Low)或高电平(USART_CPOL_High)。它设定 USART_CR2 寄存器的 CPOL 位的值*/
uint16_t USART_CPHA; //时钟相位
/*同步模式下 SCLK 引脚上输出时钟相位设置,可设置在时钟第一个变化沿捕获数据(USART_CPHA_1Edge)或在时钟第二个变化沿捕获数据。它设定 USART_CR2 寄存器的 CPHA 位的值。USART_CPHA 与 USART_CPOL 配合使用可以获得多种模式时钟关系*/
uint16_t USART_LastBit; //最尾位时钟脉冲
/*选择在发送最后一个数据位的时候时钟脉冲是否在 SCLK 引脚输 出 , 可 以 是 不 输 出 脉 冲 (USART_LastBit_Disable) 、 输 出 脉 冲(USART_LastBit_Enable)。它设定 USART_CR2 寄存器的 LBCL 位的值。*/
} USART_ClockInitTypeDef;//USART时钟初始化结构体
1.代码实现步骤
a.使能 RX 和 TX 引脚 GPIO 时钟和 USART 时钟
b.初始化 GPIO,并将 GPIO 复用到 USART 上
c.配置 USART 参数
d.配置中断控制器并使能 USART 接收中断
e.使能 USART
f.在 USART 接收中断服务函数实现数据接收和发送
bsp_uasrt.h
#ifndef __BSP_UART_H
#define __BSP_UART_H
#include "stm32f10x.h"
#include "stdio.h"
//USART1
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200
// USART GPIO
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10
#define DEBUG_USART_IRQ USART1_IRQn
#define DEBUG_USART_IRQHandler USART1_IRQHandler
void DEBUG_UART_Config(void); //中断服务函数
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
#endif /* __BSP_UART_H */
bsp_uart.c
#include "./uart/bsp_uart.h"
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/*嵌套向量中断控制器组选择*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置 USART 为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
/* 抢断优先级为 1*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级为 1*/
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 **/
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置 NVIC*/
NVIC_Init(&NVIC_InitStructure);
}
void DEBUG_UART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
//1.初始化GPIO
// 打开串口 GPIO 的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
//将 USART Tx 的 GPIO 配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将 USART Rx 的 GPIO 配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
//2.串口的初始化结构体
//打开串口外设时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_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;
// 完成串口的初始化配置
USART_Init(DEBUG_USARTx, &USART_InitStructure);
/*--------------------------------------------------------*/
// 串口中断优先级配置
NVIC_Configuration();
// 使能串口接收中断
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
/*--------------------------------------------------------*/
//3.使能串口
//使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
//发送一个字节
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
/*发送一个字节数据到USART */
USART_SendData(pUSARTx,ch);
/*等待发送数据寄存器为空*/
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
//发送字符串
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
unsigned int k=0;
do
{
Usart_SendByte( pUSARTx, *(str + k) );
k++;
} while(*(str + k)!='\0');
//等待发送完成
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
{}
}
///重定向printf到串口
int fputc(int ch, FILE *f)
{
/*发送一个字符串数据到串口*/
USART_SendData(DEBUG_USARTx, (uint8_t) ch);
/*等待完毕*/
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return (ch);
}
///重定向scanf到串口
int fgetc(FILE *f)
{
/*等待串口输入数据*/
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(DEBUG_USARTx);
}
stm32f10x_it.c中中断服务函数
//串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
{
ucTemp = USART_ReceiveData(DEBUG_USARTx);
USART_SendData(DEBUG_USARTx,ucTemp);
}
}
使能了 USART 接收中断,当 USART 有接收到数据就会执行USART_IRQHandler 函数。USART_GetITStatus 函数与 USART_GetFlagStatus 函数类似用来获取标志位状态,但USART_GetITStatus 函数是专门用来获取中断事件标志的,并返回该标志位状态。使用 if 语句来判断是否是真的产生 USART 数据接收这个中断事件,如果是真的就使用 USART 数据读取函数 USART_ReceiveData 读取数据到指定存储区。然后再调用 USART 数据发送函数USART_SendData 把数据又发送给源设备,即 PC 端的串口调试助手。
main.c
#include "stm32f10x.h"
#include "./led/bsp_led.h"
#include "./uart/bsp_uart.h"
void delay(uint32_t count)
{
for(; count!=0; count--);
}
int main(void)
{
uint8_t temp;
LED_GPIO_Config();
DEBUG_UART_Config();
Usart_SendString(DEBUG_USARTx, "野火mini");
printf("2321welcome\r\n");
while(1)
{
temp = getchar();
printf("接收到的字符为:%c\n",temp);
switch(temp)
{
case '1':
LED1_ON;
LED2_OFF;
break;
case '2':
LED2_ON;
LED1_OFF;
break;
default:
LED2_OFF;
LED1_OFF;
break;
}
}
实验现象是利用串口助手发送1,只有灯1亮,发送2,只有灯2亮,除此之外两灯都灭。
用qt做一个界面来对应显示。

本文详细介绍了STM32微控制器中串口USART的基本原理和配置方法,包括功能引脚说明、数据寄存器作用、波特率设置、奇偶校验及中断控制等内容,并通过实例展示了串口通信的代码实现过程。
1346

被折叠的 条评论
为什么被折叠?



