找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2499|回复: 17
打印 上一主题 下一主题
收起左侧

这个C语言代码怎么理解:DS1302ByteWrite((reg<<1)|0x80)

[复制链接]
跳转到指定楼层
楼主
/* 用单次写操作向某一寄存器写入一个字节,reg-寄存器地址,dat-待写入字节 */
void DS1302SingleWrite(unsigned char reg, unsigned char dat)
{
DS1302_CE = 1; //使能片选信号
DS1302ByteWrite((reg<<1)|0x80); //发送写寄存器指令
DS1302ByteWrite(dat); //写入字节数据
DS1302_CE = 0; //除能片选信号
}
其中DS1302ByteWrite((reg<<1)|0x80),这个怎么理解?REG为什么要左移一位再或上0X80?
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:752974 发表于 2023-2-20 14:51 | 只看该作者
左移一位是扔掉了REG的最高位,再或0X80是将移位后的REG的最高位置1,。
回复

使用道具 举报

板凳
ID:213173 发表于 2023-2-20 15:11 | 只看该作者
读出地址:0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d
写入地址:0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c
变量reg如果等于0x00,左移一位还是0x00,0x00|0x80 等于0x80
变量reg如果等于0x01,左移一位是0x02,0x02|0x80 等于0x82
变量reg如果等于0x02,左移一位是0x04,0x04|0x80 等于0x84
......以此类推。
回复

使用道具 举报

地板
ID:69038 发表于 2023-2-20 15:35 | 只看该作者
没有完整代码不好说,据猜测应是1302的读写地址不一样,才这么做的,比如定义的秒的写地址为80,读是81,如果是这样,那代码是不行的,要(reg|0x01)才对,大小端搞错了。
回复

使用道具 举报

5#
ID:673647 发表于 2023-2-20 15:37 | 只看该作者
wulin 发表于 2023-2-20 15:11
读出地址:0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d
写入地址:0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x ...

谢谢,也就是说DS1302读和写的寄存器地址一共有16个(80h~8fh),逐个读出来吗?默认的初始地址不一定是0X80啊?也没有循环语句,为什么要这样写呢?
回复

使用道具 举报

6#
ID:673647 发表于 2023-2-20 15:38 | 只看该作者
munuc_w 发表于 2023-2-20 14:51
左移一位是扔掉了REG的最高位,再或0X80是将移位后的REG的最高位置1,。

谢谢!REG的值是多少?为什么要扔掉最高位呢?
回复

使用道具 举报

7#
ID:673647 发表于 2023-2-20 15:41 | 只看该作者
zhuls 发表于 2023-2-20 15:35
没有完整代码不好说,据猜测应是1302的读写地址不一样,才这么做的,比如定义的秒的写地址为80,读是81,如 ...

