问题:; O$ h/ j! e8 q, F. s
该问题由某客户提出,发生在 STM8S105C6T6 STM8S105C6T6 器件上。据其工程师讲述:当他所撰写的程序不使用奇偶校验的时候,程序工作是正常的;但是当他把奇偶校验改成偶检验 EVEN 时,程序无法正常工作;现象为:一、不管上位机发送的数据是不带校验位的,还是带奇偶校验位的,STM8S 都可以正常地接收到数据;二、当奇偶校验位使能后,接收到的数据再返回上位机,显示的数据不一定是正确的。% h8 S( h) f e' @# }7 x' d
! Q# I' [) A) I: }' h- v& M% T3 H4 \; f2 n) s# w8 T6 Q
调研:6 H+ v2 }" D3 [! J: j
检查客户的 UART 程序,UART 初始化程序如下:
b4 U5 C: f' p+ M5 A, k- static void UART_Config(void)3 |5 T& K6 l; o* x
- {
' h. Z: `; `1 \" n$ T+ t1 [9 i x - /* UART2 configured as follow: v6 O7 c% \% L$ j" L3 p0 ]" r
- - BaudRate = 9600 baud
/ B* Z# [( H' @- O, ]" H. y - - Word Length = 8 Bits
; i7 c4 a" m! X# i! n) T2 ~/ J - - One Stop Bit
! I1 w8 H2 t6 T2 o( g - - No parity
8 L- |4 Z( p x4 N - - Receive and transmit enabled' o$ b' \& O/ ~8 _6 v* B
- */
0 P' x, ~* y: y4 Z" [8 s k - UART2_DeInit();
, S9 z* v3 {6 Y - UART2_Init((uint32_t)9600, UART2_WORDLENGTH_8D, UART2_STOPBITS_1, UART2_PARITY_EVEN,7 h" T. c0 @- f1 r
- UART2_SYNCMODE_CLOCK_DISABLE, UART2_MODE_TXRX_ENABLE);
. Z; |' h6 y5 M$ @# X2 |
% ?, m) t( ?* _( j, j: [5 q- /* Enable UART2 */, ^8 p* {& J. n& ~4 h
- UART2_Cmd(ENABLE);
, ~7 \, `* j0 F) ^( I! Y; N7 V - }
1 q0 U2 S$ N0 V' p3 r9 s
复制代码 - S& ?3 }1 G7 g: ^$ d3 T
主程序可简化为:4 p; o" y3 w x& R7 m( _
% o6 j9 x' |8 Y. ? I- m- o6 Y; Z2 T" u8 W3 o# h# z
- void main(void)7 C8 c' h+ v7 t* z$ Y0 j
- {, K# g2 |- j! i
- unsigned char val = 0x00;6 D2 a" G5 r7 ~$ E. g
- /* CLK configuration -----------------------------------------*/
. a9 P* d3 h/ N3 c - CLK_Config();
) b- U3 b% f! H& f - /* UART configuration -----------------------------------------*/
6 v! _" y# L' R! \3 k - UART_Config();1 s6 n) J3 q; k
- I* @' O5 C# Q6 F
- while(1)7 {7 a# o% u1 |3 @
- {4 Y, v) b' p4 M) U& }
- /* Wait the byte is entirely received by UART2 */
7 m1 _+ @5 q8 }. l- |2 S) B* d% D - while (UART2_GetFlagStatus(UART2_FLAG_RXNE) == RESET)
2 V V' D7 L1 d) r/ r - {
) Q& o( X8 {) t* y. } - }9 [, A3 w: R% }9 ]7 m, t
- /* Get data */
" [9 `, }, j& @6 f4 h5 P - val = UART2_ReceiveData8();
0 X1 S5 Q9 ~: n/ N5 I
3 j {6 }: |6 Z3 T" {3 a& H$ J- /* Wait until end of transmit */
& N' B- D* P* N1 V: h - while (UART2_GetFlagStatus(UART2_FLAG_TXE) == RESET)
$ ]* f; b H4 [/ c, l) C( a) L - {
, U( Y# `! l" g+ ?3 o# U - }
8 P# W: D1 R- O: s+ T
* \" D8 b! s5 v5 L9 Y& M7 m2 T& E- /* Send data */2 o: \4 ?4 m! ]3 C
- /* Write one byte in the UART1 Transmit Data Register */6 r$ R a: R( X: h' b/ t4 S& U
- UART2_SendData8(val);1 k% ]# h" z s8 V# [
- }5 |/ Y" z6 k+ u# ^1 [- |3 ]
- }
复制代码
3 e( x- p. }4 U0 q5 U2 A# @观察客户的程序,把 UART2_PARITY_EVEN 改回 UART2_PARITY_NO,确实就是正常的 8 个数据位不带奇偶校验的程序。那么为什么修改成带奇偶校验后,就会有客户所描述的问题呢?) I- b, f! f6 d1 b
继续使用 UART2_PARITY_EVEN,我们用串口分析工具、示波器或逻辑分析仪来分析一下数据。; I. O( {/ q7 k) I! Z# y. z7 b
• 当上位机发送 0x01 时(数据中有 1 个“1”),从串口分析工具可以看到上位机收到回来的数据变为 0x81;观察示波器波形,发现 STM8S 发出来的数据位在 Bit7 确实变为 1;
" C" z/ U: V2 M• 当上位机发送 0x03 时(数据中有 2 个“1”),从串口分析工具可以看到上位机收到回来的数据为 0x03;观察示波器波形,发现数据倒是正确的,可是校验位“0”并不存在。) |5 v5 C' p3 c& I8 s2 g
通过这两个实验,我们可以看到,好像是数据的位数不一致,STM8S 发出来的数据是 7 位的数据,也就是只有 Bit0~Bit6,Bit7 变成了奇偶校验位!
# l' e# c" X% i6 c, \2 B% t, p/ N; h
我们来看一下 STM8S 的参考手册是如何描述奇偶校验控制的,我们看一下帧格式的表格:
3 Y2 [$ @& y! B
; }" ]# m0 r/ k$ U: i) Z/ m
) B$ W* u. u9 {& q/ R6 J. j" M
9 C9 v1 }8 C5 s9 w/ A4 j# c, v% J% o& s
我们发现,在 STM8S 中 M 位所定义是帧长度,而不是数据位的长度!也就是说 M 位所定位的长度为“数据位+奇偶校验位”个数的总和。当数据位为 8 位时,不使用奇偶校验的时候,M 的长度为 8 位;而要使用奇偶校验的时候,M 的长度应该为 9 位!* T( ]. p. B" w6 I) f0 A
; G. l$ M" s5 D; v3 J- e; U7 f; T9 f到此,我们应该知道如何去修改程序了,我们将主程序和 UART 的初始化程序分别修改为:
, X# T. T1 g! O: C& ~! _( J- void main(void)6 f6 w6 ]- [% j% _2 w/ N$ o, r
- {
$ ~- W5 k9 V( X - unsigned char val = 0x00;' `( r0 p9 {3 W0 J9 G
- /* CLK configuration -----------------------------------------*/
- w$ n ]- ^; d6 v) y4 E - CLK_Config();
! I$ g$ Q1 Q. H) \3 t" [ - /* UART configuration -----------------------------------------*/
2 J5 A j$ _7 u9 r* ^, Y1 K - UART_Config();
) w! X5 w9 @* J: p- c, Q3 S
- U) U# h+ v' q7 A/ a7 k5 P0 Y# L- while(1)
* e5 ?3 U! Q `2 d _1 q' Y - {/ S! [- |. _* z/ ]1 X
- /* Wait the byte is entirely received by UART2 */
& s- J5 x, R$ t - while (UART2_GetFlagStatus(UART2_FLAG_RXNE) == RESET)* |: _9 h, e4 ^1 [5 R% T
- { R; ^0 Q, y& @# `1 ^4 c
- }+ O2 e" J' i* o/ S- S1 ~5 L
- * c& g( M( U8 A& G5 k" c
- /* Get data */: ?! S# ~7 `+ O- @3 q2 s
- val = UART2_ReceiveData9();
6 f' m- w2 A* d; g! i5 a; z
9 y; y3 i! j2 G7 o% t: ]1 X- /* Wait until end of transmit */
' x, X/ r% n1 X( P - while (UART2_GetFlagStatus(UART2_FLAG_TXE) == RESET)
! m3 i* ]' ~! e& l, u: v - {& r" Z' ^2 C U4 F. g3 _
- }1 D/ N/ W5 s/ c4 a6 I# `
: k9 ~0 M9 ] g; P5 \+ H0 G- /* Send data */9 W5 o- i1 `2 F+ O+ e% s: \
- /* Write one byte in the UART1 Transmit Data Reg+ O' v3 x* ^" ~+ d/ {" {- Z5 i& f. w
- CLK_Config();4 E$ n! \- X4 ]; |
- /* UART configuration -----------------------------------------*/
+ _( }( q1 E0 ~, @9 o8 o' l$ M - UART_Config();
; @' h. e' @, O/ g7 N r# @
# M2 V+ o5 ]# P- w& X; z- while(1)
1 C# J E/ v# L0 n$ } - {
) O: O- [4 c% |2 q* l) a* A# ] - /* Wait the byte is entirely received by UART2 */1 b/ y, G% y: C" V2 u0 H
- while (UART2_GetFlagStatus(UART2_FLAG_RXNE) == RESET)
& I# s/ J( J) Y- M; R4 `$ ^ - {
9 [1 N* C9 J9 v2 [8 q1 w; o - }8 c0 S+ _1 P- W8 G
+ c8 E7 _# C$ \( n9 \8 C0 n2 q9 B# W- /* Get data */, M' I- w9 E* F
- val = UART2_ReceiveData9();4 _: m/ I& Z1 @- \( @: I
- T6 g6 C9 t% o- if(UART2_GetFlagStatus(UART2_FLAG_PE) == RESET)
: e5 y8 s$ t% Y. u5 v - {
" F5 K2 V3 ?) z9 n H- d - /* Wait until end of transmit */
$ j. T6 A* P) X: t" T2 G - while (UART2_GetFlagStatus(UART2_FLAG_TXE) == RESET)+ N f. F0 a) b3 D' B& S# L. Z3 S
- {
, A4 r8 T4 c. k% V, v- Y3 m8 N$ B1 U - }
. ^+ t1 u6 A+ a$ F! i$ D0 P6 }7 S - 6 {1 O( g+ d/ u( J& @
- /* Send data */1 {, W: T/ t% h2 _
- /* Write one byte in the UART1 Transmit Data Register */" U8 N3 A) v9 t$ M
- UART2_SendData9(val);$ v" F P5 `# b1 q: Q+ o) N" E. t0 d) E
- }
9 J" Z7 H& A, y& @3 C. \9 S - }
! W& M; l+ A* J- l" H7 @ - }
复制代码 - }. k0 x# A: L7 @+ F
编译,再测试看看。我们将上位机端的串口分析软件的奇偶校验位仍然修改为 ODD,上位机发送“0103 07 0F”,结果发现上位机还是可以收到 STM8S 发来的“01 03 07 0F”!这是为什么呢?难道 PE是不起作用的,奇偶校验仍然是假的?当然,我们还是得来查一下为什么 PE 会检测不到呢。
0 S( M# O4 U! k% s, p6 a
+ j5 p, P' K/ I我们再来看一下 PE 标志位是在什么情况下被清除的,我们在参考手册可以看到:要清除 PE 标志位,软件要按以下操作顺序进行执行:先读取 UART_SR,再读取 UART_DR。于是我们再看一下上面的程序,可以看到,程序中先执行了 UART2_GetFlagStatus(UART2_FLAG_RXNE),这是一个读取UART_SR 的过程,然后再执行 UART2_ReceiveData9(),这是一个读取 UART_DR 的过程,那么,至此,PE 标志即使之前已经置位,那么经过这两个操作后,早已被清除,这个时候再执行 {, T, Z" F6 H6 I3 A }
if(UART2_GetFlagStatus(UART2_FLAG_PE) == RESET)显然没什么意义。- m0 |6 ~- ?! d
# y7 ~. Q1 ]3 K! G
/ {- I- p' B8 g% T所以,我们还得再继续修改一下程序:% L( ?; F/ U _" u1 [+ ?
- void main(void)
c u8 s+ A4 [! w, F% M6 x6 d - {8 z0 N1 m2 k- t4 R( f
- unsigned char val = 0x00;
" g+ O$ Z# t6 ?' [ - /* CLK configuration -----------------------------------------*/' \; t8 r; o+ w, K/ h# p
- CLK_Config();# z7 a8 c D7 P" t& ~& m
- /* UART configuration -----------------------------------------*/. `) `0 s( w4 @) g8 k
- UART_Config();
7 ^! { d% f6 | - ( C+ x, P# v/ l' I6 U# ?+ [1 ]' V
- while(1)
( O) q X m& {5 Y: A8 L0 x - {% X" r' g, p6 p9 Z9 d( I4 R, v+ W
- /* Wait the byte is entirely received by UART2 */5 F8 L, f8 M" ~! v6 v
- while (UART2_GetFlagStatus(UART2_FLAG_RXNE) == RESET)' L- i3 g' m& L( R' i
- {
: |* q# r' G+ _" Q/ c - }
5 {' t/ c1 \5 K( c% e
4 K) D9 i& v- A+ t5 S& N$ F! N& z- if(UART2_GetFlagStatus(UART2_FLAG_PE) == RESET)
* n/ N4 w5 q2 l* p - { F+ j. i$ l' z. s* f
- /* Get data */% \, ~5 c/ \1 R: k3 x7 p8 P* {
- val = UART2_ReceiveData9();
# j. e( _2 G: h/ D - 6 d0 S- J) d C( {6 ^
- /* Wait until end of transmit */
* S6 J! ]+ Y5 G/ O( h' e - while (UART2_GetFlagStatus(UART2_FLAG_TXE) == RESET)8 G+ Y: g* y# ]! p$ N
- {
: a! ~! V2 B s! Q - }( B! H7 l; q# V( ]5 F
- + T7 ~1 V* f3 w9 f, {5 m2 r; A
- /* Send data */
0 _8 l; g8 d% a" b7 r - /* Write one byte in the UART1 Transmit Data Register */
, `1 L+ z% ~3 A. s( Z - UART2_SendData9(val);2 {" H4 c, h; _$ F
- }( Z" f1 d& y- R+ K
- else
+ d# H) o! _3 P- G. C - {
8 H( b" [) ~" S6 N/ U - /* Get data */
% B; o/ I6 L3 S. z+ M/ ]0 I# o7 i - val = UART2_ReceiveData9(); // For clear PE4 s9 }5 ]8 ~% p* D
- }& g( s0 ^' g2 ~2 ~9 J, a0 w
- }
" n6 k) ]4 u' j8 m) B0 U. R2 v - }
复制代码
( y, U( n5 W$ k C3 b( \" I1 j$ F再编译,再测试,得到了正确的结果:' m( E9 l( e' E
• 将上位机端的串口分析软件的奇偶校验位设置为 ODD,上位机发送“01 03 07 0F”,STM8S发现奇偶校验位不对,没有回传任何数据给上位机;: u! X' i F- {7 Q" D; O
• 将上位机端的串口分析软件的奇偶校验位仍然设置为 EVEN,上位机发送“01 03 07 0F”,上位机正确地收到 STM8S 发回来的“01 03 07 0F”。. J, r) r+ {* Q# L1 B4 A0 |
) D) J4 w& d' v( \, h" ]1 y
结论:
! Z4 O; t" K' @5 k& h由于客户对 STM8S 的参考手册没有详细研究,误以为只要在程序中将 UART2_PARITY_NO 改为( b% P+ F* f( b
UART2_PARITY_EVEN,就可以实现 UART 的奇偶校验功能。
: ~4 z+ U1 E! d" t* y' ]# B" S% I: Y% c
处理:
1 |" u; n7 G' e3 ^$ W$ \修改程序,将帧长度相应的都改成 9 位,加入对 PE 标志位的判断,并且经过正确的操作顺序来进行判# z& D- w* ~, X$ _. W" }# y
断处理。
$ k! Q0 L" T% t: T+ K- g' s, Z8 k
建议:# v0 s6 f8 ]8 p! T: G U! H
& z! e5 m% B' X8 J3 w# u当需要使用 UART 的奇偶校验功能时,需要注意以下几个方面:
8 c* f, y7 A* O
& t+ _) N9 e+ g, f2 ?9 _' L1. M 所代表为帧长度,而不是数据位的长度。当使用奇偶校验的时候,帧长度=数据位长度+奇偶校验位长度;
5 M4 F/ M# z# k% u2. 不管数据奇偶校验位是否正确,UART 都会将数据接收回来。要对奇偶校验进行判断的话,必须对 PE 标志位进行检测,PE 标志位由硬件置位,当 PE=1 时,奇偶校验出错,做相应处理;
& H7 J) T9 |# w6 a3. PE 标志位的清除方式是:先读 UART_SR,再读 UART_DR。所以在程序中一定要注意:在判断RXNE 之后,必须在读取 UART_DR 之前就得先读取 PE,否则 PE 将在读取之前被清除;* P, ?: W) ]! c. c7 Q
4. 必须在 RXNE 标志位被置位的情况下才可对 PE 位进行清除。: R( S: S, \- E2 j6 G3 {4 o
) f3 o2 E/ h1 E$ `& v' f1 e# W
|