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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器7 @4 M* O6 N( r4 g: ~
. I, a$ e, F$ D
# Q& N/ m6 K' X4 R7 b+ R& w1 R* B% l
( C( H6 ~! ]" b3 n7 t" p, C
' p7 t# Y* Y% }
硬件上由于24c01的A0A1A2管脚不允许悬空,故暂时的想法是兼容24c02 ---24c16  }- J, V6 v! u1 U
使用一个dip8封装的芯片插座,A0 A1 A2管脚都悬空即可,换芯片方便
. L4 H( P, K, a* p; @9 k2 ^软件上24c02地址只有8位,而其他型号是大于8位的,故地址参数使用16位& B8 W  |' X/ a5 z% A+ g! g
256个字节作为一个大页,即largePage,测试芯片24c04空间有512字节
9 K5 O8 ?/ G+ T( s+ Y! F! |( x! c
7 U. i4 {1 M/ a4 K0 W+ I1 m5 l! ?上代码,求测试和讨论
) o$ ^' O! t) t- e) C
/ }8 o0 a. a1 L9 Y
; b0 Z7 p/ ^/ s- Q; @# J
#include "MY51.H"$ P; W9 l9 \( b4 K4 a' J
//转载请注明:  求测试讨论8 n! x+ m! y6 q! v
//stc89c52rc,11.0592MHz晶振7 \% j6 t6 [( b9 r$ \7 U
sbit sda=P2^0;                //总线连接口定义6 O" M; M! l- I
sbit scl=P2^1;                //总线连接口定义

1 l* \# \% F8 v( jvoid delayus()                 //需要4个机器周期,大概4.34us7 l+ h% U- P+ x! B
{
) t4 t/ V! z0 M9 C2 I, n" j        ;                                //晶振频率11.0592M,机器周期为1.085微秒5 `( P1 Z6 @9 c8 z" C0 E
}

1 I# n% J( ^" A$ Xvoid iic_start()          //启动信号9 |5 c* g/ K2 n7 T! L, V
{! Y- I- E4 o$ ]3 x# J
        sda=1;
5 Z( q8 ]1 m% U# T& A        scl=1;  n( P/ ^; ]- j' R) E2 J4 B" Q) i
        delayus();                //sda和scl同为高电平保持4.7us以上  m* d1 P% ?! J! {/ Z; M
        _nop_();                //1.085us,共5.78us
. s& [+ `9 b# S1 F        sda=0;                         //下降沿  ]4 \2 [, u! U, j8 v% j
        delayus();                //sda低电平保持4us以上        ,这里是4.34us满足要求
6 g8 U, o% q. n! W+ |}
) d- N/ ^/ C8 A# N2 ~
void iic_stop()                //停止信号9 t- ~; g2 M0 c- U
{% |7 W1 f' ]# S9 U; p* l+ |9 Q
        sda=0;_nop_();        //准备状态
; j! f8 \* T9 a. U        scl=1;; c% j  ~$ I( Q
        delayus();                //该状态稳定时间要求保持4us以上8 p: N9 _. A( c2 w
        sda=1;                        //scl高电平期间,sda来一个上升沿
7 l6 n' z. W/ D" M3 O        delayus();                //sda保持4.7us以上,4.34加上函数返回时间大于4.7us1 i; d/ q  d2 O& N* q+ w
                                        //注:此时scl和sda都为1        3 x; ~  E% d7 d
}
. {0 [7 ^& R* [, _; U& n. t7 q2 K
void iic_sendByte(u8 byteData) //mcu发送一个字节
- F& L3 M5 e: c{
6 J$ M) v) S# n) U$ F        u8 i;
- d+ j0 I$ F% D! S0 j        u8 temp=byteData;( `# ]3 L3 ]9 M7 N4 g, K' h
        for(i=0;i<8;i++)
& I. o6 U- u1 J& X/ A2 c4 r        {8 [+ I. {, n7 x
                temp=temp<<1;    //移动后最高位到了PSW寄存器的CY位中- c  C; n9 N& G+ N( u" Y6 e0 u
                scl=0;                         //准备
" q$ b. ^  f, Z7 X0 ^                _nop_();                 //稳定一下% y+ ~0 `: \" J
                sda=CY;                         //将待发送的数据一位位的放到sda上
- H, r% E# a' u2 r1 \: |                _nop_();* \7 A( h, X% Z' |8 P! j# F& |1 ~
                scl=1;                     //每一个高电平期间,ic器件都会将数据取走4 l+ L6 X1 s) g- a# X
                _nop_();                " j0 l# W7 Q3 w
        }

6 ~! a( c$ t# L4 W7 _5 b        scl=0;                                 //如果写成scl=1;sda=1就是停止信号,不能这么写, d# K) |0 [+ Z+ w5 J
        _nop_();                                / C7 k( @3 S- X& e) @9 u' D/ z
        sda=1;                                 //释放总线,数据总线不用时要释放
7 B0 g) j! Z) h# j' ?6 R/ I3 L9 W        _nop_();
" ?3 T9 f; k$ _+ `- P/ q}
; d$ s" N2 B2 Y* G8 Z4 X
u8 iic_readByte()                         //读一个字节
' F( [- d4 F+ m{
. Q5 e: o6 E( K! {        u8 i,temp;
2 ]+ u6 h: T1 W( u, E        scl=0;                                        //准备读数据
" |# h; p, b) u9 w+ J        _nop_();
- R1 z9 p, a) U- ~        sda=1;                                        //释放总线
3 S6 L7 _+ {: x: w, d# ^( B        _nop_();

) C8 y$ g: B2 O, r. r        for(i=0;i<8;i++)
7 D/ I# D0 @8 O# V2 b( T        {; Z" J+ t! a$ w& K: K7 e% W8 E
                scl=1;                                //mcu开始取数据
6 k6 L2 ~* {0 U: O                delayus();                        //scl为高电平后,ic器件就会将1位数据送到sda上
3 b" ]( H  z4 {/ c' ?* L! A                                                        //总共用时不会大于4.34us,然后就可以让mcu读sda了
3 w2 m7 U  a, t4 i. S! F                temp=(temp<<1)|sda; //读一位保存到temp中
# }9 v- {6 a" Q. b' q/ ^                scl=0;
4 A2 U$ y9 L: `+ r3 s                delayus();                ( B1 i! C. O# u7 a
        }' r' y& t; s6 q* Y5 T
        return temp;
+ w9 f9 r2 x. ~; H4 x}
7 b4 N; t7 l6 |/ A
bool iic_checkACK()                  //处理应答信号2 u$ J  a/ e+ K( D
{# a: X5 \  F, {3 ]; D
        u8 errCounts=255;           //定义超时量为255次
4 Q, a6 o$ p! d" q7 N4 x4 G        scl=1;
. A2 S  m2 ~3 n+ ^' A3 Q% Q        _nop_();
' N# e0 R/ C4 U% _9 ^' o+ g( G4 e4 z        1 b9 K( M' }$ H. H
        while(sda)                          //在一段时间内检测到sda=0的话认为是应答信号
& Z7 G( E  \' L- ?0 {2 r- z        {        ' i  I4 g2 G3 b6 w
                if(0==errCounts)- I2 E# g. p0 j' x0 Z
                {
. r; X6 e2 `. D                        scl=0;                  //钳住总线8 K  k+ q$ Y1 Q' Z4 Q) }& Y
                        _nop_();
- F! O, Q0 U& l/ W' z5 ^                        return FALSE; //没有应答信号2 g) p. _# v3 V/ `$ t' S
                }5 h% k8 f, O: g3 E% r6 A
                errCounts--;, p! B0 H; c9 m" D: ]
        }
; M0 @9 V& j; ]# l8 I; U* O, |
        scl=0;                             //钳住总线,为下1次通信做准备 5 n, z4 B$ i. f2 s9 P- G4 Y( p& c% w
        _nop_();$ y7 s2 A: J2 Y2 u* c+ x
        return TRUE;             //成功处理应答信号% {2 t! u, ?9 d0 w- b; X
}

# ]: j) X( c" ^void iic_init()                     //总线初始化
* O/ O3 a! ~+ O{  L$ _9 M6 ]) g# S
        scl=1;8 j, C1 n( ]  y9 f
        sda=1;3 W. d% J$ |7 R8 B
        delayus();: M3 _8 B+ R6 X3 W( |
}
9 ~' z; E; ]' C9 z+ u% o4 c# Q
void iic_sendACK(bool b_ACK)  //发送应答或非应答信号1 v# I+ r( N* d+ _
{
9 I8 J* t( x- F- P/ r" y        scl=0;                        //准备
. {  R) E% P1 I# U  u        _nop_();
, _; t1 M- i0 w* a% Z. ^
        if(b_ACK)                //ACK        发送应该信号
+ j: M8 O- ]; l2 U3 b2 q        {
0 D2 s. l6 P1 b7 e0 m( |, V                sda=0;
) k+ R, O5 I# |1 X' q1 r/ H0 \5 w: Z        }* n- m2 l1 i" I
        else                        //unACK        发送非应答信号
5 T2 n6 T# B' y% D3 q        {
% V( D: O. Z2 `7 q: L                sda=1;
9 R( U1 n: R! x% @# ^( L        }
; `! H+ T9 a' L3 N
        _nop_();2 z7 @+ e% k( Y3 [& \& T" Y
        scl=1;, h3 {" A( @: }2 ?
        delayus();                 //大于4us的延时* A3 Z# M; t  w* {) ^. p
        scl=0;                    //钳住scl,以便继续接收数据        
2 Y* r2 G6 g& s# _5 Z3 i- A        _nop_();) i& Y+ D( s4 d8 |* n% m: D' b9 ]
}

! P7 R/ T# p. {3 [, \- \, J' s# gvoid AT24Cxx_writeByte(u16 address,u8 dataByte)//向24cxx写一字节数据
- R# X6 N8 m. n{
/ U" c6 S% N, s/ }( L' g- T        u8 largePage     = address/256;          //24c04是512字节(寻址范围0~511),largePage最大值是1# s8 k9 D( v$ g
        u8 addressOffset = address%256;   //largePage=0的话地址范围是(0~255)
; b+ t5 `7 u9 @, u        iic_start();
6 a- Z6 I; M2 w0 C        iic_sendByte(0xa0|(largePage<<1));//控制字,前4位固定1010,后三位是器件地址,末位0是写% ^( I; |) ]7 Y& v
        iic_checkACK();                                      //mcu处理应答信号
+ ^0 y( ]4 k( x7 v" K        iic_sendByte(addressOffset);            //指定要写入的器件内地址在        largePage块中的偏移
% F" u# ?3 f) U" U6 ]+ F        iic_checkACK();
2 o; Y8 ^+ O2 y6 {% D        iic_sendByte(dataByte);                   //写数据
! I- E+ _( R$ i3 R2 _- x" t        iic_checkACK();! h- H: B! O: l. n# X9 L9 Z% Z( M
        iic_stop();
2 B9 }' a  A% j- P" F! D! F        delayms(2);        . Q/ N' _' V- ~! R- E
        //按字节写入时,24cxx在接收到停止信号后将数据擦写到内部,这需要时间8 [4 T* B0 G; {* w* @4 j5 H
        //并且在这段时间内不会响应总线上的任何请求,故让mcu有2毫秒以上的等待        
# E8 o) q7 h) ~5 G) j9 X0 G6 J}

- s; W' h8 A: s& v- a( z0 \4 avoid AT24Cxx_writeData(u16 address,u8 numBytes,u8* buf)//写入任意长度数据(最大256字节)
% D9 x9 b* `) n{  c0 B( E) h9 H+ \" P% `: O7 w
        while(numBytes--)
3 d) Z8 }* p3 U) S2 s9 P0 [; V5 A        {  h0 p# }1 w, M# \; u
                AT24Cxx_writeByte(address++,*buf++);
/ I) G$ X1 F0 }5 k        }8 K8 j; |9 L+ O$ p( l9 g- \  b
}

2 n% l0 T! W, ~3 ^+ Cvoid AT24Cxx_readData(u16 beginAddr,u8 dataSize,u8* buf)//读取任意长度字节到缓冲区buf中* H3 G7 g9 K4 Q/ |3 w! _
{7 r5 d& `* L5 |
        u8 largePage     = beginAddr/256;        //计算largePage,256字节为一大页! `  e5 U; i  p; U2 h
        u8 addressOffset = beginAddr%256;        //计算相对于largePage的偏移
* g7 d2 ^( C1 O: ]! ?4 T3 x: ~        iic_start();                                                  //起始信号
9 d6 X4 S( H$ z( P- N# x        iic_sendByte(0xa0|(largePage<<1));        //控制字,写
$ X0 |6 q) z7 T+ q# J3 I        iic_checkACK();                                                //处理应答信号
+ N7 J9 t3 C; v1 I4 t- A        iic_sendByte(addressOffset);                //要读取的目标地址偏移
! B6 Z  N6 D* T        iic_checkACK();                                                //处理应答信号        ( j' w* _( ?* P1 w/ q
        iic_start();                                                   //发送起始信号
, n2 i) [1 B* _8 W9 g        iic_sendByte(0xa1|(largePage<<1));        //控制字,读
3 @5 [& o, u! r1 O3 M        iic_checkACK();                                                //处理应答信号
4 A, H$ I0 T, Y$ O        while(dataSize--)                                        //读取dataSize个字节,最大256个字节/ w4 H4 v% ]/ u* p6 n
        {                                                                        //dataSize用u16类型会暴掉ram的9 z- J0 I4 K9 ~' x; m# p0 s
                *buf++=iic_readByte();                        //读取一个个字节并保存到缓冲区buf中! I$ a7 Z0 \* ^; l6 A( [
                iic_sendACK(dataSize);                  //发送应答,当dataSize为0时mcu发送非应答
. g% B% ?6 |. A! z* N- y        }8 S! I4 l* K! {: O% G
        iic_stop();                                                        //发送停止信号; E; `* r+ l" w: E& f1 `+ p% _
}
8 @: Z  N. M0 L; H0 D8 k6 f

/ ?# l8 \6 j% z; _6 L

" T. \0 \( U% D/ w! L, Tvoid main()//测试
) T0 ?: f- K4 [9 j& _{/ N0 h0 ~2 [% ~6 Q
        u8 buf[3];                                                                                //接受数据的缓冲区
: W9 K/ {0 U1 s' Z, y* r3 F        u8 arr[7]={0x06,1,2,3,4,0x55,0x33};                                //待写入的数据, M5 W. z) {5 e$ _1 b
                                                7 w- Z2 e# Q( X! z* ~0 ^" P7 k
        iic_init();                                                                                //总线初始化- L; Y* \  P' l* A5 X2 ?
        AT24Cxx_writeData(0x00+256,sizeof(arr),arr);        //向指定地址处开始写入7字节的数据

( A5 L( Q% t& q$ f4 d) V        P1=0xff;                                                                                 //调试代码,用P1口的led显示
! J. d) Z& v1 k5 _        delayms(1000);                                                                         //调试代码
& i, u. R. \$ h8 b' Z
        AT24Cxx_readData(0x00+256,sizeof(buf),buf);           //从指定地址开始读3个字节6 {& l6 l* Y- Z3 X% M5 q) [
        P1=buf[2];        //也就是2                                                                        //led灯显示数值
8 I, B  d: }$ a# K: n: j0 D                                                                                        % E2 \* A  I% P3 I2 _7 N, o
        while(1)! i' t- S0 K9 T: h
        {' S' p& R& W" s" ^; S2 U) q  }/ ~
                P1=~P1;4 P2 p! O* t  d7 D$ O% b; U0 \
                delayms(500);               
$ B# e9 R- O7 [; x, t# N: C        }
# g9 o8 [* Q* D1 |# e}# c" ?/ u. y3 B5 M( e" G7 Z

! L8 L$ Q6 }+ F( ?9 q2 M9 u, V
9 z: p' n, G) f5 Z
7 m0 A2 m# I' N  a
3 ?6 j/ p% e$ c  w( D7 R1 c; \  m( n. \
2 W( Z# ~- ?: T) q6 I$ X5 W6 |1 c
+ h4 O6 n7 F/ i3 M+ d6 W) x( P, S

. B3 G& `! Z) g& \. ]//my51.h中主要用到& S% S# B4 h% c2 `  e, n$ g
#include<reg52.h>

4 j- i+ ?0 i7 x( m+ k% r#include"mytype.h"
. O: P) n: A  H; pvoid delayms(u16 ms)     //软延时函数0 m9 `/ K5 i" h- r
{/ ~; }. h. X* ?6 e
        u16 i,j;
' Q- d1 I4 i/ R8 y3 d' |        for(i=ms;i>0;i--)' j% A& g1 R# d9 R
        {
3 [2 k' ~: N0 H/ m. m2 \% L$ O        for(j=113;j>0;j--)
1 O4 Q7 }* B. y: a        {}
: W7 B; {- U% N! B) Y        }
) b" S+ q/ @( n& e+ v4 r}
. ]2 b" Z0 @( V3 v( l

5 x) r/ Y% t  W* b$ k- s
% A' N; N% Z# `2 ]+ Y, I" n6 Y9 n2 ?6 e

# A4 B! j, `% R0 Q7 ~9 f2 z& s. _# c: i& r; \8 q* _
2 [  N# R0 e5 _' @
对代码进行了改进
3 U4 |, A2 i7 G9 |! X去掉了在写数据时的
) j) X' L5 z" {; r( a( fdelayms(2);5 w$ D& U' t/ Z5 o1 n/ _. j: i' T. U1 Z
这句软延时代码低效 ,而且没有保障

! f' p" M" H* v0 C. K. H- Y+ g, T% P7 B改成加一个检测函数
9 r; y4 h, D7 {) d: Kbool check_icWriteComplete()   //检测eeprom是否对内部擦写完成 9 i# u# f8 ?3 `2 _9 B$ @
{
$ r0 C# k8 Y+ b7 U! { iic_start();
4 `7 |: ~' ~8 }! D iic_sendByte(0xa0); / q) ~( `9 _( z
return iic_checkACK(); , K, X8 Y5 Q! H- C' ^0 j
}
1 u" V" w+ C& S# `

1 a! ]1 k, A. f% g

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-6-10 20:55 , Processed in 0.093750 second(s), 23 queries , Gzip On.

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

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

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