打印
[STM32F1]

STM32F103使用模拟IIC读写PCF8563

[复制链接]
4720|17
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
使用STM32F103模拟IIC读写时钟芯片PCF8563,断开外接电源较长时间后(如一晚上),再通上外接电源时,PCF8563时钟芯片无法写(程序的初始化阶段,设置时钟输出为1024Hz,示波器测得时钟输出为默认的32.768KKHz),读内置的时间也是错误的(初始化阶段写入了初值时间),但是通电一段时间后,时间又会正常显示,即使通断电也是正常的。求助,这是什么问题,该如何解决。
沙发
禁基的矮子|  楼主 | 2017-4-7 16:31 | 只看该作者
这是电路图和主函数初始化的代码,接下来会贴出PCF8563的具体代码

无标题.png (60.23 KB )

电路图

电路图

无标题1.png (57.96 KB )

主函数初始化阶段

主函数初始化阶段

使用特权

评论回复
板凳
禁基的矮子|  楼主 | 2017-4-7 16:35 | 只看该作者
#include "PCF8563.h"

#define PCF8563_GPIO        GPIOB
#define PCF8563_SDA                GPIO_Pin_15
#define PCF8563_SCL                GPIO_Pin_14

#define Write_ADD        0xA2        //PCF8563的写地址
#define Read_ADD        0xA3        //PCF8563的读地址

#define PCF8563_Start()                        IIC_Start(PCF8563_GPIO, PCF8563_SCL, PCF8563_SDA)
#define PCF8563_Send_Byte(n)        IIC_Send_Byte((n), PCF8563_GPIO, PCF8563_SCL, PCF8563_SDA)
#define PCF8563_Read_Byte(n)        IIC_Read_Byte((n), PCF8563_GPIO, PCF8563_SCL, PCF8563_SDA)
#define PCF8563_Wait_Ack()                IIC_Wait_Ack(PCF8563_GPIO, PCF8563_SCL, PCF8563_SDA)
#define PCF8563_Stop()                        IIC_Stop(PCF8563_GPIO, PCF8563_SCL, PCF8563_SDA)

//************************************
// Method:    PCF8563_Init
// FullName:  PCF8563_Init
// Access:    public
// Returns:   void
// Parameter: void
// Description:        初始化PCF8563的IO口和寄存器
//************************************
void PCF8563_Init(void)
{
        IIC_Init(PCF8563_GPIO, PCF8563_SCL, PCF8563_SDA);
        delay_ms(20);
        PCF8563_Set(CONTROL_STATUS1, 0x00);
        PCF8563_Set(CONTROL_STATUS2, 0x00);
        PCF8563_Set(CLKOUT_CONTROL, 0x81);                //使能CLK输出,1024Hz
}

//************************************
// Method:    PCF8563_Read
// FullName:  PCF8563_Read
// Access:    public
// Returns:   void
// Parameter: PCF8563 * pcf8563
// Description:        读时间 年、月、日、时、分
//************************************
void PCF8563_Read(PCF8563 * pcf8563)
{
        PCF8563_Start();
        PCF8563_Send_Byte(Write_ADD);
        PCF8563_Wait_Ack();
        PCF8563_Send_Byte(MINUTES);                //分寄存器地址0x03
        PCF8563_Wait_Ack();
        PCF8563_Start();
        PCF8563_Send_Byte(Read_ADD);
        PCF8563_Wait_Ack();
        pcf8563->minutes = PCF8563_Read_Byte(1) & 0X7F;
        pcf8563->hours = PCF8563_Read_Byte(1) & 0X3F;
        pcf8563->days = PCF8563_Read_Byte(1) & 0X3F;
        PCF8563_Read_Byte(1);                //读取星期寄存器,该数据不保存
        pcf8563->months = PCF8563_Read_Byte(1) & 0X1F;
        pcf8563->years = PCF8563_Read_Byte(0);

        PCF8563_Stop();                                        //发送停止信号

        //将时间从BCD编码变成十进制
        pcf8563->minutes = BCDToDec(pcf8563->minutes);
        pcf8563->hours = BCDToDec(pcf8563->hours);
        pcf8563->days = BCDToDec(pcf8563->days);
        pcf8563->months = BCDToDec(pcf8563->months);
        pcf8563->years = BCDToDec(pcf8563->years);
}

