问题:) b- N# a* E+ z; X
该问题由某客户提出,发生在 STM8S105C6T6 STM8S105C6T6 器件上。据其工程师讲述:当他所撰写的程序不使用奇偶校验的时候,程序工作是正常的;但是当他把奇偶校验改成偶检验 EVEN 时,程序无法正常工作;现象为:一、不管上位机发送的数据是不带校验位的,还是带奇偶校验位的,STM8S 都可以正常地接收到数据;二、当奇偶校验位使能后,接收到的数据再返回上位机,显示的数据不一定是正确的。3 Z% c- e6 ]' ~* _$ |! D; v: Y
/ O' y$ R1 n3 e5 J. c0 f
3 X" X2 V0 z3 x9 ? S调研:$ Y1 n0 R! v* k. p. o o8 r! G
检查客户的 UART 程序,UART 初始化程序如下:
( n- s/ M1 f: D2 F5 q* s) f- static void UART_Config(void)
5 _6 V' F; L2 J7 k- k/ T1 w - {
- {( q5 q# i3 z - /* UART2 configured as follow:
8 S- A# n- ~# m& _! G( P, B3 T - - BaudRate = 9600 baud$ G6 M4 m3 l3 B" S
- - Word Length = 8 Bits0 b7 e0 C. i6 g4 _( |% e [
- - One Stop Bit
! b% D6 l) u: b' `1 Q. J& T - - No parity
9 y0 R! L E: h - - Receive and transmit enabled5 `: F) ^* O8 X0 G
- */# o+ V( X& K5 ~ a& z, E' R
- UART2_DeInit();
' K; `( K) q9 l4 l. l' r - UART2_Init((uint32_t)9600, UART2_WORDLENGTH_8D, UART2_STOPBITS_1, UART2_PARITY_EVEN,( g, T/ d5 D1 `2 o4 M' C
- UART2_SYNCMODE_CLOCK_DISABLE, UART2_MODE_TXRX_ENABLE);2 V2 S0 v I! X9 s3 c8 O ~0 c
2 v. r3 i2 [9 w; B% e+ P9 f- /* Enable UART2 */6 m& D$ h7 [+ T: a) F( Y
- UART2_Cmd(ENABLE);
! h% z) P/ V& ~! N* P1 h - }
" q& Y/ j. g8 c/ H3 U
复制代码 7 G+ E6 }4 ?% m6 c$ C
主程序可简化为:* ?7 X9 v, G8 h4 o
, I0 S. b2 z1 b" @, s B: ^1 Z; m7 [+ t6 ~
- void main(void)
! c% h& F$ r1 ^0 ] i - {
# g5 p0 c# M2 K7 A1 Y# u - unsigned char val = 0x00;4 j5 A0 s: i( e1 |: S/ a
- /* CLK configuration -----------------------------------------*/ z$ R5 P6 N& h2 d- M/ G; S K
- CLK_Config();" Q& W# u7 T5 w2 ?1 u; V/ D
- /* UART configuration -----------------------------------------*/
$ j/ x1 U1 n$ A - UART_Config();: e9 j @- A( D6 o# h& ]! E% @
- v' W% j; R) N$ F* o. u5 G6 s
- while(1)
6 {6 v A$ f! Z) J - {
( f; J! w$ q% Y( m- j- f - /* Wait the byte is entirely received by UART2 */
6 b a+ L2 j/ I8 n, |. M5 H - while (UART2_GetFlagStatus(UART2_FLAG_RXNE) == RESET)& @; D4 {( a$ }9 ~5 y+ a$ |
- {
5 U9 y4 S0 D0 |4 j5 i* J9 t) C/ ^) \ - }
- D/ @6 @& i5 l O - /* Get data */
3 c& R! t* }" l6 d1 D1 { - val = UART2_ReceiveData8();
9 F# _! k. \4 O5 H, Z
" w# |/ g0 u+ q5 ~; }$ `+ I2 M$ m- /* Wait until end of transmit */
! S2 I6 b( k( |8 ] - while (UART2_GetFlagStatus(UART2_FLAG_TXE) == RESET)! i0 y! n& Q0 N8 i r/ l& I1 P
- {
" C' B3 }/ W$ K) m - }
4 ^1 i6 D* w9 }% k5 }6 V. [ U
7 s1 N& T4 U ] W- /* Send data */
, [# x9 }% n/ x/ O6 g - /* Write one byte in the UART1 Transmit Data Register */ z% Z* D3 c: N$ L6 a
- UART2_SendData8(val);- \# z0 Y: x" z* H) R+ X9 ]
- }
% ^! C+ ~1 f3 q; }8 L, e - }
复制代码
6 ]# ~6 o, r$ t$ L. x观察客户的程序,把 UART2_PARITY_EVEN 改回 UART2_PARITY_NO,确实就是正常的 8 个数据位不带奇偶校验的程序。那么为什么修改成带奇偶校验后,就会有客户所描述的问题呢? p! l, |1 j" B. c/ _- k
继续使用 UART2_PARITY_EVEN,我们用串口分析工具、示波器或逻辑分析仪来分析一下数据。
$ y" H# T1 h- i6 Z$ W• 当上位机发送 0x01 时(数据中有 1 个“1”),从串口分析工具可以看到上位机收到回来的数据变为 0x81;观察示波器波形,发现 STM8S 发出来的数据位在 Bit7 确实变为 1;- Y7 m- H* v9 ?8 ^4 c
• 当上位机发送 0x03 时(数据中有 2 个“1”),从串口分析工具可以看到上位机收到回来的数据为 0x03;观察示波器波形,发现数据倒是正确的,可是校验位“0”并不存在。
$ S- j' ~8 h$ c/ r" z" f6 s通过这两个实验,我们可以看到,好像是数据的位数不一致,STM8S 发出来的数据是 7 位的数据,也就是只有 Bit0~Bit6,Bit7 变成了奇偶校验位!
5 a% g) X. H& n; y e$ g( G( }& d6 B2 X" ?6 D1 Z' L. f* f* P
我们来看一下 STM8S 的参考手册是如何描述奇偶校验控制的,我们看一下帧格式的表格:
y( H' q/ H" ~4 I4 `; h" W" r4 K- K t9 v
) U3 ]. f/ T7 A/ r1 `7 |
' L8 p6 f! y/ Q" ?' C我们发现,在 STM8S 中 M 位所定义是帧长度,而不是数据位的长度!也就是说 M 位所定位的长度为“数据位+奇偶校验位”个数的总和。当数据位为 8 位时,不使用奇偶校验的时候,M 的长度为 8 位;而要使用奇偶校验的时候,M 的长度应该为 9 位!- L5 K4 } z, k1 y5 ?
% d) i$ ?* k- d- b3 S9 I
到此,我们应该知道如何去修改程序了,我们将主程序和 UART 的初始化程序分别修改为:
, ~5 P6 U8 C/ a# s- void main(void)+ }6 [. [* e/ k7 l* x u8 v. P
- {
; L) P* E/ u. j4 R9 l4 O) t - unsigned char val = 0x00;& q2 I E. o$ U
- /* CLK configuration -----------------------------------------*/8 A( H2 @& M; f1 A- T: T* u
- CLK_Config();$ m6 I+ q" Z1 R9 ^& L* T
- /* UART configuration -----------------------------------------*/
% u2 C7 L1 M+ D! H: c5 n - UART_Config();
: Y! j' X* W& D! ]# d
# W* y1 e6 f8 Y; I3 m1 S* v- while(1), Z6 I; H, W; [4 W x0 b# @
- {
" j0 m$ g, B- ^7 d1 ]9 u& L - /* Wait the byte is entirely received by UART2 */; e8 P ?' o$ O5 _8 x: b
- while (UART2_GetFlagStatus(UART2_FLAG_RXNE) == RESET)
7 ~% n* K' b4 R2 d% g- W - {, M3 j' p9 S4 o8 L
- }7 e5 Q, D* V# v" e; }9 ^2 u
- 7 ]& Y3 C- T& q/ {7 L6 `: }
- /* Get data */- F* Y$ L R8 u& @& P
- val = UART2_ReceiveData9();, I* Z: _3 l$ a' f; M( U8 E
- ) @- Z# M$ u0 w: n
- /* Wait until end of transmit */* ]; z8 [0 v9 }$ y
- while (UART2_GetFlagStatus(UART2_FLAG_TXE) == RESET)+ P& U2 }, _) E8 l8 y$ S1 `
- {5 Z# v l# Y" J4 i2 M# a6 G
- }2 R0 k; C g, P' f) r, t2 g X: [" w
9 O5 ?( ?. a; \9 \" ]- /* Send data */' W8 U8 y3 Q' y/ O& c! d( T5 J% b
- /* Write one byte in the UART1 Transmit Data Reg
; F& V1 f5 j- x% F - CLK_Config();
, _9 @: ^# {" L- `- l; ]' J! r - /* UART configuration -----------------------------------------*/' e! H5 n) k. k4 D' i
- UART_Config();2 {% n* i( }" @' U; c
- ' P! `; S5 a/ ]/ {( X
- while(1)* z- Y- J- K+ G, v& t7 m% V3 \
- {! Z0 B! N0 R6 K8 f! Q
- /* Wait the byte is entirely received by UART2 */
: T) r/ h+ Z, q - while (UART2_GetFlagStatus(UART2_FLAG_RXNE) == RESET)
7 J6 m; @5 G" ?6 E - {
C: }- g& R/ R6 ^! Q' D. j# v - }% R# F0 }/ n" t0 R
- 7 b! J7 v* \+ Y9 l4 S% w1 J' ~8 C
- /* Get data */
. N+ Y: Z7 K- t, p# P/ W1 j& H - val = UART2_ReceiveData9();3 Q5 L* K; {3 w9 c l# D
- , a+ K) J4 [# R# M5 V
- if(UART2_GetFlagStatus(UART2_FLAG_PE) == RESET)5 }( f% S1 ~: d% O2 S& j
- {
K9 S$ M2 `4 f1 N/ F - /* Wait until end of transmit */
# K V- ] v( H/ N0 E" ?$ I - while (UART2_GetFlagStatus(UART2_FLAG_TXE) == RESET)6 ~( _( D, D2 P* Y5 B5 u$ q- X$ {/ @
- {, ^) R2 J8 I0 L8 ]! n3 E
- }
1 U/ e% @7 I8 r) W - & G3 d4 [% E& {4 J$ M9 b
- /* Send data */1 v( T. B- }" r1 L) f8 f. ]
- /* Write one byte in the UART1 Transmit Data Register */
0 p0 b# }' {7 j U# }) j) ]6 Y - UART2_SendData9(val);7 X& r1 r& @1 V) z1 |1 v3 X
- }4 o& L8 M9 ^2 r2 N+ @* b* Z
- }( ?! A w" X3 O
- }
复制代码
9 y) b2 k. }' R. @; B6 B编译,再测试看看。我们将上位机端的串口分析软件的奇偶校验位仍然修改为 ODD,上位机发送“0103 07 0F”,结果发现上位机还是可以收到 STM8S 发来的“01 03 07 0F”!这是为什么呢?难道 PE是不起作用的,奇偶校验仍然是假的?当然,我们还是得来查一下为什么 PE 会检测不到呢。 h5 A0 @% ]7 n6 o3 J0 F" y; W9 b
& m) @1 @. {6 `3 j8 d @
我们再来看一下 PE 标志位是在什么情况下被清除的,我们在参考手册可以看到:要清除 PE 标志位,软件要按以下操作顺序进行执行:先读取 UART_SR,再读取 UART_DR。于是我们再看一下上面的程序,可以看到,程序中先执行了 UART2_GetFlagStatus(UART2_FLAG_RXNE),这是一个读取UART_SR 的过程,然后再执行 UART2_ReceiveData9(),这是一个读取 UART_DR 的过程,那么,至此,PE 标志即使之前已经置位,那么经过这两个操作后,早已被清除,这个时候再执行
6 O" \; _0 w3 j: G. }if(UART2_GetFlagStatus(UART2_FLAG_PE) == RESET)显然没什么意义。+ K4 F# J$ U9 L: j0 W
0 N% ^* F z: A* o- }* W
' f: r2 E% B% l; h0 ]! {) d- H+ R( p
所以,我们还得再继续修改一下程序:
# o+ x8 v2 @& J- void main(void); ~+ @2 E- u4 D% y
- {8 T# Q9 E1 o; T3 I( p
- unsigned char val = 0x00;, [+ ]" X/ v% ]3 C+ u, u
- /* CLK configuration -----------------------------------------*/. N- M$ w' ]+ q6 T h
- CLK_Config();6 j, C8 \1 ^$ q$ n6 g$ D- H
- /* UART configuration -----------------------------------------*/
! g3 H, E# Y, ] - UART_Config();! u! U$ i2 ?0 V0 P. T T! \$ i
- " t: L- V9 x. f9 ~ q
- while(1)
8 T0 B3 e- ]& S3 O4 {! }0 l - {! u1 m$ J5 T4 {3 S/ \+ p
- /* Wait the byte is entirely received by UART2 */. M+ z3 r& ?/ j" C7 g7 R: F7 a$ y
- while (UART2_GetFlagStatus(UART2_FLAG_RXNE) == RESET)
P# q+ C3 s% Z* t3 Q - { @, c' _0 C$ z c/ `
- }
0 @" h/ S) ], t, S, D* ~
) Y7 k; K* u0 b# L, |" n3 K- if(UART2_GetFlagStatus(UART2_FLAG_PE) == RESET)1 N) `. K0 b. Z9 C! G; p
- {- S/ n0 r4 ^4 u+ H' `9 q9 H
- /* Get data */
, J( g3 \& K& R! U8 G' o7 ? - val = UART2_ReceiveData9();
$ U6 X. J& U' O. j
' b: j) [6 h# ]! O2 {* G4 G1 n( g- /* Wait until end of transmit */& K1 A$ c# j5 ]- l- U# f
- while (UART2_GetFlagStatus(UART2_FLAG_TXE) == RESET)
7 o- \0 u( c4 b* I - {
) J; e, D6 \; V Z5 j7 B - } r2 y: L& I3 I* C5 [
1 f9 P. F6 }! G- /* Send data */
# i0 t+ k% Y: x. _3 B - /* Write one byte in the UART1 Transmit Data Register */
: Y: f! D2 n3 G; `; c7 o5 a, Q - UART2_SendData9(val);* m4 W: m( x$ S, _$ n
- }" {$ F% I2 K' G
- else
6 {- \3 q7 C5 b* b( ]8 N N - {
+ M6 w% M7 Z) Z4 c" P - /* Get data */
1 T/ K. _8 Y* R' h. |1 B' A! Y - val = UART2_ReceiveData9(); // For clear PE
; x+ u' S& [2 f1 d" S; U" e7 y) k - }) Q; j9 l5 U1 a# F D
- }
* R. `, \8 l4 m6 [4 Q9 K - }
复制代码
) I" [7 q% S5 t9 b再编译,再测试,得到了正确的结果:
( k2 Y, R% N2 f3 x! \, [' j6 _. e# T• 将上位机端的串口分析软件的奇偶校验位设置为 ODD,上位机发送“01 03 07 0F”,STM8S发现奇偶校验位不对,没有回传任何数据给上位机;
: F% i3 n, v9 ]• 将上位机端的串口分析软件的奇偶校验位仍然设置为 EVEN,上位机发送“01 03 07 0F”,上位机正确地收到 STM8S 发回来的“01 03 07 0F”。5 c# F: u" L& W: n* J
0 E3 \- k" J; t% z9 O+ \# l; x! y
结论:
+ H/ ?2 y$ d- _. n- X由于客户对 STM8S 的参考手册没有详细研究,误以为只要在程序中将 UART2_PARITY_NO 改为
3 h; \% y9 g! E+ M, `7 _2 _UART2_PARITY_EVEN,就可以实现 UART 的奇偶校验功能。8 _' `- |( T# h
8 C% C% d4 d- Z/ P+ e7 ~1 ^9 v7 O处理:
' u! V/ c- Z+ J4 u- s4 b# g修改程序,将帧长度相应的都改成 9 位,加入对 PE 标志位的判断,并且经过正确的操作顺序来进行判: b4 I' M. K' `% v0 Y
断处理。
G. o% S2 J1 y! c+ @# a& | l
1 i- {, F% p, x' }+ D5 ^0 y建议:9 I# x9 Q; {& v- @0 N3 H! I
- x/ M9 {1 S$ a0 g
当需要使用 UART 的奇偶校验功能时,需要注意以下几个方面:& y) H2 G) X @
9 m" q6 f B r: j
1. M 所代表为帧长度,而不是数据位的长度。当使用奇偶校验的时候,帧长度=数据位长度+奇偶校验位长度;" K8 j. z# D7 [7 Z
2. 不管数据奇偶校验位是否正确,UART 都会将数据接收回来。要对奇偶校验进行判断的话,必须对 PE 标志位进行检测,PE 标志位由硬件置位,当 PE=1 时,奇偶校验出错,做相应处理;
" ^" n# }9 x$ R* P: R3. PE 标志位的清除方式是:先读 UART_SR,再读 UART_DR。所以在程序中一定要注意:在判断RXNE 之后,必须在读取 UART_DR 之前就得先读取 PE,否则 PE 将在读取之前被清除;$ X. V) [/ q9 }" p0 M& I
4. 必须在 RXNE 标志位被置位的情况下才可对 PE 位进行清除。
& Z9 o& r1 z2 A5 P4 A: Q6 ~8 V- H) Z0 m* Q% d
|