打印

I2C读写程序帮忙看下哪里出错

[复制链接]
2569|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
t86964988|  楼主 | 2009-12-24 18:22 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
我写了个I2C的读写程序 在24C02的地址23中写入值0x3f 然后再读出来 通过P0口让数码管显示0 程序不知道哪里错了 有高手帮忙看下吗?硬件如下:24C02 scl 接单片机的P1.1口 sda接单片机的P1.2口 wp接单片机的P1.0口 单片机的P0口接锁存器然后接数码管的段选 P2口接数码管的片选
#include<reg52.h>
#define uchar unsigned char
sbit sda=P1^2;
sbit scl=P1^1;
sbit wp=P1^0;
uchar a;
void delay()
{ ;; }
void start()  //开始信号
{        
        sda=1;
        delay();
        scl=1;
        delay();
        sda=0;
        delay();
}

void stop()   //停止
{
        sda=0;
        delay();
        scl=1;
        delay();
        sda=1;
        delay();
}

void respons()  //应答
{
        uchar i;
        scl=1;
        delay();
        while((sda==1)&&(i<250))i++;
        delay();
        scl=0;
        delay();
}

void init()//初始化
{
        sda=1;
        delay();
        scl=1;
        delay();
}

void write_byte(uchar date)//写一个字节数据
{
        uchar i,temp;
        temp=date;
        scl=0;
        delay();
        for(i=0;i<8;i++)
        {
                temp=temp<<1;
                sda=CY;
                delay();
                scl=1;
                delay();
                scl=0;
                delay();
        }
        sda=1;
        delay();
}

uchar read_byte()//读一个字节数据
{
        uchar i,k;
        scl=0;
        delay();
        sda=1;
        delay();
        for(i=0;i<8;i++)
        {
                scl=1;
                delay();        
                k=(k<<1)|sda;
                scl=0;
                delay();        
        }
        return k;
}

void delay1(uchar x)
{
        uchar a,b;
        for(a=x;a>0;a--)
         for(b=100;b>0;b--);
}

void write_add(uchar address,uchar date)//写一串数据
{
        start();
        write_byte(0xa0);
        respons();
        write_byte(address);
        respons();
        write_byte(date);
        respons();
        stop();
}

uchar read_add(uchar address)//读一串数据
{
        uchar date;
        start();
        write_byte(0xa0);
        respons();
        write_byte(address);
        respons();
        start();
        write_byte(0xa1);
        respons();
        date=read_byte();
        stop();
        return date;
}

void main()
{
        init();
        wp=0;
        write_add(23,0x3f);
        delay1(100);
        P2=2;//选中数码管1
        P0=read_add(23);//让数码管点亮显示0
        while(1);
}
沙发
NE5532| | 2009-12-24 20:25 | 只看该作者
提几个建议
1.所有的延时都可以不要,51的蜗牛速度对I2C没问题
2.init和start有重复的地方,Init应该是上电口线初始化做,不应该是程序里来写,start里没有必要置高sda和scl
3.start一般不是这个写法,i2c总线有仲裁机制,一般让程序停留在scl为低的地方就是说

void start(void)
{
sda=0;
scl=0;
}

就可以了。

4.i2c总线是同步逻辑,等应答不是你这样等的,只要应答位时钟拉高了,要应答就是应答了,没有应答等死也没应答,所以while循环没有必要。

另外检查硬件A0-A2引脚是否接好了,否则A0的地址就不对,调I2c先调应答,只写器件地址来调,不要一来就想读写数据。

使用特权

