一、简单说明. M; o" w% q) _+ a% m7 S) F. I
本例子参考了ST官方历程,官方历程的链接如下
4 W( T+ Q; r: D
$ B1 j* S3 y/ D/ G2 I Whttp://www.st.com/content/st_com ... /stsw-stm32094.html
- l% j% B* C/ V% y4 F" N1 B( A% K- e4 n6 N& c1 J
关于i2c的协议这里就不做描述了
3 Y0 p' z+ i$ \9 h; U8 d, Y' {) W' n/ ^$ d
关于STM32 i2c的模式可以在中文数据手册中查看
/ Y& ?5 R/ c3 J0 H& C4 C3 E' q( M1 y& X& y/ g6 R
# c# Q. N6 N* \- W' j
$ @0 F- |% n% q) T' q2 `4 w# @
手册中已经描述,该模块默认工作在从模式,要想变为主模式,主要生产一个起始条件。(主模式的代码可以参考野火开发板的硬件i2c历程,本例子中也是使用野火开发板硬件i2c作为主机的)6 B. O1 z* A% U: n0 t1 e3 @1 \
1 {1 H- Y1 x( f
二、i2c从机的配置
* R! I, F( K4 g' M9 X" B! v- [cpp] view plain copy# p8 h7 O4 u6 f i3 A0 U
- I2C_DeInit(I2C1); . y' q; I2 }+ E: i R
- /* I2C1 configuration ------------------------------------------------------*/
4 s* A8 P8 o* T3 w: b: G! a - I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//模式
+ N. m5 w( n2 Y. F6 t0 A - I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
/ ^) I- m/ s+ t8 {$ [ - I2C_InitStructure.I2C_OwnAddress1 = I2C_SLAVE_ADDRESS7;//这个就是作为从机的地址,一定要配置正确
) y9 S/ E! ~5 R$ x; R - I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; . ^& ~+ D1 A2 H, Q$ O0 V. A, F
- I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//7位的地址
* \+ Q& J1 S( o/ ?" r, l - I2C_InitStructure.I2C_ClockSpeed = ClockSpeed; 1 }. G* {- @- J& Z% m+ X
- I2C_Init(I2C1, &I2C_InitStructure);
复制代码 上面配置注意的就是从机地址,这就是主机要查询的从机地址
; T/ {! r9 ]* A# S; ~
( \- a0 B1 T% x6 R4 Q三、i2c从机中断的配置
- t3 S4 x: r- B! v2 o- [cpp] view plain copy
) l. E# {% S- Y* q3 i - /* Configure and enable I2Cx event interrupt -------------------------------*/
8 G& y2 C3 m8 I, Q+ {# h - NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn; ' ~7 q8 b8 G; W0 _0 l. j. P
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
/ y. ^5 W1 t' t [, y - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
; N7 {4 o3 l0 o( e+ G; c) M - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; ! p3 h! o* T# R5 U9 _
- NVIC_Init(&NVIC_InitStructure); 6 e" [2 Y) i: ^' w) ^* C
- 8 H+ |2 v' X' @
- /* Configure and enable I2C1 error interrupt -------------------------------*/
" b4 G# v' M6 i1 T - NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
' B2 C4 ?" g; B3 y1 J! M - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
" X& Y% p, o- d2 P M- D. Q - NVIC_Init(&NVIC_InitStructure);
复制代码 标准库中的i2c一共有两个中断# d6 R/ E5 f+ l# z' s
一个是事件中断(EV_IRQ)和一个错误中断(ER_IRQ)
; i# r8 v+ o/ x4 M3 M& MEV_IRQ的中断只要响应EV1 EV2 EV4 之类的,后面会说明
; P( q7 Z% H- `% N3 }& V XER_IRQ的中断只要响应没有应答和起始和停止条件出错等
+ H- f3 [& M* S& X( R
2 r' P( l7 Y4 q1 P& E& B; D4 @四、使能中断
$ e0 W" ?* `6 I6 b# A( t9 l( U/ e+ s- [cpp] view plain copy
* H; ~) ]+ F: m - /* Enable I2C1 event and buffer interrupts */
+ o2 n1 q3 |9 j# k# A# ] - - o* `! n: k4 E8 L
- I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, ENABLE); % F$ Z2 M( o& m ] Q+ O9 c8 |8 B
-
# I6 j9 ?! c4 _ - /* Enable I2C1 Error interrupts */
! ~7 A' F% _- U( a -
3 T, A6 f) ~" F# v - I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE);
复制代码 使能了I2C1的这三个中断,每个中断的作用下面说明: ~) q9 @, v- l+ a" q" [
; {: U. F1 s' v五、中断处理函数
1 S! L" E5 C2 X, X8 A; { l- m- [cpp] view plain copy& P7 @1 l- ?6 O" U
- void I2C1_EV_IRQHandler(void) + ?$ k' B% }1 x1 a5 B
- , n$ ~. A! y% f" i2 s. j
- //事件中断处理函数 ; x/ ?) t& W1 g) E1 B1 ^; r
-
7 x# t" x' m+ g8 @% w6 w - {
$ H% \& c( x. z$ K! }! k - 0 ]- N& c% I0 B" Y% ^/ S
- switch (I2C_GetLastEvent(I2C1))
* e1 v1 _0 g1 f8 P+ r - ! Y F( Z8 ^! n! V
- //获取i2c1的中断事件
+ j- V( h2 A2 B6 u5 d9 j2 r1 ^ - % m9 v" C! m( ^4 q! j4 W3 j5 g+ U N
- { - q; U% t' n; Z2 Q) J8 z
- , @( @6 P8 g, Q- b: T* `. `! U7 ~
- /* Slave Transmitter ---------------------------------------------------*/ ) o- f: ?2 f0 j2 ~" d8 e, Z
- ; t7 s$ @9 @! \) t X7 C
- case I2C_EVENT_SLAVE_BYTE_TRANSMITTED: . _& ^9 O/ @& P! V0 q
-
7 U; r7 `$ W3 K - /* 这个和下面那个都是从发送模式下发送数据的,具体两个的区别我也不是很明白,感觉就是移位寄存器空与非 空的区别,准备好数据发送吧 */ * i" M7 d7 h9 a) \4 L! S6 u& k
-
8 L c& D3 D/ x' W {4 s; ^ - I2C_SendData(I2C1, I2C1_Buffer_Tx[Tx_Idx++]); ' s9 ~2 C/ v+ S* ^
-
" \" N8 c. }) w - break; 7 n }& c: B! v& _$ O2 ~& h
-
' ?3 p" k0 |; n; X y* q9 r( u3 ]% U - case I2C_EVENT_SLAVE_BYTE_TRANSMITTING: /* EV3 */
$ {/ r0 ~& _1 U. x" D# V - 7 B" M# F* H% I4 k m
- /* Transmit I2C1 data */
$ _8 e) q W. I( @ -
% \. {1 R, O5 `& a. F1 b/ F - I2C_SendData(I2C1, I2C1_Buffer_Tx[Tx_Idx++]);
3 h L9 p2 @; X) Z, x2 G1 {1 d -
9 q* ^- S- s. e- b5 r6 @& o - break; , z8 _" t; M% g2 r7 H
- ) w* O, a: f1 M/ {1 k, |
- /* Slave Receiver ------------------------------------------------------*/
9 H& i n0 { t: v -
, m# f" J; E) B - case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED: /* EV1 */
: u6 W8 |. L* L* t - 5 A0 G0 } q: `" i4 L1 n
- /* 地址匹配中断,不管从发送和接收都要匹配地址,如下图244、243发送地址之后都会响应EV1 */
Q( j$ p( m& `4 `: h5 y - 4 z% [- W) O: w% p4 r
- break;
9 }/ E: v3 N# W. `. y/ F$ M* ?3 q -
1 M) v7 J4 w4 K( L# ~+ m( K" W" z9 p- O* o - case I2C_EVENT_SLAVE_BYTE_RECEIVED: /* EV2 */ 8 H, K2 {. { s! `3 m& j, ?# V8 S% u
-
, a# i$ O9 h9 q$ `4 s6 s' }3 f - /* Store I2C1 received data */ 3 ~* o9 O$ g) y T
- + T7 M7 t: ^& M/ ~4 F
- /* 这个中断就是响应EV2中断,如下图244,每次主机发送完一个数据就会产生一个EV2的中断 */ 5 v+ @- Q/ \8 t) L2 A0 o2 R
-
- k: P' `) J2 }- l. `7 C5 M; G - I2C1_Buffer_Rx[Rx_Idx++] = I2C_ReceiveData(I2C1); , Z2 S7 R% V1 J. F# A) F
- $ O* E1 e* O- {# T: {
- /* 把接收到的中断填充到数组中 */
( R$ \. p% m0 y- x -
" ^8 I, V; [, _4 p. L' y - /* 注意:地址不会填充进来的 */
) }# W* Z( e m5 o -
/ m( F" y+ Z% o- p, i! m - break; + {& e+ v. G! n. j
- 6 _# [ i5 j4 i/ `' j( k
- case I2C_EVENT_SLAVE_STOP_DETECTED: /* EV4 */
, n$ ]6 l4 |0 A, e( R2 j, ] -
6 l* m" p. d' |$ T: L8 T* m - /* Clear I2C1 STOPF flag */ . H0 X# Y5 @) C/ E9 B/ A# ?
- ) Z% f9 c0 x3 }# ^8 |3 q
- /* 这个就是正常停止的时候产生的一个停止信号 */
4 O( z: A4 a7 O6 L1 y! U - % a/ c# I! h1 k- ]5 N3 ~
- I2C_Cmd(I2C1, ENABLE);
: N8 L* J. U, C -
) H# |1 L% m2 M) e( ?' f - /* 我也不清楚这个为什么要这样,如果接收完一串数据之后,不响应主机的情况可以 关闭i2c,然后在处理完数据后再 从新配置i2c,记得是从新配置 */
7 x2 P9 a4 f) j3 @ -
, k' y; s, M3 s0 ]7 i# F: m' W - Rx_Idx=0; 1 d, Y5 M$ r* G$ f. i" h
-
/ x# x6 B1 f) x5 R* l. d - i2c_event = EVENT_OPCOD_NOTYET_READ; ; e% p! R+ A) |& m* A) H7 u3 y
- 0 e) ]& V9 o' m5 B) Y* |4 K
- break; * f5 t. V0 G& Q: c5 t
-
! U; @2 n8 e+ U - default:
6 e; }; A0 W% n3 I1 @# q2 _9 b8 m -
+ [, g+ u& n" ~ - break; 4 ?4 v3 l& S; s. f& s4 J3 K: |
- % T+ h. G+ {$ J5 `
- } : @, K% p. h4 \2 U
-
, r9 w$ W, E- @5 z- g0 q - }
4 i1 j- E& C1 R4 m9 T$ }% }) j8 m -
) k' W9 u( E3 V h) s0 l - void I2C1_ER_IRQHandler(void) 4 j" |1 g! T4 G% Y o& Y6 ]) v n
-
% w* L5 G& C S - { 3 p. u3 a% P* t. }' a3 G
-
% N2 q) i/ _, ~: N5 W0 E) @ C, M - /* Check on I2C1 AF flag and clear it */ 1 a5 c9 m$ i" l* y$ d5 s
- ) }$ c: `8 W! f1 l0 I% o) |
- if (I2C_GetITStatus(I2C1, I2C_IT_AF)) : Y- J p, W* s" n3 s$ K; S( Q
-
& G: n0 N; G' |8 Y- l4 [0 H# F! `: G - {
( u3 B# [" K) c8 h% m v0 ^ -
4 a3 _ U" i/ h9 c2 y7 |# [ - /* 这个就是图243中最后那个没有应答的中断,也就是发送了一串数据后的中断,可以做清零工作 */
: k3 G. w8 k3 J: F4 h* Z( t, A - / j. Q; D" n0 O$ ]) Q/ M& m
- I2C_ClearITPendingBit(I2C1, I2C_IT_AF);
2 E8 i3 _5 f4 k8 }6 s. F2 f% | - , U: K5 o- {" n1 h) A( g) c
- Tx_Idx = 0; 2 f7 I8 h& e; v; P! {& ~
-
$ d) Z7 {3 r% j - i2c_event = EVENT_OPCOD_NOTYET_READ;
$ B7 f# U2 M0 T* v" w -
; f7 x0 T/ L% A, d) p - }
9 O8 U j* Z; G* t8 U8 L4 r5 r -
8 Y7 d0 h- L h5 X: ^& s3 z - /* Check on I2C1 AF flag and clear it */ c$ B/ [9 Q6 X* N# T# i
-
) H0 Y: {4 v6 M- \ - if (I2C_GetITStatus(I2C1, I2C_IT_BERR)) //这个就是起始和停止条件出错了 8 W! E4 ~3 H7 y1 _$ U
- 3 X* x' e1 ?1 c) X0 T' ~
- { % f5 j& Q6 `; c. e' v- H4 t# E2 ]. y
- ! ~5 i0 N" R H; W
- I2C_ClearITPendingBit(I2C1, I2C_IT_BERR);
6 N4 b- m2 X3 c, M0 T# G - 1 y% v3 c8 b% d1 y. B5 Y+ y
- }
7 I# a1 Z$ F9 h, V2 c, R6 E - ; t( k, f, ^: j: L) J2 i
- }
复制代码
* N2 u$ Y* G! B% t* M
/ J# p2 n. {7 m" j+ w
) X$ K i! }* A2 [1 s5 I+ Y
8 v! a/ K0 R3 S. L5 C0 Y" t7 \1 z7 `; k注意:i2c中断中不要printf打印信息,否则会出错# \5 b- G1 D6 k/ g
& D) j+ E: i0 S$ J主要是参考了官方的历程,然后自己再调整一下逻辑即可使用。( ?* E3 @0 z2 [' L; z3 `
6 v* l8 p; B! w4 r
+ m( V1 n( n4 I1 H7 b' b
- L' U9 u6 S- ?/ `' Z5 M* P最后的效果感觉还是挺稳定的,这篇文章主要是给大家抛砖引玉的作用,也记录一下自己调试几天的成果。0 C! L* y- \ r. g; J$ t+ t
ST官网的历程还是挺多的,没遇到的知识可以去官网找找。
" l; A1 }, f" Z* s/ h5 `) n0 n
: ]( ]" [4 k! ?0 b* |) F3 Z: r; Z- a$ b9 ^$ b
转载自酱油师兄( u, Z6 r# k" e( {' m- r' ^
- b: T! z/ ]% c9 L, G |