前言* M0 Z; e7 e# i3 V) {
本篇文章主要是对 STM8Lxxxx 在 I2C 通信调试中遇到的第一次通信正常,但第二次通信失败问题的分析和处理。% W5 }! b4 q3 [0 r
" D; r0 E# {. X
1. I2C 协议总体分析。
% E. X( ]. v! s) P/ F2 I4 P STM8Lxxx I2C 硬件逻辑使用时非常灵活,因此也造成对各个状态处理的复杂性,稍不注意,I2C 调试就不能通过。3 j; p+ j6 W; [. R7 N% ~5 L* k+ F
下面是 I2C 开始(START)和结束(STOP)波形,STM8Lxxx 也是遵守这个协议的。9 I5 u/ _8 k j* g9 F5 `( w! f
SCL 信号由主控提供。 B m, b6 q( i2 x9 C
STM8Lxxx 总线端口为开漏输出,外部需要上拉电阻。8 {; N* @/ D5 N' c3 u4 H8 V4 K; [# |: @
下面协议为简单的单主控单从机收发协议。- T% X, s. x* S! U g1 J) m' u6 ?
]2 D( I: u* z! \) O9 W; [
: p% g ~+ ] D/ v2 v2. 下面对一个简单的单主机发送,单从机接收协议进行说明
! ?! u- W7 h9 H5 o) G# C- k
2 R$ v4 ^: {, g5 O下面为主发送的一个协议图:7 T$ l: T0 T4 u" v# Q
主控给从机发送数据,等到从机应答信号后再发送下 byte 数据,直到数据发送完成后,主机发送一个 stop 信号释放总线。
3 _8 L6 x1 K ]+ N" H9 d; n+ N; j) `. n b6 V/ _" R- e4 m8 T1 A: d# O
0 v5 q8 j Z, I$ ]8 m9 l3 y6 o4 H3 z" v! [' G1 w
下面是从机接收协议图,从机接收主机发过来的数据并存储,每 byte 传输完成后发送一个应答信号给主机(从机在每 byte 传输完的第九个 Clock 把 SDA 信号线拉低到低电平)。
+ j+ Z/ l& B" H6 R9 s2 y# @3 {- p. w( W$ z
! d1 q7 I: [7 p2 O- T8 p6 L) ~
p; S/ L6 S/ I/ `6 l1 |
3. 程序中的出错现象4 y8 b0 p8 v' s$ f3 v: A4 [' l
客户的程序调时发现相同的数据连续从主控发给从机,只有第一次的通信波形是好的,第二次通信时设备地址可正常发送,从机也有应答,但当第一 byte 数据发送完成后,主机收不到从机的应答信号。
. J1 |: Z# n- e6 g8 n9 \5 r$ W7 `! Y" F. H: r
4. 问题产生原因
) M- _" ~% Z0 ?6 H; k r0 J' F1 q客户使用的是 E:\Download\STM8L\STSW-STM8016\STM8L15x-16x-05x-AL31-4 R! _" i1 J" U
L_StdPeriph_Lib\Project\STM8L15x_StdPeriph_Examples\I2C\I2C_TwoBoards\I2C_DataExchange 中的程序。
) k+ u3 @( F, h- /**7 G9 Z8 k+ K& a* D
- 1 M6 h# B$ G, E2 x2 Z( z
- * @brief I2C1 / SPI2 Interrupt routine.& M, \: W7 F5 G7 c$ v
- * @param None
5 L: U; m5 `- b% | - * @retval None
% k! o/ b2 N" b! z% W - */
, e# w( K% V6 C f9 z, Z1 H5 s: e - INTERRUPT_HANDLER(I2C1_SPI2_IRQHandler, 29)
( x( b; x( N; I. l - {
5 l8 m. H& _$ V( [& @ - /* Read SR2 register to get I2C error */7 d$ c; K W5 `) _
- if (I2C_ReadRegister(I2C1, I2C_Register_SR2))4 L, v- s4 H& o6 u
- { p" b9 z+ X e4 D, v! k
- /* Clears SR2 register */ f* }) }# h0 q! ~, m" R+ I4 ^! ~6 y
- I2C1->SR2 = 0;% d; e6 n& h, N$ f @" d5 U: ]- N
- /* Set LED2 */
# x7 J' Q" p+ }# { - STM_EVAL_LEDOn(LED2);
5 E$ L9 e4 r5 _1 W; n1 k - }8 a& S0 n/ a9 N v
- Event = I2C_GetLastEvent(I2C1);& K4 c7 ]2 m/ T2 ~
- switch (Event)
7 M; Z9 v! @7 z. a. q. b - {
) _, L- u4 ?: U% M' \' I - /******* Slave transmitter ******/
3 l* r# k D9 j. {# y7 g8 u9 P0 S - /* check on EV1 */
4 N* n2 o' _! R - case I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED:
" }! R8 o; U; S6 y - Tx_Idx = 0;
6 C8 A* l( O7 h% @ - break;
0 M" h) t c7 o: u3 p; X - /* check on EV3 */( L. |: T$ }- ~/ f# W6 r
- case I2C_EVENT_SLAVE_BYTE_TRANSMITTING:( N4 l2 i6 Y; U) r/ p
- /* Transmit data */8 j5 Z' z y4 t; I' i! b5 P/ {6 }' r
- I2C_SendData(I2C1, Slave_Buffer_Rx[Tx_Idx++]);$ C* H/ V: Q2 F" m) P% H
- break;
: n; @* F' Q* z# W7 s! w; h- Q - /******* Slave receiver **********/9 y W* o5 O5 }% w, ` _
- /* check on EV1*/8 y3 U8 B6 l6 y: l- W7 W) b
- case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED:% M- B) ~5 z3 z
- break;$ K& f8 J' b8 P" V' I5 C
- /* Check on EV2*/+ r7 `* j/ l, w5 T, X$ D+ j
- case I2C_EVENT_SLAVE_BYTE_RECEIVED:
! I( j" j$ c! Z) { - Slave_Buffer_Rx[Rx_Idx++] = I2C_ReceiveData(I2C1);' s0 w" u* g3 }4 B$ c6 f+ n" ` I
- break;# L# b0 H4 r1 R/ r0 l3 q
- /* Check on EV4 */
w+ n) P: I2 @0 q - case (I2C_EVENT_SLAVE_STOP_DETECTED):
1 N/ U& n9 ~; z I3 p% j - /* write to CR2 to clear STOPF flag */( U$ d: M2 T, b1 Z$ h, y# s" |
- I2C1->CR2 |= I2C_CR2_ACK;
! ]) l3 B" Q, G$ p' O4 b/ w' t - break;
3 I; f' O4 F6 M C( J - default:
: w5 ~( X1 @0 c3 R( S6 U - break;
( J; z' f- \1 N/ t% T( Q+ l - }5 G. m% k% n! v: D ]7 S
- }
复制代码 ; S( i4 i' h, ^( B
客户在程序编写时看到下图中用橙色标出的文字,就错误理解成从机每次收到 I2C 的 STOP 信号后,从机都要给 I2C_CR2 的STOP 置位来释放 SCL 和 SDA 信号线。( W( U* K0 H3 C3 @0 L
2 E4 n( u" W# j/ P, D- @
3 ]; P! f- w$ `1 Y
% {1 @3 S" c' h* d
& E" F8 j. ?4 w, i t+ M* \5 {
) q) A. [& |9 ?8 a. J 客户对 I2C 中断处理程序进行了修改,对应的代码如下,黄色部分标出了客户修改的代码:; e6 z0 \* v7 z$ w$ F; |
- case (I2C_EVENT_SLAVE_STOP_DETECTED):
" M: g* _7 m, ^+ B2 I3 ? - /* write to CR2 to clear STOPF flag */
: S% r) ^& d1 C! r$ i i8 t - //I2C1->CR2 |= I2C_CR2_ACK;$ Y4 `" t# h$ t3 A- E
- I2C1->CR2 |= (I2C_CR2_ACK + I2C_CR2_STOP);
" F A7 M1 H$ e& X" ]7 Q: y$ m# P0 k0 s
复制代码 0 v I# w' ?# a/ R8 `
跟踪程序发现 I2C 主机在发送第二次数据时,从第一个 byte 数据传输完成时信号波形开始不正常,从机一直没有 ACK 发出来。9 _8 C) e9 K& I* V) B
SCL 线一直保持在低电平。
/ S, l* I" k6 s$ E4 O Z. e 跟踪从机程序,发现第二次传输时 ADDR 中断能正常进入,从机也有应答,但之后 RXNE 位一直不会被置位,被置位的是I2C_SR1 的 bit7, TXE 位一直为“1”,而中断处理中并没对这一状态进行处理和清空,所以一直会不断进入中断处理程序,不做处理又跳出中断,程序通信就死在了这里。( l! Y' d3 }7 ]$ q' p, ~4 Z& n$ z, \, V
( A8 j" A9 w* _2 b. c; _2 i
# [2 K( A* \7 j$ h0 ~+ e, x
/ P) J; b5 A. b; w$ t把程序恢复成示例程序,通信正常。
0 G- v8 B* C$ e" G1 o( M5 S7 s
) ]( d" d5 `0 d" Q4 s! R
( p7 Z$ y% ^; N8 N6 ?, [( Q1 F/ V5.代码设计中应注意的地方。7 R, @$ P: g$ D
建议客户在修改示例代码时谨慎一些,特别要注意重要寄存器的操作;弄明白后再进行更改;如果需要测试,最好标注清楚,
; j' M, o( N5 L" Q不要测试完成后又忘了修改回来。( [" Y8 I! G8 w% ~# Z7 p' ^ D
3 F0 h4 _/ f# W3 O& Y |