找回密码
 立即注册

QQ登录

只需一步,快速开始

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

求助DS1302 burst模式读取错误

[复制链接]
跳转到指定楼层
楼主
用字节读取模式可以正常读取,burst模式能写,但是读出来是乱码。


一下是代码,哪位学长有空帮忙看下。
  1. /*********************************************************************


  2.                                                                                         VyoMeng 51单片机实习训练

  3.   
  4. **********************************************************************
  5. 程序名称:DS1302 Burst模式读写
  6. 程序版本:V1.0
  7. 作    者: VyoMeng                
  8. 编写时间:2021年09月11日         
  9. 硬件支持:  
  10. 接口说明:
  11. 修改日志:
  12.     N0.1:
  13. **********************************************************************/
  14. /*
  15. **********************************************************************
  16. *                        头文件包含/SFR定义
  17. **********************************************************************
  18. */
  19. #include<reg52.h>

  20. /*
  21. **********************************************************************
  22. *                       本地数据类型/宏定义
  23. **********************************************************************
  24. */
  25. typedef signed char int8;         //8位有符号字符型
  26. typedef signed int int16;         //16位有符号整型
  27. typedef signed long int32;        //32位有符号长整型
  28. typedef unsigned char uint8;      //8位无符号字符型
  29. typedef unsigned int uint16;      //16位无符号整型
  30. typedef unsigned long uint32;     //32位无符号长整型

  31. /*
  32. **********************************************************************
  33. *                        单片机IO口位定义
  34. **********************************************************************
  35. */
  36. sbit DS1302_CE     = P2^3;                        //定义DS1302芯片CE引脚  使能引脚
  37. sbit DS1302_IO     = P2^4;                        //定义DS1302芯片IO引脚  数据读写引脚
  38. sbit DS1302_SCLK   = P2^5;                        //定义DS1302芯片SCLK引脚  锁存引脚

  39. sbit DX            = P2^1;                  //定义数码管显示内容
  40. sbit WX            = P2^2;                  //定义数码管显示位置
  41. /*
  42. **********************************************************************
  43. *                      本地全局变量/数组定义
  44. **********************************************************************
  45. */
  46. uint8 code zifu[20]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0x89,0x7f,0xbf,0xc8};   //数码管所有显示情况加入数组
  47. uint8 Time_Set[7] = {0x00,0x01,0x02,0x03,0x04,0x05,0x21};  //设置初始时间 2021年9月8日23点59分50秒周3
  48. uint8 tm[8];                //定义数组放读取出的时间数据
  49. /*
  50. **********************************************************************
  51. *                         函数原型声明
  52. **********************************************************************
  53. */
  54. void DS1302Init();
  55. void delay(uint16 x_ms); //延迟  x_ms*1  毫秒
  56. void xiaoying();
  57. void DS1302ByteWrite(uint8 dat);
  58. uint8 DS1302ByteRead();
  59. void DS1302TimeWrite();
  60. void DS1302TimeRead(uint8 *time);
  61. /*********************************************************************
  62. *函数名称:main
  63. *函数功能:主函数
  64. *函数调用:
  65. *参数列表:无
  66. *返 回 值:无
  67. *结    果:
  68. *备    注:
  69. *********************************************************************/
  70. void main()
  71. {
  72.         DS1302Init();        //初始化DS1302芯片
  73.        
  74.         while(1)
  75.         {
  76.                 DS1302TimeRead(tm);

  77. ////////////////////  数码管第1位
  78.                 DX=0;           //显示内容清空
  79.                 WX=0;           //显示位置清空
  80.                 P0=0X01;        //显示位置在数码管 第1位
  81.                 WX=1;           //给高电平
  82.                 WX=0;           //给低电平 电平下衍 将显示位置写入数码管位置显示锁存器
  83.                 DX=0;           //显示内容清空
  84.                 WX=0;           //显示位置清空
  85.                 P0=zifu[tm[3] >> 4];     //写入数字
  86.                 DX=1;           //给高电平
  87.                 DX=0;           //给低电平 电平下衍 将显示内容写入数码管内容显示锁存器
  88.                 xiaoying();      //数码管消影
  89.                
  90. ////////////////////  数码管第2位
  91.                 DX=0;           //显示内容清空
  92.                 WX=0;           //显示位置清空
  93.                 P0=0X02;        //显示位置在数码管 第2位
  94.                 WX=1;           //给高电平
  95.                 WX=0;           //给低电平 电平下衍 将显示位置写入数码管位置显示锁存器
  96.                 DX=0;           //显示内容清空
  97.                 WX=0;           //显示位置清空
  98.                 P0=zifu[tm[3] & 0x0F];     //写入数字
  99.                 DX=1;           //给高电平
  100.                 DX=0;           //给低电平 电平下衍 将显示内容写入数码管内容显示锁存器
  101.                 xiaoying();      //数码管消影
  102.                
  103. ////////////////////  数码管第3位
  104.                 DX=0;           //显示内容清空
  105.                 WX=0;           //显示位置清空
  106.                 P0=0X04;        //显示位置在数码管 第3位
  107.                 WX=1;           //给高电平
  108.                 WX=0;           //给低电平 电平下衍 将显示位置写入数码管位置显示锁存器
  109.                 DX=0;           //显示内容清空
  110.                 WX=0;           //显示位置清空
  111.                 P0=zifu[tm[4] >> 4];     //写入数字
  112.                 DX=1;           //给高电平
  113.                 DX=0;           //给低电平 电平下衍 将显示内容写入数码管内容显示锁存器
  114.                 xiaoying();      //数码管消影
  115.                
  116. ////////////////////  数码管第4位
  117.                 DX=0;           //显示内容清空
  118.                 WX=0;           //显示位置清空
  119.                 P0=0X08;        //显示位置在数码管 第4位
  120.                 WX=1;           //给高电平
  121.                 WX=0;           //给低电平 电平下衍 将显示位置写入数码管位置显示锁存器
  122.                 DX=0;           //显示内容清空
  123.                 WX=0;           //显示位置清空
  124.                 P0=zifu[tm[4] & 0x0F];     //写入数字
  125.                 DX=1;           //给高电平
  126.                 DX=0;           //给低电平 电平下衍 将显示内容写入数码管内容显示锁存器
  127.                 xiaoying();      //数码管消影

  128. ////////////////////  数码管第5位
  129.                 DX=0;           //显示内容清空
  130.                 WX=0;           //显示位置清空
  131.                 P0=0X10;        //显示位置在数码管 第5位
  132.                 WX=1;           //给高电平
  133.                 WX=0;           //给低电平 电平下衍 将显示位置写入数码管位置显示锁存器
  134.                 DX=0;           //显示内容清空
  135.                 WX=0;           //显示位置清空
  136.                 P0=zifu[tm[5] >> 4];     //写入数字
  137.                 DX=1;           //给高电平
  138.                 DX=0;           //给低电平 电平下衍 将显示内容写入数码管内容显示锁存器
  139.                 xiaoying();      //数码管消影
  140.                
  141. ////////////////////  数码管第6位
  142.                 DX=0;           //显示内容清空
  143.                 WX=0;           //显示位置清空
  144.                 P0=0X20;        //显示位置在数码管 第6位
  145.                 WX=1;           //给高电平
  146.                 WX=0;           //给低电平 电平下衍 将显示位置写入数码管位置显示锁存器
  147.                 DX=0;           //显示内容清空
  148.                 WX=0;           //显示位置清空
  149.                 P0=zifu[tm[5] & 0x0F];     //写入数字
  150.                 DX=1;           //给高电平
  151.                 DX=0;           //给低电平 电平下衍 将显示内容写入数码管内容显示锁存器
  152.                 xiaoying();      //数码管消影  
  153.   
  154. ////////////////////  数码管第7位
  155.                 DX=0;           //显示内容清空
  156.                 WX=0;           //显示位置清空
  157.                 P0=0X40;        //显示位置在数码管 第7位
  158.                 WX=1;           //给高电平
  159.                 WX=0;           //给低电平 电平下衍 将显示位置写入数码管位置显示锁存器
  160.                 DX=0;           //显示内容清空
  161.                 WX=0;           //显示位置清空
  162.                 P0=zifu[tm[6] >> 4];     //写入数字
  163.                 DX=1;           //给高电平
  164.                 DX=0;           //给低电平 电平下衍 将显示内容写入数码管内容显示锁存器
  165.                 xiaoying();      //数码管消影
  166.                
  167. ////////////////////  数码管第8位
  168.                 DX=0;           //显示内容清空
  169.                 WX=0;           //显示位置清空
  170.                 P0=0X80;        //显示位置在数码管 第8位
  171.                 WX=1;           //给高电平
  172.                 WX=0;           //给低电平 电平下衍 将显示位置写入数码管位置显示锁存器
  173.                 DX=0;           //显示内容清空
  174.                 WX=0;           //显示位置清空
  175.                 P0=zifu[tm[6] & 0x0F];     //写入数字
  176.                 DX=1;           //给高电平
  177.                 DX=0;           //给低电平 电平下衍 将显示内容写入数码管内容显示锁存器
  178.                 xiaoying();      //数码管消影         
  179.   }
  180. }

  181. /*********************************************************************
  182. *函数名称:DS1302Init
  183. *函数功能:初始化DS1302芯片
  184. *函数调用:DS1302Init();
  185. *参数列表:
  186. *返 回 值:无
  187. *结    果:
  188. *备    注:初始化DS1302
  189. *********************************************************************/
  190. void DS1302Init()
  191. {

  192.         uint8 state;
  193.         DS1302TimeRead(tm);                        //读取时间数据
  194.         state = tm[0];                                        //读取秒位
  195.         state = state >> 7;                                //数据右移7位,将CH值移到右边最低位
  196.        
  197.         if (state == 0x01)                                                //如果CH值=1,说明时钟停止了,需要进行初始化
  198.         {
  199.                 DS1302_CE = 1;                                        //打开DS1302使能引脚
  200.                 DS1302ByteWrite(0x8E);        //操作写保护位
  201.                 DS1302ByteWrite(0x00);        //关闭写保护
  202.                 DS1302_CE = 0;                //关闭DS1302使能引脚
  203.        
  204.                 DS1302TimeWrite();                        //写入初始时间
  205.        
  206.                 DS1302_CE = 1;                                        //打开DS1302使能引脚
  207.                 DS1302ByteWrite(0x8E);        //操作写保护位
  208.                 DS1302ByteWrite(0x80);        //打开写保护
  209.                 DS1302_CE = 0;                                        //关闭DS1302使能引脚
  210.        
  211.         }
  212. }


  213. /*********************************************************************
  214. *函数名称:DS1302ByteWrite
  215. *函数功能:写入一个字节数据
  216. *函数调用:DS1302ByteWrite(uint8 dat);
  217. *参数列表:dat
  218. *返 回 值:无
  219. *结    果:
  220. *备    注:dat为时间日期的数据
  221. *********************************************************************/
  222. void DS1302ByteWrite(uint8 dat)
  223. {
  224.         uint8 i;
  225. //        DS1302_SCLK = 0;                                //拉低锁存器电平准备写入数据
  226.         for(i=0;i<8;i++)                                //循环8次写入8组数据
  227.                 {
  228.                         DS1302_IO = dat & 0x01;        //DS1302写入数据是从最低位开始写,所以先取最低为数值
  229.                         dat = dat >> 1;                                        //取完最低位后将dat右移1位,将原来第2位数移到最低位
  230.                         DS1302_SCLK = 1;                                //拉高锁存器电平产生上升沿,写入数据
  231.                         DS1302_SCLK = 0;                                //拉高锁存器电平产生上升沿,写入数据
  232.                 }
  233.                 DS1302_IO =1;
  234. }

  235. /*********************************************************************
  236. *函数名称:DS1302ByteRead
  237. *函数功能:读取一个字节数据
  238. *函数调用:DS1302ByteRead();
  239. *参数列表:无
  240. *返 回 值:dat
  241. *结    果:返回读取到的时间数据
  242. *备    注:
  243. *********************************************************************/
  244. uint8 DS1302ByteRead()
  245. {
  246.         uint8 i;
  247.         uint8 dat=0;                                //先给dat赋值 0X00 即 0000 0000
  248. //        DS1302_SCLK=0;                                //拉低锁存器电平准备写入数据
  249.         for(i=0;i<8;i++)                        //循环8次读取8组数据
  250.         {
  251.                 if (DS1302_IO == 0)        //判断IO口数据是否为0
  252.                 {
  253.                         dat = dat << 1;                                        //如果IO口为0,dat左移1位,左移最低位默认补0
  254.                 }
  255.                 else
  256.                 {
  257.                         dat = dat << 1;                                        //如果IO口不为0,dat左移1位,最低位先补个0
  258.                         dat = dat | 0x01;                                //最低位或上0x01,最低位变成1
  259.                 }
  260.                         DS1302_SCLK = 1;        //拉高锁存器电平产生上升沿,读入数据
  261.                         DS1302_SCLK = 0;        //拉高锁存器电平产生上升沿,读入数据
  262.         }
  263.         return dat;                                                //返回读取到的时间数据数组               
  264. }
  265. /*********************************************************************
  266. *函数名称:DS1302TimeWrite
  267. *函数功能:读取一个字节数据
  268. *函数调用:DS1302TimeWrite();
  269. *参数列表:
  270. *返 回 值:无
  271. *结    果:
  272. *备    注:
  273. *********************************************************************/
  274. void DS1302TimeWrite()
  275. {
  276.         uint8 i;
  277.         DS1302_CE = 1;                                                                                        //打开DS1302使能引脚
  278.         DS1302ByteWrite(0xBE);                                                        //开启burst写入模式,0XBE为burst写入地址
  279.         for(i=0;i<7;i++)                                                                                //循环7次,将 [年月日时分秒周] 七个字节写入
  280.                 {
  281.                         DS1302ByteWrite(Time_Set[i]);                //从Time_Set数组里提取数值写入
  282.                 }
  283.         DS1302ByteWrite(0x00);                                                        //burst模式一次要读写8个字节,实际日期总共7个字节,所以最后补充一个无意义的0x00字节
  284.         DS1302_CE = 0;                                                                                        //关闭DS1302使能引脚
  285. }

  286. /*********************************************************************
  287. *函数名称:DS1302TimeRead
  288. *函数功能:读取一个字节数据
  289. *函数调用:DS1302TimeRead(uint8 *time);
  290. *参数列表:time
  291. *返 回 值:无
  292. *结    果:
  293. *备    注:
  294. *********************************************************************/
  295. void DS1302TimeRead(uint8 *time)
  296. {
  297.         uint8 i;
  298.         DS1302_CE = 1;                                                                                        //打开DS1302使能引脚
  299.         DS1302ByteWrite(0xBF);                                                        //开启burst读取模式,0XBF为burst读取地址
  300.         for (i=0;i<7;i++)                                                                                //burst模式一次要读写8个字节,实际日期总共7个字节,所以最后一个无意义
  301.                 {
  302.                         time[i]=DS1302ByteRead();       //循环8次,将 [年月日时分秒周] 7个字节及保护字节读取出来
  303.                 }
  304.                 delay(3);
  305.         DS1302_CE = 0;                                //关闭DS1302使能引脚                       
  306. }

  307. /*********************************************************************
  308. *函数名称:delay
  309. *函数功能:延时函数
  310. *函数调用:delay(uint16 x_ms);
  311. *参数列表:x_ms
  312. *返 回 值:无
  313. *结    果:延迟时间=1* x_ms(毫秒)
  314. *备    注:
  315. *********************************************************************/
  316. void delay(uint16 x_ms) //延迟  x_ms*1  毫秒
  317. {
  318.    uint16 i,j;
  319.         for(i=x_ms;i>0;i--)
  320.                  for(j=114;j>0;j--);
  321. }

  322. /*********************************************************************
  323. *函数名称:xiaoying
  324. *函数功能:数码管扫描延迟及消影函数
  325. *函数调用:xiaoying();
  326. *参数列表:无
  327. *返 回 值:无
  328. *结    果:数码管扫描延迟及消影
  329. *备    注:
  330. *********************************************************************/
  331. void xiaoying()
  332. {
  333.                 delay(3);       //数码管扫描延迟时间 毫秒
  334.                 DX=0;           //显示内容清空
  335.                 WX=0;           //显示位置清空
  336.                 P0=0X00;        //选择显示位置
  337.                 WX=1;           //给高电平
  338.                 WX=0;           //给低电平 电平下衍 将显示位置写入数码管位置显示锁存器
  339. }