评论回复
板凳
NE5532| | 2009-12-24 20:27 | 只看该作者
;**********************************************************;
;                                                          ;
;               Atmel 8-Bit RISC MCU ATmega8               ;
;                                                          ;
;                  PCF8563 RTC Driver                      ;
;                                                          ;
;          Powered By SWUST SiChuan China JiangHaibo       ;
;                                                          ;
;             soundman@sohu.com (Not For Support)          ;
;                                                          ;
;                   V1.20       2006-03-18                 ;
;                                                          ;
;               Warning : All Right Reserved               ;
;                                                          ;
;       HardWare Interface : ATmega8, PCF8563 V1.0 Driver  ;
;       SoftWare Interface : ICCAVR 6.28                   ;
;                                                          ;
;       Last Version : V1.20                               ;
;       Publish Day  : 2006-08-19                          ;
;                                                          ;
;程序名称:      PCF8563 RTC驱动                            ;
;程序开发:      西南科技大学 江海波                        ;
;编写时间:      2005年7月5日                               ;
;软件环境:      ICCAVR上层C语言调用                        ;
;硬件环境:      基于软I2C接口的PCF8563                     ;
;MCU:           ATmega8 @8M                                ;
;程序版本:      V1.11                                      ;
;最后修订时间: 2006年08月19日                             ;
;修订部分说明: V1.10修改了set_time函数的参数格式,底层不再 ;
;               需要对松散的BCD代码进行压缩                ;
;               V1.11修正了V1.10中设置年及设置秒函数的错误 ;
;               V1.20修正了使用R28的错误                   ;
;**********************************************************;

;定义伪指令,当硬件结构改变时修改本位置上的指令即可完成程序修改

;----------------------------------------------------------
;I2C的SDA信号被设置在端口sda_port的sda_bit位上
.define sda_port        PORTB
        sda_bit         =5
.define sda_rport       PINB
;----------------------------------------------------------
;MAX7219的时钟CLK信号被设置在端口led_clk_port的led_clk_bit位上
.define scl_port        PORTB
        scl_bit         =4
;----------------------------------------------------------
;SDA端口的方向寄存器为
.define sda_ddr         DDRB
;----------------------------------------------------------
;为了程序调适方便及系统自检,硬件Debug由函数的返回值定义
;Debug 代码表:
        i2c_ack         =$01    ;PCF8563应答成功
        i2c_nack        =$02    ;PCF8563没有应答主机
        RTC_pd          =$03    ;PCF8563发生过掉电
;----------------------------------------------------------
;资源占用
;1.寄存器
;               R0在设置年的函数中用于数据计算
;               R16计数器,计算每次发送/接收的8个BIT,同时在
;               停止时作为延迟时间用计数器
;               R17计数器,计算启动总线时读取ACK的次数
;               在本软件中,系统将尝试启动总线3次
;               R17同时还作为接收,设置时间时的数据个数计数器用
;               R18接收一个字节时用的临时变量
;               R24全局变量,用于暂存最后向C的返回值
;               R25接收,发送一个字节子程序传递数据用寄存器
;               X指针,指向显示"rtc_buffer"数据传递区
;               R27,接收一个字节时的临时寄存器
;               Z指针,BCD编,解码时用的双指针
;2.RAM空间
;               rtc_buff为首的7个字节
;----------------------------------------------------------
;函数使用说明:
;1._get_time:   带参数函数,将当前时间读入到指定的数组中,数据
;               共计13个字节长度
;例如
;               debug_code=get_time(&time[0]);
;
;               其中debug_code表征本次调用的硬件错误代码
;               读到的13字节数据存放在time[0..13]中
;               数据为已经解码的松散BCD格式,数据格式见下
;               表所示
;               time[0] = Seconds Low
;               time[1] = Seconds High
;               time[2] = Minutes Low
;               time[3] = Minutes High
;               time[4] = Hour Low
;               time[5] = Hour High
;               time[6] = Data Low
;               time[7] = Data High
;               time[8] = Week Low
;               time[9] = Week High
;               time[10] = Month Low
;               time[11] = Month High
;               time[12] = Year Low
;               time[13] = Year High
;为了照顾格式,我们将"周"数据也扩展为2个字节,其高字节固定为"0"
;----------------------------------------------------------
;2._set_time:   带参数函数,调用前将数据以压缩BCD的格式放入
;               参数为首的数组中,数据的结构与PCF8563中的数据
;               结构相同
;例如
;               set_time(&time[0]);
;----------------------------------------------------------
;3._init_rtc:   无参数函数,调用本函数可初始化RTC,该函数可在其
;               他应用中改写以适应其系统要求
;例如
;               init_rtc();
;----------------------------------------------------------
;4._set_hhmm:   带参数函数,调用前将数据以松散BCD的格式放入
;               参数为首的数组中,数据的结构与上表相同
;例如
;               set_hhmm(&time[2]);
;----------------------------------------------------------
;5._set_year:   带参数函数,调用前将数据以松散BCD的格式放入
;               参数为首的数组中,数据的结构与上表相同
;例如
;               set_year(&time[12]);
;----------------------------------------------------------
;6._set_second: 无参数函数,调用本函数可将RTC的秒设置为"0"
;               调用本函数将清除掉电标志
;例如
;               set_second();
;----------------------------------------------------------
;7._set_week:   带参数函数,调用前将数据以松散BCD的格式放入
;               参数为首的数组中,有效数据长度为2个字节
;例如
;               set_week(&time[8]);
;----------------------------------------------------------
;8._set_ddmm:   带参数函数,调用前将数据以松散BCD的格式放入
;               参数为首的数组中,有效数据长度为4个字节
;例如
;               set_ddmm(&time[6]);
;----------------------------------------------------------

