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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器2 E6 `; c- D* D
' `1 \; v6 [  m$ r% Z5 X$ L# f

0 ]) p' c3 }5 `$ M
! @5 q. Y+ ^1 I
, w0 b- t4 Z. o3 d# Y. [5 F硬件上由于24c01的A0A1A2管脚不允许悬空,故暂时的想法是兼容24c02 ---24c16
9 u  ~# F/ g7 C& c7 O" Z使用一个dip8封装的芯片插座,A0 A1 A2管脚都悬空即可,换芯片方便
+ U& F2 `  C+ W; h$ u& r软件上24c02地址只有8位,而其他型号是大于8位的,故地址参数使用16位% X2 O: w0 S: s. a* h; z1 o0 [( q7 v) v
256个字节作为一个大页,即largePage,测试芯片24c04空间有512字节
6 Z+ z: c# o, ]( ^$ S! v) \' t3 A% o& ~: ~% y3 V
上代码,求测试和讨论

% V+ s0 D/ B* o2 B1 I' U
# l) v1 K: f' _
, _) w/ U. [; e7 e+ l( y8 F#include "MY51.H"0 p5 l% K1 t" `& @
//转载请注明:  求测试讨论* z1 d/ B6 [/ |
//stc89c52rc,11.0592MHz晶振4 S% G( J9 h5 X+ k/ d6 ?2 a
sbit sda=P2^0;                //总线连接口定义
. y1 G! p9 k, J7 e7 g& ~sbit scl=P2^1;                //总线连接口定义

4 ?( W1 O! N$ N. Yvoid delayus()                 //需要4个机器周期,大概4.34us
6 j  ]2 e. T3 e" |$ S$ f9 A7 L. L{
  l" L1 p; K: N8 w4 l: v        ;                                //晶振频率11.0592M,机器周期为1.085微秒6 C$ J: `: {, j1 e
}

! O" {' C& i  l1 j* F9 w  evoid iic_start()          //启动信号
# s) Z5 m; T$ k4 O; m1 r2 p% y; b{# `9 E" ^% ?6 ~) Y( L" @
        sda=1;- ~/ M1 i' U/ v( a- q5 R4 k
        scl=1;5 _) z. T( y  l5 `/ p# x- f
        delayus();                //sda和scl同为高电平保持4.7us以上
- V5 D% M: v2 j9 t1 o) ~! `        _nop_();                //1.085us,共5.78us/ \& O* `1 s) K* ~5 U/ g
        sda=0;                         //下降沿* Z% W. u+ k& i0 Q7 S2 \
        delayus();                //sda低电平保持4us以上        ,这里是4.34us满足要求
' Z+ n" g  ?) ^$ Q5 u6 f( O}

9 u7 l" t1 f+ [. |& E9 Q: K2 P6 yvoid iic_stop()                //停止信号
. A. `* V" ]. |/ H{1 R5 O' N  |9 e# D" g( L" ?* }
        sda=0;_nop_();        //准备状态
% V  U1 F- T& I! A3 e) }5 x        scl=1;
: X% M+ U" R4 k* k- n2 J4 L        delayus();                //该状态稳定时间要求保持4us以上! O" g. X: H, i( _% F
        sda=1;                        //scl高电平期间,sda来一个上升沿
& ]# c) R7 `% Y/ F/ a        delayus();                //sda保持4.7us以上,4.34加上函数返回时间大于4.7us
4 l! v  H! \5 i/ V, z! L                                        //注:此时scl和sda都为1        
9 a8 J# m; n$ ]7 e}
, \1 _  i/ \! R& \
void iic_sendByte(u8 byteData) //mcu发送一个字节
5 Y) Y" r! C" y- n  L{! l6 B( L) l8 a* h
        u8 i;
6 g' ^* i+ N7 Q: @$ l: Y7 @# J+ J: M        u8 temp=byteData;
. w7 t2 c0 H" s        for(i=0;i<8;i++)
2 ]. A0 E1 x0 x; V        {
$ a, D$ x3 L9 S/ s                temp=temp<<1;    //移动后最高位到了PSW寄存器的CY位中
+ a' p9 |# v! s1 W                scl=0;                         //准备
. k1 f+ e) L1 _7 O- ]6 I) s                _nop_();                 //稳定一下
9 s+ M4 M+ d& X3 b  n$ y                sda=CY;                         //将待发送的数据一位位的放到sda上
  E$ K& g$ J9 n; i2 }                _nop_();