复制代码




分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:624769 发表于 2021-9-12 17:05 | 只看该作者
用Burst 读取,有两个比较 重要的注意点。
1, 发送 0xBF  或者  0xff 前, 在拉高 CE 之后, CLK 动作之前, 先拉低数据传授口  IO = 0(如果每次读到错误值都一样,就是这个问题); 然后再开始发送, 0xBF 或者 0xFF.
2,  条件如果允许, 数据串口IO   上拉10K电阻到 VCC , 然后选择模式为开漏, 如果硬件上改造比较难,那么在Burst 读取时,确保, 每次 CLK = 0 之后, 等待时间 0.5us 之后 再去 读 IO 的值(没有上拉电阻这点很重要,如果你每次读到的错误值没有规律,就是这个问题。),读完一个字节之后,等待 2us 之后再继续读下一个字节(这个你用C语言写代码的话到基本不用特别考虑,肯定会超过2us)。
回复

使用道具 举报

板凳
ID:965600 发表于 2021-9-12 18:39 | 只看该作者
188610329 发表于 2021-9-12 17:05
用Burst 读取,有两个比较 重要的注意点。
1, 发送 0xBF  或者  0xff 前, 在拉高 CE 之后, CLK 动作之 ...

拉低了IO口电平,显示还是这样
CE,IO,SCLK三个引脚都加了10k上拉电阻,我之前加的是4.7K的
  1. void DS1302TimeRead(uint8 *time)
  2. {
  3.         uint8 i;
  4.         DS1302_CE = 1;                //打开DS1302使能引脚
  5.         DS1302_IO = 0;
  6.         DS1302ByteWrite(0xBF);                                                        //开启burst读取模式,0XBF为burst读取地址
  7.         for (i=0;i<7;i++)                                                                                //burst模式一次要读写8个字节,实际日期总共7个字节,所以最后一个无意义
  8.                 {
  9.                         time[i]=DS1302ByteRead();       //循环8次,将 [年月日时分秒周] 7个字节及保护字节读取出来
  10.                 }
  11.                 delay(3);
  12.         DS1302_CE = 0;                                //关闭DS1302使能引脚                       
  13. }
