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

HAL库的I2C模块函数库改造

[复制链接]
shiyongzhu 发布时间:2015-8-14 14:10
本帖最后由 shiyongzhu 于 2015-8-14 14:55 编辑 ) x. }; C$ ^6 o9 G

0 A6 L: l3 G. T0 O+ j3 Z
ST的HAL库的I2C模块函数库中提供了大量的函数,方便了用户的使用。近期由于需要使用下I2C接口,对HAL库中的函数以及相关的例程进行了一下研究,发现当mcu处于从机的时候,其提供的函数只能满足从机处于接收或者发送状态之一(本人观点,欢迎指正),当主机既需要对从机既需要读又需要写时,特别的不方便。为此本人对HAL库中的I2C进行了一下改造,使其处于从机时收发皆可。
主要的思想是开启地址匹配中断,主机对从机进行读或写时进入该中断后,在中断中读取状态寄存器ISR,看主机是读还是写,然后分别进入相关的子程序即可。 捕获.JPG
具体实现以stm32f072为例,初始化,注意开启中断:
  1. void MX_I2C1_Init(void)8 K- H: ~& ~/ ~5 d1 w! f- ~# r: p
  2. {6 t7 i; H7 T' A( F

  3. 9 F0 d$ ]& O' q1 E2 {
  4.   hi2c1.Instance = I2C1;
    + G. h0 S8 M7 @; ?( E
  5. //  hi2c1.Init.Timing = 0x00700000;                                                                                                        //1MHz,100ns,100ns,slave+ W3 j& Y# I8 e/ O7 R" |( C6 O, z
  6.         hi2c1.Init.Timing = 0x00900000;                                                                                                                //400kHz,10ns,10ns,slave
    . r$ L0 l" \) e( N
  7.   hi2c1.Init.OwnAddress1 = ADDRESS;                                                                                                //µçµ÷µØÖ·( e. s3 h$ m/ J
  8.   hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;                // µØÖ·Îª7λ# F1 B( b( X& `4 C3 a
  9.   hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;& R& Z1 S4 D+ u  d* h- Y% c# [3 L' T. z
  10.   hi2c1.Init.OwnAddress2 = 0;
    9 [& U8 C* D6 T
  11.   hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;4 l/ C0 g8 A+ N
  12.   hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED;
    % z4 p. |: v8 a) ^' a/ a7 Q
  13.   hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED;
    . r4 Z" r8 J# |! l+ D# t, H
  14.   HAL_I2C_Init(&hi2c1);$ L5 B! j- @: U* o
  15. * |2 `# M1 w3 H6 b$ s
  16.     /**Configure Analogue filter # ?- N$ A3 w' O
  17.     */
    % Y  K3 U: \; i( W6 z& ~
  18.   HAL_I2CEx_AnalogFilter_Config(&hi2c1, I2C_ANALOGFILTER_ENABLED);0 I2 R: c( T4 ]6 e3 A
  19.        
    : k$ D8 C7 a  T7 a- w, G7 G& e
  20.         HAL_NVIC_SetPriority(I2C1_IRQn, 0, 0);                //i2c1
    ' J1 G6 G- m4 w* A  ~( I- E
  21.   HAL_NVIC_EnableIRQ(I2C1_IRQn);1 b" l, {5 Z  v# S* n
  22. }
复制代码
( ^, e# U6 C$ M) t: i
然后写I2C中断服务函数void I2C1_IRQHandler(),具体程序如下
  1. void I2C1_IRQHandler(void)
    6 W1 K$ |% e6 T# `" L% |
  2. {, S; K1 k. `, m3 l' G
  3.   if (hi2c1.Instance->ISR & (I2C_FLAG_BERR | I2C_FLAG_ARLO | I2C_FLAG_OVR)) {
    $ H# T+ p3 Y$ O8 v( a
  4.     HAL_I2C_ER_IRQHandler(&hi2c1);
    ! u' b# v( |" a
  5.   } else {
    4 I, p( [) ^  p" A& Z
  6.     HAL_I2C_EV_IRQHandler(&hi2c1);
    " h5 N9 l) E, p  Z% o9 }7 Q0 \  T
  7.   }
    ; k+ a  O, K# c; L* y0 f
  8. }
复制代码
. S2 C  O7 G2 U" u2 o; ?# g
具体干活的程序如下,本处对原有的函数库进行了改造
  1. void HAL_I2C_EV_IRQHandler(I2C_HandleTypeDef *hi2c)
    ; {/ s6 e$ r# u% n$ w/ k
  2. {
    4 b& Z6 }. U* G. T  q
  3. if ((__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_ADDR) == SET) && (__HAL_I2C_GET_IT_SOURCE(hi2c, I2C_IT_ADDRI) == SET))- ~/ O4 e' g- K( R' I2 `5 G
  4.   {
    * i. m' m* V8 b/ s5 q/ _: g2 v, N9 H
  5.                 HAL_I2C_SlaveRxTxCallback(hi2c);8 B, a: _7 E5 y1 n5 I
  6.   }
    ; k9 J5 p4 ]" K7 y; u
  7.        
    8 f! e0 T% K! k4 E# |1 s1 J' I& `8 R
  8. }
