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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器
+ O* Y- B/ v, W1 _
6 m( Q8 J- u! Z6 y2 J$ v' g" D9 [
- W! |2 l+ ~& k& n: O4 |: v

" W1 d! }4 `$ t" J/ p( z3 _硬件上由于24c01的A0A1A2管脚不允许悬空,故暂时的想法是兼容24c02 ---24c16
7 w9 O* j4 W( H5 i1 I/ B! X使用一个dip8封装的芯片插座,A0 A1 A2管脚都悬空即可,换芯片方便
+ G5 z" B' i( Q+ e" \7 y+ v# t软件上24c02地址只有8位,而其他型号是大于8位的,故地址参数使用16位2 W+ Y' e6 Z+ Z9 b
256个字节作为一个大页,即largePage,测试芯片24c04空间有512字节% O9 g; O3 G- I% d8 [
! _" c* S" h3 I! t! C5 S0 e
上代码,求测试和讨论

/ M2 e( P$ z: P/ {5 ]) [: K0 e8 p0 c
: a+ y/ H- o) O0 K7 o: [  \2 ?
#include "MY51.H": i2 \0 S+ H% L- D& b: |# B. ]
//转载请注明:  求测试讨论# a& @. _9 J; c0 @- T( W" O% V' ~
//stc89c52rc,11.0592MHz晶振
% M7 O) d3 S, f- zsbit sda=P2^0;                //总线连接口定义
3 d! o- U* r& V+ k/ ^sbit scl=P2^1;                //总线连接口定义

& i4 a8 N3 O7 \6 k3 d- K, ]void delayus()                 //需要4个机器周期,大概4.34us6 G- R, b" m( A* b* t
{" j) D$ t- x0 `
        ;                                //晶振频率11.0592M,机器周期为1.085微秒* _! B$ |& z! T9 n4 R1 e7 l
}
4 ]6 c* M, a  O
void iic_start()          //启动信号
4 s6 \# t0 r8 J7 K$ D0 c- J+ T+ X& a{
4 s! @0 ~3 \2 a) V0 [        sda=1;
! q7 R! }" \7 y0 t7 F        scl=1;2 N) g4 X8 W4 q
        delayus();                //sda和scl同为高电平保持4.7us以上
) ?/ H5 V' @* Y" O2 h2 I- t        _nop_();                //1.085us,共5.78us
- O  F% G( s% t5 K        sda=0;                         //下降沿8 T* E4 L5 Q& |/ d: a/ Y# S
        delayus();                //sda低电平保持4us以上        ,这里是4.34us满足要求! s5 h/ m' f0 N2 X" ?% W4 i5 |+ M
}
; j, v* M# @5 }7 {, F
void iic_stop()                //停止信号
/ d2 c0 x% Q2 u- t6 S0 a  I{) }) J2 r/ v  A5 j/ @3 N& x5 g' b( U
        sda=0;_nop_();        //准备状态9 G: b1 C8 r9 V& j) [# m$ b6 B
        scl=1;
. p% }) h  V. [" F$ z  M/ o        delayus();                //该状态稳定时间要求保持4us以上" X- G% V0 t. I0 K! P: H
        sda=1;                        //scl高电平期间,sda来一个上升沿6 m. h0 p% B3 H: w
        delayus();                //sda保持4.7us以上,4.34加上函数返回时间大于4.7us- Z) v" k8 {3 n3 @
                                        //注:此时scl和sda都为1        
* i- |* A; @+ s0 L' L}
3 b6 c4 H8 c$ [, A+ s, O
void iic_sendByte(u8 byteData) //mcu发送一个字节
$ R$ U5 ~3 Q  T# b6 R( Q{
. H! y; ~: s- w        u8 i;/ ]# W* s1 O8 |5 }/ W
        u8 temp=byteData;
. z- c+ B8 W/ K+ H2 ~7 I        for(i=0;i<8;i++)
* X  t, ~! o: q0 {: v, X0 G/ C6 z        {
6 p7 p7 d/ U& Z) K, S1 @                temp=temp<<1;    //移动后最高位到了PSW寄存器的CY位中3 x" @4 C( N. |
                scl=0;                         //准备. A  j  u" K$ V( M
                _nop_();                 //稳定一下
2 A1 B. X) W+ c" m; s- }5 `) y& T                sda=CY;                         //将待发送的数据一位位的放到sda上
- s7 T  O, s: h. H' j+ {" M                _nop_();
" ]1 y/ M: N0 u2 f                scl=1;                     //每一个高电平期间,ic器件都会将数据取走# E3 G8 l2 p! n9 e! w4 L8 L! U* J
                _nop_();               
4 e# q" W, L! Z        }
+ N5 A9 A0 J) N7 j$ ?% r
        scl=0;                                 //如果写成scl=1;sda=1就是停止信号,不能这么写