完整代码如下:
  1. #include <reg52.h>
  2. sbit DS1302_CE = P1^7;
  3. sbit DS1302_CK = P3^5;
  4. sbit DS1302_IO = P3^4;
  5. bit flag200ms = 0; //200ms 定时标志
  6. unsigned char T0RH = 0; //T0 重载值的高字节
  7. unsigned char T0RL = 0; //T0 重载值的低字节
  8. void ConfigTimer0(unsigned int ms);
  9. void InitDS1302();
  10. unsigned char DS1302SingleRead(unsigned char reg);
  11. extern void InitLcd1602();
  12. extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
  13. void main()
  14. {
  15. unsigned char i;
  16. unsigned char psec=0xAA; //秒备份,初值 AA 确保首次读取时间后会刷新显示
  17. unsigned char time[8]; //当前时间数组
  18. unsigned char str[12]; //字符串转换缓冲区
  19. EA = 1; //开总中断
  20. ConfigTimer0(1); //T0 定时 1ms
  21. InitDS1302(); //初始化实时时钟
  22. InitLcd1602(); //初始化液晶

  23. while (1)
  24. {
  25. if (flag200ms) //每 200ms 读取一次时间
  26. {
  27. flag200ms = 0;
  28. for (i=0; i<7; i++) //读取 DS1302 当前时间
  29. {
  30. time[i] = DS1302SingleRead(i);
  31. }
  32. if (psec != time[0]) //检测到时间有变化时刷新显示
  33. {
  34. str[0] = '2'; //添加年份的高 2 位:20
  35. str[1] = '0';
  36. str[2] = (time[6] >> 4) + '0'; //“年”高位数字转换为 ASCII 码
  37. str[3] = (time[6]&0x0F) + '0'; //“年”低位数字转换为 ASCII 码
  38. str[4] = '-'; //添加日期分隔符
  39. str[5] = (time[4] >> 4) + '0'; //“月”
  40. str[6] = (time[4]&0x0F) + '0';
  41. str[7] = '-';
  42. str[8] = (time[3] >> 4) + '0'; //“日”
  43. str[9] = (time[3]&0x0F) + '0';
  44. str[10] = '\0';
  45. LcdShowStr(0, 0, str); //显示到液晶的第一行

  46. str[0] = (time[5]&0x0F) + '0'; //“星期”
  47. str[1] = '\0';
  48. LcdShowStr(11, 0, "week");
  49. LcdShowStr(15, 0, str); //显示到液晶的第一行

  50. str[0] = (time[2] >> 4) + '0'; //“时”
  51. str[1] = (time[2]&0x0F) + '0';
  52. str[2] = ':'; //添加时间分隔符
  53. str[3] = (time[1] >> 4) + '0'; //“分”
  54. str[4] = (time[1]&0x0F) + '0';
  55. str[5] = ':';
  56. str[6] = (time[0] >> 4) + '0'; //“秒”
  57. str[7] = (time[0]&0x0F) + '0';
  58. str[8] = '\0';
  59. LcdShowStr(4, 1, str); //显示到液晶的第二行

  60. psec = time[0]; //用当前值更新上次秒数
  61. }
  62. }
  63. }
  64. }
  65. /* 发送一个字节到 DS1302 通信总线上 */
  66. void DS1302ByteWrite(unsigned char dat)
  67. {
  68. unsigned char mask;

  69. for (mask=0x01; mask!=0; mask<<=1) //低位在前,逐位移出
  70. {
  71. if ((mask&dat) != 0) //首先输出该位数据
  72. DS1302_IO = 1;
  73. else
  74. DS1302_IO = 0;
  75. DS1302_CK = 1; //然后拉高时钟
  76. DS1302_CK = 0; //再拉低时钟,完成一个位的操作
  77. }
  78. DS1302_IO = 1; //最后确保释放 IO 引脚
  79. }
  80. /* 由 DS1302 通信总线上读取一个字节 */
  81. unsigned char DS1302ByteRead()
  82. {
  83. unsigned char mask;
  84. unsigned char dat = 0;

  85. for (mask=0x01; mask!=0; mask<<=1) //低位在前,逐位读取
  86. {
  87. if (DS1302_IO != 0) //首先读取此时的 IO 引脚,并设置 dat 中的对应位
  88. {
  89. dat |= mask;
  90. }
  91. DS1302_CK = 1; //然后拉高时钟
  92. DS1302_CK = 0; //再拉低时钟,完成一个位的操作
  93. }
  94. return dat; //最后返回读到的字节数据
  95. }
  96. /* 用单次写操作向某一寄存器写入一个字节,reg-寄存器地址,dat-待写入字节 */
  97. void DS1302SingleWrite(unsigned char reg, unsigned char dat)
  98. {
  99. DS1302_CE = 1; //使能片选信号
  100. DS1302ByteWrite((reg<<1)|0x80); //发送写寄存器指令
  101. DS1302ByteWrite(dat); //写入字节数据
  102. DS1302_CE = 0; //除能片选信号
  103. }
  104. /* 用单次读操作从某一寄存器读取一个字节,reg-寄存器地址,返回值-读到的字节 */
  105. unsigned char DS1302SingleRead(unsigned char reg)
  106. {
  107. unsigned char dat;
  108. DS1302_CE = 1; //使能片选信号
  109. DS1302ByteWrite((reg<<1)|0x81); //发送读寄存器指令
  110. dat = DS1302ByteRead(); //读取字节数据
  111. DS1302_CE = 0; //除能片选信号

  112. return dat;
  113. }
  114. /* DS1302 初始化,如发生掉电则重新设置初始时间 */
  115. void InitDS1302()
  116. {
  117. unsigned char i;
  118. unsigned char code InitTime[] = { //2013 年 10 月 8 日 星期二 12:30:00
  119. 0x00,0x30,0x12, 0x08, 0x10, 0x02, 0x13
  120. };

  121. DS1302_CE = 0; //初始化 DS1302 通信引脚
  122. DS1302_CK = 0;
  123. i = DS1302SingleRead(0); //读取秒寄存器
  124. if ((i & 0x80) != 0) //由秒寄存器最高位 CH 的值判断 DS1302 是否已停止
  125. {
  126. DS1302SingleWrite(7, 0x00); //撤销写保护以允许写入数据
  127. for (i=0; i<7; i++) //设置 DS1302 为默认的初始时间
  128. {
  129. DS1302SingleWrite(i, InitTime[i]);
  130. }
  131. }
  132. }
  133. /* 配置并启动 T0,ms-T0 定时时间 */
  134. void ConfigTimer0(unsigned int ms)
  135. {
  136. unsigned long tmp; //临时变量

  137. tmp = 11059200 / 12; //定时器计数频率
  138. tmp = (tmp * ms) / 1000; //计算所需的计数值
  139. tmp = 65536 - tmp; //计算定时器重载值
  140. tmp = tmp + 12; //补偿中断响应延时造成的误差
  141. T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
  142. T0RL = (unsigned char)tmp;
  143. TMOD &= 0xF0; //清零 T0 的控制位
  144. TMOD |= 0x01; //配置 T0 为模式 1
  145. TH0 = T0RH; //加载 T0 重载值
  146. TL0 = T0RL;
  147. ET0 = 1; //使能 T0 中断
  148. TR0 = 1; //启动 T0
  149. }
  150. /* T0 中断服务函数,执行 200ms 定时 */
  151. void InterruptTimer0() interrupt 1
  152. {
  153. static unsigned char tmr200ms = 0;

  154. TH0 = T0RH; //重新加载重载值
  155. TL0 = T0RL;
  156. tmr200ms++;
  157. if (tmr200ms >= 200) //定时 200ms
  158. {
  159. tmr200ms = 0;
  160. flag200ms = 1;
  161. }
  162. }