复制代码

1 Q& l4 N3 Q! m+ }: }% m$ f6 ?& o
HAL_I2C_SlaveRxTxCallback(hi2c)为自己写的函数,具体如下
  1. void HAL_I2C_SlaveRxTxCallback(I2C_HandleTypeDef *hi2c)               
    ( k; ?4 A6 n) u; k6 j
  2. {
    ) B+ f/ Z4 }) V$ j" r
  3.         if(hi2c->Instance==I2C1)
    4 ?1 F6 L9 }* I, h+ h* H6 Y2 N1 W
  4.         {5 J, R7 V- ~' a' B- Y1 |+ M! ]
  5.                  /* Slave mode selected */
    0 K% X. y, P; ]: U7 A! A
  6.     if (hi2c->State == HAL_I2C_STATE_READY)
    6 j' f% Z. V) O7 B) \6 @- j0 A
  7.     {
    ) t9 B9 g. X! @6 K2 m
  8.       __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_ADDR); // ÇåÖжÏ" ?1 N, ~9 }2 z5 G5 D; X: Z
  9.                         if(__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_DIR))                // ÊÕ·¢·½Ïò
    , V* B; M/ T  v$ \3 U
  10.                         {8 M* g$ x7 A& E5 d4 e, H" f
  11. HAL_I2C_Slave_Transmit_DMA_NEW(hi2c,(uint8_t*)i2c1_tx_buffer, sizeof(i2c1_tx_buffer));- U; m1 L# D7 w5 G# X
  12.                         }, R; e' x# H5 M# Y0 h* A0 h
  13.                         else0 A' o8 V& S& t8 n4 `
  14.                         {
    8 F. W, R* o$ z
  15. HAL_I2C_Slave_Receive_DMA_NEW(hi2c,(uint8_t*)i2c1_rx_buffer, sizeof(i2c1_rx_buffer));1 ?/ ]8 T; k& h# E6 Z
  16.                         }+ a; n/ @8 q4 d7 p# b! m
  17.     }
      ]3 y( P9 k) L% r
  18.         }8 j% ~5 Y1 q/ R8 Q

  19. 2 g- y0 B5 f9 k/ `& j4 R2 M9 Y6 O
  20. }
复制代码
# }5 E' c3 n) M/ _- M
上面函数中的函数:
HAL_I2C_Slave_Transmit_DMA_NEW(hi2c,(uint8_t*)i2c1_tx_buffer,sizeof(i2c1_tx_buffer))
HAL_I2C_Slave_Receive_DMA_NEW(hi2c,(uint8_t*)i2c1_rx_buffer,sizeof(i2c1_rx_buffer));
为HAL_I2C_Slave_Transmit_DMA和HAL_I2C_Slave_Receive_DMA_NEW的改造,删除了原有函数中的地址匹配和方向判断的内容。
7 @9 L9 `6 e! o9 v
使用时,只要开启地址匹配中断一切就OK了
  1. __HAL_I2C_ENABLE_IT(&hi2c1,I2C_IT_ADDRI);