: M- D+ Q% ]! j8 \6 J9 A4 f                scl=1;                     //每一个高电平期间,ic器件都会将数据取走
  I( J3 C- |! D                _nop_();                9 `2 Y; b9 k7 N+ B3 b9 R! e1 n4 E
        }
  C  W. g; }* \
        scl=0;                                 //如果写成scl=1;sda=1就是停止信号,不能这么写
& S1 K3 P4 t" I: Z+ W. d2 u        _nop_();                                
2 P: A7 f* c" z, c; v        sda=1;                                 //释放总线,数据总线不用时要释放! c" H% f9 p1 h# L9 {' U6 m
        _nop_();2 n4 S4 k9 b+ r# a' d3 L  S
}

7 q, U4 o& t2 ~9 Q+ J* Iu8 iic_readByte()                         //读一个字节
3 S) [, x+ l- s7 U* t7 S8 I9 S{2 i$ o/ e( u/ Z5 C% ]6 T
        u8 i,temp;8 y. e9 g% I6 [/ P4 T
        scl=0;                                        //准备读数据
. _, C6 B% V) J& M5 [        _nop_();/ [3 @0 a( S- h4 Q( G2 D& E
        sda=1;                                        //释放总线1 L$ p5 Y! t5 i, L& h
        _nop_();
( C8 x2 z- v) z
        for(i=0;i<8;i++)
. E8 M4 i+ G# \% P  |. T        {
  }1 V' I# Z9 H0 ]1 ]& T8 C" W                scl=1;                                //mcu开始取数据
0 }; ^" {( b0 e& E6 f                delayus();                        //scl为高电平后,ic器件就会将1位数据送到sda上
% u; D, p: P% v  N( h5 b$ P                                                        //总共用时不会大于4.34us,然后就可以让mcu读sda了) N! l) E! [, y# X; O0 w1 N3 t3 s0 o
                temp=(temp<<1)|sda; //读一位保存到temp中
. W% l& ~2 a) l                scl=0;
$ O1 Y2 X$ U5 L1 C7 o% v/ M! P                delayus();               
+ m' V2 A* ?* `" y9 h, M3 |        }) y; I! z/ F. o% U# A8 q3 x! s' Q
        return temp;3 t5 v$ d+ G- u" H
}

: W7 u6 K% K2 G+ B+ U' ^bool iic_checkACK()                  //处理应答信号4 W( f% V9 C- s: z
{5 [1 Y, |) O1 a5 N4 N9 X
        u8 errCounts=255;           //定义超时量为255次: t7 F8 N- ]( Z  r: S+ _9 Z; \
        scl=1;2 u) y9 H6 d0 U2 z) u' u9 T7 [
        _nop_();
* `& x& U% }7 d        - ~. R$ G" A' _0 U% I2 s
        while(sda)                          //在一段时间内检测到sda=0的话认为是应答信号7 ?( O! l; {( C4 _' n. ?
        {        
  x& n/ }0 ^. z) a                if(0==errCounts): q' a: ]: X8 J' `( J9 r2 N  |, ~
                {; @* P- T' E) {6 Q( I5 i
                        scl=0;                  //钳住总线: |5 `7 r$ }! G8 u: J
                        _nop_();
  ?1 i% l8 d( H' n/ j% g+ c                        return FALSE; //没有应答信号
( a+ b8 L$ |0 K6 O9 o4 ]                }# ]: r1 u) u6 P* F& @
                errCounts--;; j; j5 g6 l/ c2 y
        }

9 g8 K7 V* @. h+ ?        scl=0;                             //钳住总线,为下1次通信做准备 $ R$ s" j- b! M# g* x
        _nop_();" t* m6 x7 d& K0 o9 a( V+ `* x
        return TRUE;             //成功处理应答信号  |1 x- J; I1 ?2 |
}
9 P( y4 M% E& D; {* e. ^3 }
void iic_init()                     //总线初始化
# h& N0 l* O: j6 L{/ W6 F" k' X+ p# H6 h  u/ Q
        scl=1;0 L$ o" H7 T8 m! d" l4 d
        sda=1;
5 a; B) `- A" }- F; B! r6 n& @* y# E        delayus();0 i& b/ k5 J6 z+ w
}
9 N/ l( k: @. L- {  [
void iic_sendACK(bool b_ACK)  //发送应答或非应答信号9 ^0 R- C+ o* O' x7 G
{$ o/ _7 l( z5 A# \% \
        scl=0;                        //准备
9 s! ]) @3 e: w& U, d8 |        _nop_();
" K! C. o% X3 }9 h
        if(b_ACK)                //ACK        发送应该信号
6 ]" M% F7 E# w& a- p8 Q5 }9 _' T        {0 S2 ~: `' S- |3 Y
                sda=0;/ y( H4 Z! o* x7 F. o
        }6 C# v, W$ b2 p
        else                        //unACK        发送非应答信号
6 I& n6 O6 i5 j5 x        {
; g" \7 a. g! T1 L                sda=1;3 v+ h8 |7 A* p* ~  [: ~1 o* v
        }

