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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器  O6 N) _& x1 `  F* G7 _
: C+ S5 {4 @- T$ j( @8 _" _: g
$ p( z4 B0 s5 _+ v' L' C8 U9 G
; O! P3 k+ D0 }0 L  B8 U! m
. a0 _. M9 {* G) C
硬件上由于24c01的A0A1A2管脚不允许悬空,故暂时的想法是兼容24c02 ---24c16
) \# C9 U$ N: p3 @) K使用一个dip8封装的芯片插座,A0 A1 A2管脚都悬空即可,换芯片方便
$ t# j" g0 w8 d1 R2 y- p0 @, R软件上24c02地址只有8位,而其他型号是大于8位的,故地址参数使用16位
# u$ ^5 j! l% g+ X256个字节作为一个大页,即largePage,测试芯片24c04空间有512字节4 v' |2 I5 ^/ ^5 n2 z# I: F
- y6 a* Q" N5 H! v. o, I: q. l
上代码,求测试和讨论
/ s8 V2 ~7 h% r

: J& y$ p; R( w$ Z
: |4 z" z2 [0 l2 k. O" n5 I( J#include "MY51.H"4 w; g9 s! }" o% |! a5 t0 G' R$ o
//转载请注明:  求测试讨论$ c- s" |9 L5 v) e) A/ B
//stc89c52rc,11.0592MHz晶振
, `' O& ]4 I" {" R8 `2 u* E' Gsbit sda=P2^0;                //总线连接口定义
: r" l% N  X  W7 q: o' V( l$ m8 osbit scl=P2^1;                //总线连接口定义
; k- R4 L6 n' m- K8 T# X2 A
void delayus()                 //需要4个机器周期,大概4.34us
3 T$ S" g& F: m3 M& G1 v{
9 c' a! X, S% e, b' i        ;                                //晶振频率11.0592M,机器周期为1.085微秒( J' n2 b$ a8 K/ j. `3 i' I6 A
}
% h/ f# z+ P; [# T- J
void iic_start()          //启动信号: k& Q" N& e' M, ]4 r% [3 F
{
7 u8 J% S; B7 ~6 j" W( R2 O3 c        sda=1;
$ y3 s( G$ e/ c; X( L2 Q% ^5 e        scl=1;; ]( M* C3 g3 p- D
        delayus();                //sda和scl同为高电平保持4.7us以上7 e+ Z% Y. ~1 m% W0 x+ U8 U
        _nop_();                //1.085us,共5.78us+ H; {1 p/ H; [; w9 V3 ?
        sda=0;                         //下降沿/ S( Z" u1 y1 B3 z4 b* \0 ~
        delayus();                //sda低电平保持4us以上        ,这里是4.34us满足要求0 @6 g) X" w. c8 V' V9 p. y# u
}

* _  X  d) E1 }$ y0 w9 y- E0 r% ivoid iic_stop()                //停止信号
1 F3 f* |9 J* Z8 x, I/ A{& B1 H% \9 ]6 |) w5 e1 W
        sda=0;_nop_();        //准备状态
