M41T11-RTC(实时时钟)

一、理论准备

1. 主要器件:STM8单片机、M41T11时钟IC、32.768kHz晶振等。

2. 外围设备:烧录工具ST-Link/v2、串口、5v供电SATA线。

3. 主要思想:通过单片机对时钟IC进行写入和读取操作。

  主设备:STM8单片机。提供SCL、SDA线,用于发送和读取数据,这里需要熟悉I2C协议;

  从设备:M41T11时钟IC。内部有56Byte的NVRAM,前8个Byte寄存储找我们想要得到的数据。

(一)、M41T11引脚示意图及解释如下:

问题简单化了,我们只需要找到访问该时钟IC的方法,读取它的前8Byte即可解决问题。

对以上寄存器表做说明:

(1)读出来的数据为BCD码,所谓BCD码也就是binary-coded decimal format,例如读出Address 0的数据为0x56,及表示56秒;

(2)其中Address 0的D7位为ST(Stop bit),可以理解为时钟IC的使能位,当ST=1时停止,ST=0时开始;因此为了确保时钟IC处于工作状态,在上电后应该首先对时钟IC做Reset操作(即先使ST=1,后置ST=0);

(3)Address 3存储Day(day of week)表示星期几,Address 4存储Date(day of month)表示几号;

(4)Address 7为控制寄存器默认为0xAF,其中默认OUT为1、FT为0;

(5)校验:当FT=1时,且在第7脚(FT/OUT)外加一个上拉电阻时,只要时钟IC正常工作(32.768kHz),就可以用示波器测得FT/OUT脚为512kHz左右;

(6)对于只需要实现简单的读取实时时间,寄存器中其他标志位暂时不做说明。

(二)、时钟IC作为从设备的地址

由图中可以看出,从地址由两部分组成,由7bit的“1101000”和1bit的R/W位共同组成一个Byte;

当为write mode时,R/W=0;当为read mode时,R/W=1;

所以当要写数据进时钟IC时,从地址为0xD0;当要读时钟IC中的数据时,从地址为0xD1;

值得注意的是,我的代码中没有体现,因为我的I2C内部实现了函数I2C_Send7bitAddress(SLAVE_ADDRESS, I2C_DIRECTION_TX);其中SLAVE_ADDRESS为“1101000”,I2C_DIRECTION_TX相当于R/W;

4. Write mode:

(1)发送slave address 0xD0(时钟IC作为从设备的slave address为0xD0);

(2)发送时钟IC的寄存器地址,如发送Address 0的地址0x00;

(3)发送要写入的数据,如设置ST为1,则发送数据0x80;

5. Read mode:

(1)发送slave address 0xD0;

(2)发送时钟IC的寄存器地址;

(3)想要得到的数据已经被传送后存到I2C的数据寄存器中,因此直接拿I2C->DR中的内容即可;

想要得到实时时间,需要读取前8Byte,只需重复Read mode 8次。

通过以上讲解,解决问题的思路应该相当清晰了,因此

二、解决步骤:

(1)关闭时钟IC:通过Write mode先将Address 0置0x80(最高位ST=1);

(2)打开时钟IC并初始化:

  • uint8_t timeData[7]={0x58,0x59,0x23,0x07,0x30,0x05,0x17};
  • 将寄存器的前7Byte初始化为timeData中的值,代表初始化时间为17年5月30日星期二23:59:58;
  • 为了检验读取的正确性,不对第8位做初始化;
  • 由于Address 0被初始化为0x58,最高位ST为0,所以时钟已经开启;

(3)Read mode 8次,对寄存器地址为0x00~0x06的寄存器做读操作,将每次读到的内容存放到 uint8_t curtime[8]数组中;

(4)将数组内容通过串口格式化输出到终端里,为了获取实时时间,死循环读取并输出到串口,通过终端中不停的打印信息,可以看到时间的变化。

三、核心代码:

 在我的代码中,发送操作需要中断,并且从地址的发送已经在中断里实现自动发送;

 1 //real-time clock 
 2   
 3   
 4   //set ST 1
 5   I2C_ITConfig((I2C_IT_TypeDef)(I2C_IT_EVT | I2C_IT_BUF) , ENABLE);
 6   enableInterrupts();
 7   Tx_Idx = 0;
 8   NumOfBytes =2;
 9   TxBuffer[0]=0x00;
10   TxBuffer[1]=0x80;
11   I2C_Send();
12   disableInterrupts();
13   
14   //set ST 0 and set the right time
15   __IO uint8_t i,j;
16   for(i=0;i<8;i++){
17     
18     I2C_ITConfig((I2C_IT_TypeDef)(I2C_IT_EVT | I2C_IT_BUF) , ENABLE);
19     enableInterrupts();
20     
21     Tx_Idx = 0;
22     NumOfBytes =2;
23     TxBuffer[0]=i;  //address
24     TxBuffer[1]=timeData[i];
25     I2C_Send();
26     
27     disableInterrupts();
28   }
29   
30   //issue read adddress
31   while(1) {
32     for(i=0;i<7;i++){
33       
34       I2C_ITConfig((I2C_IT_TypeDef)(I2C_IT_EVT | I2C_IT_BUF) , ENABLE);
35       enableInterrupts();
36       
37       Tx_Idx = 0;
38       NumOfBytes =1;
39       TxBuffer[0]=i;   //address
40       I2C_Send();
41       
42       Rx_Idx = 0; 
43       NumByteToRead =1; 
44       I2C_Read(); 
45       curtime[i] = RxBuffer[0];
46       uart2str(uartbuff,curtime[i],2,16,'0');   //output press times
47       printf("%s: ",uartbuff); 
48     
49       disableInterrupts();
50     }
51     printf("\n\r",uartbuff); 
52   }