- D! v& p; S' z4 ?( z: l: N        _nop_();                                  X7 X# ?/ j* u- B* a! [
        sda=1;                                 //释放总线,数据总线不用时要释放
' U3 f- v" m  w6 G% q  M1 |        _nop_();
& c  n3 o; m% D8 V( y. q}
9 |9 o$ y: g, q1 e
u8 iic_readByte()                         //读一个字节
: Z' {! j9 u: A5 Z: _  F; Y3 E+ Z{8 u% N1 f# S8 `0 A: j+ Y# ?
        u8 i,temp;
* y: Z1 i  P* G% ]# I. E        scl=0;                                        //准备读数据
* z/ J7 M6 Y& h# E  T+ U' z0 I        _nop_();! m0 Z8 `8 h) d2 i1 D  c
        sda=1;                                        //释放总线
. S7 @5 O6 U  X& [3 M        _nop_();

) X! D7 o) n: N& r9 @7 [        for(i=0;i<8;i++)
$ _) `( H; O" t1 ^        {
* h- j, p6 H  E1 m                scl=1;                                //mcu开始取数据; _& r: U" Z- E( _" x
                delayus();                        //scl为高电平后,ic器件就会将1位数据送到sda上
" `- K, d3 ?/ I- @9 e                                                        //总共用时不会大于4.34us,然后就可以让mcu读sda了
& d( @+ z# d  N9 ?                temp=(temp<<1)|sda; //读一位保存到temp中6 Q: @; L  w% f4 c6 }
                scl=0;
- M# |* `4 G5 p, J2 Q) n) [                delayus();               
0 w3 }) f5 i6 V; }$ V        }
0 {# ^+ Z% w% P! I5 M/ l  y        return temp;
- X+ V+ r  E1 i6 }3 M6 B) {5 W: c: J}
, Z% E  m+ C1 j6 n+ `: k8 ~
bool iic_checkACK()                  //处理应答信号
* J2 ?7 e1 z1 m+ s{. r0 i9 I! J) H
        u8 errCounts=255;           //定义超时量为255次8 I5 W- Z5 w$ r+ t$ n% x  N7 U0 k* ^
        scl=1;  T$ K4 p( {# X  T1 J, K6 _
        _nop_();, g* V+ k0 M9 Z  Q. E2 z
        * S" g$ [% x! ^2 F. _: h
        while(sda)                          //在一段时间内检测到sda=0的话认为是应答信号- U; ^. P% v8 I) a( L
        {        6 j+ L5 w7 y! [
                if(0==errCounts)1 K' h/ R$ E4 X% V2 W& s
                {
, l: w! g' b& C3 ~1 l) N# u                        scl=0;                  //钳住总线9 {/ `9 W( n6 N* q1 E
                        _nop_();  s# V  Z/ J- }6 S
                        return FALSE; //没有应答信号" }0 n* J* q1 n4 p0 ^
                }
' u: ~$ q' j# |( Y( p/ l3 j8 D' J9 E                errCounts--;
6 B, B+ n2 @3 N* {        }

; F/ C8 ~7 d+ D8 Y, b        scl=0;                             //钳住总线,为下1次通信做准备 ! C) P& F! [* Y  {
        _nop_();# `4 z: D! F$ T  I* |8 W
        return TRUE;             //成功处理应答信号
- h- Z9 e# ?+ j) R2 Z}

+ [9 B' Z4 n9 g9 M3 Mvoid iic_init()                     //总线初始化
5 |8 k/ I& X" I6 a5 o{- m9 [& c/ g3 `" u
        scl=1;3 u7 O; Z) U1 `7 G1 p3 Z( S
        sda=1;& }- M' T* p& O% A" Q$ g$ F$ ^# U0 |' e
        delayus();
- }( H6 k- R; w  _9 m( R}

5 x" H3 I6 S$ r4 wvoid iic_sendACK(bool b_ACK)  //发送应答或非应答信号
6 }  Z' C& e" Z, g( l9 f' x) r{
4 t+ C8 j" T/ o, b! |* S! b7 F/ F* b        scl=0;                        //准备# {9 ?' X3 Y  ?; O
        _nop_();

& v) \5 A$ y6 ^9 R1 c' e9 a        if(b_ACK)                //ACK        发送应该信号
5 e+ h# O0 u! A4 t& K. [/ H% R# I' f        {+ q- X" E% A" F; v, H1 j
                sda=0;3 i+ |5 Z; k! y- |
        }
% W4 Z# X( K% H9 e+ v- N& N( R        else                        //unACK        发送非应答信号6 C- d, }/ k4 t( F: A9 ?
        {. O- H, c* M& |: j
                sda=1;
' n$ O% @2 D0 t7 [8 P        }
  ?& {# n, }; O! `
        _nop_();
