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

HAL库的I2C模块函数库改造

[复制链接]
shiyongzhu 发布时间:2015-8-14 14:10
本帖最后由 shiyongzhu 于 2015-8-14 14:55 编辑
5 F2 ^  p( ]# i" T7 T  _9 D
# j5 x# ]4 Q) K9 _9 b% m
ST的HAL库的I2C模块函数库中提供了大量的函数,方便了用户的使用。近期由于需要使用下I2C接口,对HAL库中的函数以及相关的例程进行了一下研究,发现当mcu处于从机的时候,其提供的函数只能满足从机处于接收或者发送状态之一(本人观点,欢迎指正),当主机既需要对从机既需要读又需要写时,特别的不方便。为此本人对HAL库中的I2C进行了一下改造,使其处于从机时收发皆可。
主要的思想是开启地址匹配中断,主机对从机进行读或写时进入该中断后,在中断中读取状态寄存器ISR,看主机是读还是写,然后分别进入相关的子程序即可。 捕获.JPG
具体实现以stm32f072为例,初始化,注意开启中断:
  1. void MX_I2C1_Init(void)
    % N$ Z# B+ q: }. P( \
  2. {
    ' k+ ]& t& v; e. w& \0 k: r

  3. 2 l5 Q0 R" @4 V8 T
  4.   hi2c1.Instance = I2C1;
      z: [% B0 f) H( [( K) Q
  5. //  hi2c1.Init.Timing = 0x00700000;                                                                                                        //1MHz,100ns,100ns,slave5 G  f/ h/ Y1 |1 N# }8 c2 D
  6.         hi2c1.Init.Timing = 0x00900000;                                                                                                                //400kHz,10ns,10ns,slave5 y- B) b1 L3 q( V/ B7 |
  7.   hi2c1.Init.OwnAddress1 = ADDRESS;                                                                                                //µçµ÷µØÖ·( k; @: Z* @. X3 \$ I! u) P
  8.   hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;                // µØÖ·Îª7λ0 K& J+ e+ g% t* `1 E& C( A& q4 I
  9.   hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;: N9 c. K2 x1 `
  10.   hi2c1.Init.OwnAddress2 = 0;4 A+ ?. w- _7 `& y
  11.   hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;' T! t! L, F# q6 ~5 E/ A2 S, a/ D
  12.   hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED;
    + M3 [. H6 M6 Z, a5 P; J" r( H
  13.   hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED;
    , C8 Q, w6 Q5 @0 I  \4 u6 W7 O
  14.   HAL_I2C_Init(&hi2c1);
    - V* ~+ L# w7 V: Z3 _+ H0 }+ V
  15. ; M, z# D8 u' ]" H& d0 F
  16.     /**Configure Analogue filter
    3 F2 u3 }8 S  v9 @
  17.     */4 i1 x& i9 H4 Z: Z' _% I
  18.   HAL_I2CEx_AnalogFilter_Config(&hi2c1, I2C_ANALOGFILTER_ENABLED);
    4 {- N8 w! X8 P, H( c" k0 Q
  19.         * e' u) e& G- X, i+ A- o! y
  20.         HAL_NVIC_SetPriority(I2C1_IRQn, 0, 0);                //i2c1
    ( J9 \$ [" f+ @+ ?0 w# F/ i- z
  21.   HAL_NVIC_EnableIRQ(I2C1_IRQn);
    $ @7 D. [) ~& y! e
  22. }
复制代码

1 {# ^# F$ t4 L3 z
然后写I2C中断服务函数void I2C1_IRQHandler(),具体程序如下
  1. void I2C1_IRQHandler(void)
    . X- h1 Q4 Y8 a
  2. {6 g% s/ t' s7 }4 r8 b1 N
  3.   if (hi2c1.Instance->ISR & (I2C_FLAG_BERR | I2C_FLAG_ARLO | I2C_FLAG_OVR)) {  Z5 s2 A% w; A) Z& x
  4.     HAL_I2C_ER_IRQHandler(&hi2c1);
    8 U& x# J8 b$ D" w
  5.   } else {9 j$ T3 F. p  Z0 W5 W. V' e  L
  6.     HAL_I2C_EV_IRQHandler(&hi2c1);. ~/ ]1 j, k5 x# J1 X
  7.   }& }) A+ V+ i% C) S5 \: c
  8. }
复制代码
- L6 n- Y% o, q$ V% \2 c  X
具体干活的程序如下,本处对原有的函数库进行了改造
  1. void HAL_I2C_EV_IRQHandler(I2C_HandleTypeDef *hi2c)
    2 y0 P6 u" F8 Z: d2 X1 F- M! j0 |. p
  2. {9 F7 \! z- y4 h+ ~3 G
  3. if ((__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_ADDR) == SET) && (__HAL_I2C_GET_IT_SOURCE(hi2c, I2C_IT_ADDRI) == SET))& r0 C" H/ @! T( A0 _2 g! l
  4.   {
    2 R$ D2 B* w+ M9 Q7 {
  5.                 HAL_I2C_SlaveRxTxCallback(hi2c);
    / j' F2 X9 I4 z  |
  6.   } ) u. d0 v- |  w  h) M0 D) J
  7.        
    ! J0 ^# c) \  Q% n& K
  8. }