;.area text

;-----------------------Delay For I2C----------------------

i2c_delay_5us:                                  ;Delay 5uS
        ldi     R16,7                           ;Counter For Delay

i2c_d_5_loop:
        nop
        subi    R16,1
        brne    i2c_d_5_loop
        ret

i2c_delay_1us3:                                 ;Delay 1.3uS, e.x. 11 "nop"s
        nop
        nop
i2c_delay_0us6:                                 ;Delay 0.6uS, e.x. 5 "nop"s
        ret

;---------------------Start The I2C Bus--------------------

i2c_start:                                      ;Start I2C Bus, The Slave Address Is Transd In R25
        cbi     sda_port,sda_bit                ;SDA=0, Start The I2C Bus
        rcall   i2c_delay_0us6                  ;Thd:STA=0.6uS
        cbi     scl_port,scl_bit                ;SCL=0
        rcall   i2c_delay_0us6                  ;Tlow=1.3uS

;Then Trans The I2C Slave Address, Write Direction To The Bus Using The Following Function

;-----------------------Trans One Byte---------------------

i2c_trans:                                      ;---I2C TRANS 1 byte in R25
        ldi     R16,8                           ;Counter, For 8 BIT

i2c_t_loop:                                     ;---I2C Trans 1 byte LOOP
        cbi     sda_port,sda_bit                ;Clear SDA, Default Trans '0'
        rol     R25
        brcc    i2c_t_l_0
        sbi     sda_port,sda_bit                ;Trans BIT '1'

i2c_t_l_0:                                      ;---I2C Trans Loop '0'
        sbi     scl_port,scl_bit                ;SCL=1
        rcall   i2c_delay_0us6                  ;Thigh=0.6uS
        cbi     scl_port,scl_bit                ;SCL=0
        subi    R16,1
        brne    i2c_t_loop                      ;Trans 8 BIT Loop

;-------------------------Get ACK--------------------------

i2c_ack_rd:                                     ;---I2C ACKnowledge ReaD
        rcall   i2c_delay_0us6                  ;Tlow=1.3uS
        cbi     sda_ddr,sda_bit                 ;SDA Port Become Input To Read ACK
        sbi     sda_port,sda_bit                ;Enable Onchip Pull-up
        sbi     scl_port,scl_bit                ;SCL=1, Start The Acknowledge Clock
        ldi     R24,i2c_nack                    ;Because The Onchip Pull-up Resistor Is Not Very Strong, We Placed A Delay Function
        rcall   i2c_delay_0us6                  ;There To Immunity Parasitic Capacitance
        sbis    sda_rport,sda_bit               ;Check SDA Line
        ldi     R24,i2c_ack                     ;Device Ackloaged
        cbi     scl_port,scl_bit                ;SCL=0, Tlow=1.3uS
        cbi     sda_port,sda_bit                ;To Combin With Re-Start, To Set The SDA Line '0'
        sbi     sda_ddr,sda_bit                 ;SDA Port Become Output After Read ACK
        ret

;-------------------------ACK GEN--------------------------

i2c_nack_gen:                                   ;---I2C Not-ACKnowledge GENerate
        sbi     sda_port,sda_bit                ;SDA=1, To Not-Acknowledge The Slave
        rjmp    i2c_ack_trans

