比神乐 发表于 2024-10-7 11:17

HC89S105AC8读写PCF8563不正常

代码
PCF8563.C
#include "HC89S105AC8.h"

#include "PCF8563.h"

void _nop1_(void)
{
        int i;
        for(i=0;i<5;i++);
}
//----------------------------------------------------------------------------
void PCF8563_INIT()//初始化
{
        Delay8ms();                                //IIC总线从通电到开始操作要求8ms延时
        Register_INIT();        //寄存器初始化
        Time_INIT();                        //时间初始化
}
//----------------------------------------------------------------------------
void Register_INIT()//寄存器初始化
{
        CS1_Set();                        //设置控制/状态寄存器1
        CS2_Set();                        //设置控制/状态寄存器1
        DayA_Set();                        //设置天闹铃寄存器
        WeekdayA_Set();        //设置周闹铃寄存器
        CO_Set();                                //设置时钟输出寄存器
}
//----------------------------------------------------------------------------
void CS1_Set(){        Write(CS1, 0x00);}//CS1:寄存器地址,0x00:全部设为正常模式
void CS2_Set(){        Write(CS2, 0x02);}//0x02:开闹铃中断,关定时器中断
void DayA_Set(){ Write(DayA, 0x00);}                                //关天闹铃
void WeekdayA_Set(){ Write(WeekdayA, 0x00);}//关周闹铃
void CO_Set(){Write(CO, 0x00);}                                                        //关时钟输出
//----------------------------------------------------------------------------
void Time_INIT()//时间初始化,2015-05-01 Fri 12:30:00,压缩型BCD码格式
{
        Write(Year, 0x23);
        Write(Month, 0x12);//最高位表示世纪:0-20xx,1-19xx
        Write(Weekday, 0x31);//周不会自动生成,需要自己确认并写入
        Write(Day, 0x07);
        Write(Hour, 0x23);
        Write(Minute, 0x59);
        Write(Second, 0x50);
}
//----------------------------------------------------------------------------
uchar Sec_Read()
{
        uchar sec = Read(Second);
        sec &= 0x7F;
        return sec;
}
//----------------------------------------------------------------------------
/*void Time_Read(uchar *buff)//采用多字节连续读取方式,功能与下面函数一样,但两个只能存在一个
{
        Read_Sequence(Second, 7, buff);//从sec单元开始,连续读取7字节,分别为:sec min hour day weekday month year
        Time_Adjust(buff);
}*/
//----------------------------------------------------------------------------
void Time_Read(uchar *buff)//采用单字节读取方式,每次读取1字节,分多次读取,功能与上面函数一样,但两个只能存在一个
{
        uchar base_addr = Second;
        uchar i, dat;
        for (i=0; i<7; i++)
          {
                          dat = Read(base_addr + i);
                          buff = dat;
        }
Time_Adjust(buff);
}
//----------------------------------------------------------------------------
/*void Time_Write(uchar *buff)//采用多字节连续读取方式,功能与下面函数一样,但两个只能存在一个
{                           //时间写入,将缓冲区buff内的sec min hour day weekday month year写入PCF8563
        Write_Sequence(Second, 7, buff);
}*/
//----------------------------------------------------------------------------
void Time_Write(uchar *buff)//采用单字节写入方式,每次写入1字节,分多次写入,功能与上面函数一样,但两个只能存在一个
{                               //时间写入,将缓冲区buff内的sec min hour day weekday month year写入PCF8563
        uchar base_addr = Second;
        uchar i, dat;
        for (i=0; i<7; i++)
          {
                        dat = buff;
                        Write(base_addr + i, dat);
                }
}
//----------------------------------------------------------------------------
void Time_Adjust(uchar *buff)//时间调整,去除不必要的位,固定处理7字节
{
        buff &= 0x7F;//调整秒
        buff &= 0x7F;//调整分
        buff &= 0x3F;//调整时
        buff &= 0x3F;//调整日
        buff &= 0x07;//调整周
        buff &= 0x1F;//调整月,年不用调整
}
//----------------------------------------------------------------------------
void Alarm_Read(uchar *buff)//闹钟时间读取,只开启了时,分闹铃
{
        Read_Sequence(MinuteA, 2, buff);//从MinuteA单元开始,连续读取2字节,分别为分,时的闹钟时间值
}
//----------------------------------------------------------------------------
void Alarm_Stop()//停止闹铃
{
        uchar dat = Read(CS2);//读取当前控制/状态寄存器2的值
        dat &= 0xF7;                                                //清AF闹铃标志,AF在CS2第3位
        Write(CS2, dat);
}
//----------------------------------------------------------------------------
void Write(uchar RegAddr, uchar dat)//向目标寄存器写入1字节数据
{
        Start();                                                                                        //开始信号
        Write_8bits(PCF8563_WRITE);                //IIC写入8位,将写操作指令写入
        Acknowledge();
        Write_8bits(RegAddr);                                        //写入目标写入寄存器的地址
        Acknowledge();
        Write_8bits(dat);                                                        //将数值写入
        Acknowledge();
        Stop();                                                                                                //停止信号
}
//----------------------------------------------------------------------------
uchar Read(uchar RegAddr)//字节读取
{
        uchar dat;
       
        Start();
        Write_8bits(PCF8563_WRITE);                //IIC写入8位,将写操作指令写入
        Acknowledge();
        Write_8bits(RegAddr);                                        //写入目标读取寄存器的地址
        Acknowledge();
        Start();
        Write_8bits(PCF8563_READ);                //IIC写入8位,将读操作指令写入
        Acknowledge();
        dat = Read_8bits();                                                //读取数据1字节
        MasterNoACK();
        Stop();
        return dat;
}
//----------------------------------------------------------------------------
void Read_Sequence(uchar RegAddr, uchar n, uchar *buff)//连续读取多字节,n<=16(pdf:P15of30fig14)
{
        uchar i;
       
        Start();
        Write_8bits(PCF8563_WRITE);
        Acknowledge();
        Write_8bits(RegAddr);                                        //写入目标读取寄存器的地址
        Acknowledge();
        Start();
        Write_8bits(PCF8563_READ);                //IIC写入8位,将读操作指令写入
        Acknowledge();
        for (i=0; i<n; i++)
          {
                        buff = Read_8bits();
                        if (i!=n-1)                                        //除最后一个字节外,主机需给从机ACK
                                MasterACK();
                        else
                                MasterNoACK();                //最后一个字节读取完毕,主机需给从机NoACK,再给stop信号
                }
        Stop();
}
//----------------------------------------------------------------------------
void Write_Sequence(uchar RegAddr, uchar n, uchar *buff)//连续写入多字节(pdf:P15of30fig13)
{
        uchar i;
       
        Start();
        Write_8bits(PCF8563_WRITE);
        Acknowledge();
        Write_8bits(RegAddr);
        Acknowledge();
        for (i=0; i<n; i++)
          {
                        Write_8bits(buff);
                        Acknowledge();
                }
        Stop();
}
//----------------------------------------------------------------------------
void Write_8bits(uchar dat)//写入8位
{
        uchar i, m = 0x80;
       
        for (i=0; i<8; i++)
          {
                        SCL = 0;                                                //在SCL低电平期间将数据送上SDA
                        _nop1_();
                        SDA = (dat&m)==0?0:1;
                        _nop1_();
                        SCL = 1;
                        _nop1_();
                        _nop1_();
                        m /= 2;
                }
        SCL = 0;
}
//----------------------------------------------------------------------------
uchar Read_8bits()//读取8位
{
        uchar i, dat=0;
        P0M0=0X0E;
        for(i=0;i<10;i++);
        for (i=0; i<8; i++)
          {
                        SCL = 0;
                        _nop1_();
                        SCL = 1;//SCL高电平时,读取SDA数据
                        _nop1_();
                        _nop1_();
                        dat *= 2;
                        dat += (SDA==1?1:0);
                }
        SCL = 0;
        P0M0=0XAA;
        for(i=0;i<10;i++);
        return dat;
}
//----------------------------------------------------------------------------
void Start()
{
        SDA = 1;
        SCL = 1;
        _nop1_();
        _nop1_();
        SDA = 0;
        _nop1_();
        SCL = 0;
}
//----------------------------------------------------------------------------
void Stop()
{
        _nop1_();
        SDA = 0;
        SCL = 1;
        _nop1_();
        SDA = 1;
}
//----------------------------------------------------------------------------
void Acknowledge()//从机应答查询
{
        uchar i;
        _nop1_();
        SDA = 1;                                //要查询应答信号,要先释放总线
        SCL = 1;
        _nop1_();
        _nop1_();
        P0M0=0X0E;
        for(i=0;i<10;i++);
        while(SDA==1){};        //对方以拉低SDA来表示应答
        SCL = 0;
        P0M0=0XAA;
        for(i=0;i<10;i++);
}
//----------------------------------------------------------------------------
void MasterACK()//主机应答,见 PCF8563 pdf p14 fig.11
{
        _nop1_();
        SDA = 0;
        _nop1_();
        SCL = 1;
        _nop1_();
        _nop1_();
        SCL = 0;
        SDA = 1;
}
//----------------------------------------------------------------------------
void MasterNoACK()//主机无应答,见 PCF8563 pdf p14 fig.11
{
        _nop1_();
        SDA = 1;
        _nop1_();
        SCL = 1;
        _nop1_();
        _nop1_();
        SCL = 0;
        SDA = 1;
}
//----------------------------------------------------------------------------
void Delay8ms()
{
        uint i;
        for (i=0; i<20000; i++);//实际器件1000可运行,Proteus仿真是否太严格要求时间达标?
}
//----------------------------------------------------------------------------
PCF8563.H
#define uchar unsigned char
#define uintunsigned int

