最近能抽点时间学一下stm32了,串口通信用的挺多的,比如wifi模块,GSM模块,指纹模块等等…在这里用自己理解的写一下总结,如果有误的话请大家多多指点。
1 C/ j! Z* J R* t& W串口的基本概念
?" M0 ]( Y7 @& h串行和并行) T% N5 X# v+ V a
串行
& @! v' ?( P2 c. Z/ f( f1 R% ?串行是一位一位的传输。
' D6 K2 X! Y: _" L常用的有 USART、IIC、SPI等…
+ U1 H* s* i% v. z串行也分为 同步通信 和 异步通信
, |1 l- R. q0 t8 E! n: o) F) I同步:
3 f1 z# ?' B9 {8 B3 N就是一般有一根时钟线,有时钟就可以大家一起同步嘛。靠时钟来约定。一根数据线。一般一个时钟传输一个Bit位。同步的话 他们大部分都是有效数据来的。但是对于双方的时钟允许误差较小。
( _* m3 d6 b6 ?; _9 u% J* s& ]4 e% H! k: x. @8 L
异步:" M3 F V- V5 Z5 p* H
异步就是我们平常玩串口用的最多的,它不像同步有个时钟,异步是没有时钟,那我们得为了数据不出错,所以通过 起始位、奇偶校验位、停止位这些来降低数据的错误。4 y" I0 C+ ]" A: R3 k1 k( E! E
所以异步的有效数据就没有同步的有效数据那么多。效率也没有同步的那么高。
: t) R ^7 }6 X$ n; B" B5 C
& @1 S" `, F' x* K2 e! p优点:传输距离远、抗干扰能力强、成本较低
3 ?' ^3 F" m# t缺点:传输速率慢: v2 I* T3 ?' { g( ?
, R' k# Q0 x; l& J" k并行
; \( T I5 l; m5 n) B并行是指多比特数据同时通过并行线进行传送,这样数据传送速度大大提高。
' [2 H( O; Z9 u- Y4 K1 y ] B简单的理解就是 比如我们用的LCD1602不是有一共10几个引脚么,然后8根是数据线,这就是属于并行,它们通常需要数据总线(八、十六或更多线路)。! X) P/ |; l- E% ~- n7 v
. Q' z! W- }9 ?" G' s2 P( M
优点:线比较多嘛,所以我传输就快,这个容易理解。
/ H4 D1 U" d; @缺点:线这么多,那成本也会高嘛,别人一根线搞定了,你用了8根是吧。然后距离长了,那也容易受到干扰。* x* \, B( t) P
) V' w3 H, h: h: _ C7 T3 H通信方式5 R# {) f# s0 J' ~5 v; j S# F
全双工9 `* f C$ D+ j! S' e; B& _
就是双方都可以同时发送和接收,相当于我们的打电话。6 |+ ]9 h$ V; w9 ]2 A
! o4 n" b" x9 h0 r半双工
$ x6 w! `+ s! a; M- r3 k8 v在同一时刻,只能一个发送、一个接收,相当于我们的对讲机。9 E+ {8 u- D$ p
7 t+ l4 H. x/ h# k! q3 h$ I- K/ w8 I
单工3 E" A1 U2 s+ V1 t2 c+ n6 t6 d0 W
这个就是一个只能发,一个只能收。相当于广播,他那边只能发,我们只能听。- @ Q. `" G, h* l4 }1 [# z
9 P8 K8 i1 ~ L. [7 `5 H0 z. U6 P
波特率( H! d* k# h% u5 @% W+ }8 H" r
首先讲一下比特率/ [ j/ s6 S, _3 Z" P" R
比特率:每秒钟传输的二进制位数,单位是(bit/s)& C# S8 B3 e- m: i
跟波特率有点区别的,但是有时候也一样。; S4 ?! _9 B) E+ U2 c0 @
8 S& {6 c7 p& t7 R波特率:表示每秒钟传输的码元个数。 ^9 r* j7 A2 y5 M, {8 i
码元:比如我们玩51的时候基本都是5V OV那么 5V相当于二进制1 0V就相当于二进制0。
1 r0 c' ?* F R3 {" N" Z5V——1
% Z+ [$ h* O$ t$ q0V——0# U7 E: j( d9 x
& D; F$ [3 R, [% l- H有时候多个,比如: R! t) I/ H, @: T
0V——00" p/ G/ V# f. N/ v
2V——01
W+ [9 N- _& M! D+ f: n* @& G% M" @4V——10
9 M8 w3 i6 o( K) U: S$ ^! I9 n+ Y6V——11 S; a. R: I" F7 g& k% `* E9 b* X! n
这样就跟比特率不一样了。! D& E: F" q9 }) B/ S1 t" F
- N6 \0 W! M4 U2 G4 V, U3 z波特率计算
7 R7 a2 Y. p" n( b, E0 A, |2 `* o波特率 = Fck/(16*USARTDIV)4 }& M0 s- b# D& `$ I5 d- c
Fck:串口的时钟
+ L) Z6 L$ L- X* ^( ~+ jUSARTDIV:无符号定点数2 m$ I, j0 c# N
/ V$ {$ L/ s7 O$ s# s$ `+ S
比如我想设置115200波特率8 B: i9 q) H) q7 W" ]+ N
时钟是72M, M$ a, F& E$ c) z' Q
那USARTDIV = 39.06251 ~- Z' e; J4 J5 y; s
; U, h3 c r! }& I; {8 ^7 S
7 h$ c3 |* I: T
' I' A6 D' B- P I2 K0 B串口的配置
7 k& |# v2 x( I, F4 Y1 w/ `3 _. t代码编写步骤" X( m- u3 @( q, |) l V# {3 v
我这里用USART1来举例。我这里是A9(TX)、A10(RX)(异步通信). ^6 a( M4 a+ J
1:打开对应 的时钟9 L; s& ^8 } z) y3 s: o- M; I
1.1:GPIO的时钟
: G7 ^! w3 u! @* t1.2:串口的时钟+ d2 J4 M- r- Z- u- L- D
; y- {, @, \, j- ]2.配置GPIO结构体+ z( K L* P2 r
2.1:引脚
, K! T7 [ k$ V/ f( B5 \8 g2.2:模式(输入还是输出)
! o* P) H/ g( d r2.3:速率(输出才用 输入不用)
, ]6 V9 U, ^2 {! p) [# C* E9 W2.4:对结构体成员初始化& u2 W8 D# l0 g/ V% |* C. h
* H3 m! q; _0 d6 V( R5 l# L0 w: C
3配置串口结构体/ u% L1 s0 c/ f
3.1:波特率" A+ W* q) t+ \7 Y2 C
3.2:有效数据位
- A) C" M# o8 w% _6 o! J' j% N. K4 f3.3:停止位6 w" O( b: N( K* a
3.4:奇偶校验位
3 s8 Z: G. \6 B( `3.5:硬件控制流
1 N# G7 r, L% m8 Z- V3.6:模式
/ N5 W8 I1 I2 z( L! D. r8 L3.7:对结构体成员初始化& m+ }5 W. i8 r2 r
4 |, d& @( u: V
4配置NVIC中断优先级(misc.h)
- e% i: p3 ]) {' z/ E如果不需要中断可以不配置NVIC
. {* [" m2 X' ~: `% z4.1:选择哪个组 NVIC_PriorityGroupConfig
; i+ h0 [. t: I" f3 i, V4 K5 T4.2::哪个中断通道 (stm32f10x.h里 IRQn_Type结构体里找)$ {" X( p2 J8 l3 v! U. _
4.3:主优先级6 X5 R9 f0 s* O$ T4 |8 ^+ Q u
4.4:次优先级
/ M F6 a+ W6 D2 [4.5:通道使能开启/ H7 L! A7 Z3 P+ p, G6 F9 [& _
4.6:对成员初始化
* W1 u2 `3 `% i# F3 Y* X6 c4.7:串口中断配置 USART_ITConfig(采用什么方式中断)
* B9 Y8 B: ?. {; k' g# i, d
3 j/ b3 `0 \. h% H5串口使能/ l1 b, M9 w+ G* q% G/ H' n
USART_Cmd();
7 j3 t5 U1 L. Q: z# G" a7 @) j
: p: ]. _$ i. M! A6:编写中断服务函数
* K; R6 i/ }: i/ e函数名字在启动文件里找。
3 N8 k% |7 p0 e7 o/ b. \( w& C8 r8 g1 a" Y9 ~7 a
串口的重定向# |5 O! [7 O ^0 x
比如我们常用输出函数0 }3 o6 B8 b b+ v/ h
printf();
7 t3 L# `; g1 c3 bputchar();$ }9 ]; t$ X1 P2 Y
常用的输入函数
9 X8 ]* Y9 A, ?) e9 Q) h) fscanf();1 h# {" |0 [: j! y9 R
getchar();# v& Y; K. `+ f3 H% H m
5 e" L# L3 y: h. J! G
输出( `7 p, e* I4 w0 i
==记得包含头文件 stdio.h ==/ W6 e) M- o; x$ g
其次你添加下面的函数既可以用printf 和 putchar了。- /*重定向C库函数printf到串口*/" d3 F. \9 [8 ^ m
- int fputc(int ch, FILE *f)" U- z9 o+ z% B, b1 R% M
- {
1 n. v8 ~7 T6 a# E - USART_SendData(USART1, (uint8_t)ch);; //发送一字节到串口& }: w! W3 j/ F; J1 D) P
- while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //等待发送寄存器为空 证明发送完8 `6 u0 O4 } q! I0 I K: r
- - ^4 _% ]8 w* I8 g5 d
- return ch;7 Y. i' ^' k9 T( S! l
- }
复制代码 9 G- `* ^& T" {6 s; p+ |9 b5 M7 e, f$ Z" A
输入7 b; n! F K6 U% g' x% U5 Y& h
==记得包含头文件 stdio.h ==
# O. W3 Z( v. F* P$ m其次你添加下面的函数既可以用scanf 和 getchar了。+ T) Q# C4 W3 { m/ S. {
' Y# l, Q) \) f2 P) K" o- /*重定向C库函数 scanf到串口*/
; @! C9 ^/ V8 S - int fgetc(FILE *f)
! g3 g) u( F2 m3 A- l9 T: E. v - {% E. }4 Q& T7 \3 E: d& a& O
- while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET); //如果读数据寄存器非空
8 P4 h* B) j6 ^ - return (int)USART_ReceiveData(USART1);# l& Z! E. D7 }7 W5 w* X
- }
复制代码
( Z3 {: ^, V) \6 f' b+ g串口的例程
# e. ?0 X' B( n" I# I我这里就实现一下 电脑端串口助手发送单字符到单片机,单片机接收到就立刻也发给电脑串口助手。
' d7 |+ T. {$ Z0 Q" t9 l比如 我在串口助手发送了 A
5 r6 o0 e% I, }8 e+ b$ J然后单片机接收到之后,让单片机发送receive data:A2 Z' h' b9 c6 q# J8 \+ [
: b2 g4 f' W; w. ^" ?& d4 t; jmain.c
5 ~* y9 d. S; o7 H$ o- #include "stm32f10x.h"
$ p* X I9 j: O3 k3 ^6 p - #include "usart.h"
0 M6 V5 \0 L8 z6 j" {
) ~" \* s N: F' B2 }! k5 T- int main()
; N6 `) V) V: u3 {. ?" [# P - {/ K; L) N& l2 v- R
- usart1_init(115200); //串口1初始化函数
0 I8 M9 N- T1 v2 o, F - while(1)1 p& R# H: C6 M! w! U. J4 a3 k
- {' s/ Q+ t% @% u- T& g, p! w
- }- ], K, T8 W* M5 O/ I0 q0 [
- }
. t% s; f: |. D4 P - ) w N' W( x# b6 X8 g% g, @
- /*因为用的是中断接收所以少不了中断服务函数*/) p1 I1 j) F& ]! @
- /*串口1 中断服务函数*/; b! i d+ c A* i( e! W3 f" x
- void USART1_IRQHandler(void)4 P2 K" `9 q2 a- f
- {4 W8 P7 _+ W4 h$ b# T. b6 ?0 |
- uint8_t data;
" i( W: m* O4 @ - if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //再次判断是否中断是否发生1 @, Q: w3 D6 ?3 t6 B( s% c6 u T
- {
! U( e' X; Q- v, G5 L0 T- N - data = USART_ReceiveData(USART1);; u" d& R. ]& e% C; m7 D
- }" q' U& y: o0 n& F& b. Z
- usart_sendString(USART1,"receive data:");. E; H- a* D0 {+ G, g5 E D
- usart_sendByte(USART1,data);
" W, w$ ?, l$ T- a7 O" a. b - usart_sendByte(USART1,'\n');
! ~" U' ]3 s2 j% F4 c: C - }
复制代码 ' H8 @' Y$ v& r& O! v
usart.c$ {( o1 {/ W6 ^6 S. s/ K: p
- /* 配置串口1 优先级 函数 */
5 U3 V' D$ p% @7 n7 R - static void NVIC_USART1_configuration(void)) r/ B/ |( o' r1 t
- {
O7 k1 v4 D+ y7 n4 {4 T' g - NVIC_InitTypeDef NVIC_initStruction;% w3 L1 R2 |- y6 I; a9 k
-
2 y6 r4 j% y& g. s$ ]8 Q3 s - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //组7 @4 R% j/ ^: {4 x1 l& I
- NVIC_initStruction.NVIC_IRQChannel = USART1_IRQn; //串口1中断/ Z" R3 m0 ^, y
- NVIC_initStruction.NVIC_IRQChannelPreemptionPriority = 0; //主优先级
8 g' D+ Y* W6 C4 T8 H - NVIC_initStruction.NVIC_IRQChannelSubPriority = 0; //次优先级
0 H/ F- H1 e3 M! j7 R6 k+ Y" I - NVIC_initStruction.NVIC_IRQChannelCmd = ENABLE; //使能
% |# A0 M2 i5 {- o - NVIC_Init(&NVIC_initStruction);
" B! k$ ?" ]' P" u* M. B - }0 e8 {6 X! T8 {
- ! a8 {, p& T/ @& r$ h
- /* 配置串口1 函数*/; x- T9 j9 R" W! ^+ Z; Y& v# m
- void usart1_init(uint32_t baudRate) o) Y7 {% k+ q2 q0 }
- {
+ Y7 q6 _& U+ B& ? - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //打开GPIOA时钟
/ ?1 E; v+ A0 k# A9 ^. c: @ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //打开串口1时钟
4 V6 r# Q* J9 @/ ]9 b+ N4 v7 K - * H: S! u" n% o6 _4 X0 w
- GPIO_InitTypeDef GPIO_initStruction; X2 |& F% |/ @/ r" P$ w7 e3 G# U
- USART_InitTypeDef USART_initStruction;
! l! D, k. W1 C+ G) F7 @# J - + {1 L! q8 |+ k+ x1 h
- /*配置GPIOA TX */% D3 b5 z: b, Y8 v8 \8 z
- GPIO_initStruction.GPIO_Pin = USART1_TX; // TX
. w& G& }% K/ D) l - GPIO_initStruction.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
: X4 R- @- D) @" ?; L! w# U+ e - GPIO_initStruction.GPIO_Speed = GPIO_Speed_50MHz;
; q' P# K$ h4 H0 j- u - GPIO_Init(GPIOA, &GPIO_initStruction);
% G6 ?( G D# x: n( W7 S8 I# s% b+ o, M - ; {* x T2 ?* U# I
- /*配置GPIOA RX */
0 p5 t. e" B( L9 V( s - GPIO_initStruction.GPIO_Pin = USART1_RX; // RX6 X4 b6 U; u" J* \$ [
- GPIO_initStruction.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入) b9 y" ^1 \; h7 L) t' U
- GPIO_Init(GPIOA, &GPIO_initStruction);
3 A% `- Y+ ?8 w; q. f2 S - % b' S/ S, X& r+ S( X7 B
- /*配置USART1 TX和RX */
% b$ M/ h% ?$ C0 x - USART_initStruction.USART_BaudRate = baudRate; //波特率
H( ^# W& `( H" k/ L. \ - USART_initStruction.USART_WordLength = USART_WordLength_8b; //8位有效数据位) z$ }7 O( ^: I* I* `6 u- ]
- USART_initStruction.USART_StopBits = USART_StopBits_1; //1个停止位" L' o: C0 \* ]
- USART_initStruction.USART_Parity = USART_Parity_No; //无奇偶校验位+ f3 s. k5 L& \9 [# Q
- USART_initStruction.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //不硬件控制流% [- I6 |5 S. {. v0 i
- USART_initStruction.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送 和 接收6 O& x$ \9 N: k! Q+ V" G; n& k& i Z
- USART_Init(USART1, &USART_initStruction);
: H2 N( H: ], g' Z- ^ - 7 y9 |; ]6 P9 O' s* Z I6 S2 B/ {: V
- NVIC_USART1_configuration(); //串口1中断优先级配置
5 y% r( h" y; }# ?, a [ - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//使能接收中断
8 I# }/ a1 b# r7 v/ i - USART_Cmd(USART1, ENABLE); //使能串口1
1 U, l; Q4 r7 j2 R - }+ r9 C3 u" p, _; c6 Z8 |# F
复制代码
8 `; j! j( `( ]$ k1 v效果呈现
7 J3 q6 ^+ o6 \2 b
C7 Y: g8 d& ~. z0 A
. K; d* ] e* W$ _————————————————
; A4 }6 w$ n& }: ~7 J0 u转载:皮卡丘吉尔
/ B, Y, R# z$ a1 y
}% h$ c- ]. }+ J& v; _
( M: x0 n4 j& J' L8 o2 N* ^' g5 x. V0 |+ j, L
|