复制代码
显示结果和仿真结果一样

回复

使用道具 举报

地板
ID:624769 发表于 2021-9-12 22:16 | 只看该作者
uint8 DS1302ByteRead()
{
        uint8 i;
        uint8 dat=0;                                //先给dat赋值 0X00 即 0000 0000
//        DS1302_SCLK=0;                                //拉低锁存器电平准备写入数据
        for(i=0;i<8;i++)                        //循环8次读取8组数据
        {
                if (DS1302_IO == 0)        //判断IO口数据是否为0
                {
                        dat = dat << 1;                                        //如果IO口为0,dat左移1位,左移最低位默认补0
                }
                else
                {
                        dat = dat << 1;                                        //如果IO口不为0,dat左移1位,最低位先补个0
                        dat = dat | 0x01;                                //最低位或上0x01,最低位变成1
                }
                        DS1302_SCLK = 1;        //拉高锁存器电平产生上升沿,读入数据
                        DS1302_SCLK = 0;        //拉高锁存器电平产生上升沿,读入数据
        }
        return dat;                                                //返回读取到的时间数据数组               
}

读的时候,是低位开始读,所以你要高位开始写,
        for(i=0;i<8;i++)                        //循环8次读取8组数据
        {
               dat >>= 1;                    //数据右移
               if (DS1302_IO)     dat |= 0x80;   //判断IO口数据是否为1,唯一高位置1,反之啥也不干
               DS1302_SCLK = 1;        //拉高锁存器电平产生上升沿,读入数据
               DS1302_SCLK = 0;        //拉高锁存器电平产生上升沿,读入数据
         }
   