. V) X: k: K1 x        scl=1;
: M( I( j& V# @) M        delayus();                //该状态稳定时间要求保持4us以上
" u( e0 h( O9 c+ l4 i4 @$ i        sda=1;                        //scl高电平期间,sda来一个上升沿
! N! ~3 X1 a- _4 j        delayus();                //sda保持4.7us以上,4.34加上函数返回时间大于4.7us
1 n' M7 g& Z! ~6 G% w) x2 u# d$ x                                        //注:此时scl和sda都为1        
0 D! Y! \( [0 r! x& v}

, x) s$ e% w1 Evoid iic_sendByte(u8 byteData) //mcu发送一个字节
4 \! [1 _* Q7 R{
& @8 |6 x9 n2 y        u8 i;. ^9 Q$ E# q5 ?  ^5 Q; @
        u8 temp=byteData;- |' l! }: C7 |0 ]( v" m/ F; c
        for(i=0;i<8;i++)
# q0 c4 E5 g" Q2 s. U        {  ~' o0 K$ L3 K* \# \4 w8 j" C0 ^
                temp=temp<<1;    //移动后最高位到了PSW寄存器的CY位中
7 u* w5 ?7 k' a8 W' p                scl=0;                         //准备
9 N( S* |; f: w8 w) p                _nop_();                 //稳定一下3 p3 K# @; c, ^9 O. x  D' S
                sda=CY;                         //将待发送的数据一位位的放到sda上+ `$ k/ ]% X6 _* H
                _nop_();
  A  D* Y/ n1 E$ ]                scl=1;                     //每一个高电平期间,ic器件都会将数据取走* Z# h9 r. [- D5 G1 P
                _nop_();               
9 _3 z% x' i$ M6 l3 c8 n        }
% V" y4 s, f: |4 |" O$ c. ]
        scl=0;                                 //如果写成scl=1;sda=1就是停止信号,不能这么写
+ G6 j( l7 N- M9 Z        _nop_();                                
# F  ]# ?4 b9 Q        sda=1;                                 //释放总线,数据总线不用时要释放9 d4 ?/ N- U6 T2 n3 k2 z
        _nop_();
  p& j+ D# i  @+ ~" ]. S}
* A5 s7 `8 {( S0 Y* \" M8 q: b. V
u8 iic_readByte()                         //读一个字节
7 h0 P4 r- K% C% K{
' ]2 S2 t$ b" h/ z7 C' p        u8 i,temp;. Y) P( ^( V6 o5 G% U3 K
        scl=0;                                        //准备读数据
6 ]- W+ _+ q7 t2 t0 C- j        _nop_();0 l1 A! Z4 V0 R
        sda=1;                                        //释放总线4 {* ]: L. B4 ]
        _nop_();

# j$ `' m% ~& \* O( x        for(i=0;i<8;i++)
/ H7 E' e  K4 w/ A0 v        {) G2 m" b5 `" P- H# i: \9 O
                scl=1;                                //mcu开始取数据9 }  l# I2 w5 x
                delayus();                        //scl为高电平后,ic器件就会将1位数据送到sda上
( L) l. j* r+ M                                                        //总共用时不会大于4.34us,然后就可以让mcu读sda了+ p, F; p' Q7 P% H2 Q; q, S4 l
                temp=(temp<<1)|sda; //读一位保存到temp中9 ^3 ]; g: J' k* \
                scl=0;
: e% r3 Q7 N8 E4 U) r! v                delayus();                7 t1 e2 W  `8 V. z
        }
9 Q5 {' E( q4 P; O2 a' \7 b! m        return temp;
; U% X. L4 R, @6 N) z7 b2 a6 K}
5 z0 u% s1 n+ n0 {
bool iic_checkACK()                  //处理应答信号
1 [8 a9 D6 l. f4 L{  u8 N# G: v: D/ N6 f8 x9 [
        u8 errCounts=255;           //定义超时量为255次/ i' C, i, d% I4 K1 ~
        scl=1;
  ~, G- ?$ `* K        _nop_();. j8 n" t! ?  y6 p5 u
        
$ A- d: F: V3 ?/ N        while(sda)                          //在一段时间内检测到sda=0的话认为是应答信号1 M7 t2 }& o4 y& f# x3 c+ r0 {* n
        {        : e% E. y8 H! O. H5 x2 ^8 R9 l
                if(0==errCounts)
" o' G4 f9 y) E0 L" r8 y/ P& _                {
4 C  A$ |/ E* T+ E6 d. F6 r                        scl=0;                  //钳住总线
' `* F. A# h; Q/ b: ^: ]1 p                        _nop_();8 C+ G+ k. e* r
                        return FALSE; //没有应答信号! \7 {. a0 F0 J8 M. x
                }
# s) \# g8 f- d& V; B. Z                errCounts--;5 j# `8 D  W; v0 B" |7 `. P$ l
        }
, U" t6 u! j- M- o" Z5 p
        scl=0;                             //钳住总线,为下1次通信做准备
$ Q3 S/ D% n9 _- S, U& [        _nop_();
1 Q; y0 l# s3 r        return TRUE;             //成功处理应答信号% {3 |* e) J1 @# ?9 ~* ]
}

+ t+ [0 `4 Q! `5 m7 ^. r' G& R, \void iic_init()                     //总线初始化( g2 Y7 U& t1 W- ^5 w; w
{
: ~/ I: J' N- H# }0 g        scl=1;
* R  o' f% ~! n, J3 u1 G8 l: `        sda=1;3 i' n& c; V4 d9 u1 i4 j( c! M$ u4 k
        delayus();
( i& [! }5 q2 U0 Y# M/ N; P$ l4 Z}
/ H& e+ d/ A; T6 D
void iic_sendACK(bool b_ACK)  //发送应答或非应答信号
9 [; u4 h# ]& l; ^' ?{+ _4 @8 N1 ^9 m
        scl=0;                        //准备* P  a! b: v' u& W9 H
        _nop_();
0 k+ ?" X1 h5 J" o" @$ g) O0 ]
        if(b_ACK)                //ACK        发送应该信号* t! f7 c: ?! K
        {
( u5 q: g8 W* a& |* n' t                sda=0;
0 |- @$ w1 M/ {* J4 Z        }
- N. H) z9 _' d, v% ~! f8 c+ T        else                        //unACK        发送非应答信号
+ e2 c5 \0 q2 x0 s6 @; `4 d" Z        {- m6 Z4 T5 n- T0 `9 D' y
                sda=1;
. e' r) l, U7 {- T+ a7 A        }
* {. P! J. V1 q: A- M7 ~
        _nop_();
( }) `2 a* l# L$ n, B' X- `        scl=1;
' m7 I- Z8 J- [        delayus();                 //大于4us的延时
* m3 s3 Y) D+ ^/ ~' Y        scl=0;                    //钳住scl,以便继续接收数据        , M! v# ~* G  O
        _nop_();
9 j* E: g1 Y- K; X}
$ v, {. K- R# L
void AT24Cxx_writeByte(u16 address,u8 dataByte)//向24cxx写一字节数据
% Z; q6 _, i4 D0 T5 I  c8 P) L6 ]{
4 \( V2 F1 Q; ~0 z: Y        u8 largePage     = address/256;          //24c04是512字节(寻址范围0~511),largePage最大值是1
9 U$ O& S9 d( T/ }- _6 @; u; t        u8 addressOffset = address%256;   //largePage=0的话地址范围是(0~255)1 _' ^, q+ S$ b6 p: i  ^/ T) Z
        iic_start();( \6 ^$ _) s3 e1 w# t! a  y( t
        iic_sendByte(0xa0|(largePage<<1));//控制字,前4位固定1010,后三位是器件地址,末位0是写6 p+ K: r3 Y! N) E& A0 o: @$ ^
        iic_checkACK();                                      //mcu处理应答信号2 K, z* R0 ^' `4 Z  U
        iic_sendByte(addressOffset);            //指定要写入的器件内地址在        largePage块中的偏移
  ]5 g$ b( A* L( z$ Z, @        iic_checkACK();" h$ l! B/ K+ a) u  V4 D! A4 |
        iic_sendByte(dataByte);                   //写数据
2 H, j# R2 n) i9 I        iic_checkACK();1 m4 w1 D- s+ M, ~8 p, ^
        iic_stop();
$ Y8 g  w5 Z. ?  S- w        delayms(2);        ; X2 J7 l! [/ H% i
        //按字节写入时,24cxx在接收到停止信号后将数据擦写到内部,这需要时间1 B# t9 p- }) T/ K& D
        //并且在这段时间内不会响应总线上的任何请求,故让mcu有2毫秒以上的等待        
6 U9 X: z: _& Q! W}

+ n& K1 _! b5 [; V8 v( O) @% gvoid AT24Cxx_writeData(u16 address,u8 numBytes,u8* buf)//写入任意长度数据(最大256字节), X% ]0 R+ A4 O" t" \5 N
{6 D4 ~2 O' d6 f' t& _
        while(numBytes--)
6 ]  _. s5 s) ^% C$ \4 p( m        {/ ]% j& ^3 [3 |2 a
                AT24Cxx_writeByte(address++,*buf++);$ Q  ^" o% R, g/ P4 i
        }. Q0 y8 y# l7 ^3 j4 f
}

4 z" B, I& w$ tvoid AT24Cxx_readData(u16 beginAddr,u8 dataSize,u8* buf)//读取任意长度字节到缓冲区buf中9 \! N+ y3 W$ i% b3 @
{
  B4 i3 G2 D+ c7 _( A" d  w5 a; g9 n        u8 largePage     = beginAddr/256;        //计算largePage,256字节为一大页  {/ ~: w1 i9 I1 `+ Q4 m
        u8 addressOffset = beginAddr%256;        //计算相对于largePage的偏移
# m; q, z+ k5 `% r1 t  ]        iic_start();                                                  //起始信号. N9 `; A" m, i/ {! S, T' \$ {$ M
        iic_sendByte(0xa0|(largePage<<1));        //控制字,写) V7 I( @/ z' n4 |" L; X
        iic_checkACK();                                                //处理应答信号
3 j1 v  D9 v6 G8 \        iic_sendByte(addressOffset);                //要读取的目标地址偏移
: L8 s/ k/ l/ V/ j6 X) n7 ]0 Y        iic_checkACK();                                                //处理应答信号        * k8 g' H" @8 {, k% Z. r0 J
        iic_start();                                                   //发送起始信号! o3 B. _& U  f
        iic_sendByte(0xa1|(largePage<<1));        //控制字,读$ x$ x" h0 Q3 A4 I/ M2 v
        iic_checkACK();                                                //处理应答信号: N# f: p" x  S/ Q( K
        while(dataSize--)                                        //读取dataSize个字节,最大256个字节  L9 v0 T) K% D1 {* W4 v
        {                                                                        //dataSize用u16类型会暴掉ram的  L6 Y7 L9 a0 q  R# z' ]1 ?0 P: G
                *buf++=iic_readByte();                        //读取一个个字节并保存到缓冲区buf中
% O2 c' l- I: W$ z2 |1 s                iic_sendACK(dataSize);                  //发送应答,当dataSize为0时mcu发送非应答
3 W8 o* N9 ~- U% D3 O! R        }
9 J* e: K- m' ~1 k) ?        iic_stop();                                                        //发送停止信号
2 U+ R3 q* E2 @1 q% C}
8 N# T2 V; J* U0 c% H9 O, ^

# q9 R+ s  ~6 H" O+ z
3 L' m/ a5 T- \! ?+ z. p8 d& L
void main()//测试
/ s: {* ^& m, i% A" k{
0 V% ^5 N4 e6 L1 m$ ?( I( J! m9 T        u8 buf[3];                                                                                //接受数据的缓冲区/ g5 K; A  @( t( W7 H
        u8 arr[7]={0x06,1,2,3,4,0x55,0x33};                                //待写入的数据" X& w) v& c) ?; g
                                                4 I: c! v/ d1 C! W
        iic_init();                                                                                //总线初始化6 D' o4 p: C5 j$ X* O2 ]
        AT24Cxx_writeData(0x00+256,sizeof(arr),arr);        //向指定地址处开始写入7字节的数据
5 q/ c8 L3 v- n$ [' e; {3 E7 M  g7 d
        P1=0xff;                                                                                 //调试代码,用P1口的led显示
; b. \8 u2 q0 O0 b7 P        delayms(1000);                                                                         //调试代码
: `8 u: q, S4 x
        AT24Cxx_readData(0x00+256,sizeof(buf),buf);           //从指定地址开始读3个字节
# \/ B: o6 S# A, `9 W) {8 W        P1=buf[2];        //也就是2                                                                        //led灯显示数值1 l! N+ o' L1 h) h
                                                                                        / d# l$ D1 G9 L0 x
        while(1): H5 t' T  E0 {1 `; S! C9 |
        {$ f& y! a! P% R- R# I& L/ w
                P1=~P1;1 Q* R' l) X* @! y6 Z
                delayms(500);               
! \8 c  f, R3 p* |, Q! s2 m2 _6 U        }
) Q% f) Q# p( R0 ]}
* j6 g2 d* Y8 _9 y7 F
0 C- d4 H" S, A3 y, l
) n6 N  v) @8 A  K. Z* J- r% t

# q' ?3 a" w. D( n1 @% e$ R( s1 p6 n3 V9 h  w& K

/ q' }- X* f+ P2 I. e( I
, @+ u- y# G6 }' E- |$ e/ V7 B- E2 d" _8 A5 e
) ?/ t: X& X1 R/ d/ _
//my51.h中主要用到
4 C6 K! r$ b6 {% I* r% [; S) m#include<reg52.h>
' g+ C( w6 R! C/ ]# i5 o
#include"mytype.h"7 v. L# s: L0 S; X1 j- f
void delayms(u16 ms)     //软延时函数9 _: g! ~% z, g0 l: s( ?' ~
{
8 b" N. k5 ^6 J        u16 i,j;" f- f& z$ k* Z4 ]
        for(i=ms;i>0;i--)9 q+ S: l3 Q, n  T  K
        {# G( h6 A1 h6 ?# U
        for(j=113;j>0;j--)# K4 q* O2 i0 h* \
        {}
  o; Y1 K, |2 O* K+ U8 r* G' \        }
4 l7 L6 F  e; h1 [5 k+ b}
& L7 [! I$ K1 ]2 I4 ^" n! \

' A; `% o& ?& _( u% N) N, x0 z& ]5 {; `" e

1 c8 I6 H" G, r7 j7 _6 T' z# m1 D4 c! s( ~. B

7 N+ W, i2 I/ m5 r
3 @# v6 f1 R; @  d! O对代码进行了改进
7 m# Y, Q( z8 _去掉了在写数据时的 0 n9 @) q9 B+ {- K/ D% V6 S
delayms(2);, g/ ?0 @0 M6 I1 m8 x. P( J. e
这句软延时代码低效 ,而且没有保障
& c7 Z' k3 M. `, h5 q0 n
改成加一个检测函数 & N4 h8 U  x: C% R
bool check_icWriteComplete()   //检测eeprom是否对内部擦写完成 - z( s6 @; N7 x! w' O! Y& i3 `+ v
{ # m5 l- L& c5 y; [
iic_start(); , n, E% C( d  u/ R% K0 Y, r& O
iic_sendByte(0xa0);
' c7 n2 e4 Z: x; V! o0 L return iic_checkACK();
4 z: u% V* ?/ [% j3 Z' i4 j}
! v3 p2 a+ C5 x  ^; N0 P2 l* B7 C
* V& ^3 o; j, U( _. j

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-7-18 20:21 , Processed in 0.109375 second(s), 23 queries , Gzip On.

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

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

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