• 51单片机内部外设:实时时钟(SPI)


    RTC引入

    何为实时时钟
    real time clock,真实时间,就是所谓的xx年x月x日x时x分x秒星期x
    RTC是SoC中一个内部外设,RTC有自己独立的晶振提供RTC时钟源,内部有一些寄存器用来记录时间(年月日时分秒星期)。一般情况下为了在系统关机时时间仍然在走,还会给RTC提供一个电池供电。

    RTC在早期的单片机应用中是外置的,后来因为这个功能比较基础比较常用,比较高端的单片机或者SOC就将RTC直接内置了,所以我这里就当RTC是内部外设,但其实在操作上,没有本质的区别。

    时序

    时序:字面意思,时序就是时间顺序,实际上在通信中时序就是通信线上按照时间顺序发生的电平变化,以及这些变化对通信的意义就叫时序。 

    注意,时序的横轴的时间,所以对于字节是先发低位还是先发高位,可以看谁在前谁在后,在前的先发,在后的后发。

    DS1302

    DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。

    它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能。

    引脚图:

    DS1302 与单片机之间能简单地采用同步串行的方式进行通信,仅需用到三个口线:

    • RST 复位
    • I/O 数据线
    • SCLK 串行时钟

    Vcc2为主电源,VCC1为后备电源。

    X1和X2是振荡源,外接32.768kHz晶振。

    RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据传送的方法。

    当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。上电运行时,在Vcc>2.0V之前,RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。

    I/O为串行数据输入输出端(双向)。SCLK为时钟输入端。

    具体内容详见DS1302数据手册:ds1302中文资料 - 百度文库

    以下记录本人容易搞错的地方:

    1、时钟操作可通过AM/PM 指示决定采用24 或12 小时格式;

    2、DS1302有12个寄存器,其中有7个寄存器与日历、时钟相关;

    3、寄存器中存放的数据位为BCD码形式;

    操作DS1302的大致过程,就是将各种数据写入DS1302的寄存器,以设置它当前的时间和格式。然后使DS1302开始运作,DS1302时钟会按照设置情况运转,再用单片机将其寄存器内的数据读出。再用液晶显示,就是我们常说的简易电子钟。

    DS1302的寄存器样式如下,我们看到:

    第7 位永远都是1;

    第6 位,1表示RAM,寻址内部存储器地址;0表示CK,寻址内部寄存器;

    第5位到第1位,为RAM或者寄存器的地址;

    最低位,高电平表示RD:即下一步操作将要“读”;低电平表示W:即下一步操作将要“写”。

    下面来看看寄存器地址和对应的值:

    SEC:秒寄存器,注意具体右边内容:低四位为SEC,高的次三位为10SEC,最高位CH为DS1302 的运行标志,当CH=0时,DS1302内部时钟运行,反之CH=1时停止;

    MIN:分寄存器;

    HR:时寄存器,最高位为12/24 小时的格式选择位,该位为1时表示12小时格式。当设置为12小时显示格式时,第5位的高电平表示下午(PM);而当设置为24 小时格式时,第5位为具体的时间数据。

    DATE:日寄存器;

    MONTH:月寄存器;

    DAY:周寄存器,注意一周只有7天,所以该寄存器只有低三位有效,周一到周日;

    YEAR:年寄存器;

    CONTROL:写保护寄存器,当该寄存器最高位WP为1时,DS1302 只读不写,所以要在往DS1302 写数据之前确保WP为0;

    TRICKLE CHARGE REGISTER:涓细电流充电设置寄存器,我们知道,当DS1302掉电时,可以马上调用外部电源保护时间数据。该寄存器就是配置备用电源的充电选项的。其中高四位(4个TCS)只有在1010 的情况下才能使用充电选项;低四位的情况,与DS1302内部电路有关。

    CLOCK BURST:批量读写操作设置寄存器,设置该寄存器后,可以对DS1302的各个寄存器进行连续写入。DS1302的另外一种读写方式。笔者还没用过,感兴趣的朋友可以尝试。

    寄存器定义表:

    单字节读写时序图如下:

    可参考时序图写程序

    注意:

    1、从最低位开始传送数据。(是不是说不管是主到从,还是从到主,都是最低位开始)

    2、时序图中有的信号是高低电平的形式,但是有的是以方框的形式表示的,这代表它可能是高电平,也可能是低电平,一般都是数据信号的时序表示,里面放的是要发送或者接收的数据。

    浅析SPI总线协议

    SPI接口是一种同步串行总线(Serial Peripheral Interface)多用于实时时钟、Flash存储器(如NOR Flash&Nand Flash),ADC、LCD控制器等外围器件的通讯接口。大大增强了处理器的外设扩展能力。

    SPI是一种全双工、高速的、同步的通信总线。

    SPI接口至少有4根线,分别是CS、SCLK、MOSI、MISO

    SPI接口的一些缩写
    SSEL:slave select,常常也被写作CS(chip select)或SS(slave select)
    SCK:serial clock,常常也写作SCLK或SCL
    MISO:master input slave output,常常被简写为SO(slave output,也有说是serial output)
    MOSI:master output slave input,常常被简写为SI(slave input,也有说是serial input)

    SCLK是同步信号,一般由主控来控制;

    CS代表片选信号,不同的从设备,不同的电平使能;

    MOSI英文全称是Master Output Slave Input,主输出从输入;

    MISO英文全称是Slave Input Master Output,从输入主输出。

    片选示意图:

    极性和相位(了解):

    CPOL :Clock Polarity(时钟极性 )
    CPHA :Clock Phase(时钟相位)
    时钟极性(CPOL)对传输协议没有重大的影响。如果CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。

    时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI主模块和与之通信的外设的时钟相位和极性应该一致。

    上升沿读,则下降沿写;反之上升沿写,则下降沿读。

    极性和相位一般重点关注从设备,主设备根据从设备的时序要求进行编程即可。

    SPI和串口

    串口通信中,有起始位,有停止位;但是SPI中没有,发完8个字节后,接着发,继续发。其步调是由时钟信号统一协调的。

    串口因为是异步的,没有统一的工作时钟,所以要事先规定好波特率,起始位和停止位。也正因为没有统一时钟,所以容易出错。

    另外,SPI和串口都没有应答机制。有没有接收到发送方也不知道。

    SPI没有特定的空闲时状态,也没有固定的上升沿下降沿读写,有几种模式搭配。

    SPI单线通讯模式
    SPI单线模式是将原来的两根数据线改成一根,通讯方式变成了半双工的通讯方式,在接线上,只需要三根线分别是SCLK、I/O、CS。

    DS1302采用的就是这种模式(引脚名称可能不同,但原理是一样的)。

    DS1302读写数据

    1. /************************************************************
    2. 日期:2022年7月26日
    3. 作者:星辰
    4. 文件内容:RTC
    5. **************************************************************/
    6. #include
    7. #include
    8. typedef unsigned char uchar;
    9. sbit DSIO = P3^4;
    10. sbit CE = P3^5;
    11. sbit SCLK = P3^6;
    12. /*************************************************************
    13. 函数入口
    14. **************************************************************/
    15. void main()
    16. {
    17. ……
    18. }
    19. /*************************************************************
    20. 写入时钟
    21. **************************************************************/
    22. void RtcWrite(uchar regAddr, uchar regValue)
    23. {
    24. uchar i = 0, j = 0;
    25. SCLK = 0;
    26. CE = 1;
    27. //写入8位控制字节
    28. for(i; i < 8; i++)
    29. {
    30. DSIO = regAddr & 0x01;
    31. regValue >>= 1;
    32. SCLK = 1;
    33. _nop_();
    34. SCLK = 0;
    35. _nop_();
    36. }
    37. //写入8位数据字节
    38. for(j; j < 8; j++)
    39. {
    40. DSIO = regAddr & 0x01;
    41. regValue >>= 1;
    42. SCLK = 1;
    43. _nop_();
    44. SCLK = 0;
    45. _nop_();
    46. }
    47. SCLK = 0;
    48. CE = 0;
    49. }
    50. /*************************************************************
    51. 读取时钟
    52. 返回值:读取到的寄存器的值
    53. **************************************************************/
    54. uchar RtcRead(uchar regAddr)
    55. {
    56. uchar i = 0, j = 0;
    57. uchar tempBit = 0;
    58. uchar rtcResult = 0;
    59. SCLK = 0;
    60. CE = 1;
    61. //写入8位控制字节
    62. for(i; i < 8; i++)
    63. {
    64. DSIO = regAddr & 0x01;
    65. regAddr >>= 1;
    66. SCLK = 1;
    67. _nop_();
    68. SCLK = 0;
    69. _nop_();
    70. }
    71. //读取数据,从设备从低位开始传输,所以主设备也是从低位开始读
    72. for(j; j < 8; j++)
    73. {
    74. tempBit = DSIO;
    75. rtcResult |= (tempBit << j);
    76. SCLK = 1;
    77. _nop_();
    78. SCLK = 0;
    79. _nop_();
    80. }
    81. SCLK = 0;
    82. CE = 0;
    83. DSIO = 0; //防止出现FF现象
    84. return rtcResult;
    85. }

    总结来说,对于数据的写入和读取是这样的:

    都是从最低位开始读或者写。

    写数据时,先把一位二进制数放到IO口,然后构建上升沿,就可以将数据发送出去。

    读数据时,先构建下降沿,这时候数据就到了IO口,我们将其读出来即可。

    重点注意:

    另外,在DS1302的代码编写中,常会遇到一个问题:读出来的时间数据,经常会看到FF,一般是在前一个单元是偶数的时候会出现,比如:

    有两种解决方法:

    1、硬件上在IO线上设置10K的电阻做弱上拉处理。
    2、如果没有做弱上拉,也有办法解决。在代码的读取寄存器时序之后,加一个将IO置为低电平的代码进去,就可以了。

    BCD码

    DS1302是用BCD码(8421码)来显示时间的。

    那什么是BCD码呢?

    BCD码是一种数字编码,这种计数编码有个特点:很像10进制和16进制的结合。看起来很像10进制(29下来是30而不是2A),BCD码实际是用十六进制来表示的模拟十进制数(就比如BCD码的21,其实在计算机中就是0x21,但是代表的就是十进制21)。
    BCD码中只有0-9,而没有ABCDEF等字目。

    换一种说法:BCD码其实就是看起来很像十进制数的十六进制数。意思就是:BCD码本质上是十六进制数,但是因为他没有ABCDEF,所以看起来很像十进制数。

    对于上面提到的寄存器定义表,其实没太看明白。

    实际上,要读哪个寄存器的数据,读出来的就是要的数据,比如读秒读出来的8位二进制数为:00000011,用十六进制表示就是0x03,代表的就是当前处于第3秒。

    如果要写入8秒,那么就写入0x08即可。

    有个疑惑,如果写入12/24小时制的选择位,不是就会影响到数据位吗?这个是啥情况没想明白,后面再查阅资料补充,当前不赘述。

    重点在理解BCD码的应用和意义。

    小补充:

    如何拆分十进制的各个位?比如21,则21/10得到十位数,21%10得到个位数;

    十六进制如何拆分数据?比如0x21,>>七位可获取高位,&上0x01可获得低位。

    对于计算机来说,移位和位运算是最喜欢的,速度很快,而十进制的计算比较麻烦,尤其是除法运算,效率其实很低。

  • 相关阅读:
    论软件工程
    【SpringCloud-学习笔记】http客户端Feign
    C++ 算数运算符 学习资料
    pytest setup与用例之间传参
    Java面试(基础篇)——解构Java常见的基础面试题 & 结合Java源码分析
    Mac入门 使用brew安装软件
    好用的 WAF 工具(SafeLine)
    基于Springboot的特产销售平台设计与实现毕业设计源码091036
    ssm+vue+elementUI 电子资源管理系统-#毕业设计
    欧拉公式的三种证明方法:导数、幂级数、极坐标
  • 原文地址:https://blog.csdn.net/qq_28576837/article/details/125966564