找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索

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

查看数: 4091 | 评论数: 17 | 收藏 0
关灯 | 提示:支持键盘翻页<-左 右->
    组图打开中,请稍候......
发布时间: 2023-2-20 13:07

正文摘要:

/* 用单次写操作向某一寄存器写入一个字节,reg-寄存器地址,dat-待写入字节 */ void DS1302SingleWrite(unsigned char reg, unsigned char dat) { DS1302_CE = 1; //使能片选信号 DS1302ByteWrite((reg<<1) ...

回复

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

哦,谢谢!
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就可以了
ID:673647 发表于 2023-2-21 11:17
lkc8210 发表于 2023-2-20 23:34
把命令值化作二进制就清楚了

谢谢解答!还想请教一下,你这命令值怎么转化过来的?DS1302寄存器不是有5位,A4~A0吗?你说的“Address:A2~A0”是指什么?
ID:673647 发表于 2023-2-21 11:09
wulin 发表于 2023-2-20 21:48
你看明白下列语句,前面的问题就解决了

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

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


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.                 }
复制代码
ID:213173 发表于 2023-2-20 17:22
zhuls 发表于 2023-2-20 15:41
学习了~~那定义写入地址,读时直接地址+1不就OK了?还整这么多弯弯,

这是各人编程习惯的差异,没有好差之分。
ID:596109 发表于 2023-2-20 17:17
应该是这个寄存器的最低位必须写0,最高位必须是1,reg保存的是中间6位有效指令,刚好加起来是8位
ID:752974 发表于 2023-2-20 16:52
cwb2038 发表于 2023-2-20 15:38
谢谢!REG的值是多少?为什么要扔掉最高位呢?

这个要看芯片的指令格式。
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了?还整这么多弯弯,
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. }
复制代码
ID:673647 发表于 2023-2-20 15:38
munuc_w 发表于 2023-2-20 14:51
左移一位是扔掉了REG的最高位,再或0X80是将移位后的REG的最高位置1,。

谢谢!REG的值是多少?为什么要扔掉最高位呢?
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啊?也没有循环语句,为什么要这样写呢?
ID:69038 发表于 2023-2-20 15:35
没有完整代码不好说,据猜测应是1302的读写地址不一样,才这么做的,比如定义的秒的写地址为80,读是81,如果是这样,那代码是不行的,要(reg|0x01)才对,大小端搞错了。
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:752974 发表于 2023-2-20 14:51
左移一位是扔掉了REG的最高位,再或0X80是将移位后的REG的最高位置1,。

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

Powered by 单片机教程网

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