* E, t6 S7 F6 }2 A/ }        _nop_();  I9 p$ Y4 e3 O( z: m: O- J5 d+ m
        scl=1;' G6 Q' J6 O4 v0 x2 r
        delayus();                 //大于4us的延时+ X" {: D" |6 J, O
        scl=0;                    //钳住scl,以便继续接收数据        + T: Q8 N) {# a7 U7 U3 f1 k  l# o5 d
        _nop_();4 g  X& P6 R6 i1 a6 g
}

2 b7 ?4 C) p% ~! `) Cvoid AT24Cxx_writeByte(u16 address,u8 dataByte)//向24cxx写一字节数据
; D- B1 e" C6 H{
" V7 O. i" z2 h8 p2 H5 X        u8 largePage     = address/256;          //24c04是512字节(寻址范围0~511),largePage最大值是1! h* E4 p: S7 e6 T4 T4 W( S
        u8 addressOffset = address%256;   //largePage=0的话地址范围是(0~255)7 I+ X, r8 l4 t  U* S9 B( E
        iic_start();4 q( \& E" g! U7 f3 V
        iic_sendByte(0xa0|(largePage<<1));//控制字,前4位固定1010,后三位是器件地址,末位0是写2 E+ ]& ^, k5 ?1 y
        iic_checkACK();                                      //mcu处理应答信号- N9 t( O/ |+ r6 {; U- d7 N6 b- w
        iic_sendByte(addressOffset);            //指定要写入的器件内地址在        largePage块中的偏移, ?" }% k* }! u' J' d
        iic_checkACK();" I2 ~& N2 e2 i+ S8 o+ E: T
        iic_sendByte(dataByte);                   //写数据1 D& G! N1 F) O+ O  [/ v
        iic_checkACK();3 A- e' ~) |. F; h
        iic_stop();) O  g2 u, N8 _' A) e
        delayms(2);        
# v) L3 `3 ?, _        //按字节写入时,24cxx在接收到停止信号后将数据擦写到内部,这需要时间
! z; W/ z" [7 R7 V7 e9 Y: p- }        //并且在这段时间内不会响应总线上的任何请求,故让mcu有2毫秒以上的等待        
% Q) i* [. e3 _* k- `: ^}

! {, F/ t( w. o  z9 j$ R) |void AT24Cxx_writeData(u16 address,u8 numBytes,u8* buf)//写入任意长度数据(最大256字节)6 q+ z6 `% C; I. p  I& r5 M
{
( R' r* t6 `1 R, \" V) C% U. R9 T        while(numBytes--)
# n& V* w7 H; s, n& c! R        {
/ C5 F$ a" N) d, }% u* G) @- c6 z                AT24Cxx_writeByte(address++,*buf++);; m% I7 u$ a; {' R* y1 D
        }
+ C$ h; _& i& P$ W+ D}

9 p/ F* ]7 f6 U# v2 t! @, z1 P. Pvoid AT24Cxx_readData(u16 beginAddr,u8 dataSize,u8* buf)//读取任意长度字节到缓冲区buf中; d! p2 S; B3 {9 H7 b+ k, B  a4 y
{4 E* K5 c! {4 H
        u8 largePage     = beginAddr/256;        //计算largePage,256字节为一大页+ P7 Q) k* N& Y. x, W, E2 \/ ^
        u8 addressOffset = beginAddr%256;        //计算相对于largePage的偏移
8 t$ ]7 t. Q0 x/ a' n        iic_start();                                                  //起始信号3 }5 [' g8 ?" C% z6 O% r3 o* e
        iic_sendByte(0xa0|(largePage<<1));        //控制字,写
5 A: _, J  P+ P        iic_checkACK();                                                //处理应答信号
+ ?8 G5 r! i( g9 n; I3 N        iic_sendByte(addressOffset);                //要读取的目标地址偏移
/ n+ m; o+ p! N9 I4 R        iic_checkACK();                                                //处理应答信号        / A. i/ A) c. `5 [
        iic_start();                                                   //发送起始信号
; w' x! ]6 [: Y- _% A, X        iic_sendByte(0xa1|(largePage<<1));        //控制字,读/ m( P4 x7 [6 G# F& D
        iic_checkACK();                                                //处理应答信号
" }( x! f6 K' r* _7 I        while(dataSize--)                                        //读取dataSize个字节,最大256个字节8 ^& v0 d& t: ]' N
        {                                                                        //dataSize用u16类型会暴掉ram的
; r; k* ]) B" ]8 g3 }+ s                *buf++=iic_readByte();                        //读取一个个字节并保存到缓冲区buf中
7 Q6 @+ H' N/ T; g                iic_sendACK(dataSize);                  //发送应答,当dataSize为0时mcu发送非应答
: H$ F, L3 c+ p1 u& l        }# z' t9 F* X$ j- I2 e9 {4 W' M
        iic_stop();                                                        //发送停止信号
