野火学习笔记(13) —— USART —串口通讯
文章目录
- 1. 串口通讯协议简介
- 1.1 物理层
- 1.1.1 电平标准
- 1.1.2 RS-232 信号线
- 1.2 协议层
- 1.2.1 波特率
- 1.2.2 通讯的起始和停止信号
- 1.2.3 有效数据
- 1.2.4 数据校验
- 2. STM32 的 USART 简介
- 3. USART 功能框图
- 3.1 校验控制
- 3.2 中断控制
- 4. USART 初始化结构体详解
- 5. USART1 接发通信实验
- 5.1 硬件设计
- 5.2 软件设计
- 5.2.1 编程要点
- 5.2.2 代码分析
1. 串口通讯协议简介
串口通讯 (Serial Communication) 是一种设备间非常常用的串行通讯方式,因为它简单便捷,因此大部分电子设备都支持该通讯方式,电子工程师在调试设备时也经常使用该通讯方式输出调试信息。
在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和片上外设;STM32 标准库则是在寄存器与用户代码之间的软件层。对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理层和协议层。物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。简单来说物理层规定我们用嘴巴还是用肢体来交流,协议层则规定我们用中文还是英文来交流。
1.1 物理层
串口通讯的物理层有很多标准及变种,我们主要讲解 RS-232 标准, RS-232 标准主要规定了信号的用途、通讯接口以及信号的电平标准。
使用 RS-232 标准的串口设备间常见的通讯结构见 图串口通讯结构图。
图串口通讯结构图
在上面的通讯方式中,两个通讯设备的 “DB9 接口”之间通过串口信号线建立起连接,串口信号线中使用“RS-232 标准”传输数据信号。由于 RS-232 电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的 “TTL 标准”的电平信号,才能实现通讯。
1.1.1 电平标准
根据通讯使用的电平标准不同,串口通讯可分为 TTL 标准及 RS-232 标准,见 表 TTL 电平标准与 RS232 电平标准。
表 TTL 电平标准与 RS232 电平标准
我们知道常见的电子电路中常使用 TTL 的电平标准,理想状态下,使用 5V 表示二进制逻辑 1,使用 0V 表示逻辑 0;而为了增加串口通讯的远距离传输及抗干扰能力,它使用 -15V 表示逻辑 1,+15V 表示逻辑 0。使用 RS232 与 TTL 电平校准表示同一个信号时的对比见图 RS-232 与 TTL 电平标准下表示同一个信号。
因为控制器一般使用 TTL 电平标准,所以常常会使用 MA3232 芯片对 TTL 及 RS-232 电平的信号进行互相转换。
1.1.2 RS-232 信号线
在最初的应用中, RS-232 串口标准常用于计算机、路由与调制调解器 (MODEN,俗称“猫” ) 之间的通讯,在这种通讯系统中,设备被分为数据终端设备 DTE(计算机、路由) 和数据通讯设备 DCE(调制调解器)。我们以这种通讯模型讲解它们的信号线连接方式及各个信号线的作用。
在旧式的台式计算机中一般会有 RS-232 标准的 COM 口 (也称 DB9 接口),见 图电脑主板上的 COM 口及串口线。
图电脑主板上的 COM 口及串口线
其中接线口以针式引出信号线的称为公头,以孔式引出信号线的称为母头。在计算机中一般引出公头接口,而在调制调解器设备中引出的一般为母头,使用上图中的串口线即可把它与计算机连接起来。通讯时,串口线中传输的信号就是使用前面讲解的 RS-232 标准调制的。
在这种应用场合下, DB9 接口中的公头及母头的各个引脚的标准信号线接法见 图 DB9 标准的公头及母头接法 及 表 DB9 信号线说明。
图 DB9 标准的公头及母头接法
表 DB9 信号线说明
上表中的是计算机端的 DB9 公头标准接法,由于两个通讯设备之间的收发信号 (RXD 与 TXD) 应交叉相连,所以调制调解器端的 DB9 母头的收发信号接法一般与公头的相反,两个设备之间连接时,只要使用“直通型”的串口线连接起来即可,见 图计算机与调制调解器的信号线连接。
图计算机与调制调解器的信号线连接
串口线中的 RTS、 CTS、 DSR、 DTR 及 DCD 信号,使用逻辑 1 表示信号有效,逻辑 0 表示信号无效。例如,当计算机端控制 DTR 信号线表示为逻辑 1 时,它是为了告知远端的调制调解器,本机已准备好接收数据, 0 则表示还没准备就绪。
在目前的其它工业控制使用的串口通讯中,一般只使用 RXD、 TXD 以及 GND 三条信号线,直接传输数据信号,而 RTS、 CTS、 DSR、 DTR 及 DCD 信号都被裁剪掉了。
1.2 协议层
串口通讯的数据包由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口。在串口通讯的协议层中,规定了数据包的内容,它由启始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据,其组成见 图串口数据包的基本组成。
图串口数据包的基本组成
1.2.1 波特率
本章中主要讲解的是串口异步通讯,异步通讯中由于没有时钟信号 (如前面讲解的 DB9 接口中是没有时钟信号的),所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码,图串口数据包的基本组成 中用虚线分开的每一格就是代表一个码元。常见的波特率为 4800、 9600、 115200 等。
1.2.2 通讯的起始和停止信号
串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑 0 的数据位表示,而数据包的停止信号可由 0.5、 1、 1.5 或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。
1.2.3 有效数据
在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、 6、 7 或 8 位长。
1.2.4 数据校验
在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验 (odd)、偶校验(even)、 0 校验 (space)、 1 校验 (mark) 以及无校验 (noparity)。
奇校验要求有效数据和校验位中“1”的个数为奇数,比如一个 8 位长的有效数据为: 01101001,此时总共有 4 个“1”,为达到奇校验效果,校验位为“1”,最后传输的数据将是 8 位的有效数据加上 1 位的校验位总共 9 位。
偶校验与奇校验要求刚好相反,要求帧数据和校验位中“1”的个数为偶数,比如数据帧: 11001010,此时数据帧“1”的个数为 4 个,所以偶校验位为“0”。
0 校验是不管有效数据中的内容是什么,校验位总为“0”, 1 校验是校验位总为“1”。
2. STM32 的 USART 简介
通用同步异步收发器 (Universal Synchronous Asynchronous Receiver and Transmitter) 是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。有别于 USART 还有一个 UART(Universal Asynchronous Receiver and Transmitter),它是在 USART 基础上裁剪掉了同步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是 UART。
串行通信一般是以帧格式传输数据,即是一帧一帧的传输,每帧包含有起始信号、数据信息、停止信息,可能还有校验信息。 USART 就是对这些传输参数有具体规定,当然也不是只有唯一一个参数值,很多参数值都可以自定义设置,只是增强它的兼容性。
USART 满足外部设备对工业标准 NRZ 异步串行数据格式的要求,并且使用了小数波特率发生器,可以提供多种波特率,使得它的应用更加广泛。 USART 支持同步单向通信和半双工单线通信;还支持局域互连网络 LIN、智能卡 (SmartCard) 协议与 lrDA(红外线数据协会) SIR ENDEC 规范。
USART 支持使用 DMA,可实现高速数据通信,有关 DMA 具体应用将在 DMA 章节作具体讲解。
USART 在 STM32 应用最多莫过于“打印”程序信息,一般在硬件设计时都会预留一个 USART 通信接口连接电脑,用于在调试程序是可以把一些调试信息“打印”在电脑端的串口调试助手工具上,从而了解程序运行是否正确、如果出错哪具体哪里出错等等。
3. USART 功能框图
USART 的功能框图包含了 USART 最核心内容,掌握了功能框图,对 USART 就有一个整体的把握,在编程时就思路就非常清晰。 USART 功能框图见 图 USART 功能框图。
图 USART 功能框图
① 功能引脚
TX:发送数据输出引脚。
RX:接收数据输入引脚。
SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引脚。
nRTS:请求以发送 (Request To Send), n 表示低电平有效。如果使能 RTS 流控制,当 USART 接收器准备好接收新数据时就会将 nRTS 变成低电平;当接收寄存器已满时, nRTS 将被设置为高电平。该引脚只适用于硬件流控制。
nCTS:清除以发送 (Clear To Send), n 表示低电平有效。如果使能 CTS 流控制,发送器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。
SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。
USART 引脚在 STM32F103ZET6 芯片具体分布见表 STM32F103VET6 芯片的 USART 引脚。
STM32F103VET6 系统控制器有三个 USART 和两个 UART,其中 USART1 和时钟来源于 APB2 总线时钟,其最大频率为 72MHz,其他四个的时钟来源于 APB1 总线时钟,其最大频率为 36MHz。UART 只是异步传输功能,所以没有 SCLK、 nCTS 和 nRTS 功能引脚。
② 数据寄存器
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。
USART 支持 DMA 传输,可以实现高速数据传输,具体 DMA 使用将在 DMA 章节讲解。
③ 控制器
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 个停止位用于智能卡模式。
当选择 8 位字长,使用 1 个停止位时,具体发送字符时序 图见图字符发送时序图。
图见图字符发送时序图
当发送使能位 TE 置 1 之后,发送器开始会先发送一个空闲帧 (一个数据帧长度的高电平),接下来就可以往 USART_DR 寄存器写入要发送的数据。在写入最后一个数据后,需要等待 USART 状态寄存器 (USART_SR) 的 TC 位为 1,表示数据传输完成,如果 USART_CR1 寄存器的 TCIE 位置 1,将产生中断。
在发送数据时,编程的时候有几个比较重要的标志位我们来总结下。
接收器
如果将 USART_CR1 寄存器的 RE 位置 1,使能 USART 接收,使得接收器在 RX 线开始搜索起始位。在确定到起始位后就根据 RX 线电平状态把数据存放在接收移位寄存器内。接收完成后就把接收移位寄存器数据移到 RDR 内,并把 USART_SR 寄存器的 RXNE 位置 1,同时如果 USART_CR2 寄存器的 RXNEIE 置 1 的话可以产生中断。
在接收数据时,编程的时候有几个比较重要的标志位我们来总结下。
④ 小数波特率生成
波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,单位为波特。比特率指单位时间内传输的比特数,单位 bit/s(bps)。对于 USART 波特率与比特率相等,以后不区分这两个概念。波特率越大,传输速率越快。
USART 的发送器和接收器使用相同的波特率。计算公式如下:
其中, fPLCK 为 USART 时钟, USARTDIV 是一个存放在波特率寄存器 (USART_BRR) 的一个无符号定点数。其中 DIV_Mantissa[11:0] 位定义 USARTDIV 的整数部分, DIV_Fraction[3:0] 位定义USARTDIV 的小数部分。
例如: DIV_Mantissa=24(0x18), DIV_Fraction=10(0x0A),此时 USART_BRR 值为 0x18A;那么 USARTDIV 的小数位 10/16=0.625;整数位 24,最终 USARTDIV 的值为 24.625。
波特率的常用值有 2400、 9600、 19200、 115200。下面以实例讲解如何设定寄存器值得到波特率的值。
我们知道 USART1 使用 APB2 总线时钟,最高可达 72MHz,其他 USART 的最高频率为 36MHz。我们选取 USART1 作为实例讲解,即 fPLCK=72MHz。为得到 115200bps 的波特率,此时:
115200 = 72000000 / (16 * USARTDIV)
解得 USARTDIV=39.0625,可算得 DIV_Fraction = 0.0625 * 16 = 1 = 0x01, DIV_Mantissa = 39 = 0x17,即应该设置 USART_BRR 的值为 0x171。
3.1 校验控制
STM32F103 系列控制器 USART 支持奇偶校验。当使用校验位时,串口传输的长度将是 8 位的数据帧加上 1 位的校验位总共 9 位,此时 USART_CR1 寄存器的 M 位需要设置为 1,即 9 数据位。将 USART_CR1 寄存器的 PCE 位置 1 就可以启动奇偶校验控制,奇偶校验由硬件自动完成。启动了奇偶校验控制之后,在发送数据帧时会自动添加校验位,接收数据时自动验证校验位。接收数据时如果出现奇偶校验位验证失败,会见 USART_SR 寄存器的 PE 位置 1,并可以产生奇偶校验中断。
使能了奇偶校验控制后,每个字符帧的格式将变成:起始位 + 数据帧 + 校验位 + 停止位。
3.2 中断控制
USART 有多个中断请求事件,具体见 表 USART 中断请求。
表 USART 中断请求
4. USART 初始化结构体详解
标准库函数对每个外设都建立了一个初始化结构体,比如 USART_InitTypeDef,结构体成员用于设置外设工作参数,并由外设初始化配置函数,比如 USART_Init() 调用,这些设定参数将会设置外设相应的寄存器,达到配置外设工作环境的目的。
初始化结构体和初始化库函数配合使用是标准库精髓所在,理解了初始化结构体每个成员意义基本上就可以对该外设运用自如了。初始化结构体定义在 stm32f10x_usart.h 文件中,初始化库函数定义在 stm32f10x_usart.c 文件中,编程时我们可以结合这两个文件内注释使用。
UART 初始化结构体
typedef struct {uint32_t USART_BaudRate; // 波特率uint16_t USART_WordLength; // 字长uint16_t USART_StopBits; // 停止位uint16_t USART_Parity; // 校验位uint16_t USART_Mode; // USART 模式uint16_t USART_HardwareFlowControl; // 硬件流控制
}USART_InitTypeDef;
USART_BaudRate:波特率设置。一般设置为 2400、 9600、 19200、 115200。标准库函数会根据设定值计算得到 USARTDIV 值,从而设置 USART_BRR 寄存器值。
USART_WordLength:数据帧字长,可选 8 位或 9 位。它设定 USART_CR1 寄存器的 M 位的值。如果没有使能奇偶校验控制,一般使用 8 数据位;如果使能了奇偶校验则一般设置为 9 数据位。
USART_StopBits:停止位设置,可选 0.5 个、 1 个、 1.5 个和 2 个停止位,它设定 USART_CR2 寄存器的 STOP[1:0] 位的值,一般我们选择 1 个停止位。
USART_Parity:奇偶校验控制选择,可选 USART_Parity_No(无校验)、 USART_Parity_Even(偶校验) 以及 USART_Parity_Odd(奇校验),它设定 USART_CR1 寄存器的 PCE 位和 PS 位的值。
USART_Mode: USART 模式选择,有 USART_Mode_Rx 和 USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定 USART_CR1 寄存器的 RE 位和 TE 位。
USART_HardwareFlowControl:硬件流控制选择,只有在硬件流控制模式才有效,可选有⑴ 使能 RTS、⑵ 使能 CTS、⑶ 同时使能 RTS 和 CTS、⑷ 不使能硬件流。
当使用同步模式时需要配置 SCLK 引脚输出脉冲的属性,标准库使用一个时钟初始化结构体 USART_ClockInitTypeDef 来设置,该结构体内容也只有在同步模式才需要设置。
USART 时钟初始化结构体
typedef struct {uint16_t USART_Clock; // 时钟使能控制uint16_t USART_CPOL; // 时钟极性uint16_t USART_CPHA; // 时钟相位uint16_t USART_LastBit; // 最尾位时钟脉冲
}USART_ClockInitTypeDef;
USART_Clock:同步模式下 SCLK 引脚上时钟输出使能控制,可选禁止时钟输出 (USART_Clock_Disable) 或开启时钟输出 (USART_Clock_Enable);如果使用同步模式发送,一般都需要开启时钟。它设定 USART_CR2 寄存器的 CLKEN 位的值。
USART_CPOL:同步模式下 SCLK 引脚上输出时钟极性设置,可设置在空闲时 SCLK 引脚为低电平 (USART_CPOL_Low) 或高电平 (USART_CPOL_High)。它设定 USART_CR2 寄存器的 CPOL位的值。
USART_CPHA:同步模式下 SCLK 引脚上输出时钟相位设置,可设置在时钟第一个变化沿捕获数据 (USART_CPHA_1Edge) 或在时钟第二个变化沿捕获数据。它设定 USART_CR2 寄存器的 CPHA 位的值。 USART_CPHA 与 USART_CPOL 配合使用可以获得多种模式时钟关系。
USART_LastBit:选择在发送最后一个数据位的时候时钟脉冲是否在 SCLK 引脚输出,可以是不输出脉冲 (USART_LastBit_Disable)、输出脉冲 (USART_LastBit_Enable)。它设定 USART_CR2 寄存器的 LBCL 位的值。
5. USART1 接发通信实验
USART 只需两根信号线即可完成双向通信,对硬件要求低,使得很多模块都预留 USART 接口来实现与其他模块或者控制器进行数据传输,比如 GSM 模块, WIFI 模块、蓝牙模块等等。在硬件设计时,注意还需要一根“共地线”。
我们经常使用 USART 来实现控制器与电脑之间的数据传输。这使得我们调试程序非常方便,比如我们可以把一些变量的值、函数的返回值、寄存器标志位等等通过 USART 发送到串口调试助手,这样我们可以非常清楚程序的运行状态,当我们正式发布程序时再把这些调试信息去除即可。
我们不仅仅可以将数据发送到串口调试助手,我们还可以在串口调试助手发送数据给控制器,控制器程序根据接收到的数据进行下一步工作。
首先,我们来编写一个程序实现开发板与电脑通信,在开发板上电时通过 USART 发送一串字符串给电脑,然后开发板进入中断接收等待状态,如果电脑有发送数据过来,开发板就会产生中断,我们在中断服务函数接收数据,并马上把数据返回发送给电脑。
5.1 硬件设计
为利用 USART 实现开发板与电脑通信,需要用到一个 USB 转 USART 的 IC,我们选择 CH340G 芯片来实现这个功能, CH340G 是一个 USB 总线的转接芯片,实现 USB 转 USART、 USB 转 lrDA 红外或者 USB 转打印机接口,我们使用其 USB 转 USART 功能。具体电路设计见 图 USB 转串口硬件设计。
我们将 CH340G 的 TXD 引脚与 USART1 的 RX 引脚连接, CH340G 的 RXD 引脚与 USART1 的TX 引脚连接。 CH340G 芯片集成在开发板上,其地线 (GND) 已与控制器的 GND 连通。
图 USB 转串口硬件设计
5.2 软件设计
这里只讲解核心的部分代码,有些变量的设置,头文件的包含等并没有涉及到,完整的代码请参考本章配套的工程。我们创建了两个文件: usart.c 和 usart.h 文件用来存放 USART 驱动程序及相关宏定义。
5.2.1 编程要点
- 使能 RX 和 TX 引脚 GPIO 时钟和 USART 时钟;
- 初始化 GPIO,并将 GPIO 复用到 USART 上;
- 配置 USART 参数;
- 配置中断控制器并使能 USART 接收中断;
- 使能 USART;
- 在 USART 接收中断服务函数实现数据接收和发送。
5.2.2 代码分析
注意事项:
- 硬件连接。主机 TX 引脚接从机 RX 引脚,主机 RX 引脚接从机 TX 引脚,必须交叉连接。主机的地线必须与从机地线相连。
- 使用串口时,主机与从机的配置要一致(波特率、校验位、数据位、停止位,传输数据类型是 HEX,还是其他类型)。
- 使用 printf 函数时,要包含 stdio.h 文件。用以下代码,或者野火的代码,需要勾选一下设置
STM32 use microlib是干什么的
uart.h
#ifndef __UART_H
#define __UART_H#include "stm32f10x.h"
#include <stdio.h>void UART_GPIO_Init(void);
void UART_Config(u32 bound);
void UART_NVIC_Init(void);
void UART_Init(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);#endif
uart.c
#include "uart.h"/*******************************************************************************
* 函 数 名 : UART_GPIO_Init
* 函数功能 : UART GPIO 初始化函数
* 输 入 : 无
* 输 出 : 无
* 说 明 :无
*******************************************************************************/
void UART_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;// 使能PA端口时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// USART1_TX GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // 复用推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // IO口速度为50MHzGPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;// 根据设定参数初始化GPIO_Init(GPIOA, &GPIO_InitStructure);// USART1_RX GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;// 浮空输入模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;// 根据设定参数初始化GPIO_Init(GPIOA, &GPIO_InitStructure);
}/*******************************************************************************
* 函 数 名 : UART_Config
* 函数功能 : UART 配置初始化函数
* 输 入 : 串口波特率
* 输 出 : 无
* 说 明 :无
*******************************************************************************/
void UART_Config(u32 bound)
{USART_InitTypeDef USART_InitStructure;// 使能 USART1 时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);// 串口波特率USART_InitStructure.USART_BaudRate = bound;// 字长为8位数据格式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; // 初始化串口1USART_Init(USART1, &USART_InitStructure);
}/*******************************************************************************
* 函 数 名 : UART_NVIC_Init
* 函数功能 : UART NVIC 初始化函数
* 输 入 : 无
* 输 出 : 无
* 说 明 :无
*******************************************************************************/
void UART_NVIC_Init(void)
{NVIC_InitTypeDef NVIC_InitStructure;// Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;// 抢占优先级3NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;// 子优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;// IRQ通道使能NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;// 根据指定的参数初始化VIC寄存器NVIC_Init(&NVIC_InitStructure); // 开启串口接受中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}/*******************************************************************************
* 函 数 名 : UART_Init
* 函数功能 : UART 总初始化函数
* 输 入 : 无
* 输 出 : 无
* 说 明 :无
*******************************************************************************/
void UART_Init(void)
{// UART GPIO 初始化UART_GPIO_Init();// UART 配置初始化,波特率为 9600UART_Config(9600);// UART NVIC 初始化UART_NVIC_Init();// 使能串口1USART_Cmd(USART1, ENABLE);
}/*******************************************************************************
* 函 数 名 : Usart_SendByte
* 函数功能 : Usart 发送一个字节
* 输 入 : pUSARTx:串口ch:字节
* 输 出 : 无
* 说 明 :无
*******************************************************************************/
void Usart_SendByte(USART_TypeDef * pUSARTx, uint8_t ch)
{// 发送一个字节数据到USARTUSART_SendData(pUSARTx,ch);// 等待发送数据寄存器为空while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}/*******************************************************************************
* 函 数 名 : Usart_SendArray
* 函数功能 : Usart 发送数组
* 输 入 : pUSARTx:串口array:数组头num:数组个数
* 输 出 : 无
* 说 明 :无
*******************************************************************************/
void Usart_SendArray(USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{uint8_t i;for(i=0; i<num; i++){// 发送一个字节数据到USARTUsart_SendByte(pUSARTx,array[i]); }// 等待发送完成while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}/*******************************************************************************
* 函 数 名 : Usart_SendString
* 函数功能 : Usart 发送字符串
* 输 入 : pUSARTx:串口str:字符串
* 输 出 : 无
* 说 明 :无
*******************************************************************************/
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);
}/*******************************************************************************
* 函 数 名 : Usart_SendHalfWord
* 函数功能 : Usart 发送一个16位数
* 输 入 : pUSARTx:串口ch:16位数
* 输 出 : 无
* 说 明 :无
*******************************************************************************/
void Usart_SendHalfWord(USART_TypeDef * pUSARTx, uint16_t ch)
{uint8_t temp_h, temp_l;// 取出高八位temp_h = (ch&0XFF00)>>8;// 取出低八位temp_l = ch&0XFF;// 发送高八位USART_SendData(pUSARTx,temp_h); // 等待发送完毕while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);// 发送低八位USART_SendData(pUSARTx,temp_l); // 等待发送完毕while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}/*******************************************************************************
* 函 数 名 : fputc
* 函数功能 : 重定向c库函数printf到串口,重定向后可使用printf函数
* 输 入 : 无
* 输 出 : 无
* 说 明 :无
*******************************************************************************/
int fputc(int ch, FILE *f)
{// 发送一个字节数据到串口USART_SendData(USART1, (uint8_t) ch);// 等待发送完毕while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); return (ch);
}/*******************************************************************************
* 函 数 名 : fgetc
* 函数功能 : 重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
* 输 入 : 无
* 输 出 : 无
* 说 明 :无
*******************************************************************************/
int fgetc(FILE *f)
{// 等待串口输入数据while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);return (int)USART_ReceiveData(USART1);
}/*******************************************************************************
* 函 数 名 : USART1_IRQHandler
* 函数功能 : USART1 中断函数
* 输 入 : 无
* 输 出 : 无
* 说 明 :每接收到一个数据就发送出去
*******************************************************************************/
void USART1_IRQHandler(void)
{uint8_t ucTemp;if(USART_GetITStatus(USART1, USART_IT_RXNE)!= RESET){// 接收数据ucTemp = USART_ReceiveData(USART1);// 发送接收到的数据USART_SendData(USART1,ucTemp); }
}
main.c
#include "uart.h"int main(void)
{ // 串口初始化UART_Init();Usart_SendString(USART1,"这个使用Usart_SendString发送的\n");printf("这个使用printf发送的\n\n\n\n");while(1);
}
摘抄自:
[野火EmbedFire]《STM32库开发实战指南——基于野火霸道开发板》.pdf
野火学习笔记(13) —— USART —串口通讯相关推荐
- 《STM32从零开始学习历程》——USART串口通讯实验篇1——中断接收与发送
<STM32从零开始学习历程>@EnzoReventon USART串口通讯实验篇1--中断接收与发送 最近开始接触了STM32F4xx系列单片机,对于我这个从零开始学习的小白来说,可谓困 ...
- 《STM32从零开始学习历程》——USART串口通讯实验篇2——指令控制LED灯实验
<STM32从零开始学习历程>@EnzoReventon USART串口通讯实验篇2--指令控制LED灯实验 本实验是在<USART串口通讯实验篇1--中断接收与发送>的基础上 ...
- 学习记录:USART—串口通讯
目录 1.串口通讯协议简介 1.1物理层 1.2 电平标准 2.协议层 3.printf函数的支持代码 4.usart.h程序 5.main.c主函数 6.实验现象 1.串口通讯协议简介 对于通讯协议 ...
- STM32 HAL库开发学习笔记: USART1串口通讯(中断方式) IDE-STM32CubeIDE
STM32串口通讯有三种方式,分别为阻塞(轮询).中断.DMA.这里将用中断的方式开发. 笔者也是刚入门STM32 HAL库开发,该笔记致希望于能帮到初学者,文中配置步骤.代码.实验现象均是笔者实践可 ...
- stm32学习笔记-9 USART串口
9 USART串口 文章目录 9 USART串口 9.1 串口通信协议 9.2 stm32的片上外设-USART 9.3 USART收发相关实验 9.3.1 实验1:串口发送 9.3.2 实验2:移植 ...
- 【STM32入门100步--学习笔记】USART串口接收程序(含教学视频)
STM32串口接收的程序和8051单片机一样,有2种接收方式:中断处理和查询方式. 试过两种方式之后发现,中断处理方式更方便,可以扩展多个数据的接收.推荐中断方式. 我已经将本资料内容整理后录制了视频 ...
- 基于STM32F103C8的USART串口通讯程序
目录 一.串口协议和RS-232.485标准,RS232.485电平.与TTL电平的区别,"USB/TTL转232"模块的工作原理(以CH340芯片模块为例) 一.串口协议 STM ...
- 基于STM32安装Stm32CubeMX,配合Keil完成LED流水灯和USART串口通讯程序,及其管脚的时序波形观察
目录 一.使用HAL库完成LED流水灯 (一).用STM32CUBEMX生成工程文件 (二).用Keil仿真调试并生成hex文件 (三).用STM32串口烧录程序 (四).实物连接 二.使用HAL库完 ...
- STM32的USART串口通讯程序(查询方式)
STM32的USART串口通讯程序(查询方式) 文章目录 STM32的USART串口通讯程序(查询方式) 一.USART介绍 1.异步通信: 2.同步通信: 二.CubeMX创建项目 1.点击ACCE ...
- 基于STM32的USART串口通讯程序
文章目录 一.串口协议和RS-232.485标准,以及RS232.485电平与TTL电平的区别,USB/TTL转232模块的工作原理 1.串口协议 2.RS-232.RS-485标准 3.RS232. ...
最新文章
- 服务器空岛怎么修改地形,迷你世界空岛地形码是什么 空岛地形码怎么输入[多图]...
- H5_ 多媒体video,autio使用示例
- 转载]SA权限九种上传方法
- 使用mp4v2将aac音频h264视频数据封装成mp4开发心得
- iHealth基于Docker的DevOps CI/CD实践
- linux下DHCP的安装配置
- java 网页正文抽取算法_网页正文抽取算法 ContentExtractor
- 三维计算机视觉(二)--点云滤波
- 前后端交互模式大总结 艾提拉 总结 attilax总结 目录 1. 通过ajax ajax就是js的网络api 完全解耦合 推荐	1 1.1. Query Ajax 操作函数	1 1.2. 服务
- java object 转 double_Java 类型转换以及Object转成其他类型
- bzoj1127: [POI2008]KUP
- 如何关闭任务栏中chrome浏览器的通知图标
- VMware中共享文件夹功能的使用(ubuntu)
- 使用LM324实现信号放大及电压比较综合报警电路
- unicode、utf区别
- 【chrome插件开发二】chrome常用语法汇总
- 魔法宝石 spfa
- 聊一聊数学中的基本定理(三)——代数基本定理
- 洛谷P2403 [SDOI2010]所驼门王的宝藏
- 引见GPG饬令加密文件的措施
热门文章
- 关于C语言从键盘输入内容到数组
- 基于MATLAB的函数信号发生器
- sniffer 嗅探器
- 最小拍控制算法c语言,第六章最小拍控制
- html 仿微信语音输入,AudioRecord仿微信录制语音
- Spring Boot:Canonical names should be kebab-case (‘-‘ separated), lowercase alpha-numeric characters
- 在Qt环境下进行人机交互界面设计--工具条
- 系统好看 字体font-family
- Problem L: 最简分式
- numpy.ones用法