复制代码
回复

使用道具 举报

8#
ID:69038 发表于 2023-2-20 15:41 | 只看该作者
wulin 发表于 2023-2-20 15:11
读出地址:0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d
写入地址:0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x ...

学习了~~那定义写入地址,读时直接地址+1不就OK了?还整这么多弯弯,
回复

使用道具 举报

9#
ID:752974 发表于 2023-2-20 16:52 | 只看该作者
cwb2038 发表于 2023-2-20 15:38
谢谢!REG的值是多少?为什么要扔掉最高位呢?

这个要看芯片的指令格式。
回复

使用道具 举报

10#
ID:596109 发表于 2023-2-20 17:17 | 只看该作者
应该是这个寄存器的最低位必须写0,最高位必须是1,reg保存的是中间6位有效指令,刚好加起来是8位
回复

使用道具 举报

11#
ID:213173 发表于 2023-2-20 17:22 | 只看该作者
zhuls 发表于 2023-2-20 15:41
学习了~~那定义写入地址,读时直接地址+1不就OK了?还整这么多弯弯,

这是各人编程习惯的差异,没有好差之分。
回复

使用道具 举报

12#
ID:673647 发表于 2023-2-20 18:43 | 只看该作者
munuc_w 发表于 2023-2-20 16:52
这个要看芯片的指令格式。

谢谢指点,但还是有点不是很理解!搞不清楚为什么要这样写,这是DS1302的寄存器介绍,能再解释一下吗?
15.3.3 DS1302 寄存器介绍

DS1302 的一条指令一个字节共 8 位,其中第 7 位(即最高位)固定为 1,这一位如果是
0 的话,那写进去也是无效的。第 6 位是选择 RAM 还是 CLOCK 的,我前边说过,我们这里
主要讲 CLOCK 时钟的使用,它的 RAM 功能我们不用,所以如果选择 CLOCK 功能,第 6
位是 0,如果要用 RAM,那第 6 位就是 1。从第 5 到第 1 位,决定了寄存器的 5 位地址,而
第 0 位是读写位,如果要写,这一位就是 0,如果要读,这一位就是 1。指令字节直观位分
配如图 15-9 所示。
图 15-9 DS1302 命令字节
DS1302 时钟的寄存器,其中 8 个和时钟有关的,5 位地址分别是 0b00000~0b00111,还
有一个寄存器的地址是 01000,这是涓流充电所用的寄存器,我们这里不讲。在 DS1302 的
数据手册里的地址,直接把第 7 位、第 6 位和第 0 位值给出来了,所以指令就成了 0x80、0x81
那些了,最低位是 1,那么表示读,最低位是 0 表示写,如图 15-10 所示。
图 15-10 DS1302 的时钟寄存器


