[STM32F1] STM32F103使用模拟IIC读写PCF8563

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

电路图

电路图

主函数初始化阶段

主函数初始化阶段
 楼主| 禁基的矮子 发表于 2017-4-7 16:35 | 显示全部楼层
  1. #include "PCF8563.h"

  2. #define PCF8563_GPIO        GPIOB
  3. #define PCF8563_SDA                GPIO_Pin_15
  4. #define PCF8563_SCL                GPIO_Pin_14

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

  7. #define PCF8563_Start()                        IIC_Start(PCF8563_GPIO, PCF8563_SCL, PCF8563_SDA)
  8. #define PCF8563_Send_Byte(n)        IIC_Send_Byte((n), PCF8563_GPIO, PCF8563_SCL, PCF8563_SDA)
  9. #define PCF8563_Read_Byte(n)        IIC_Read_Byte((n), PCF8563_GPIO, PCF8563_SCL, PCF8563_SDA)
  10. #define PCF8563_Wait_Ack()                IIC_Wait_Ack(PCF8563_GPIO, PCF8563_SCL, PCF8563_SDA)
  11. #define PCF8563_Stop()                        IIC_Stop(PCF8563_GPIO, PCF8563_SCL, PCF8563_SDA)

  12. //************************************
  13. // Method:    PCF8563_Init
  14. // FullName:  PCF8563_Init
  15. // Access:    public
  16. // Returns:   void
  17. // Parameter: void
  18. // Description:        初始化PCF8563的IO口和寄存器
  19. //************************************
  20. void PCF8563_Init(void)
  21. {
  22.         IIC_Init(PCF8563_GPIO, PCF8563_SCL, PCF8563_SDA);
  23.         delay_ms(20);
  24.         PCF8563_Set(CONTROL_STATUS1, 0x00);
  25.         PCF8563_Set(CONTROL_STATUS2, 0x00);
  26.         PCF8563_Set(CLKOUT_CONTROL, 0x81);                //使能CLK输出,1024Hz
  27. }

  28. //************************************
  29. // Method:    PCF8563_Read
  30. // FullName:  PCF8563_Read
  31. // Access:    public
  32. // Returns:   void
  33. // Parameter: PCF8563 * pcf8563
  34. // Description:        读时间 年、月、日、时、分
  35. //************************************
  36. void PCF8563_Read(PCF8563 * pcf8563)
  37. {
  38.         PCF8563_Start();
  39.         PCF8563_Send_Byte(Write_ADD);
  40.         PCF8563_Wait_Ack();
  41.         PCF8563_Send_Byte(MINUTES);                //分寄存器地址0x03
  42.         PCF8563_Wait_Ack();
  43.         PCF8563_Start();
  44.         PCF8563_Send_Byte(Read_ADD);
  45.         PCF8563_Wait_Ack();
  46.         pcf8563->minutes = PCF8563_Read_Byte(1) & 0X7F;
  47.         pcf8563->hours = PCF8563_Read_Byte(1) & 0X3F;
  48.         pcf8563->days = PCF8563_Read_Byte(1) & 0X3F;
  49.         PCF8563_Read_Byte(1);                //读取星期寄存器,该数据不保存
  50.         pcf8563->months = PCF8563_Read_Byte(1) & 0X1F;
  51.         pcf8563->years = PCF8563_Read_Byte(0);

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

  53.         //将时间从BCD编码变成十进制
  54.         pcf8563->minutes = BCDToDec(pcf8563->minutes);
  55.         pcf8563->hours = BCDToDec(pcf8563->hours);
  56.         pcf8563->days = BCDToDec(pcf8563->days);
  57.         pcf8563->months = BCDToDec(pcf8563->months);
  58.         pcf8563->years = BCDToDec(pcf8563->years);
  59. }

  60. //************************************
  61. // Method:    PCF8563_Write
  62. // FullName:  PCF8563_Write
  63. // Access:    public
  64. // Returns:   void
  65. // Parameter: PCF8563 * pcf8563
  66. // Description:        将时间(年、月、日、时、分)写入PCF8563
  67. //************************************
  68. void PCF8563_Write(PCF8563 * pcf8563)
  69. {
  70.         //将时间从十进制变成BCD编码
  71.         pcf8563->minutes = DecToBCD(pcf8563->minutes);
  72.         pcf8563->hours = DecToBCD(pcf8563->hours);
  73.         pcf8563->days = DecToBCD(pcf8563->days);
  74.         pcf8563->months = DecToBCD(pcf8563->months);
  75.         pcf8563->years = DecToBCD(pcf8563->years);

  76.         PCF8563_Start();
  77.         PCF8563_Send_Byte(Write_ADD);
  78.         PCF8563_Wait_Ack();
  79.         PCF8563_Send_Byte(MINUTES);                //分寄存器地址0x03
  80.         PCF8563_Wait_Ack();
  81.         PCF8563_Send_Byte(pcf8563->minutes);        //发送分
  82.         PCF8563_Wait_Ack();
  83.         PCF8563_Send_Byte(pcf8563->hours);        //发送时
  84.         PCF8563_Wait_Ack();
  85.         PCF8563_Send_Byte(pcf8563->days);        //发送天
  86.         PCF8563_Wait_Ack();
  87.         PCF8563_Send_Byte(0x00);        //发送星期,不关注星期几
  88.         PCF8563_Wait_Ack();
  89.         PCF8563_Send_Byte(pcf8563->months);        //发送月
  90.         PCF8563_Wait_Ack();
  91.         PCF8563_Send_Byte(pcf8563->years);                //发送年
  92.         PCF8563_Wait_Ack();
  93.         PCF8563_Stop();                                        //发送停止信号
  94. }

  95. //************************************
  96. // Method:    PCF8563_Set
  97. // FullName:  PCF8563_Set
  98. // Access:    public
  99. // Returns:   void
  100. // Parameter: PCF8563_REGISTER regaddr
  101. // Parameter: unsigned char _data
  102. // Description:        设置寄存器的值
  103. //************************************
  104. void PCF8563_Set(PCF8563_REGISTER regaddr, unsigned char _data)
  105. {
  106.         PCF8563_Start();
  107.         PCF8563_Send_Byte(Write_ADD);
  108.         PCF8563_Wait_Ack();
  109.         PCF8563_Send_Byte(regaddr);
  110.         PCF8563_Wait_Ack();
  111.         PCF8563_Send_Byte(_data);
  112.         PCF8563_Wait_Ack();
  113.         PCF8563_Stop();
  114. }

  115. //************************************
  116. // Method:    DecToBCD
  117. // FullName:  DecToBCD
  118. // Access:    public
  119. // Returns:   unsigned char
  120. // Parameter: unsigned char _dec
  121. // Description:        十进制转BCD编码
  122. //************************************
  123. unsigned char DecToBCD(unsigned char _dec)
  124. {
  125.         unsigned char temp = 0;
  126.         while (_dec >= 10)
  127.         {
  128.                 temp++;
  129.                 _dec -= 10;
  130.         }
  131.         return ((unsigned char)(temp << 4) | _dec);
  132. }

  133. //************************************
  134. // Method:    BCDToDec
  135. // FullName:  BCDToDec
  136. // Access:    public
  137. // Returns:   unsigned char
  138. // Parameter: unsigned char _BCD
  139. // Description:        BCD编码转十进制
  140. //************************************
  141. unsigned char BCDToDec(unsigned char _BCD)
  142. {
  143.         unsigned char temp = 0;

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

  2. //#define SDA_H()        SET_BIT(GPIO->BSRR,SDA_Pin)
  3. //#define SDA_L()        SET_BIT(GPIO->BRR,SDA_Pin)
  4. //#define SCL_H()        SET_BIT(GPIO->BSRR,SCL_Pin)
  5. //#define SCL_L()        SET_BIT(GPIO->BRR,SCL_Pin)
  6. #define SDA_H()        (GPIO->BSRR|=SDA_Pin)
  7. #define SDA_L()        (GPIO->BRR|=SDA_Pin)
  8. #define SCL_H()        (GPIO->BSRR|=SCL_Pin)
  9. #define SCL_L()        (GPIO->BRR|=SCL_Pin)

  10. //************************************
  11. // Method:    IIC_Init
  12. // FullName:  IIC_Init
  13. // Access:    public
  14. // Returns:   void
  15. // Parameter: GPIO_TypeDef * GPIO
  16. // Parameter: uint16_t SCL_Pin
  17. // Parameter: uint16_t SDA_Pin
  18. // Description:        初始化IIC,设置SDA、SCL为开漏输出,初始为高电平
  19. //************************************
  20. void IIC_Init(GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
  21. {
  22.         GPIO_InitTypeDef GPIO_InitStructure;
  23.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);        //本次应用中,两个IIC器件均在GPIOB上

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

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

  32. //************************************
  33. // Method:    IIC_Start
  34. // FullName:  IIC_Start
  35. // Access:    public
  36. // Returns:   void
  37. // Parameter: GPIO_TypeDef * GPIO
  38. // Parameter: uint16_t SCL_Pin
  39. // Parameter: uint16_t SDA_Pin
  40. // Description:        产生起始信号,SCL为高电平时,SDA下降沿
  41. //************************************
  42. void IIC_Start(GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
  43. {
  44.         SDA_H();
  45.         SCL_H();
  46.         delay_us(4);
  47.         SDA_L();        //开始信号
  48.         delay_us(4);
  49.         SCL_L();        //拉低SCL电平,准备发送或接收数据
  50. }

  51. //************************************
  52. // Method:    IIC_Stop
  53. // FullName:  IIC_Stop
  54. // Access:    public
  55. // Returns:   void
  56. // Parameter: GPIO_TypeDef * GPIO
  57. // Parameter: uint16_t SCL_Pin
  58. // Parameter: uint16_t SDA_Pin
  59. // Description:        产生停止信号,SCL为高电平时,SDA上升沿
  60. //************************************
  61. void IIC_Stop(GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
  62. {
  63.         SCL_L();
  64.         SDA_L();
  65.         delay_us(4);
  66.         SCL_H();
  67.         SDA_H();
  68.         delay_us(4);
  69. }

  70. //************************************
  71. // Method:    IIC_Send_Byte
  72. // FullName:  IIC_Send_Byte
  73. // Access:    public
  74. // Returns:   void
  75. // Parameter: u8 txd
  76. // Parameter: GPIO_TypeDef * GPIO
  77. // Parameter: uint16_t SCL_Pin
  78. // Parameter: uint16_t SDA_Pin
  79. // Description:        IIC发送一个字节
  80. //************************************
  81. void IIC_Send_Byte(u8 txd, GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
  82. {
  83.         u8 t;
  84.         SCL_L();        //拉低时钟开始数据传输
  85.         for (t=0;t<8;t++)
  86.         {
  87.                 GPIO_WriteBit(GPIO, SDA_Pin, (BitAction)((txd & 0x80) >> 7));
  88.                 txd <<= 1;
  89.                 delay_us(2);
  90.                 SCL_H();
  91.                 delay_us(2);
  92.                 SCL_L();
  93.                 delay_us(2);
  94.         }
  95. }

  96. //************************************
  97. // Method:    IIC_Read_Byte
  98. // FullName:  IIC_Read_Byte
  99. // Access:    public
  100. // Returns:   u8
  101. // Parameter: unsigned char ack
  102. //                                ack=1,发送ACK
  103. //                                ack=0,发送NACK
  104. // Parameter: GPIO_TypeDef * GPIO
  105. // Parameter: uint16_t SCL_Pin
  106. // Parameter: uint16_t SDA_Pin
  107. // Description:        读一个字节,SCL低电平时,改变SDA
  108. //************************************
  109. u8 IIC_Read_Byte(unsigned char ack, GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
  110. {
  111.         unsigned char i, receive = 0;
  112.         SDA_H();        //释放总线
  113.         for (i = 0; i < 8; i++)
  114.         {
  115.                 SCL_L();
  116.                 delay_us(2);
  117.                 SCL_H();
  118.                 receive <<= 1;
  119.                 if (GPIO_ReadInputDataBit(GPIO, SDA_Pin))
  120.                 {
  121.                         receive++;
  122.                 }
  123.                 //receive |= READ_BIT(GPIO->IDR, SDA_Pin);
  124.                 delay_us(1);
  125.         }
  126.         if (!ack)
  127.                 IIC_NAck(GPIO, SCL_Pin, SDA_Pin);//发送nACK
  128.         else
  129.                 IIC_Ack(GPIO, SCL_Pin, SDA_Pin); //发送ACK   
  130.         return receive;
  131. }

  132. //************************************
  133. // Method:    IIC_Wait_Ack
  134. // FullName:  IIC_Wait_Ack
  135. // Access:    public
  136. // Returns:   u8        1-接收应答失败,0-接收应答成功
  137. // Parameter: GPIO_TypeDef * GPIO
  138. // Parameter: uint16_t SCL_Pin
  139. // Parameter: uint16_t SDA_Pin
  140. // Description:        等待应答信号到来
  141. //************************************
  142. u8 IIC_Wait_Ack(GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
  143. {
  144.         u8 ucErrTime = 0;
  145.         SDA_H();                //释放总线
  146.         delay_us(1);
  147.         SCL_H();
  148.         delay_us(1);

  149.         while (READ_BIT(GPIO->IDR,SDA_Pin))
  150.         {
  151.                 ucErrTime++;
  152.                 if (ucErrTime > 250)
  153.                 {
  154.                         IIC_Stop(GPIO, SCL_Pin, SDA_Pin);
  155.                         return 1;
  156.                 }
  157.         }
  158.         SCL_L();   
  159.         return 0;
  160. }

  161. //************************************
  162. // Method:    IIC_Ack
  163. // FullName:  IIC_Ack
  164. // Access:    public
  165. // Returns:   void
  166. // Parameter: GPIO_TypeDef * GPIO
  167. // Parameter: uint16_t SCL_Pin
  168. // Parameter: uint16_t SDA_Pin
  169. // Description:        产生ACK应答,设备在接收或发送到一个字节后能拉低SDA电平
  170. //************************************
  171. void IIC_Ack(GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
  172. {
  173.         SCL_L();
  174.         SDA_L();
  175.         delay_us(2);
  176.         SCL_H();
  177.         delay_us(2);
  178.         SCL_L();
  179. }

  180. //************************************
  181. // Method:    IIC_NAck
  182. // FullName:  IIC_NAck
  183. // Access:    public
  184. // Returns:   void
  185. // Parameter: GPIO_TypeDef * GPIO
  186. // Parameter: uint16_t SCL_Pin
  187. // Parameter: uint16_t SDA_Pin
  188. // Description:        产生NACK应答,设备在接收或发送到一个字节后能拉低SDA电平
  189. //************************************
  190. void IIC_NAck(GPIO_TypeDef * GPIO, uint16_t SCL_Pin, uint16_t SDA_Pin)
  191. {
  192.         SCL_L();
  193.         SDA_H();
  194.         delay_us(2);
  195.         SCL_H();
  196.         delay_us(2);
  197.         SCL_L();
  198. }

mmuuss586 发表于 2017-4-7 20:03 | 显示全部楼层
这和时钟芯片本身有关,和STM32又没啥关系;

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

实在解决不了的话,你只能上电后,过段时间再读取数据了;
 楼主| 禁基的矮子 发表于 2017-4-7 20:28 | 显示全部楼层
mmuuss586 发表于 2017-4-7 20:03
这和时钟芯片本身有关,和STM32又没啥关系;

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

中间大概有3分钟的时间,芯片都是不可读,不可写的。用示波器看过了。
iamaiqiyi 发表于 2017-4-7 23:10 | 显示全部楼层
使用DS1820
dongnanxibei 发表于 2017-4-8 12:58 | 显示全部楼层
不知道为何当时不能写啊,你可以这样一直等待它能写了再操作,弄个while判断,要不就一直不启用它。
Prry 发表于 2017-4-8 16:11 | 显示全部楼层
单片机常用的总线如i2c、spi、uart等,将其硬件抽象层分离,相关外设在其抽象层上的应用函数接口调用。这样的好处是 :
1、外设的驱动方便移植到任何单片机;
2、外设总线方便移植到其他单片机,只需更改部分寄存器配置;
3、总线调试OK后,调试新的外设程序时,不用重重复复调试总线的程序。
比如,i2c总线,eeprom、时钟芯片、其他i2c外设等,都拿来即用。
 楼主| 禁基的矮子 发表于 2017-4-8 17:52 | 显示全部楼层

公司里老板要求使用这款时钟芯片。。。
 楼主| 禁基的矮子 发表于 2017-4-8 17:53 | 显示全部楼层
dongnanxibei 发表于 2017-4-8 12:58
不知道为何当时不能写啊,你可以这样一直等待它能写了再操作,弄个while判断,要不就一直不启用它。 ...

今天测试了下,发现当时间出错时,RTC晶振没有波形。通电大概3分钟后,才能写。(备用电池是有电的)
dongnanxibei 发表于 2017-4-10 18:29 | 显示全部楼层
禁基的矮子 发表于 2017-4-8 17:53
今天测试了下,发现当时间出错时,RTC晶振没有波形。通电大概3分钟后,才能写。(备用电池是有电的) ...

也就是说是哪国RTC的晶振坏掉了,。
 楼主| 禁基的矮子 发表于 2017-4-11 11:55 | 显示全部楼层
dongnanxibei 发表于 2017-4-10 18:29
也就是说是哪国RTC的晶振坏掉了,。

额,解决了。我负载电容取了下限,断电之后,备用电池的电压逐渐下降,晶振就不起振了
houbin1234 发表于 2017-5-9 11:31 | 显示全部楼层
PCF8563     优势现货供应  电话13717076781侯斌       QQ657290025
Stannis 发表于 2017-5-10 21:56 | 显示全部楼层
外设总线方便移植到其他单片机,只需更改部分寄存器配置
pmp 发表于 2017-5-10 23:41 | 显示全部楼层
IO的初始化有问题。
pmp 发表于 2017-5-10 23:43 | 显示全部楼层
添加上拉电阻,驱动能力上来就可以了。
 楼主| 禁基的矮子 发表于 2017-5-13 22:40 | 显示全部楼层
pmp 发表于 2017-5-10 23:43
添加上拉电阻,驱动能力上来就可以了。

不是上拉的问题,是负载电容取值太小了。
lc1296925640 发表于 2017-10-10 09:58 | 显示全部楼层
楼主,能看一下你的。h文件码?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

13

主题

60

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部