前言
, E' n5 r! s$ `8 J* ~本篇文章主要是对 STM8Lxxxx 在 I2C 通信调试中遇到的第一次通信正常,但第二次通信失败问题的分析和处理。
, o. m, c/ S; g
4 n9 a t( S* z# e( a: R# G( h1. I2C 协议总体分析。
) @0 T x* {8 h" ^: u- f4 K8 j$ j STM8Lxxx I2C 硬件逻辑使用时非常灵活,因此也造成对各个状态处理的复杂性,稍不注意,I2C 调试就不能通过。) y3 `. b* }! }& a+ j7 K' N
下面是 I2C 开始(START)和结束(STOP)波形,STM8Lxxx 也是遵守这个协议的。
8 R6 W. ~ W% { j SCL 信号由主控提供。
' x6 d* ~/ B5 _: n STM8Lxxx 总线端口为开漏输出,外部需要上拉电阻。
7 M( U# p' v3 {. M+ {' D/ x 下面协议为简单的单主控单从机收发协议。
$ {* D& G* L7 ?" |6 {- a; @
9 n1 @; |# d. V4 e/ B
; k9 ~5 ]9 h- O/ r2 t+ ~2. 下面对一个简单的单主机发送,单从机接收协议进行说明7 Z6 q m6 X+ \5 O
0 \0 s. u8 F0 j
下面为主发送的一个协议图:
$ {+ L9 ]+ r: x0 Y; F主控给从机发送数据,等到从机应答信号后再发送下 byte 数据,直到数据发送完成后,主机发送一个 stop 信号释放总线。+ E" P, ?) p0 ?3 ?/ |
3 d& O+ T+ _7 i/ w/ o
: ]- }- D |# H* T8 K( N& i
" w) K* c2 u5 n) o" C3 r+ x) d
下面是从机接收协议图,从机接收主机发过来的数据并存储,每 byte 传输完成后发送一个应答信号给主机(从机在每 byte 传输完的第九个 Clock 把 SDA 信号线拉低到低电平)。
R1 t W; J- P
2 S1 s% w5 O; ^: f# t
* c/ i. j |5 b5 W
& [8 v4 D; Z* x' n3. 程序中的出错现象: C7 U9 r- C" \% q4 c) K. R
客户的程序调时发现相同的数据连续从主控发给从机,只有第一次的通信波形是好的,第二次通信时设备地址可正常发送,从机也有应答,但当第一 byte 数据发送完成后,主机收不到从机的应答信号。
) i; ^, R1 {5 j8 I0 G. b' m8 \( s D5 @ s9 A4 J. L* m
4. 问题产生原因5 T \8 g8 ~7 d% j, r" p
客户使用的是 E:\Download\STM8L\STSW-STM8016\STM8L15x-16x-05x-AL31-
( |0 j& J, V/ ~% J7 I, r: gL_StdPeriph_Lib\Project\STM8L15x_StdPeriph_Examples\I2C\I2C_TwoBoards\I2C_DataExchange 中的程序。7 M$ w* Q! o6 O- S* E# @2 G1 O# i* T
- /**( k: h; z3 u4 ^( p7 y
+ ]) Y- f4 j4 C* d* ]8 P6 P- * @brief I2C1 / SPI2 Interrupt routine.
- `9 o. P- y/ W+ _5 n: ^( T - * @param None
8 T) w& m8 ?# g2 K- `4 I2 l0 L - * @retval None5 g* R4 ]" W, Q' A+ {6 ?
- */( _" e( N4 T6 h5 c% X* O; d) a
- INTERRUPT_HANDLER(I2C1_SPI2_IRQHandler, 29)6 Y& M( C: Q, n. y) k
- {9 s! \+ w2 v+ p% i, |
- /* Read SR2 register to get I2C error */0 g1 M/ C t' S
- if (I2C_ReadRegister(I2C1, I2C_Register_SR2))
1 f( F" @9 \, ~/ R. g; c3 G - {
- ~4 e5 ?( G5 w! K$ @) h" | - /* Clears SR2 register */
6 M6 v) I! f' z% E8 D - I2C1->SR2 = 0;* v# |" T/ K% ?% a: U
- /* Set LED2 */
' `: g% q. J% `1 A - STM_EVAL_LEDOn(LED2);
1 i, _$ l% t; @1 F. X- {# K - }
+ F! X! j8 U7 J4 m0 q4 u8 h - Event = I2C_GetLastEvent(I2C1);: `, {. |8 B8 _- C! `1 K
- switch (Event)
& C, L& q/ ` Z+ ^ - {
& A7 c7 ?, A6 w: l - /******* Slave transmitter ******/
% s' V( g# v% l) w - /* check on EV1 */1 I: j* Q5 j0 j' X7 z* D7 Q
- case I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED:
C' b7 i2 M& j* D - Tx_Idx = 0;
- C2 c; O( t0 s% | - break;
* U: o# Z. F- z& o4 Q0 S - /* check on EV3 */
7 m4 r# ?2 L# x6 ?( f( ` - case I2C_EVENT_SLAVE_BYTE_TRANSMITTING:7 h8 S- n, u; ~
- /* Transmit data */: Z' T+ B$ s) ?
- I2C_SendData(I2C1, Slave_Buffer_Rx[Tx_Idx++]);# w( g" \6 u7 P, p. a7 k: Q
- break;2 i3 ]6 K- s) Z2 T& k& X8 U6 V; m
- /******* Slave receiver **********/
! i% H. X5 o F3 Z/ A - /* check on EV1*/8 U0 ]* p! c$ a1 m2 W6 V# B
- case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED:
/ p+ b- t' P; v5 g7 e- T - break;, C2 g$ a7 C3 `. R: g
- /* Check on EV2*/
; K, z: h7 _/ @/ c# } - case I2C_EVENT_SLAVE_BYTE_RECEIVED:
+ A+ o" l: X+ F1 {2 E; _. S: Y$ |: M - Slave_Buffer_Rx[Rx_Idx++] = I2C_ReceiveData(I2C1);
8 ], H* q, Y4 T5 s0 i! r; I- a - break;
! v5 ?9 s- ~5 g7 ~ - /* Check on EV4 */" x+ n( J) V/ Q3 L8 m
- case (I2C_EVENT_SLAVE_STOP_DETECTED):
' g! S3 z3 I! @% [3 p - /* write to CR2 to clear STOPF flag */
, V6 G, |) R+ P8 A/ e/ ~9 Y - I2C1->CR2 |= I2C_CR2_ACK;
; D6 t" _. z- e3 J+ n - break;
( P2 A+ |3 R9 T' ~ - default:
/ d1 \0 h1 h! C# W0 j% g. M* N! p+ w1 G- r6 d - break;, H- o2 p. S& g8 p
- }! s" C7 c) ^; t% s
- }
复制代码 / v: P1 O N' B0 D6 v& D
客户在程序编写时看到下图中用橙色标出的文字,就错误理解成从机每次收到 I2C 的 STOP 信号后,从机都要给 I2C_CR2 的STOP 置位来释放 SCL 和 SDA 信号线。
* q& l' F0 k4 V. B+ |
7 {! { u, ]! |# w
$ D3 H) F6 U1 w: F8 W% w6 c' t. W
- F: q* j) U, m; W: V- }' z" @
& x8 L1 D0 k$ @" O3 P# |/ L! y
6 E- y+ p1 Z! F7 e8 N' h2 {% E 客户对 I2C 中断处理程序进行了修改,对应的代码如下,黄色部分标出了客户修改的代码:0 |; u2 {( q3 u" i* ~* j
- case (I2C_EVENT_SLAVE_STOP_DETECTED):4 V, j) ^7 q& U9 e
- /* write to CR2 to clear STOPF flag */
8 J9 s, G. W6 C! y9 B1 ^ - //I2C1->CR2 |= I2C_CR2_ACK;
' e7 n( r& q; ^: x& B Y! q - I2C1->CR2 |= (I2C_CR2_ACK + I2C_CR2_STOP);
( W# J" n: P7 W( a! ^0 X( a3 {9 w
复制代码 9 y7 ]$ t9 ] j+ \8 k
跟踪程序发现 I2C 主机在发送第二次数据时,从第一个 byte 数据传输完成时信号波形开始不正常,从机一直没有 ACK 发出来。
0 U) b% S9 R" z0 ^ SCL 线一直保持在低电平。
' K# b0 h( |% _- s2 ^9 i3 N 跟踪从机程序,发现第二次传输时 ADDR 中断能正常进入,从机也有应答,但之后 RXNE 位一直不会被置位,被置位的是I2C_SR1 的 bit7, TXE 位一直为“1”,而中断处理中并没对这一状态进行处理和清空,所以一直会不断进入中断处理程序,不做处理又跳出中断,程序通信就死在了这里。
1 Y! j+ {. Z4 u$ C3 Q& I3 R# y3 z( ?: l, M9 n0 {
8 X, A& P4 O) i/ I1 V, b$ a
$ O( s' V$ D* M- a
把程序恢复成示例程序,通信正常。$ f7 U h0 f$ w& d) f
9 _' a+ g' h9 Z; b" z2 y
9 n- K `( d* Y4 |$ L" y% O
5.代码设计中应注意的地方。4 C# f* }6 g- e; |- ~8 h
建议客户在修改示例代码时谨慎一些,特别要注意重要寄存器的操作;弄明白后再进行更改;如果需要测试,最好标注清楚,
- S/ T8 ^. i6 g Z# v! A不要测试完成后又忘了修改回来。4 `4 J& t8 Z$ ^/ P* _/ B
- U1 y0 E" N/ ]2 `# F. G' } |