回复

使用道具 举报

5#
ID:624769 发表于 2021-9-12 22:43 | 只看该作者
vyo 发表于 2021-9-12 18:39
拉低了IO口电平,显示还是这样
CE,IO,SCLK三个引脚都加了10k上拉电阻,我之前加的是4.7K的
显示结果和仿 ...

你个/////!! ///////了, 你说你字节读取时正确的???
你的  DS1302ByteRead()  压根是错的,你居然说字节读取能正确,用这个代码你能正确读取??……

你把DS1302ByteRead()里面循环节如下修改:

       for(i=0;i<8;i++)                        //循环8次读取8组数据
        {
                      dat >>=  1;        //先右移 !!!!!!!!!!! 不是左移
                if (DS1302_IO)      dat  |=0x80;   //判断IO口数据是否为1, 为1置1高位,否则不动
                        DS1302_SCLK = 1;        //拉高锁存器电平产生上升沿,读入数据
                        DS1302_SCLK = 0;        //拉高锁存器电平产生上升沿,读入数据
       }

最后说一下,DS1302 的时钟比较特别,写一个地址的内容, 地址+数据,其实总共只有 15个半时钟。读一个地址的内容, 地址+内容,其实总共只有15个时钟,两种方式都不是用满16个时钟,你这种强改时钟的方式,我以前强迫症状况下也作过,后来发现,强行16个时钟,下一次的读操作很容易被误判写操作。如果写保护位不置80的话,在没有数据口上拉电阻情况下,很容易误写0xff 到DS1302,。这个是后话了,你先搞定眼前的吧,总之,如果要完全按照手册的时序,代码会多很多。你之后如果,不需要频繁改内容的话,就写保护置0x80吧。要改时间了再置0。
回复