#define SCL P0_1//时钟
#defineSDA P0_0        //数据


//PCF8563内部寄存器地址
#define CS1                        0x00        //控制/状态寄存器1
#define CS2                        0x01        //控制/状态寄存器2
#define Second        0x02        //秒寄存器
#define Minute        0x03        //分寄存器
#define Hour                0x04        //时寄存器
#define Day                        0x05        //天寄存器
#define Weekday        0x06        //周寄存器
#define Month                0x07        //月寄存器
#define Year                0x08        //年寄存器
#define MinuteA        0x09        //分闹铃寄存器
#define HourA                0x0A        //时闹铃寄存器
#define DayA                0x0B        //天闹铃寄存器
#define WeekdayA 0x0C        //周闹铃寄存器
#define CO                        0x0D        //时钟输出控制寄存器
#define TimerCtrl        0x0E        //定时控制寄存器
#define Timer                0x0F                //定时设置寄存器

#define PCF8563_WRITE 0xA2        //写指令
#define PCF8563_READ        0xA3        //读指令
//----------------------------------------------------------------------------
void PCF8563_INIT();        //初始化
void Register_INIT();//寄存器初始化
void CS1_Set();
void CS2_Set();
void DayA_Set();
void WeekdayA_Set();
void CO_Set();
void Time_INIT();
uchar Sec_Read();
void Time_Read(uchar *buff);
void Time_Write(uchar *buff);
void Alarm_Read(uchar *buff);
void Time_Adjust(uchar *buff);
void Alarm_Stop();
void Write(uchar RegAddr, uchar dat);
uchar Read(uchar RegAddr);
void Read_Sequence(uchar RegAddr, uchar n, uchar *buff);
void Write_Sequence(uchar RegAddr, uchar n, uchar *buff);
void Write_8bits(uchar dat);
uchar Read_8bits();
void Start();
void Stop();
void Acknowledge();
void MasterACK();
void MasterNoACK();
void Delay8ms();
//----------------------------------------------------------------------------
主程序
#define ALLOCATE_EXTERN
#include "HC89S105AC8.h"
#include "OLED.h"
#include "pcf8563.h"