//************************************
// Method:    PCF8563_Write
// FullName:  PCF8563_Write
// Access:    public
// Returns:   void
// Parameter: PCF8563 * pcf8563
// Description:        将时间(年、月、日、时、分)写入PCF8563
//************************************
void PCF8563_Write(PCF8563 * pcf8563)
{
        //将时间从十进制变成BCD编码
        pcf8563->minutes = DecToBCD(pcf8563->minutes);
        pcf8563->hours = DecToBCD(pcf8563->hours);
        pcf8563->days = DecToBCD(pcf8563->days);
        pcf8563->months = DecToBCD(pcf8563->months);
        pcf8563->years = DecToBCD(pcf8563->years);

        PCF8563_Start();
        PCF8563_Send_Byte(Write_ADD);
        PCF8563_Wait_Ack();
        PCF8563_Send_Byte(MINUTES);                //分寄存器地址0x03
        PCF8563_Wait_Ack();
        PCF8563_Send_Byte(pcf8563->minutes);        //发送分
        PCF8563_Wait_Ack();
        PCF8563_Send_Byte(pcf8563->hours);        //发送时
        PCF8563_Wait_Ack();
        PCF8563_Send_Byte(pcf8563->days);        //发送天
        PCF8563_Wait_Ack();
        PCF8563_Send_Byte(0x00);        //发送星期,不关注星期几
        PCF8563_Wait_Ack();
        PCF8563_Send_Byte(pcf8563->months);        //发送月
        PCF8563_Wait_Ack();
        PCF8563_Send_Byte(pcf8563->years);                //发送年
        PCF8563_Wait_Ack();
        PCF8563_Stop();                                        //发送停止信号
}

//************************************
// Method:    PCF8563_Set
// FullName:  PCF8563_Set
// Access:    public
// Returns:   void
// Parameter: PCF8563_REGISTER regaddr
// Parameter: unsigned char _data
// Description:        设置寄存器的值
//************************************
void PCF8563_Set(PCF8563_REGISTER regaddr, unsigned char _data)
{
        PCF8563_Start();
        PCF8563_Send_Byte(Write_ADD);
        PCF8563_Wait_Ack();
        PCF8563_Send_Byte(regaddr);
        PCF8563_Wait_Ack();
        PCF8563_Send_Byte(_data);
        PCF8563_Wait_Ack();
        PCF8563_Stop();
}

//************************************
// Method:    DecToBCD
// FullName:  DecToBCD
// Access:    public
// Returns:   unsigned char
// Parameter: unsigned char _dec
// Description:        十进制转BCD编码
//************************************
unsigned char DecToBCD(unsigned char _dec)
{
        unsigned char temp = 0;
        while (_dec >= 10)
        {
                temp++;
                _dec -= 10;
        }
        return ((unsigned char)(temp << 4) | _dec);
}

//************************************
// Method:    BCDToDec
// FullName:  BCDToDec
// Access:    public
// Returns:   unsigned char
// Parameter: unsigned char _BCD
// Description:        BCD编码转十进制
//************************************
unsigned char BCDToDec(unsigned char _BCD)
{
        unsigned char temp = 0;

        temp = ((unsigned char)(_BCD & (unsigned char)0xF0) >> (unsigned char)0x04) * 10;
        return (temp + (_BCD & (unsigned char)0x0F));
}
#include "IIC.h"

//#define SDA_H()        SET_BIT(GPIO->BSRR,SDA_Pin)
//#define SDA_L()        SET_BIT(GPIO->BRR,SDA_Pin)
//#define SCL_H()        SET_BIT(GPIO->BSRR,SCL_Pin)
//#define SCL_L()        SET_BIT(GPIO->BRR,SCL_Pin)
#define SDA_H()        (GPIO->BSRR|=SDA_Pin)
#define SDA_L()        (GPIO->BRR|=SDA_Pin)
#define SCL_H()        (GPIO->BSRR|=SCL_Pin)
#define SCL_L()        (GPIO->BRR|=SCL_Pin)

//************************************
// Method:    IIC_Init
// FullName:  IIC_Init
// Access:    public
// Returns:   void
// Parameter: GPIO_TypeDef * GPIO
// Parameter: uint16_t SCL_Pin
// Parameter: uint16_t SDA_Pin
// Description:        初始化IIC,设置SDA、SCL为开漏输出,初始为高电平
//************************************
void IIC_Init(GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);        //本次应用中,两个IIC器件均在GPIOB上

        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;                //开漏输出
        GPIO_InitStructure.GPIO_Pin = SCL_Pin | SDA_Pin;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
        GPIO_Init(GPIO, &GPIO_InitStructure);

        //GPIO->BSRR |= SDA_Pin;
        //GPIO->BRR |= SCL_Pin;
        GPIO_SetBits(GPIO, SDA_Pin | SCL_Pin);        //SDA、SCL初始状态为高电平
}

