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

HAL库的I2C模块函数库改造

[复制链接]
shiyongzhu 发布时间:2015-8-14 14:10
本帖最后由 shiyongzhu 于 2015-8-14 14:55 编辑
. p! M/ t$ Y# b6 @0 }* j( G3 Z" D- _! W: R* |- T" w( v7 i
ST的HAL库的I2C模块函数库中提供了大量的函数,方便了用户的使用。近期由于需要使用下I2C接口,对HAL库中的函数以及相关的例程进行了一下研究,发现当mcu处于从机的时候,其提供的函数只能满足从机处于接收或者发送状态之一(本人观点,欢迎指正),当主机既需要对从机既需要读又需要写时,特别的不方便。为此本人对HAL库中的I2C进行了一下改造,使其处于从机时收发皆可。
主要的思想是开启地址匹配中断,主机对从机进行读或写时进入该中断后,在中断中读取状态寄存器ISR,看主机是读还是写,然后分别进入相关的子程序即可。 捕获.JPG
具体实现以stm32f072为例,初始化,注意开启中断:
  1. void MX_I2C1_Init(void)
    ; Z$ d" X3 @/ Z# w& m
  2. {
      G! h! g& O$ x+ l: P5 l) D; A: A
  3. 2 z* v' H- c7 t* ^$ F6 G# k3 H. f3 Y
  4.   hi2c1.Instance = I2C1;( u& C  j8 n$ N# }& V( ~  Z3 P
  5. //  hi2c1.Init.Timing = 0x00700000;                                                                                                        //1MHz,100ns,100ns,slave8 N7 B, u6 l' x, h5 |: P
  6.         hi2c1.Init.Timing = 0x00900000;                                                                                                                //400kHz,10ns,10ns,slave, w5 S/ \$ ^. B. ^1 I- E: |
  7.   hi2c1.Init.OwnAddress1 = ADDRESS;                                                                                                //µçµ÷µØÖ·& q/ ?: |) b/ |5 ]& o; U6 C5 C
  8.   hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;                // µØÖ·Îª7λ4 X- K2 v! f  q0 q9 Y" B3 Z6 F
  9.   hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;
    % u# ~2 k. g! F! b2 A1 \9 N0 f# C7 w
  10.   hi2c1.Init.OwnAddress2 = 0;
    ! O; [' Z0 |. s+ h* `
  11.   hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;8 A4 g* K& ^: f3 _! t
  12.   hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED;
    1 s  h3 a$ ^$ ^3 P2 C3 M
  13.   hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED;
    ) D) U  Z% |$ G
  14.   HAL_I2C_Init(&hi2c1);( k; T' p1 c4 n$ V8 j' {+ O- h
  15. 4 i$ U: k9 D5 _- |5 ?
  16.     /**Configure Analogue filter ' G! U# ^( l9 `0 K
  17.     */
    ; o. M% o. e/ T) d
  18.   HAL_I2CEx_AnalogFilter_Config(&hi2c1, I2C_ANALOGFILTER_ENABLED);
    . K! T8 u, _- J- k
  19.        
    ' `9 s1 t: i  D$ l. j. R! |8 ]
  20.         HAL_NVIC_SetPriority(I2C1_IRQn, 0, 0);                //i2c1; U8 {  t( m+ x2 k
  21.   HAL_NVIC_EnableIRQ(I2C1_IRQn);
    5 y2 V8 B- Y( C
  22. }
复制代码
* S6 [4 C9 W5 e. |* N
然后写I2C中断服务函数void I2C1_IRQHandler(),具体程序如下
  1. void I2C1_IRQHandler(void)
    . H. Z# P: |$ j2 o% {
  2. {
    : `& K( ^4 }7 R3 f, U7 z
  3.   if (hi2c1.Instance->ISR & (I2C_FLAG_BERR | I2C_FLAG_ARLO | I2C_FLAG_OVR)) {# Q; [6 S0 z7 Z, U
  4.     HAL_I2C_ER_IRQHandler(&hi2c1);" P- K: }* b6 A9 O) ]
  5.   } else {
    6 f$ i+ m! \7 [* K1 `
  6.     HAL_I2C_EV_IRQHandler(&hi2c1);
    6 W' |: C- d2 Z( P
  7.   }
    2 T/ l2 f( m9 e! Y, `
  8. }
复制代码

: X. S3 m4 `$ y% q8 ^0 q
具体干活的程序如下,本处对原有的函数库进行了改造
  1. void HAL_I2C_EV_IRQHandler(I2C_HandleTypeDef *hi2c)) R: j& G' m7 z0 e
  2. {" s0 {5 w4 O& n5 e# W
  3. if ((__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_ADDR) == SET) && (__HAL_I2C_GET_IT_SOURCE(hi2c, I2C_IT_ADDRI) == SET))
    0 I- f0 a' w: [" u$ j* _' ]& B
  4.   {
    + `5 x: Y6 s/ ~! N: Y
  5.                 HAL_I2C_SlaveRxTxCallback(hi2c);% |3 I" Q; F: a( N( k: g
  6.   }
    ! y1 O5 h, n* m+ a- ]0 M
  7.         5 W; n) U' M. O
  8. }
复制代码

+ X2 y" t% |" C
HAL_I2C_SlaveRxTxCallback(hi2c)为自己写的函数,具体如下
  1. void HAL_I2C_SlaveRxTxCallback(I2C_HandleTypeDef *hi2c)               
    9 H9 i- F4 J; r: J: k
  2. {: M6 @7 T- J- [( R. E2 @5 y
  3.         if(hi2c->Instance==I2C1)
    ! T4 R" P. h% a" q! n% o/ E
  4.         {
    6 ]" G1 Q# S! \
  5.                  /* Slave mode selected */: m& i5 r# ~- \. ?# Z. h, I3 z/ Y! i/ ]
  6.     if (hi2c->State == HAL_I2C_STATE_READY)
    0 D7 c* k0 l% b% I: q+ O
  7.     {9 v4 R1 G6 r1 G2 O, o, g
  8.       __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_ADDR); // ÇåÖжÏ
    ) u  u0 w+ ~6 Q0 C3 U' q* {7 J* x
  9.                         if(__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_DIR))                // ÊÕ·¢·½Ïò
    / M1 L9 ?7 W( H& p
  10.                         {# M. J8 }: h5 n$ T* X* a
  11. HAL_I2C_Slave_Transmit_DMA_NEW(hi2c,(uint8_t*)i2c1_tx_buffer, sizeof(i2c1_tx_buffer));
    , e6 f" Q( D. ]' l! c6 j1 O
  12.                         }
    2 S; F2 s, g  \- |
  13.                         else( A' T$ D: j: e3 D9 v
  14.                         {; n7 o2 W  u! x) f" L' V( i
  15. HAL_I2C_Slave_Receive_DMA_NEW(hi2c,(uint8_t*)i2c1_rx_buffer, sizeof(i2c1_rx_buffer));5 t  z1 t; f+ S
  16.                         }+ K2 t! F+ j8 p: \/ s0 M, O. Z
  17.     }: s! J, n# C- r
  18.         }
    8 I9 D" M/ M& K, |( \
  19. * M/ c- v- {: D7 G" F
  20. }
复制代码

& b" _9 B% S1 l6 l! x
上面函数中的函数:
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的改造,删除了原有函数中的地址匹配和方向判断的内容。
" S, ^* q  P# O4 L
使用时,只要开启地址匹配中断一切就OK了
  1. __HAL_I2C_ENABLE_IT(&hi2c1,I2C_IT_ADDRI);
复制代码

0 B) [6 W7 X# A+ D6 g, w
! O8 I) A3 f/ m7 D

) o( J3 g% q$ a; }/ L
赞 收藏 5 评论16 发布时间:2015-8-14 14:10

举报

16个回答
moyanming2013 回答时间:2017-3-28 11:12:02
yc2168 发表于 2017-3-28 09:045 L' P# U2 F  {+ F1 K% l8 @- A
是不是以结束位判断比较好呢?! N# j' h5 |! _( ^  ^( a
IIC 都是以结束位结束,HAL库接收发送都要定长度,这很郁闷。 ...
' ]6 W- F6 H! |4 e) C
IIC的结束符属于底层的细节(类似于物理层的协议),HAL库封装了IIC的实现细节,你直接关注上面的应用就行了。
8 r, C, \% f/ c% X" \+ B至于收发数量,即使你自己实现物理层也不会知道具体的数量吧?!你只是知道来了数据了,每次读若干个。
yc2168 回答时间:2017-3-28 12:29:39
moyanming2013 发表于 2017-3-28 11:12$ \5 {$ @# d+ }/ z6 {, [
IIC的结束符属于底层的细节(类似于物理层的协议),HAL库封装了IIC的实现细节,你直接关注上面的应用就 ...
/ ^7 q$ E0 X) v$ ~( K* |
如果上位机发过来的数据不定长怎么办? 我也不知道他要发多少字节。那我怎么知道应该接收多少个。& E3 @3 s" x: W0 r& C/ ^0 b/ z
以前其他单片机都是已结束位判断一次接收已经完成?
yc2168 回答时间:2017-3-28 09:04:06
moyanming2013 发表于 2017-3-27 20:35) x2 g2 L' `0 k( a, m
在中断回调函数中设置读1个字节,然后根据你的需要判断就可以了。接受完1个字节后根据需要再次打开中断。 ...

1 ^" L5 _: u: Q8 o是不是以结束位判断比较好呢?- j3 s0 j+ p6 s8 ?  _( \
IIC 都是以结束位结束,HAL库接收发送都要定长度,这很郁闷。
沐紫 回答时间:2015-8-14 14:47:11
谢谢楼主
) r* `' S9 h, o5 p' ^6 ^3 I) e8 v; J: C0 \: N$ ]4 q5 W/ T
楼主在编辑帖子的时候,右上角有个添加代码文字按钮,用来放代码特别好用 QQ截图20150814144826.jpg
shiyongzhu 回答时间:2015-8-14 14:55:51
沐紫 发表于 2015-8-14 14:47
1 V4 `7 k) Q: ^4 _) V谢谢楼主
; M5 u  Z. A% z" H0 y' S3 z
1 D4 L( D. ^6 ?0 L3 `; S% r5 W楼主在编辑帖子的时候,右上角有个添加代码文字按钮,用来放代码特别好用 ...

! H$ K& ]' ]: `3 Z. ^谢谢沐紫,用了下效果好很多!
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

4 x7 [3 M0 d, s7 q0 u,我只是路过打酱油的
6 I8 _, i+ ~$ d$ Y
moyanming2013 回答时间:2017-3-27 20:35:21
yc2168 发表于 2017-3-27 17:50
8 ?/ h1 j) \: B( s: A' H请问如何接收不定长的数据??

9 n( N0 y, x( @% u- f在中断回调函数中设置读1个字节,然后根据你的需要判断就可以了。接受完1个字节后根据需要再次打开中断。
12下一页

所属标签

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