|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器
1 E% Q1 I/ o: t N6 C& M$ X# [
2 I7 R6 @, C t) [0 k# Y; K
4 y. S1 [* x+ G' h4 ]* [/ y. N- h# }2 W5 W8 F3 P) v2 C
% D+ Q/ i- Z( D/ @2 K3 z硬件上由于24c01的A0A1A2管脚不允许悬空,故暂时的想法是兼容24c02 ---24c16& S5 ~$ ]6 P8 g( A; W" d$ ]
使用一个dip8封装的芯片插座,A0 A1 A2管脚都悬空即可,换芯片方便& l3 Y; k6 \* `, o0 @. W9 H
软件上24c02地址只有8位,而其他型号是大于8位的,故地址参数使用16位3 l9 w( \. O C+ r( H9 T
256个字节作为一个大页,即largePage,测试芯片24c04空间有512字节- b8 P0 e! @& a! V/ f
- ?8 Y$ y3 b6 W6 b1 F J+ R% q上代码,求测试和讨论5 u5 E# K8 W3 [3 N
* w; {; T+ F" I; r- h0 ]7 @6 m+ j+ S$ ^5 V6 `7 ~: m6 i& S% {. r
#include "MY51.H"
' i: k; l! {2 Q: }; \//转载请注明: 求测试讨论
" s$ k @ d1 u//stc89c52rc,11.0592MHz晶振8 ~! t2 m P P) W) T; @& U8 C
sbit sda=P2^0; //总线连接口定义
; g$ a$ O' e: d. n0 A, i lsbit scl=P2^1; //总线连接口定义
+ y c0 [* ^4 j9 k# M: Nvoid delayus() //需要4个机器周期,大概4.34us6 ]# C9 p5 } ~. }; d3 f. n
{ S# Y. G# C3 u$ ^1 t( W
; //晶振频率11.0592M,机器周期为1.085微秒& t) u; u' H- {9 D/ v" k
}' s. P0 b* ?7 }; l8 \
void iic_start() //启动信号
( w, c& J7 u9 @$ v{
% A( s5 r6 c4 f n; f' U* C$ o! u sda=1;- p) t) ^9 Y( l$ B6 J4 Q" e
scl=1; d0 A* O, d$ L/ e+ P5 D! F+ `
delayus(); //sda和scl同为高电平保持4.7us以上
5 L- }! n7 G3 z6 p _nop_(); //1.085us,共5.78us
% U! q/ \1 X1 @6 u sda=0; //下降沿
( i8 A. X G3 T' t* \, R delayus(); //sda低电平保持4us以上 ,这里是4.34us满足要求
% S) x( i: P/ `9 s}6 G, E: B+ W" n5 h& r0 _5 D
void iic_stop() //停止信号; ]7 M `3 y* E$ j6 w
{" N% Y: H2 x& k! {4 ~
sda=0;_nop_(); //准备状态
" g( Z6 S" i( J; P/ E( M scl=1;
# B6 o5 P) ?: U n delayus(); //该状态稳定时间要求保持4us以上
4 K* `' o' s$ g- R sda=1; //scl高电平期间,sda来一个上升沿
4 |* G" r. S% }* R0 I& w delayus(); //sda保持4.7us以上,4.34加上函数返回时间大于4.7us
0 e) u) f' R" F: W* B% z+ B: @1 B/ L7 T //注:此时scl和sda都为1 + j/ i+ `6 S9 R5 e0 L
}5 {; e' k: _ a/ m( H
void iic_sendByte(u8 byteData) //mcu发送一个字节
0 B2 ]) @ c0 F, p{
9 G0 @& k! y. h) u. } |% P u8 i;
4 L" ?7 F: K2 Y2 j0 I! r u8 temp=byteData;+ t6 O3 E3 X ]& D- r% W( u) r
for(i=0;i<8;i++)5 N) S) h( U/ h- N
{9 N" s" o5 E' |- Y2 D$ |7 k! T
temp=temp<<1; //移动后最高位到了PSW寄存器的CY位中$ w! w1 g5 a1 }4 V! o v. u5 d L
scl=0; //准备
9 v2 P" I) A7 N1 Y+ N0 ?1 c- } _nop_(); //稳定一下3 w# t) V" q: M& C- u& F
sda=CY; //将待发送的数据一位位的放到sda上
6 O) a* e6 L. z0 ]' a% Y: {$ L _nop_();0 l5 d5 |$ n9 |% o& b: |2 z
scl=1; //每一个高电平期间,ic器件都会将数据取走. T' K( W) Z" P1 [1 r( d
_nop_(); & N! P* a& P* T$ y' L
}
6 W- H: D% X& Y" d scl=0; //如果写成scl=1;sda=1就是停止信号,不能这么写& ~; a b/ }* t( B. n
_nop_();
9 k9 |* J, p; W. f4 H# k sda=1; //释放总线,数据总线不用时要释放7 |# v) `) P1 E
_nop_();
8 ^/ x# q3 \8 H9 q" O}
7 }2 L; l/ G- L1 lu8 iic_readByte() //读一个字节
: ]( ^' v( `0 O' O* c, \9 {: }1 }) c{9 k, V6 A! _! b$ t! O. V
u8 i,temp;8 q9 X' u$ ?+ n3 f0 D4 q$ c
scl=0; //准备读数据
! h B# p" Z- u$ b _nop_();! q% v! p m+ H2 p
sda=1; //释放总线
- ^) q7 H+ w+ q% q _nop_();" |2 v0 n4 m& s9 e2 P6 j, G
for(i=0;i<8;i++)3 I0 K2 E5 x' j3 K I) z- l6 L
{
+ C7 f0 c8 x, b6 d/ E1 g scl=1; //mcu开始取数据1 L, W! R, i) F( ]0 A
delayus(); //scl为高电平后,ic器件就会将1位数据送到sda上2 z1 _$ W1 V, ]0 e8 o
//总共用时不会大于4.34us,然后就可以让mcu读sda了
. F/ i+ q, e2 R4 o- Z/ f temp=(temp<<1)|sda; //读一位保存到temp中! O' x" X# @6 C( A% g
scl=0;
8 n! ~0 b5 Q% @1 ]- @" h( v" u) ?, \ delayus();
! s8 j1 C8 Z+ \ }
& D0 U E( y* [8 [2 U; ^1 J4 ^) C7 Z! M return temp;0 H( f+ I& d: C" ?, w$ a/ y
}, Z. j6 _" q- W3 D4 E9 D5 s
bool iic_checkACK() //处理应答信号0 H# D8 e' ?0 r( q( p5 \* j
{. }" K# T1 {2 M+ E
u8 errCounts=255; //定义超时量为255次
# {4 k+ H, a6 K/ P! U+ Q7 l scl=1;
3 f w: L7 E' \: v, t _nop_();4 J& j& u# w5 O; z# R" m$ L
6 g. x8 z3 l- D& ~4 h0 E. V while(sda) //在一段时间内检测到sda=0的话认为是应答信号; i$ _4 c# \4 o- n: U$ a Z6 l
{ & T# w6 D+ V* z: j9 B
if(0==errCounts)
' S6 ?0 @9 z* J! E) i% P {! _; L; {9 H: b) r
scl=0; //钳住总线% j4 r C% a+ M' H& ]# c r' e
_nop_();) }0 U& S* u" [( [
return FALSE; //没有应答信号, X% G. T( l) R( F) K! ?
}7 y& D1 P9 R! O
errCounts--;
- r0 j3 X* b5 f! S( s2 W }% s4 p2 A0 ?; V4 {$ L
scl=0; //钳住总线,为下1次通信做准备 . o/ L+ Z0 O8 @5 v% v7 P; B( r
_nop_();) ~- m3 z7 E4 N7 D
return TRUE; //成功处理应答信号
4 B! I. a# u0 h% u; f" s}
6 a6 W+ V% ?+ a. q: b! E% t: D6 lvoid iic_init() //总线初始化, M/ D6 ^# ?) D' `" g* F+ T5 O
{) i5 L/ X" G8 Y+ W9 |4 I ?
scl=1;' s" O. _# h. B/ [% X/ t( f
sda=1;
# i" U) g6 J# I delayus();6 A" m3 h& @+ Q' v4 k4 b
}# e' F9 W4 Y! P
void iic_sendACK(bool b_ACK) //发送应答或非应答信号
. A; W7 y, y1 x{/ c) b0 m" x- G5 s( U# C, P! T
scl=0; //准备& g1 \7 g2 Q4 l( B& c5 h6 b, v4 n3 z
_nop_();5 `2 D4 d$ s2 o( Z4 n# }
if(b_ACK) //ACK 发送应该信号
0 n2 C6 v! f- O1 H {# J2 c# A3 e) [, |
sda=0;" N' E3 g- H2 o
}
. L0 Q P8 [: v2 e else //unACK 发送非应答信号
, k0 n' E9 s! f) x {: L+ r# t. @" R( h0 U, Q
sda=1;
A n+ f- o! {; m }
: }$ `5 }5 ]# ?" y" {3 W# E _nop_();
' l+ o1 e4 {8 f5 W scl=1;
8 z d' b# c0 s delayus(); //大于4us的延时! g' ]3 S0 ?0 [! b& W
scl=0; //钳住scl,以便继续接收数据
@# g, t3 X" X/ m' c) D _nop_();7 ^6 P# j% ^, P' l! w2 M% z
}
8 V, b+ d! G0 n9 n! b% ovoid AT24Cxx_writeByte(u16 address,u8 dataByte)//向24cxx写一字节数据. R0 |) B! L# d& J5 P" f0 O7 q
{. J" v2 E" G7 S7 T
u8 largePage = address/256; //24c04是512字节(寻址范围0~511),largePage最大值是1, [0 a* a4 Q4 U; q ]/ ~: }
u8 addressOffset = address%256; //largePage=0的话地址范围是(0~255)
7 L: F Y, ?6 _: K4 l iic_start(); w- h" e5 ~4 }2 U2 O4 w
iic_sendByte(0xa0|(largePage<<1));//控制字,前4位固定1010,后三位是器件地址,末位0是写" P$ k1 h, A; E3 v! v
iic_checkACK(); //mcu处理应答信号! t- l6 v; W/ g
iic_sendByte(addressOffset); //指定要写入的器件内地址在 largePage块中的偏移
3 E! q$ x' i6 O$ o iic_checkACK();
# m; K2 | J$ ~# _* B q* G. E iic_sendByte(dataByte); //写数据
/ l* M/ i8 S! H6 T6 {3 B9 B& i iic_checkACK();
; F% d3 K3 E& W; L iic_stop();/ ]! X K s% Y% j7 q& v1 V! w
delayms(2); T, X% J( b" K& O
//按字节写入时,24cxx在接收到停止信号后将数据擦写到内部,这需要时间
6 S" {! n* ]/ s: u3 F% G9 C- q& q //并且在这段时间内不会响应总线上的任何请求,故让mcu有2毫秒以上的等待 : b& Q& M5 P1 f" q$ q
}) p" u p7 J: Q6 d) s% m! D
void AT24Cxx_writeData(u16 address,u8 numBytes,u8* buf)//写入任意长度数据(最大256字节) n; h B# P" a7 B% U: O6 P& R
{
+ \& K0 }$ j2 c while(numBytes--)
( b4 I+ M' _( W! l$ h {
# r: b! S3 H' S% L' s T AT24Cxx_writeByte(address++,*buf++);4 A, R/ t+ O d; |/ X
}
1 K0 w2 s1 O! K4 q( n; m1 d# o} v# b% `" X* a/ {: n& I. ]
void AT24Cxx_readData(u16 beginAddr,u8 dataSize,u8* buf)//读取任意长度字节到缓冲区buf中
' K% m$ O3 D' O1 J{+ W* n4 A5 I* E) `$ B% l- M- l
u8 largePage = beginAddr/256; //计算largePage,256字节为一大页; S2 S! f4 f& D8 P& D# h
u8 addressOffset = beginAddr%256; //计算相对于largePage的偏移
+ g: a" H% g; K% A$ U# i3 t' t iic_start(); //起始信号
( B6 c" F7 P9 y; ^3 O+ u* \ iic_sendByte(0xa0|(largePage<<1)); //控制字,写8 D; m% v+ f. [) I( R1 `$ i$ e8 D
iic_checkACK(); //处理应答信号
) n" F+ P+ i3 ~ iic_sendByte(addressOffset); //要读取的目标地址偏移) y9 D: Q1 t% ?& ~0 b3 O9 J
iic_checkACK(); //处理应答信号 K: X! ^# F4 S b" ]
iic_start(); //发送起始信号4 a2 c$ l5 T- t; q$ Q( \2 G
iic_sendByte(0xa1|(largePage<<1)); //控制字,读
2 N3 [+ Q2 a* T+ U6 d( W0 ?% b iic_checkACK(); //处理应答信号
4 p3 Q4 `2 `0 b( y$ `" l while(dataSize--) //读取dataSize个字节,最大256个字节1 a1 j% Y6 K l
{ //dataSize用u16类型会暴掉ram的
) e0 s7 b3 X% H5 w *buf++=iic_readByte(); //读取一个个字节并保存到缓冲区buf中/ {4 i1 v+ r' B: N8 E. ~( q1 g8 \
iic_sendACK(dataSize); //发送应答,当dataSize为0时mcu发送非应答/ f2 R0 e P' T4 f
}
) i, T; Q( {) |8 D h iic_stop(); //发送停止信号
" X. L. Z7 t0 b5 G0 I& y8 e' |}
0 |9 `& s4 Q6 E8 b5 J' O
4 W/ P/ l6 t( \2 A% O- s# ~0 \0 K( ^9 e2 V7 ~! x. y
void main()//测试
* H# C! S$ @ r% P w7 G{
+ I V1 {* \& ]8 _8 B u8 buf[3]; //接受数据的缓冲区0 y+ f) K1 j9 {: t5 E% K) {9 U
u8 arr[7]={0x06,1,2,3,4,0x55,0x33}; //待写入的数据! ~. [2 m$ Z, _ ]& w9 y( @7 f
: p( i% v7 x* W3 x& `$ D" o
iic_init(); //总线初始化& ?4 A+ A3 j# Y4 E. T& h2 k* E
AT24Cxx_writeData(0x00+256,sizeof(arr),arr); //向指定地址处开始写入7字节的数据) H; Y' b% p" x' P) _
P1=0xff; //调试代码,用P1口的led显示" H1 ]1 T' g. e' b' k2 a
delayms(1000); //调试代码( ]$ U0 u% b, b% i# V4 s; p M- ]
AT24Cxx_readData(0x00+256,sizeof(buf),buf); //从指定地址开始读3个字节
0 t" w; ]; M5 m4 D* p2 O* r9 Z P1=buf[2]; //也就是2 //led灯显示数值
; Q, g: {4 G6 I$ L& t- e' m# U
) i6 V/ ^9 C4 a" V while(1)5 S8 K2 _ `& W' g1 u6 U/ t! s9 G
{
$ _- ?% q+ P/ H P1=~P1;: \5 P: v' q% Q% P. i) I. `: W
delayms(500); ) D- ^) }8 a0 K7 C N: ]
}
8 e7 u+ ?* }. J1 f}% [3 i# _" |7 |$ p# N1 y
; H; }% L' a* W
" l5 i2 q7 R0 t9 ]! j: H) K" V- ?3 C. `! [, e! r# J
9 y- s6 P; l% K( |
" e9 e5 }* p7 b/ U9 Y4 c$ W w
* f. j2 n6 r \$ a
8 k" M7 |2 d7 O1 d
. g: @3 j3 S6 z2 H3 G' X; Z" k//my51.h中主要用到; i2 {7 o6 T Z/ I' A: V1 K' Q }
#include<reg52.h>
) r9 p* f/ N! T+ c b% u#include"mytype.h"% _) A3 t4 n: }: A
void delayms(u16 ms) //软延时函数
5 w; _" Y$ B9 ]* v+ G( ^+ q3 I( v{
4 z# p+ p7 @0 z8 l0 r% y u16 i,j;6 h4 K9 Y+ f% J6 @: j
for(i=ms;i>0;i--)
1 H P5 @$ e G% ?% M/ I& V/ N {0 R+ N0 ~' y( P
for(j=113;j>0;j--): a0 I! d( l2 O8 r" g: x
{}1 `, D; T' w: {" D( _4 C2 i! c
}
% G+ U2 G; y- _) g}! Y/ {, E* r1 ~ {$ u/ c. o
$ P+ ~ q& k1 G7 V! Z
- X% T7 ?; K2 j# _2 `: S
! Q L; W! O" U% D" Z
& Y( F. M3 [, {
3 m! u* T# ]6 y' h# B$ Y7 N5 Y2 N) E! W9 O P0 Z) T6 _1 x. M
对代码进行了改进 / Q! r, x3 `4 x
去掉了在写数据时的 : |5 m0 o2 H0 o6 }' F' N" l: t
delayms(2);
0 A0 Z# {" ^+ H% _9 U& L' ~这句软延时代码低效 ,而且没有保障
+ ~; B2 H5 z: ~改成加一个检测函数
1 V4 j0 {2 P% G% W" V2 tbool check_icWriteComplete() //检测eeprom是否对内部擦写完成 6 G8 p4 W' y1 E5 C5 O' S2 N1 K* ~
{
5 @% p: W7 u: Y0 U iic_start(); * I- v% d1 i# C% [! j
iic_sendByte(0xa0);
4 t$ N: g: i, f% d2 e8 y k' l/ ? Q return iic_checkACK(); 3 H/ L9 J0 p+ k1 u
}
) c# N, d c. V7 l
0 y$ s- r( L" ]4 |" c4 ] |
|