复制代码

) U* [4 Z8 t% D( x2 K: x( B

) ~* X  y4 R* d- U+ {: w% |2 f  |) t8 _
赞 收藏 5 评论16 发布时间:2015-8-14 14:10

举报

16个回答
moyanming2013 回答时间:2017-3-28 11:12:02
yc2168 发表于 2017-3-28 09:04; x' p- Z6 r1 ]$ [+ C$ c* y
是不是以结束位判断比较好呢?
1 E/ o- f. F$ }3 H& gIIC 都是以结束位结束,HAL库接收发送都要定长度,这很郁闷。 ...

6 N4 y' F  g, Q6 |" N3 sIIC的结束符属于底层的细节(类似于物理层的协议),HAL库封装了IIC的实现细节,你直接关注上面的应用就行了。; G2 g& G1 Y) Z" d1 b9 u: R! W8 O( s' `
至于收发数量,即使你自己实现物理层也不会知道具体的数量吧?!你只是知道来了数据了,每次读若干个。
yc2168 回答时间:2017-3-28 12:29:39
moyanming2013 发表于 2017-3-28 11:12- b2 `- s. H! F# z
IIC的结束符属于底层的细节(类似于物理层的协议),HAL库封装了IIC的实现细节,你直接关注上面的应用就 ...

, I3 ^5 F8 Y2 I$ A& N; |8 i如果上位机发过来的数据不定长怎么办? 我也不知道他要发多少字节。那我怎么知道应该接收多少个。  f0 L' o2 a; s( F7 }
以前其他单片机都是已结束位判断一次接收已经完成?
yc2168 回答时间:2017-3-28 09:04:06
moyanming2013 发表于 2017-3-27 20:359 H( Q& Q4 S/ d1 p# C% u
在中断回调函数中设置读1个字节,然后根据你的需要判断就可以了。接受完1个字节后根据需要再次打开中断。 ...
$ C5 I+ ~3 k7 N9 x' ^7 h) c3 b
是不是以结束位判断比较好呢?2 y$ o8 ~* |/ A
IIC 都是以结束位结束,HAL库接收发送都要定长度,这很郁闷。
沐紫 回答时间:2015-8-14 14:47:11
谢谢楼主
/ t, V" A: e) x: B. F, _
! J) E- m; y$ j' B0 O8 s楼主在编辑帖子的时候,右上角有个添加代码文字按钮,用来放代码特别好用 QQ截图20150814144826.jpg
shiyongzhu 回答时间:2015-8-14 14:55:51
沐紫 发表于 2015-8-14 14:47
3 S/ y9 j, j; d- w2 @谢谢楼主2 M, x& O' i4 b
, W# X' S7 c6 s# H1 q8 J. B
楼主在编辑帖子的时候,右上角有个添加代码文字按钮,用来放代码特别好用 ...
1 z* q# @% z# X* x/ F
谢谢沐紫,用了下效果好很多!
Paderboy 回答时间:2015-8-14 17:53:04
多谢楼主,学习了。。
党国特派员 回答时间:2015-8-16 14:07:36
多谢楼主,学习了。。
wamcncn 回答时间:2015-8-17 19:52:34
学习下的经验楼主
wyxy163@126.com 回答时间:2015-8-18 16:39:03
提示: 作者被禁止或删除 内容自动屏蔽
微雪电子 回答时间:2017-2-8 10:44:53
楼主有工程源码吗、可否发给我看下,最新一直在弄i2c 从模式。
草民星空 回答时间:2017-2-10 13:49:22
学习学习
yc2168 回答时间:2017-3-27 17:50:58
请问如何接收不定长的数据??
zbber 回答时间:2017-3-27 18:13:59
; F( @. @% q4 G, W7 W9 _9 a
,我只是路过打酱油的. h3 B* f; v( ?0 o& H- z
moyanming2013 回答时间:2017-3-27 20:35:21
yc2168 发表于 2017-3-27 17:50
  S; c! B. }, x( `6 F请问如何接收不定长的数据??
1 x  s5 \! A$ I/ b! j, k& N9 N
在中断回调函数中设置读1个字节,然后根据你的需要判断就可以了。接受完1个字节后根据需要再次打开中断。
12下一页
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版