|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器
+ O* Y- B/ v, W1 _
6 m( Q8 J- u! Z6 y2 J$ v' g" D9 [
- W! |2 l+ ~& k& n: O4 |: v
" W1 d! }4 `$ t" J/ p( z3 _硬件上由于24c01的A0A1A2管脚不允许悬空,故暂时的想法是兼容24c02 ---24c16
7 w9 O* j4 W( H5 i1 I/ B! X使用一个dip8封装的芯片插座,A0 A1 A2管脚都悬空即可,换芯片方便
+ G5 z" B' i( Q+ e" \7 y+ v# t软件上24c02地址只有8位,而其他型号是大于8位的,故地址参数使用16位2 W+ Y' e6 Z+ Z9 b
256个字节作为一个大页,即largePage,测试芯片24c04空间有512字节% O9 g; O3 G- I% d8 [
! _" c* S" h3 I! t! C5 S0 e
上代码,求测试和讨论
/ M2 e( P$ z: P/ {5 ]) [: K0 e8 p0 c
: a+ y/ H- o) O0 K7 o: [ \2 ?
#include "MY51.H": i2 \0 S+ H% L- D& b: |# B. ]
//转载请注明: 求测试讨论# a& @. _9 J; c0 @- T( W" O% V' ~
//stc89c52rc,11.0592MHz晶振
% M7 O) d3 S, f- zsbit sda=P2^0; //总线连接口定义
3 d! o- U* r& V+ k/ ^sbit scl=P2^1; //总线连接口定义
& i4 a8 N3 O7 \6 k3 d- K, ]void delayus() //需要4个机器周期,大概4.34us6 G- R, b" m( A* b* t
{" j) D$ t- x0 `
; //晶振频率11.0592M,机器周期为1.085微秒* _! B$ |& z! T9 n4 R1 e7 l
}4 ]6 c* M, a O
void iic_start() //启动信号
4 s6 \# t0 r8 J7 K$ D0 c- J+ T+ X& a{
4 s! @0 ~3 \2 a) V0 [ sda=1;
! q7 R! }" \7 y0 t7 F scl=1;2 N) g4 X8 W4 q
delayus(); //sda和scl同为高电平保持4.7us以上
) ?/ H5 V' @* Y" O2 h2 I- t _nop_(); //1.085us,共5.78us
- O F% G( s% t5 K sda=0; //下降沿8 T* E4 L5 Q& |/ d: a/ Y# S
delayus(); //sda低电平保持4us以上 ,这里是4.34us满足要求! s5 h/ m' f0 N2 X" ?% W4 i5 |+ M
}; j, v* M# @5 }7 {, F
void iic_stop() //停止信号
/ d2 c0 x% Q2 u- t6 S0 a I{) }) J2 r/ v A5 j/ @3 N& x5 g' b( U
sda=0;_nop_(); //准备状态9 G: b1 C8 r9 V& j) [# m$ b6 B
scl=1;
. p% }) h V. [" F$ z M/ o delayus(); //该状态稳定时间要求保持4us以上" X- G% V0 t. I0 K! P: H
sda=1; //scl高电平期间,sda来一个上升沿6 m. h0 p% B3 H: w
delayus(); //sda保持4.7us以上,4.34加上函数返回时间大于4.7us- Z) v" k8 {3 n3 @
//注:此时scl和sda都为1
* i- |* A; @+ s0 L' L}3 b6 c4 H8 c$ [, A+ s, O
void iic_sendByte(u8 byteData) //mcu发送一个字节
$ R$ U5 ~3 Q T# b6 R( Q{
. H! y; ~: s- w u8 i;/ ]# W* s1 O8 |5 }/ W
u8 temp=byteData;
. z- c+ B8 W/ K+ H2 ~7 I for(i=0;i<8;i++)
* X t, ~! o: q0 {: v, X0 G/ C6 z {
6 p7 p7 d/ U& Z) K, S1 @ temp=temp<<1; //移动后最高位到了PSW寄存器的CY位中3 x" @4 C( N. |
scl=0; //准备. A j u" K$ V( M
_nop_(); //稳定一下
2 A1 B. X) W+ c" m; s- }5 `) y& T sda=CY; //将待发送的数据一位位的放到sda上
- s7 T O, s: h. H' j+ {" M _nop_();
" ]1 y/ M: N0 u2 f scl=1; //每一个高电平期间,ic器件都会将数据取走# E3 G8 l2 p! n9 e! w4 L8 L! U* J
_nop_();
4 e# q" W, L! Z }+ N5 A9 A0 J) N7 j$ ?% r
scl=0; //如果写成scl=1;sda=1就是停止信号,不能这么写
- D! v& p; S' z4 ?( z: l: N _nop_(); X7 X# ?/ j* u- B* a! [
sda=1; //释放总线,数据总线不用时要释放
' U3 f- v" m w6 G% q M1 | _nop_();
& c n3 o; m% D8 V( y. q}9 |9 o$ y: g, q1 e
u8 iic_readByte() //读一个字节
: Z' {! j9 u: A5 Z: _ F; Y3 E+ Z{8 u% N1 f# S8 `0 A: j+ Y# ?
u8 i,temp;
* y: Z1 i P* G% ]# I. E scl=0; //准备读数据
* z/ J7 M6 Y& h# E T+ U' z0 I _nop_();! m0 Z8 `8 h) d2 i1 D c
sda=1; //释放总线
. S7 @5 O6 U X& [3 M _nop_();
) X! D7 o) n: N& r9 @7 [ for(i=0;i<8;i++)
$ _) `( H; O" t1 ^ {
* h- j, p6 H E1 m scl=1; //mcu开始取数据; _& r: U" Z- E( _" x
delayus(); //scl为高电平后,ic器件就会将1位数据送到sda上
" `- K, d3 ?/ I- @9 e //总共用时不会大于4.34us,然后就可以让mcu读sda了
& d( @+ z# d N9 ? temp=(temp<<1)|sda; //读一位保存到temp中6 Q: @; L w% f4 c6 }
scl=0;
- M# |* `4 G5 p, J2 Q) n) [ delayus();
0 w3 }) f5 i6 V; }$ V }
0 {# ^+ Z% w% P! I5 M/ l y return temp;
- X+ V+ r E1 i6 }3 M6 B) {5 W: c: J}, Z% E m+ C1 j6 n+ `: k8 ~
bool iic_checkACK() //处理应答信号
* J2 ?7 e1 z1 m+ s{. r0 i9 I! J) H
u8 errCounts=255; //定义超时量为255次8 I5 W- Z5 w$ r+ t$ n% x N7 U0 k* ^
scl=1; T$ K4 p( {# X T1 J, K6 _
_nop_();, g* V+ k0 M9 Z Q. E2 z
* S" g$ [% x! ^2 F. _: h
while(sda) //在一段时间内检测到sda=0的话认为是应答信号- U; ^. P% v8 I) a( L
{ 6 j+ L5 w7 y! [
if(0==errCounts)1 K' h/ R$ E4 X% V2 W& s
{
, l: w! g' b& C3 ~1 l) N# u scl=0; //钳住总线9 {/ `9 W( n6 N* q1 E
_nop_(); s# V Z/ J- }6 S
return FALSE; //没有应答信号" }0 n* J* q1 n4 p0 ^
}
' u: ~$ q' j# |( Y( p/ l3 j8 D' J9 E errCounts--;
6 B, B+ n2 @3 N* { }
; F/ C8 ~7 d+ D8 Y, b scl=0; //钳住总线,为下1次通信做准备 ! C) P& F! [* Y {
_nop_();# `4 z: D! F$ T I* |8 W
return TRUE; //成功处理应答信号
- h- Z9 e# ?+ j) R2 Z}
+ [9 B' Z4 n9 g9 M3 Mvoid iic_init() //总线初始化
5 |8 k/ I& X" I6 a5 o{- m9 [& c/ g3 `" u
scl=1;3 u7 O; Z) U1 `7 G1 p3 Z( S
sda=1;& }- M' T* p& O% A" Q$ g$ F$ ^# U0 |' e
delayus();
- }( H6 k- R; w _9 m( R}
5 x" H3 I6 S$ r4 wvoid iic_sendACK(bool b_ACK) //发送应答或非应答信号
6 } Z' C& e" Z, g( l9 f' x) r{
4 t+ C8 j" T/ o, b! |* S! b7 F/ F* b scl=0; //准备# {9 ?' X3 Y ?; O
_nop_();
& v) \5 A$ y6 ^9 R1 c' e9 a if(b_ACK) //ACK 发送应该信号
5 e+ h# O0 u! A4 t& K. [/ H% R# I' f {+ q- X" E% A" F; v, H1 j
sda=0;3 i+ |5 Z; k! y- |
}
% W4 Z# X( K% H9 e+ v- N& N( R else //unACK 发送非应答信号6 C- d, }/ k4 t( F: A9 ?
{. O- H, c* M& |: j
sda=1;
' n$ O% @2 D0 t7 [8 P } ?& {# n, }; O! `
_nop_();
: |6 \8 e( M( E1 ]- s scl=1;4 l; m1 _; N: Q; z8 Q- F9 T
delayus(); //大于4us的延时9 c& n- {; y" V! a' ~
scl=0; //钳住scl,以便继续接收数据 9 F, m! A3 c' J9 t
_nop_();" e% g: O* r+ D2 ?, Y% L5 {
}
) R% S$ U5 z* K! `% S: t7 i; E% r2 Ivoid AT24Cxx_writeByte(u16 address,u8 dataByte)//向24cxx写一字节数据. p, u/ C. o# ~- T
{- [( P2 b# s& I/ M: R& ]
u8 largePage = address/256; //24c04是512字节(寻址范围0~511),largePage最大值是1
J3 i0 k1 K! d8 A u8 addressOffset = address%256; //largePage=0的话地址范围是(0~255)9 \9 \9 `4 q+ N p, B
iic_start();2 z r& X0 G* d9 ^) {( j
iic_sendByte(0xa0|(largePage<<1));//控制字,前4位固定1010,后三位是器件地址,末位0是写' J( S& o1 D: O6 A- e+ r
iic_checkACK(); //mcu处理应答信号# G) E+ h% \. S$ G) w
iic_sendByte(addressOffset); //指定要写入的器件内地址在 largePage块中的偏移
8 ^0 r1 W1 a. X* V iic_checkACK();7 V ? V8 u0 w$ p5 L' K
iic_sendByte(dataByte); //写数据* @6 I6 a' \. P! ~9 [, ~5 E8 y
iic_checkACK();8 z; D- N" w Q4 L% J- w
iic_stop();& A% M* @' g) P6 t. Z4 m; m0 r1 ?3 x
delayms(2); ! |* R9 `% v. d4 s) B5 C
//按字节写入时,24cxx在接收到停止信号后将数据擦写到内部,这需要时间. h; |. H3 l0 q" j& [
//并且在这段时间内不会响应总线上的任何请求,故让mcu有2毫秒以上的等待
- r; k4 b' T* N# V) M}$ ~6 |' o \" P
void AT24Cxx_writeData(u16 address,u8 numBytes,u8* buf)//写入任意长度数据(最大256字节)
& {+ h4 s; ?/ t5 ~& m9 I$ i{
/ w7 F: k U# `7 t- | while(numBytes--), y# }6 Z* y0 o2 n5 L$ O
{' C, n: u' O2 j. O2 i
AT24Cxx_writeByte(address++,*buf++);) O/ B7 w+ x" z4 |6 k9 E
}
7 t( x4 Y* ?+ l+ f( y+ H+ J}" j' l. g. J$ b; _) `% L( R
void AT24Cxx_readData(u16 beginAddr,u8 dataSize,u8* buf)//读取任意长度字节到缓冲区buf中7 E* ~9 V2 [3 @0 M0 H
{( R8 m) r \. n6 d
u8 largePage = beginAddr/256; //计算largePage,256字节为一大页
1 b9 B2 q' B3 D4 e( @9 n u8 addressOffset = beginAddr%256; //计算相对于largePage的偏移+ C1 E/ R% M. m% k
iic_start(); //起始信号8 z+ }" s* w6 c$ S/ t
iic_sendByte(0xa0|(largePage<<1)); //控制字,写
* Y+ `# {: ?8 [7 F# w! i9 B# y0 | iic_checkACK(); //处理应答信号- ~# _* Y+ S5 V$ t/ t5 B, s; O9 }
iic_sendByte(addressOffset); //要读取的目标地址偏移
1 L R+ }! |; H) j iic_checkACK(); //处理应答信号
. W# ]. J- I# Q* T- ~" V9 Y4 I iic_start(); //发送起始信号
4 ]9 m# G4 i7 f8 l iic_sendByte(0xa1|(largePage<<1)); //控制字,读' z. i: ?4 N7 U/ l/ [- r @
iic_checkACK(); //处理应答信号
( _2 O7 C, b4 ]' r+ ~ while(dataSize--) //读取dataSize个字节,最大256个字节& K% V" n2 e: a
{ //dataSize用u16类型会暴掉ram的
! A |9 O5 T" ]8 g* t5 | *buf++=iic_readByte(); //读取一个个字节并保存到缓冲区buf中; m4 Z3 L2 G5 r1 ^ A5 `) J
iic_sendACK(dataSize); //发送应答,当dataSize为0时mcu发送非应答( k3 R; q: D( |: k; U! N
}4 }9 n5 |, l; X+ q% ] C
iic_stop(); //发送停止信号7 P) Y1 w1 U# d' H3 ^; p1 Q$ _( g
}
" V x/ M8 _; @9 h
$ q* t2 K. C7 V# O# H; m- l$ w
Z+ x8 T2 G, U( s$ q( _! H# K6 fvoid main()//测试0 z& z' l7 H7 ~1 c
{
3 J/ l# K9 O0 o3 \8 ] u8 buf[3]; //接受数据的缓冲区) [1 u6 D1 ?$ x- J
u8 arr[7]={0x06,1,2,3,4,0x55,0x33}; //待写入的数据
. Y' |4 T. s' C( @9 p. ]: c
6 M$ }3 }2 X ` iic_init(); //总线初始化8 L/ Y" `/ j9 t4 n
AT24Cxx_writeData(0x00+256,sizeof(arr),arr); //向指定地址处开始写入7字节的数据. ~9 [( o+ u9 d9 E7 |; U+ s
P1=0xff; //调试代码,用P1口的led显示5 o1 P% V8 F( [0 P7 ^/ f' i J( }
delayms(1000); //调试代码
|! y1 h6 v0 V- J/ K AT24Cxx_readData(0x00+256,sizeof(buf),buf); //从指定地址开始读3个字节1 ^9 o/ F4 L; L- D. h+ R* ], l
P1=buf[2]; //也就是2 //led灯显示数值* R( C. I- q9 p3 g2 U
2 k0 Q3 V# |+ `7 x, Z8 X2 s) x, l( y
while(1)
% d# x) y$ i) v7 S1 `5 h8 D9 R9 B {
2 i2 q) u- h/ ^* @# C9 k% J% o P1=~P1;* |4 Q' D8 L2 C" ^! H a
delayms(500);
/ ]. d7 G6 n4 E( b- s% L } 7 e9 u6 z9 y9 G2 v d! @" t
}
4 M, G/ K* ^, D& I, [7 C6 C7 D2 I- T. c6 L! A9 q2 X- a( T2 {1 i0 T
) }6 m6 G+ \; v! `1 t3 {) ]' S
) g0 q1 ^" v# u5 x% l$ `: I# E D
1 d- e1 k( F# o% f9 S& ]9 o
5 n, i' B, S4 u8 w2 e: f2 U. q
" L( @" F( i* |, g8 W) [+ e7 T) }% j" t2 u" m- V, @, `
//my51.h中主要用到5 f. ~- i' n7 _/ Y$ w' }
#include<reg52.h>/ J4 w- u" U& T1 g' k
#include"mytype.h"
- H: F+ H, X, x( f ivoid delayms(u16 ms) //软延时函数
) g$ e9 B% q5 ?8 s6 _{
7 U/ B: S$ W1 V3 o* A! v u16 i,j;! F- d' N7 E" j% y3 B3 f
for(i=ms;i>0;i--)1 }: J5 }8 l; {
{2 b: \& s& n) N5 V8 S0 V$ i+ M
for(j=113;j>0;j--)) O( r1 ~& I2 m% L8 s
{}
/ ]8 ~/ |: ]5 I+ O5 K } o/ W* N$ r; P# {# {6 M
}
- m* r \3 v0 }5 t. [. W# s6 b$ q7 p* {2 D! O' g9 c$ T' X5 b, j
2 `! W' d9 O( ^" e" ^+ I+ ^; e. ^# H1 S$ x1 S$ [4 S% ]
0 C B; |7 j2 {& [8 V; H9 `4 {) P
- V8 n9 J$ |' G/ r5 n L
( E3 \9 L6 {; U6 D' h8 }0 s3 {( `对代码进行了改进 2 V2 m4 w# j9 H4 U4 _& ?' M
去掉了在写数据时的 7 |" r# L! \, X% c x
delayms(2);# V) s& q- F! @
这句软延时代码低效 ,而且没有保障
3 ^& f" t, a- a( ?3 R& W4 g改成加一个检测函数
. U5 B' A8 e& R, E) [bool check_icWriteComplete() //检测eeprom是否对内部擦写完成 ' j0 s% ~, \' k1 j" d. L+ T
{ % L, R1 H/ n: Z) `0 z
iic_start();
# O" t; W' L. h' \1 Y8 t iic_sendByte(0xa0);
; O9 w/ I2 ^$ Q1 [9 K return iic_checkACK(); / ^4 _2 l/ P0 z6 q: f0 h R
}
& A+ ?6 Y+ P3 l( d; |0 ]. A# f# k/ f
|
|