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