! f+ I7 u; E; M8 i0 s2 p}
) Z7 g+ s( N' J1 i

8 w9 {( j6 E6 r. p
* F( Q% \1 }1 M$ W/ D7 R# S
void main()//测试
8 R1 o& i# e1 ]5 k4 A  X{
4 o5 \; t$ z2 h/ {# H1 Z8 i5 `        u8 buf[3];                                                                                //接受数据的缓冲区
) b- N- i! x# B  v7 |- x1 T        u8 arr[7]={0x06,1,2,3,4,0x55,0x33};                                //待写入的数据
( k( B4 R* t2 h/ \! P2 F2 {                                                
( Z( A  o" N# z. I        iic_init();                                                                                //总线初始化8 I1 ?/ @# e! ~5 D6 b# C8 c
        AT24Cxx_writeData(0x00+256,sizeof(arr),arr);        //向指定地址处开始写入7字节的数据

/ E! K2 t! I; A) x$ Q0 w# K4 T        P1=0xff;                                                                                 //调试代码,用P1口的led显示4 P1 v, p# r; q' X0 G
        delayms(1000);                                                                         //调试代码

7 J. f7 W6 v: u2 m; t        AT24Cxx_readData(0x00+256,sizeof(buf),buf);           //从指定地址开始读3个字节0 N( i' Y7 k8 G/ r6 r; o( [& g. R
        P1=buf[2];        //也就是2                                                                        //led灯显示数值' j, B& c; }+ k8 h( V
                                                                                        5 {7 C: c, a/ E6 R
        while(1)
; k, B" |! Q2 p; O        {/ T* ]) {3 i3 ^' R! M1 O
                P1=~P1;
9 {& L! A5 Z! ]' n3 |                delayms(500);               
: ^9 a+ \3 x* h+ O1 H# c, q        } . ^6 W$ w# v% {8 P- ~
}
, k( Y" U& O+ g& K
' c! `7 w+ O; `$ h; G4 f& Q4 E

5 f! g+ l2 M' {$ c6 E6 a1 A
+ q3 c- [9 R/ R6 p. T
6 P1 q0 P, \, Z8 _' T8 I; M# q0 ]( }* r5 W% q

( {8 l  L; H+ \6 R; \  j; D
' d- Q5 J0 R' O* J' H( ]5 t7 c) i% v
//my51.h中主要用到
4 y. Y- i) a& B; Q( S( B, O% {#include<reg52.h>
9 s% s. [: E8 i5 v
#include"mytype.h"
8 j" L0 ^( Y# x. Z; K3 k6 gvoid delayms(u16 ms)     //软延时函数
) z* H9 m# _2 V' y1 ^$ F* l/ @{
/ x! y0 E! S" N9 X8 g5 C+ ^3 u. M        u16 i,j;
/ N+ K5 W( T" b6 I        for(i=ms;i>0;i--)2 Q; l; M: }  z; X$ ~
        {# u7 q: L1 }6 p/ ]" z
        for(j=113;j>0;j--)) m6 Z" X- O' o/ x
        {}$ ]" p+ o9 ]0 F/ A8 J
        }& i0 Y0 P  H- ^
}

# V: R9 D( o& r- o7 O/ m) S7 r7 y
2 P& j/ j6 a, j- @$ p1 r7 R  t% G# j

4 G1 L+ ^) X$ M! S0 s# ^
+ h% j1 L3 \$ d; h! Z  L$ U* V+ T; v# t- P
; E( e7 |2 r; T* _
对代码进行了改进 5 G9 a0 _3 s; b! h8 U# J
去掉了在写数据时的 ( s5 r7 X/ V4 u7 h, ?9 p
delayms(2);* @& K, i8 Q, T- Q
这句软延时代码低效 ,而且没有保障
0 g; P; {/ b2 h
改成加一个检测函数
( q7 I) v6 F8 u7 Dbool check_icWriteComplete()   //检测eeprom是否对内部擦写完成 : z/ Y/ z5 d* n
{
$ {  @# z: s" l5 R7 d iic_start(); / O8 J$ B& p) T0 B' z7 Y
iic_sendByte(0xa0);
" @7 y' z" G8 }3 T return iic_checkACK();
5 y4 a8 B& U: ]  Z; v" G, D}

0 V4 R; X% E: Y6 [, s0 t
( p; H6 n: h1 x

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-6-10 00:58 , Processed in 0.078125 second(s), 23 queries , Gzip On.

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

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

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