|
弄了好几天,才弄好。 T" ]* P' ]! g& T+ {# U3 K /*; T; s1 Z0 d' k. O$ x; o ************************************************************************************************************ 版权所有 (C),2010-2012,磊元科技有限公司2 o( `) T$ F: t$ u E& Y --------------------------------------------------------------------------------------------------, d: f7 @. I1 y; ]- [ 头文件名: CommI2c.h 头文件名称: 两线串行总线接口TWI(I2C总线)的头文件 程序版本: 1.00 Q$ ^5 S) v Q6 f 文件功能:+ V% U- w, N% C& {2 Z9 [- w 本文件是对两线串行总线接口TWI(I2C总线)的源程序文件的编译控制声明、字符化常数和输入/输出引脚1 F" S/ q. w6 f$ R3 L 定义、全局常量声明、全局数据结构类型定义、全局变量声明和函数原型声明的头文件。: [$ p# u o5 S# B" g 文件说明: 1.本文件中的所有标识符定义前缀字母表示意义如下: c ---------- 常量+ y" [6 V* @) B& k# P) S" N3 k v ---------- 变量. s" Z8 e0 r4 i2 `5 x g ---------- 全局变量(Global)# v& R4 L) D# n8 ]- D- r* B, c3 C l ---------- 局部变量(Local)* a% H3 i e' ]# k, K8 W uc ---------- 数据类型(Unsigned Char) ui ---------- 数据类型(Unsigned Word) p ---------- 指针; b! f* B# T9 V9 p" \ st ---------- 数据体" A$ l( y7 z6 u# W% [; H pt ---------- 指向数据体的指针 Pre --------- 条件编译预定义( C( @) n3 Q) t4 _: {) V MFH --------- 宏定义硬件操作函数 MFS --------- 宏定义软件操作和条件编译函数% U W* ]9 B: Y" B \* R4 b MFO --------- 宏定义其他函数 2.注意:使用源程序的I2C总线接收和发送数据的函数之前,必须使能全局中断。9 x! T) L$ p( o/ [& n# ^ 3.注意:使用I2C总线作为主、从机进行通信,主机在接收数据时,须连续接收2次才可正确接收到. o, T4 `" o" W$ n 从机发送的数据;因为主机第一次接收数据的第1个数据为I2C总线上出现的最后一个字节。' }) w8 I% n8 J# y 但在与外围器件通信正常,只须接收1次即可。" B' b- m' [& q9 @# f1 e$ _* i 主要函数列表: ▲.在主函数中运行的函数: 1.void UsartsInit(void)(USARTs端口和查询式串行通讯协议的初始化)9 W0 t9 `/ A7 y0 w9 I% W 2.void Rs422UsartsAskManage(void)(查询式RS422的通讯协议处理)' |+ }5 ?0 d& x8 m0 i0 u ▲.中断请求服务函数: 1.void usart1_txc_isr(void)(发送结束中断方式发送数据的中断服务程序) 2.void usart1_rxc_isr(void)(接收结束中断方式接收数据的中断服务程序)1 a3 Q$ M( ~& A* L4 |4 l5 M/ y 编译工具软件: IAR Embedded Workbench for Atmel AVR 版本:5.20A 以上 链接文件: 1.SysCfg.h ------------ 系统运行结构定义配置的头文件。9 ~2 E1 P/ p: M 编作者: 磊元 编制日期: 2009年4月8日 -------------------------------------------------------------------------------------------------- 头文件版本历史: 2006年07月26日 〖磊元〗 -------- 版本 1.0 :原始版本(AVR) 2016年09月20日 〖磊元〗 -------- 版本 2.0 :扩增版本(STM8S)1 ?. T0 ~; r+ V, w! R6 }6 Y( j# r6 G \ ************************************************************************************************************$ X5 \2 x. M' y0 O7 ]+ X$ Y" { */- {& f+ ~; F+ v; F8 I4 V- p* T #ifndef _COMMI2C_H_ // “_COMMI2C_H_”条件编译开始, }7 q! e3 c' Z. v" C" Z #define _COMMI2C_H_ 5 }7 b! z6 l n% f% H) C( d /*========================================================================================================== 本头文件包括的其他头文件 建议:包含本项目的文件使用 #include "文件名.扩展名" , 包含系统库的文件使用 #include <文件名.扩展名> 。 ==========================================================================================================*/ #include "SysCfg.h" // 系统运行结构定义配置的头文件" K4 L5 s, `; W# g0 L9 V, {: g5 Q #ifdef __cplusplus4 y L/ H9 ?& V- x4 U4 r* D2 F extern "C" { // 声明接在下面的指令为C程序编译系统3 N$ t: \7 e% W4 E# U. g5 B8 h #endif /*========================================================================================================== 条件编译控制声明 ==========================================================================================================*/6 V. O8 S8 i0 r8 c #ifndef PreGlobalCompilerHardware // 非“全局统一的条件编译控制和硬件定义”条件编译 #if defined(PreGlobalWithRedundantCode)$ V/ C7 T! L _: L5 w /*------------------------------------------------------------------------------------------------1 |4 g& x* b, r1 e 声明程序指令使用冗余代码的条件定义:6 s: i0 g ^/ M8 @; h+ T9 L5 I ------------------------------------------------------------------------------------------------*/ #define PreCommI2cWithRedundantCode // “使用冗余程序代码”条件定义6 J2 u4 c+ D' K #endif) M3 b6 q, T5 y% G/ I# [ r #ifdef PreCommI2cWithRedundantCode // “使用冗余程序代码”条件编译开始 #endif // “PreCommI2cWithRedundantCode”8 h- l: f3 V8 q- s/ L! Y /*------------------------------------------------------------------------------------------------ 声明TWI接口的I2C总线工作于哪种方式的条件定义:6 K3 _8 q" Q: ~6 P Q+ x 注意:只能使用下面几个条件定义中的1个条件定义,不允许使用2个以上条件定义!!! 警告:此处的声明定义对于本源程序文件和其他源程序文件的编译有决定性作用,应慎重!3 S% L2 M" [( K r8 T) Q2 d ------------------------------------------------------------------------------------------------*/ #define PreCommI2cUseSingleMasterMode // “I2C总线工作于单主机方式”条件定义 //#define PreCommI2cUseSingleSlaveMode // “I2C总线工作于单从机方式”条件定义 //#define PreCommI2cUseMultiSlaveMode // “I2C总线工作于多从机方式”条件定义 #if ( (defined(PreCommI2cUseSingleMasterMode) || defined(PreCommI2cUseSingleSlaveMode)) \7 d( f! ]: E5 @& ~+ @) }+ u && (defined(PreCommI2cUseSingleMasterMode) || defined(PreCommI2cUseMultiSlaveMode )) \4 B/ d* o5 [2 ^ Q && (defined(PreCommI2cUseSingleSlaveMode ) || defined(PreCommI2cUseMultiSlaveMode )) ) #error 错误:<声明TWI接口的I2C总线工作于哪种方式的条件定义>不允许使用2个以上条件定义!!! #endif* ?+ {" H6 V' ? /*------------------------------------------------------------------------------------------------. @6 \ `- W# z2 L 声明在应用中对于具有多个I2C接口的目标器件芯片,具体指定使用哪一个I2C端口的条件定义: ------------------------------------------------------------------------------------------------*/ #define PreCommI2cMultipleI2cEnable0 // 使用I2C0接口的声明定义' R7 o4 [" ~8 d" A w5 _ #endif // “PreGlobalCompilerHardware” " r( E U3 w5 a. p0 L) | |
STM8自学笔记(推荐STM8很好的入门电子书)
无刷直流电机控制应用+基于STM8S系列单片机---电子书
【精华资料】STM8Sxx固件库FOR IAR
基于STM8的DALI (数字可寻址调光协议)
分享STM8 风驰光盘的资料,是完整的(包括原理图+例程+PDF注释)
《无刷直流电机控制应用 基于STM8S系列单片机》
STM8S库函数中文参考 小软件
【资料分享】STM8L的智能手持血糖监测设备的源码
基于STM8S207工程模板
【培训资料】STM8系列PPT培训资料
微信公众号
手机版
/*==========================================================================================================
本源程序文件内部使用的一些操作指令的宏定义
==========================================================================================================*/1 {% f* O! ?- P0 S
/*------------------------------------------------------------------------------------------------
输入/输出端口和引脚宏定义:(注:下列这些定义用以简化宏函数参数字符。)
------------------------------------------------------------------------------------------------*/
#define MFH_PIN_CFG_INPU_I2cScl MFH_PIN_CFG_INPU(cDdrI2cScl, cPurI2cScl, cCirI2cScl, cOdrI2cScl, cIdrI2cScl, cBitI2cScl)" I4 d9 s+ A# }* p0 v! y/ m
#define MFH_PIN_CFG_INHZ_I2cScl MFH_PIN_CFG_INHZ(cDdrI2cScl, cPurI2cScl, cCirI2cScl, cOdrI2cScl, cIdrI2cScl, cBitI2cScl): M( `+ A. |2 R7 d
#define MFH_PIN_CFG_OOUT_I2cScl MFH_PIN_CFG_OOUT(cDdrI2cScl, cPurI2cScl, cCirI2cScl, cOdrI2cScl, cIdrI2cScl, cBitI2cScl)
#define MFH_PIN_CFG_OUTH_I2cScl MFH_PIN_CFG_OUTH(cDdrI2cScl, cPurI2cScl, cCirI2cScl, cOdrI2cScl, cIdrI2cScl, cBitI2cScl)
#define MFH_PIN_CFG_OUTL_I2cScl MFH_PIN_CFG_OUTL(cDdrI2cScl, cPurI2cScl, cCirI2cScl, cOdrI2cScl, cIdrI2cScl, cBitI2cScl)
#define MFH_PIN_OUT_SETH_I2cScl MFH_PIN_OUT_SETH(cDdrI2cScl, cPurI2cScl, cCirI2cScl, cOdrI2cScl, cIdrI2cScl, cBitI2cScl)
#define MFH_PIN_OUT_CLRL_I2cScl MFH_PIN_OUT_CLRL(cDdrI2cScl, cPurI2cScl, cCirI2cScl, cOdrI2cScl, cIdrI2cScl, cBitI2cScl)( ^* y" \& g5 V! e. M M2 `
#define MFH_PIN_IN_STATE_I2cScl MFH_PIN_IN_STATE(cDdrI2cScl, cPurI2cScl, cCirI2cScl, cOdrI2cScl, cIdrI2cScl, cBitI2cScl)
#define MFH_PIN_CFG_INPU_I2cSda MFH_PIN_CFG_INPU(cDdrI2cSda, cPurI2cSda, cCirI2cSda, cOdrI2cSda, cIdrI2cSda, cBitI2cSda)
#define MFH_PIN_CFG_INHZ_I2cSda MFH_PIN_CFG_INHZ(cDdrI2cSda, cPurI2cSda, cCirI2cSda, cOdrI2cSda, cIdrI2cSda, cBitI2cSda)
#define MFH_PIN_CFG_OOUT_I2cSda MFH_PIN_CFG_OOUT(cDdrI2cSda, cPurI2cSda, cCirI2cSda, cOdrI2cSda, cIdrI2cSda, cBitI2cSda). B- t" p- g6 l9 G; l8 I
#define MFH_PIN_CFG_OUTH_I2cSda MFH_PIN_CFG_OUTH(cDdrI2cSda, cPurI2cSda, cCirI2cSda, cOdrI2cSda, cIdrI2cSda, cBitI2cSda) s* O7 w+ o$ ?" f( m8 ^+ g
#define MFH_PIN_CFG_OUTL_I2cSda MFH_PIN_CFG_OUTL(cDdrI2cSda, cPurI2cSda, cCirI2cSda, cOdrI2cSda, cIdrI2cSda, cBitI2cSda)
#define MFH_PIN_OUT_SETH_I2cSda MFH_PIN_OUT_SETH(cDdrI2cSda, cPurI2cSda, cCirI2cSda, cOdrI2cSda, cIdrI2cSda, cBitI2cSda); j( ?' w. j. {0 J# i8 J( O
#define MFH_PIN_OUT_CLRL_I2cSda MFH_PIN_OUT_CLRL(cDdrI2cSda, cPurI2cSda, cCirI2cSda, cOdrI2cSda, cIdrI2cSda, cBitI2cSda)
#define MFH_PIN_IN_STATE_I2cSda MFH_PIN_IN_STATE(cDdrI2cSda, cPurI2cSda, cCirI2cSda, cOdrI2cSda, cIdrI2cSda, cBitI2cSda)+ Y- C( ?7 p! j& o2 U) J
1 h% E0 _: P8 S* |2 M
/*------------------------------------------------------------------------------------------------
MFHI2cInnEventReg( ) --------- 输入I2C总线的事件和状态寄存器值
MFHI2cChkEventReg( ) --------- 检查I2C总线事件状态寄存器中的事件状态值
MFHI2cInnEventMem( ) --------- 输入I2C总线的事件和状态存储器值 n/ ] Q6 m: v) a
MFHI2cChkEventMem( ) --------- 检查I2C总线事件状态存储器中的事件状态值7 H. y: S1 J4 y2 Q. d# D
------------------------------------------------------------------------------------------------*/! }: C( Q- M7 B# g# o: h. d
#if (defined(_IAR_EW_AVR_))
#define MFHI2cInnEventMem( ) gucvI2cEvent = TWSR
#define MFHI2cChkEventMem( ) (gucvI2cEvent & cI2cTwsrMask)9 U4 W9 O3 N( ]
#elif ((defined(_IAR_EW_MCS51_)) || (defined(_KEIL_UV_MCS51_))) J) K. o" I. E; D4 X
1 B6 z) K; X. y. Y' m
#elif ( defined(_IAR_EW_STM8_) \
|| defined(_COSMIC_STM8_) ) // “......_STM8 编译器”条件编译开始
#ifdef _USE_STM8_FWLIB_ // “使用 STM8 FWLib 的库文件”条件编译开始
8 B0 q7 R7 k. S5 n
#else // “_USE_STM8_FWLIB_”
#define MFHI2cInnEventMem( ) NOTHING% l) Q* _# S T8 ~
//#define MFHI2cInnEventMem( ) gucvI2cEvent = temp" O: l R. ?& x: K
#define MFHI2cChkEventMem( ) (gucvI2cEvent & cI2cEvtSr2Mask)) I! B b. n( W
#endif // “_USE_STM8_FWLIB_”
#else
#error 错误:
#endif // “PreMcuIs__......” M5 x, p" C+ R+ ?: b& g
/*------------------------------------------------------------------------------------------------* ~! k/ o- `4 u: [
MFHI2cBusModSwRst( ) --------- 软件复位I2C模块,释放I2C总线
MFHI2cFlgEventClr( ) --------- 清0I2C总线事件中断标志位. f3 ]2 Z1 k2 ?" V" e! i2 |
MFHI2cIntEventClr( ) --------- 进入中断后,在中断句柄中,清0I2C总线事件中断标志位(在中断服务程序中)' P' V; Q" K3 ]
MFHI2cIntEventChk( ) --------- 检查I2C总线事件中断状态值) e: v* W3 Y% m/ h4 }
MFHI2cIntEventOnn( ) --------- 使能I2C总线事件全部中断8 J% i- l6 N- R8 Y" {( q6 A
MFHI2cIntEventOne( ) --------- 使能I2C总线事件中断、错误中断,禁止缓冲中断
MFHI2cIntEventOff( ) --------- 禁止I2C总线事件全部中断
MFHI2cBusErrorChk( ) --------- 检查I2C总线在收发过程中存在的全部错误状态值
MFHI2cBusErrorClr( ) --------- 清0I2C总线在收发过程中存在的全部错误标记
MFHI2cBusNackStar( ) --------- 发出 START 信号(起始条件),没有 ACK 脉冲
MFHI2cBusNackReps( ) --------- 发出重复 START 信号(重新开始条件),没有 ACK 脉冲 a5 q H- J$ D" W5 w* P9 w" [. w
MFHI2cBusNackStop( ) --------- 发出 STOP 信号(结束条件),没有 ACK 脉冲- Q! J; U1 s4 e3 \6 m3 G6 v
MFHI2cBusRdataAck( ) --------- 在接收之后发出 ACK 脉冲: \, Z7 L: I. f2 J5 p* ?* U0 S
MFHI2cBusRdatNack( ) --------- 在接收之后发出 NOT ACK
------------------------------------------------------------------------------------------------*/3 `3 u+ z% ?) ?# H0 b% l& T$ `8 [
#if (defined(_IAR_EW_AVR_))
#define MFHI2cBusModSwRst( ) NOTHING
#define MFHI2cFlgEventClr( ) NOTHING: V3 M( N) u( `6 z
#define MFHI2cIntEventClr( ) TWCR = (1<<TWEN )| \
(1<<TWIE )|(1<<TWINT)| \
(0<<TWEA )| \
(0<<TWSTA)|(0<<TWSTO)| \
(0<<TWWC);& S, ]; h- C1 C9 i* ~+ i5 [2 A3 \, Q
#define MFHI2cIntEventChk( ) NOTHING
#define MFHI2cIntEventOnn( ) NOTHING
#define MFHI2cIntEventOne( ) NOTHING- A' F; p! v& d% E. L
#define MFHI2cIntEventOff( ) NOTHING
#define MFHI2cBusErrorChk( ) NOTHING, y! K: P$ j2 k$ u
#define MFHI2cBusErrorClr( ) NOTHING8 M) {, |8 m! z F# N! G
#define MFHI2cBusNackStar( ) TWCR = (1<<TWEN )| \
(1<<TWIE )|(1<<TWINT)| \$ q& i) R7 H- |" v ?
(0<<TWEA )| \ |% D7 N+ ]" T
(1<<TWSTA)|(0<<TWSTO)| \
(0<<TWWC);
#define MFHI2cBusNackReps( ) TWCR = (1<<TWEN )| \
(1<<TWIE )|(1<<TWINT)| \4 \4 G; F2 h! \* J' D# M* H- f A
(0<<TWEA )| \
(1<<TWSTA)|(0<<TWSTO)| \/ B# _5 u6 {0 O
(0<<TWWC);& C+ p) i% o% I+ C
#define MFHI2cBusNackStop( ) TWCR = (1<<TWEN )| \1 e4 t$ I5 g6 Y8 |% s2 ?7 T
(0<<TWIE )|(1<<TWINT)| \% t8 Z( Q; `+ V8 |; G0 D0 s
(0<<TWEA )| \
(0<<TWSTA)|(1<<TWSTO)| \
(0<<TWWC);" g7 Q$ A% Y" w5 q6 E
#define MFHI2cBusRdataAck( ) TWCR = (1<<TWEN )| \+ z, M' E/ p, | ^3 S6 S; i+ M
(1<<TWIE )|(1<<TWINT)| \
(1<<TWEA )| \
(0<<TWSTA)|(0<<TWSTO)| \ {$ _0 W* g0 y7 b0 _1 f# R
(0<<TWWC);
#define MFHI2cBusRdatNack( ) TWCR = (1<<TWEN )| \
(1<<TWIE )|(1<<TWINT)| \& l. Q& W% i d- {. n9 T7 p+ g1 x2 `
(0<<TWEA )| \5 Q/ g: f! K* S3 _* M
(0<<TWSTA)|(0<<TWSTO)| \' M& _& e; r; ^$ E
(0<<TWWC);
#elif ((defined(_IAR_EW_MCS51_)) || (defined(_KEIL_UV_MCS51_)))8 o9 F! k0 P0 f( Y4 o' n9 x
#elif ( defined(_IAR_EW_STM8_) \
|| defined(_COSMIC_STM8_) ) // “......_STM8 编译器”条件编译开始& c4 ~6 A. N- u1 x% M0 F
#ifdef _USE_STM8_FWLIB_ // “使用 STM8 FWLib 的库文件”条件编译开始
#else // “_USE_STM8_FWLIB_”& B3 p$ V! C7 u( C) i4 w% i/ f
#define MFHI2cBusModSwRst( ) I2CCR1 = (u8)( ((0<<i2cEN )| \: N. Z; R% y$ T$ v- S
(0<<i2cGCEN )| \
(0<<i2cCKTEN) )); \2 A5 \) A/ g5 a: z1 \
I2CCR1 = (u8)( ((1<<i2cEN )| \
(0<<i2cGCEN )| \
(0<<i2cCKTEN) ));. p8 K% [! x& p+ ~
#define MFHI2cFlgEventClr( ) NOTHING
#define MFHI2cIntEventClr( ) temp = I2CSR1;
#define MFHI2cIntEventChk( ) NOTHING: G0 U/ s7 A2 v5 D; o
#define MFHI2cIntEventOnn( ) I2CICR = (u8)( ((1<<i2cERRIE)| \ a+ r) c# u7 T% F
(1<<i2cEVTIE)| \
(1<<i2cBUFIE) ));
#define MFHI2cIntEventOne( ) I2CICR = (u8)( ((1<<i2cERRIE)| \
(1<<i2cEVTIE)| \9 `' f+ \( I! e7 U' r
(0<<i2cBUFIE) ));
#define MFHI2cIntEventOff( ) I2CICR = (u8)( ((0<<i2cERRIE)| \
(0<<i2cEVTIE)| \ `# u* [: W6 t) I- v! {8 R
(0<<i2cBUFIE) ));& O: m% C, J: r6 L9 `' r
#define MFHI2cBusErrorChk( ) NOTHING! @& T8 g, |9 f' |" W
#define MFHI2cBusErrorClr( ) NOTHING
#define MFHI2cBusNackStar( ) I2CCR2 = (u8)( ((1<<i2cSTART)| \7 K u" v4 d, r7 ~
(0<<i2cSTOP )| \7 S6 I8 [# z! j5 s, V( W6 |
(0<<i2cACKEN)| \9 Z2 T$ c! @% |
(0<<i2cPOS )| \
(0<<i2cRST ) ));
#define MFHI2cBusNackReps( ) I2CCR2 = (u8)( ((1<<i2cSTART)| \
(0<<i2cSTOP )| \% ^ P$ m( s5 {" o* f% J
(0<<i2cACKEN)| \
(0<<i2cPOS )| \
(0<<i2cRST ) ));9 J$ A8 a& V- b& H7 F# R9 _
#define MFHI2cBusNackStop( ) I2CCR2 = (u8)( ((0<<i2cSTART)| \1 F0 L" U" m% I+ R8 a( s% q
(1<<i2cSTOP )| \2 {1 K: N! l- v9 B# H
(0<<i2cACKEN)| \* [9 s3 m) o3 U$ y
(0<<i2cPOS )| \! k; I5 Y* n5 M# Y4 q7 f ~& t
(0<<i2cRST ) ));
#define MFHI2cBusRdataAck( ) I2CCR2 = (u8)( ((0<<i2cSTART)| \
(0<<i2cSTOP )| \
(1<<i2cACKEN)| \* ?# Z4 ?! e) E, G
(0<<i2cPOS )| \8 b( N* M% R* k8 b
(0<<i2cRST ) ));
#define MFHI2cBusRdatNack( ) I2CCR2 = (u8)( ((0<<i2cSTART)| \4 d6 e; z; r' I+ q( ?3 Z
(0<<i2cSTOP )| \, q4 {7 h- a7 Y: M9 v0 V( |
(0<<i2cACKEN)| \) H' ?( I$ M% z o) V: p& |
(0<<i2cPOS )| \
(0<<i2cRST ) ));! |! U2 n, Y' l* y! c
#endif // “_USE_STM8_FWLIB_”. C& ]: ?4 W: o n* n8 A
#else
#error 错误:6 x% d2 R$ w$ A
#endif // “PreMcuIs__......”
; _& B6 Y; j# X, E. S; \
& K, k' B0 ^2 q1 F7 Z$ r; u$ ]
; d, s3 W7 {* x6 `
/*
************************************************************************************************************4 t8 m* F8 s* [) W f9 ]
程序指令代码清单
************************************************************************************************************$ F+ f0 m M p- \
*/
/*==========================================================================================================, H( F& X7 l" p
函数名称: 两线串行总线接口TWI(I2C总线),使用中断方式接收和发送数据通信的初始化
函数功能: 本函数用于两线串行总线接口TWI(I2C总线),使用中断方式接收和发送数据通信的初始化操作。2 u8 W4 g$ m* U P
函数入口参数:没有, v$ A$ u4 {1 F* N; \8 t! }
函数返回值: 没有
调用函数: 1.1 `$ V9 p2 i; [# C8 e( k" V7 P6 ]9 I# g
备注: 1.本函数仅在主函数中调用一次即可。0 I$ B% @% _/ t* c+ ^6 D
==========================================================================================================*/
void CommI2cInit (void)% C; B! B6 m* O2 z* y
{
CommI2cDataStru *pI2c; // 指向I2C总线通讯收发数据处理变量的指针( a% Z0 O/ E, I0 j7 f6 Y- j# I3 i
pI2c = &gstvI2c; // 取I2C总线通讯收发数据处理变量的首个地址
#ifdef PreCommI2cUseSingleMasterMode // “I2C总线工作于单主机方式”的条件编译8 o4 P1 Y( F2 l
/*------------------------------------------------------------------------------------------------
“I2C总线工作于主机方式”的初始化操作。8 N. @. T5 `, L
------------------------------------------------------------------------------------------------*/3 m1 }% \. N3 N) D( F1 D5 g
#if (defined(_IAR_EW_AVR_))6 K* G3 b( R, L+ g/ d$ ]
TWBR = cI2c0BrBr; // 置 TWI 比特率寄存器的数值4 A& l* o: r0 O9 ]/ S+ e1 U
TWSR = cI2c0BrSr; // 置 TWI 预分频位(TWPS)的数值
TWDR = 0xFF; // 默认数据字节内容 = SDA 释放
TWCR = (1<<TWEN)| // 使能TWI接口为I2C总线和释放TWI引脚' e- b ?3 Z1 U1 {9 v2 n
(0<<TWIE)|(0<<TWINT)| // 禁止TWI中断
(0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| // 没有请求信号
(0<<TWWC); // 清零TWI写冲突标志* f7 F; c) H. P( t" @
#elif ((defined(_IAR_EW_MCS51_)) || (defined(_KEIL_UV_MCS51_)))
#elif ( defined(_IAR_EW_STM8_) \
|| defined(_COSMIC_STM8_) ) // “......_STM8 编译器”条件编译开始
#ifdef _USE_STM8_FWLIB_ // “使用 STM8 FWLib 的库文件”条件编译开始
I2C_Cmd(ENABLE);( ?/ A* K' p" B4 w
I2C_Init(cI2c0BaudRate, cI2cOwnAddr, I2C_DUTYCYCLE_2, I2C_ACK_CURR, I2C_ADDMODE_7BIT, (cSysCpuClk / 1000000UL));% m3 Y8 [) Z$ g( b
I2C_ITConfig((I2C_IT_ERR | I2C_IT_EVT | I2C_IT_BUF), ENABLE);
#else // “_USE_STM8_FWLIB_”6 G' `, @# I9 t2 v8 k# n5 r
I2cDeInit( );
I2CFR = (u8)( (cSysCpuClk / 1000000UL)); // 置I2C频率寄存器的数值
I2CCCRH = (u8)( ((0<<i2cDUTY )| // 快速模式下的占空比=0:Tlow/Thigh=2/1,1:Tlow/Thigh=16/9。5 b1 Q# H# i0 e1 N
(0<<i2cSFM )| // 标准/快速模式选择位=标准模式
cI2c0BrCcrS11H )); // 置I2C比特率高位寄存器的数值,注:只有禁用I2C模块时,才能设置。1 a9 m# u- H, s9 n$ Q
I2CCCRL = (u8)( cI2c0BrCcrS11L); // 置I2C比特率低位寄存器的数值2 t4 p& Y' l1 j
// I2CCR1 &= (u8)(~((1<<i2cEN ) )); // 禁止I2C模块,用以设置 I2CTRISER。注:只有禁用I2C模块时,才能设置。
I2CTRISER = (u8)( ((cSysCpuClk / 1000000UL) + 1));// 标准模式:最大上升时间=1000ns,注:只有禁用I2C模块时,才能设置。& T# `( j# E' `/ U8 U# _! ^; R7 P) ]
// I2CTRISER = (u8)( ((((cSysCpuClk / 1000000UL) * 3UL) / 10UL) + 1));
// // 快速模式:最大上升时间=300ns,注:只有禁用I2C模块时,才能设置。! D1 b" W9 T- c$ B* ~
I2CDR = (u8)( 0xFF); // 默认数据字节内容 = SDA 释放: q* l8 [6 d+ y/ |6 C; P! i0 M
I2CCR1 = (u8)( ((1<<i2cEN )|(0<<i2cGCEN )| // 使能I2C模块,禁止广播呼叫
(0<<i2cCKTEN) )); // 禁止时钟延展: e7 J p3 `" C" K# W3 v% H
I2CCR2 = (u8)( ((0<<i2cSTART)| // 禁止 START 信号(起始条件)9 C2 Z2 Z2 l; u8 _: Z! }9 t
(0<<i2cSTOP )|(1<<i2cACKEN)| // 禁止 STOP 信号(停止条件),使能 ACK 应答
(0<<i2cPOS )|(0<<i2cRST ) ));// 应答的位置=当前字节,I2C模块不处于复位状态
I2CARH = (u8)( ((1<<i2cCFG )| // 地址模式配置=软件必须配置该位(只能配置成 1)
(0<<i2cMODE )| // 寻址模式=7 位从地址(对 10 位地址不响应)
cI2cOwnAddrH )); // 置I2C(从机)地址高位寄存器的数值
// I2CARL = (u8)( cI2cOwnAddrL); // 置I2C(从机)地址低位寄存器的数值
I2CICR = (u8)( ((0<<i2cERRIE)|(0<<i2cEVTIE)| // 禁止错误中断,禁止事件中断
(0<<i2cBUFIE) )); // 禁止缓冲中断
#endif // “_USE_STM8_FWLIB_”% e( z" e9 [ Z( _, d
#else9 ]! h4 z. k1 H6 N( O: i( w( P
#error 错误:$ i* _- C$ J1 [" _
#endif // “PreMcuIs__......”: U9 _6 y- U) c& \' N8 Z7 \4 q
#endif // “PreCommI2cUseSingleMasterMode”
#if defined(PreCommI2cUseSingleSlaveMode) || defined(PreCommI2cUseMultiSlaveMode)
// “I2C总线工作于从机方式”的条件编译& m3 ~2 N$ x! V4 }! v$ w
/*------------------------------------------------------------------------------------------------
“I2C总线工作于从机方式”的初始化操作。8 B3 |4 G% ^3 a( {
------------------------------------------------------------------------------------------------*/9 `9 P9 @' }3 X
#if (defined(_IAR_EW_AVR_))% k% H# P3 x2 J* U* c. Z
TWAR = cI2c0OwnAddr; // 置本器件的从机地址值. P( ^' D5 ^6 J; _- g" t |2 y
#if (defined(PreMcuIs__AtMega48) || defined(PreMcuIs__AtMega48A) || \
defined(PreMcuIs__AtMega88) || defined(PreMcuIs__AtMega88A) || \- M& D- U& D, ^; r5 O8 @
defined(PreMcuIs__AtMega168) || defined(PreMcuIs__AtMega168A) )
TWAMR = cI2c0OwnAddrMask; // 置本器件的从机地址屏蔽位值8 y7 r6 C' j6 W* R# h( V6 u: A5 o, e
#endif+ e4 ?' f- A. W8 s3 P2 E
TWDR = 0xFF; // 默认数据字节内容 = SDA 释放
TWCR = (1<<TWEN)| // 使能TWI接口为I2C总线和释放TWI引脚
(0<<TWIE)|(0<<TWINT)| // 禁止TWI中断- N% G2 Y% T6 w4 O6 y! \
(0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)| // 目前对任何请求不发出 ACK 应答脉冲 i0 e# U' n1 Q! P) k" J
(0<<TWWC); // 清零TWI写冲突标志
#elif ((defined(_IAR_EW_MCS51_)) || (defined(_KEIL_UV_MCS51_)))
#elif ( defined(_IAR_EW_STM8_) \
|| defined(_COSMIC_STM8_) ) // “......_STM8 编译器”条件编译开始
#ifdef _USE_STM8_FWLIB_ // “使用 STM8 FWLib 的库文件”条件编译开始
I2C_Cmd(ENABLE);
I2C_Init(cI2c0BaudRate, cI2cOwnAddr, I2C_DUTYCYCLE_2, I2C_ACK_CURR, I2C_ADDMODE_7BIT, (cSysCpuClk / 1000000UL));
I2C_ITConfig((I2C_IT_ERR | I2C_IT_EVT | I2C_IT_BUF), ENABLE);
#else // “_USE_STM8_FWLIB_”
I2cDeInit( );
I2CFR = (u8)( (cSysCpuClk / 1000000UL)); // 置I2C频率寄存器的数值
I2CCCRH = (u8)( ((0<<i2cDUTY )| // 快速模式下的占空比=0:Tlow/Thigh=2/1,1:Tlow/Thigh=16/9。: P: A. X* v0 Y! y, M' l. l. `
(0<<i2cSFM )| // 标准/快速模式选择位=标准模式
cI2c0BrCcrS11H )); // 置I2C比特率高位寄存器的数值
I2CCCRL = (u8)( cI2c0BrCcrS11L); // 置I2C比特率低位寄存器的数值,注:只有禁用I2C模块时,才能设置。
// I2CCR1 &= (u8)(~((1<<i2cEN ) )); // 禁止I2C模块,用以设置 I2CTRISER。注:只有禁用I2C模块时,才能设置。
I2CTRISER = (u8)( ((cSysCpuClk / 1000000UL) + 1));// 标准模式:最大上升时间=1000ns,注:只有禁用I2C模块时,才能设置。" q, s% m4 }* s- o) \
// I2CTRISER = (u8)( ((((cSysCpuClk / 1000000UL) * 3UL) / 10UL) + 1));
// // 快速模式:最大上升时间=300ns,注:只有禁用I2C模块时,才能设置。
I2CDR = (u8)( 0xFF); // 默认数据字节内容 = SDA 释放
I2CCR1 = (u8)( ((1<<i2cEN )|(1<<i2cGCEN )| // 使能I2C模块,使能广播呼叫
(0<<i2cCKTEN) )); // 禁止时钟延展
I2CCR2 = (u8)( ((0<<i2cSTART)| // 禁止 START 信号(起始条件)9 @6 e' e6 h& C4 J
(0<<i2cSTOP )|(1<<i2cACKEN)| // 禁止 STOP 信号(停止条件),使能 ACK 应答+ v5 q$ ^: k9 N% r9 {
(0<<i2cPOS )|(0<<i2cRST ) ));// 应答的位置=当前字节,I2C模块不处于复位状态* q# F' e% H4 ~1 @
I2CARH = (u8)( ((1<<i2cCFG )| // 地址模式配置=软件必须配置该位(只能配置成 1)1 J& \% C3 u4 _1 a! l! ~3 u
(0<<i2cMODE )| // 寻址模式=7 位从地址(对 10 位地址不响应)# W+ @% } @& P) G- K
cI2cOwnAddrH )); // 置I2C(从机)地址高位寄存器的数值
I2CARL = (u8)( cI2cOwnAddrL); // 置I2C(从机)地址低位寄存器的数值. q+ D0 |; I7 T# `! G
I2CICR = (u8)( ((0<<i2cERRIE)|(0<<i2cEVTIE)| // 禁止错误中断,禁止事件中断+ B4 y" Q: q F( O7 p# Z
(0<<i2cBUFIE) )); // 禁止缓冲中断
#endif // “_USE_STM8_FWLIB_”- Y, z5 ^% d; }/ r: v5 ]6 g
#else z% i5 d6 Y( f3 t; G6 H4 U7 y( V1 o
#error 错误:
#endif // “PreMcuIs__......”7 b4 n, j. n3 i6 I
#endif // “PreCommI2cUse......SlaveMode”
/*------------------------------------------------------------------------------------------------
初始化各个标志位) Y" D. a3 ]7 G2 r
------------------------------------------------------------------------------------------------*/
gbitI2c.Busy = CLEAR; // 清除“I2C总线〖忙〗”标志
gbitI2c.RxOk = CLEAR; // 清除“接收数据缓冲区己更新”标志
gbitI2c.RxOvf = CLEAR; // 清除“接收数据缓冲区溢出”标志
gbitI2c.Error = CLEAR; // 清除“通讯收发作业发生错误”标志
gbitI2c.Txd = CLEAR; // 清除“正在进行发送数据作业”标志
gbitI2c.Rxd = CLEAR; // 清除“正在进行接收数据作业”标志
gbitI2c.CallG = CLEAR; // 清除“响应广播地址”标志( d0 l; V9 D& }; F; Y
gbitI2c.OverS = CLEAR; // 清除“通讯任务时间超时”标志
#if defined(PreCommI2cUseSingleSlaveMode) || defined(PreCommI2cUseMultiSlaveMode)
gbitI2c.Txd = SET; // 置“正在进行发送数据作业”标志
#endif9 q4 |' N' p! z) C" G
+ Z0 _8 [! @- o3 B$ H0 F/ k# b
// 初始化I2C总线通讯收发数据处理变量0 S0 C; B. V0 j" ~; N+ ~
gucvI2cEvent = cI2cAllNoState; // 置I2C总线的状态码 = 没有相关的状态信息
MemClearSram(pI2c, sizeof(CommI2cDataStru));
}
$ x' P" \( o$ Z" B9 @3 P' N, Z' I
/*==========================================================================================================
函数名称: 查询I2C总线通讯发送数据作业工作状态 X5 F/ h( Q8 ?6 l- |5 `
函数功能: 本函数用于检查I2C总线通讯发送数据作业工作状态的函数。1 b2 D, h4 J& P! B. _+ R
函数入口参数:没有
函数返回值: I2C总线收发器的工作状态,0(空闲状态)。
调用函数: 1.7 l, W6 \6 y# a/ i9 R
备注: 1., r0 Z9 C; Q% \& p0 @
==========================================================================================================*/
unsigned char CommI2cBusBusy (void)' ]- a5 n9 o. R& x
{5 X9 G* ? l! y/ L- q
#if (defined(_IAR_EW_AVR_))
return (TWCR & (1<<TWIE)); // 如果TWI中断使能则收发器处于忙碌状态 $ O$ j C7 _5 _( W
#elif ((defined(_IAR_EW_MCS51_)) || (defined(_KEIL_UV_MCS51_)))5 z% j+ j: {4 V4 r1 q8 z
" E" p b }" E; E8 p; T
#elif ( defined(_IAR_EW_STM8_) \
|| defined(_COSMIC_STM8_) ) // “......_STM8 编译器”条件编译开始
#ifdef _USE_STM8_FWLIB_ // “使用 STM8 FWLib 的库文件”条件编译开始+ v; \1 E% x* T% F' ]
#else // “_USE_STM8_FWLIB_”
return (I2CSR3 & (1<<i2cBSY )); // 如果I2C总线忙则总线处于忙碌状态( n3 O6 H* X, F9 d6 ~- V" {4 O* ~
#endif // “_USE_STM8_FWLIB_”- }1 c0 s( p2 v, K/ o4 d, R, i
#else
#error 错误:: x4 \! w3 k! ^2 ^; ^+ E9 E" w
#endif // “PreMcuIs__......”
}
#elif ((defined(_IAR_EW_MCS51_)) || (defined(_KEIL_UV_MCS51_)))
1 m# f, A7 x* U
#elif ( defined(_IAR_EW_STM8_) \
|| defined(_COSMIC_STM8_) ) // “......_STM8 编译器”条件编译开始
{4 Q' ~# l7 ~; }3 d3 ?& ]
#if ((cI2cTxBufSize < 256) && (cI2cRxBufSize < 256))
register unsigned char cnt; // USART每次发送数据个数计数器变量
#else2 }6 i) X/ [7 u5 |* f9 X8 s
register unsigned int cnt;- G# j" T# J, X: p% J
#endif6 D* c/ A. O! k2 V
unsigned char temp; // 临时变量
CommI2cDataStru *pI2c; // 指向I2C总线通讯收发数据处理变量的指针: k# e: j+ b! w* E! ^3 v
8 s5 x+ {! M+ @# K' F
temp = I2CSR2; // 取I2C状态寄存器2值 I/ z& M% D: \8 A, w. g1 L
if (temp & cI2cEvtSr2Mask) // 判断已屏蔽非状态位的I2C状态寄存器2值?; j2 W' ~% p& c) d
{
pI2c = &gstvI2c; // 取I2C总线通讯收发数据处理变量的首个地址
switch (temp & cI2cEvtSr2Mask) // 判断已屏蔽非状态位的 TWI 状态寄存器状态位值?
{# Y5 l% t) n5 @& Z
case cI2cAllArbLost: // 仲裁失败【I2CSR2】
{' l: K9 K& f4 \' ]0 f& w
if (pI2c->ErrNum > cI2cErrorMax) // 检查通讯收发作业发生错误次数是否大于最大值?
{4 l5 G$ d5 u: E; Z$ J# }/ L
gbitI2c.Error = SET; // 置“通讯收发作业发生错误”标志! D) O% V' h: R. i. F3 ]. A
MFHI2cInnEventMem( ); // 存储I2C总线的事件和状态存储器值
MFHI2cIntEventOff( ); // 禁止I2C总线事件中断" n, ~/ W6 n" k$ L# f
MFHI2cBusNackStop( ); // 发出 STOP 信号(结束条件),没有 ACK 脉冲. a. _3 w/ i% p8 O6 T# I. S- M5 @2 u1 ^
}
else/ M% u. p, y* R' W- c
{$ W S" d8 [* _4 U4 R# y! O% Q" }4 D
pI2c->ErrNum++; // I2C总线的通讯收发作业发生错误次数值+1) P7 t3 a+ U, y9 ^6 ?) k& K
MFHI2cBusNackReps( ); // 发出 START 或重复的 START 信号(起始条件),没有 ACK 脉冲
}6 }! P5 y, x& D* S; e
break;
}
// case cI2cMasterNack: // SLA 已发送,接收到 NOT ACK;或者数据已发送/接收到数据,接收到 NOT ACK/NOT ACK 已返回【I2CSR2】
// case cI2cAllNoState: // 没有相关的状态信息; TWINT = "0"1 h: d3 M* N& A/ n+ I
// case cI2cAllBusError: // 由于非法的 START 信号或 STOP 信号引起的总线错误: V) b- ~* k( H" f1 l. O! g
default:+ |' _3 x7 ~4 a7 p4 d
{
gbitI2c.Error = SET; // 置“通讯收发作业发生错误”标志
MFHI2cInnEventMem( ); // 存储I2C总线的事件和状态存储器值2 e6 f! u5 ], M, U5 p
MFHI2cIntEventOff( ); // 禁止I2C总线事件中断
MFHI2cBusNackStop( ); // 发出 STOP 信号(结束条件),没有 ACK 脉冲
}
}2 f( o6 @+ t! L. d' f
}4 a# [" V. } q2 L5 v
else // I2C总线没有错误
{+ |; L: Z, U" _8 S# a r' p' g8 c4 D
// 通过先读 I2CSR1,然后再读 I2CSR3,来清0地址已被发送(主模式)/地址匹配(从模式)标志
temp = I2CSR1; // 取I2C状态寄存器1值
if ((I2CSR3 & (u8)( (1<<i2cRXTX ))) == (u8)( (1<<i2cRXTX )))
{ // 检查I2C总线是否为发送数据?
temp |= (u8)( BIT5); // 是,* X# H0 _) Z# |3 H: f8 F* q
}
pI2c = &gstvI2c; // 取I2C总线通讯收发数据处理变量的首个地址: w0 @/ G) Z$ I6 \7 ^. }+ ]1 p
switch (temp) // 判断I2C状态寄存器1+I2C状态寄存器3值?/ ?6 k) y9 h0 P, Y& z
{, a/ R! t" C0 Z
case cI2cAllStart: // START 信号已发送
case cI2cAllRepStart: // 重复 START 信号已发送
{& M1 C7 k* _$ P# m) T# I
pI2c->TxCnt = 0x00; // 每次发送数据计数器值清05 I& w0 ], Z' `# j/ M L+ S, X) g
}
case cI2cMtxAddrAck: // SLA+W 已发送,接收到 ACK4 h. g: G& M9 W1 }: M" j. r+ m4 F" P
{1 ?7 [: u: [8 c6 e: n: x# j) h$ l
// if (temp == cI2cMtxAddrAck)+ |! E/ u3 z% r, _/ m2 s
// {
MFHI2cIntEventOnn( ); // 使能缓冲中断8 E* q5 D$ p9 U9 z' B$ Y
// }
}
case cI2cMtxDataAck: // 数据已发送,接收到 ACK
case cI2cMtxDataAckWait: // 数据已发送,接收到 ACK,且没有写新的数据到 I2CDR# y- M% S8 P. L% v- s
{
cnt = pI2c->TxCnt; // 取I2C总线每次发送数据个数计数器变量 T5 Z! o; g5 g9 b$ ^& F
if (cnt >= pI2c->TxSize) // 检查是否己完成发送通讯任务的全部数据?; X# Z8 W& g3 {& C' k, T5 G
{ // 是,在最后一个字节之后发出 STOP 信号
if (gbitI2c.Rxd == SET) // 检查是否为读取I2C总线数据?
{
MFHI2cBusNackReps( ); // 发出重复 START 信号(重新开始条件),没有 ACK 脉冲8 _% P$ _% M: f+ V) b
pI2c->TxBuf[0] |= (u8)( BIT0); // 设置数据传送方向为接收数据
// 通过先读 I2CSR1,然后写入 I2CDR,来清0发送数据寄存器空,字节发送结束,起始位(主模式)标志8 G: @ g* D$ ^6 O
// MFHI2cIntEventClr( ); // 进入中断后,在中断句柄中,清0I2C总线事件中断标志位(在中断服务程序中)( Y2 X) F% R* M6 z) M0 f
I2CDR = pI2c->TxBuf[0]; // 从发送缓冲区中将发送数据写入到I2C总线* o+ h9 F; k$ \7 u, j) I
MFHI2cIntEventOff( ); // 禁止I2C总线事件中断% j+ n3 Z6 u. `5 U
MFHI2cIntEventOne( ); // 使能I2C总线事件中断、错误中断,禁止缓冲中断
}
else
{
gbitI2c.Busy = CLEAR; // 清除“I2C总线〖忙〗”标志! }: k* _8 P+ r6 E0 m
gbitI2c.OverS = CLEAR; // 清除“通讯任务时间超时”标志, h+ d* d# o: c5 ]& A5 t
MFHI2cIntEventOff( ); // 禁止I2C总线事件中断
MFHI2cBusNackStop( ); // 发出 STOP 信号(结束条件),没有 ACK 脉冲
}8 s8 S& o+ M" O- E3 w
}! W: u3 v7 |. h2 Q
else
{3 ^6 H! u! ]0 Q$ m+ w( e+ ?. L
// 通过先读 I2CSR1,然后写入 I2CDR,来清0发送数据寄存器空,字节发送结束,起始位(主模式)标志
// MFHI2cIntEventClr( ); // 进入中断后,在中断句柄中,清0I2C总线事件中断标志位(在中断服务程序中)* N) ?; C( u4 \. f* g5 L2 e
I2CDR = pI2c->TxBuf[cnt]; // 从发送缓冲区中将发送数据写入到I2C总线% g% F2 u: y% r3 U
cnt++; // I2C总线每次发送数据个数计数器+1
pI2c->TxCnt = cnt; // 返回I2C总线每次发送数据个数计数器值' x1 c* z& m1 e7 p
}
break;* E& U$ q0 B9 X( w6 }4 B
}; N; X, n% Y" N: |
case cI2cMrxDataAck: // 接收到数据,ACK 已返回6 f x( ?& i7 G' B
case cI2cMrxDataAckWait: // 接收到数据,ACK 已返回,且 I2CDR 中的数据没有被读取
{
cnt = pI2c->RxCnt; // 取I2C总线每次接收数据个数计数器变量
// 通过先读 I2CSR1,然后读取 I2CDR,来清0接收数据寄存器非空标志2 s1 d i$ e% p
// MFHI2cIntEventClr( ); // 进入中断后,在中断句柄中,清0I2C总线事件中断标志位(在中断服务程序中)7 i0 i+ t5 |# C9 }" u- g/ X o
pI2c->RxBuf[cnt] = I2CDR; // 复制I2C总线读取到的字节到接收数据缓冲区中
cnt++; // I2C总线每次接收数据个数计数器+1$ l* d1 e* P( R& P* a% [
pI2c->RxCnt = cnt; // 返回I2C总线每次接收数据个数计数器值$ C% j- Y& [* `
cnt = pI2c->RxCnt; // 取I2C总线每次接收数据个数计数器变量
if (cnt >= pI2c->RxSize) // 检查是否完成需要读取的数据长度?
{8 M; ~; V3 K+ X$ z
gbitI2c.Busy = CLEAR; // 清除“I2C总线〖忙〗”标志
gbitI2c.OverS = CLEAR; // 清除“通讯任务时间超时”标志
MFHI2cIntEventOff( ); // 禁止I2C总线事件中断
}
else if (cnt >= (pI2c->RxSize - 1)) // 在最后一个字节数据接收之后发出 NOT ACK% u8 O: X. U5 S9 O3 G
{
MFHI2cBusRdatNack( ); // 在接收之后发出 NOT ACK: l% o( g' {$ j1 k! n4 L: Y
MFHI2cBusNackStop( ); // 发出 STOP 信号(结束条件),没有 ACK 脉冲
}, H% ?& o6 I7 S% w$ F% ~" S1 f$ x
else3 U1 ^8 d9 \' j) H
{
MFHI2cBusRdataAck( ); // 在接收之后发出 ACK 脉冲
}
break;
}
case cI2cMrxAddrAck: // SLA+R 已发送,接收到 ACK3 H& M1 B& D5 W/ y- O3 O
{2 B; V* U4 m8 i' Y+ [9 O, D
MFHI2cBusRdataAck( ); // 在接收之后发出 ACK 脉冲
MFHI2cIntEventOnn( ); // 使能缓冲中断& a' t' n# z: E+ z4 l/ _
break;6 p* g' ]0 m9 a) S& Q; P: r
}
default:
{: \5 i" e2 {# a; h% [8 H3 G
gbitI2c.Error = SET; // 置“通讯收发作业发生错误”标志9 B( ^/ x3 M# }4 }3 j
MFHI2cInnEventMem( ); // 存储I2C总线的事件和状态存储器值
MFHI2cIntEventOff( ); // 禁止I2C总线事件中断9 @4 B# k# F- f K
MFHI2cBusNackStop( ); // 发出 STOP 信号(结束条件),没有 ACK 脉冲7 u& [+ E7 t- `! H, m! H1 ]
}
}
}# p. F9 a. \% }$ o8 J
9 G# k/ F/ E5 ]- s: v6 Y0 j5 N
(void)temp; // 用以阻止编译器产生[temp]未使用的警告消息
INTERRUPT_RETURN( );- e+ }: a& @+ Z" A$ a# B" r
}
#else' V+ D G0 P9 g" W' V. G
#error 错误:
#endif // “PreMcuIs__......”
& t( c' w2 w( J' }
# V7 @1 H6 h- o9 b# J
' Z" w7 a$ p# ^# n
& ^2 L4 y& @( i/ v+ C
#endif // “I2C总线工作于单主机方式”条件编译结束
各类字符化常数和硬件设计相关的输入/输出引脚定义
==========================================================================================================*/8 ?( ]4 L$ L: M! o* T0 z5 _3 n
#ifndef PreGlobalCompilerHardware // 非“全局统一的条件编译控制和硬件定义”条件编译! ]. g- s9 M, K, M0 ?
/*------------------------------------------------------------------------------------------------2 G3 U/ D, y, Z. j( R
I2C接口的输入/输出引脚定义:: J8 n* }( M5 w9 ?7 W# B% p# D
注:下列这些定义与硬件设计相关,如需改变控制的输入/输出引脚仅在此重新定义即可。
0--A,1--B,2--C,3--D,4--E,5--F,6--G,7--H,8--I,9--J。, P- t$ o4 `& ]3 q1 m/ E5 r" J
------------------------------------------------------------------------------------------------*/
#if (defined(PreMcuIs__AtMega64) || defined(PreMcuIs__AtMega64A) || \1 ]) n6 P" L. I9 k+ V9 M
defined(PreMcuIs__AtMega128) || defined(PreMcuIs__AtMega128A) || \" O8 Z3 e% r2 Y7 ]( T6 b; C& G% A Q
defined(PreMcuIs__AtMega162) || defined(PreMcuIs__AtMega162A) )
3 R9 x c5 D) S+ `3 d0 n8 Y8 I
#elif (defined(PreMcuIs__STM8S207R6) || defined(PreMcuIs__STM8S208R6) || \6 _& q$ @4 H" u' @/ n' g9 E% h9 r
defined(PreMcuIs__STM8S207R8) || defined(PreMcuIs__STM8S208R8) || \
defined(PreMcuIs__STM8S207RB) || defined(PreMcuIs__STM8S208RB) )
#ifdef _USE_STM8_FWLIB_ // “使用 STM8 FWLib 的库文件”条件编译开始
#define cI2c0Gpio GPIOE1 m9 t8 X7 O f8 c+ g- s& m8 R' E/ u
#define cI2c0SclPinBit GPIO_PIN_1) G4 \3 ]/ ^3 K+ Q
#define cI2c0SdaPinBit GPIO_PIN_2# H7 x5 \: o+ j6 M6 M& M# W7 I* k4 t
#else // “_USE_STM8_FWLIB_”
#define cDdrI2c0Scl P4DDR // I2C0时钟引脚的数据方向寄存器
#define cPurI2c0Scl P4PUR // I2C0时钟引脚的上拉电阻寄存器(控制寄存器1)
#define cCirI2c0Scl P4CIR // I2C0时钟引脚的控制中断寄存器(控制寄存器2)
#define cOdrI2c0Scl P4ODR // I2C0时钟引脚的输出数据寄存器8 r: A6 r# P y" T: C$ f, |, p
#define cIdrI2c0Scl P4IDR // I2C0时钟引脚的输入数据寄存器: F% x9 Y. X. r$ i: K; p5 ^9 G
#define cPidI2c0Scl PI41 // I2C0时钟引脚的引脚输入2 L% ~# U4 W2 e+ f+ I0 Z( U
#define cPodI2c0Scl PO41 // I2C0时钟引脚的引脚输出7 e, P w6 M- l0 s
#define cBitI2c0Scl BIT1 // I2C0时钟引脚的引脚数字索引
#define cDdrI2c0Sda P4DDR // I2C0数据引脚的数据方向寄存器
#define cPurI2c0Sda P4PUR // I2C0数据引脚的上拉电阻寄存器(控制寄存器1)
#define cCirI2c0Sda P4CIR // I2C0数据引脚的控制中断寄存器(控制寄存器2)
#define cOdrI2c0Sda P4ODR // I2C0数据引脚的输出数据寄存器
#define cIdrI2c0Sda P4IDR // I2C0数据引脚的输入数据寄存器& C: j, A* {( C9 F* _4 ]% S
#define cPidI2c0Sda PI42 // I2C0数据引脚的引脚输入
#define cPodI2c0Sda PO42 // I2C0数据引脚的引脚输出
#define cBitI2c0Sda BIT2 // I2C0数据引脚的引脚数字索引- X+ `7 h% F3 C! S
#endif // “_USE_STM8_FWLIB_”, z: m+ J; ]) W( w/ w D+ Y
#else. o" U5 ~$ O! I" D/ [% T
#error 错误:没有定义I2C接口的输入引脚端口!!! x/ p; E* W% c' y
#endif // “PreMcuIs__......”& R( a: o! ?& ~+ [& U. T
/*------------------------------------------------------------------------------------------------/ \) m% @' `2 O9 W% ~1 z4 l$ X
I2C总线收发数据缓冲区大小字符化定义:3 a& R# l. _8 S1 G1 f F0 C
------------------------------------------------------------------------------------------------*/
#define cI2c0BufSize 0x22 // I2C总线接收和发送数据缓冲区大小7 R# ~% E( z1 M3 K# J
#endif // “PreGlobalCompilerHardware”! U* Y. a9 C8 H8 S' G8 L
#ifdef PreCommI2cUseMultiSlaveMode // “I2C总线工作于多从机方式”条件编译结束" r8 X. v O, }. o& g' v
#define cI2c0RxBufSize (cI2c0BufSize + 1) // I2C总线接收数据缓冲区大小, w: t1 {/ v7 k
#define cI2c0TxBufSize (cI2c0BufSize + 2) // I2C总线发送数据缓冲区大小 ^& j1 T1 v/ k6 P
#else+ |# U0 R4 H* b* _( g
#define cI2c0RxBufSize (cI2c0BufSize + 0) // I2C总线接收数据缓冲区大小9 g0 q- L6 _/ o/ ]2 U: Y
#define cI2c0TxBufSize (cI2c0BufSize + 1) // I2C总线发送数据缓冲区大小& p" B3 w5 R* ]2 y
#endif
#ifndef PreGlobalCompilerHardware // 非“全局统一的条件编译控制和硬件定义”条件编译( @6 h$ [9 q0 p+ ~' Y1 v
#define cI2cErrorMax 0x10 // I2C总线通讯收发作业最大发生错误次数值
#define cI2cOverSec 0x08 // 通讯任务超时时间的最大秒数值(8s)
" G l2 O+ a* f
#define cI2c0AddrNum 1 // I2C总线目标设备地址的字节数
#endif // “PreGlobalCompilerHardware”
#ifdef PreCommI2cUseSingleMasterMode // “I2C总线工作于单主机方式”的条件编译
/*------------------------------------------------------------------------------------------------4 z$ P7 d1 {% @0 M
TWI接口的I2C总线的比特率常数定义:
下面以单片机的时钟= 11.0592 MHz,I2C总线的比特率≈307K(Bit)为依据进行定义。
单片机的时钟; h* O9 E5 ^/ b7 T7 [
比特率计算公式 = ─────────────────1 p7 o0 W H- J. x8 S$ c
TWPS
16+2×(I2CTWI_BR_BR)×4
------------------------------------------------------------------------------------------------*/
#define cI2c0BaudRate 10000UL // I2C0波特率(bps)【标准模式】
#define cI2c0FsmMul 80UL // 快速模式/标准模式的倍数 \" |* \6 X5 b' `0 k7 Y& m
#if (defined(PreMcuIs__AtMega64) || defined(PreMcuIs__AtMega64A) || \
defined(PreMcuIs__AtMega128) || defined(PreMcuIs__AtMega128A) || \
defined(PreMcuIs__AtMega162) || defined(PreMcuIs__AtMega162A) )$ }) s3 ?- z+ X+ s/ D1 X4 W, U
#define cI2c0BrBr 0x0A // TWI 比特率寄存器值(TWI 在主机模式时,TWBR 值应该不小于10)
#define cI2c0BrSr 0x00 // TWI 状态寄存器中(TWI 预分频位(TWPS)的数值)8 `8 m( V; ~: c# y' Z/ m
#elif (defined(PreMcuIs__STM8S207R6) || defined(PreMcuIs__STM8S208R6) || \
defined(PreMcuIs__STM8S207R8) || defined(PreMcuIs__STM8S208R8) || \
defined(PreMcuIs__STM8S207RB) || defined(PreMcuIs__STM8S208RB) )+ c C7 q% f' ?" E0 D9 H
// 标准模式:占空比=Tlow/Thigh=1/1
#define cI2c0BrCcrS11 (cSysCpuClk / ( 2UL * cI2c0BaudRate))9 C0 _/ X) N$ _7 @6 ]! d
#define cI2c0BrCcrS11L ((cI2c0BrCcrS11 ) & 0x00FF)! Z9 c2 r$ B2 y, G1 B M
#define cI2c0BrCcrS11H ((cI2c0BrCcrS11 >> 8) & 0x00FF)
// 快速模式:占空比=Tlow/Thigh=2/1
#define cI2c0BrCcrF21 (cSysCpuClk / ( 3UL * cI2c0BaudRate * cI2c0FsmMul))( K7 l4 n: L6 D: o
#define cI2c0BrCcrF21L ((cI2c0BrCcrF21 ) & 0x00FF)
#define cI2c0BrCcrF21H ((cI2c0BrCcrF21 >> 8) & 0x00FF)0 o3 d! l8 {/ U) W' ~
// 快速模式:占空比=Tlow/Thigh=16/90 w9 R, ?+ ]& p- T" [! s8 c* X) D
#define cI2c0BrCcrF69 (cSysCpuClk / (25UL * cI2c0BaudRate * cI2c0FsmMul))( f; S: v1 w( o8 A8 H. _% E% g
#define cI2c0BrCcrF69L ((cI2c0BrCcrF69 ) & 0x00FF): V+ y! L& Y7 K$ f1 j( Q
#define cI2c0BrCcrF69H ((cI2c0BrCcrF69 >> 8) & 0x00FF)
#else0 Q* j5 @6 Z! @( q6 R
#error 错误:没有定义I2C接口的比特率常数!!!
#endif // “PreMcuIs__......”, h5 O0 R% m' U- \ r$ \6 Y. r. m
#endif // “I2C总线工作于单主机方式”条件编译结束 v N0 F: V; g) o0 N7 u, \
/*------------------------------------------------------------------------------------------------7 k9 f* k, v) H! ~. g; T- r/ x3 p
I2C总线工作于从机方式时,本器件的从机地址定义:4 }; l8 o* y6 s
------------------------------------------------------------------------------------------------*/
#define cI2cOwnAddr ((u8)( 0x10)) // 本器件的从机自身地址值3 P( ^! ~$ x1 j- N) ?
#define cI2cOwnAddrL ((u8)( ((cI2cOwnAddr ) & 0x00FF)))) Q8 L9 i: A- Z
#define cI2cOwnAddrH ((u8)( ((cI2cOwnAddr >> 8) & 0x00FF)))3 H. L3 U0 G. d
#if (defined(PreMcuIs__AtMega48) || defined(PreMcuIs__AtMega48A) || \/ w7 M1 J3 B: X! C* _1 I1 e' R
defined(PreMcuIs__AtMega88) || defined(PreMcuIs__AtMega88A) || \
defined(PreMcuIs__AtMega168) || defined(PreMcuIs__AtMega168A) )% R: y1 G. w7 ^+ O/ y0 A5 V
#define cI2cOwnAddrMask ((u8)( 0x00)) // 本器件的从机自身地址屏蔽位值4 U7 M M5 l& V
#endif3 N( s! @. s: o# `" l/ i( N1 I7 I% W" B
#define cI2cGenAddr ((u8)( 0x00)) // 广播方式地址值
, K: U, I/ E V6 ]" F& i& O
/*------------------------------------------------------------------------------------------------# P; D2 N& W8 o
I2C总线的状态码定义:2 n) g$ Q* Q( F- n1 Y& k% w
------------------------------------------------------------------------------------------------*/1 T" U1 s0 z, X: A4 O% l, G* E
#if (defined(_IAR_EW_AVR_))
#define cI2cTwsrMask ((u8)( 0xF8)) // TWI 状态寄存器的状态位屏蔽值
6 l8 H& r( G0 a$ I
#ifdef PreCommI2cUseSingleMasterMode // “I2C总线工作于单主机方式”的条件编译
// I2C总线主机所有模式的通用状态码; K5 S" ~7 D- P1 o, |1 k
#define cI2cAllStart ((u8)( 0x08)) // START 信号已发送8 {6 i d( k( u) ~+ {' X# H% |- t
#define cI2cAllRepStart ((u8)( 0x10)) // 重复 START 信号已发送
#define cI2cAllArbLost ((u8)( 0x38)) // SLA+W 或数据的仲裁失败
// I2C总线主机发送模式的状态码7 l5 \: | d; I! c
#define cI2cMtxAddrAck ((u8)( 0x18)) // SLA+W 已发送,接收到 ACK5 I( K1 C: N4 I/ e) s. ]7 `: o
#define cI2cMtxAddrNack ((u8)( 0x20)) // SLA+W 已发送,接收到 NOT ACK
#define cI2cMtxDataAck ((u8)( 0x28)) // 数据已发送,接收到 ACK- n5 n9 t. J5 I/ o, P6 L' q
#define cI2cMtxDataNack ((u8)( 0x30)) // 数据已发送,接收到 NOT ACK
// I2C总线主机接收模式的状态码7 V5 |4 @* \3 E) \: `
#define cI2cMrxAddrAck ((u8)( 0x40)) // SLA+R 已发送,接收到 ACK- K( B, I3 X2 Y8 m4 O: U, t/ l
#define cI2cMrxAddrNack ((u8)( 0x48)) // SLA+R 已发送,接收到 NOT ACK1 b( g/ ^! ]( e6 h
#define cI2cMrxDataAck ((u8)( 0x50)) // 接收到数据,ACK 已返回
#define cI2cMrxDataNack ((u8)( 0x58)) // 接收到数据,NOT ACK 已返回
#endif // “PreCommI2cUseSingleMasterMode”6 F0 l* ~- C2 [7 y2 N: h
* Z( E# v- K0 D4 [+ n* {
#if defined(PreCommI2cUseSingleSlaveMode) || defined(PreCommI2cUseMultiSlaveMode)3 ^4 e; R7 Q: b' N0 ]- ?
// “I2C总线工作于从机方式”的条件编译5 ^& U+ _4 c0 _: r
// I2C总线从机发送模式的状态码/ U* V0 y) h# o" z _- ]( c! b1 r
#define cI2cStxAddrAck ((u8)( 0xA8)) // 自己的 SLA+R 已经被接收,ACK 已返回! t$ s4 ?4 H* }- |7 y; |
#define cI2cStxAddrAckLost ((u8)( 0xB0)) // SLA+R/W 作为主机的仲裁失败;自己的 SLA+R 已经被接收,ACK 已返回4 S+ `/ a& c+ R: p0 \5 c$ [1 k
#define cI2cStxDataAck ((u8)( 0xB8)) // TWDR 里的数据字节已经发送,接收到 ACK, @ Q; i$ W, H
#define cI2cStxDataNack ((u8)( 0xC0)) // TWDR 里的数据字节已经发送,接收到 NOT ACK
#define cI2cStxDataAckLast ((u8)( 0xC8)) // TWDR 的最后一字节数据已经发送(TWEA = "0"),接收到 ACK
- D* i& D2 [( t( c; ]; ~$ j
#define cI2cStxAddrAck ((u8)( 0xA8)) // 自己的 SLA+R 已经被接收,ACK 已返回
#define cI2cStxAddrAckLost ((u8)( 0xB0)) // SLA+R/W 作为主机的仲裁失败;自己的 SLA+R 已经被接收,ACK 已返回, J8 f' Z# F5 [5 L% P2 H/ N
#define cI2cStxDataAck ((u8)( 0xB8)) // TWDR 里的数据字节已经发送,接收到 ACK! e* l/ \, j2 Y
#define cI2cStxDataNack ((u8)( 0xC0)) // TWDR 里的数据字节已经发送,接收到 NOT ACK# ]4 u& `0 u( I! ^1 O. J
#define cI2cStxDataAckLast ((u8)( 0xC8)) // TWDR 的最后一字节数据已经发送(TWEA = "0"),接收到 ACK9 w9 o$ J- w' e
// I2C总线从机接收模式的状态码
#define IcI2cSrxAddrAck ((u8)( 0x60)) // 自己的 SLA+W 已经被接收,ACK 已返回. e: o' n- F5 }& Z& K) s n4 g
#define cI2cSrxAddrAckLost ((u8)( 0x68)) // SLA+R/W 作为主机的仲裁失败;自己的 SLA+W 已经被接收,ACK 已返回
#define cI2cSrxGenaAck ((u8)( 0x70)) // 接收到广播地址,ACK 已返回! ]* p& _" X2 {$ j8 w8 @1 b- Q. C
#define cI2cSrxGenaAckLost ((u8)( 0x78)) // SLA+R/W 作为主机的仲裁失败;接收到广播地址,ACK 已返回4 B: u# q" `) _4 L
#define cI2cSrxDataAck ((u8)( 0x80)) // 以前以自己的 SLA+W 被寻址;数据已经被接收,ACK 已返回
#define cI2cSrxDataNack ((u8)( 0x88)) // 以前以自己的 SLA+W 被寻址;数据已经被接收,NOT ACK 已返回
#define cI2cSrxGendAck ((u8)( 0x90)) // 以前以广播方式被寻址;数据已经被接收,ACK 已返回* p& [/ l& |; g5 @+ R; @4 g
#define cI2cSrxGendNack ((u8)( 0x98)) // 以前以广播方式被寻址;数据已经被接收,NOT ACK 已返回! c1 D- ]1 ?; }; _! r% I
#define cI2cSrxStopRestart ((u8)( 0xA0)) // 在以从机方式工作时接收到 STOP 信号或重复 START 信号7 w0 v$ C5 v; l' ~; o! q& g
#endif // “PreCommI2cUse......SlaveMode”
! c: N6 J+ P" W4 ^0 z) }
// I2C总线其它的状态码
#define cI2cAllNoState ((u8)( 0xF8)) // 没有相关的状态信息; TWINT = "0"
#define cI2cAllBusError ((u8)( 0x00)) // 由于非法的 START 信号或 STOP 信号引起的总线错误
#elif ((defined(_IAR_EW_MCS51_)) || (defined(_KEIL_UV_MCS51_))), Y* I4 L! r0 \6 s1 T
#elif ( defined(_IAR_EW_STM8_) \
|| defined(_COSMIC_STM8_) ) // “......_STM8 编译器”条件编译开始
#define cI2cEventMask ((u8)( b11111111)) // I2C总线状态寄存器的状态位屏蔽值【I2CSR3(i2cRXTX) | I2CSR1】% M/ H4 m' s9 G2 I* p0 ]6 ]
#define cI2cEvtSr1Mask ((u8)( b11011111)) // I2C状态寄存器1的状态位屏蔽值3 Y9 ?" w9 o' M/ B* l% z4 q; O9 W* H
#define cI2cEvtSr2Mask ((u8)( b00101111)) // I2C状态寄存器2的状态位屏蔽值: _+ A+ e# Q9 c
#ifdef PreCommI2cUseSingleMasterMode // “I2C总线工作于单主机方式”的条件编译
// I2C总线主机所有模式的通用状态码
#define cI2cAllStart ((u8)( b00000001)) // START 信号已发送【I2CSR1】
#define cI2cAllRepStart ((u8)( b00000101)) // 重复 START 信号已发送【I2CSR1】. t" e5 Y! w7 M( B4 @
#define cI2cAllArbLost ((u8)( b00000010)) // 仲裁失败【I2CSR2】 f x9 l9 O2 w5 P9 t, O. a
8 w5 a, x# |+ [9 E) i
// I2C总线主机发送模式的状态码
#define cI2cMtxAddrAck ((u8)( b10100010)) // SLA+W 已发送,接收到 ACK【I2CSR1】, ~5 Y, t& M' g) |" R
#define cI2cMtxAddrNack ((u8)( b10100010)) // SLA+W 已发送,接收到 NOT ACK ^8 J5 r5 y$ `0 [4 ]
#define cI2cMtxDataAck ((u8)( b10100000)) // 数据已发送,接收到 ACK(只有当收到 ACK ,i2cDRE 才会置 1)【I2CSR1】
#define cI2cMtxDataAckWait ((u8)( b10100100)) // 数据已发送,接收到 ACK,且没有写入新的数据到 I2CDR 【I2CSR1】
#define cI2cMtxDataNack ((u8)( b10100000)) // 数据已发送,接收到 NOT ACK) n2 G+ q# e9 Z% N, [+ M( G) w
// I2C总线主机接收模式的状态码& P P! k0 Q5 H& O+ h0 J) Z- O
#define cI2cMrxAddrAck ((u8)( b00000010)) // SLA+R 已发送,接收到 ACK【I2CSR1】6 t" U; Z) p8 @2 e
#define cI2cMrxAddrNack ((u8)( b00000010)) // SLA+R 已发送,接收到 NOT ACK3 j+ S) O0 z/ y4 t- p6 b
#define cI2cMrxDataAck ((u8)( b01000000)) // 接收到数据,ACK 已返回【I2CSR1】
#define cI2cMrxDataAckWait ((u8)( b01000100)) // 接收到数据,ACK 已返回,且 I2CDR 中的数据没有被读取【I2CSR1】
#define cI2cMrxDataNack ((u8)( b01000000)) // 接收到数据,NOT ACK 已返回
0 p& l+ @8 o: _7 J. o
#define cI2cMasterNack ((u8)( b00000100)) // SLA 已发送,接收到 NOT ACK;或者数据已发送/接收到数据,接收到 NOT ACK/NOT ACK 已返回【I2CSR2】/ m# \+ R& _, V7 t; _
#endif // “PreCommI2cUseSingleMasterMode”
#if defined(PreCommI2cUseSingleSlaveMode) || defined(PreCommI2cUseMultiSlaveMode)5 j0 \. w$ f& H) { Z) ]! t
// “I2C总线工作于从机方式”的条件编译: V3 q# B0 Q! t( C* u
// I2C总线从机发送模式的状态码
#define cI2cStxAddrAck ((u16)( 0xA8)) // 自己的 SLA+R 已经被接收,ACK 已返回
#define cI2cStxAddrAckLost ((u16)( 0xB0)) // SLA+R/W 作为主机的仲裁失败;自己的 SLA+R 已经被接收,ACK 已返回
#define cI2cStxDataAck ((u16)( 0xB8)) // TWDR 里的数据字节已经发送,接收到 ACK
#define cI2cStxDataNack ((u16)( 0xC0)) // TWDR 里的数据字节已经发送,接收到 NOT ACK
#define cI2cStxDataAckLast ((u16)( 0xC8)) // TWDR 的最后一字节数据已经发送(TWEA = "0"),接收到 ACK
#define cI2cStxAddrAck ((u16)( 0xA8)) // 自己的 SLA+R 已经被接收,ACK 已返回) u9 J- L) m$ C/ Z& g+ k* _) ?
#define cI2cStxAddrAckLost ((u16)( 0xB0)) // SLA+R/W 作为主机的仲裁失败;自己的 SLA+R 已经被接收,ACK 已返回3 X* Y3 ]6 }# q* w# E
#define cI2cStxDataAck ((u16)( 0xB8)) // TWDR 里的数据字节已经发送,接收到 ACK: U( P. [$ \# W' |
#define cI2cStxDataNack ((u16)( 0xC0)) // TWDR 里的数据字节已经发送,接收到 NOT ACK
#define cI2cStxDataAckLast ((u16)( 0xC8)) // TWDR 的最后一字节数据已经发送(TWEA = "0"),接收到 ACK. l9 y( @, [8 [9 j
// I2C总线从机接收模式的状态码! U- C7 A+ \2 h* F l9 Y: X
#define IcI2cSrxAddrAck ((u16)( 0x60)) // 自己的 SLA+W 已经被接收,ACK 已返回. y n7 C1 n; A5 H
#define cI2cSrxAddrAckLost ((u16)( 0x68)) // SLA+R/W 作为主机的仲裁失败;自己的 SLA+W 已经被接收,ACK 已返回- Y( M# `" [/ y1 D' p
#define cI2cSrxGenaAck ((u16)( 0x70)) // 接收到广播地址,ACK 已返回
#define cI2cSrxGenaAckLost ((u16)( 0x78)) // SLA+R/W 作为主机的仲裁失败;接收到广播地址,ACK 已返回
#define cI2cSrxDataAck ((u16)( 0x80)) // 以前以自己的 SLA+W 被寻址;数据已经被接收,ACK 已返回
#define cI2cSrxDataNack ((u16)( 0x88)) // 以前以自己的 SLA+W 被寻址;数据已经被接收,NOT ACK 已返回 s( z5 f- D f n r& x. `- X* w
#define cI2cSrxGendAck ((u16)( 0x90)) // 以前以广播方式被寻址;数据已经被接收,ACK 已返回
#define cI2cSrxGendNack ((u16)( 0x98)) // 以前以广播方式被寻址;数据已经被接收,NOT ACK 已返回+ r3 w$ J. R F! f5 @+ o# ]; n
#define cI2cSrxStopRestart ((u16)( 0xA0)) // 在以从机方式工作时接收到 STOP 信号或重复 START 信号! @! K7 W w. a. i; [8 I* u$ C
#endif // “PreCommI2cUse......SlaveMode”
( X' S8 r/ e0 |2 }1 S
// I2C总线其它的状态码) l( a6 ?6 ^9 }
#define cI2cAllNoState ((u8)( b00000000)) // 没有相关的状态信息; TWINT = "0"3 X9 J0 F2 x( Y9 u6 u" x+ [
#define cI2cAllBusError ((u8)( b00000001)) // 由于非法的 START 信号或 STOP 信号引起的总线错误【I2CSR2】
#else
#error 错误:2 |# m" L' O# I0 J) R' h8 e# y3 F0 i
#endif // “PreMcuIs__......”
8 n! G/ \0 f- j$ t
" Y- r6 w/ U+ _$ f+ {) R
- l6 p' f1 q+ A) {, @+ w7 I+ M O
/*==========================================================================================================$ ?7 Y$ p, a6 a) C
全局变量数据结构类型定义
==========================================================================================================*/
/*------------------------------------------------------------------------------------------------
I2C总线通讯处理消息任务标志的全局数据结构类型定义:8 w4 s* i) K: P
------------------------------------------------------------------------------------------------*/- H2 R$ v2 W2 ^3 p7 Y; ]' U
#define CommI2c0FlagDefaults \
{ CLEAR ,\5 |: P/ J7 _* t/ c9 _! q
CLEAR ,\6 g) ~0 N, s! @) Z% g8 b* V. t2 ]" w
CLEAR ,\7 E+ @: K& x @9 C, U5 s
CLEAR ,\' M( ]9 y) o- y p( T4 x* e
CLEAR ,\ G. [; c K/ y0 b8 Y0 F
CLEAR ,\" A/ r9 j0 I- ?- W9 l/ q5 `7 s+ c
CLEAR ,\
0 } // I2C0消息任务标志变量的默认初始化值5 B* }& p7 x! H x. \
typedef struct3 N; Y" a* V% g' t
{
unsigned char Busy : 1; // I2C总线〖忙〗# h; _$ E l. ^
unsigned char RxOk : 1; // 接收数据缓冲区己更新) r; [' W% _. y/ ` C
unsigned char RxOvf : 1; // 接收数据缓冲区溢出/ j! d% k4 ^* I" d- |" N& \: Z7 T
unsigned char Error : 1; // 通讯收发作业发生错误
unsigned char Txd : 1; // 正在进行发送数据作业2 F* }$ P: j8 S& S7 o' e$ U
unsigned char Rxd : 1; // 正在进行接收数据作业4 O2 o+ M9 {4 Q! s. O
unsigned char CallG : 1; // 响应广播地址9 F/ q: ?8 V! B4 \1 r. s7 S7 J2 O
unsigned char OverS : 1; // 通讯任务时间超时
} CommI2c0FlagBits;$ [3 S5 S1 \0 r0 X' l
/*------------------------------------------------------------------------------------------------& J4 L9 P f( d/ X7 p
I2C通讯收发数据处理的全局数据结构类型定义:# Q ?( R5 R! {+ h# M8 T
------------------------------------------------------------------------------------------------*/
typedef struct
{ \# Q4 h7 c- f# v$ l6 Y
unsigned char TxSize ; // 每次发送数据个数大小
unsigned char TxCnt ; // 每次发送数据个数计数器3 X7 h. F/ X; {9 \: `
// unsigned char Object ; // 每次发送数据的目标设备地址6 Q3 @6 }1 ], j1 C% v1 R k6 f
// const unsigned char *pTxData ; // 每次发送数据的地址指针; i9 M* T4 o) S/ }
unsigned char TxBuf[cI2c0RxBufSize] ; // 每次发送数据缓冲区
; D7 @, e) r1 w' Y
unsigned char RxSize ; // 每次接收数据个数大小
unsigned char RxCnt ; // 每次接收数据个数计数器
unsigned char RxBuf[cI2c0TxBufSize] ; // 每次接收数据缓冲区- w# p4 O$ p: P: V$ A) Y; z8 _
" W; B: A. L5 O- n% Z
unsigned char OverS ; // 通讯任务时间超时的计时秒变量
unsigned char Cycle ; // 重新启动通讯收发作业圈数. c |2 S5 b9 @0 q2 a! |; m
#ifdef PreCommI2cUseSingleMasterMode // “I2C总线工作于单主机方式”的条件编译
unsigned char ErrNum ; // 通讯收发作业发生错误次数
#endif // “PreCommI2cUseSingleMasterMode”
} CommI2c0DataStru;: g+ r1 X4 F R
; ~/ y. [0 s1 V4 Z
! [% K1 V1 E/ i- D2 R" `2 o
/*==========================================================================================================
全局常量声明2 X; p u0 [2 v1 L, d; I
==========================================================================================================*/; ~/ ]7 a3 Z, t
* @, {8 }" d! d9 _5 X
4 N7 e7 k+ l8 I% X. R* E S
/*==========================================================================================================
全局变量声明
==========================================================================================================*/! Q3 h; K" y* w9 P. }
/*------------------------------------------------------------------------------------------------3 u5 j" y% k1 f) l4 _$ u. ]; t3 w6 ?
I2C总线通讯处理消息任务标志的全局变量声明:& I4 X3 m* \" H$ J0 e" s1 } G
------------------------------------------------------------------------------------------------*/
extern bdata CommI2c0FlagBits gbitI2c0;" O1 P7 @8 a& E- k
/*------------------------------------------------------------------------------------------------
I2C总线的事件和状态码的全局变量声明:
------------------------------------------------------------------------------------------------*/
extern data unsigned char gucvI2c0Event;
/*------------------------------------------------------------------------------------------------
I2C总线通讯收发数据处理的全局变量声明:5 S- @ y5 B' W% S1 x
------------------------------------------------------------------------------------------------*/
extern xdata CommI2c0DataStru gstvI2c0;3 W" M: Z4 b: g: a O$ ]
/*==========================================================================================================: m Q' G; o, Z! `$ _! c5 H
源程序文件中的函数原型声明0 `% E1 d `$ ~0 X
==========================================================================================================*/+ o9 r6 d& P# h
/*------------------------------------------------------------------------------------------------6 }4 C: |' o; }. b. J
函数名称: 两线串行总线接口TWI(I2C总线),使用中断方式接收和发送数据通信的初始化
函数功能: 本函数用于两线串行总线接口TWI(I2C总线),使用中断方式接收和发送数据通信的初始化操作。0 u a% B7 [* C( ?; H, m. J1 ]
函数入口参数:没有
函数返回值: 没有# ` t; W5 ?5 }$ d/ ]" z4 ^9 o
调用函数: 1." ~- l: k p3 k- q: I9 Z
备注: 1.本函数仅在主函数中调用一次即可。5 N, S' r. Q, z- v- {' x8 P
------------------------------------------------------------------------------------------------*/% y# N7 n4 B# L* m5 h+ N
void CommI2cInit(void);
/ a: m/ \! X5 \- T
/*------------------------------------------------------------------------------------------------8 i( M$ L1 k2 h1 ]/ b
函数名称: 查询I2C总线通讯发送数据作业工作状态+ G$ @ f0 j$ B. d. V/ s
函数功能: 本函数用于检查I2C总线通讯发送数据作业工作状态的函数。
函数入口参数:没有
函数返回值: I2C总线收发器的工作状态,0(空闲状态)。
调用函数: 1.
备注: 1.6 K3 ~4 ^* @) z! Q' ^( w
------------------------------------------------------------------------------------------------*/
unsigned char CommI2cBusBusy(void);1 Z( X6 y$ o2 `8 o7 c
: V: {" Q; `6 ?7 ^. B
/*------------------------------------------------------------------------------------------------ P2 i/ y& U* b* ~! A$ C4 d2 j
函数名称: 设置I2C总线为标准/快速模式和占空比2 [5 i1 f/ ]' u. y, ^+ }
函数功能: 本函数用于设置I2C总线为标准/快速模式和占空比操作的函数。
函数入口参数:Model ----------------- 标准/快速模式和占空比, ON(快速模式)/ OFF(标准模式)。
函数返回值: 没有
调用函数: 1.+ I/ r; r) {: F3 s$ r, l
备注: 1.+ V8 O+ ^' E7 ~4 k
------------------------------------------------------------------------------------------------*/- k) T/ m; z. @$ e* K9 Q7 t
void CommI2cBusModeDuty(unsigned char Model);
#ifdef PreCommI2cUseSingleMasterMode // “I2C总线工作于单主机方式”的条件编译
/*------------------------------------------------------------------------------------------------# f5 B% f- M# p9 [$ x+ P
: w9 }, O' @1 r6 u! g4 o
------------------------------------------------------------------------------------------------*/
#if cI2cTxBufSize < 256* P1 `+ K' O7 f! G
void CommI2cTxWithData(unsigned char Object, unsigned char *pData, unsigned char Length);# r" r/ r& a4 e6 C& _- t( Z
#else
void CommI2cTxWithData(unsigned char Object, unsigned char *pData, unsigned int Length);" Q ?, j* P, J: C
#endif+ |2 ^8 S+ _5 A$ g
3 Z0 r& ^/ O5 ^% U; V/ V
/*------------------------------------------------------------------------------------------------& }/ E! T$ ?+ p/ ] S6 U
------------------------------------------------------------------------------------------------*/
#if cI2cRxBufSize < 256. Z, l; |3 T3 R$ S) y U* y8 c9 d
void CommI2cRxWithData(unsigned char Object, unsigned char Length);
#else
void CommI2cRxWithData(unsigned char Object, unsigned int Length);
#endif
% E( X# w, |1 {! O2 P" o
/*------------------------------------------------------------------------------------------------
) p @- }- M5 _2 E" |3 x# N
------------------------------------------------------------------------------------------------*/$ b* z! n3 @/ h e- y
#if cI2cTxBufSize < 256
void CommI2cTxWithAddr(unsigned char Object, unsigned char *pAddr, unsigned char LenAddr, unsigned char *pData, unsigned char Length);7 O9 U- h) r' M0 v; m
#else
void CommI2cTxWithAddr(unsigned char Object, unsigned char *pAddr, unsigned char LenAddr, unsigned char *pData, unsigned int Length);7 {/ q# I, |; C$ ]
#endif
& Y g3 Q4 ]' @1 {6 ?4 X
/*------------------------------------------------------------------------------------------------1 @0 t( `3 J9 K7 z8 C
! {; t5 I( Q% |" e8 m& Y
------------------------------------------------------------------------------------------------*/" c6 K, s: g4 Q0 U
#if cI2cTxBufSize < 2563 g" |# S# I, D8 q( ^, a: Y% a6 Y; p
#if cI2cRxBufSize < 256
void CommI2cRxWithAddr(unsigned char Object, unsigned char *pAddr, unsigned char LenAddr, unsigned char Length); ?7 R5 V k, `$ T- _- N8 c) B
#else
void CommI2cRxWithAddr(unsigned char Object, unsigned char *pAddr, unsigned char LenAddr, unsigned int Length);' M! t/ h7 B! o! ?: O; B
#endif
#else
#if cI2cRxBufSize < 256
void CommI2cRxWithAddr(unsigned char Object, unsigned char *pAddr, unsigned int LenAddr, unsigned char Length);) r3 L. @+ A* G! J0 j
#else
void CommI2cRxWithAddr(unsigned char Object, unsigned char *pAddr, unsigned int LenAddr, unsigned int Length);
#endif0 C- y- d" Y" {
#endif
4 ]: {- S: C4 w# n1 u. a4 E# T, d
4 v4 Q5 g' _! X' e
5 A4 c' |) \( i% y- y0 W- e
% c: x5 H1 l ~$ b3 S. c, a
D3 h7 W2 t3 j* ^5 C
. }; Z2 g& a+ C0 P4 X" n6 H2 l
8 u4 T& @. S6 f' V9 H- l: G+ z2 ?0 R
, x0 \7 F- v5 X2 K2 q
5 y* e( r6 b3 R+ t5 u( C) r
; K4 e0 w. b' q" t8 k' A% j. K
" L7 i4 P( M; e% h2 W
8 \2 y( @ w$ b9 f& Y
/*------------------------------------------------------------------------------------------------+ _' C/ s/ A: A1 N8 h) ?
函数名称: I2C总线工作于单主机方式,使用中断进行接收和发送数据的中断服务程序。
函数功能: 本函数用于I2C总线工作于单主机方式,使用中断进行接收和发送数据的中断服务程序。
函数入口参数:gbitI2c.Rxd ------------- “正在进行接收数据作业”标志。
gstvI2c.TxSize ---------- 每次发送数据个数大小变量。# [. S; \2 D; ]9 c3 _
gstvI2c.TxCnt ----------- 每次发送数据个数计数器变量。
gstvI2c.TxBuf[] --------- 每次发送数据缓冲区数组变量。: ~. i/ G& J4 e7 _$ c1 g2 G
gstvI2c.RxSize ---------- 每次接收数据个数大小变量。/ J g9 k) ^* N, J& i
函数返回值: 没有4 [' x9 H1 u9 F
函数出口参数:gbitI2c.Busy ------------ I2C总线〖忙〗,出口值=清位(可能)。" X" Y. c7 Y, @# j1 A+ R
gbitI2c.Error ----------- 通讯收发作业发生错误,出口值=置位(可能)。! H8 b U9 v* q
gucvI2cEvent ------------ I2C总线的状态码变量,出口值= 不确定。
gstvI2c.TxCnt ----------- 每次发送数据个数计数器变量,出口值=gstvI2c.TxCnt。4 @8 D: Q- g: S# o+ a1 \
gstvI2c.ErrNum ---------- 通讯收发作业发生错误次数,出口值=不确定。
gstvI2c.RxCnt ----------- 每次接收数据个数计数器变量,出口值=gstvI2c.RxSize。& n/ b/ y; r& C5 i( N, y. m9 K
gstvI2c.RxBuf[] --------- 每次接收数据缓冲区变量,出口值=从I2C总线上接收到的数据。2 t0 Y/ q& ?( V2 k! d) U; v7 \0 [
调用函数: 1.% Z$ t; b" f1 o* s* u+ P4 G _/ _
备注: 1.注意:本函数需在I2C总线中断请求的中断向量函数中调用。5 X; m; n. L4 }3 B/ ]- ^; L, ~3 n
在调用之前需对该中断向量进行初始化,使能该中断。
2.全部完成通讯收发作业后,本函数将禁止I2C总线中断。
------------------------------------------------------------------------------------------------*/
#if ( defined(_IAR_EW_AVR_) \
|| defined(_IAR_EW_MCS51_) \
|| defined(_IAR_EW_STM8_) ): [* r8 y; `+ Y7 a) @8 X
#pragma vector = int_I2C_vector6 n. g3 r8 K, W- V2 F
INTERRUPT_KEY void CommI2c_isr(void);
#elif (defined(_KEIL_UV_MCS51_) )
#elif ( defined(_COSMIC_STM8_) )
INTERRUPT_KEY void CommI2c_isr(void);& l- }3 ~3 d7 z6 o
#else- W2 C0 D5 h' a" c& k: ?0 r
void CommI2cIsr(void);
#endif // “_IAR_EW_......”4 {& g1 B1 i$ O2 e
#endif // “I2C总线工作于单主机方式”条件编译结束
' [( L2 R2 G0 N- D0 f0 ^: l
6 o' z9 C6 L- e% w$ M- i
/ [) d( `. R3 |8 f' k2 K( `
h' r5 O8 ]4 X% y$ b' `
/*------------------------------------------------------------------------------------------------9 u" R5 I: u& R8 J6 C
函数功能: 本函数是I2C总线通信接收和发送作业不间断循环调用运行函数。
备注: ▲.注意:本函数为消息任务队列函数,要在主函数中不间断循环调用运行。4 ]# a. {- [( C+ f, z
▲.当I2C总线工作于从机方式时,I2C总线中断总是处于使能状态,以便
I2C总线处于随时接收数据状态。" @, _ A$ |- v
------------------------------------------------------------------------------------------------*/
void CommI2cCircleFunc(void);
, u- z' |1 ?, L+ U5 @( J
! _- D5 T# _* B) c! V. P8 f
# C. B# Z5 s$ m& T/ `* F0 {
#ifdef __cplusplus
} // extern "C" 链接指示符作用域结束
#endif
#endif // “_COMMI2C_H_”条件编译结束
/*: k( M. Y( U3 J' a6 N& q
************************************************************************************************************( G9 X4 f( u+ g- [$ P1 }" a
本头文件到此结束# X- f& j0 \7 j
************************************************************************************************************
*/( f( s3 Z0 Q/ O
/*$ K/ V4 ]: u# f4 v
************************************************************************************************************- t# T5 L3 Y- @+ v; b
版权所有 (C),2010-2012,磊元科技有限公司 |9 ^0 N5 e7 h& g& N3 H! t
--------------------------------------------------------------------------------------------------9 O, c6 |5 Z# |1 ?+ K
源程序文件名: CommI2c.c9 S4 m" m8 [ A0 E3 Q+ K
源程序名称: 两线串行总线接口TWI(I2C总线)的源程序文件* a5 W! y) m f: N3 j
程序版本: 1.0
程序功能:
本文件是两线串行总线接口TWI(I2C总线)使用中断方式接收和发送数据通信的源程序文件。
程序说明:
1.注意:使用源程序的I2C总线接收和发送数据的函数之前,必须使能全局中断。
2.注意:使用I2C总线作为主、从机进行通信,主机在接收数据时,须连续接收2次才可正确接收到/ M8 p5 [, I) y X: W; Q/ n" v7 w- g6 c
从机发送的数据;因为主机第一次接收数据的第1个数据为I2C总线上出现的最后一个字节。
但在与外围器件通信正常,只须接收1次即可。' V2 e5 F* q. a9 K, p7 v* y! Q
) K& t7 `' I! M8 x/ p
主要函数列表: / a8 i; F' k$ _9 L
▲.在主函数中运行的函数:
1.void Drv74595Init(void)
(串行输出端口初始化)
▲.芯片74HC595输出驱动函数:
1.void Drv74595Drive(const unsigned char *pParallel)6 N; r1 [2 J2 v& I
(串入并出芯片74HC595输出驱动)
2.void Drv74595Single(unsigned char Parallel, unsigned char Series)
(串入并出芯片74HC595单个驱动)& M: a$ w- ^0 L, s3 f3 T
链接子程序文件:
1.
+ g# T% u" W% D# N
编译工具软件: IAR Embedded Workbench for Atmel AVR 版本:5.20A 以上
0 w( `6 m/ ^9 m
************************************************************************************************************8 A! r( K! L- O- |
*/
* [8 u3 I2 X1 O& G& O
% h' q, v% Z' } |* r5 f
) A( O; B4 q( s6 p3 A0 a
/*==========================================================================================================
调试开关声明3 j H; q- o5 o. S. o
==========================================================================================================*/
//#define DEBUG
/*==========================================================================================================
本源程序包括的头文件" L2 e+ t! b2 A4 c, e8 v
建议:包含本项目的文件使用 #include "文件名.扩展名" ,2 M* b2 e$ j7 i N$ _: e2 g
包含系统库的文件使用 #include <文件名.扩展名> 。1 k- J! D0 N3 L/ w% C- U" u6 I
==========================================================================================================*/
#include "CommI2c.h" // 本源程序的头文件& k& e* U t+ j2 J
#include "DataMath.h" // 算术运算及数制转换等数学算法的头文件# N* Y ~2 B# J7 `7 A* i
#include "RtcTimer.h" // 应用内部定时器计时的实时时钟的头文件
#ifdef DEBUG0 G% G& I0 ?2 E+ d: u# T+ W
#include "LcdHt1621.h" // HT1621 字符液晶模块显示接口驱动的头文件
//#include "LcdSed1520.h" // SED1520 图形液晶模块显示接口驱动的头文件件, m* H' G6 B4 m' {1 [+ T
//#include "LcdHd61202.h" // HD61202/3 图形液晶模块显示接口驱动的头文件
//#include "LedDigital.h" // 发光数码管驱动显示接口的头文件
//#include "LcdTftBus.h" // TFT液晶模块总线驱动显示接口的头文件
#endif1 T I$ L u% W
! a U! I. C- ?% E# v9 _
#ifdef __cplusplus9 u5 M2 Q5 e7 A
extern "C" { // 声明接在下面的指令为C程序编译系统
#endif. e8 C. x( l& @
9 o/ X( }/ T' N: M
3 n8 e- i1 k- ^9 z! J: a
/*==========================================================================================================) z. k: |. @6 s8 ^/ d" \
全局常量定义( _5 ]2 h/ T) U; C+ a* Q* J
==========================================================================================================*/4 s) J7 K' M, h( L" `4 f; v
#if (defined(_IAR_EW_AVR_))
9 N# c' r" W8 z. d7 h; b
#elif ((defined(_IAR_EW_MCS51_)) || (defined(_KEIL_UV_MCS51_)))0 u2 `5 F( S# a# I
#elif ( defined(_IAR_EW_STM8_) \; c; e5 Q/ p( f' m# R1 K
|| defined(_COSMIC_STM8_) ) // “......_STM8 编译器”条件编译开始1 S* p( {9 B; n- ^$ b
#ifdef _USE_STM8_FWLIB_ // “使用 STM8 FWLib 的库文件”条件编译开始
#else // “_USE_STM8_FWLIB_”2 c/ R' g0 D- ~1 X$ z3 _- V
2 O* \. W4 V# O
#endif // “_USE_STM8_FWLIB_”& C& z' ~% L. S' d
#else
#error 错误:
#endif // “PreMcuIs__......”
^ f3 A# D: {& x, s* \! \1 J
+ Q0 {+ s' h/ j
/*==========================================================================================================5 t6 v# J9 N @+ M* d& r) T, @- ?
全局变量定义
==========================================================================================================*/
/*------------------------------------------------------------------------------------------------7 `# v1 a8 X% S% W6 G
I2C总线通讯处理消息任务标志的全局变量定义: m; o, K. u- I$ K7 V) w
------------------------------------------------------------------------------------------------*/4 |$ f+ y' Y7 w% Y b8 j
bdata CommI2c0FlagBits gbitI2c0;
/*------------------------------------------------------------------------------------------------6 g. v# o0 k h8 H- J; m4 U
I2C总线的事件和状态码的全局变量定义:
------------------------------------------------------------------------------------------------*/
data unsigned char gucvI2c0Event;
/*------------------------------------------------------------------------------------------------8 R& a8 c2 r/ j5 y/ Q
I2C总线通讯收发数据处理的全局变量定义:
------------------------------------------------------------------------------------------------*/
xdata CommI2c0DataStru gstvI2c0;) y. J5 ~- A3 B; _" O& k0 z w) T P
- v$ d e( f& d8 R. z. W- i5 b' a
/*==========================================================================================================
本源程序文件内部使用的字符化常数定义6 I' J9 t4 D8 @6 O, X+ M: e0 @
==========================================================================================================*/1 C% _* w& H+ Q, f5 S4 J' L
#if defined(PreCommI2cMultipleI2cEnable0) // “使用I2C0”条件编译开始
#define cDdrI2cScl cDdrI2c0Scl
#define cPurI2cScl cPurI2c0Scl$ _$ D+ ~8 \) ~. e* h3 n
#define cCirI2cScl cCirI2c0Scl& K \4 p7 E6 l1 f2 ^
#define cOdrI2cScl cOdrI2c0Scl
#define cIdrI2cScl cIdrI2c0Scl
#define cPidI2cScl cPidI2c0Scl8 ?- |' O5 A, u& C
#define cPodI2cScl cPodI2c0Scl1 s( `) y5 C/ b6 v3 |
#define cBitI2cScl cBitI2c0Scl" r- W/ j3 V3 [. x) {# v
#define cDdrI2cSda cDdrI2c0Sda
#define cPurI2cSda cPurI2c0Sda
#define cCirI2cSda cCirI2c0Sda8 |8 _1 g: }. Q* U
#define cOdrI2cSda cOdrI2c0Sda
#define cIdrI2cSda cIdrI2c0Sda% _; V% F( E v( m& o. s
#define cPidI2cSda cPidI2c0Sda
#define cPodI2cSda cPodI2c0Sda
#define cBitI2cSda cBitI2c0Sda
#define cI2cRxBufSize cI2c0RxBufSize/ `" D# M0 F- _% R7 |
#define cI2cTxBufSize cI2c0TxBufSize& f% O& A6 R" [( q! f( S
#define CommI2cDataStru CommI2c0DataStru0 B. W% d5 ~! T' v
#define cI2cAddrNum cI2c0AddrNum3 k* R7 x/ |* s) J" K8 T
#define gbitI2c gbitI2c07 L3 w# F9 Z; d! c
#define gstvI2c gstvI2c06 u, Y" v# ]) g: k( `
#define gucvI2cEvent gucvI2c0Event
#elif defined(PreCommI2cMultipleI2cEnableS) // “使用I2Cs”条件编译开始
#endif // “使用I2C......”5 G. H. I( b+ D+ ]9 R2 p
9 O& k/ i' i1 j% x! @
2 ?7 S) h/ i4 `( Z+ B
/*==========================================================================================================4 |" V: S" |, q! `7 P8 v
本源程序文件内部使用的数据结构类型定义4 D( T( U/ M9 H* F J
==========================================================================================================*/1 |1 m7 S7 S) E1 ?- R
" `" o6 B5 e* a9 s) b
3 v# X! u/ P) F* A& Z& o
/*==========================================================================================================2 p" W( ?3 s3 _6 c8 a$ O$ f) x' r7 Z
本源程序文件内部使用的局部常量定义' X6 L) O1 H& N, A# H
==========================================================================================================*/
( c1 C: e/ Y2 A5 A) c& [
6 U7 K/ ?4 V3 j1 N1 @- t, |
+ v6 u0 [- e" n# g/ S; C
/*==========================================================================================================
本源程序文件内部使用的局部变量定义
==========================================================================================================*/
#ifdef DEBUG
static unsigned char display_y_cnt = 0;- v$ ^/ w; x/ i! r4 L
#endif
* g4 U) x1 `2 x* U1 @. F
/*==========================================================================================================3 {6 u" j; v! R( e9 k
本源程序文件内部使用的函数原型声明$ `+ K q( |1 [ r1 q
==========================================================================================================*/3 t3 m7 I4 l& K5 v9 }$ t
static void CommI2cBusAgain(void);) b9 n& T+ w& z( E+ T' n0 m; O
/*==========================================================================================================
函数名称: 设置I2C总线为标准/快速模式和占空比. v8 N7 g0 n$ f
函数功能: 本函数用于设置I2C总线为标准/快速模式和占空比操作的函数。, g$ A0 ? T; _
函数入口参数:Model ----------------- 标准/快速模式和占空比, ON(快速模式)/ OFF(标准模式)。
函数返回值: 没有
调用函数: 1.
备注: 1.
==========================================================================================================*/
void CommI2cBusModeDuty (unsigned char Model)
{
#if (defined(_IAR_EW_AVR_))
#elif ((defined(_IAR_EW_MCS51_)) || (defined(_KEIL_UV_MCS51_)))
1 I9 y* N# B: Z; @+ W6 S
#elif ( defined(_IAR_EW_STM8_) \3 p! C0 C8 w/ h
|| defined(_COSMIC_STM8_) ) // “......_STM8 编译器”条件编译开始* @$ C4 u% A C# j& K+ m+ N& T, }7 K
#ifdef _USE_STM8_FWLIB_ // “使用 STM8 FWLib 的库文件”条件编译开始
. L9 Z. X. j+ a, X( K# C
#else // “_USE_STM8_FWLIB_”2 m! c; c/ Y4 V' l
I2CCR1 &= (u8)(~((1<<i2cEN ) )); // 禁止I2C模块,用以设置 I2CTRISER。
if (Model == ON) // 快速模式:占空比=Tlow/Thigh=2/1, k/ E* O- [; b0 `4 z4 }7 `' q2 {( k
{
I2CCCRH = (u8)( ((0<<i2cDUTY )| // 快速模式下的占空比=0:Tlow/Thigh=2/1,1:Tlow/Thigh=16/9。
(1<<i2cSFM )| // 标准/快速模式选择位=快速模式
cI2c0BrCcrF21H )); // 置I2C比特率高位寄存器的数值,注:只有禁用I2C模块时,才能设置。8 t% C9 H/ v5 P" {- k' ^
I2CCCRL = (u8)( cI2c0BrCcrF21L); // 置I2C比特率低位寄存器的数值,注:只有禁用I2C模块时,才能设置。
I2CTRISER = (u8)( ((((cSysCpuClk / 1000000UL) * 3UL) / 10UL) + 1));/ D, M2 G3 E# }$ k1 A
// 快速模式:最大上升时间=300ns,注:只有禁用I2C模块时,才能设置。6 H& h8 Q4 J4 ]* Q+ H3 \" ]
}9 `5 t& a% K4 O+ x) P h ^
else // 标准模式:占空比=Tlow/Thigh=1/1
{- I2 ]/ `( [: V
I2CCCRH = (u8)( ((0<<i2cDUTY )| // 快速模式下的占空比=0:Tlow/Thigh=2/1,1:Tlow/Thigh=16/9。
(0<<i2cSFM )| // 标准/快速模式选择位=标准模式
cI2c0BrCcrS11H )); // 置I2C比特率高位寄存器的数值,注:只有禁用I2C模块时,才能设置。% ?; ^7 J @9 l
I2CCCRL = (u8)( cI2c0BrCcrS11L); // 置I2C比特率低位寄存器的数值,注:只有禁用I2C模块时,才能设置。1 I# ^. S' }4 E) }! [
I2CTRISER = (u8)( ((cSysCpuClk / 1000000UL) + 1));// 标准模式:最大上升时间=1000ns,注:只有禁用I2C模块时,才能设置。' v( H; N& ]. ^$ f
}" o/ `/ W( W6 ^2 @; t
I2CCR1 |= (u8)( ((1<<i2cEN ) )); // 使能I2C模块& u+ M3 @7 n, y
#endif // “_USE_STM8_FWLIB_”3 m3 W. ?: {0 U Q2 C2 F/ P# J. E
#else* S% p8 B% R7 x9 ]- O% a
#error 错误:; S! s0 z$ g, J% Q& p5 U% |
#endif // “PreMcuIs__......”
}
#ifdef PreCommI2cUseSingleMasterMode // “I2C总线工作于单主机方式”的条件编译
/*========================================================================================================== G2 B3 ^ S! \# D7 D+ b
函数名称: I2C总线工作于单主机方式,重新启动I2C总线前一个通讯收发作业+ N1 t& \4 i2 l
函数功能: 本函数用于I2C总线工作于单主机方式,重新启动I2C总线前一个通讯收发作业操作的函数。6 u; A; x; H0 m' |
函数入口参数:gbitI2c.Txd ------------- “正在进行发送数据作业”标志。 c/ X& r6 B% y" y, V# B6 S
gbitI2c.Rxd ------------- “正在进行接收数据作业”标志。
函数返回值: 没有
函数出口参数:gucvI2cEvent ---------- I2C总线的状态码变量,出口值= cI2cAllNoState(没有相关的状态信息) 。
gstvI2c.TxBuf[0] ------ 每次发送数据缓冲区数组变量,出口值=要发送的地址。
gstvI2c.ErrNum -------- I2C总线的通讯收发作业发生错误次数值变量,出口值=0。* g: b8 F0 x$ r7 Q% c) O
gstvI2c.RxCnt --------- 每次接收数据个数计数器变量,出口值=0。
调用函数: 1.+ X d* }% K3 W9 [7 Y# B) X
备注: 1.调用本函数将触发两线串行总线接口TWI中断请求和向I2C总线发出 START 信号(起始条件)。
==========================================================================================================*/$ J, D" o8 D: ~5 l" f' S* A
static void CommI2cBusAgain (void). K% v" B0 P& |: B7 b C
{4 x- _3 Q8 }$ \% D( |
#if cI2cTxBufSize < 256; ?+ A8 }0 E1 ~! J4 g
register unsigned char i; // 循环变量
#else
register unsigned int i;7 B! t% {+ l: S3 Z' \6 n
#endif
unsigned char temp; // 临时变量3 H E4 o8 o E; j$ ^
CommI2cDataStru *pI2c; // 指向I2C总线通讯收发数据处理变量的指针
pI2c = &gstvI2c; // 取I2C总线通讯收发数据处理变量的首个地址
& b) ?) [5 ?1 ], ?
MFHI2cBusModSwRst( ); // 软件复位I2C模块,释放I2C总线) l2 m5 ]. D0 L) r
gucvI2cEvent = cI2cAllNoState; // 置I2C总线的状态码 = 没有相关的状态信息
if ( (gbitI2c.Txd == SET) // 如果是先发送若干地址然后再进行发送数据作业?1 b* K5 r3 P) V7 u* L
&& (gbitI2c.Rxd == SET) )2 a2 h8 ^/ t3 y1 C& H& O
{
pI2c->TxBuf[0] &= (u8)(~BIT0); // 设置数据传送方向为写入数据' m* ]) L# k$ B. D& J) j
}
pI2c->RxCnt = 0x00; // I2C总线每次接收数据个数计数器清0& H+ l. U( w: `8 Q# i/ J1 Y
pI2c->ErrNum = 0x00; // I2C总线的通讯收发作业发生错误次数值清0
MFHI2cFlgEventClr( ); // 清0I2C总线事件中断标志位! ^ t% p' G+ A$ Q9 v' F
MFHI2cIntEventOne( ); // 使能I2C总线事件中断、错误中断,禁止缓冲中断5 {% T+ c1 M8 I
MFHI2cBusNackStar( ); // 发出 START 信号(起始条件),没有 ACK 脉冲* N- s5 R8 V$ |' X
}6 O3 p: Z+ n/ H2 Q: Q5 E
/*==========================================================================================================/ N: e# o+ K+ w1 Z
函数名称: 启动I2C总线应用中断方式发送数据 Z7 Z4 l4 u, Z. f, ]; K T: X7 F
函数功能: 本函数用于启动I2C总线使用中断方式,进行通讯发送数据作业操作的函数。
函数入口参数:Object ------------------ 要发送数据的的目标设备地址值。
*pData ------------------ 指向首个要发送数据在SRAM中的地址指针。
Length ------------------ 要发送数据的数据总长度。2 n0 g; p$ k2 @$ x, H
函数返回值: 没有! K0 @, k- j' A8 E9 @& `
函数出口参数:gbitI2c.Busy ------------ I2C总线〖忙〗,出口值=置位。" n3 u: p) V5 E% M. F' [
gbitI2c.Txd ------------- “正在进行发送数据作业”标志,出口值=置位。+ r) V' V1 u H$ z! G
gbitI2c.Rxd ------------- “正在进行接收数据作业”标志,出口值=清位。9 Z4 f* `. G: E) t' ~. X0 ^1 d: m
gucvI2cEvent ------------ I2C总线的状态码变量,出口值= cI2cAllNoState(没有相关的状态信息) 。9 h8 X) J7 b! w( T. c7 f" X
gstvI2c.TxSize ---------- 每次发送数据个数大小变量,出口值=Length + cI2cAddrNum。
gstvI2c.TxCnt ----------- 每次发送数据个数计数器变量,出口值=0。
gstvI2c.TxBuf[] --------- 每次发送数据缓冲区数组变量,出口值=要发送的数据。7 V3 k' c. P0 b. ^
gstvI2c.Cycle ----------- 重新启动通讯收发作业圈数,出口值=0。
gstvI2c.ErrNum ---------- 通讯收发作业发生错误次数,出口值=0。
调用函数: 1.
备注: 1.注意:调用本函数之前,必须确定I2C总线在空闲状态。即必须先调用+ {4 F, ?, B3 O
“unsigned char CommI2cBusBusy(void)”函数,来检查I2C总线的工作状态,9 I: V, d, v) C& ?; x9 a; S
只有I2C总线处于空闲状态,才允许调用本函数。
2.调用本函数将触发两线串行总线接口TWI中断请求和向I2C总线发出 START 信号(起始条件)。- b' K: M( S+ b) o
==========================================================================================================*/
#if cI2cTxBufSize < 2561 V# G- @5 o- @: _; S" V
void CommI2cTxWithData (unsigned char Object, unsigned char *pData, unsigned char Length)$ w Y1 M! R; V7 A6 t- @7 w8 c. f
#else
void CommI2cTxWithData (unsigned char Object, unsigned char *pData, unsigned int Length); b8 C9 h( |" b) I5 v9 N" U. q
#endif
{- n* H: ^* ^* C( V+ [8 N
#if cI2cTxBufSize < 256
register unsigned char i; // 循环变量
#else/ K& W6 q0 H& h8 U4 \+ M L
register unsigned int i;
#endif* R1 O. X$ `/ C. M4 x# ?
unsigned char temp; // 临时变量
CommI2cDataStru *pI2c; // 指向I2C总线通讯收发数据处理变量的指针
% u+ \& W+ [) d
pI2c = &gstvI2c; // 取I2C总线通讯收发数据处理变量的首个地址" K1 W) K# M1 u6 ? K- g9 b
! K/ b' }1 d8 X
#ifdef PreCommI2cWithRedundantCode // “使用冗余程序代码”条件编译开始' K$ L+ ]% r/ q7 `
if ( (Length >= (cI2cTxBufSize - 1))4 @: {9 q! B# N5 K. K6 z4 z# s1 ]
|| (Length == 0) )3 ~5 u; G- ^! S$ _- ]3 y2 l( A
{) u2 s5 b, X: u
return;
}" c6 z( \( T" a1 ?9 d' n
#endif // “PreCommI2cWithRedundantCode”# a0 y3 F' y+ h0 Y$ A
! w. p6 k4 f% e6 l1 d+ k0 ?
MFHI2cBusModSwRst( ); // 软件复位I2C模块,释放I2C总线
// while(CommI2cBusBusy( )); // 等待直到对TWI下一个收发作业准备就绪为止
3 l' O8 p- g. ~' p3 b7 E% u
gbitI2c.Busy = SET; // 置“I2C总线〖忙〗”标志4 _' j; b1 e, G
gbitI2c.Txd = SET; // 置“正在进行发送数据作业”标志
gbitI2c.Rxd = CLEAR; // 清除“正在进行接收数据作业”标志
gbitI2c.OverS = SET; // 置“通讯任务时间超时”标志
gucvI2cEvent = cI2cAllNoState; // 置I2C总线的状态码 = 没有相关的状态信息' U0 ?+ i0 k F: ~
pI2c->OverS = RtcGetSecByte(cI2cOverSec);// 启动通讯任务时间计时秒表
// pI2c->Object = Object; // 复制目标设备地址到每次发送数据的目标设备地址中1 O7 f' X) L& s# e
// pI2c->Object &= (u8)(~BIT0); // 设置数据传送方向为写入数据; @ k; I/ _8 c: s, m( b
// pUsart->pTxData = pData; // 保存要发送的数据序列的地址指针 W7 w" M) P2 P- l3 O
MemCopySramToSram(&Object, pI2c->TxBuf, cI2cAddrNum); // 复制目标设备地址到发送数据缓冲区中4 z b% ^7 b) p4 f0 P/ T
pI2c->TxBuf[0] &= (u8)(~BIT0); // 设置数据传送方向为写入数据
MemCopySramToSram(pData, &pI2c->TxBuf[cI2cAddrNum], Length);
// 复制发送的数据序列到发送数据缓冲区中
pI2c->TxSize = Length + cI2cAddrNum; // 置每次发送数据个数大小值
pI2c->TxCnt = 0x00; // 每次发送数据计数器值清02 x- T- T+ o* x. {8 w* y
pI2c->Cycle = 0x00; // 重新启动通讯收发作业圈数清0
pI2c->ErrNum = 0x00; // I2C总线的通讯收发作业发生错误次数清0' e6 b; v4 _+ A% ?4 }: X7 P7 r/ B6 j
MFHI2cFlgEventClr( ); // 清0I2C总线事件中断标志位$ }- D8 m8 N2 g9 ^% Q2 g6 C
MFHI2cIntEventOne( ); // 使能I2C总线事件中断、错误中断,禁止缓冲中断" T2 B5 M. t2 }/ ^3 y4 d M3 Y# n; R
MFHI2cBusNackStar( ); // 发出 START 信号(起始条件),没有 ACK 脉冲) v7 \7 a9 v5 c, h- w4 n4 l% E" ~) Y
}
/*==========================================================================================================8 y+ f3 w7 p$ s' I& M- P; E/ f
函数名称: 启动I2C总线应用中断方式接收数据
函数功能: 本函数用于启动I2C总线使用中断方式,进行通讯接收数据作业操作的函数。
函数入口参数:Object ------------------ 要接收数据的的目标设备地址值。
Length ------------------ 要接收数据的数据总长度。
函数返回值: 没有
函数出口参数:gbitI2c.Busy ------------ I2C总线〖忙〗,出口值=置位。4 {. W0 C, A0 P0 X0 r
gbitI2c.Txd ------------- “正在进行发送数据作业”标志,出口值=清位。
gbitI2c.Rxd ------------- “正在进行接收数据作业”标志,出口值=置位。
gucvI2cEvent ------------ I2C总线的状态码变量,出口值= cI2cAllNoState(没有相关的状态信息) 。$ K/ [0 b5 t* T
gstvI2c.TxSize ---------- 每次发送数据个数大小变量,出口值=cI2cAddrNum。7 I; k, t; W5 T5 i9 @3 \
gstvI2c.TxCnt ----------- 每次发送数据个数计数器变量,出口值=0。7 [, z% Q8 i% l0 X9 N- I4 J
gstvI2c.TxBuf[] --------- 每次发送数据缓冲区数组变量,出口值=要发送的数据。
gstvI2c.Cycle ----------- 重新启动通讯收发作业圈数,出口值=0。; o% e% R) G% m, M' K
gstvI2c.ErrNum ---------- 通讯收发作业发生错误次数,出口值=0。
gstvI2c.RxSize ---------- 每次接收数据个数大小变量,出口值=Length。
gstvI2c.RxCnt ----------- 每次接收数据个数计数器变量,出口值=0。9 w" a2 ?" _& L ]. X L" r
调用函数: 1.
备注: 1.注意:调用本函数之前,必须确定I2C总线在空闲状态。即必须先调用
“unsigned char CommI2cBusBusy(void)”函数,来检查I2C总线的工作状态,9 n( y @) x+ K/ k% S8 [1 `
只有I2C总线处于空闲状态,才允许调用本函数。/ S& u+ k" G% @% b
2.调用本函数将触发两线串行总线接口TWI中断请求和向I2C总线发出 START 信号(起始条件)。
==========================================================================================================*/
#if cI2cRxBufSize < 256
void CommI2cRxWithData (unsigned char Object, unsigned char Length): c+ C5 e0 Q# N3 Y* D5 V( L
#else
void CommI2cRxWithData (unsigned char Object, unsigned int Length)& S8 Y" w: t7 k9 \: q$ P1 _7 M% M
#endif
{
#if cI2cRxBufSize < 256
register unsigned char i; // 循环变量# y; A; {, A ?
#else/ L1 W; _( \; M5 Q
register unsigned int i;! t/ }# K5 ^3 z0 l$ |1 n
#endif: Z, s+ r# G3 F- `. U
unsigned char temp; // 临时变量; F9 ~) Y: U. E5 S: ]; n" F) D
CommI2cDataStru *pI2c; // 指向I2C总线通讯收发数据处理变量的指针
pI2c = &gstvI2c; // 取I2C总线通讯收发数据处理变量的首个地址
#ifdef PreCommI2cWithRedundantCode // “使用冗余程序代码”条件编译开始
if ( (Length > cI2cRxBufSize) ) // 检查要接收数据的总个数是否超过缓冲区长度?# B5 W- |% U( ~9 c
{3 I+ X9 x) b# U' A/ g* m8 \9 P
Length = cI2cRxBufSize; // 截断要接收数据的总个数5 R5 p0 B. ^/ i; G7 Z Q# o
}8 v% r" a! \1 W
#endif // “PreCommI2cWithRedundantCode”
# N$ T9 @: `0 \! ^$ N6 k
MFHI2cBusModSwRst( ); // 软件复位I2C模块,释放I2C总线
// while(CommI2cBusBusy( )); // 等待直到对TWI下一个收发作业准备就绪为止5 q2 o7 R% \$ [' _8 Q% X7 `
( E X% X' A/ z+ V% G
gbitI2c.Busy = SET; // 置“I2C总线〖忙〗”标志
gbitI2c.Txd = CLEAR; // 清除“正在进行发送数据作业”标志
gbitI2c.Rxd = SET; // 置“正在进行发送数据作业”标志
gbitI2c.OverS = SET; // 置“通讯任务时间超时”标志" z* O1 t8 U9 l$ J
gucvI2cEvent = cI2cAllNoState; // 置I2C总线的状态码 = 没有相关的状态信息, {: G* T0 h7 R4 Q: M' `
pI2c->OverS = RtcGetSecByte(cI2cOverSec);// 启动通讯任务时间计时秒表, \% x6 ^) A( i% y0 s' w
MemCopySramToSram(&Object, pI2c->TxBuf, cI2cAddrNum); // 复制目标设备地址到发送数据缓冲区中
pI2c->TxBuf[0] |= (u8)( BIT0); // 设置数据传送方向为接收数据' o3 J7 d) c& v* U0 f1 a3 W$ g
pI2c->TxSize = cI2cAddrNum; // 置每次发送数据个数大小值
pI2c->TxCnt = 0x00; // 每次发送数据计数器值清0
pI2c->Cycle = 0x00; // 重新启动通讯收发作业圈数清0
pI2c->ErrNum = 0x00; // I2C总线的通讯收发作业发生错误次数清0) ]! \1 S B0 q2 c; |
! X0 _1 R5 D* i* T5 i
pI2c->RxSize = Length; // 置I2C总线每次接收数据个数大小的值1 @/ Z+ ?( Z# m) r. @& E+ R
pI2c->RxCnt = 0x00; // I2C总线每次接收数据个数计数器清0- ~% w2 U6 P) @2 K' q' e) P
MFHI2cFlgEventClr( ); // 清0I2C总线事件中断标志位
MFHI2cIntEventOne( ); // 使能I2C总线事件中断、错误中断,禁止缓冲中断
MFHI2cBusNackStar( ); // 发出 START 信号(起始条件),没有 ACK 脉冲, c9 }- ~3 w$ p1 d/ j# I
}
8 V1 W# O5 x% S3 E5 c& V5 G' G1 [
/*==========================================================================================================
函数名称: 启动I2C总线应用中断方式先发送若干地址然后再进行发送数据作业的函数。
函数功能: 本函数用于启动I2C总线使用中断方式,进行通讯先发送若干地址然后再进行发送数据作业操作的函数。
函数入口参数:Object ------------------ 要发送数据的的目标设备地址值。% E: U* e2 O$ w0 Z( q6 ]" _
*pAddr ------------------ 指向首个要发送地址在SRAM中的地址指针。/ e" F6 ~, \; E
LenAddr ----------------- 要发送地址的地址总长度。; V' f& d! y# Z7 H
*pData ------------------ 指向首个要发送数据在SRAM中的地址指针。
Length ------------------ 要发送数据的数据总长度。
函数返回值: 没有
函数出口参数:gbitI2c.Busy ------------ I2C总线〖忙〗,出口值=置位。7 S0 ~3 ~6 s' G2 ~" @$ E7 |2 ~8 A; R
gbitI2c.Txd ------------- “正在进行发送数据作业”标志,出口值=置位。4 {& n6 ^0 H g
gbitI2c.Rxd ------------- “正在进行接收数据作业”标志,出口值=清位。: a f. U; _% u) Y
gucvI2cEvent ------------ I2C总线的状态码变量,出口值= cI2cAllNoState(没有相关的状态信息) 。
gstvI2c.TxSize ---------- 每次发送数据个数大小变量,出口值=LenAddr + Length + cI2cAddrNum。
gstvI2c.TxCnt ----------- 每次发送数据个数计数器变量,出口值=0。
gstvI2c.TxBuf[] --------- 每次发送数据缓冲区数组变量,出口值=要发送的地址和数据。6 n+ U$ N4 w: w& T2 M
gstvI2c.Cycle ----------- 重新启动通讯收发作业圈数,出口值=0。1 @9 s/ z' `( f) {- r) x- ?
gstvI2c.ErrNum ---------- 通讯收发作业发生错误次数,出口值=0。
调用函数: 1.
备注: 1.注意:调用本函数之前,必须确定I2C总线在空闲状态。即必须先调用& T! U& z8 u, C& ]& A
“unsigned char CommI2cBusBusy(void)”函数,来检查I2C总线的工作状态,
只有I2C总线处于空闲状态,才允许调用本函数。& }) c+ [- Z( y2 [2 D
2.调用本函数将触发两线串行总线接口TWI中断请求和向I2C总线发出 START 信号(起始条件)。5 {, o; I7 k: n. M1 f1 W( s
==========================================================================================================*/9 I+ I7 A" J3 S0 f
#if cI2cTxBufSize < 256% L3 T" Z' R( ?1 j) d
void CommI2cTxWithAddr (unsigned char Object, unsigned char *pAddr, unsigned char LenAddr, unsigned char *pData, unsigned char Length)
#else- h" V( D( I4 |, C: ? T
void CommI2cTxWithAddr (unsigned char Object, unsigned char *pAddr, unsigned char LenAddr, unsigned char *pData, unsigned int Length)
#endif+ ?: w! ]+ v" Z
{
#if cI2cTxBufSize < 256# h- S1 C. L+ q9 @6 ]" R7 b
register unsigned char i; // 循环变量
#else
register unsigned int i;
#endif/ R+ |7 C! r# a% ?3 M3 w! u6 i
unsigned char temp; // 临时变量
CommI2cDataStru *pI2c; // 指向I2C总线通讯收发数据处理变量的指针( F m5 a- t$ f F) i6 l$ J3 Z6 u
pI2c = &gstvI2c; // 取I2C总线通讯收发数据处理变量的首个地址4 K" M8 i; J3 b- F4 z2 I0 U. [! O
#ifdef PreCommI2cWithRedundantCode // “使用冗余程序代码”条件编译开始5 }$ b% w9 M1 K, I% S% S
if ( ((LenAddr + Length) > (cI2cTxBufSize - 1)) // 检查要接收数据的总个数是否超过缓冲区长度?- L0 }$ _8 q3 A6 W6 o
|| (LenAddr == 0) )9 V5 U" Q! O# j' C3 ]$ G
{+ }7 e- f' C3 `5 \" d
return;
}% h1 w3 A/ Y! B- W* H% l
#endif // “PreCommI2cWithRedundantCode” b' y+ ?7 j7 P+ y) R
, w+ z" U% ?4 c5 \$ j
MFHI2cBusModSwRst( ); // 软件复位I2C模块,释放I2C总线
// while(CommI2cBusBusy( )); // 等待直到对TWI下一个收发作业准备就绪为止6 c, \2 a! _" S" R; v1 w
gbitI2c.Busy = SET; // 置“I2C总线〖忙〗”标志0 m7 S+ N' U: \. N9 ~, G3 `! ~( Q
gbitI2c.Txd = SET; // 置“正在进行发送数据作业”标志
gbitI2c.Rxd = CLEAR; // 清除“正在进行接收数据作业”标志" v k3 ^* @' _8 ~' E
gbitI2c.OverS = SET; // 置“通讯任务时间超时”标志: M3 ^; W \6 \# ?, h4 i- z* z
gucvI2cEvent = cI2cAllNoState; // 置I2C总线的状态码 = 没有相关的状态信息7 f+ G" Q) F! b1 l( m, Y% c& e+ Q
pI2c->OverS = RtcGetSecByte(cI2cOverSec);// 启动通讯任务时间计时秒表+ \9 `- e# [. x' D
// pI2c->Object = Object; // 复制目标设备地址到每次发送数据的目标设备地址中
// pI2c->Object &= (u8)(~BIT0); // 设置数据传送方向为写入数据
// pUsart->pTxData = pData; // 保存要发送的数据序列的地址指针* [% J) E" `2 A. r
MemCopySramToSram(&Object, pI2c->TxBuf, cI2cAddrNum); // 复制目标设备地址到发送数据缓冲区中
pI2c->TxBuf[0] &= (u8)(~BIT0); // 设置数据传送方向为写入数据, s1 H8 Q7 Q% I! g' S
MemCopySramToSram(pAddr, &pI2c->TxBuf[cI2cAddrNum], LenAddr);
// 复制发送的地址序列到发送数据缓冲区中0 c5 D; J9 K9 K- ?+ Z, j1 l
MemCopySramToSram(pData, &pI2c->TxBuf[LenAddr + cI2cAddrNum], Length);6 s4 A/ H$ y2 v: A" K0 o2 C& w
// 复制发送的数据序列到发送数据缓冲区中
pI2c->TxSize = LenAddr + Length + cI2cAddrNum;
// 置每次发送数据个数大小值
pI2c->TxCnt = 0x00; // 每次发送数据计数器值清0# h; v7 R# Z8 w8 s- a
pI2c->Cycle = 0x00; // 重新启动通讯收发作业圈数清0
pI2c->ErrNum = 0x00; // I2C总线的通讯收发作业发生错误次数清0
2 R J0 B3 k2 Y& C
MFHI2cFlgEventClr( ); // 清0I2C总线事件中断标志位$ a" |) `0 z7 C& K4 Y" ?
MFHI2cIntEventOne( ); // 使能I2C总线事件中断、错误中断,禁止缓冲中断
MFHI2cBusNackStar( ); // 发出 START 信号(起始条件),没有 ACK 脉冲% X$ u" j: d5 }6 I$ [ N
}
/*==========================================================================================================
函数名称: 启动I2C总线应用中断方式先发送若干地址然后再进行接收数据作业的函数。$ i) k6 a# ?/ r# {; z7 R. S* I0 J
函数功能: 本函数用于启动I2C总线使用中断方式,进行通讯先发送若干地址然后再进行接收数据作业操作的函数。& r5 x& ^$ p: [7 z! f
函数入口参数:Object ------------------ 要接收数据的的目标设备地址值。
*pAddr ------------------ 指向首个要发送地址在SRAM中的地址指针。6 W# Q! S8 g ^ A6 _2 A: a
LenAddr ----------------- 要发送地址的地址总长度。
Length ------------------ 要接收数据的数据总长度。
函数返回值: 没有
函数出口参数:gbitI2c.Busy ------------ I2C总线〖忙〗,出口值=置位。 v) ~, o( ^; f" i, R1 ?$ A7 |
gbitI2c.Txd ------------- “正在进行发送数据作业”标志,出口值=置位。# }+ F* y, ^( V- P
gbitI2c.Rxd ------------- “正在进行接收数据作业”标志,出口值=置位。
gucvI2cEvent ------------ I2C总线的状态码变量,出口值= cI2cAllNoState(没有相关的状态信息) 。/ y* K5 D3 {( K& f9 R2 ]) p: N
gstvI2c.TxSize ---------- 每次发送数据个数大小变量,出口值=LenAddr + cI2cAddrNum。
gstvI2c.TxCnt ----------- 每次发送数据个数计数器变量,出口值=0。, w! E# D5 P5 H
gstvI2c.TxBuf[] --------- 每次发送数据缓冲区数组变量,出口值=要发送的地址。
gstvI2c.Cycle ----------- 重新启动通讯收发作业圈数,出口值=0。7 c. q& p# ~: r0 n" s$ Z, z
gstvI2c.ErrNum ---------- 通讯收发作业发生错误次数,出口值=0。, C" k9 Z$ ~' ^- [1 d3 C7 Q
gstvI2c.RxSize ---------- 每次接收数据个数大小变量,出口值=Length。
gstvI2c.RxCnt ----------- 每次接收数据个数计数器变量,出口值=0。! h! L3 w2 T5 w( d/ }
调用函数: 1., h! s# U# X8 k1 T
备注: 1.注意:调用本函数之前,必须确定I2C总线在空闲状态。即必须先调用2 g+ d! J' g: f' ]' w
“unsigned char CommI2cBusBusy(void)”函数,来检查I2C总线的工作状态,* I& c* a/ }0 d( \# R
只有I2C总线处于空闲状态,才允许调用本函数。
2.调用本函数将触发两线串行总线接口TWI中断请求和向I2C总线发出 START 信号(起始条件)。
==========================================================================================================*/
#if cI2cTxBufSize < 256
#if cI2cRxBufSize < 256
void CommI2cRxWithAddr (unsigned char Object, unsigned char *pAddr, unsigned char LenAddr, unsigned char Length)& A% B/ m% y: m) b, m& `/ p8 S; k
#else
void CommI2cRxWithAddr (unsigned char Object, unsigned char *pAddr, unsigned char LenAddr, unsigned int Length)7 T J& p' N) A7 D8 r% O
#endif `& ^8 l8 p( m% v; Z8 a# B
#else
#if cI2cRxBufSize < 256
void CommI2cRxWithAddr (unsigned char Object, unsigned char *pAddr, unsigned int LenAddr, unsigned char Length)$ u! ~# a J1 b) S5 p
#else' z6 R* i$ L ~# k) a; H* [
void CommI2cRxWithAddr (unsigned char Object, unsigned char *pAddr, unsigned int LenAddr, unsigned int Length)% d6 z! D& m" J8 A2 y
#endif
#endif5 S" }4 z) ]" [
{
#if cI2cTxBufSize < 256% z9 L( ?' [% I) U/ V
register unsigned char i; // 循环变量7 `" j! B6 o& j Z
#else
register unsigned int i;3 W9 E6 d( m+ e8 w' t& L
#endif% e5 [1 c( z/ P& ]. X
unsigned char temp; // 临时变量
CommI2cDataStru *pI2c; // 指向I2C总线通讯收发数据处理变量的指针
( O7 W6 j. x; X
pI2c = &gstvI2c; // 取I2C总线通讯收发数据处理变量的首个地址8 }! y" B) k' H. S+ [% P6 ?( M, t
#ifdef PreCommI2cWithRedundantCode // “使用冗余程序代码”条件编译开始
if ( (LenAddr >= (cI2cTxBufSize - 1))4 Z: I# B J$ o+ R# s' P& C
|| (LenAddr == 0) )+ A; E! b; b, z: O5 X5 `6 ~
{
return;: C3 N" {* N/ J" a
}) v, ~" y0 M6 Y: a
if ( (Length > cI2cRxBufSize) ) // 检查要接收数据的总个数是否超过缓冲区长度?
{
Length = cI2cRxBufSize; // 截断要接收数据的总个数7 G5 M- R0 q& o7 ~$ \( X
}1 Z: n% |9 Y- r& I4 C
#endif // “PreCommI2cWithRedundantCode”
MFHI2cBusModSwRst( ); // 软件复位I2C模块,释放I2C总线
// while(CommI2cBusBusy( )); // 等待直到对TWI下一个收发作业准备就绪为止) K; G9 R- V8 K
gbitI2c.Busy = SET; // 置“I2C总线〖忙〗”标志% u3 T; s0 g$ D& |
gbitI2c.Txd = SET; // 置“正在进行发送数据作业”标志
gbitI2c.Rxd = SET; // 置“正在进行接收数据作业”标志! q' u( R/ \2 {
gbitI2c.OverS = SET; // 置“通讯任务时间超时”标志
gucvI2cEvent = cI2cAllNoState; // 置I2C总线的状态码 = 没有相关的状态信息+ Z$ L" x4 R! W1 W/ Q3 T0 e; C
pI2c->OverS = RtcGetSecByte(cI2cOverSec);// 启动通讯任务时间计时秒表: L8 _. y2 W4 H5 e. [
// pI2c->Object = Object; // 复制目标设备地址到每次发送数据的目标设备地址中% m: u; [: U, [
// pI2c->Object &= (u8)(~BIT0); // 设置数据传送方向为写入数据- f7 O0 J, H ]: E. C6 D6 e
// pUsart->pTxData = pData; // 保存要发送的数据序列的地址指针
MemCopySramToSram(&Object, pI2c->TxBuf, cI2cAddrNum); // 复制目标设备地址到发送数据缓冲区中
pI2c->TxBuf[0] &= (u8)(~BIT0); // 设置数据传送方向为写入数据% {+ m! k2 {+ l0 N$ L
MemCopySramToSram(pAddr, &pI2c->TxBuf[cI2cAddrNum], LenAddr);2 J5 ]6 X* s! C( w/ C
// 复制发送的地址序列到发送数据缓冲区中5 Z& s: q! V1 j1 q* |
pI2c->TxSize = LenAddr + cI2cAddrNum; // 置每次发送数据个数大小值! _- p4 h/ t w |5 c
pI2c->TxCnt = 0x00; // 每次发送数据计数器值清0
pI2c->Cycle = 0x00; // 重新启动通讯收发作业圈数清0- K$ _7 {3 N/ `6 B- F: `: K$ d
pI2c->ErrNum = 0x00; // I2C总线的通讯收发作业发生错误次数清0
" C6 w( k# }, ?% ^2 @
pI2c->RxSize = Length; // 置I2C总线每次接收数据个数大小的值
pI2c->RxCnt = 0x00; // I2C总线每次接收数据个数计数器清0+ u+ z n7 Z- e/ [) J+ R! b
MFHI2cFlgEventClr( ); // 清0I2C总线事件中断标志位
MFHI2cIntEventOne( ); // 使能I2C总线事件中断、错误中断,禁止缓冲中断# U- C* D' S: N
MFHI2cBusNackStar( ); // 发出 START 信号(起始条件),没有 ACK 脉冲
}
/*==========================================================================================================
函数名称: I2C总线工作于单主机方式,使用中断进行接收和发送数据的中断服务程序。% D4 b" K; C0 a: d g+ @/ N! S
函数功能: 本函数用于I2C总线工作于单主机方式,使用中断进行接收和发送数据的中断服务程序。; |6 P @: r' g* W2 D
函数入口参数:gbitI2c.Rxd ------------- “正在进行接收数据作业”标志。! i9 }. N/ d' l P) e: c( K+ w6 Q/ v
gstvI2c.TxSize ---------- 每次发送数据个数大小变量。
gstvI2c.TxCnt ----------- 每次发送数据个数计数器变量。
gstvI2c.TxBuf[] --------- 每次发送数据缓冲区数组变量。
gstvI2c.RxSize ---------- 每次接收数据个数大小变量。
函数返回值: 没有
函数出口参数:gbitI2c.Busy ------------ I2C总线〖忙〗,出口值=清位(可能)。2 e# V7 x" G7 } d; J3 m9 `
gbitI2c.Error ----------- 通讯收发作业发生错误,出口值=置位(可能)。
gucvI2cEvent ------------ I2C总线的状态码变量,出口值= 不确定。- U+ ?( e: U6 a
gstvI2c.TxCnt ----------- 每次发送数据个数计数器变量,出口值=gstvI2c.TxCnt。" E9 L2 O0 c: P, y* H; r# _
gstvI2c.ErrNum ---------- 通讯收发作业发生错误次数,出口值=不确定。2 v; e) C& j1 {
gstvI2c.RxCnt ----------- 每次接收数据个数计数器变量,出口值=gstvI2c.RxSize。' S% s; _* g% I5 C, K" P
gstvI2c.RxBuf[] --------- 每次接收数据缓冲区变量,出口值=从I2C总线上接收到的数据。
调用函数: 1.) a4 J; n7 T. b. `
备注: 1.注意:本函数需在I2C总线中断请求的中断向量函数中调用。
在调用之前需对该中断向量进行初始化,使能该中断。
2.全部完成通讯收发作业后,本函数将禁止I2C总线中断。5 q, y. a- D; @) h; s& U$ m
==========================================================================================================*/
#if ( defined(_IAR_EW_AVR_) \7 g1 K5 F( G* ^5 S$ S9 `$ S
|| defined(_IAR_EW_STM8_) )
#pragma vector = int_I2C_vector( M7 l* v( U q
INTERRUPT_KEY void CommI2c_isr (void)
#elif (defined(_KEIL_UV_MCS51_) )
void CommI2c_isr (void) INTERRUPT_KEY int_I2C_vector using 0+ V( U' c* w" y) L, y+ s
#elif ( defined(_COSMIC_STM8_) ), ^/ m T5 f8 J9 u- B( g
INTERRUPT_KEY void CommI2c_isr (void)
#else0 k6 Z ?% r1 _4 f/ Q) b2 P
void CommI2cIsr(void);/ f6 o g" F& A; b8 [% e5 X! X4 `
#endif // “_IAR_EW_......”
#if (defined(_IAR_EW_AVR_))
{
#if ((cI2cTxBufSize < 256) && (cI2cRxBufSize < 256))3 o0 w* Z& }! S9 Z
register unsigned char cnt; // USART每次发送数据个数计数器变量 ]* m# ~% g2 O
#else
register unsigned int cnt;: v+ ]& A/ |9 r4 T) F
#endif
unsigned char temp; // 临时变量4 W- T, c: Z9 k9 i0 I7 t8 J, t0 H8 Z
CommI2cDataStru *pI2c; // 指向I2C总线通讯收发数据处理变量的指针
3 |: v1 t0 y9 X+ x" v2 R+ y
pI2c = &gstvI2c; // 取I2C总线通讯收发数据处理变量的首个地址
switch (TWSR & cI2cTwsrMask) // 判断已屏蔽非状态位的 TWI 状态寄存器状态位值?, H5 Z( M4 }3 Q
{1 o) h5 a3 B m$ m4 G& A) o; o& x
case cI2cAllStart: // START 信号已发送( Y& _3 I% c4 B, o5 O/ f5 V% z, O
case cI2cAllRepStart: // 重复 START 信号已发送1 D* q# X: ^, z0 f& a
{0 b9 H, I- b0 B% M: Z2 y* ~( x
pI2c->TxCnt = 0x00; // 每次发送数据计数器值清0$ Q! \! \) z- B1 H
}0 a+ H6 n- Z2 T! ?& R U, R g) I) v
case cI2cMtxAddrAck: // SLA+W 已发送,接收到 ACK: k" M% j. K9 P2 q# U( f& L. h3 L
case cI2cMtxDataAck: // 数据已发送,接收到 ACK
{
cnt = pI2c->TxCnt; // 取I2C总线每次发送数据个数计数器变量$ |( u& V' s, C2 O
if (cnt >= pI2c->TxSize) // 检查是否己完成发送通讯任务的全部数据?
{ // 是,在最后一个字节之后发出 STOP 信号4 i% k" d1 b9 G6 W, j
if (gbitI2c.Rxd == SET) // 检查是否为读取I2C总线数据?% X- x7 {- u4 Q+ E/ ^9 b
{
pI2c->TxBuf[0] |= (u8)( BIT0); // 设置数据传送方向为接收数据
MFHI2cBusNackReps( ); // 发出重复 START 信号(重新开始条件),没有 ACK 脉冲
}
else
{
gbitI2c.Busy = CLEAR; // 清除“I2C总线〖忙〗”标志2 V( A% z" L8 f8 h+ e) d
gbitI2c.OverS = CLEAR; // 清除“通讯任务时间超时”标志
MFHI2cIntEventOff( ); // 禁止I2C总线事件中断& O9 P; k# D* i) E7 t9 H8 {
MFHI2cBusNackStop( ); // 发出 STOP 信号(结束条件),没有 ACK 脉冲
}
}3 s6 C7 U I9 Z% H* U$ r
else
{; c& A9 N1 G4 F1 D2 P
I2CDR = pI2c->TxBuf[cnt]; // 从发送缓冲区中将发送数据写入到I2C总线! `& q- f+ K$ r* m+ t$ b5 | [* V
cnt++; // I2C总线每次发送数据个数计数器+1
pI2c->TxCnt = cnt; // 返回I2C总线每次发送数据个数计数器值( X8 _+ l0 C6 p
// 通过先读 I2CSR1,然后写入 I2CDR,来清0发送结束、发送数据寄存器空标志" t) B( }( f4 R' v, z4 |$ U7 l
MFHI2cIntEventClr( ); // 进入中断后,在中断句柄中,清0I2C总线事件中断标志位(在中断服务程序中)* ?9 y! }! ~. s1 I' _
}; R5 ]1 V* l8 |8 b7 C' i n
break;
}% j7 w7 ]3 g: ?4 E
case cI2cMrxDataAck: // 接收到数据,ACK 已返回
{/ n9 ^2 R* _( p J+ v' g1 O% U
cnt = pI2c->RxCnt; // 取I2C总线每次接收数据个数计数器变量
pI2c->RxBuf[cnt] = I2CDR; // 复制I2C总线读取到的字节到接收数据缓冲区中
cnt++; // I2C总线每次接收数据个数计数器+1* S* f9 Y. S) X h$ b) p$ w# B
pI2c->RxCnt = cnt; // 返回I2C总线每次接收数据个数计数器值
}
case cI2cMrxAddrAck: // SLA+R 已发送,接收到 ACK
{8 ^! O$ g: p% d6 ~. Q
cnt = pI2c->RxCnt; // 取I2C总线每次接收数据个数计数器变量
if (cnt < (pI2c->RxSize - 1)) // 检查是否完成需要读取的数据长度?; b3 T4 X! \4 `" X$ X$ }: e
{
MFHI2cBusRdataAck( ); // 在接收之后发出 ACK 脉冲
}, P2 z; Q% q$ [' @/ `
else // 在最后一个字节数据接收之后发出 NOT ACK
{
MFHI2cBusRdatNack( ); // 在接收之后发出 NOT ACK
}: t5 ]. i8 y$ H/ V8 ^4 o
break;, I a9 Q3 C( @1 `4 z4 z
}
case cI2cMrxDataNack: // 接收到数据,NOT ACK 已返回$ u, B1 U8 d4 w2 J9 E: J
{
cnt = pI2c->RxCnt; // 取I2C总线每次接收数据个数计数器变量
pI2c->RxBuf[cnt] = I2CDR; // 复制I2C总线读取到的字节到接收数据缓冲区中0 X1 f( v- @1 a+ v3 f. q, @; y0 H/ c/ p
gbitI2c.Busy = CLEAR; // 清除“I2C总线〖忙〗”标志
gbitI2c.OverS = CLEAR; // 清除“通讯任务时间超时”标志! ^* O! D/ [0 u. }
MFHI2cIntEventOff( ); // 禁止I2C总线事件中断: V, P7 u& k9 U; L9 ~0 D$ j* ~7 K
MFHI2cBusNackStop( ); // 发出 STOP 信号(结束条件),没有 ACK 脉冲
break;2 O- e+ a: s) n" N* u5 ~/ T7 Z
}
case cI2cAllArbLost: // SLA+W 或数据的仲裁失败 c' {. N; n5 p! R9 i
{/ Q" d4 L% W3 G4 x9 M- g
if (pI2c->ErrNum > cI2cErrorMax) // 检查通讯收发作业发生错误次数是否大于最大值?
{
gbitI2c.Error = SET; // 置“通讯收发作业发生错误”标志
MFHI2cInnEventMem( ); // 存储I2C总线的事件和状态存储器值
MFHI2cIntEventOff( ); // 禁止I2C总线事件中断* `7 w( V4 V0 X! X
}9 r Z3 ^$ Z) }; [: q9 h
else$ _3 v8 q% _5 I t) ~( `
{$ R E8 S3 j7 l& V
pI2c->ErrNum++; // I2C总线的通讯收发作业发生错误次数值+16 i/ v( s- Z. X
MFHI2cBusNackReps( ); // 发出 START 或重复的 START 信号(起始条件),没有 ACK 脉冲
}% y: N' l5 p8 |3 J3 o% q' y
break;
}
case cI2cMtxAddrNack: // SLA+W 已发送,接收到 NOT ACK9 t+ o. h& K: w+ c+ v% |
case cI2cMrxAddrNack: // SLA+R 已发送,接收到 NOT ACK% P, ^% n' n) w# o
case cI2cMtxDataNack: // 数据已发送,接收到 NOT ACK, G! q; u* I) P) l
case cI2cAllNoState: // 没有相关的状态信息; TWINT = "0"
case cI2cAllBusError: // 由于非法的 START 信号或 STOP 信号引起的总线错误4 n3 o8 @: }2 j4 h+ _; \
default:
{$ d% D$ D" I3 L# G# T* D; Y
gbitI2c.Error = SET; // 置“通讯收发作业发生错误”标志* @* ~$ i1 U9 t! U0 e- C
MFHI2cInnEventMem( ); // 存储I2C总线的事件和状态存储器值
MFHI2cIntEventOff( ); // 禁止I2C总线事件中断
}
}; c% l0 M) i. c; z* t' W2 ^5 b
INTERRUPT_RETURN( );! n0 X4 Y! K. u3 ?3 h
}