你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【STM库应用】stm32 之 IIC应用

[复制链接]
aimejia 发布时间:2018-5-29 11:08
本帖最后由 aimejia 于 2018-5-29 17:18 编辑
/ X( I+ O$ B* u, I) }6 Q/ o3 X  d4 i. L
iic协议是比较简单的双线协议,时钟线CLK和数据线SDA。
  m$ }& o. h# b( D  f# G
# K7 [+ F% S+ H+ z4 f一般我们常见的还有spi总线,这种总线可以可以根据需要扩展,还有单总线等等# }2 B- P) b: r( w9 g
; P! y8 o. l8 N8 I. x: v
这次还以at240c2为例进行操作!
$ T1 ?. t3 S( e- ^3 [% \4 b! n5 O" H6 [7 X9 Z: _. b
1.jpg

. X1 \" n% f4 j" v7 S! U+ |9 ~
: C. q) ?2 C: e( r$ N' _+ LPS:这就是传说中的iic时序图/ t$ P! q7 T; x) b5 {
7 I! i' g' H/ c7 |4 G$ h; J/ \
硬件构造我们不过多的分析,今天用到库了!我们先从库函数硬件iic初始化说起!
. t8 f2 j# j. H1 C# D, \
/ S& z+ M) Z7 c$ x4 X6 \! oPB6   --   CLK$ }, l+ P, G$ R8 d* E3 c
) g6 @/ W! |8 g$ A2 ?% Q
PB7   --   SDA
: F5 F0 @8 @; {4 Y& C
  1. [cpp] view plain copy
    - r, T  C. |" o. Q* \
  2. void i2c_init(u8 addr,u32 clock)  
    # z) ]7 o! d8 V& L0 N& J7 \  H# i5 Q* N
  3. {  
    : ]! `) z3 N' o, S
  4.     I2C_InitTypeDef i2c;  6 m$ ]( `+ y8 @/ i) _
  5.     RCC->APB2ENR |= 1<<3;  4 F0 R  B* x8 n
  6.     GPIOB->CRL |= (u32)0xff<<(6*4);  - c4 U. f5 j  A
  7.       2 I2 G3 K5 G# v% H$ B2 T4 q; a
  8.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);  
    6 R3 ?  r5 i8 z6 p0 d. \* ~
  9.     i2c.I2C_Ack = I2C_Ack_Enable;  4 H, J3 ]1 I6 Y& L
  10.     i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;  
    & Z0 ~9 J9 b( u5 v0 N
  11.     i2c.I2C_ClockSpeed = clock*1000;  % s- Q0 [! t8 K. q" s& [, r
  12.     i2c.I2C_DutyCycle = I2C_DutyCycle_2;  - M- P' ^6 b8 S2 W( {; ]
  13.     i2c.I2C_Mode = I2C_Mode_I2C;  
    % g6 j+ [: a7 K+ B+ t
  14.     i2c.I2C_OwnAddress1 = addr;  
    " E* \# p* R% I; P
  15.   
    0 N1 r9 @$ u: I3 T8 c8 \
  16.     I2C_Cmd(I2C1,ENABLE);  
    5 s4 v* o) ~6 s+ k# r
  17.     I2C_Init(I2C1,&i2c);  
    & g" c( ~- l: h$ H: f
  18. }  
复制代码
在配置管脚方面,我还是喜欢用寄存器配置,因为我的两行代码可以解决库函数的N多行代码的问题!
! L2 t* |; j" @4 t$ w
6 a) {1 B2 f% K; }' i% v还有在结构体变量命名方面也是属于我自己的独创吧,这样反正我觉得是既容易识别,也少打几个字!
5 Q! g& H* Y9 O* s% P" y
. u' R: k. A7 T+ utypedef struct
" p6 A* C$ I+ W  I0 F) w{0 A4 ~0 F0 n4 I4 x1 l- v' v6 U
  uint32_t I2C_ClockSpeed; //I2C时钟频率设置
3 N1 D# s- D( b2 d  t3 r  uint16_t I2C_Mode;             //I2C模式设置+ {1 s* [* z! M! j# |
  uint16_t I2C_DutyCycle;     //高低电平时间之比