i2c_ack_gen:                                    ;---I2C ACKnowledge GENerate
        cbi     sda_port,sda_bit                ;SDA=0, To Acknowledge The Slave

i2c_ack_trans:                                  ;Trans ACK/NACK To The Bus
        nop                                     ;Tlow=1.3uS
        nop
        nop
        nop
        nop
        sbi     scl_port,scl_bit                ;SCL=1, Generate The Ack. Clock
        rcall   i2c_delay_0us6                  ;Thigh=0.6uS
        cbi     scl_port,scl_bit                ;SCL=0, End The Ack. Clock
        ret

;-----------------------Receive One Byte-------------------

i2c_receive:                                    ;---I2C RECEIVE 1 byte to R25
        ldi     R16,$80
        mov     R2,R16
        ldi     R16,8                           ;Counter, For 8 BIT
        cbi     sda_ddr,sda_bit                 ;SDA Port Become Input To Read Data
        sbi     sda_port,sda_bit                ;Enable Onchip Pull-up
        clr     R25                             ;Clear R25 Register To Init. Receive

i2c_r_loop:
        sbi     scl_port,scl_bit                ;SCL=1
        rcall   i2c_delay_1us3                  ;Thigh=0.6uS, For '1' Signal Stabilization
        sbic    sda_rport,sda_bit               ;Sample SDA Line
        or      R25,R2                          ;Received a '1'
        lsr     R2
        cbi     scl_port,scl_bit                ;SCL=0
        rcall   i2c_delay_1us3                  ;Tlow=1.3uS
        subi    R16,1
        brne    i2c_r_loop

        sbi     sda_ddr,sda_bit                 ;SDA Port Become Output After Read Data
        ret

;--------------------------Decode--------------------------

decode:                                         ;Decode Time Date
        ld      R25,Z+                          ;Load Low Half
        ld      R16,Z+                          ;Load High Half
        swap    R16
        or      R16,R25
        st      X+,R16
        subi    R17,1                           ;The No. Of Date Is Passed In R17
        brne    decode                          ;Encode All dates
        ret

;-------------------------Get Time-------------------------

_get_time::                                     ;Get Time Form The RTC
        movw    XL,R16                          ;Init Pointer X
        ldi     R17,3                           ;Counter, Count For At Most 3 Times Bus Start

get_t_st:                                       ;Get Time Start The Bus
        ldi     R25,$A2                         ;Start The I2C Bus Write Dir.
        rcall   i2c_start
        cpi     R24,i2c_ack
        breq    get_t_read

        sbi     scl_port,scl_bit                ;SCL=1
        rcall   i2c_delay_5us                   ;Tsu:STO=4uS
        sbi     sda_port,sda_bit                ;SDA=1
        rcall   i2c_delay_5us                   ;Tbuf=4.7uS Accord To Philips I2C-Bus Specification V2.1

        subi    R17,1
        brne    get_t_st                        ;Re-try For 3 Times
        mov     R16,R24                         ;Declare I2C Start Error
        ret

get_t_read:
        ldi     R25,$02                         ;Word Address
        rcall   i2c_trans

        sbi     scl_port,scl_bit                ;SCL=1
        rcall   i2c_delay_5us                   ;Tsu:STO=4uS
        sbi     sda_port,sda_bit                ;SDA=1
        rcall   i2c_delay_5us                   ;Tbuf=4.7uS Accord To Philips I2C-Bus Specification V2.1

        ldi     R25,$A3                         ;Start The I2C Bus Read Dir.
        rcall   i2c_start

        ldi     R17,6                           ;Counter, Count For 6 Dates