寄存器 0:最高位 CH 是一个时钟停止标志位。如果时钟电路有备用电源,上电后,我
们要先检测一下这一位,如果这一位是 0,那说明时钟芯片在系统掉电后,由于备用电源的
供给,时钟是持续正常运行的;如果这一位是 1,那么说明时钟芯片在系统掉电后,时钟部
分不工作了。如果 Vcc1 悬空或者是电池没电了,当我们下次重新上电时,读取这一位,那
这一位就是 1,我们可以通过这一位判断时钟在单片机系统掉电后是否还正常运行。剩下的
7 位高 3 位是秒的十位,低 4 位是秒的个位,这里再提请注意一次,DS1302 内部是 BCD 码,
而秒的十位最大是 5,所以 3 个二进制位就够了。
寄存器 1:最高位未使用,剩下的 7 位中高 3 位是分钟的十位,低 4 位是分钟的个位。
寄存器 2:bit7 是 1 的话代表是 12 小时制,0 代表是 24 小时制;bit6 固定是 0,bit5 在
12 小时制下 0 代表的是上午,1 代表的是下午,在 24 小时制下和 bit4 一起代表了小时的十
位,低 4 位代表的是小时的个位。
寄存器 3:高 2 位固定是 0,bit5 和 bit4 是日期的十位,低 4 位是日期的个位。
寄存器 4:高 3 位固定是 0,bit4 是月的十位,低 4 位是月的个位。
寄存器 5:高 5 位固定是 0,低 3 位代表了星期。
寄存器 6:高 4 位代表了年的十位,低 4 位代表了年的个位。请特别注意,这里的 00~
99 指的是 2000 年~2099 年。
寄存器 7:最高位一个写保护位,如果这一位是 1,那么是禁止给任何其它寄存器或者
那 31 个字节的 RAM 写数据的。因此在写数据之前,这一位必须先写成 0。
回复

使用道具 举报

13#
ID:213173 发表于 2023-2-20 21:48 | 只看该作者
cwb2038 发表于 2023-2-20 15:41
完整代码如下:

你看明白下列语句,前面的问题就解决了
  1.                 DS1302SingleWrite(7, 0x00); //撤销写保护以允许写入数据  【 地址7<<1|0x80等于0x8E 】
  2.                 for (i=0; i<7; i++) //设置 DS1302 为默认的初始时间
  3.                 {
  4.                         DS1302SingleWrite(i, InitTime[i]); //【 地址i<<1|0x80等于0x80,0x82,0x84,0x86,0x88,0x8A,0x8C】
  5.                 }
复制代码
回复

使用道具 举报

14#
ID:161164 发表于 2023-2-20 23:34 | 只看该作者
cwb2038 发表于 2023-2-20 18:43
谢谢指点,但还是有点不是很理解!搞不清楚为什么要这样写,这是DS1302的寄存器介绍,能再解释一下吗?
...

把命令值化作二进制就清楚了


回复

使用道具 举报

15#
ID:673647 发表于 2023-2-21 11:09 | 只看该作者
wulin 发表于 2023-2-20 21:48
你看明白下列语句,前面的问题就解决了

谢谢,有点明白了!
回复

使用道具 举报

16#
ID:673647 发表于 2023-2-21 11:17 | 只看该作者
lkc8210 发表于 2023-2-20 23:34
把命令值化作二进制就清楚了

谢谢解答!还想请教一下,你这命令值怎么转化过来的?DS1302寄存器不是有5位,A4~A0吗?你说的“Address:A2~A0”是指什么?
回复

使用道具 举报

17#
ID:161164 发表于 2023-2-21 11:38 | 只看该作者
cwb2038 发表于 2023-2-21 11:17
谢谢解答!还想请教一下,你这命令值怎么转化过来的?DS1302寄存器不是有5位,A4~A0吗?你说的“Address ...

这表是HT1381的寄存器表,但和DS1302的有99.999%相似,所以拿来示范
命令值只是16进制转2进制,如0x80=0B10000000
A4~A0对应命令值bit5~bit1
可以看得到A4,A3全是零,所以只看A2~A0就可以了
回复

使用道具 举报

18#
ID:673647 发表于 2023-2-21 14:35 | 只看该作者
lkc8210 发表于 2023-2-21 11:38
这表是HT1381的寄存器表,但和DS1302的有99.999%相似,所以拿来示范
命令值只是16进制转2进制,如0x80=0 ...

哦,谢谢!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表