/ y7 m+ ~3 F% C! ]" j  uint16_t I2C_OwnAddress1;      //主设备地址设置,也就是自己的地址
( P+ c6 X1 w, C$ E. [  uint16_t I2C_Ack;                 //Check
' H0 F8 E% @+ k  @; I  uint16_t I2C_AcknowledgedAddress; //地址长度,可以为7bit的也可以为10bit的
9 Z8 a& P1 G" {. O  n1 {* S}I2C_InitTypeDef;: a8 |  Q$ Z' `5 F4 K2 X& x# O& U

1 r# P  r  N) l- k- a' U  HIIC初始化完之后,我们开始来研究eeprom
! d. F2 b$ c$ a1 @" z. u: U% |! E7 j! G) J

; _  ]: V$ S; i$ K: P! V" w! ^
2.jpg
1 f6 ]- p; R- u, d6 @

' X* O, w1 \0 x! Y完这个写一个字节的协议之后,我们应该对这个写已经没有什么问题了,很简单的。' T& r9 }' _5 L$ |! j
: [! \/ P6 ^0 Z9 t5 L$ Z6 m0 J
3.jpg
6 a: N2 y7 X' l# o- x- N
9 @% Z. Z. q% ^+ t3 i
这个是写一个page9 b: O+ T4 g7 n9 k$ `0 J! o  O
( B" K: w  g. R7 e% |
注:在eeprom里面写数据时,一次最多只能写一个page,一个page为8byte,同时这个也有字节对齐的要求!9 C7 M& Q/ o9 M% I" R/ b% b' c

) v. p* W: k9 r, x& v7 }
4.jpg
  M5 Q6 g& q/ T" h2 D; K5 R2 U% G

, G% t* z& R. o6 E/ Z. G% r比如我们从Address = 4开始写,那么我们最多一次性可写4个byte,如果我们从8开始写的话,我们就可以8个byte,最后偏移到15。
1 U3 b, ~& t( t8 W; @
  1. [cpp] view plain copy
    9 r( j9 w& n6 l1 j3 U5 a7 N
  2. void eeprom_write_byte(u8 wt_addr,u8 data)  
    & y- w# E$ K: u' |1 S* ^, c* ]; W
  3. {  1 J" ?  H2 n/ f/ m7 J; |  M+ \; O
  4.   2 e- v- Y" `6 Y0 A5 ~
  5.     I2C_GenerateSTART(I2C1,ENABLE);delay(5);  : @+ O: }: w3 A  r
  6.     I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Transmitter);  8 `0 }# f& r7 r" Z/ |
  7.     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));   
    1 I/ u" p8 q6 O8 R1 h9 o9 r
  8.     I2C_SendData(I2C1,wt_addr);delay(5);  
    2 j8 R9 Q# R6 V' W; U$ l
  9.     I2C_SendData(I2C1,data);delay(5);  0 e4 g# w) f( L* M
  10.     I2C_GenerateSTOP(I2C1, ENABLE);  
    , @, ^# Z8 G0 n
  11.     delay(20);  
    0 X2 Z9 s6 ~& \
  12. }  
复制代码
由于stm32的i2c确实做的不怎么样,标着寄存器太多,也不容易识别,我们就不要检测这些标志寄存器,用延时了把他们隔离了。不过在把地址发送出去之后,要检测设备是否被选中,这个在我们的模拟的i2c里面也是必须检测的!可以认为是必不可少的!
( z: F8 d7 \- \" T! {
  1. [cpp] view plain copy8 B9 I9 A( z! g# \
  2. void eeprom_write_page(u8 wt_addr,u8 *buff,u32 length)  1 Q4 r, U' b: d  |/ ?6 N6 ]& @/ R, n
  3. {  
    $ y% L. [  Z9 y/ x
  4.     int i = 0;  - M# N) _) g) L5 g( }# D6 F" F
  5.     I2C_GenerateSTART(I2C1,ENABLE);delay(5);  - H1 f% c  i( ^
  6.     I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Transmitter);  9 h* k& F- |+ e4 J
  7.     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));   
    : u7 z; b& n' u+ L! T" w+ e2 W
  8.     I2C_SendData(I2C1,wt_addr);delay(5);  
    * n1 P' T) e8 E! g* j
  9.     for(i=0; i<length; i++)  
    + i7 [% X2 W* P& ^
  10.     {  
    / M; e( o  l, i* ?2 [5 o. |
  11.         I2C_SendData(I2C1,buff[i]);delay(5);  0 q+ S3 g5 ^9 W/ ~2 r- m: T. f
  12.     }  7 Y& i3 P! W* H$ F5 e) n' u
  13.     I2C_GenerateSTOP(I2C1, ENABLE);  
    % u. O. X, j0 ~) m
  14. }  
复制代码
这个是写一个page的函数,如果大家想看比较灵活的写函数,去我看我前面发表的博客里面找找,那个无论你写几个都无所谓,只要不超过eeprom的大小!9 X. L4 V  T' Z# b

$ O6 y9 Q. J* O) d* A7 Z. D
5.jpg
* |' {/ n+ k! S' T

+ W' b5 h$ u* L( D9 h  l4 ]0 h读数据是稍稍复杂一点点的,我们首先要选中设备,然后选择我们要操作的地址,这时候不要stop,如果stop信号一发出,总线就被释放掉了,设备也就跟处理器断开,所以这里需要一个RSTART,跟START不一样,多了个R,这个可以理解为重新开始,这个信号不会选中其他设备,也不会丢失当前设备。! d( s$ N# R  c; `8 a
" L7 t9 ?" x7 g$ }' c1 f6 n( B
然后还有个注意点是,在读完第N个字节后,不要返回回应,直接stop,不然设备会以为你没有结束,会一直占据总线,等待下一个数据的发送,这样等你下一次来访问他的时候,他就不让你访问了,因为他还停留在给你传数据的状态,所以这里一定不要返回acK直接stop信号发出哦!" _2 P9 Q+ ]4 z& D2 n/ v3 K
  1. [cpp] view plain copy: l% Z& `  N$ Q8 ]
  2. unsigned char eeprom_read_byte(u8 rd_addr)  
    3 l" h- Y5 h- Y. r) F& G
  3. {  6 G7 [! d7 F- b0 @. I% Y+ j+ o( V, |! l
  4.     u8 temp = 0;  
    : j7 Y/ J# j+ A) M
  5.   
    6 R. e4 O' }+ t- u: b
  6.     I2C_GenerateSTART(I2C1,ENABLE);delay(5);  
    / B. X! J9 m$ C# F; }& M
  7.       . j% {4 P1 u" ?7 O
  8.     I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Transmitter);  
    ) L* Q% h$ j' z9 A0 {
  9.     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));  & ]+ G' Y) a; J9 D7 k% x5 q
  10.       
    , s) D. L2 `( F3 |6 X: B8 y7 S, S
  11.     I2C_Cmd(I2C1, ENABLE);  - P1 J$ {+ \* h. t/ i& j
  12.     I2C_SendData(I2C1,rd_addr);delay(10);  
      D1 D" g. n) C& N* |+ N! h
  13.     I2C_GenerateSTART(I2C1,ENABLE);delay(5);  
    # o8 k2 m# m: }
  14.       
    6 d( O) x0 d) X4 i$ l; R
  15.     I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Receiver);  
    $ |0 p! \* O, ]6 D* x
  16.     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));    x8 D( B7 U5 f0 l& g
  17.       
    / _$ f; L- K/ f
  18.     I2C_AcknowledgeConfig(I2C1, DISABLE);  2 Q. A. J7 n) [2 V9 P' K
  19.     while(!(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)));  " L% D8 W* m* M
  20.     temp = I2C_ReceiveData(I2C1);delay(5);  
    9 N0 z% @6 V9 `" t$ a7 m
  21.       
    ) l( f0 U9 {6 W- ^5 L7 ^/ K
  22.     I2C_GenerateSTOP(I2C1, ENABLE);  ; s' n5 s1 r( C* M, F
  23.     delay(20);  
    9 d+ e/ T3 C) R8 |7 k6 H5 \
  24.     I2C_AcknowledgeConfig(I2C1, ENABLE);  # {7 I+ a" J7 S1 n( e
  25.   
    ! R! E3 A4 @. z& R* U) n/ s
  26.     return temp;  
    / H: B& y$ n( c
  27. }  
复制代码
还是有几个信号是必须确认的,设备地址发送出去,看设备是否有回应!
' ^% I7 N5 b  \# R0 \2 M& {/ _. Y
这里最后一个NOACK必须在发送最后一个字节前使能,在stop信号发出后,记得吧ACK信号重新使能,因为我们刚刚开始是需要ack的,只是最后有时候不需要!
  c. u, s& |3 b( e. S8 M; s# c5 W: x6 X
6.jpg
6 E2 J+ @  Y7 M# N# T( s+ L' y3 I$ ^

% o& C  l" l4 n( Q& q2 Y1 M% A  g对于数据的读,在所读数据长度上,是没有要求的,也没有page限制,想读多少,读多少!7 Y1 W! R3 d& g# `7 Y6 f, a2 c
  1. [cpp] view plain copy/ t  y9 p# b* L+ o; G" j
  2. void eeprom_read(u8 rd_addr,u8 *buff,u32 length)  
    1 X: s! |3 }+ k  H
  3. {    ~# v  @: R( a3 B: d( M7 E
  4.     int i = 0;    h1 h) W0 a6 P2 j( L' w
  5.     I2C_GenerateSTART(I2C1,ENABLE);delay(5);  4 k' V1 O2 X1 ~3 i# F2 n$ s# v6 t# v
  6.     I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Transmitter);  / G; t  e. E3 F9 Z, V
  7.     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));  
    ( x  L5 o% F5 L3 a5 e
  8.     I2C_Cmd(I2C1, ENABLE);  
    ; s/ C/ D; H5 C( O
  9.     I2C_SendData(I2C1,rd_addr);delay(20);  
    $ ~  P# k1 o1 k: \: ~
  10.     I2C_GenerateSTART(I2C1,ENABLE);delay(5);  
    / t. R- y2 g9 ]3 C0 K
  11.     I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Receiver);  
    ( n6 {  n" ?& z2 R
  12.     while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));  5 {. T' I& F+ o2 ]( Y: {) X
  13.     for(i=0; i<length; i++)  
    5 ]' c9 }) s# a$ C- `6 D" Z/ f
  14.     {  
    " i  c4 Q) Y7 z
  15.         if(i == length-1)  ) h4 }7 w) f6 p7 c
  16.             I2C_AcknowledgeConfig(I2C1, DISABLE);  
      h) l6 v0 ?4 h* p+ F1 v+ P, w- x8 [* P/ P
  17.         while(!(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)));  2 y' ?5 e# H6 c! v) b* T; W
  18.         buff[i] = I2C_ReceiveData(I2C1);delay(5);  
    ' K; U; @8 k& K- E
  19.     }  ; {' V. g3 U2 F1 ~
  20.     I2C_GenerateSTOP(I2C1, ENABLE);  , P+ A3 F: J; a. @
  21.     I2C_AcknowledgeConfig(I2C1, ENABLE);  
    5 c; d* ~- E6 D/ g; _
  22. }  
复制代码
PS:在提一下,在接收最后一个数据之前,必须关闭ACK,不然,。。。后果很严重!
' {" G& B  a- F  T) m; U9 u
0 L# i% f1 C; m* a4 E7 u4 i: T
3 o& S: J, ^$ p* Z% Q
1 i3 n% s9 G) a) v" o
3 T, i/ q4 H/ p9 b* e转载自ieczw
& r5 K- ?5 h  h+ {5 d3 o8 ]4 e% d- @$ q# C$ `
6 l; r) ^( I* G$ @& J8 i. j
赞 收藏 1 评论0 发布时间:2018-5-29 11:08

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版