: |6 \8 e( M( E1 ]- s        scl=1;4 l; m1 _; N: Q; z8 Q- F9 T
        delayus();                 //大于4us的延时9 c& n- {; y" V! a' ~
        scl=0;                    //钳住scl,以便继续接收数据        9 F, m! A3 c' J9 t
        _nop_();" e% g: O* r+ D2 ?, Y% L5 {
}

) R% S$ U5 z* K! `% S: t7 i; E% r2 Ivoid AT24Cxx_writeByte(u16 address,u8 dataByte)//向24cxx写一字节数据. p, u/ C. o# ~- T
{- [( P2 b# s& I/ M: R& ]
        u8 largePage     = address/256;          //24c04是512字节(寻址范围0~511),largePage最大值是1
  J3 i0 k1 K! d8 A        u8 addressOffset = address%256;   //largePage=0的话地址范围是(0~255)9 \9 \9 `4 q+ N  p, B
        iic_start();2 z  r& X0 G* d9 ^) {( j
        iic_sendByte(0xa0|(largePage<<1));//控制字,前4位固定1010,后三位是器件地址,末位0是写' J( S& o1 D: O6 A- e+ r
        iic_checkACK();                                      //mcu处理应答信号# G) E+ h% \. S$ G) w
        iic_sendByte(addressOffset);            //指定要写入的器件内地址在        largePage块中的偏移
8 ^0 r1 W1 a. X* V        iic_checkACK();7 V  ?  V8 u0 w$ p5 L' K
        iic_sendByte(dataByte);                   //写数据* @6 I6 a' \. P! ~9 [, ~5 E8 y
        iic_checkACK();8 z; D- N" w  Q4 L% J- w
        iic_stop();& A% M* @' g) P6 t. Z4 m; m0 r1 ?3 x
        delayms(2);        ! |* R9 `% v. d4 s) B5 C
        //按字节写入时,24cxx在接收到停止信号后将数据擦写到内部,这需要时间. h; |. H3 l0 q" j& [
        //并且在这段时间内不会响应总线上的任何请求,故让mcu有2毫秒以上的等待        
- r; k4 b' T* N# V) M}
$ ~6 |' o  \" P
void AT24Cxx_writeData(u16 address,u8 numBytes,u8* buf)//写入任意长度数据(最大256字节)
& {+ h4 s; ?/ t5 ~& m9 I$ i{
/ w7 F: k  U# `7 t- |        while(numBytes--), y# }6 Z* y0 o2 n5 L$ O
        {' C, n: u' O2 j. O2 i
                AT24Cxx_writeByte(address++,*buf++);) O/ B7 w+ x" z4 |6 k9 E
        }
7 t( x4 Y* ?+ l+ f( y+ H+ J}
" j' l. g. J$ b; _) `% L( R
void AT24Cxx_readData(u16 beginAddr,u8 dataSize,u8* buf)//读取任意长度字节到缓冲区buf中7 E* ~9 V2 [3 @0 M0 H
{( R8 m) r  \. n6 d
        u8 largePage     = beginAddr/256;        //计算largePage,256字节为一大页
1 b9 B2 q' B3 D4 e( @9 n        u8 addressOffset = beginAddr%256;        //计算相对于largePage的偏移+ C1 E/ R% M. m% k
        iic_start();                                                  //起始信号8 z+ }" s* w6 c$ S/ t
        iic_sendByte(0xa0|(largePage<<1));        //控制字,写
* Y+ `# {: ?8 [7 F# w! i9 B# y0 |        iic_checkACK();                                                //处理应答信号- ~# _* Y+ S5 V$ t/ t5 B, s; O9 }
        iic_sendByte(addressOffset);                //要读取的目标地址偏移
1 L  R+ }! |; H) j        iic_checkACK();                                                //处理应答信号        
. W# ]. J- I# Q* T- ~" V9 Y4 I        iic_start();                                                   //发送起始信号
4 ]9 m# G4 i7 f8 l        iic_sendByte(0xa1|(largePage<<1));        //控制字,读' z. i: ?4 N7 U/ l/ [- r  @
        iic_checkACK();                                                //处理应答信号
( _2 O7 C, b4 ]' r+ ~        while(dataSize--)                                        //读取dataSize个字节,最大256个字节& K% V" n2 e: a
        {                                                                        //dataSize用u16类型会暴掉ram的
! A  |9 O5 T" ]8 g* t5 |                *buf++=iic_readByte();                        //读取一个个字节并保存到缓冲区buf中; m4 Z3 L2 G5 r1 ^  A5 `) J
                iic_sendACK(dataSize);                  //发送应答,当dataSize为0时mcu发送非应答( k3 R; q: D( |: k; U! N
        }4 }9 n5 |, l; X+ q% ]  C
        iic_stop();                                                        //发送停止信号7 P) Y1 w1 U# d' H3 ^; p1 Q$ _( g
}

