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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器+ o: b' z4 {0 w

" [! d8 X: _4 h/ n; B3 @* r) |) E/ |9 n" o( P
$ I; W' ?2 C; W6 @7 M9 }- A

1 p2 m" N  h! l5 b* m( `硬件上由于24c01的A0A1A2管脚不允许悬空,故暂时的想法是兼容24c02 ---24c16" b1 L( h7 ]4 e8 ?5 U7 X7 U
使用一个dip8封装的芯片插座,A0 A1 A2管脚都悬空即可,换芯片方便9 I! t+ k+ t8 ^5 u$ D8 p/ [
软件上24c02地址只有8位,而其他型号是大于8位的,故地址参数使用16位/ e! U8 G* v8 H
256个字节作为一个大页,即largePage,测试芯片24c04空间有512字节
3 @/ ~7 o/ S# x5 f+ n3 g) D  h8 I( O, s+ v3 i/ J
上代码,求测试和讨论
6 A# }3 R! M" O1 n* i
3 Y7 p8 p6 d2 X# b
0 n7 {4 J0 J. s* V% k
#include "MY51.H"+ |8 E, s3 A2 S  A' c0 ~9 d, n% O1 {
//转载请注明:  求测试讨论
+ O9 Q) k( F% z- n6 H- Q//stc89c52rc,11.0592MHz晶振6 D$ q6 J' g. H: E# V
sbit sda=P2^0;                //总线连接口定义8 N) L; x! r! Q6 w2 Z+ q6 _
sbit scl=P2^1;                //总线连接口定义

- j: |+ P, W- ]# Qvoid delayus()                 //需要4个机器周期,大概4.34us
- J/ R  |9 z$ y( F. @{
" T7 `0 U9 L5 {3 N        ;                                //晶振频率11.0592M,机器周期为1.085微秒
9 Y) C5 C; J$ G9 I+ ?8 _6 B}
/ C) G1 H9 W' m- ?8 j( C+ e
void iic_start()          //启动信号
. ~, O+ H, F/ ?& n9 e{
8 M  i, ?7 t; M' S0 ^8 B# V6 f% d        sda=1;7 k, Y$ z. b( V, K' w, M5 j: B
        scl=1;+ F; e  h6 H3 H$ ~+ P; f9 Q
        delayus();                //sda和scl同为高电平保持4.7us以上2 T: s3 r% t3 p: X3 t+ `
        _nop_();                //1.085us,共5.78us
: e$ t/ Q" g0 @0 ]. O        sda=0;                         //下降沿
; k1 U' D$ n. }2 o1 G  B( q        delayus();                //sda低电平保持4us以上        ,这里是4.34us满足要求
0 {- j5 o6 z' d: s$ @}
+ r* \' s: k' q5 A0 W  y
void iic_stop()                //停止信号
4 z4 A# ~3 }7 b. J' t. x{
$ q' T( M4 H9 i" y  Z        sda=0;_nop_();        //准备状态( B- H0 Y4 B7 b2 L
        scl=1;/ X' q9 {) P( y& z' q: w% P
        delayus();                //该状态稳定时间要求保持4us以上
' M$ G9 c9 ~5 c! X        sda=1;                        //scl高电平期间,sda来一个上升沿  `, q3 |) B+ {+ M1 [
        delayus();                //sda保持4.7us以上,4.34加上函数返回时间大于4.7us/ T' e; [$ A# A( x2 W& k- l4 Y6 I2 D
                                        //注:此时scl和sda都为1        # n# p0 n; S/ o' O5 _; l
}

( D& A7 x: x  [void iic_sendByte(u8 byteData) //mcu发送一个字节
6 m! I* [3 [# P{
6 x4 t$ ^. G4 Y, G2 u        u8 i;
& E9 x5 T) `, T5 P1 A7 F7 c4 {$ ]        u8 temp=byteData;
- U' D: K& w! F" L" N! F3 Y+ M        for(i=0;i<8;i++)
5 f! i5 c- T3 q9 L& F/ }5 x        {
0 D! C( D, r  O0 e3 T                temp=temp<<1;    //移动后最高位到了PSW寄存器的CY位中
  G3 [% i& i7 |  m1 N4 ^                scl=0;                         //准备4 [2 L9 z; A* L* j! [* Z' h
                _nop_();                 //稳定一下* r. v: K. h. x- }& i. ]& K. B0 O
                sda=CY;                         //将待发送的数据一位位的放到sda上) A& x6 H3 m6 _! u7 ^  M' w, v
                _nop_();
2 z; A' ^! W# {1 q2 n) r5 V! h$ D                scl=1;                     //每一个高电平期间,ic器件都会将数据取走
' L3 o+ M- ~2 q% {                _nop_();               
; \/ {% O% I# R3 p$ h6 m2 Q6 L        }
0 o& Z) u7 t4 Q# P: X$ x
        scl=0;                                 //如果写成scl=1;sda=1就是停止信号,不能这么写# ~! P7 D5 N" u* C5 O
        _nop_();                                
9 x1 ]' Z! h' z: ]3 T, F2 N        sda=1;                                 //释放总线,数据总线不用时要释放( e& _) h3 I7 t" X8 w2 r
        _nop_();
- e: k' ]+ C4 f2 n- Y}

2 j: F8 N7 k: nu8 iic_readByte()                         //读一个字节- x- n6 O/ B& K+ W8 f
{
. _; [- g* _" j5 c' ~        u8 i,temp;0 m1 X0 O, A  M2 x/ b- x
        scl=0;                                        //准备读数据
5 K8 R6 v# `2 i8 P: @, w! b        _nop_();0 h  R' \- X# U. G# p' t
        sda=1;                                        //释放总线
+ {* b- p5 T3 N' v        _nop_();

; b& C- I& m. a* C% q2 N        for(i=0;i<8;i++)
2 P8 `0 R4 W9 I" |" I) W' h        {
! J" ]: j" N2 i- n5 ?$ Y9 Q                scl=1;                                //mcu开始取数据+ D& S  O' W& Q9 t% x$ I
                delayus();                        //scl为高电平后,ic器件就会将1位数据送到sda上
  m: U' Y4 a6 q" o. M                                                        //总共用时不会大于4.34us,然后就可以让mcu读sda了
2 a. J6 y1 V- p- x- j* A2 p. }                temp=(temp<<1)|sda; //读一位保存到temp中
4 j0 T8 E9 o+ p                scl=0;
# a* E4 r  H. ~                delayus();                9 o, U3 g! s! {4 M5 e7 ?0 S$ ~
        }) b2 K6 F: v! @2 N  b. U
        return temp;) w( ~% ?  S; U2 R, E
}

& b7 h9 ]; l: r/ A$ i4 g6 ^% Ybool iic_checkACK()                  //处理应答信号
; I" ^4 Y+ M+ e; y5 k" ?% ]: G; M; C6 w{
1 k# J) w! B! _. Z" o/ x0 d        u8 errCounts=255;           //定义超时量为255次
0 j2 q( L- k. C9 _! U        scl=1;
7 H8 `! c2 x% j# U9 Q        _nop_();& |2 x6 R# `7 B
        8 r$ N8 q6 ^. v: y( o
        while(sda)                          //在一段时间内检测到sda=0的话认为是应答信号/ J' G) Z; O3 ~" T0 |& K
        {        
4 m4 K  _) {+ D1 l; u2 H1 z                if(0==errCounts)4 X; K3 J, w5 b3 p
                {! s( ^. F- s1 X& S* w, T
                        scl=0;                  //钳住总线
0 x: ~/ k7 O  {: X1 }                        _nop_();
. j$ \2 E) b5 t) l                        return FALSE; //没有应答信号
7 M2 d: G! \; b$ W" W                }, `' k% t6 n: ~6 I3 A: w( r
                errCounts--;
3 v  m) v. D  C3 V, Q/ E        }
- f8 [: D: A2 O* S
        scl=0;                             //钳住总线,为下1次通信做准备
+ C6 j  E. N5 w9 d        _nop_();! w& ^; `* ]5 |! M
        return TRUE;             //成功处理应答信号
9 r6 w3 O) {1 L) {}
/ I- c2 g7 _+ E/ |) N  J6 e! G" D5 i
void iic_init()                     //总线初始化. i! z0 [& k2 R5 c% ]' g
{
/ k! f2 N, |3 |4 L. \2 ^        scl=1;
, B" z1 K- M, ~& T  H" r/ }/ q        sda=1;- b( n: m& D' O2 _
        delayus();0 X: s7 u2 l: I* Z6 X  L) \( h+ p
}
' K& c9 g7 ?/ }  B8 z% B8 J
void iic_sendACK(bool b_ACK)  //发送应答或非应答信号
6 r, v. v: Z' l% [: z6 z# m{6 _2 X" I  [8 R
        scl=0;                        //准备
8 T/ D! R' z3 r, |, N+ y* g# s$ j3 e        _nop_();

. H2 y& v+ @; l, P        if(b_ACK)                //ACK        发送应该信号
0 w+ g# [" {4 B( M4 u/ ^$ V        {6 s6 `) F) e) u* k: E/ c4 I
                sda=0;
( Y. A( U$ {# x3 R        }
4 R$ z# n3 U5 O! U" G  _/ t        else                        //unACK        发送非应答信号
' S; Y1 D2 B2 L8 E$ {8 |        {( X" @) ^6 o% w8 U/ k/ c0 A7 \
                sda=1;4 {6 ~6 f; {# y$ m. r: U
        }

$ p: d9 J& P3 {1 O2 j        _nop_();
% ?! I  ^( [& ~- R3 k$ r2 S+ @5 M        scl=1;; W9 n  Y  Y5 ?' [0 d2 s; ~
        delayus();                 //大于4us的延时
# w! s6 r1 J: |' U' [        scl=0;                    //钳住scl,以便继续接收数据        ; u2 r! x/ C+ u. g1 d7 o; U! K) f
        _nop_();
4 ]  S6 Q8 ~& f4 r9 f; M5 X}

1 `% `; O$ S& E) w. S, [void AT24Cxx_writeByte(u16 address,u8 dataByte)//向24cxx写一字节数据4 T$ N+ e" ~- c! y) r, l8 l" v2 |* c$ r
{0 i! r0 Y' E  n+ M- N
        u8 largePage     = address/256;          //24c04是512字节(寻址范围0~511),largePage最大值是11 i/ a' L2 v% o4 V! p4 {2 V( a
        u8 addressOffset = address%256;   //largePage=0的话地址范围是(0~255), p5 T* @9 j: z
        iic_start();
+ E2 ?$ w0 C" G0 k' g        iic_sendByte(0xa0|(largePage<<1));//控制字,前4位固定1010,后三位是器件地址,末位0是写7 e: a( b$ o1 u$ \+ N
        iic_checkACK();                                      //mcu处理应答信号
2 ]) J) |* j' y, l% C, t# l( K/ D        iic_sendByte(addressOffset);            //指定要写入的器件内地址在        largePage块中的偏移+ ]3 ]+ C" M* m8 s
        iic_checkACK();3 P, k" E# ~7 R" _8 f3 I7 L- X. E
        iic_sendByte(dataByte);                   //写数据
* S6 n. O+ A( ]- P; P        iic_checkACK();
1 @( i: e, f( s3 @9 L# i8 J' I        iic_stop();* c9 U* E& }. \6 P! |& R0 D
        delayms(2);        
) p" b0 y, B7 a7 k        //按字节写入时,24cxx在接收到停止信号后将数据擦写到内部,这需要时间
! B, A8 j- j! h0 ~        //并且在这段时间内不会响应总线上的任何请求,故让mcu有2毫秒以上的等待        8 X) d% {" I4 U: i0 x3 E
}

2 U: e0 _: |; ?; J: y. m/ N. }, Fvoid AT24Cxx_writeData(u16 address,u8 numBytes,u8* buf)//写入任意长度数据(最大256字节)
' D4 h+ W6 x! E* g6 B. @{
9 K- s% i1 f: C        while(numBytes--); u3 G3 {. o& ?$ X1 |. E9 @
        {
( ?4 ~+ L% Z4 @! U                AT24Cxx_writeByte(address++,*buf++);
( f. N" V/ c- ~3 r        }$ K8 K7 k% k5 T
}
7 X0 _% B  ^/ ^1 i0 ~- h
void AT24Cxx_readData(u16 beginAddr,u8 dataSize,u8* buf)//读取任意长度字节到缓冲区buf中
  H% m7 g9 W- C% Y8 @{
4 `8 n) h/ {. g* ~% B        u8 largePage     = beginAddr/256;        //计算largePage,256字节为一大页- f" h" _. i8 q% u5 r" t8 D& Q
        u8 addressOffset = beginAddr%256;        //计算相对于largePage的偏移
! h6 n# p- U0 z7 I        iic_start();                                                  //起始信号0 {/ \  `# {* z* ?9 u9 Z; K
        iic_sendByte(0xa0|(largePage<<1));        //控制字,写
5 o3 m2 p. o. ]$ ]7 `" h0 U        iic_checkACK();                                                //处理应答信号1 I% f4 l2 C# ^
        iic_sendByte(addressOffset);                //要读取的目标地址偏移3 H  X7 P; N' z# {
        iic_checkACK();                                                //处理应答信号        
6 b- Q! ]+ N% T2 y* _        iic_start();                                                   //发送起始信号# v  _, E) P3 S
        iic_sendByte(0xa1|(largePage<<1));        //控制字,读
. q7 \: o" L4 ]7 o5 b* m. H% K" ~; ~8 A        iic_checkACK();                                                //处理应答信号
3 J  G: e. L8 m% O6 A+ |, P$ b/ K9 E        while(dataSize--)                                        //读取dataSize个字节,最大256个字节: m$ |$ H8 M$ u  T
        {                                                                        //dataSize用u16类型会暴掉ram的
  X# X2 P( A  J. P4 ~4 E                *buf++=iic_readByte();                        //读取一个个字节并保存到缓冲区buf中8 @  h5 q) E% ~5 O# S2 V
                iic_sendACK(dataSize);                  //发送应答,当dataSize为0时mcu发送非应答! n* k2 j  b1 W8 I0 P) ~, i1 D
        }
2 I, }! h( S% w        iic_stop();                                                        //发送停止信号+ o5 [( s& M! K' ~7 c9 f3 L
}
% t4 b; b4 K* l7 J- v
" G7 g# U, h% A  a: Q8 |
! D6 C0 V8 t1 U
void main()//测试# u/ L8 D, u: W  ~2 A* i* l
{
( R, W& \6 B# w5 R0 v$ e        u8 buf[3];                                                                                //接受数据的缓冲区
8 r" J) r/ `0 U: V8 ~        u8 arr[7]={0x06,1,2,3,4,0x55,0x33};                                //待写入的数据% u9 o) c* q  o. U" i
                                                + r( y( ?5 Y# x8 q
        iic_init();                                                                                //总线初始化
. u: ^1 ~, |- s. \4 W        AT24Cxx_writeData(0x00+256,sizeof(arr),arr);        //向指定地址处开始写入7字节的数据
- b, P5 S1 Q& M& ^0 _
        P1=0xff;                                                                                 //调试代码,用P1口的led显示  `0 u  M- B6 `* Y7 W
        delayms(1000);                                                                         //调试代码
4 l6 X( h. ^7 O" M
        AT24Cxx_readData(0x00+256,sizeof(buf),buf);           //从指定地址开始读3个字节, w, T) T& k1 p3 I9 g# L/ Q
        P1=buf[2];        //也就是2                                                                        //led灯显示数值7 ^5 V  A8 W& ^9 X; |1 }
                                                                                       
  ^' D2 o+ c# k, Z+ D* J  u; Q        while(1)
1 L/ @7 Y" u& Z1 ^) m7 B        {
! O- w1 n* u" r! e" ]3 M3 [                P1=~P1;  T$ N5 q/ f3 A% _6 i: F: O0 m
                delayms(500);                6 `' C) K/ |- _! v& L
        }
  J% O/ K' O' m5 L" m$ {$ [}( E! m  j8 g7 x' t8 V7 _! W, q

+ K3 t7 E2 K# f. u% A# x" r1 \. k( L7 x1 V' _/ P, y
. ^( a% H" T) p
% D: S& X3 o( R* L7 t
8 X% i6 ?, S) P. I$ y3 {$ n0 j

" k. ^- t: H% f7 E- p8 i* S3 t# m0 o6 E9 h& T1 W

% j) b- a1 u4 G5 \//my51.h中主要用到
  u) y" C+ k; Q#include<reg52.h>

" }0 R5 A3 m5 X1 A2 ^#include"mytype.h"
" ?: m0 f" m& s( d  ^0 T; V1 Vvoid delayms(u16 ms)     //软延时函数* ~7 Z  R! Q; [" v
{
/ R# o; w+ e8 d) M/ a7 c        u16 i,j;+ h. w' H0 h3 A' d/ K
        for(i=ms;i>0;i--)
% U* P5 q% O0 \/ i4 [        {
: [7 w- e! q, }; n! {        for(j=113;j>0;j--)8 ~+ ^; m" F5 X# O: [
        {}
/ B. k9 i& U  {/ }6 P: H! K' J        }
9 g$ I# t7 c9 b8 ?) h% |}

) y2 L" z: Z5 W" L4 E. B; |8 t: W* b7 Z$ p% D% e  X7 O& O
9 c& {- X9 |% W5 v, V

2 ~  k1 B2 L* h' c3 f) V0 A; ~/ M7 U; u; `
1 L+ n8 g7 v8 J- E

; d! V3 D1 A! G  j0 P# a4 f7 M% q对代码进行了改进
  h* W, B# d' Q! z' W3 h- D去掉了在写数据时的 - d( P& `2 ~! Q. I, f) R4 {% n) K
delayms(2);3 o$ \# l7 }+ M" B+ q
这句软延时代码低效 ,而且没有保障

; ?9 L2 @, `5 ?# @! k- K) Z4 @8 f改成加一个检测函数   g) r" U& {6 c# b7 R+ C' a* q
bool check_icWriteComplete()   //检测eeprom是否对内部擦写完成
! E0 y/ [( o  C) m" A* N9 r& `0 R, w! \{ 5 U. @/ n( ~+ |2 A
iic_start();
' J  C8 t7 V5 [! c iic_sendByte(0xa0); : b+ Q* [; F6 I8 s2 B( ~
return iic_checkACK();
& L3 P9 P4 j3 s; {  \( V}
: X% W9 V; H% B0 N0 D9 T3 D

9 J& c# V2 S, C( v8 f  h3 v

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-10-9 13:46 , Processed in 0.125000 second(s), 23 queries , Gzip On.

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

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

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