复制代码
4 E8 S1 o8 u- C' K
HAL_I2C_SlaveRxTxCallback(hi2c)为自己写的函数,具体如下
  1. void HAL_I2C_SlaveRxTxCallback(I2C_HandleTypeDef *hi2c)                " \! j* b! I& n. ]( b
  2. {3 n5 H. {% _1 P: o! f0 T; E  ]
  3.         if(hi2c->Instance==I2C1): h* i8 l5 c" N# P; E' T7 @0 i
  4.         {
    . U& M7 ~2 U2 N
  5.                  /* Slave mode selected */
    9 l: Z: U& m3 n
  6.     if (hi2c->State == HAL_I2C_STATE_READY)
    ' r, \1 A2 ^8 I. o, p4 K
  7.     {5 Q1 Z& E! E, p- S- |
  8.       __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_ADDR); // ÇåÖжÏ
    % X9 `/ L6 X2 Y+ n' G1 H7 B
  9.                         if(__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_DIR))                // ÊÕ·¢·½Ïò
    3 k4 ?8 v* ]3 ~5 g
  10.                         {  q0 `2 u# V# G
  11. HAL_I2C_Slave_Transmit_DMA_NEW(hi2c,(uint8_t*)i2c1_tx_buffer, sizeof(i2c1_tx_buffer));
    / z, _! V; \, {
  12.                         }2 f+ w- G0 I% V" C$ z
  13.                         else+ K3 z  l( ]5 S1 r
  14.                         {- j- H/ M* I* h5 R* a  ~+ r6 I
  15. HAL_I2C_Slave_Receive_DMA_NEW(hi2c,(uint8_t*)i2c1_rx_buffer, sizeof(i2c1_rx_buffer));
    : R$ d) T8 C  d9 n" f2 y
  16.                         }
      b$ w1 B& Z  |" I7 i" i& M
  17.     }
      l  r9 z6 X. f/ j  ]. `
  18.         }* @2 O( d7 B- c

  19. % |3 R. M7 [" e
  20. }
复制代码

1 |' j( U4 W( l% A) E7 z
上面函数中的函数:
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的改造,删除了原有函数中的地址匹配和方向判断的内容。
( Z. U0 h. l# V0 R( ~
使用时,只要开启地址匹配中断一切就OK了
  1. __HAL_I2C_ENABLE_IT(&hi2c1,I2C_IT_ADDRI);
复制代码
( M1 ]0 a, x+ S- ]. i' i

# J; f: c6 ?5 \/ G0 W0 M
/ g3 I; j6 W+ v+ V9 n7 L# Q7 j+ H9 Q
赞 收藏 5 评论16 发布时间:2015-8-14 14:10

举报

16个回答
moyanming2013 回答时间:2017-3-28 11:12:02
yc2168 发表于 2017-3-28 09:04
4 L/ w. X$ O, S- L6 I, e是不是以结束位判断比较好呢?$ i; d! {  Y# c/ Z4 A
IIC 都是以结束位结束,HAL库接收发送都要定长度,这很郁闷。 ...

3 i0 [1 O, r3 q) u; o, g( bIIC的结束符属于底层的细节(类似于物理层的协议),HAL库封装了IIC的实现细节,你直接关注上面的应用就行了。
" ^1 R2 ?' g# S! ]至于收发数量,即使你自己实现物理层也不会知道具体的数量吧?!你只是知道来了数据了,每次读若干个。
yc2168 回答时间:2017-3-28 12:29:39
moyanming2013 发表于 2017-3-28 11:12
7 {. l' k8 x6 U1 C, p! h* dIIC的结束符属于底层的细节(类似于物理层的协议),HAL库封装了IIC的实现细节,你直接关注上面的应用就 ...
( A; g6 ?6 o! F3 E3 k- u6 Z
如果上位机发过来的数据不定长怎么办? 我也不知道他要发多少字节。那我怎么知道应该接收多少个。
) |9 e  m  q) M( h$ W( J  Y0 Q; C/ `以前其他单片机都是已结束位判断一次接收已经完成?
yc2168 回答时间:2017-3-28 09:04:06
moyanming2013 发表于 2017-3-27 20:35
/ y( F$ J. m6 P6 v3 W; }  K8 S在中断回调函数中设置读1个字节,然后根据你的需要判断就可以了。接受完1个字节后根据需要再次打开中断。 ...
: C. {$ c* q7 }8 b. b  m
是不是以结束位判断比较好呢?5 }5 T5 W1 f" B, s. d
IIC 都是以结束位结束,HAL库接收发送都要定长度,这很郁闷。
沐紫 回答时间:2015-8-14 14:47:11
谢谢楼主" {. E/ R2 D0 s* A

$ I! l9 X- V7 c4 e) C楼主在编辑帖子的时候,右上角有个添加代码文字按钮,用来放代码特别好用 QQ截图20150814144826.jpg
shiyongzhu 回答时间:2015-8-14 14:55:51
沐紫 发表于 2015-8-14 14:47
' i! G; a+ s) ]. b: L0 B. U8 Q2 x谢谢楼主
# l/ u; J9 Q$ [4 C" A! [, }  Y8 S( C7 \9 `1 }+ A, z& I' d
楼主在编辑帖子的时候,右上角有个添加代码文字按钮,用来放代码特别好用 ...

" y4 e# ?  @) u, K; _7 e谢谢沐紫,用了下效果好很多!
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

  m8 }, o( N9 |( },我只是路过打酱油的! F% j$ W' j2 Z; [' [
moyanming2013 回答时间:2017-3-27 20:35:21
yc2168 发表于 2017-3-27 17:50
3 C# w+ `! f' \请问如何接收不定长的数据??
6 q8 r+ \/ X3 x; j
在中断回调函数中设置读1个字节,然后根据你的需要判断就可以了。接受完1个字节后根据需要再次打开中断。
12下一页

所属标签

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