" V  x/ M8 _; @9 h
$ q* t2 K. C7 V# O# H; m- l$ w

  Z+ x8 T2 G, U( s$ q( _! H# K6 fvoid main()//测试0 z& z' l7 H7 ~1 c
{
3 J/ l# K9 O0 o3 \8 ]        u8 buf[3];                                                                                //接受数据的缓冲区) [1 u6 D1 ?$ x- J
        u8 arr[7]={0x06,1,2,3,4,0x55,0x33};                                //待写入的数据
. Y' |4 T. s' C( @9 p. ]: c                                                
6 M$ }3 }2 X  `        iic_init();                                                                                //总线初始化8 L/ Y" `/ j9 t4 n
        AT24Cxx_writeData(0x00+256,sizeof(arr),arr);        //向指定地址处开始写入7字节的数据
. ~9 [( o+ u9 d9 E7 |; U+ s
        P1=0xff;                                                                                 //调试代码,用P1口的led显示5 o1 P% V8 F( [0 P7 ^/ f' i  J( }
        delayms(1000);                                                                         //调试代码

  |! y1 h6 v0 V- J/ K        AT24Cxx_readData(0x00+256,sizeof(buf),buf);           //从指定地址开始读3个字节1 ^9 o/ F4 L; L- D. h+ R* ], l
        P1=buf[2];        //也就是2                                                                        //led灯显示数值* R( C. I- q9 p3 g2 U
                                                                                        2 k0 Q3 V# |+ `7 x, Z8 X2 s) x, l( y
        while(1)
% d# x) y$ i) v7 S1 `5 h8 D9 R9 B        {
2 i2 q) u- h/ ^* @# C9 k% J% o                P1=~P1;* |4 Q' D8 L2 C" ^! H  a
                delayms(500);               
/ ]. d7 G6 n4 E( b- s% L        } 7 e9 u6 z9 y9 G2 v  d! @" t
}
4 M, G/ K* ^, D& I, [7 C6 C7 D2 I
- T. c6 L! A9 q2 X- a( T2 {1 i0 T

) }6 m6 G+ \; v! `1 t3 {) ]' S
) g0 q1 ^" v# u5 x% l$ `: I# E  D

1 d- e1 k( F# o% f9 S& ]9 o
5 n, i' B, S4 u8 w2 e: f2 U. q
" L( @" F( i* |, g8 W) [+ e7 T) }% j" t2 u" m- V, @, `
//my51.h中主要用到5 f. ~- i' n7 _/ Y$ w' }
#include<reg52.h>
/ J4 w- u" U& T1 g' k
#include"mytype.h"
- H: F+ H, X, x( f  ivoid delayms(u16 ms)     //软延时函数
) g$ e9 B% q5 ?8 s6 _{
7 U/ B: S$ W1 V3 o* A! v        u16 i,j;! F- d' N7 E" j% y3 B3 f
        for(i=ms;i>0;i--)1 }: J5 }8 l; {
        {2 b: \& s& n) N5 V8 S0 V$ i+ M
        for(j=113;j>0;j--)) O( r1 ~& I2 m% L8 s
        {}
/ ]8 ~/ |: ]5 I+ O5 K        }  o/ W* N$ r; P# {# {6 M
}

- m* r  \3 v0 }5 t. [. W# s6 b$ q7 p* {2 D! O' g9 c$ T' X5 b, j

2 `! W' d9 O( ^" e" ^+ I+ ^; e. ^# H1 S$ x1 S$ [4 S% ]
0 C  B; |7 j2 {& [8 V; H9 `4 {) P
- V8 n9 J$ |' G/ r5 n  L

( E3 \9 L6 {; U6 D' h8 }0 s3 {( `对代码进行了改进 2 V2 m4 w# j9 H4 U4 _& ?' M
去掉了在写数据时的 7 |" r# L! \, X% c  x
delayms(2);# V) s& q- F! @
这句软延时代码低效 ,而且没有保障

3 ^& f" t, a- a( ?3 R& W4 g改成加一个检测函数
. U5 B' A8 e& R, E) [bool check_icWriteComplete()   //检测eeprom是否对内部擦写完成 ' j0 s% ~, \' k1 j" d. L+ T
{ % L, R1 H/ n: Z) `0 z
iic_start();
# O" t; W' L. h' \1 Y8 t iic_sendByte(0xa0);
; O9 w/ I2 ^$ Q1 [9 K return iic_checkACK(); / ^4 _2 l/ P0 z6 q: f0 h  R
}

& A+ ?6 Y+ P3 l( d; |0 ]. A# f# k/ f

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-9-11 03:24 , Processed in 0.109375 second(s), 23 queries , Gzip On.

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

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

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