get_t_loop:
        rcall   i2c_receive
        st      X+,R25
        rcall   i2c_ack_gen
        subi    R17,1
        brne    get_t_loop

        rcall   i2c_receive                     ;Receive The Last Date

        rcall   i2c_nack_gen

        cbi     sda_port,sda_bit                ;SDA=0, Stop The Bus
        sbi     scl_port,scl_bit                ;SCL=1
        rcall   i2c_delay_5us                   ;Tsu:STO=4uS
        sbi     sda_port,sda_bit                ;SDA=1

        movw    ZL,XL
        adiw    ZL,7

        mov     R16,R25                         ;Decode Year
        swap    R25
        andi    R25,$0F
        st      Z,R25                           ;High Half Of Year
        andi    R16,$0F
        sbiw    ZL,1
        st      Z,R16                           ;Low Half Of Year

        ld      R25,-X
        mov     R16,R25                         ;Decode Month
        swap    R25
        andi    R25,$01                         ;High Half Of Month
        st      -Z,R25
        andi    R16,$0F                         ;Low Half Of Month
        st      -Z,R16

        ldi     R16,$00                         ;High Half Of Week, Always $00
        st      -Z,R16                          ;Decode Week
        ld      R25,-X
        andi    R25,$07                         ;Low Half Of Week
        st      -Z,R25

        ld      R25,-X
        mov     R16,R25                         ;Decode Day
        swap    R25
        andi    R25,$03                         ;High Half Of Day
        st      -Z,R25
        andi    R16,$0F                         ;Low Half Of Day
        st      -Z,R16

        ld      R25,-X
        mov     R16,R25                         ;Decode Hour
        swap    R25
        andi    R25,$03                         ;High Half Of Hour
        st      -Z,R25
        andi    R16,$0F                         ;Low Half Of Hour
        st      -Z,R16

        ld      R25,-X
        mov     R16,R25                         ;Decode Minute
        swap    R25
        andi    R25,$07                         ;High Half Of Minute
        st      -Z,R25
        andi    R16,$0F                         ;Low Half Of Minute
        st      -Z,R16

        ld      R25,-X
        sbrc    R25,7                           ;Test Bit.7 Of Seconds
        ldi     R24,RTC_pd                      ;If Bit.7='1', Indicate The Power Has Been Droped
        mov     R16,R25                         ;Decode Seconds
        swap    R25
        andi    R25,$0F                         ;High Half Of Seconds
        st      -Z,R25
        andi    R16,$0F                         ;Low Half Of Seconds
        st      -Z,R16

;       sei                                     ;Enable Interrupt
        mov     R16,R24
        ret

;-------------------------Set Time-------------------------

_set_time::
        movw    XL,R16                          ;Init Pointer X
        ldi     R25,$A2                         ;Start The I2C Bus Write Dir.
        rcall   i2c_start

        ldi     R25,$02                         ;Word Address
        rcall   i2c_trans

        ldi     R17,7                           ;Counter, Count For 7 Dates
set_t_loop:
        ld      R25,X+
        rcall   i2c_trans
        subi    R17,1
        brne    set_t_loop

        rjmp    i2c_stop

;-------------------------Init RTC-------------------------

_init_rtc::
        ldi     R25,$A2                         ;Start The I2C Bus Write Dir.
        rcall   i2c_start

        ldi     R25,$00                         ;Word Address
        rcall   i2c_trans

        ldi     R25,$00                         ;Cont/Stat1, TEST1=Normal, STOP=Run, TESTC=No POR
        rcall   i2c_trans

        ldi     R25,$00                         ;Cont/Stat2, AF/TF=Clear, AIE/TIE=Disable
        rcall   i2c_trans

        sbi     scl_port,scl_bit                ;SCL=1
        rcall   i2c_delay_5us                   ;Tsu:STO=4uS
        sbi     sda_port,sda_bit                ;SDA=1
        rcall   i2c_delay_5us                   ;Tbuf=4.7uS Accord To Philips I2C-Bus Specification V2.1

        ldi     R25,$0D                         ;Word Address
        rcall   i2c_trans

        ldi     R25,$00                         ;Clock Out, Clk=Stop, Freq.=32768
        rcall   i2c_trans

        ldi     R25,$03                         ;Timer Reg, Timer=Disable, Freq.=1/60 For Power Saving
        rcall   i2c_trans

;---------------------Stop The I2C Bus---------------------

i2c_stop:                                       ;Stop I2C Bus
        cbi     sda_port,sda_bit                ;SDA=0
        sbi     scl_port,scl_bit                ;SCL=1
        rcall   i2c_delay_5us                   ;Tsu:STO=4uS
        sbi     sda_port,sda_bit                ;SDA=1
        mov     R16,R24
        ret

