STMCU小助手
发布时间:2022-8-30 19:13
|
15.1 关于串口 15.1.1 串口理论知识8 Y# B0 z$ M4 \1 l 说到串口,经常提到TTL、RS232、RS422、RS485,简单的说,就是为了适应不同的环境条件,使用了不同的电平标准。假如微处理器和板载的蓝牙透传模块通信时,一般就使用TTL电平,引脚直接连接即可。假如微处理器在工业现场,需要连接一个几十米外的装置,则应该考虑将TTL电平转为RS232、RS422、RS485。, ^4 Z1 `0 Y: x. u7 C7 S _' V }. u- U+ S' p8 Z) J( t0 j0 g1 c
; I5 C3 ]: x1 E" L& z! l, F 图 15.1.1 不同通信电平应用的场景 . \4 K4 z9 z7 N1 V9 Q& l# A r. N7 u" j# ]; Y 如下表 15.1.1 是几个通信接口标准总结。可以发现为了加大传输距离,人们依次尝试了增加电压、差分传输等方式。
- I8 b6 @8 h- R K- A. u 表 15.1.1 常见通信接口标准 3 s9 S$ U! _+ b4 \2 z/ K- F' k以TTL电平的接口为例讲解串口怎么传输数据,对于RS232/RS422/RS485等接口,仅仅是把TTL电平转换为不同的电平值,或者转换为差分信号。/ K W) c# I" S- a" g " m/ e8 m* l% o- s6 { TTL接口的串口,硬件连接如图 15.1.2 所示。
图 15.1.2 TTL电平的串口硬件连接示意图 4 ]2 `5 n! A1 m' `% V% Q+ I+ D: c1 M) T 串口传输中的一些概念如下: 6 M) z6 n5 ?! V% A \ 波特率:一般选波特率都会有9600,19200,115200等选项。其实意思就是每秒传输这么多个比特位数(bit); 起始位:先发出一个逻辑”0”的信号,表示传输数据的开始;( {1 e) S+ Z2 M/ w 数据位:可以是5~8位逻辑”0”或”1”,先传输bit 0,再传输bit 1,依次类推;0 ^, `+ N2 ^9 _3 d0 W5 ` 5 f2 p u3 l, L W 校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。校验位是可选的,可以不传输;4 ~" G# M% E7 r0 s$ C 停止位:它是一个字符数据的结束标志,数据线变回逻辑”1”;6 U8 B* e" Q Q% Q2 @7 H6 k! x 1 _: f( l& }5 r+ @$ _ . ~. Y) @+ a, t) v8 {- L 怎么发送一字节数据,比如“A”?“A”的ASCII值是0x41,二进制就是01000001,怎样把这8位数据发送给对方呢? 6 M% v* h" h [/ ^% Z 双方约定好波特率、数据格式(数据位个数、停止位个数、是否使用校验位、奇校验还是偶校验),假设数据位是8,停止位是1,校验位是1;, y* q0 O1 z0 O) b* m* {' u 初始电平为逻辑1; 发送方输出逻辑0,并保持1位的时间;接收方检测到逻辑0,就知道对方准备发送数据了;* U* h4 ~( |) M $ g% H. M) B4 S2 \. N* W 发送方根据数据的bit 0设置引脚电平,并保持1位的时间;接收方读取引脚电平,得到bit 0; 发送方根据数据的bit 1设置引脚电平,并保持1位的时间;接收方读取引脚电平,得到bit 1;0 L% M4 Q* o- }0 O4 _# A' I) z L 1 G& f, n- \' K' K! m( b" k- x 以此类推,发出8位数据; ) }6 [6 Q+ N Z8 ]0 Q; F( H l发送方计算出校验值,设置引脚,并保持1位的时间;接收方读取引脚电平,得到校验值;注意,这步可以省略; 发送方输出逻辑1,并保持1位的时间;接收方读取引脚电平,直到数据传输结束; 5 y, K4 [: z8 Z8 ^ 信号的波形图如图 15.1.3 所示。8 ?- G$ ^2 c5 D C, o/ V; y. ^( K , g& A9 I0 O, O' k7 H2 s) Q, Y% D5 J
图 15.1.3 串口信号传输波形图 7 N7 S7 q% {+ K7 m+ a 15.1.2 STM32的串口 . R2 V$ s B; T( F1 f; R' l在嵌入式中,很多MCU和外设模块都集成有UART外设。STM32F103系列最多有3个通用同步异步收发器(Universal synchronous asynchronous receiver transmitter,USART),2个通用异步收发器(Universal asynchronous receiver transmitter,UART)。USART和UART的主要区别在于,USART支持同步通信,该模式有一根时钟线提供时钟。$ n/ d* b, B1 Q* U# E* z 2 ~, g: W9 P3 U5 o* \; L C/ x 串口在嵌入式中经常使用,一般使用UART就足够了,常见的用途如下:( h Y4 E' O. @ 6 ~) _ u/ `8 s1 r) ?& [ 1. 作为调试口,打印程序运行的状态信息; 2. 连接串口接口的模块(比如GPS模块),传输数据;% p$ q& {% \7 O( k 3. 通过电平转换芯片变为RS232/RS485电平,连接工控设备;0 V8 `$ t0 f8 ~9 S% Z) I5 z , G, h! N- N- `; g: J/ O# H STM32F103系列不同USART所支持的功能如表 15.1.2 所示,STM32F103C8T6只有USART1/2/3。8 `: b) H( }$ x0 d4 F, ]3 [
表 15.1.2 STM32F103系列不同USART所支持功能 USART内部结构的结构如图 15.1.4 所示。 , f( w# |8 ~$ J' c1 q
图 15.1.4 USART内部结构 + M. k1 z3 w, e3 F3 t& w& j; T. d2 |* X' o. \ 可以把USART分成四部分:5 W. e9 [3 a# R& T ①:USART引脚8 P$ L8 E2 [& L* N; Z- W- Z' q' o TX:数据发送; RX:数据接收; " g$ K! G6 M, m4 ?# X7 v SW_RX:在单线和智能卡模式下接收数据,属于内部引脚,没有具体外部引脚;6 O7 C5 G* g" | $ O$ L, {# s6 @2 U. k. x RTS:在硬件流控制时,用于指示本设备准备好可接收数据,低电平说明本设备可以接收数据; CTS:在硬件流控制时,用于指示本设备准备好可发送数据,低电平说明本设备可以发送数据; 1 t S7 o0 i3 q& p Z9 J CK:在同步模式时,用于输出时钟;; m. ^* q% e- H1 W }* j; s: n ②:波特率发生器 通过设置USART_BRR寄存器的值,实现串口通信数据传输速率的设置。由《参考手册》可知计算公式为: 8 l9 \8 y7 e/ _4 o
其中“Tx/Rx Baudrate”为波特率,“f_PCLK”为该外设USART的时钟频率,“USARTDIV”为USART_BRR寄存器的值。7 b- u$ d& q1 v" e, M 6 w( I2 N9 x D 假设所需波特率为115200,当前USART时钟为72MHz,则USARTDIV=72000000/(115200*16)=39.0625。USART_BRR寄存器使用高12位[15:4]存放整数部分,低4位[3:0]存放小数部分,小数部分每一位对应1/24=0.0625。因此,整数39对应16进制为0x27,左移4位为0x270,小数0.0625,对应0x1,USART_BRR=0x271即可。5 I" \$ u+ K; C+ A0 t* F# E2 \$ C 在利用寄存器配置USART的波特率的时候需要依据此公式计算USART_BRR的值,而在HAL库中无需计算,只需传入所需波特率,自动写USART_BRR寄存器值,但是我们仍然要学习这个波特率的计算公式,也许的开发调试过程中会使用到。 ; N1 O; J4 D2 x! P% Q 前面计算波特率需要知道外设时钟的值,由前面图 6.1.2 可知,USART1挂载APB1上,USART2/3和USART4/5挂载APB2上。由前面图 9.1.1 可知,APB1时钟最大为36MHz,APB2时钟最大为72MHz。因此,只有USART1的波特率计算中的能取最大系统时钟72MHz,而其它的USART/UART只能取36MHz。6 |5 _. t# t! P' U( F ③:发送器/接收器控制单元7 \1 I# F- z$ o$ o; P 通过向控制寄存器CR1、CR2、CR3和状态寄存器SR写入相应的位,可实现对USART数据的发送和接收控制。其中CR1主要用于配置USART的数据位、校验位和中断使能,CR2用于配置USART的停止位和SCLK时钟控制,CR3用于CTS硬件流控制、DMA多缓冲控制等。通过读取状态寄存器SR的值,可查询USART的状态。" Y7 @) H6 g2 E 0 ~7 {! X' O" i6 p4 E " V+ O( f7 S$ U& b ④:数据收发寄存器单元" p% `! P+ d2 Y5 T" D' t/ t% r) C 该部分主要由发送数据寄存器(TDR)、发送移位寄存器、接收数据寄存器(RDR)、接收移位寄存器组成。发送移位寄存和接收移位寄存器,分别负责将发送数据并串转换和接收数据串并转换,从而实现数据在传输时,是一位一位的发送和接收。1 _; e# K9 T! a- z9 G+ ~; w! r 15.2 硬件设计, ]6 F+ o* A% c" \" R6 W! c 如图 15.2.1 为开发板USB串口部分的原理图,U2是UART转USB的芯片CH340G,可自动将UART信号转换为USB信号,右边是串口自动下载电路,在前面5.2.4 调试/下载电路有过分析,这里不再赘述。 1 {4 f ]# A# T" g C MCU的USART1_TX(PA9)和USART1_RX(PA10)接到U2,U2收到数据后自动将UART信号转换成USB信号。 : V8 n; z) q! K& @" C 如图 15.2.2 所示为开发板USB切换电路,拨码开关P2决定开关芯片U4、U5的6脚电平高低。如果6脚为低,开关芯片3脚与4脚接通,即串口的USB信号连接USB口;如果6脚为高,开关芯片1脚与4脚接通,即MCU的USB信号连接USB口,实现了USB口的两种状态。6 D1 i- S7 M, r- N $ y h* U0 U A) k; ^2 |
' i4 o, _. e3 A6 u9 B0 _ 图 15.2.1 USB调试串口电路 3 J) S. }0 G. B1 Z+ q
% U/ Y! ~" U# o; q$ c& g3 b8 w 图 15.2.2 USB切换电路 : I4 N/ Y* k8 R$ }. }1 G* h& Z( m+ }! Y; w( B 3 f U, F) n6 M4 ]6 K 15.3 软件设计 15.3.1 软件设计思路& ], {. f! V! r: V. W ; l; K1 b# v; q; T$ E( I 实验目的:本实验使用USART1向电脑发送打印信息。 - m$ D5 L/ j& K1 f 1) 初始化USART:设置波特率,收发选择,有效数据位等 6 N4 {4 ], a1 I {* h2 o6 _ 2) 串口引脚初始化:USART使能、GPIO端口时钟使能、GPIO引脚设置为USART复用;, J1 B3 p3 P. a9 o' h 8 Z5 {: J5 p( ~; V5 ^0 T 3) 重定向printf和scanf; 5 R3 _ X) P- G2 ] 4) 主函数调用USRAT初始化函数,使用printf打印输出,使用scanf获取输入; 本实验配套代码位于“5_程序源码\7_通信—调试串口\”。: L9 i1 i* \3 p9 M' G " P$ F4 E1 r( n {% H 15.3.2 软件设计讲解 5 t: W2 a9 p: l; G/ e 1) GPIO引脚选择与串口选择 代码段 15.3.1 引脚宏定义(driver_usart.h)$ `* E5 x* R; d: p' D- j 0 B9 `4 ~% E3 T, {$ j) ?
定义使用串口、对应GPIO、时钟使能,方便代码复用。 0 @* v) B) |, _ | / }3 ^) u0 }) u& a1 n a! p7 f 2) 初始化USART USART初始化包含两部分:协议部分和硬件部分。/ C0 Z4 |5 ]. f: S5 T 协议部分与硬件无关,比如USART的波特率、奇偶校验、停止位等,通过“HAL_UART_Init()”设置。 硬件部分指承载的硬件载体,比如串口所使用的发送、接收引脚的复用,通过“HAL_UART_MspInit()”设置。函数名中的Msp(MCU Specific Package,MCU特定软件包),就是指MCU相关的硬件初始化。 代码段 15.3.2 USART初始化(driver_usart.c)4 W4 f: [- X) V1 t6 C5 g% g: Y
4行:定义一个“UART_HandleTypeDef”结构体(官方也称句柄)变量husart,用于保存串口参数设置; $ W; _' x; ^( i+ Y. {" Q 15~21行:设置串口参数,也就是husart: j/ t, X( h) g2 e, o) T # ?4 o4 _+ ~9 ]; a; D0 Y: \; V Instance:USART寄存器及地址,即哪一个USART; Init.BaudRate:“UART_HandleTypeDef”结构体里“UART_InitTypeDef”结构体成员,该属性为波特率;: n' Z7 `" w! f' D! d ! A% j* b: g- G7 \( w Init.WordLength:每次发送的字节长度,通常设置为8bit;7 \7 u* F' h9 a5 }7 F : G2 ^) u9 Q' |+ H Init.StopBits:每次发送后停止位长度,通常设置为1bit;' \9 k7 Z- T8 J; c1 C& p ' J: B1 N2 o2 I Init.Parity:奇偶校验,通常设置为不校验;6 w& I" t3 |& m2 g7 [ 7 O6 I5 c" i4 ?. ]0 _& ~ Init.Mode:收发模式,通常设置为可收可发;1 }) D* E( h T" @. w w, B& f1 j8 v 3 l+ d* L% j! h X) X# {1 m& V3 g Init.HwFlowCtl:流控设置,没有用到(CTS/RTS),通常设置为None; 24行:调用“HAL_UART_Init()”,传入设置的husart,初始化串口;) T+ O; P6 K2 D “HAL_UART_Init()”会调用“HAL_UART_MspInit()”,从而实现对涉及的硬件初始化,用户需要覆写(HAL库提供函数名,函数内容需要自己编写)该函数,完成使能串口时钟、初始化TX/RX的引脚、设置USART1的中断优先级且使能中断等。" D5 l; A" q7 t' ]( M5 F 代码段 15.3.3 USART MSP初始化(driver_usart.c) 6 g& I% x) u: d K! T. S5 L- r' {2 @
- ^% N. f1 m) q# N 11行:定义一个“GPIO_InitTypeDef”结构体变量GPIO_InitStruct,用于保存GPIO参数设置;6 \1 j7 E S7 ]+ J! a* D 12行:当系统中用到多个串口时,都会调用“HAL_USART_MspInit”进行硬件初始化,因此需要判断是否为USARTx(宏定义对应USART1); * ?3 \; r* S* u; K) \# ? ^4 y 15行:使能USART1时钟;: E* k( y, x, k! U- \# [ 18行:使能USART1_TX(PA9)和USART1_RX(PA10)所在组时钟; ' e. v3 R$ ~" L0 C 23~26行:设置USART的发送引脚: u- y4 ^! q% n# N& c; \ Pin:指定引脚号;6 A& f- }5 p1 ? Z# R Mode:配置为复用推挽功能;5 T9 v! G; z* K# G) e5 K Pull:默认上拉即可;' |/ g4 g& D8 I3 L Speed:作为通信信号引脚,设置为High; ! ~- C | a0 g3 n, @4 z 27行:使用“HAL_GPIO_Init()”初始化该引脚; 29~31行:设置USART的接收引脚;- e2 m8 ]6 R; y2 s0 |: {0 g : h1 A) _ A) b. ~1 T( |* z * U6 p# u V3 A2 a: Q3 Z 3) 重定向打印函数/ h" k% R7 q5 ?- n4 B* } 以上初始化完成后,就可以使用HAL库提供的“HAL_UART_Transmit()”从串口发送数据,使用“HAL_UART_Receive()”接收数据,但这样使用不方便,需要自己处理数据类型。在学习C语言时,通常使用printf将数据格式化打印,比较方便。因此,这里需要重定向打印函数,在使用printf时调用“HAL_UART_Transmit()”打印。 代码段 15.3.4 重定向打印函数(driver_usart.c) # m9 e9 n1 }1 }& a, M
% {% s' B' }/ E% U$ n+ d printf和scanf会分别调用“fputc()”和“fgetc()”,因此这里覆写这两个函数,使用HAL提供的函数实现收发数据。 同时,还需要需要点击“ ”,打开工程选项界面,切换到“Target”标签,勾选上“Use MicroLIB”,如图 15.3.1 所示。
! Q& \2 R8 b. s9 W: I 图 15.3.1 勾选“Use MicroLIB” ! N* c% _7 z: f7 Q7 P6 G4 T* j t9 a/ `. w: z4 s0 G1 p 也可以添加如下代码,就不用勾选“Use MicroLIB”,两个方法二选其一即可。) A' K9 c: o- f5 v 代码段 15.3.5 可选添加内容(driver_usart.c) ( W/ A9 }) q4 [8 W6 f6 Y
4) 主函数测试5 `: z. t* ^% l 8 ]8 i# O1 T1 B3 _ E
主函数直接使用“printf()”便可调用USART1打印,使用“scanf()”便可调用USART1接收数据。 & m/ b7 f$ h0 K8 y4 F* C( z 2行:初始化USART1,设置波特率为常用的115200;! ~1 {+ U: u' | 6~7行:中文打印测试;如果编译报错,需要点击“ ”,打开工程选项界面,切换到“C/C++”标签,在“Misc Controls”里加上“"--locale=english"”,如图 15.3.2 所示。+ K; `/ E3 L& {: ]; ^/ B5 r8 N 8行:字符打印测试; ' c" e' t5 x( ^, w% p; [, S 9~10行:字符串打印测试;' v0 |; L# Y0 b 2 d5 j# O4 g7 o% [ 11~12行:正负整打印疑测试; 13~14行:十六进制打印测试; 15~16行:浮点数打印测试; 0 s- J% J/ }) J8 t3 T3 o5 ?) u 21~29行:通过“scanf()”函数获取输入,如果输入的指定内容,则打印指定内容;
' h) }' d8 h3 K' p* ^* N9 ^7 B 图 15.3.2 指定本地编译环境 ' B3 N2 R% S3 M: q- m% B 15.4 实验效果' U7 H i& }7 J$ E) o- q6 O 本实验对应配套资料的“5_程序源码\7_通信—调试串口\”。首先如图 15.4.1 所示将开发板插入电脑USB口连接,注意拨码开关拨向非ON一端。打开MobaXterm,设置串口会话,如图 15.4.2 所示。 ①点击做上角“Session”,创建会话; 2 N8 [: w+ }# o ②在中间弹出的窗口,选择“Serial”,即串口;8 F* b. P/ T( W ③下拉选择串口端口,我这里是COM6,读者可能编号不一样,但后面显示的芯片型号是一样的,选择“USB-SERIAL CH340 ”即可;- E' I0 C1 }3 H* l$ t ④波特率选择115200,与主函数中串口初始化设置的波特率保持一致; ⑤以为串口是异步通信,需要双方统一传输规则,这里和代码段 15.3.2 中的设置保持一致。数据位8位,停止位1位,无校验,无流控;/ x% p' d8 c9 C7 s: p- y& J ⑥设置完后,点击“OK”; ( l. ~4 L9 N# `6 v1 V3 j 打开代码工程,使用Keil编译,下载程序,MobaXtrem显示如图 15.4.3 所示界面,所有打印正常显示。在电脑键盘输入“C”或“c”,即可控制串口打印“Hello World”。! V" r1 X/ J: D6 R/ c' `
图 15.4.1 调试串口连接实物图 ; j6 j! \7 W; y% p4 o, w1 s& G) U/ {1 h g
图 15.4.2 创建串口会话 1 h& a# o9 z! O. Z$ h2 }+ Z5 N0 K- w5 p) q$ L. v j6 A
图 15.4.3 串口打印效果 ( C0 X4 q9 C, I. ^作者:攻城狮子黄 ' j4 N% Y) [+ m5 |/ H |
STM32C542RC开发板的串口不定长数据接收实验
STM32C542RC开发板的串口DMA发送及接收实验
终于完成了STM32C542RC开发板的串口发送实验
STM32L562DK探索板——串口通讯的再测试
经验分享 | STM32 IAP应用时的跳转注意事项
实战经验 | 碰到hardfault问题你还在死磕代码吗?
【逢7发帖赢大礼】STM32开发之WIFI实时时钟
【逢7发帖赢大礼】STM32开发之指纹识别!
【逢7发帖赢大礼】STM32开发之环境空气质量监测
【逢7发帖赢大礼】STM32开发之人体实时运动信息监测!
微信公众号
手机版