函数解释:

  uart2str(uartbuff,curtime[i],2,16,'0')为格式转换函数,意思是将curtime[i]以2位16进制输出,数据先存放在uartbuff中,再用printf打印到终端;

  I2C_ITConfig((I2C_IT_TypeDef)(I2C_IT_EVT | I2C_IT_BUF) , ENABLE)为打开全局中断;

  enableInterrupts()为打开中断;

  disableInterrupts()为关闭中断;

  Tx_Idx为写指针,指向当前要写入的位置,写后加一;Rx_Idx为读指针,指向当前要读取的位置,读后加一;

  NumOfBytes为待写入数据的个数,写后减一;NumByteToRead为待读取数据的个数,读后减一;

  TxBuffer[ ]为待写入的数据,RxBuffer[ ]为待读取的数据(即为I2C->DR的返回值);

  I2C_Send()为发送数据函数,I2C_Read()为读取数据函数,详细实现见STM8单片机的I2C(TwoBoards、DataExchange、Master)实现实例;

四、串口输出:

从左到右输出的分别是寄存器0~6的内容,即显示的是:

17年5月30日星期二23:59:58;

17年5月31日星期二23:59:59;

17年5月31日星期三00:00:00;

五、校验

(1)第7脚(FT/OUT)外加一个上拉电阻,即在FT/OUT脚和VCC供电脚之间加一个上拉电阻;

(2)修改Control Register(Address 7)中的FT 为1,即将默认的0xAF改为0xEF,同理用write mode将0xEF写入即可;

(3)用示波器检测第二脚(OSCO:Oscillator output)是否达到32.768kHz;

(4)若达到,再用示波器检测FT/OUT脚时候达到512kHz左右;若达到则验证正确。

注意事项:M41T11需要在32.768kHz的条件下才能正常工作,若用示波器测试OSCO(第2脚)输出频率达不到32.768kHz则需要在OSCI和OSCO间加一个32.768kHz的晶振,不出意外都是需要加上的。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏linux驱动个人学习

内存的分类

一:SDRAM SDRAM(Synchronous Dynamic Random Access Memory),同步动态随机存储器,同步是指 Memory工作需...

2974
来自专栏芋道源码1024

重磅:JDK 11 正式发布!东半球第二全特性解读!

千呼万唤,JDK11于2018-09-25正式发布!你是不是和笔者一样还在使用JDK8呢?甚至有些开发者还在使用JDK7!没关系,让我们先一睹JDK11的风采。

922
来自专栏跟着阿笨一起玩NET

DataTable.AcceptChanges & DataAdapter.Update

The AcceptChanges method makes rows status to Unchanged, then the DataAdapter.Up...

361
来自专栏我是业余自学C/C++的

汇编语言-第一章 基础知识

1752
来自专栏Google Dart

为Flutter应用程序添加交互性 顶

你如何修改你的应用程序,使其对用户输入做出反应? 在本教程中,您将为仅包含非交互式小部件的应用添加交互性。 具体来说,您将通过创建一个管理两个无状态小部件的自定...

1222
来自专栏开源项目

码云推荐 | Java 持久层工具 jSqlBox

一个支持动态配置、ORM、SQL 重构、跨数据库的 Java 持久层工具。 ? 1jSqlBox 是什么? jSqlBox 是一个微型的、易学易用的、支持简单的...

3797
来自专栏程序员的SOD蜜

ORM查询语言(OQL)简介--概念篇

相关文章内容索引: ORM查询语言(OQL)简介--概念篇 ORM查询语言(OQL)简介--实例篇 ORM查询语言(OQL)简介--高级篇:脱胎换骨 ORM查...

24010
来自专栏逸鹏说道

我为NET狂官方面试题-数据库篇答案

说明:如有错误可以批评指正,有更好写法也可以提点下~ 1. 求结果:select "1"? 报错,SQL里面只有单引号,列如:'xx' 2. 查找包含"obj...

3408
来自专栏向治洪

android离线缓存技术

离线缓存是指在有网络的状态下将从服务器获取的网络数据,如Json 数据缓存到本地,在断网的状态下启动APP时读取本地缓存数据显示在界面上,常用的APP(网易新...

2529
来自专栏后端之路

部分门店访问客户新增画面慢

现象 某些用户访问客户新增画面报卡,售后小伙伴确认后确实该用户的账号确实比较卡。具体表现如下 ? 拿到用户账号发现确实存在如下问题。观察后发现确实一个页面打开...

1975

扫码关注云+社区