;-----------------------Set HH-MiMi------------------------

_set_hhmm::                                     ;Set Hour And Minute
        movw    XL,R16                          ;Init Pointer X
        movw    ZL,XL

        ldi     R17,2
        rcall   decode                          ;Decode 2 Date

        ldi     R25,$A2                         ;Start The I2C Bus Write Dir.
        rcall   i2c_start

        ldi     R25,$03                         ;Word Address
        rcall   i2c_trans

        sbiw    XL,2
        ld      R25,X+                          ;Minute
        rcall   i2c_trans

        ld      R25,X                           ;Hour
        rcall   i2c_trans

        rjmp    i2c_stop

;-----------------------Set Year---------------------------

_set_year::                                     ;Set Year
        movw    ZL,R16
        ld      R0,Z+
        ld      R16,Z
        swap    R16
        or      R0,R16                          ;Year Date
        ldi     R16,$08
        mov     R1,R16                          ;Word Address, Year

set_1b_trans:                                   ;Set 1 Byte Time Date Trans
        ldi     R25,$A2                         ;Start The I2C Bus Write Dir.
        rcall   i2c_start

        mov     R25,R1                          ;Word Address
        rcall   i2c_trans

        mov     R25,R0                          ;Date That Will Be Write To RTC
        rcall   i2c_trans

        rjmp    i2c_stop

;----------------------Set Second--------------------------

_set_second::                                   ;Set Second, Clear To "0"
        clr     R0                              ;Second Date, Clear To "0"S
        ldi     R25,$02                         ;Word Address, Second
        mov     R1,R25
        rjmp    set_1b_trans

;----------------------Set WeekDay-------------------------

_set_week::                                     ;Set WeekDay
        movw    ZL,R16
        ld      R0,Z                            ;Week Date
        ldi     R16,$06
        mov     R1,R16                          ;Word Address, Year
        rjmp    set_1b_trans

;------------------------Set DD-MM-------------------------

_set_ddmm::                                     ;Set Day And Month
        movw    XL,R16                          ;Init Pointer X
        movw    ZL,XL

        ldi     R17,3
        rcall   decode                          ;Decode 3 Date, Include WeekDay

        ldi     R25,$A2                         ;Start The I2C Bus Write Dir.
        rcall   i2c_start

        ldi     R25,$05                         ;Word Address, Day
        rcall   i2c_trans

        sbiw    XL,3
        ld      R25,X
        rcall   i2c_trans

        cbi     sda_port,sda_bit                ;SDA=0, Stop The I2C Bus
        sbi     scl_port,scl_bit                ;SCL=1
        rcall   i2c_delay_5us                   ;Tsu:STO=4uS
        sbi     sda_port,sda_bit                ;SDA=1

        adiw    XL,2
        ld      R0,X
        ldi     R16,$07
        mov     R1,R16                          ;Word Address, Month
        rjmp    set_1b_trans

;----------------------------------------------------------

使用特权

评论回复
地板
NE5532| | 2009-12-24 20:28 | 只看该作者
给你个例子,AVR的mega8操作PCF8563的,不过是汇编写的,ICC下用,你得费点时间来看了。:)

使用特权

评论回复
5
t86964988|  楼主 | 2009-12-24 22:44 | 只看该作者
谢谢~但还是有点问题啊 比如不是说start 需要在scl为高电平时,sda有个高向低的跳变嘛,同时还需要有一定的延时的啊。

使用特权

评论回复
6
NE5532| | 2009-12-25 08:28 | 只看该作者
就51的那个蜗牛速度,早就够了,不信你自己算算看。

使用特权

评论回复
7
t86964988|  楼主 | 2009-12-25 08:36 | 只看该作者
12MHZ的晶振 机器周期1us啊 但是I2C的延时需要至少4us啊 不够啊

使用特权

评论回复
8
NE5532| | 2009-12-25 20:44 | 只看该作者
那个是极限下的条件,5V一般条件下是几百nS,实在要用极限条件考虑,填几个NOP就是了。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

19

主题

77

帖子

1

粉丝