背景介绍
: m2 `( [% A6 l$ U0 ?2 x7 rI2C协议介绍
2 Z; y! I% y# W& |$ Q3 q' D; ]. \6 b' K2 V$ q$ d! O( ]" s
I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
# U, k b& u* l% p8 c2 `" f& x4 c; @' F# ~7 |) Y
I2C 总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来 产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。/ ?+ g+ K- V* u9 u }7 T
9 W3 F, z. r. G' B* |3 k7 f
/ M8 D7 C5 w9 g/ [6 U( R* u) O
5 U/ \8 A" t/ d- ]1 jI2C总线特征
8 Q5 v$ G9 h- Q$ X- e9 sI2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址(可以从I2C器件的数据手册得知),主从设备之间就通过这 个地址来确定与哪个器件进行通信,在通常的应用中,我们把CPU带I2C总线接口的模块作为主设备,把挂接在总线上的其他设备都作为从设备。) ^& x* u* D9 ~+ w% x
3 G4 i3 B O7 f6 h# x: e$ o
I2C总线上可挂接的设备数量受总线的最大电容400pF 限制,如果所挂接的是相同型号的器件,则还受器件地址位的限制。
) h; Q ]4 S9 l3 C& G) u' W* A
5 a/ Q! [; E7 c" T* S0 i* P/ oI2C总线数据传输速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s。一般通过I2C总线接口可编程时钟来实现传输速率的调整,同时也跟所接的上拉电阻的阻值有关。* M0 y- N+ r' y: L" F, s( q$ a
% o# Z, M% {4 v% S: s0 f. J1 a% rI2C总线上的主设备与从设备之间以字节(8位)为单位进行双向的数据传输。
/ ^7 J4 g+ }9 Y# g. N% R1 o
3 l" ^' x0 r4 ~2 fI2C总线协议
/ V0 r" p8 B/ o# q9 ?6 zI2C协议规定,总线上数据的传输必须以一个起始信号作为开始条件,以一个结束信号作为传输的停止条件。起始和结束信号总是由主设备产生。总线在空闲状态 时,SCL和SDA都保持着高电平,当SCL为高电平而SDA由高到低的跳变,表示产生一个起始条件;当SCL为高而SDA由低到高的跳变,表示产生一个 停止条件。在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线;而在停止条件产生后,本次数据传输的主从设备 将释放总线,总线再次处于空闲状态。如图所示:3 o6 V; j2 }1 W; S% }
+ j7 j) } T- l$ D1 W g; z8 x$ R* Q! C% m! A5 |
在了解起始条件和停止条件后,我们再来看看在这个过程中数据的传输是如何进行的。前面我们已经提到过,数据传输以字节为单位。主设备在SCL线上产生每个 时钟脉冲的过程中将在SDA线上传输一个数据位,当一个字节按数据位从高位到低位的顺序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应答位, 此时才认为一个字节真正的被传输完成。当然,并不是所有的字节传输都必须有一个应答位,比如:当从设备不能再接收主设备发送的数据时,从设备将回传一个否 定应答位。数据传输的过程如图所示:$ s( ^0 l6 z1 d& J$ d; p3 U0 d
1 B! A: C5 N. C, c' p+ Y
( {" C2 }3 r1 {! a, J4 f# F
4 I: [* M/ D9 t; {: ]2 F在前面我们还提到过,I2C总线上的每一个设备都对应一个唯一的地址,主从设备之间的数据传输是建立在地址的基础上,也就是说,主设备在传输有效数据之前 要先指定从设备的地址,地址指定的过程和上面数据传输的过程一样,只不过大多数从设备的地址是7位的,然后协议规定再给地址添加一个最低位用来表示接下来 数据传输的方向,0表示主设备向从设备写数据,1表示主设备向从设备读数据。如图所示:
: r! @* j3 I5 _) f( d8 }- S. A" C- J" w2 N2 ?
3 g6 c- `* v0 Z% ~& Q" d4 l$ H
& s+ t- j/ M8 x3 F, _4 F
4 {. R" i4 x: \* O% B- U8 p
I2C总线操作7 [0 z9 N& e, I7 E" Q
对I2C总线的操作实际就是主从设备之间的读写操作。大致可分为以下三种操作情况:) ^$ }$ ?- r2 x9 L& S# j* y' `( L
第一,主设备往从设备中写数据。数据传输格式如下:0 ]8 \ ]2 @' p) O6 R
- Y4 F( O$ G& C2 S7 r5 c9 W
9 c( T/ I, F8 N- J- G2 X% S
+ E( B) W. r& T4 O: L Q9 a第二,主设备从从设备中读数据。数据传输格式如下:% A1 D7 G: V+ ~2 j
6 d9 B+ p+ e$ n* h, _2 J3 t
! q3 R& u. W. \! C/ l2 _( Z
; W5 U: J+ J, g3 z0 w9 _7 b第三,主设备往从设备中写数据,然后重启起始条件,紧接着从从设备中读取数据;或者是主设备从从设备中读数据,然后重启起始条件,紧接着主设备往从设备中写数据。数据传输格式如下:$ p. R3 M, x0 k' i+ K
9 H5 e3 s' e. r# f
! ^ ]$ g$ r# i0 W& `7 h7 S! |' |" G
第三种操作在单个主设备系统中,重复的开启起始条件机制要比用STOP终止传输后又再次开启总线更有效率。
, J2 F) `! I" U- ?! [
( t! \4 X( M6 a1 \4 o本套库的特点
; ]$ v2 M& |3 u0 T2 A1 S本套库的设计基于三个工程现实问题:* r5 y$ \8 i: M
1.硬件I2C数量有限而且会影响代码的可移植性能;. c1 s1 }! T4 k& P9 v/ F# {
2.I2C通常不会有很高的数据速度和使用频率;
* n6 v: K# |! N' \5 [- y; {3.不同的从机器件一般不会挂载在同一个总线上;; G& h0 ^ k# u7 ~
$ H5 j# l, }" D# b第一个问题决定了必要性。设计一套软件模拟I2C库函数可以使得I2C的使用不受数量的限制且可移植行更强。第二个问题决定了可行性。使用I2C总线的低速低频特性决定了使用软件直接操作单片机的GPIO模拟I2C协议不至于占用过多的单片机资源或者无法满足性能的要求。第三个问题决定了通用性。软件模拟的I2C库函数必须有一套机制可以通过分时复用的方式驱动多条总线。笔者编写的库函数较好的符合了上述三个特征,以下给出源码以供大家参考。) w/ i2 K2 q ?" s# e; Z v
+ r$ W% T5 G, T/ o- I/ f; {( a
源码实现$ K2 X7 ?1 C4 x$ j) }5 q5 c+ g
注意,笔者的源码是基于STM32F407平台运行的,延时函数也是从它处调用。在移植本代码的时候应当注意与平台硬件相关部分(例如void I2C_Virtual_SetSDA_Out())以及延时函数(delay_us())的实现。7 \8 t Z! f% ]0 H" z
5 T( H- y" F, v) g
头文件
?- Z0 U- h, i! Q* r- #ifndef __I2C_H__
3 T; c1 z4 i- o3 D! W+ w' t; G - #define __I2C_H__2 f: c1 |* ^+ ] _( n
- , M- H8 C( r4 B2 s% x% C' Q# T
- #include "stm32f4xx.h"
3 w5 W- t( [* X$ }7 F - #include "stm32f4xx_conf.h"! A- A; @' `* j. M
- 9 f" o+ t- B5 n% _
- #ifdef I2C_VIRTUAL
. N1 x9 Q3 M+ H" n - $ @" {% x- n3 ?
- typedef struct+ t) L* `* ?7 N6 X: `5 w
- {- |7 D0 B1 s. U5 W" q! Y& l T% C
- char SclPort;
$ g% c& G4 ~( [1 _( F; F - uint8_t SclPin;( a3 i3 v7 w3 ?, k" m% C* x
- char SdaPort;
" M* K8 C" o* I+ f - uint8_t SdaPin;, n3 U+ f7 A$ A. }, A
- } I2CPort_Struct;
" y, L. Q& K! B; }# p p - ; V2 B% x! }. g! \: n; A) c* q* Q
- typedef struct# J8 S- ?, c, }
- {: R4 z( {. |6 i
- //I2C从机设备的端口2 ?) l! \, S0 v4 S
- I2CPort_Struct port;
) V v- H) P5 O3 E0 g0 n - /*. F+ W& f- i" Z$ i, A, m- y
- * I2C设备的写地址 = I2C设备地址 << 1 + 0
8 R9 `" |! t, h+ y$ O - * I2C设备的读地址 = (I2C设备地址 << 1) + 13 e9 l- ^8 z, Q; C
- */% Y; _& c/ G- W U" k5 n
- //I2C从机设备地址(器件地址占7位,D7~D1)2 S K: h9 B/ k
- uint8_t address;6 }/ E9 D2 W' T3 `) C
- //I2C从机设备读写状态(0:写,1:读). Z' D o2 a% e8 g8 r3 u
- // uint8_t rwStatus;( P* Z' r* [7 v: ^* p5 x& U
- //I2C从机设备的状态(0:错误或不存在,1:正常)0 x9 K5 G! ]3 {, J3 w- y: \4 t
- uint8_t devStatus;7 }; T" k7 @- R! l! ]& o
- } I2CDevice_Struct;
I$ c `! R. `% L' W% X
4 x6 e. b& ]) J6 w- extern uint8_t I2C_Virtual_ack;
+ h4 }0 C9 E5 Y. k
1 N4 q, i+ f9 g1 I& N- void I2C_Virtual_ConfigPort(char SDA_Port, uint8_t SDA_Pin, char SCL_Port, uint8_t SCL_Pin);# w, T" R7 `* A9 W
- void I2C_Virtual_SwitchBus(char SDA_Port, uint8_t SDA_Pin, char SCL_Port, uint8_t SCL_Pin);
; I3 l% c3 S9 v# x1 V - void I2C_Virtual_SetSDA_Out(void);
2 z2 h( N# V& F' t - void I2C_Virtual_SetSDA_In(void);
: X) x( o$ h/ i3 ~2 L3 L6 B - void I2C_Virtual_Init(void);
9 g: g9 r; x) S, i$ B - void I2C_Virtual_Start(void);
5 m q5 d% M) `! ]. U- {3 J - void I2C_Virtual_Stop(void); E, h0 `2 a- n2 u8 ?- |# J$ t9 n
- uint8_t I2C_Virtual_SendByte(uint8_t c);
# i$ {+ x1 e" X. G. I/ O - uint8_t I2C_Virtual_RcvByte(void);
- h# d/ f4 m2 h- _; L) C- n2 X8 T2 g - void I2C_Virtual_Ack(void);
; s! w# C- ]7 W - void I2C_Virtual_NoAck(void);5 y/ ~$ v3 k% C
- void I2C_Virtual_WaitAck(uint16_t time);& V# e( j; j, ~4 R) z
5 V, H! c# A! S R, N% y2 H/ m- #endif7 ]% P, G9 J9 G) D' q: g+ o5 C
- #endif
7 D0 _ Z: ~$ T% N( l2 h) ^
复制代码 5 h8 r# H8 I. v9 _! u6 j+ e
源文件' e* u' ?% O0 v9 F5 Z' N, d+ m0 I5 |
STM32F407平台
U N7 Z7 }* ?' K/ _- #include "i2c_virtual.h"
# w! [" z0 c/ p' W
8 g4 ~' z; f/ P3 k* @- #ifdef I2C_VIRTUAL
6 L( j0 F' K; w2 V
5 k6 ]4 q! Q* S7 V; m) q- static uint16_t I2C_Virtual_SDA_Pin;
+ n1 R1 P. z2 F8 p% U6 ^2 Q4 H - static GPIO_TypeDef *I2C_Virtual_SDA_Port;; G; w. s o3 b, E( ~
- static unsigned long *I2C_Virtual_SDA_OUT_Addr;, D& {7 |' i# J0 ^
- static unsigned long *I2C_Virtual_SDA_IN_Addr;
^0 k( c* ]3 d - static unsigned long *I2C_Virtual_SCL_OUT_Addr;9 D/ X7 F! f* w* F6 q
7 q3 {- C7 v; c$ m; N A- uint8_t I2C_Virtual_ack; //应答标志位$ P% R0 }+ x4 n; ]( q
1 N0 T3 I. ]! g8 b- /**
1 |+ _+ W7 M+ v9 H5 X: U4 k9 d - * @brief 配置模拟I2C的引脚端口。* L, H) ?, K |3 o( U/ n9 O" j* g
- * 主要实现了MCU的GPIO端口配置,SDA需要配置为开漏输出,SCL需要配置为推挽输出。
3 u) f+ {8 {- S4 u5 n - * 此函数实现与硬件平台相关。5 n2 g6 a6 ?1 S; k
- * @param SDA_Port: 'A'~'G'2 `" l3 _' W8 s! E* Q3 P) \
- * @param SDA_Pin: 0~154 E1 |+ U) @: D# r9 Q! o
- * @param SCL_Port: 'A'~'G'% c" T0 `" a- I( M. ^$ ^
- * @param SCL_Pin: 0~159 ^% M8 O+ M" J8 q0 e( {# H: o7 s! W
- */% x# Q$ U" Y0 o: g, w
- void I2C_Virtual_ConfigPort(char SDA_Port, uint8_t SDA_Pin, char SCL_Port, uint8_t SCL_Pin)
2 h3 n# c d- c' Q4 c - {# e+ h$ e) r; y- Z% v. T2 ^: c
- uint16_t I2C_Virtual_SDA_Pin;
2 K& H. h$ S& E2 }! Y) m& y2 R - uint16_t I2C_Virtual_SCL_Pin;' m# J" M- n3 S# X) z
- GPIO_TypeDef *I2C_Virtual_SDA_Port;: m8 `- B! N4 a6 p! Q0 T6 C
- GPIO_TypeDef *I2C_Virtual_SCL_Port;
% j. V# {3 t! G+ `1 R - GPIO_InitTypeDef GPIO_InitStructure;& K2 e% D# S( \4 r+ f$ i
- * X; n1 h4 g$ {
- switch (SDA_Port)
7 Y3 L# D' f k l5 n2 b - {# [0 m9 q6 C; |
- case 'A':
c# G( O& }9 o/ d) F - I2C_Virtual_SDA_Port = GPIOA;
0 g- n( g% D- j# F* @4 K2 f - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);& U# _& {: x" a4 t2 M
- break;2 M! r$ j3 f; t- K: ~
- case 'B':
6 m! z' E- j% N9 U - I2C_Virtual_SDA_Port = GPIOB;
H: G* j5 X+ D# j) X& W# ? - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);; u& i8 E- D! H' h" Y9 E; J3 h. N
- break;
. p0 c$ ~7 l, L Q5 A ~1 ]9 L' ` - case 'C':
" e. `' E$ D+ z' y" o - I2C_Virtual_SDA_Port = GPIOC;
& u! F! e6 [! F9 V" T$ ?7 E. E. d - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);
% u3 M0 [0 ~' e6 c. j7 y! Y6 S - break;
* M! Z; g z/ J8 Q/ @+ P - case 'D':1 m7 ^" ]' F- O) @: w3 o
- I2C_Virtual_SDA_Port = GPIOD;( |5 l6 o% L: m
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);
^. |, Q+ \7 t4 B- }/ h A. l% _. k* h" O - break;
1 S6 ?; c' Z; e6 x" O - case 'E':, m+ g+ ]' L, {# f6 i6 B5 i
- I2C_Virtual_SDA_Port = GPIOE;, j* M& f1 u% B. M* y
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);" P' U; `* P+ `- |, w
- break;! D6 s3 m8 t6 k7 l
- case 'F':
# | o4 L9 C, s - I2C_Virtual_SDA_Port = GPIOF;7 |: V2 M5 x1 K$ e
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);4 g- h: i" X3 b
- break;
. V1 z* b3 p0 K) n1 q+ i. X - case 'G':+ m: [4 ~" w1 T/ |8 S! E& p
- I2C_Virtual_SDA_Port = GPIOG;
' T: d& h' z2 X - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);* B/ G8 I& ?# `/ k
- break;
8 V( {- C/ ?+ s: }: {% l' @0 ` - default:
/ A3 p% u( K! ^& j - break;' N4 Q$ Q& ~1 c+ {. T' P1 @
- }0 o# h3 h+ n$ F @
4 o0 e7 j V( Q. g& V: o- I2C_Virtual_SDA_Pin = (uint16_t) (0x0001 << SDA_Pin);& v/ V0 c2 K: r
' T6 w f9 m; J x- switch (SCL_Port)4 P" O) T) W, [
- {
3 x: }3 l \3 m9 H$ k& z - case 'A':
8 m- ~; J. B; C* W - I2C_Virtual_SCL_Port = GPIOA;( B. V0 x0 K# C, Y: D1 S$ F: c
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);% p6 P" h: |% W! c' P: F
- break;
9 I/ W- Y4 c$ C, D+ f8 J. N; J+ ~$ V - case 'B':9 w7 o3 d5 |5 i3 \& m# `$ t/ `
- I2C_Virtual_SCL_Port = GPIOB;
" l. B4 z! X$ n' T( L; N0 ~ - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);* ~ C& v7 ]: x' O% o
- break;
7 p7 j- D3 \4 E- p - case 'C':& z6 |& [ {6 k4 U4 B! ~
- I2C_Virtual_SCL_Port = GPIOC;
; ?. Z. a( w' p: f; s. Q - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);
! p/ ?+ P' W' }3 t* o2 w, ^ - break;
2 K: c1 O d( y7 o" W" `5 j X - case 'D':9 K' @4 `+ |& h3 P- X
- I2C_Virtual_SCL_Port = GPIOD;- Z4 ?" e: y) l- f
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);# p( c0 S8 A8 Z9 G2 U% ^7 {
- break;
, V {( F) m5 u6 g - case 'E':0 g D# q H6 ^4 I6 ~
- I2C_Virtual_SCL_Port = GPIOE;
. D4 z. |5 {" X* R - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);1 K- |: K! E2 Q+ [8 {1 y0 N, l+ j& x4 N
- break;
7 T2 Q6 i- |5 p; s# B3 z - case 'F':" q5 ], O5 y3 M0 x( {; H6 K
- I2C_Virtual_SCL_Port = GPIOF;
3 E* ~6 L$ |4 O3 I; I - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
% w# V) {" T* Q% \" j; D - break;+ y. }, i* Y2 |" Z1 F
- case 'G':
! `3 a' J! K7 `! c) W% y - I2C_Virtual_SCL_Port = GPIOG;
, m" M7 p7 Q9 r; U, c" U3 n: @ - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);) O9 o" c* |3 D/ `) I3 H
- break;
3 [% q V5 V) a2 T7 P* ? - default:% r, a+ g3 S; O8 D. n+ j9 x, g
- break;$ k$ C: U4 j; O
- }8 |+ b% d1 b! t& O( ~ d
- / g- Y& T% z% w* k
- I2C_Virtual_SCL_Pin = (uint16_t) (0x0001 << SCL_Pin);) y1 {; l! G% n3 n1 a; m" h |
9 H' v/ P1 g1 x8 e) ^- GPIO_InitStructure.GPIO_Pin = I2C_Virtual_SDA_Pin;
) a9 |% N( T, g+ W% \ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;4 B9 l$ `4 [$ J$ r3 c
- GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
; l5 d/ g% F) l/ | - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;0 V: r0 q" }, Z$ l' L/ l
- GPIO_Init(I2C_Virtual_SDA_Port, &GPIO_InitStructure);
& b" @) K+ `, I0 I - 3 I/ l# p |; j; E/ B
- GPIO_InitStructure.GPIO_Pin = I2C_Virtual_SCL_Pin;
9 t5 k7 R% o0 j4 o6 ?8 f2 y - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; W5 \: `( j% x6 _8 b8 c2 z* u
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
$ K) c9 o8 T. Y& L. o+ b# {& _4 }& Y - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; q2 t2 _9 N+ s2 W% U( U: S! h
- GPIO_Init(I2C_Virtual_SCL_Port, &GPIO_InitStructure);3 z' l2 k, A9 E# Q; w( j7 P
- }6 n: ^' P P% Z% L8 g
0 E6 b+ H+ S* `3 Z$ Q) h3 e) r: q# t- /**
7 y( C# r% s' `0 \/ Z - * @brief 使本套库函数可以支持多个独立的软件模拟I2C总线。5 C; h; o& h/ H
- * 通过切换I2C总线引脚,以分别控制不同的芯片。
& W4 @4 X; u8 O2 [# | - * 一般在配置模拟I2C的引脚端口之后,需要调用此函数设置总线。
2 R7 |6 i) O; o: O8 Q1 f3 |7 R - * @param SDA_Port: 'A'~'G'9 `9 B$ W4 @- M. e* h: x$ E: \7 F
- * @param SDA_Pin: 0~152 N* O$ Y1 S6 B4 I' B% i4 X
- * @param SCL_Port: 'A'~'G'% x4 D7 i5 U7 f# f' c
- * @param SCL_Pin: 0~15
- F3 j A/ p% |6 z - */2 K- c: @3 [/ h. g5 ]; N0 g) r
- void I2C_Virtual_SwitchBus(char SDA_Port, uint8_t SDA_Pin, char SCL_Port, uint8_t SCL_Pin)
. t& U' T3 p% F4 Z. r* e - {
1 x z2 c$ [ t% s/ z2 F+ i - / e7 f9 B6 I3 ~) w& F* |3 q
- switch (SDA_Port)- ?* \: A. s$ e0 p* R8 P
- {
- I2 `& }4 f7 v$ a2 r - case 'A':
q2 J' l6 ] T' S/ k" K - I2C_Virtual_SDA_Port = GPIOA;
2 P" B0 _8 T6 V5 c" b% S. ` - I2C_Virtual_SDA_OUT_Addr = GPIOA_OUT_ADDR(SDA_Pin);
5 W" x" N" g3 ` - I2C_Virtual_SDA_IN_Addr = GPIOA_IN_ADDR(SDA_Pin);1 Y5 d1 S" e# B0 x
- break;
* z* ~) ]- L1 F+ A$ R. b% z1 u1 y# o - case 'B':
' Q' P! j1 W0 b: S - I2C_Virtual_SDA_Port = GPIOB;5 m5 a/ J& o$ a! K2 S0 ]
- I2C_Virtual_SDA_OUT_Addr = GPIOB_OUT_ADDR(SDA_Pin);
) p+ o' G' P! w - I2C_Virtual_SDA_IN_Addr = GPIOB_IN_ADDR(SDA_Pin);& X/ k& d5 w+ u& \1 ]% C; ~
- break;: w n/ c- e" c- J$ n, m2 ~
- case 'C':
" S% B7 g2 v/ W7 Q0 E3 ~' _! W4 ^ - I2C_Virtual_SDA_Port = GPIOC;; |, L) Z6 }; @, l5 d
- I2C_Virtual_SDA_OUT_Addr = GPIOC_OUT_ADDR(SDA_Pin);
; \' l8 k$ U4 P - I2C_Virtual_SDA_IN_Addr = GPIOC_IN_ADDR(SDA_Pin);# R: g( K# S+ Y9 f
- break;9 y& Q4 I% u# |3 t! Y. S
- case 'D':8 A& u0 c* ^; V* l! p# \1 r
- I2C_Virtual_SDA_Port = GPIOD;3 t% q5 E$ u8 a% ?* t
- I2C_Virtual_SDA_OUT_Addr = GPIOD_OUT_ADDR(SDA_Pin);
% T' D4 ~* M5 f$ ]( z% { - I2C_Virtual_SDA_IN_Addr = GPIOD_IN_ADDR(SDA_Pin);' o' {* H" t0 N, E% S6 G
- break;+ t m9 Q8 v' s* m' x2 G2 s {
- case 'E':
9 n4 ~2 ?1 u! b% }4 L - I2C_Virtual_SDA_Port = GPIOE;" y# y1 ?. _: T# `/ Y
- I2C_Virtual_SDA_OUT_Addr = GPIOE_OUT_ADDR(SDA_Pin);& v6 z* \' Z3 R6 @
- I2C_Virtual_SDA_IN_Addr = GPIOE_IN_ADDR(SDA_Pin);0 J3 O& e( q: f, j! Y
- break;
/ [6 w4 S2 R. ?0 z) P - case 'F':
2 r4 ~: Y8 b% M7 Y - I2C_Virtual_SDA_Port = GPIOF;7 v+ E% c) g2 } z5 E& d$ `
- I2C_Virtual_SDA_OUT_Addr = GPIOF_OUT_ADDR(SDA_Pin);. E7 g. ` c, ^/ O# [
- I2C_Virtual_SDA_IN_Addr = GPIOF_IN_ADDR(SDA_Pin);+ u+ m. ~3 b, H ?8 Y3 [
- break;
& T2 j/ r% e/ B' V) H8 _ - case 'G':- E6 ^/ \1 |1 h5 P7 w
- I2C_Virtual_SDA_Port = GPIOG;" P/ F3 w5 y4 K; `1 ~
- I2C_Virtual_SDA_OUT_Addr = GPIOG_OUT_ADDR(SDA_Pin);- l( f9 h2 n9 }
- I2C_Virtual_SDA_IN_Addr = GPIOG_IN_ADDR(SDA_Pin);- V+ @! B, g+ O* c8 w- }
- break;4 [; |& M$ _; Z
- default:0 j$ T5 H4 y; B& @( I
- break;7 P5 ^. A6 N( V7 G: l7 n
- }
" h+ g8 `/ x- ]
: y$ d5 o+ r0 ]9 k/ q5 U* w6 d) O- I2C_Virtual_SDA_Pin = (uint16_t) (0x0001 << SDA_Pin);9 {1 Z8 V* c" f; w" D( ]
7 P+ k, k1 g# R+ C- switch (SCL_Port)
# L$ f+ M7 C8 _ |* n9 k' s* c - {
% j! M6 L: A9 g9 G - case 'A':
$ t; _: f4 o. A1 A9 _ - I2C_Virtual_SCL_OUT_Addr = GPIOA_OUT_ADDR(SCL_Pin);
7 r) g& w2 D7 n! \/ r& h. [ - break;
" {$ C5 m) F* @; I4 m, h2 h8 r6 X - case 'B':
: `4 `/ i, I, H. t - I2C_Virtual_SCL_OUT_Addr = GPIOB_OUT_ADDR(SCL_Pin);
: t* V% g% D0 B' F% O4 ]5 R- b+ Z - break;+ |8 o$ s1 w! Z7 E! g2 |
- case 'C':; O$ P* E' h" `
- I2C_Virtual_SCL_OUT_Addr = GPIOC_OUT_ADDR(SCL_Pin);, m: y( R! g& G) H, J$ q
- break;% R3 ~: h8 `2 f5 v4 j3 I8 j% \
- case 'D':* A0 ?( Y* a D/ |; q, B
- I2C_Virtual_SCL_OUT_Addr = GPIOD_OUT_ADDR(SCL_Pin);' _9 O: e2 F9 V6 Y/ o q
- break;, W6 ?; N% d8 e
- case 'E':. q+ Z, a3 z& t$ V
- I2C_Virtual_SCL_OUT_Addr = GPIOE_OUT_ADDR(SCL_Pin);/ D5 N" e% \* u* x \5 B+ C: j1 F
- break;
; ]+ \% ^* E G( `7 B( R - case 'F':/ Z0 c. X+ Z1 {% T v2 o
- I2C_Virtual_SCL_OUT_Addr = GPIOF_OUT_ADDR(SCL_Pin);
( D4 p6 V0 T: V6 T2 H6 t( J4 L5 f2 v - break;
( w) j# f/ f- ], D! E, B8 i - case 'G':$ a4 i9 B0 `1 e7 R) s
- I2C_Virtual_SCL_OUT_Addr = GPIOG_OUT_ADDR(SCL_Pin);4 S9 O) L5 V9 x8 P
- break;) t+ u: v T a2 D
- default:
3 T5 c4 E# w7 |( V - break;
( x; Y; { N0 d4 l - }
" O `! l( f+ E. I% X - }
8 ?7 G3 D4 K( d4 l* _7 {3 k( J
" R. V$ A1 e- U" T9 u& A# J- , b, D2 r' w% z5 t
u" g6 o+ f, }5 K7 _7 ~- /**
/ P4 s% p0 s2 G& N b: G - * @brief Switch data bus input/output state.
* ~( p: R9 O9 U - */
: J$ s" v( V6 W, N' l - void I2C_Virtual_SetSDA_Out(void)# y2 F" R$ {6 {+ D9 D8 m
- {
; |" c5 {+ M. Z0 r( ? - GPIO_InitTypeDef GPIO_InitStructure;. ?$ A: _$ R" f; n# D
9 g' m$ \ M8 [8 d+ k0 R7 U) ?' Z. Q- GPIO_InitStructure.GPIO_Pin = I2C_Virtual_SDA_Pin;+ [# }5 t- s1 G `! \
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;! h3 f/ t r/ H. G5 v& y
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
% j" Q6 R& q' ^% F) Q - GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
+ N6 ]* i1 k5 c - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;% {7 r. l7 I) }$ n% w
- 5 z+ |, c/ m1 O* V0 \6 J
- GPIO_Init(I2C_Virtual_SDA_Port, &GPIO_InitStructure);
7 L1 u4 A- Y# r# | - }
1 f5 D/ u0 C! Z! |2 m3 S7 x - ' W) P! n, ]! [9 s! o
- / Y1 E- Y8 C7 z* T
- - f* v. n! t( ]! m
- /**
6 K& i2 C+ c* i - * @brief Switch data bus input/output state.
0 [4 E* Z& R2 P% q3 G( d# ? - */
( X( s' ~( H& Z" d. B - void I2C_Virtual_SetSDA_In(void)9 g7 U1 h3 b2 s' }* @$ o! h
- {
" B# S C- n. q1 ^ | - GPIO_InitTypeDef GPIO_InitStructure;
! m% P8 X( x( R/ p, s2 T$ [& p - * I! _) B0 y+ H' ~+ F0 k$ p$ W1 |
- GPIO_InitStructure.GPIO_Pin = I2C_Virtual_SDA_Pin;: d) G+ Y% w; x1 V- B' ?8 b
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;: {; b3 K" R. m1 `/ j
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
9 \* h. l" }7 r8 ^% J/ b( G4 M - GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
" M( G/ s' @( C; o - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;. Y: D9 ?; I1 W; G
- " M, z3 g+ _ L1 G: l3 H' @) W
- GPIO_Init(I2C_Virtual_SDA_Port, &GPIO_InitStructure);
/ Y6 t& d, g. A) D5 V7 V+ y - }
' N' X+ [8 b$ _ - 2 U& l+ f- g1 ~# l: t1 n! x
- / G) C* c0 {# Y( W. R- g+ f& a
- /**
* P* M7 G2 b0 n5 K6 ?. m! b - * @brief Initialization of the virtual I2C.
" L' J# z* q( r; w - */
% h' @0 K8 V6 Q - void I2C_Virtual_Init(void)- I" ?# T, [" _7 \- z" v. Y
- {
$ x: f. p+ L# _; e - *I2C_Virtual_SCL_OUT_Addr = 1;" {+ ]/ E1 a/ x0 _, Z4 A
- ' }" z8 @ X% |6 i7 Q5 X* O
- I2C_Virtual_SetSDA_Out();
7 [ W$ v0 U; [3 z4 c1 ]! a. c( v9 A - *I2C_Virtual_SDA_OUT_Addr = 1;
3 F0 y, ]+ m" r& n - }7 p% ^" |0 Y& E. R" ?# S2 e, U* A% V
- . m- U* M( ~/ b/ X
J, V* c( {+ c9 L/ d
+ _$ t+ D/ T; @6 f7 n- /** l' W. b) ?" A# N+ e
- * @brief Start I2C.' l4 x: s! L" h
- */! I2 x9 n V+ V
- void I2C_Virtual_Start(), Y3 |8 w7 \+ d, m$ M+ n
- {8 u% a) m9 B$ e; ] V
- *I2C_Virtual_SDA_OUT_Addr = 1; //发送起始条件的数据信号7 y; v! Z7 _, T) C$ d* B
- delay_us(1);
5 |: \) [! |; j6 H' W8 J - + K9 J& j7 |( `: {( A) N# A
- *I2C_Virtual_SCL_OUT_Addr = 1;# B; Q; k: e; I( q# P9 I. }8 S
- delay_us(10); //起始条件建立时间大于4.7us,延时& h. O6 F; L1 q* v3 [: B2 K
- [* S6 O, V" ^9 U! y8 m6 x
- *I2C_Virtual_SDA_OUT_Addr = 0; //发送起始信号( G8 q8 l4 z5 v2 \; y2 W: c' p$ a
- delay_us(10); //起始条件锁定时间大于4μ7 [% k2 ] ?- q3 J7 M& I5 U/ u
: U6 [0 K" O# U$ ?6 W- *I2C_Virtual_SCL_OUT_Addr = 0; //钳住I2C总线,准备发送或接收数据) G2 Q/ U" H: P6 B5 q
- delay_us(2);
8 j/ C. b U6 m0 Q* v+ I' P& X - }
{5 Y' ] a. z% i; v3 v - 0 K9 y. C; k: i" [& p
- 7 k; s# C) @, h. n9 n
' P- T# e) R0 ]* q- /**
# v, q* s6 O# U% n2 J: j4 D - * @brief Stop I2C.7 {- H, m; w3 I: d6 W* b* D0 S
- */5 N% }+ n4 e7 I4 K2 x' [! P ~
- void I2C_Virtual_Stop()
& H' l0 z( \1 |* K# M - {. ]- @1 V0 B# d' ]9 D/ N; D
- *I2C_Virtual_SCL_OUT_Addr = 0;
" ]$ {# K9 w1 S7 N - delay_us(2);
2 b9 ]5 N |, Y2 h8 K - , C2 j2 V7 P7 l; T |9 F& y0 l
- *I2C_Virtual_SDA_OUT_Addr = 0; //发送结束条件的数据信号5 ^$ P, d2 n4 L" v" T6 ]. d) G7 K
- delay_us(1);
+ u" \2 L P9 G6 X) a
" v6 f- _8 V, u* r2 U- *I2C_Virtual_SCL_OUT_Addr = 1; //发送结束条件的时钟信号
; v3 m8 u3 ?& ?3 U - delay_us(10); //结束条件建立时间大于4μ
1 A/ f% e- S5 d+ r( L. b - 4 T& p' J e9 W; g. Y4 M
- *I2C_Virtual_SDA_OUT_Addr = 1; //发送I2C总线结束信号
9 K6 O m6 P$ V# e) {- ?7 N - delay_us(10);
. C' q' V9 k7 ^8 A z - }, p* |- o8 U# A( G5 g
. M# p P% U& }2 K+ `
, t$ t g0 }& d! Z5 m- $ ~9 ?. x! c5 }& O$ N
- /**0 A$ }% w' W! E4 m; {- @" V
- * @brief 主机字节数据发送函数
- c' y Y8 m9 Y5 _1 k; O) E - * 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,: Y5 u7 [, ~8 B7 w( y! c) V
- * 并对此状态位进行操作.(不应答或非应答都使ack=0 假)
3 n" b, c, K' f8 S: I" k; w - * 发送数据正常,ack=1; ack=0表示被控器无应答或损坏。3 r6 _0 z* T" r h. `& p
- * @retval 应答位
; V- y5 D$ O F! v' a, c9 [7 { - * @arg 0:有应答" @ D. p9 J* ^
- * @arg 1:无应答6 Q; }7 a2 l" b& {% d1 j
- */6 {- H+ h0 ]: ^) w$ U9 ?" d, r$ N
- uint8_t I2C_Virtual_SendByte(uint8_t c)
4 Q" t- }- c- S$ V - {3 Q9 M, a6 |! b/ c7 ^! m6 b# G
- for (uint8_t BitCnt = 0; BitCnt < 8; BitCnt++) //要传送的数据长度为8位7 b i( ^$ z/ ]' \2 n
- {
. R' O* }" |. H, y* \ - if ((c << BitCnt) & 0x80) //判断发送位5 b0 \3 |& P$ o' ^4 _! i
- *I2C_Virtual_SDA_OUT_Addr = 1;
8 L% W K, Z) Y& F - else
- t) N' n& I6 J8 l a9 u) Y; W - *I2C_Virtual_SDA_OUT_Addr = 0;2 e9 Q8 k' U% V8 W( ^
- ! [* [2 v2 x- k
- delay_us(1);- i" Y1 q* L$ h! |6 W) t
! k' [5 y7 W! i0 Z- *I2C_Virtual_SCL_OUT_Addr = 1; //置时钟线为高,通知被控器开始接收数据位- O1 x5 |9 \' X3 C0 V5 d' ~! P- E
- delay_us(10); //保证时钟高电平周期大于4μ- g' b& P; o i
- ) p* O5 W6 S8 [- N! Q
- *I2C_Virtual_SCL_OUT_Addr = 0;
3 \6 R* d8 M; F" a2 S2 S - }/ q/ q8 u5 B5 Z
- & Y7 q* ]6 d. D' p
- I2C_Virtual_WaitAck(1000);
$ L+ {% r4 @' l) e& `0 h - 4 K8 b" m' i( f( @4 q5 S! f
- return I2C_Virtual_ack;: I" |" F, y+ U1 {
- }* o" C1 H2 f5 u
/ y# ? L4 b0 c- ! d1 e" w8 M5 Z. w: R/ l0 o9 y
1 }5 X$ Y% g. J, U* K: e+ ~( k- /**
* |7 e" I9 F- ?# f1 Z: V - * @brief 主机字节数据接收函数
) E% ?9 Z+ F9 F3 M4 i - * 用来接收从器件传来的数据,并判断总线错误(不发应答信号),
* G7 B. I* [# b7 @# \* X3 t - * 发完后请用应答函数。
/ x9 U) t1 `! _. `$ v- O - */
( J) O2 j3 P$ _. a' S - uint8_t I2C_Virtual_RcvByte()
! w- h& Y- d+ S! U3 x1 Z - {
1 H* C5 e U- v( B% M0 R - uint8_t retc;( K# ~' \/ c- o8 h* H O- A
- uint8_t BitCnt;
. \# Y" x+ C6 C
3 O9 R. H. H% L3 V ?8 U0 J7 ~- retc = 0;
/ x% \1 l8 R- ?( I1 f - ; C: C; v @# N! z
- *I2C_Virtual_SDA_OUT_Addr = 1; //置数据线为输入方式
& f4 C4 j% @6 b! W2 t* x - ! q0 _' j4 ~5 P
- for (BitCnt = 0; BitCnt < 8; BitCnt++)' Q! d [) N- w. a
- {2 r( H. S" a1 n8 ?0 F6 Y
- delay_us(1);
. n5 P6 x0 h$ N4 ^$ y, u - *I2C_Virtual_SCL_OUT_Addr = 0; //置时钟线为低,准备接收数据位
6 T/ v9 i) E u: b" N - delay_us(10); //时钟低电平周期大于4.7us3 c4 j/ l5 b" O+ Q5 l# M
- *I2C_Virtual_SCL_OUT_Addr = 1; //置时钟线为高使数据线上数据有效
* R, a. V5 r/ T* n - //delay1us();, b! _- L0 F" f
- retc = retc << 1;
Y. b1 A$ _ ]! @ - I2C_Virtual_SetSDA_In();8 [# |' M4 l& Z& q7 W
. Z2 j- e$ Y7 `# \+ i0 Y1 H- if (*I2C_Virtual_SDA_IN_Addr == 1)
: U) U/ U% I! E - retc = retc + 1; //读数据位,接收的数据位放入retc中& Z! F, r5 E# e2 _3 ^$ c
- 3 p' u8 t1 t% R* f" f) C4 x
- I2C_Virtual_SetSDA_Out();
7 G$ ~3 m" _, x; W - delay_us(1);
8 C" [. v" t B6 \9 z& Z+ a - }
0 |& l5 `9 i P# ^$ y
; `. |7 j. j: p) m1 D2 k! A5 q- *I2C_Virtual_SCL_OUT_Addr = 0;
0 R* ]5 V& ]+ t% X. ]! S7 v - delay_us(2);( @8 d [# {" q) E+ m
- ' |* q( K4 H7 e5 w
- return (retc);
8 w' ]: ^0 P' X9 k - }! A; |" I2 H5 o& J0 {
- 1 x8 Q1 m5 A, F3 K: h) r! Y
- * d8 X( _* \+ o3 e2 r- p3 I
6 O2 Y' ?; c6 W; H3 p1 V! c- /**
0 [( [& S* y% d$ J/ t% ~ - * @brief 主机应答函数
. u# L/ z0 s3 S; a% |; b1 s( E - */
1 i( \/ I. t8 U+ l; t" N - void I2C_Virtual_Ack(void)
6 @$ w( G" s! k - {
% o- v. p2 y: g# c1 O1 w" } - *I2C_Virtual_SDA_OUT_Addr = 0;
( h, p0 V4 z, ?, n. L - delay_us(2);
0 }3 b7 l. m8 {# g2 L
8 @$ a& z2 M/ G! ~1 N$ x- *I2C_Virtual_SCL_OUT_Addr = 1;) z9 R$ n3 L7 I/ K. C' k4 q R
- delay_us(10); //时钟低电平周期大于4μ. J) t2 b8 J8 W9 c+ Z/ z
- , [+ z7 x! p J1 b
- *I2C_Virtual_SCL_OUT_Addr = 0; //清时钟线,钳住I2C总线以便继续接收
' A t! m+ c9 O1 i/ M5 R - delay_us(2);' y% _) F* R6 S) g0 y
- }
& k! s$ I5 \: E v$ M$ F
# R; C" A% s5 l* [
2 g8 y* n5 F3 W+ |8 W- 6 l3 N, n( e4 _5 @9 r
- /**
) e; e# l/ ], x+ T - * @brief 主机不应答函数
( l4 [0 w: h5 v5 o+ }9 F - */
) Q' ?. J6 X2 k( l3 ]& W - void I2C_Virtual_NoAck(void)4 G" E7 A. q% [3 S4 n" F* ?
- {
% _) ?1 L. U E( b - *I2C_Virtual_SDA_OUT_Addr = 1;
0 f4 j8 f! X& d( |5 N - delay_us(1);" Y0 e! \( L C0 }% D/ W; K+ m8 w
- 7 l* R4 c; p! y Q2 f
- *I2C_Virtual_SCL_OUT_Addr = 1;: A0 `' K( @- S, J/ r
- delay_us(10); //时钟低电平周期大于4μ
0 |0 e2 A( o8 X - 3 g( l6 Z4 Z! j2 G; i# d7 H
- *I2C_Virtual_SCL_OUT_Addr = 0; //清时钟线,钳住I2C总线以便继续接收2 } {8 X( W" [
- delay_us(2);
6 B1 n" l* J$ z+ m* g0 A1 g5 d - }
+ j1 N9 M" _9 z
- j& q2 |5 C! G- t- H3 J: d3 W9 o8 L2 Y! C
- /**. u1 b* A( a5 Y" q6 _" ]6 Q
- * @brief 主机等待应答函数7 Q; m2 {) P$ a7 X
- * @param time: wait for timeout2 K7 C5 o2 q# d( e
- */
% E0 s' F" r5 ? - void I2C_Virtual_WaitAck(uint16_t time)
/ _2 m2 [" U; z9 p7 D - {$ T6 k# p' V* O0 ]4 ]& a
- uint16_t timeout = 0;0 P& N9 ^% L- l7 g4 m
% w- d, H E, q9 ? d% t- *I2C_Virtual_SDA_OUT_Addr = 1; //8位发送完后释放数据线,准备接收应答位! J& C. Y. F& p9 f
- I2C_Virtual_SetSDA_In();; G2 E* z6 x( Y- |& c0 B' k& g8 G
- ! g3 C; Z( j0 |+ h
- *I2C_Virtual_SCL_OUT_Addr = 1;- L3 t/ Z' n4 V/ F3 K3 m0 l7 |
- ) l# e, Q" u* O, |. w3 I
- delay_us(1);
0 I& e" A. P' l4 U; z% x
( y) r4 o- h" ~3 s4 X0 o0 F6 j- while(*I2C_Virtual_SDA_IN_Addr != 0)- O: X) D2 ?0 T& F3 K- l' D
- {
0 |8 l: z' m8 i% j - if(timeout++>time)1 }; H5 T/ v' z, A" W# u+ T8 l
- {
" h' P( ?9 C& W2 z/ N/ }6 B - I2C_Virtual_ack = 0;
+ ?3 O4 H, g/ Z& n5 a U1 ^ j - $ P. c) `) h" Y p3 l
- I2C_Virtual_SetSDA_Out();
' q* [1 c; g0 |) A - I2C_Virtual_Stop();) t8 B- g4 M0 O. _0 {0 h
- & V6 g9 i3 Q: ?" Y! |6 `
- return;
& V/ F: P; C8 N# ^/ c7 H, I - }
. w6 e% l$ P4 a" ?! [ - } S& I8 D U* d2 a# Q$ `" o
- 3 ]- _: m& |% u! {& s* V% `
- I2C_Virtual_ack = 1;' Y& R+ S! x/ }2 |! y2 O% C
5 ~5 c* V0 q7 O- n" ]- I2C_Virtual_SetSDA_Out();6 G B1 N) q4 }# S& b" n0 X
- *I2C_Virtual_SCL_OUT_Addr = 0;, \' ]! i( G( N# b% p! ~" y
- # W+ x4 y+ O4 B7 q/ ~+ K- o
- delay_us(2);
, @! E3 j8 H4 c - }4 k+ A E5 Z$ v% \; f4 z: B
- ! I4 d' E' d; x x# ~9 s, E# R
- #endif* x5 O9 b- F4 S6 ^' e E- T6 J
复制代码
* b3 I# M K( L% |# K, KSTM32F103平台, |% L- N e7 t5 l8 d
平台的不同影响了void I2C_Virtual_ConfigPort(char SDA_Port, uint8_t SDA_Pin, char SCL_Port, uint8_t SCL_Pin), void I2C_Virtual_SetSDA_Out(void),void I2C_Virtual_SetSDA_In(void)这三个与底层硬件相关的函数。. C2 i+ q3 }/ b1 U1 n
) P- J2 h0 ] h" Y0 z8 H- /**3 [/ I2 Z* L; {' H* M2 T
- * @brief 配置模拟I2C的引脚端口。
# [ |5 j/ t. z" w3 i( A - * 主要实现了MCU的GPIO端口配置,SDA需要配置为开漏输出,SCL需要配置为推挽输出。5 h' c6 Z# Z" v( x# J* H
- * 此函数实现与硬件平台相关。
. F! _/ b7 E) N( X - * @param SDA_Port: 'A'~'G'" S( n4 p3 ?0 j0 N7 k q
- * @param SDA_Pin: 0~15
: M) W' Y+ X4 v8 p$ N* @- _ - * @param SCL_Port: 'A'~'G'; y* s: ?& q0 \8 S3 u D
- * @param SCL_Pin: 0~15
8 t$ a4 y) U }1 j: R - */5 k# \! a: L6 I( m/ x5 G0 N& j. N
- void I2C_Virtual_ConfigPort(char SDA_Port, uint8_t SDA_Pin, char SCL_Port, uint8_t SCL_Pin) W+ R- k9 Q& K' j# U* P( r
- {% m9 y6 A$ Z6 x- g" D# d# n
- uint16_t I2C_Virtual_SDA_Pin;. R# Y" o) ~3 P- g* v
- uint16_t I2C_Virtual_SCL_Pin; A7 ^& `+ b1 V. v2 g! E
- GPIO_TypeDef *I2C_Virtual_SDA_Port;1 Q1 X0 r) k; Q/ ] {
- GPIO_TypeDef *I2C_Virtual_SCL_Port;
1 x- T* a$ W2 b - GPIO_InitTypeDef GPIO_InitStructure;/ Q) c' F ?2 F. h$ a
- 7 E2 F' B) Q- l/ v. K
- switch (SDA_Port)' ]1 l( i! s' B
- {
( w0 t' d2 e% c! S2 [) } - case 'A':
9 m: |2 v! X) Q1 {/ U0 V - I2C_Virtual_SDA_Port = GPIOA;
* m5 ?- [/ G# }. d7 ` - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
% b0 A! P/ W) [$ i$ |9 O* h% y8 T - break;
& t2 y* C; N$ A/ y; ~ - case 'B':
. \$ G/ \* P, q6 m6 l# V - I2C_Virtual_SDA_Port = GPIOB;
; Y, A, q& S0 `, t! @ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
* n) }% m" P. Z6 s - break;
; o& @; ?; m; M y. ? M5 ]) N - case 'C':# H1 y8 f$ v$ Q; T
- I2C_Virtual_SDA_Port = GPIOC;5 k& c2 u# o7 r, n" k4 ~6 H0 d
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
1 O' g& o! x- c! h. ? - break;
9 i) a4 @- d- V2 `1 Q$ q - case 'D':
" D" j3 f" f7 Y( x7 [ - I2C_Virtual_SDA_Port = GPIOD;
F+ G1 [( S7 r! d - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
5 k3 E& d) w! h5 {8 Z/ ? - break;9 G% K# h# Y; V% C
- case 'E':2 S* @) L+ Q- t+ s9 X) C1 v! h. ?
- I2C_Virtual_SDA_Port = GPIOE;
2 X9 }+ i- ^9 R - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
0 W' _' p. T9 f8 F6 i0 X- A% l - break;" y' x0 V4 u% O% _5 w
- case 'F':) D& i$ I3 q. @/ Y7 v
- I2C_Virtual_SDA_Port = GPIOF;
+ R" n! f8 \4 _; ~ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
7 e( |9 e6 o, w1 c - break;
) p- n# p7 k a- w; g - case 'G':% C: T& n# i9 o4 R$ K
- I2C_Virtual_SDA_Port = GPIOG;* S6 \, z& m3 w( {6 S. N/ l! |
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);1 H) G: y* ^3 C) N+ J& c+ h
- break;" S# i" V' z6 T" H6 h* y2 o
- default:/ u+ W- ^) N1 ^0 f2 u3 Q% K7 n3 ~
- break;9 W$ k& S N' n
- }6 m+ ~) e- q+ D; b, [4 P
3 y- u* a- i+ t7 q0 o+ d8 Y- I2C_Virtual_SDA_Pin = (uint16_t) (0x0001 << SDA_Pin);
: U1 D; _8 I& h0 r& w# _: n% s0 F7 A
/ }. O( ?5 e1 C4 I- switch (SCL_Port)
: ]+ d/ r, }* f8 v - {4 N7 _% t0 ~5 q7 R7 L% T6 }4 u
- case 'A': a6 @, \1 z2 |3 l# K/ B3 X0 S) L
- I2C_Virtual_SCL_Port = GPIOA;
$ C7 F4 B5 g, u# v - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
% j) T* _ d+ T5 P% n# ^ - break;" |* x {. @# C) ?$ e6 Q8 b/ Q
- case 'B':# j0 \! ^: D/ N0 c* z
- I2C_Virtual_SCL_Port = GPIOB;/ a. j5 S$ a: U
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);& z# U3 O' s8 [& ]) x: g. v
- break;
3 U/ g8 y* `% s1 P( Y/ n - case 'C':
, q+ s0 W) \5 z! z - I2C_Virtual_SCL_Port = GPIOC;
6 W' J: p# W* w$ n: F5 w0 { - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
" ?9 |5 z0 {& E# F& j5 d$ | - break; v9 {# j0 G) i) V
- case 'D':0 b1 \- `( Z2 t4 [: ?
- I2C_Virtual_SCL_Port = GPIOD;! u# }; t5 X* Z3 V" B, S" {5 [
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
; d! A8 d1 d+ w8 N( Y5 U/ K - break;
, y1 d, D" f: Q' V* I7 e3 }, K8 I - case 'E':5 w+ _1 k1 }) j4 N$ y1 \
- I2C_Virtual_SCL_Port = GPIOE;
( G' P ^& f" ^# P- ]7 ~( ] - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);8 z" _! }! w. Z6 C/ p' s) X
- break;7 d9 ?' s! f' {) L3 T" @2 u
- case 'F':
1 A: C+ Y o# o# ? - I2C_Virtual_SCL_Port = GPIOF;* h8 Z" K* y/ }+ c9 E' M% \5 a
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);% }! k! h* b8 a- l- F- _, ?& y9 ^
- break;
* @/ o% m% O9 A4 ~# D) o - case 'G':( z+ P- R4 ]- N1 H4 p `% p& ]
- I2C_Virtual_SCL_Port = GPIOG;) U# D/ p6 k$ s% s( {7 w6 u* ]
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);
7 x" o3 S, M l. Z0 k) u - break;9 ]8 Q' M# e3 ?( t2 Z+ t
- default:% b- a( O' d; ?% J4 B) F
- break;+ ?9 I2 G k( B1 m9 Y0 h3 Y
- }1 q# G5 ]; Z- P$ A; M
1 R/ A4 I1 _+ i, t$ t- I2C_Virtual_SCL_Pin = (uint16_t) (0x0001 << SCL_Pin);
2 z5 d$ ^# o5 n7 H8 u7 Q4 O* p
x/ W0 G$ P3 J1 k- GPIO_InitStructure.GPIO_Pin = I2C_Virtual_SDA_Pin;6 i2 k5 k6 W/ |" G' k
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;! D+ W6 g7 L% J
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;8 I, @' D) ^# Y2 x$ `7 q
- GPIO_Init(I2C_Virtual_SDA_Port, &GPIO_InitStructure);7 }8 J4 P" T& i& X* ?
) S, Z# A! g+ K1 R- D- GPIO_InitStructure.GPIO_Pin = I2C_Virtual_SCL_Pin;3 C7 x, c7 n8 o8 B
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;3 t6 C+ j# X4 i9 J& E6 |7 F
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;( ^, D7 S) i/ C H' w1 }4 ?/ L% m0 g q0 F
- GPIO_Init(I2C_Virtual_SCL_Port, &GPIO_InitStructure);0 z! S6 d, D5 D0 `7 t) Q& ~3 @
- }
9 {6 ]4 s" m8 j5 D, X - 4 `5 @' M# d& g$ b3 @* c
- u: u! X- @. \1 P- }$ ?" N
( j; _2 w+ k8 l$ b9 M5 {1 u$ M- /**% G$ C' m G+ _9 J. v D8 m8 \- \
- * @brief Switch data bus input/output state.
5 r+ A e0 M1 B Y, \ - */
7 L8 k* x/ i6 C( ?" I! _+ G - void I2C_Virtual_SetSDA_Out(void)
6 o: l6 l( p$ x e' G0 P! E - {2 Q$ n4 X% S& S) O/ x
- GPIO_InitTypeDef GPIO_InitStructure;* G3 Z2 w+ G* d0 X' q- W
- : V4 r9 U6 r% |) }* b
- GPIO_InitStructure.GPIO_Pin = I2C_Virtual_SDA_Pin;) M% S3 `) N8 i: q, f6 J. d
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;: Y+ i) W; t2 _: E7 _% h
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
% E }1 Y: L/ V9 n5 Y( i" n d3 m
: B, l# O- ]. }8 d- GPIO_Init(I2C_Virtual_SDA_Port, &GPIO_InitStructure);* @" E9 H+ P* m/ G
- }
* }; k0 u# w) J. O5 r- E - / J1 c- I, n8 e% B, n% f9 [: E
' n3 h0 K$ Q/ W9 a4 V& Q- $ ]( F& R7 E' L
- /**
; o$ n/ j g$ _: i* o: g - * @brief Switch data bus input/output state.
5 Y0 B+ N2 G9 q# ~1 j - */) _4 A2 k6 _6 _' @& b
- void I2C_Virtual_SetSDA_In(void)1 B+ B$ K( [% R6 S* C1 \7 f
- {
; d/ x' N# _$ N4 ]5 w - GPIO_InitTypeDef GPIO_InitStructure;; ?7 {) I' \; [+ R1 c: ~
. w" c3 u: Q \+ x- GPIO_InitStructure.GPIO_Pin = I2C_Virtual_SDA_Pin;
# c$ T9 z" K. h$ Y - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
8 S& Q6 r. Y2 T3 E8 {, P - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
. V' C+ y/ y, s+ y- b5 n2 Q7 g2 w
" [ u+ M; e) ~8 K _3 {- GPIO_Init(I2C_Virtual_SDA_Port, &GPIO_InitStructure);
2 ?- Y1 r' b1 }" _ - }
复制代码 $ Q: H6 ~. F' p O' B9 z
应用指南
) `8 t% k, n9 G; x3 C$ d/ e$ K: U软件模拟I2C属于底层驱动类型的库,一般情况下不会直接使用,而是为各种芯片的库函数提供底层接口。本章节从框架结构上对软件模拟I2C库驱动多个不同总线上的芯片做一个简要说明。
2 Q0 E. D" k S2 U( Z" J( a3 K. a$ o* X( n
首先应完成GPIO的初始化配置,SDA功能的GPIO配置为开漏输出(总线要上拉)、SCL功能的GPIO配置为推挽或者开漏输出。此处以STM32F103单片机为例:/ c5 s9 l- t0 d" f2 j1 S/ q+ \
- /**0 e6 G B4 c& }5 ?
- * ADS1115 - SDA: PB0, SCL: PB1/ r: m* K; ]( G5 W& Q* b$ z- E
- */+ n7 X" U/ U H: q* [% S
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
+ X- I; t/ d( V3 `; m$ S - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+ d. V( k6 x0 `* _" z - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;6 E4 [+ }: P/ u+ L% c. M- }
- GPIO_Init(GPIOE, &GPIO_InitStructure);
+ b4 U" a+ A% l$ X- R - ) t+ A, l9 g i' A& H
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;& w$ b7 R- z: @8 \9 p7 S$ @' E
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;1 Z& W4 p. u" J
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;: B* K' U! X7 F! f9 z
- GPIO_Init(GPIOE, &GPIO_InitStructure);' f2 {) A' j; v1 f' T4 p
- U! s0 _7 ]! \* a# V- /**3 F9 R' f7 I' _' F! Z' P$ i4 Z
- * ADS1115 - SDA: PB2, SCL: PB3
, n4 o& j- T; b( u - */
! R V0 T* G+ q - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;7 d& O7 ~8 O. I1 S# b7 y) M
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
* Q+ E, q* E# q$ V! @4 r. A - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
: ?# N' N5 N! E# Q k. I8 U- P - GPIO_Init(GPIOE, &GPIO_InitStructure);
; M5 u( |9 L+ p' A' m% a8 k x
3 E- @( D1 r% q- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
7 p& W& b/ O/ X/ ]: k - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;1 G+ e, I/ L' N& ]3 r
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
8 E3 Y* f$ `: Y$ a - GPIO_Init(GPIOE, &GPIO_InitStructure);
q% d( m5 }2 H: \" T" i9 b
复制代码
8 \ p: E Q6 D9 P本库函数中内置了void I2C_Virtual_ConfigPort(char SDA_Port, uint8_t SDA_Pin, char SCL_Port, uint8_t SCL_Pin)函数,可以直接完成GPIO端口的初始化工作。应当注意的是,此函数的实现与硬件平台密切相关,因此在移植的时候需要注意。
Y7 t- i6 A. P0 u
. @0 a8 n# b8 R1 k" s宏定义挂载设备的总线引脚,字符A, B, …,G表示端口号,数字0~15表示引脚号。7 T3 D1 {! j5 B7 D2 ]
- #define ADS1115_SCL_PORT 'A'
8 S% R$ V5 J ^$ q9 d. w5 C - #define ADS1115_SCL_PIN 6+ I, q5 N7 _/ m# ^: z' V4 B9 t
- : i5 |* r$ J9 w$ ~
- #define ADS1115_SDA_PORT 'A'8 i+ t2 m1 ~) M
- #define ADS1115_SDA_PIN 5
复制代码
: V2 ]! f+ P1 y' u9 e- v设置Device1的I2C总线。3 _( B V1 Z: Z( ^3 ]5 l% D, }+ F
- I2C_Virtual_SwitchBus(ADS1115_SDA_PORT, ADS1115_SDA_PIN, ADS1115_SCL_PORT, ADS1115_SCL_PIN);
复制代码 执行Device1的相关操作。
1 D2 Z# ^8 Q) n3 C/ e ?在Device1的库函数的I2C通讯部分,应该调用本库的各种I2C通讯接口,例如void I2C_Virtual_Start(),void I2C_Virtual_Stop(),uint8_t I2C_Virtual_SendByte(uint8_t c)。6 n9 t# w: g, E7 U! |
- ADS1115_Config();9 w8 \$ v4 g0 k6 ^) \+ M- X9 `
- ADS1115_ReadData();
复制代码
3 U. g+ z. S/ o( r, J8 `) H切换Device2的I2C总线
+ N: |8 \& w- q1 x9 c' x- I2C_Virtual_SwitchBus(sts31Dev.SdaPort, sts31Dev.SdaPin, sts31Dev.SclPort, sts31Dev.SclPin);
复制代码 & Y0 D6 d0 w$ E8 D B3 e0 c: G
执行Device2的相关操作
# i6 m& m' o' d1 ]9 N) U4 i- STS31_Config();. T9 Y1 f; q5 R
- STS31_ReadData();
复制代码
6 q, ]: \- j' }5 a; M/ [1 [- j! ?3 C" W! g) Q X8 z" t
, ^9 p6 {/ F I4 F
& P7 l- p- ^' b$ Z# Q |