找回密码
 注册
关于网站域名变更的通知
查看: 610|回复: 1
打印 上一主题 下一主题

硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2019-1-15 13:44 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器
5 E& b7 {9 \! n" B" {2 m8 Z5 A# L5 }2 |: t! S
' r5 b$ F( F+ G4 L  A+ S; K
% c: w! ?- |2 Z/ z7 s
% c& S  v. A$ z2 U. h+ b
硬件上由于24c01的A0A1A2管脚不允许悬空,故暂时的想法是兼容24c02 ---24c16) y& u5 y% Y4 Y( P  }( l3 |
使用一个dip8封装的芯片插座,A0 A1 A2管脚都悬空即可,换芯片方便
# X$ ]/ R6 z) M5 c8 B3 `2 O/ l软件上24c02地址只有8位,而其他型号是大于8位的,故地址参数使用16位
" s5 Z. W* [, |- m( m256个字节作为一个大页,即largePage,测试芯片24c04空间有512字节' I. {: j6 p& e! p
2 e- ^6 t+ O' Y
上代码,求测试和讨论
% i+ D" ~, a2 ?* e" x3 F* Z1 V* H
* [! Y, y8 F- }2 W: g0 N
. O# E  n3 z' N$ w$ G
#include "MY51.H"
4 F/ b$ {; c% b' i//转载请注明:  求测试讨论
' {8 u) Y- W# w% E//stc89c52rc,11.0592MHz晶振  N( t  o% j$ c: \/ y9 G2 C
sbit sda=P2^0;                //总线连接口定义
' \$ {+ V+ p; k6 c5 Q1 vsbit scl=P2^1;                //总线连接口定义
- _' r/ r# G- F% }% b
void delayus()                 //需要4个机器周期,大概4.34us
& r4 ?& W( p1 K{9 r) L+ X7 w' @
        ;                                //晶振频率11.0592M,机器周期为1.085微秒$ d& P6 U6 U! d) U& l0 |
}
: L: K' {; S1 ~3 V
void iic_start()          //启动信号4 I3 P" S# b" j2 Y* z5 d0 D
{! o  F; j3 F5 z7 i0 i( @
        sda=1;  u# G2 }* M7 e" i( {7 g) Q' I8 `
        scl=1;
" X9 m; U" G  M2 b- ~2 i- a8 H        delayus();                //sda和scl同为高电平保持4.7us以上
" |) f2 Y  z, Z% N; M% j: i. h% n        _nop_();                //1.085us,共5.78us) O5 l% M5 |1 Z9 g( P$ z6 ^  {9 D
        sda=0;                         //下降沿+ ~+ K/ E- X& S, H( ^4 `3 }
        delayus();                //sda低电平保持4us以上        ,这里是4.34us满足要求! N4 A  G( [: M# w% ~) C
}
) t! o% M; f% N5 h
void iic_stop()                //停止信号) ?6 R: w4 v! s# R. m" \
{
# I; i! |7 q# h: [# ?5 V        sda=0;_nop_();        //准备状态
  ^3 n" \$ ^6 ~9 F        scl=1;
5 a; E! N$ k$ W7 D2 M4 D        delayus();                //该状态稳定时间要求保持4us以上$ q! L; X$ C- ]0 O/ ]
        sda=1;                        //scl高电平期间,sda来一个上升沿1 s+ Y; S) P) V$ v6 R6 ?
        delayus();                //sda保持4.7us以上,4.34加上函数返回时间大于4.7us5 f6 L5 D% h" b
                                        //注:此时scl和sda都为1        
- c# H4 |- P2 y4 G) U, Q}
$ M0 ?* |  S2 V' `% e8 k; ^
void iic_sendByte(u8 byteData) //mcu发送一个字节
* R3 F; `4 h: c* u! u" i1 T{$ k: m1 c* D' {6 {. M
        u8 i;
* Y- k0 a& g6 J6 s: z  ?( ~        u8 temp=byteData;
2 x8 v; S7 v- m" `        for(i=0;i<8;i++)! O+ Q' F% _, r& ~. K
        {9 F7 Z/ b; i% J* `- F
                temp=temp<<1;    //移动后最高位到了PSW寄存器的CY位中
; S5 C# X+ X; m! F8 P! R9 V9 _1 ]                scl=0;                         //准备
/ P3 G# X1 L) p8 u                _nop_();                 //稳定一下
" X1 A* t9 @% J! c                sda=CY;                         //将待发送的数据一位位的放到sda上
5 j& Y! J) l7 Z7 h" @                _nop_();
* t& M8 c2 v+ p4 H0 O                scl=1;                     //每一个高电平期间,ic器件都会将数据取走
- |' z0 Z9 `9 C2 c9 J- v                _nop_();                . _- O. C5 T8 @- o. t2 [
        }

, f. d: k: S- T8 k        scl=0;                                 //如果写成scl=1;sda=1就是停止信号,不能这么写" ]" m; N8 o, C0 Q% q% A$ c7 x
        _nop_();                                
9 y, t( b) o, Z7 h. z: x- c- O- ~7 Q+ u        sda=1;                                 //释放总线,数据总线不用时要释放* X" f2 h/ i# z3 O) H
        _nop_();
$ m5 K; w% W4 G4 q, g0 p$ l}
' q9 }" f) d; l% R) P; |
u8 iic_readByte()                         //读一个字节$ h5 W3 P$ w$ U
{2 s8 n8 m8 n3 I/ g
        u8 i,temp;4 y; M: N- Y, H2 h2 m3 o5 A! B. b
        scl=0;                                        //准备读数据% O* }$ B' H" T, ]' l% V
        _nop_();3 e( ]& b$ X9 W7 u
        sda=1;                                        //释放总线
, g% _( B  u; ~" B1 v2 ]' `6 n        _nop_();
  i) s- f; Y$ D+ E/ B/ b( s4 e9 Y9 k
        for(i=0;i<8;i++)* M) T4 D5 @6 @9 Y9 K- K
        {% p* c! I) r" d% H6 A, J
                scl=1;                                //mcu开始取数据3 G/ C6 M! H" T
                delayus();                        //scl为高电平后,ic器件就会将1位数据送到sda上& K# u5 R: t4 y  R6 G
                                                        //总共用时不会大于4.34us,然后就可以让mcu读sda了
7 n# O% B8 @3 z# G: b                temp=(temp<<1)|sda; //读一位保存到temp中
$ D! K( P/ B2 t* {! |! S( U$ F                scl=0;3 P0 t$ h0 v6 \- p! [6 z/ O) }
                delayus();                2 S: r% A( f. c- U& ]8 a
        }
! N3 Y& D: o! Y8 t3 |        return temp;* n# I& W0 s: Y
}
% o% x; h$ s+ a( j( P" w0 r
bool iic_checkACK()                  //处理应答信号
4 Y6 _9 Q  X( n8 G2 e" A( u) U{; w( Q% S4 I7 u
        u8 errCounts=255;           //定义超时量为255次) f2 }' Z% ~3 P8 H- t8 \# w
        scl=1;
* u9 b- l$ v8 j3 Z" B! d/ E        _nop_();, [2 g! h+ p9 B4 E. j; P6 F
        
# i7 w- _1 e5 q+ [        while(sda)                          //在一段时间内检测到sda=0的话认为是应答信号
6 o& v( ]) P: b2 o, N0 Z        {        " V4 f5 d* a7 T+ D: q# y( S( o
                if(0==errCounts)
; i/ R8 ~( G2 Q/ t2 ^5 F                {' Y$ C+ Q% g( o' L) _
                        scl=0;                  //钳住总线8 [2 u3 P4 @; q" q& y) x
                        _nop_();
. L1 k$ X8 g! I- ]9 E                        return FALSE; //没有应答信号
/ [- c$ x$ i4 ~2 T7 @( N9 p                }
$ z2 Z% g8 s, S( Y3 {) r  g' E                errCounts--;
) `5 u/ R( |% G( K0 q# s; I        }

/ z! N. H- ^  J" p+ ?+ A        scl=0;                             //钳住总线,为下1次通信做准备
' i7 ]# W5 f8 C9 O& l4 s  c        _nop_();; B2 T2 m1 U; K  M. W$ T& Y4 _$ O
        return TRUE;             //成功处理应答信号1 n4 L- ?( Y# S, W
}

+ A6 T% ~- X4 x8 b" B2 dvoid iic_init()                     //总线初始化; G4 T# _) _. @/ ]1 t2 k9 I
{# r6 A1 E0 ]( l; g1 m( h5 M& a
        scl=1;
, N3 F4 j+ R' ]; Z. \' [8 A; B        sda=1;7 F9 v, x1 a% n: R4 u! U* V, A
        delayus();
* K+ _# ]* H0 U}

4 r8 ]# D! n( u1 Q8 I/ Evoid iic_sendACK(bool b_ACK)  //发送应答或非应答信号# n8 u) v8 g3 p* q# e% u
{
$ l( {4 M9 P+ B5 m6 `3 p$ m1 Q        scl=0;                        //准备
) ?  T; `+ q- t- ]( A5 c        _nop_();
4 a- y- I4 k7 |! U- c& d; o( O
        if(b_ACK)                //ACK        发送应该信号& }" b- A9 F, A% d- D
        {
8 e* Y( o9 \+ G                sda=0;( Y6 W5 A2 N) ]# F) N
        }
2 z1 G# X; T" }& I/ F        else                        //unACK        发送非应答信号* O/ h# d' U" u" [  R
        {1 K% T2 ^2 Y( O! L
                sda=1;4 |- Q5 o9 l, Z4 M8 m. ]
        }
# X) a+ j  ]; a
        _nop_();/ S! w8 e* i7 C: L" h8 H$ O
        scl=1;
- N/ U6 G; l  O0 p4 b        delayus();                 //大于4us的延时: z3 q5 V1 C. p5 V. j2 b
        scl=0;                    //钳住scl,以便继续接收数据        & K* o0 y3 n0 n0 c( }
        _nop_();
! i! c. t8 k* A6 J2 ^# I, k}
0 J' C5 @. t- F9 [: b
void AT24Cxx_writeByte(u16 address,u8 dataByte)//向24cxx写一字节数据* k. {; W, c6 m, c; d/ y1 X
{& k$ X( w: ?5 m# ^; a0 v  [
        u8 largePage     = address/256;          //24c04是512字节(寻址范围0~511),largePage最大值是1* |1 {9 l' z0 x) @+ J5 m* \5 @
        u8 addressOffset = address%256;   //largePage=0的话地址范围是(0~255)4 U% h; e" C, A
        iic_start();
( v2 s1 {: N! O! R3 `        iic_sendByte(0xa0|(largePage<<1));//控制字,前4位固定1010,后三位是器件地址,末位0是写
3 W& Q6 E! B3 z        iic_checkACK();                                      //mcu处理应答信号
$ u) B6 A( w8 D        iic_sendByte(addressOffset);            //指定要写入的器件内地址在        largePage块中的偏移
2 O4 u( Q3 b- B8 l' I9 Q, K        iic_checkACK();* w9 G6 j0 {, H$ ^
        iic_sendByte(dataByte);                   //写数据
) V+ v- ]" ?! Y; W        iic_checkACK();6 R  ?% r( ~3 C" ~7 q" u# s
        iic_stop();
: F8 z( v- G. P/ E& [. D4 q" h        delayms(2);        4 E( F+ d# F/ Y; p
        //按字节写入时,24cxx在接收到停止信号后将数据擦写到内部,这需要时间
$ Z0 ?: n. o- N* R        //并且在这段时间内不会响应总线上的任何请求,故让mcu有2毫秒以上的等待        % E; ?5 d% l% R( t- W* r3 w: t1 q; C
}

" Q  l( v. q% z2 O  O$ Bvoid AT24Cxx_writeData(u16 address,u8 numBytes,u8* buf)//写入任意长度数据(最大256字节): f; D( M4 W4 i* M! A
{
8 c8 Z( D8 S, T        while(numBytes--)$ S/ t% g" @' ?2 M& e) W% e5 }
        {
' G* ]; b+ g7 i& T1 i                AT24Cxx_writeByte(address++,*buf++);# F% s7 m( R6 j* w" m
        }
) c3 @! m. D" M" S}

0 @$ z- V! Y* V' Hvoid AT24Cxx_readData(u16 beginAddr,u8 dataSize,u8* buf)//读取任意长度字节到缓冲区buf中! f& X6 }* h3 d& _- I6 C
{- X; u4 u# I- _+ p: O8 D: P
        u8 largePage     = beginAddr/256;        //计算largePage,256字节为一大页
( R4 b5 L  `% P+ x        u8 addressOffset = beginAddr%256;        //计算相对于largePage的偏移
+ {9 U( C, k7 n3 _3 j$ Y' Q3 h        iic_start();                                                  //起始信号
. l8 U5 @. S3 G1 O, b! A+ R' }        iic_sendByte(0xa0|(largePage<<1));        //控制字,写! _2 H3 X/ G! ~8 ?0 R- Y& x+ B* b, ^
        iic_checkACK();                                                //处理应答信号
. d: f& U) a: L, u+ f        iic_sendByte(addressOffset);                //要读取的目标地址偏移% ?) M8 \8 F5 E4 B2 ~3 Z
        iic_checkACK();                                                //处理应答信号        
  e. Q; X0 Z( t5 q( r( v+ U        iic_start();                                                   //发送起始信号
  A3 F0 L+ Z, g5 H        iic_sendByte(0xa1|(largePage<<1));        //控制字,读1 R5 s" Y* H2 @8 q; C
        iic_checkACK();                                                //处理应答信号- `# x' K7 K0 T1 h/ L
        while(dataSize--)                                        //读取dataSize个字节,最大256个字节* m; o# z( G1 F% K( ~& a
        {                                                                        //dataSize用u16类型会暴掉ram的
% j6 T4 C* e8 q                *buf++=iic_readByte();                        //读取一个个字节并保存到缓冲区buf中: r3 R0 E% g( B! x  Z# z6 v6 W. ~3 e
                iic_sendACK(dataSize);                  //发送应答,当dataSize为0时mcu发送非应答- _. ^# V& {2 [" y6 [1 ]* J5 s
        }6 V* T. ?  F$ v
        iic_stop();                                                        //发送停止信号
4 X, x1 r8 |3 \0 F) I# v}
4 W4 u1 n' P; r1 U

! v% K* P( u7 l0 U9 D: a
! ]( A$ E6 f7 H4 c; ?) ?# }8 D
void main()//测试
2 ~/ O8 A' E) S{; c- m: t8 h1 s: H
        u8 buf[3];                                                                                //接受数据的缓冲区
0 M1 R; V# i8 I2 x! T, e! m% R' x        u8 arr[7]={0x06,1,2,3,4,0x55,0x33};                                //待写入的数据" Z8 F2 O" w" C6 w4 J
                                                
$ B, m! o+ s2 e$ I* [        iic_init();                                                                                //总线初始化5 ^: {! o, ?  P7 v, |* U
        AT24Cxx_writeData(0x00+256,sizeof(arr),arr);        //向指定地址处开始写入7字节的数据
" D- G2 z# i  _( N5 M8 z( }
        P1=0xff;                                                                                 //调试代码,用P1口的led显示- w# |3 y$ A- C% v# \( E
        delayms(1000);                                                                         //调试代码
! ]- b) R$ Z0 c, j
        AT24Cxx_readData(0x00+256,sizeof(buf),buf);           //从指定地址开始读3个字节
$ j& ~0 |! Z# E" e% V/ z        P1=buf[2];        //也就是2                                                                        //led灯显示数值) Q0 E4 q0 t, Y3 ]: ], R+ t- O& R* S
                                                                                       
2 s. D% X  P# `. a        while(1)
9 E" p8 m0 u9 p7 ]        {
: G- ~% y( d5 O1 B+ R2 v                P1=~P1;& v; k# h; G0 ^  m% |" z
                delayms(500);                ; H. N8 z$ R3 m9 E+ ~
        }
  w, V6 ]1 b& B0 ~* {: P. j, R}! t2 u) e3 j5 a' `

3 [$ R$ U( M; g. p. J. w
9 O( R  M& @- Y$ x" m1 e: n+ d+ v! b2 P
; Q6 I3 L. g' [! P1 m  F8 s& ?
/ W" [5 i3 C- S7 s* ^

/ J$ k! t3 D6 g/ c& S/ u" E3 K) ~* J! m4 l* y& z
5 ~( _2 W- Z* R4 M1 y6 v
//my51.h中主要用到; A3 Q2 q/ P  \. I; ?9 f- k% q
#include<reg52.h>

8 V2 Z/ q$ |0 d8 Z  t0 T# y#include"mytype.h"
) Z% R( X! q6 [# g8 ]. _void delayms(u16 ms)     //软延时函数' D7 z6 n% _: ]5 O. m2 Q: f. n/ d
{% f4 u9 e! ~1 Z# ~7 U
        u16 i,j;
; u+ o0 |1 P7 J; G6 ?. A        for(i=ms;i>0;i--)& `1 `; z9 q) L7 v
        {) R/ v" D3 |5 U
        for(j=113;j>0;j--)! l& k( Q3 p% T0 p/ j8 U4 N3 U
        {}" T: g5 v0 a/ G8 t6 Y0 }' V( {
        }
7 ?) k4 e4 U( v3 X, I! j}
7 _4 o2 B" c  g! B1 e
; f! s( T4 v! m6 b/ X3 _& {' J

; D& [% |- C% L; Q, G1 L! U
1 ?* q7 u- q3 I8 R# I; g# b, w& w1 v3 C: n1 b. [4 i2 T1 o

4 X3 o( M# ]1 J( P5 A9 r: |: o5 ]; M- M5 a
对代码进行了改进 $ k3 h) o8 M- ]8 d
去掉了在写数据时的
( e7 B. f& P$ y' P- m( edelayms(2);
$ X! B- K9 Z/ p; K7 j( I* ]这句软延时代码低效 ,而且没有保障
4 V3 K& Y0 m8 g' V; P+ ~9 T. H! q
改成加一个检测函数 9 y3 z5 I* d( F$ k+ E
bool check_icWriteComplete()   //检测eeprom是否对内部擦写完成 - b+ e% D, I$ w2 F
{ 6 d& n; x( B" ]0 E& @9 ]7 z
iic_start(); " G! d3 G, g  B  T$ Q
iic_sendByte(0xa0);
' k9 ]. l* l' r( ~- P9 { return iic_checkACK();
9 d3 l! N2 [. S' u: n3 ^! K}

, ?7 A! k. J3 z# C1 X) Y! ]8 ]& H1 i9 D6 H( A0 j, C5 O' B! S6 C) X

该用户从未签到

2#
发表于 2019-1-15 23:29 | 只看该作者
这个不错,谢谢楼主分享
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-6-15 22:33 , Processed in 0.078125 second(s), 23 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表