使用道具 举报

6#
ID:965600 发表于 2021-9-13 12:51 来自手机 | 只看该作者
188610329 发表于 2021-9-12 22:43
你个/////!! ///////了, 你说你字节读取时正确的???
你的  DS1302ByteRead()  压根是错的,你居然说 ...

非常感谢,太谢谢了
之前数码管显示的是年月日,数值一直没动,没发现问题,昨天夜里把显示改成时分秒,发现秒虽然是乱码,但一直在有规律的跳动,记录倒推了一下才发现是读取时高低位顺序反了。
我之前说字节读取正确是用仿照教程写的按字节读取方式的代码能正确读出。这个burst模式的代码是我自己写的。我之前一直以为是没有读到数据,所以检查的方向错了。
多谢耐心指正
回复

使用道具 举报

7#
ID:1052462 发表于 2022-11-17 16:04 | 只看该作者
void DS1302_WriteByte(uint8_t addr)
{
        uint8_t i=0;
        for(i=0;i<8;i++)
        {
                if(addr&0x01)
                {
                        DATA_H;
                }
                else
                {
                        DATA_L;
                }
                addr>>=1;
                SCLK_H;
                SCLK_L;
        }
}
uint8_t DS1302_ReadByte()
{
        uint8_t i=0,date=0;
        for(i=0;i<8;i++)
        {
                date >>=1;
                if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3))
                {
                        date |=0x80;
                }
                SCLK_H;
                SCLK_L;
        }
        return date;
}
void DS1302_BURST_Read(uint8_t *dat)
{
        uint8_t i=0;
        CE_H;
        DS1302_DAT_GPIO_Out();
        DS1302_WriteByte(0x8f);
        DS1302_DAT_GPIO_IN();
        for(i=0;i<8;i++)
        {
                dat[i]=DS1302_ReadByte();
        }
        CE_L;
}
我这个为什么BURST不行
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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