|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器2 E6 `; c- D* D
' `1 \; v6 [ m$ r% Z5 X$ L# f
0 ]) p' c3 }5 `$ M
! @5 q. Y+ ^1 I
, w0 b- t4 Z. o3 d# Y. [5 F硬件上由于24c01的A0A1A2管脚不允许悬空,故暂时的想法是兼容24c02 ---24c16
9 u ~# F/ g7 C& c7 O" Z使用一个dip8封装的芯片插座,A0 A1 A2管脚都悬空即可,换芯片方便
+ U& F2 ` C+ W; h$ u& r软件上24c02地址只有8位,而其他型号是大于8位的,故地址参数使用16位% X2 O: w0 S: s. a* h; z1 o0 [( q7 v) v
256个字节作为一个大页,即largePage,测试芯片24c04空间有512字节
6 Z+ z: c# o, ]( ^$ S! v) \' t3 A% o& ~: ~% y3 V
上代码,求测试和讨论
% V+ s0 D/ B* o2 B1 I' U
# l) v1 K: f' _
, _) w/ U. [; e7 e+ l( y8 F#include "MY51.H"0 p5 l% K1 t" `& @
//转载请注明: 求测试讨论* z1 d/ B6 [/ |
//stc89c52rc,11.0592MHz晶振4 S% G( J9 h5 X+ k/ d6 ?2 a
sbit sda=P2^0; //总线连接口定义
. y1 G! p9 k, J7 e7 g& ~sbit scl=P2^1; //总线连接口定义
4 ?( W1 O! N$ N. Yvoid delayus() //需要4个机器周期,大概4.34us
6 j ]2 e. T3 e" |$ S$ f9 A7 L. L{
l" L1 p; K: N8 w4 l: v ; //晶振频率11.0592M,机器周期为1.085微秒6 C$ J: `: {, j1 e
}
! O" {' C& i l1 j* F9 w evoid iic_start() //启动信号
# s) Z5 m; T$ k4 O; m1 r2 p% y; b{# `9 E" ^% ?6 ~) Y( L" @
sda=1;- ~/ M1 i' U/ v( a- q5 R4 k
scl=1;5 _) z. T( y l5 `/ p# x- f
delayus(); //sda和scl同为高电平保持4.7us以上
- V5 D% M: v2 j9 t1 o) ~! ` _nop_(); //1.085us,共5.78us/ \& O* `1 s) K* ~5 U/ g
sda=0; //下降沿* Z% W. u+ k& i0 Q7 S2 \
delayus(); //sda低电平保持4us以上 ,这里是4.34us满足要求
' Z+ n" g ?) ^$ Q5 u6 f( O}
9 u7 l" t1 f+ [. |& E9 Q: K2 P6 yvoid iic_stop() //停止信号
. A. `* V" ]. |/ H{1 R5 O' N |9 e# D" g( L" ?* }
sda=0;_nop_(); //准备状态
% V U1 F- T& I! A3 e) }5 x scl=1;
: X% M+ U" R4 k* k- n2 J4 L delayus(); //该状态稳定时间要求保持4us以上! O" g. X: H, i( _% F
sda=1; //scl高电平期间,sda来一个上升沿
& ]# c) R7 `% Y/ F/ a delayus(); //sda保持4.7us以上,4.34加上函数返回时间大于4.7us
4 l! v H! \5 i/ V, z! L //注:此时scl和sda都为1
9 a8 J# m; n$ ]7 e}, \1 _ i/ \! R& \
void iic_sendByte(u8 byteData) //mcu发送一个字节
5 Y) Y" r! C" y- n L{! l6 B( L) l8 a* h
u8 i;
6 g' ^* i+ N7 Q: @$ l: Y7 @# J+ J: M u8 temp=byteData;
. w7 t2 c0 H" s for(i=0;i<8;i++)
2 ]. A0 E1 x0 x; V {
$ a, D$ x3 L9 S/ s temp=temp<<1; //移动后最高位到了PSW寄存器的CY位中
+ a' p9 |# v! s1 W scl=0; //准备
. k1 f+ e) L1 _7 O- ]6 I) s _nop_(); //稳定一下
9 s+ M4 M+ d& X3 b n$ y sda=CY; //将待发送的数据一位位的放到sda上
E$ K& g$ J9 n; i2 } _nop_();
: M- D+ Q% ]! j8 \6 J9 A4 f scl=1; //每一个高电平期间,ic器件都会将数据取走
I( J3 C- |! D _nop_(); 9 `2 Y; b9 k7 N+ B3 b9 R! e1 n4 E
} C W. g; }* \
scl=0; //如果写成scl=1;sda=1就是停止信号,不能这么写
& S1 K3 P4 t" I: Z+ W. d2 u _nop_();
2 P: A7 f* c" z, c; v sda=1; //释放总线,数据总线不用时要释放! c" H% f9 p1 h# L9 {' U6 m
_nop_();2 n4 S4 k9 b+ r# a' d3 L S
}
7 q, U4 o& t2 ~9 Q+ J* Iu8 iic_readByte() //读一个字节
3 S) [, x+ l- s7 U* t7 S8 I9 S{2 i$ o/ e( u/ Z5 C% ]6 T
u8 i,temp;8 y. e9 g% I6 [/ P4 T
scl=0; //准备读数据
. _, C6 B% V) J& M5 [ _nop_();/ [3 @0 a( S- h4 Q( G2 D& E
sda=1; //释放总线1 L$ p5 Y! t5 i, L& h
_nop_();( C8 x2 z- v) z
for(i=0;i<8;i++)
. E8 M4 i+ G# \% P |. T {
}1 V' I# Z9 H0 ]1 ]& T8 C" W scl=1; //mcu开始取数据
0 }; ^" {( b0 e& E6 f delayus(); //scl为高电平后,ic器件就会将1位数据送到sda上
% u; D, p: P% v N( h5 b$ P //总共用时不会大于4.34us,然后就可以让mcu读sda了) N! l) E! [, y# X; O0 w1 N3 t3 s0 o
temp=(temp<<1)|sda; //读一位保存到temp中
. W% l& ~2 a) l scl=0;
$ O1 Y2 X$ U5 L1 C7 o% v/ M! P delayus();
+ m' V2 A* ?* `" y9 h, M3 | }) y; I! z/ F. o% U# A8 q3 x! s' Q
return temp;3 t5 v$ d+ G- u" H
}
: W7 u6 K% K2 G+ B+ U' ^bool iic_checkACK() //处理应答信号4 W( f% V9 C- s: z
{5 [1 Y, |) O1 a5 N4 N9 X
u8 errCounts=255; //定义超时量为255次: t7 F8 N- ]( Z r: S+ _9 Z; \
scl=1;2 u) y9 H6 d0 U2 z) u' u9 T7 [
_nop_();
* `& x& U% }7 d - ~. R$ G" A' _0 U% I2 s
while(sda) //在一段时间内检测到sda=0的话认为是应答信号7 ?( O! l; {( C4 _' n. ?
{
x& n/ }0 ^. z) a if(0==errCounts): q' a: ]: X8 J' `( J9 r2 N |, ~
{; @* P- T' E) {6 Q( I5 i
scl=0; //钳住总线: |5 `7 r$ }! G8 u: J
_nop_();
?1 i% l8 d( H' n/ j% g+ c return FALSE; //没有应答信号
( a+ b8 L$ |0 K6 O9 o4 ] }# ]: r1 u) u6 P* F& @
errCounts--;; j; j5 g6 l/ c2 y
}
9 g8 K7 V* @. h+ ? scl=0; //钳住总线,为下1次通信做准备 $ R$ s" j- b! M# g* x
_nop_();" t* m6 x7 d& K0 o9 a( V+ `* x
return TRUE; //成功处理应答信号 |1 x- J; I1 ?2 |
}9 P( y4 M% E& D; {* e. ^3 }
void iic_init() //总线初始化
# h& N0 l* O: j6 L{/ W6 F" k' X+ p# H6 h u/ Q
scl=1;0 L$ o" H7 T8 m! d" l4 d
sda=1;
5 a; B) `- A" }- F; B! r6 n& @* y# E delayus();0 i& b/ k5 J6 z+ w
}9 N/ l( k: @. L- { [
void iic_sendACK(bool b_ACK) //发送应答或非应答信号9 ^0 R- C+ o* O' x7 G
{$ o/ _7 l( z5 A# \% \
scl=0; //准备
9 s! ]) @3 e: w& U, d8 | _nop_();" K! C. o% X3 }9 h
if(b_ACK) //ACK 发送应该信号
6 ]" M% F7 E# w& a- p8 Q5 }9 _' T {0 S2 ~: `' S- |3 Y
sda=0;/ y( H4 Z! o* x7 F. o
}6 C# v, W$ b2 p
else //unACK 发送非应答信号
6 I& n6 O6 i5 j5 x {
; g" \7 a. g! T1 L sda=1;3 v+ h8 |7 A* p* ~ [: ~1 o* v
}
* E, t6 S7 F6 }2 A/ } _nop_(); I9 p$ Y4 e3 O( z: m: O- J5 d+ m
scl=1;' G6 Q' J6 O4 v0 x2 r
delayus(); //大于4us的延时+ X" {: D" |6 J, O
scl=0; //钳住scl,以便继续接收数据 + T: Q8 N) {# a7 U7 U3 f1 k l# o5 d
_nop_();4 g X& P6 R6 i1 a6 g
}
2 b7 ?4 C) p% ~! `) Cvoid AT24Cxx_writeByte(u16 address,u8 dataByte)//向24cxx写一字节数据
; D- B1 e" C6 H{
" V7 O. i" z2 h8 p2 H5 X u8 largePage = address/256; //24c04是512字节(寻址范围0~511),largePage最大值是1! h* E4 p: S7 e6 T4 T4 W( S
u8 addressOffset = address%256; //largePage=0的话地址范围是(0~255)7 I+ X, r8 l4 t U* S9 B( E
iic_start();4 q( \& E" g! U7 f3 V
iic_sendByte(0xa0|(largePage<<1));//控制字,前4位固定1010,后三位是器件地址,末位0是写2 E+ ]& ^, k5 ?1 y
iic_checkACK(); //mcu处理应答信号- N9 t( O/ |+ r6 {; U- d7 N6 b- w
iic_sendByte(addressOffset); //指定要写入的器件内地址在 largePage块中的偏移, ?" }% k* }! u' J' d
iic_checkACK();" I2 ~& N2 e2 i+ S8 o+ E: T
iic_sendByte(dataByte); //写数据1 D& G! N1 F) O+ O [/ v
iic_checkACK();3 A- e' ~) |. F; h
iic_stop();) O g2 u, N8 _' A) e
delayms(2);
# v) L3 `3 ?, _ //按字节写入时,24cxx在接收到停止信号后将数据擦写到内部,这需要时间
! z; W/ z" [7 R7 V7 e9 Y: p- } //并且在这段时间内不会响应总线上的任何请求,故让mcu有2毫秒以上的等待
% Q) i* [. e3 _* k- `: ^}
! {, F/ t( w. o z9 j$ R) |void AT24Cxx_writeData(u16 address,u8 numBytes,u8* buf)//写入任意长度数据(最大256字节)6 q+ z6 `% C; I. p I& r5 M
{
( R' r* t6 `1 R, \" V) C% U. R9 T while(numBytes--)
# n& V* w7 H; s, n& c! R {
/ C5 F$ a" N) d, }% u* G) @- c6 z AT24Cxx_writeByte(address++,*buf++);; m% I7 u$ a; {' R* y1 D
}
+ C$ h; _& i& P$ W+ D}
9 p/ F* ]7 f6 U# v2 t! @, z1 P. Pvoid AT24Cxx_readData(u16 beginAddr,u8 dataSize,u8* buf)//读取任意长度字节到缓冲区buf中; d! p2 S; B3 {9 H7 b+ k, B a4 y
{4 E* K5 c! {4 H
u8 largePage = beginAddr/256; //计算largePage,256字节为一大页+ P7 Q) k* N& Y. x, W, E2 \/ ^
u8 addressOffset = beginAddr%256; //计算相对于largePage的偏移
8 t$ ]7 t. Q0 x/ a' n iic_start(); //起始信号3 }5 [' g8 ?" C% z6 O% r3 o* e
iic_sendByte(0xa0|(largePage<<1)); //控制字,写
5 A: _, J P+ P iic_checkACK(); //处理应答信号
+ ?8 G5 r! i( g9 n; I3 N iic_sendByte(addressOffset); //要读取的目标地址偏移
/ n+ m; o+ p! N9 I4 R iic_checkACK(); //处理应答信号 / A. i/ A) c. `5 [
iic_start(); //发送起始信号
; w' x! ]6 [: Y- _% A, X iic_sendByte(0xa1|(largePage<<1)); //控制字,读/ m( P4 x7 [6 G# F& D
iic_checkACK(); //处理应答信号
" }( x! f6 K' r* _7 I while(dataSize--) //读取dataSize个字节,最大256个字节8 ^& v0 d& t: ]' N
{ //dataSize用u16类型会暴掉ram的
; r; k* ]) B" ]8 g3 }+ s *buf++=iic_readByte(); //读取一个个字节并保存到缓冲区buf中
7 Q6 @+ H' N/ T; g iic_sendACK(dataSize); //发送应答,当dataSize为0时mcu发送非应答
: H$ F, L3 c+ p1 u& l }# z' t9 F* X$ j- I2 e9 {4 W' M
iic_stop(); //发送停止信号
! f+ I7 u; E; M8 i0 s2 p}) Z7 g+ s( N' J1 i
8 w9 {( j6 E6 r. p* F( Q% \1 }1 M$ W/ D7 R# S
void main()//测试
8 R1 o& i# e1 ]5 k4 A X{
4 o5 \; t$ z2 h/ {# H1 Z8 i5 ` u8 buf[3]; //接受数据的缓冲区
) b- N- i! x# B v7 |- x1 T u8 arr[7]={0x06,1,2,3,4,0x55,0x33}; //待写入的数据
( k( B4 R* t2 h/ \! P2 F2 {
( Z( A o" N# z. I iic_init(); //总线初始化8 I1 ?/ @# e! ~5 D6 b# C8 c
AT24Cxx_writeData(0x00+256,sizeof(arr),arr); //向指定地址处开始写入7字节的数据
/ E! K2 t! I; A) x$ Q0 w# K4 T P1=0xff; //调试代码,用P1口的led显示4 P1 v, p# r; q' X0 G
delayms(1000); //调试代码
7 J. f7 W6 v: u2 m; t AT24Cxx_readData(0x00+256,sizeof(buf),buf); //从指定地址开始读3个字节0 N( i' Y7 k8 G/ r6 r; o( [& g. R
P1=buf[2]; //也就是2 //led灯显示数值' j, B& c; }+ k8 h( V
5 {7 C: c, a/ E6 R
while(1)
; k, B" |! Q2 p; O {/ T* ]) {3 i3 ^' R! M1 O
P1=~P1;
9 {& L! A5 Z! ]' n3 | delayms(500);
: ^9 a+ \3 x* h+ O1 H# c, q } . ^6 W$ w# v% {8 P- ~
}
, k( Y" U& O+ g& K' c! `7 w+ O; `$ h; G4 f& Q4 E
5 f! g+ l2 M' {$ c6 E6 a1 A
+ q3 c- [9 R/ R6 p. T
6 P1 q0 P, \, Z8 _' T8 I; M# q0 ]( }* r5 W% q
( {8 l L; H+ \6 R; \ j; D
' d- Q5 J0 R' O* J' H( ]5 t7 c) i% v
//my51.h中主要用到
4 y. Y- i) a& B; Q( S( B, O% {#include<reg52.h>9 s% s. [: E8 i5 v
#include"mytype.h"
8 j" L0 ^( Y# x. Z; K3 k6 gvoid delayms(u16 ms) //软延时函数
) z* H9 m# _2 V' y1 ^$ F* l/ @{
/ x! y0 E! S" N9 X8 g5 C+ ^3 u. M u16 i,j;
/ N+ K5 W( T" b6 I for(i=ms;i>0;i--)2 Q; l; M: } z; X$ ~
{# u7 q: L1 }6 p/ ]" z
for(j=113;j>0;j--)) m6 Z" X- O' o/ x
{}$ ]" p+ o9 ]0 F/ A8 J
}& i0 Y0 P H- ^
}
# V: R9 D( o& r- o7 O/ m) S7 r7 y
2 P& j/ j6 a, j- @$ p1 r7 R t% G# j
4 G1 L+ ^) X$ M! S0 s# ^
+ h% j1 L3 \$ d; h! Z L$ U* V+ T; v# t- P
; E( e7 |2 r; T* _
对代码进行了改进 5 G9 a0 _3 s; b! h8 U# J
去掉了在写数据时的 ( s5 r7 X/ V4 u7 h, ?9 p
delayms(2);* @& K, i8 Q, T- Q
这句软延时代码低效 ,而且没有保障 0 g; P; {/ b2 h
改成加一个检测函数
( q7 I) v6 F8 u7 Dbool check_icWriteComplete() //检测eeprom是否对内部擦写完成 : z/ Y/ z5 d* n
{
$ { @# z: s" l5 R7 d iic_start(); / O8 J$ B& p) T0 B' z7 Y
iic_sendByte(0xa0);
" @7 y' z" G8 }3 T return iic_checkACK();
5 y4 a8 B& U: ] Z; v" G, D}
0 V4 R; X% E: Y6 [, s0 t
( p; H6 n: h1 x |
|