#define LCD_SCL_0P3_0=0 //_pc5
#define LCD_SCL_1P3_0=1 //_pc5
#define LCD_SDA_0P3_6=0
#define LCD_SDA_1P3_6=1
#define LCD_RST_0P3_4=0//_pc3
#define LCD_RST_1P3_4=1//_pc3
#define LCD_DC_0   P3_2=0
#define LCD_DC_1   P3_2=1

#define XLevelL                0x00
#define XLevelH                0x10
#define XLevel                ((XLevelH&0x0F)*16+XLevelL)
#define Max_Column        128
#define Max_Row                64
#define        Brightness        0xCF


#define X_WIDTH 128
#define Y_WIDTH 64

unsigned char i,j;
uchar buff;

/***************************************************************************************
* @实现效果        设置各个IO口的工作状态
***************************************************************************************/
void main(void)       
{       
        int k;
        unsigned char shi,ge;
/********************************系统初始化*******************************************/               
        WDTCCR = 0x00;                           //关闭看门狗
                                                           //本例程为方便测试关闭看门狗,实际使用中,建议客户打开看门狗,详见WDT复位例程
        CLKCON = 0x02;                           //选择内部高频RC为系统时钟, Fosc=32MHz
        CLKDIV = 0x02;                           //Fosc 2分频得到Fcpu,Fcpu=16MHz
       
/***********************************设置IO口模式***************************************/
       
       
       
        //P0M0=0XAA;           //开漏带上拉输出       
        LCD_Init();
        LCD_CLS();
       
        LCD_P8x16Str(20,4,(u8*)"20--");
        LCD_P8x16Str(20,6,(u8*)"::");
        PCF8563_INIT();
        //LCD_Rectangle(0,0,10,10,0);
        //LCD_P8x16Str(10,10,'0');
        while(1)
        {
//                SCL=0;
//                SDA=0;
//                for(k=0;k<2000;k++);
//                SCL=1;
//                SDA=1;
//                for(k=0;k<2000;k++);
                Time_Read(buff);
                shi=(buff>>4)+0x30;
                ge=(buff&0x0f)+0x30;
                LCD_P8x16Char(68,6,shi);
                LCD_P8x16Char(76,6,ge);
                for(k=0;k<20000;k++);
        }
}

请问高手,哪里有错?谢谢

jcky001 发表于 2024-10-12 11:44

检查是否有连接不良或短路的情况。

elephant00 发表于 2024-10-12 11:46

调整I2C总线时序

elephant00 发表于 2024-10-12 11:47

用示波器检查是否存在时序错误或数据错误。

两只袜子 发表于 2024-10-12 13:00

起始条件、停止条件、数据位发送和接收的时序是否正确。

jcky001 发表于 2024-10-12 15:00

检查HC89S105AC8的读写代码是否正确实现了I2C通信协议。

suncat0504 发表于 2024-10-30 17:57

既然是I2C,建议你严重关注时钟周期。逻辑本身没问题的话,时钟频率会是最可能的因素。

suncat0504 发表于 2024-10-30 18:00

你可以先用能正常访问的,用示波器、逻辑分析仪、或者自制简单的计数器,获得SCL的周期;然后对比不正常,看看差多少。
页: [1]
查看完整版本: HC89S105AC8读写PCF8563不正常