//************************************
// Method:    IIC_Start
// FullName:  IIC_Start
// Access:    public
// Returns:   void
// Parameter: GPIO_TypeDef * GPIO
// Parameter: uint16_t SCL_Pin
// Parameter: uint16_t SDA_Pin
// Description:        产生起始信号,SCL为高电平时,SDA下降沿
//************************************
void IIC_Start(GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
{
        SDA_H();
        SCL_H();
        delay_us(4);
        SDA_L();        //开始信号
        delay_us(4);
        SCL_L();        //拉低SCL电平,准备发送或接收数据
}

//************************************
// Method:    IIC_Stop
// FullName:  IIC_Stop
// Access:    public
// Returns:   void
// Parameter: GPIO_TypeDef * GPIO
// Parameter: uint16_t SCL_Pin
// Parameter: uint16_t SDA_Pin
// Description:        产生停止信号,SCL为高电平时,SDA上升沿
//************************************
void IIC_Stop(GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
{
        SCL_L();
        SDA_L();
        delay_us(4);
        SCL_H();
        SDA_H();
        delay_us(4);
}

//************************************
// Method:    IIC_Send_Byte
// FullName:  IIC_Send_Byte
// Access:    public
// Returns:   void
// Parameter: u8 txd
// Parameter: GPIO_TypeDef * GPIO
// Parameter: uint16_t SCL_Pin
// Parameter: uint16_t SDA_Pin
// Description:        IIC发送一个字节
//************************************
void IIC_Send_Byte(u8 txd, GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
{
        u8 t;
        SCL_L();        //拉低时钟开始数据传输
        for (t=0;t<8;t++)
        {
                GPIO_WriteBit(GPIO, SDA_Pin, (BitAction)((txd & 0x80) >> 7));
                txd <<= 1;
                delay_us(2);
                SCL_H();
                delay_us(2);
                SCL_L();
                delay_us(2);
        }
}

//************************************
// Method:    IIC_Read_Byte
// FullName:  IIC_Read_Byte
// Access:    public
// Returns:   u8
// Parameter: unsigned char ack
//                                ack=1,发送ACK
//                                ack=0,发送NACK
// Parameter: GPIO_TypeDef * GPIO
// Parameter: uint16_t SCL_Pin
// Parameter: uint16_t SDA_Pin
// Description:        读一个字节,SCL低电平时,改变SDA
//************************************
u8 IIC_Read_Byte(unsigned char ack, GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
{
        unsigned char i, receive = 0;
        SDA_H();        //释放总线
        for (i = 0; i < 8; i++)
        {
                SCL_L();
                delay_us(2);
                SCL_H();
                receive <<= 1;
                if (GPIO_ReadInputDataBit(GPIO, SDA_Pin))
                {
                        receive++;
                }
                //receive |= READ_BIT(GPIO->IDR, SDA_Pin);
                delay_us(1);
        }
        if (!ack)
                IIC_NAck(GPIO, SCL_Pin, SDA_Pin);//发送nACK
        else
                IIC_Ack(GPIO, SCL_Pin, SDA_Pin); //发送ACK   
        return receive;
}

//************************************
// Method:    IIC_Wait_Ack
// FullName:  IIC_Wait_Ack
// Access:    public
// Returns:   u8        1-接收应答失败,0-接收应答成功
// Parameter: GPIO_TypeDef * GPIO
// Parameter: uint16_t SCL_Pin
// Parameter: uint16_t SDA_Pin
// Description:        等待应答信号到来
//************************************
u8 IIC_Wait_Ack(GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
{
        u8 ucErrTime = 0;
        SDA_H();                //释放总线
        delay_us(1);
        SCL_H();
        delay_us(1);

        while (READ_BIT(GPIO->IDR,SDA_Pin))
        {
                ucErrTime++;
                if (ucErrTime > 250)
                {
                        IIC_Stop(GPIO, SCL_Pin, SDA_Pin);
                        return 1;
                }
        }
        SCL_L();   
        return 0;
}

//************************************
// Method:    IIC_Ack
// FullName:  IIC_Ack
// Access:    public
// Returns:   void
// Parameter: GPIO_TypeDef * GPIO
// Parameter: uint16_t SCL_Pin
// Parameter: uint16_t SDA_Pin
// Description:        产生ACK应答,设备在接收或发送到一个字节后能拉低SDA电平
//************************************
void IIC_Ack(GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
{
        SCL_L();
        SDA_L();
        delay_us(2);
        SCL_H();
        delay_us(2);
        SCL_L();
}

//************************************
// Method:    IIC_NAck
// FullName:  IIC_NAck
// Access:    public
// Returns:   void
// Parameter: GPIO_TypeDef * GPIO
// Parameter: uint16_t SCL_Pin
// Parameter: uint16_t SDA_Pin
// Description:        产生NACK应答,设备在接收或发送到一个字节后能拉低SDA电平
//************************************
void IIC_NAck(GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
{
        SCL_L();
        SDA_H();
        delay_us(2);
        SCL_H();
        delay_us(2);
        SCL_L();
}

使用特权

评论回复
地板
mmuuss586| | 2017-4-7 20:03 | 只看该作者
这和时钟芯片本身有关,和STM32又没啥关系;

你联系时钟芯片原厂,这种问题他们有没有遇到过;

实在解决不了的话,你只能上电后,过段时间再读取数据了;

使用特权

评论回复
5
禁基的矮子|  楼主 | 2017-4-7 20:28 | 只看该作者
mmuuss586 发表于 2017-4-7 20:03
这和时钟芯片本身有关,和STM32又没啥关系;

你联系时钟芯片原厂,这种问题他们有没有遇到过;

中间大概有3分钟的时间,芯片都是不可读,不可写的。用示波器看过了。

使用特权

评论回复
6
iamaiqiyi| | 2017-4-7 23:10 | 只看该作者
使用DS1820

使用特权

评论回复
7
dongnanxibei| | 2017-4-8 12:58 | 只看该作者
不知道为何当时不能写啊,你可以这样一直等待它能写了再操作,弄个while判断,要不就一直不启用它。

使用特权

评论回复
8
Prry| | 2017-4-8 16:11 | 只看该作者
单片机常用的总线如i2c、spi、uart等,将其硬件抽象层分离,相关外设在其抽象层上的应用函数接口调用。这样的好处是 :
1、外设的驱动方便移植到任何单片机;
2、外设总线方便移植到其他单片机,只需更改部分寄存器配置;
3、总线调试OK后,调试新的外设程序时,不用重重复复调试总线的程序。
比如,i2c总线,eeprom、时钟芯片、其他i2c外设等,都拿来即用。

使用特权

评论回复
9
禁基的矮子|  楼主 | 2017-4-8 17:52 | 只看该作者

公司里老板要求使用这款时钟芯片。。。

使用特权

评论回复
10
禁基的矮子|  楼主 | 2017-4-8 17:53 | 只看该作者
dongnanxibei 发表于 2017-4-8 12:58
不知道为何当时不能写啊,你可以这样一直等待它能写了再操作,弄个while判断,要不就一直不启用它。 ...

今天测试了下,发现当时间出错时,RTC晶振没有波形。通电大概3分钟后,才能写。(备用电池是有电的)

使用特权

评论回复
11
dongnanxibei| | 2017-4-10 18:29 | 只看该作者
禁基的矮子 发表于 2017-4-8 17:53
今天测试了下,发现当时间出错时,RTC晶振没有波形。通电大概3分钟后,才能写。(备用电池是有电的) ...

也就是说是哪国RTC的晶振坏掉了,。

使用特权

评论回复
12
禁基的矮子|  楼主 | 2017-4-11 11:55 | 只看该作者
dongnanxibei 发表于 2017-4-10 18:29
也就是说是哪国RTC的晶振坏掉了,。

额,解决了。我负载电容取了下限,断电之后,备用电池的电压逐渐下降,晶振就不起振了

使用特权

评论回复
13
houbin1234| | 2017-5-9 11:31 | 只看该作者
PCF8563     优势现货供应  电话13717076781侯斌       QQ657290025

使用特权

评论回复
14
Stannis| | 2017-5-10 21:56 | 只看该作者
外设总线方便移植到其他单片机,只需更改部分寄存器配置

使用特权

评论回复
15
pmp| | 2017-5-10 23:41 | 只看该作者
IO的初始化有问题。

使用特权

评论回复
16
pmp| | 2017-5-10 23:43 | 只看该作者
添加上拉电阻,驱动能力上来就可以了。

使用特权

评论回复
17
禁基的矮子|  楼主 | 2017-5-13 22:40 | 只看该作者
pmp 发表于 2017-5-10 23:43
添加上拉电阻,驱动能力上来就可以了。

不是上拉的问题,是负载电容取值太小了。

使用特权

评论回复
18
lc1296925640| | 2017-10-10 09:58 | 只看该作者
楼主,能看一下你的。h文件码?

使用特权

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

本版积分规则

11

主题

58

帖子

0

粉丝