[PIC®/AVR®/dsPIC®产品] 【Curiosity Nano测评报告】I2C通讯实验

[复制链接]
2021|5
 楼主| hu9jj 发表于 2024-9-27 17:37 | 显示全部楼层 |阅读模式
本帖最后由 hu9jj 于 2024-9-27 17:37 编辑

#申请原创#
    I2C是常见的通讯协议之一,用于本次实验的I2C器件是DS1307日历模块,下图就是从某宝购入的日历模块:

    这个模块背面是一颗CR2032电池,正面是一颗DS1307日历芯片及一颗AT24C32EEPROM,以及相应的外围电路。板上还留有DS18B20温湿度传感器的焊盘,但没有安装元件。
    本次先后实验了硬件I2C和软件模拟I2C。

一、硬件I2C实验
    首先进入MCC进行配置,PIC16F13146的I2C外设有主从之分,我手上的I2C设备都是从设备,因此单片机选择为I2C主设备,传输率为最常见的100k:

    生成代码后,根据mssp1.h文件,我们需要使用的主要有三个函数:
    使用I2C1_Write()函数对DS1307进行写入操作:
  1. /******************************************************************************************
  2. * 函数名称:        DS1307_write_date()
  3. * 功能说明: 读取DS1307日期时间数据
  4. * 输    入: 无
  5. * 输    出: 无
  6. ******************************************************************************************/
  7. void DS1307_WriteDate(void)
  8. {
  9.     uint8_t i, dat[9];
  10.     week = GetWeek(year,month,day);
  11.     dat[0] = 0;                                //起始地址
  12.     dat[1] = 0;                                //秒
  13.     dat[2] = ((minute/10)<<4|(minute%10));     //分
  14.     dat[3] = ((hour/10)<<4|(hour%10));         //时
  15.     dat[4] = week;                             //星期
  16.     dat[5] = ((day/10)<<4|(day%10));           //日
  17.     dat[6] = ((month/10)<<4|(month%10));       //月
  18.     dat[7] = ((year%100)/10<<4|(year%10));     //年
  19.     dat[8] = 16;                               //0001 0000 = 允许按1Hz输出方波,低电平触发

  20.     i = I2C1_Write(DS1307_Addr_7,dat,9);       //写入1字节地址及8个字节的数据
  21. }

   经测试能够正常写入到DS1307芯片中,但读DS1307却不成功。我最初的读取代码如下:
  1. /******************************************************************************************
  2. * 函数名称:        DS1307_read_date()
  3. * 功能说明: 读取DS1307日期时间数据
  4. * 输    入: 无
  5. * 输    出: 无
  6. ******************************************************************************************/
  7. void DS1307_ReadDate(void)
  8. {
  9.     uint8_t d=0;
  10.     bool i;
  11.    
  12.     i = I2C1_Write(DS1307_Addr_7,d,1);                     //写入地址0
  13. //    printf("写地址返回值=%d\n",i);
  14.     i = I2C1_Read(DS1307_Addr_7,DS_Buff,7);                //读出7个字节的数据

  15.     second = ((DS_Buff[0]&0x70)>>4)*10 + (DS_Buff[0]&0x0F);//秒,屏蔽秒的第7位的标志
  16.     minute = ((DS_Buff[1]&0x70)>>4)*10 + (DS_Buff[1]&0x0F);//分(取低7位)
  17.     hour = ((DS_Buff[2]&0x30)>>4)*10 + (DS_Buff[2]&0x0F);  //时(取低6位)
  18.     week = (DS_Buff[3]&0x07);                              //周(取低3位)
  19.     day = ((DS_Buff[4]&0x30)>>4)*10 + (DS_Buff[4]&0x0F);   //日(取低6位)
  20.     month = ((DS_Buff[5]&0x10)>>4)*10 + (DS_Buff[5]&0x0F); //月(取低5位)
  21.     year = 2000 + (DS_Buff[6]>>4)*10 + (DS_Buff[6]&0x0F);  //年
  22. }
   也就是先写入要读取数据的首地址,再连续读出7个字节的数据,即写地址与读数据是分开操作的。可按照DS1307芯片数据手册,写入地址后没有stop,直接start开始读数据的操作,其时序图如下:

    由于时序不符合要求,因此读出的日期数据不正确。
    之后我改用了I2C1_WriteRead()函数,也就是先写入数据接着再读出数据,按说可以符合DS1307的时序要求,可测试结果却读不出数据。用逻辑分析仪抓取的SCL和SDA时序却是连续不断的固定规律的波形(见下图),折腾了多日仍没有头绪。
    至此,硬件I2C的实验只好告一段落。

二、软件模拟I2C实验
    在MCC中删除I2C的外设,然后RB4引脚为输入模式,RB6引脚为输出模式。其中RB4引脚作为SDA使用,使用过程中根据需要动态设置成输入或输出。RB6引脚则作为SCL使用。

    分析I2C的时序,不外是下列6种时序:

    1、在空闲时,SCL和SDA同时为高,当SDA先拉低,然后SCL紧接着也拉低,此时在总线上产生一个START,即开始信号,这是由主设备发出的;
    2、在SCL由低拉高后,SDA的电平为低,则表示为数据'0',SDA的电平为高,则表示为数据'1';
    3、在传输完8byte数据后,接收方在SCL为低电平时拉低SDA,一直保持到SCL变高后再变低,这就是给发送方的ACK应答信号,表示可以继续进行下一个字节的传递。如果在这个周期中SDA的电平一直为高,则是给发送方的NOACK非应答信号,表示结束字节的传递;
    4、在SCL和SDA同时为低电平时,先拉高SCL的电平,紧接着再拉高SDA的电平,则产生一个STOP停止信号。
    实际上数据‘0’的时序与ACK的时序完全一样,数据‘1’的时序与NOACK的时序完全一样,只不过ACK和NOACK是在一个字节传输结束后,由接收方发出的。
    弄清楚了I2C所必须的时序,就可以编写软件模拟I2C的代码了:
  1. /******************************************************************************************************************************************
  2. * 函数名称: SI2C_Start()
  3. * 功能说明: 产生I2C传输的Start信号
  4. * 输    入: 无
  5. * 输    出: 无
  6. ******************************************************************************************************************************************/
  7. void SI2C_Start(void)
  8. {
  9.     SDA_OUT();           //SDA输出
  10.     SDA_1();
  11.     SCL_1();             //scl = 1;
  12.     DELAY_microseconds(1);
  13.         SDA_0();             //sda = 0;        scl为高时sda的下降沿表示“起始”
  14.     DELAY_microseconds(1);
  15.         SCL_0();             //scl = 0;钳住I2C总线,准备发送或接收数据 START:when CLK is high,DATA change form high to low
  16. }


  17. /******************************************************************************************************************************************
  18. * 函数名称: SI2C_Stop()
  19. * 功能说明: 产生I2C传输的Stop信号
  20. * 输    入: 无
  21. * 输    出: 无
  22. ******************************************************************************************************************************************/
  23. void SI2C_Stop(void)
  24. {
  25.     SDA_OUT();
  26.     SCL_0();             // scl = 0;
  27.     SDA_0();             // STOP:when CLK is high DATA change form low to high
  28.     DELAY_microseconds(1);
  29.     SCL_1();             // scl = 1;
  30.     DELAY_microseconds(1);
  31.     SDA_1();             // sda = 1;        sclk为高时sdat的上升沿表示“停止”
  32. //    DELAY_microseconds(2);
  33. }


  34. /******************************************************************************************************************************************
  35. * 函数名称: SI2C_Send()
  36. * 功能说明: 向IIC总线发送一个字节的数据
  37. * 输    入: byte dat         要发送的数据
  38. * 输    出: 无
  39. ******************************************************************************************************************************************/
  40. void SI2C_Send(uint8_t dat)
  41. {
  42.     uint8_t i;
  43.     SDA_OUT();
  44.     SCL_0();             //拉低时钟开始数据传输
  45.     for(i=0;i<8;i++)
  46.     {
  47.         if(dat&0x80)
  48.             SDA_1();     //准备好SDA数据
  49.         else
  50.             SDA_0();
  51.         dat<<=1;
  52.         DELAY_microseconds(1);
  53.         SCL_1();                //拉高时钟等待从设备读取数据
  54.         DELAY_microseconds(2);
  55.         SCL_0();               //拉低时钟准备下一位数据
  56. //  
  57.     }
  58. }


  59. /******************************************************************************************************************************************
  60. * 函数名称: SI2C_Receive()
  61. * 功能说明: 从IIC总线接收一个字节的数据
  62. * 输    入: 无
  63. * 输    出: byte   从IIC总线上接收到得数据
  64. * 注意事项: 无
  65. ******************************************************************************************************************************************/
  66. uint8_t SI2C_Receive(void)
  67. {
  68.     uint8_t i,dat = 0;
  69.     SDA_IN();        //设置为输入       
  70.     for(i=0;i<8;i++)
  71.     {
  72.         SCL_0();
  73.         DELAY_microseconds(2);
  74.         SCL_1();
  75.         dat<<=1;
  76.         if(SDA_X() > 0)
  77.             dat|=0x01;
  78.         DELAY_microseconds(2);
  79.     }
  80.     SCL_0();
  81.     return dat;
  82. }


  83. /******************************************************************************************************************************************
  84. * 函数名称: SI2CDoAck()
  85. * 功能说明: 在应答位位置产生应答,从而继续连续传输
  86. * 输    入: 无
  87. * 输    出: 无
  88. ******************************************************************************************************************************************/
  89. void SI2CDoAck(void)
  90. {
  91.     SCL_0();
  92.     SDA_OUT();
  93.     SDA_0();              //sda = 0;        /拉低数据线,即给于应答
  94.     DELAY_microseconds(1);
  95.     SCL_1();              //scl = 1;
  96.     DELAY_microseconds(2);
  97.     SCL_0();                  //scl = 0;
  98. }


  99. /******************************************************************************************************************************************
  100. * 函数名称: SI2CNoAck()
  101. * 功能说明: 在应答位位置不产生应答,从而终止连续传输
  102. * 输    入: 无
  103. * 输    出: 无
  104. ******************************************************************************************************************************************/
  105. void SI2CNoAck(void)
  106. {
  107.     SCL_0();
  108.     SDA_OUT();
  109.     SDA_1();              // sda = 1;        不拉低数据线,即不给于应答
  110.     DELAY_microseconds(1);
  111.     SCL_1();              // scl = 1;
  112.     DELAY_microseconds(2);
  113.     SCL_0();              // scl = 0;
  114. }


  115. /******************************************************************************************************************************************
  116. * 函数名称: SI2CIsAck()
  117. * 功能说明: 检测从机应答位
  118. * 输    入: 无
  119. * 输    出: uint8_t        0=ACK_OK 从机产生了应答;1=ACK_NO 从机没有产生应答
  120. ******************************************************************************************************************************************/
  121. uint8_t SI2CIsAck(void)
  122. {
  123.     uint8_t i;
  124.     SDA_OUT();
  125.     SDA_1();              // sda = 1; 释放数据线
  126.     DELAY_microseconds(1);
  127.     SDA_IN();
  128.     SCL_1();              // scl = 1;
  129.     DELAY_microseconds(1);
  130.     while(SDA_X() > 0){
  131.         i++;
  132.         if(i > 250){
  133.             SI2C_Stop();//数据线未被拉低,即未收到应答
  134.             SDA_OUT();
  135.             return 1;
  136.         }
  137.     }
  138.     SCL_0();
  139.     DELAY_microseconds(1);
  140.     SDA_OUT();
  141.     return 0;
  142. }
   以上是模拟I2C的基础代码,由此可以写出8位寄存器地址的单个字节读写或多个字节读写的函数:
  1. /**********************************************************************************************
  2. * 函数名称: I2C_8bitByteWrite()
  3. * 功能说明: 向I2C器件的地址addr开始写入一个字节的数据
  4. * 输    入: uint8_t addr     器件地址
  5. *           uint8_t reg      寄存器地址
  6. *           uint8_t data     要写入的数据
  7. * 输    出: uint8_t        0=成功向器件写入数据  大于1=向器件写入数据过程中出现错误
  8. **********************************************************************************************/
  9. uint8_t SI2C_8bitByteWrite(uint8_t addr,uint8_t reg,uint8_t data)
  10. {
  11.     SI2C_Start();         //产生起始信号
  12.     SI2C_Send(addr|0);    //发送器件地址及读写位,0表示写
  13.     if(SI2CIsAck())       //检测器件是否有响应
  14.     {
  15.         SI2C_Stop();      //产生停止信号
  16.         return 1;
  17.     }

  18.     SI2C_Send(reg);       //发送数据要写入的地址
  19.     if(SI2CIsAck())       //检测器件是否有响应
  20.     {
  21.         SI2C_Stop();      //产生停止信号
  22.         return 2;
  23.     }

  24.     SI2C_Send(data);      //写入数据
  25.     if(SI2CIsAck())       //检测器件是否有响应
  26.     {
  27.         SI2C_Stop();      //产生停止信号
  28.         return 3;
  29.     }

  30.     SI2C_Stop();          //产生停止信号
  31.     return 0;
  32. }

  33. /**********************************************************************************************
  34. * 函数名称: I2C_8bitByteRead()
  35. * 功能说明: 从I2C器件的地址addr读取1字节数据
  36. * 输    入: uint8_t addr     器件地址
  37. *           uint8_t reg      读取数据的地址
  38. * 输    出: 读取的数据
  39. **********************************************************************************************/
  40. uint8_t SI2C_8bitByteRead(uint8_t addr,uint8_t reg)
  41. {
  42.     uint8_t data;

  43.     SI2C_Start();         //产生起始信号
  44.     SI2C_Send(addr);      //发送器件地址及读写位,0表示写
  45.     if(SI2CIsAck())       //检测器件是否有响应
  46.     {
  47.         SI2C_Stop();      //产生停止信号
  48.         return 2;
  49.     }
  50.     SI2C_Send(reg);       //发送读取数据的起始地址
  51.     if(SI2CIsAck())       //检测器件是否有响应
  52.     {
  53.         SI2C_Stop();          //产生停止信号
  54.         return 3;
  55.     }

  56.     SI2C_Start();         //产生Repeated Start
  57.     SI2C_Send(addr|1);    //发送器件地址及读写位,1表示读
  58.     if(SI2CIsAck())       //检测器件是否有响应
  59.     {
  60.         SI2C_Stop();      //产生停止信号
  61.         return 4;
  62.     }

  63.     data = SI2C_Receive();//从addr处读取1个字节的数据
  64.       
  65.     SI2CNoAck();          //器件要求必须使用NOAck来结束数据读取
  66.     SI2C_Stop();          //产生停止信号

  67.     return data;
  68. }

  69. /**********************************************************************************************
  70. * 函数名称: I2C_8bitBuffWrite()
  71. * 功能说明: 向I2C器件的地址addr开始写入size个字节的数据,将要写入的数据存储在全局变量I2C_Buff中
  72. * 输    入: uint8_t addr     器件地址
  73. *           uint8_t reg      写入数据的寄存器地址
  74. *           uint8_t size     要设置的数据个数(1~16),数据在buf[]中
  75. * 输    出: uint8_t        0=成功向器件写入数据  大于1=向器件写入数据过程中出现错误
  76. **********************************************************************************************/
  77. uint8_t SI2C_8bitBuffWrite(uint8_t addr,uint8_t reg,uint8_t size,uint8_t *buf)
  78. {
  79.     uint8_t i = 0;

  80.     SI2C_Start();         //产生起始信号
  81.     SI2C_Send(addr|0);    //发送器件地址及读写位,0表示写
  82.     if(SI2CIsAck())       //检测器件是否有响应
  83.     {
  84.         SI2C_Stop();      //产生停止信号
  85.         return 1;
  86.     }

  87.     SI2C_Send(reg);       //发送数据要写入的地址
  88.     if(SI2CIsAck())       //检测器件是否有响应
  89.     {
  90.         SI2C_Stop();      //产生停止信号
  91.         return 2;
  92.     }

  93.     for(i=0; i<size; i++)
  94.     {
  95.         SI2C_Send(buf[i]);
  96.         if(SI2CIsAck())   //检测器件是否有响应
  97.         {
  98.             SI2C_Stop();  //产生停止信号
  99.             return 3;
  100.         }
  101.     }

  102.     SI2C_Stop();          //产生停止信号
  103.        
  104.     return 0;
  105. }


  106. /******************************************************************************************
  107. * 函数名称: I2C_8bitBuffRead()
  108. * 功能说明: 从指定的I2C器件地址addr开始获取size个字节的数据,获取的数据存储在全局变量I2C_Buff中
  109. * 输    入: uint8_t addr     I2C器件地址
  110. *           uint8_t reg      获取数据从addr开始
  111. *           uint8_t size     要获取的数据个数(1~16)
  112. * 输    出: 从器件获取数据(在全局变量数组DS_Buff中)
  113. ******************************************************************************************/
  114. uint8_t SI2C_8bitBuffRead(uint8_t addr,uint8_t reg,uint8_t size)
  115. {
  116.     uint8_t i = 0;

  117.     SI2C_Start();         //产生起始信号
  118.     SI2C_Send(addr);      //发送器件地址及读写位,0表示写
  119.     if(SI2CIsAck())       //检测器件是否有响应
  120.     {
  121.         SI2C_Stop();      //产生停止信号
  122.         return 2;
  123.     }

  124.     SI2C_Send(reg);       //发送读取数据的起始地址
  125.     if(SI2CIsAck())       //检测器件是否有响应
  126.     {
  127.         SI2C_Stop();      //产生停止信号
  128.         return 3;
  129.     }

  130.     SI2C_Start();         //产生Repeated Start
  131.     SI2C_Send(addr|1);    //发送器件地址及读写位,1表示读
  132.     if(SI2CIsAck())       //检测器件是否有响应
  133.     {
  134.         SI2C_Stop();      //产生停止信号
  135.         return 4;
  136.     }

  137.     for(i=0;i<size;i++)    //从addr处开始读取size个字节的数据
  138.     {
  139.         DS_Buff[i] = SI2C_Receive();
  140.         if(i+1==size)
  141.             SI2CNoAck();   //器件要求必须使用NOAck来结束数据读取
  142.         else
  143.             SI2CDoAck();
  144.     }

  145.     SI2C_Stop();          //产生停止信号

  146.     return 0;
  147. }
   有了以上函数,就可以进一步写出DS1307的读写函数了:
  1. /******************************************************************************************
  2. * 函数名称:        GetWeek(Y,M,D)
  3. * 功能说明: 通过日期(yesr,month,day)计算星期
  4. * 输    入: Y,M,D
  5. * 输    出: W(周日=7)
  6. ******************************************************************************************/
  7. uint8_t GetWeek(uint16_t Y,uint8_t M,uint8_t D)
  8. {
  9.     uint8_t W;
  10.     if(M<3){
  11.         M += 12;
  12.         Y--;
  13.     }
  14.     W = (D+1+2*M+3*(M+1)/5+Y+Y/4-Y/100+Y/400)%7;
  15.     if(0==W)
  16.         W = 7;
  17.     return W;
  18. //    W = (Y-1) * 365.2425 + 0.5 + (M-1) * 30 +D;
  19.    
  20. }

  21. /******************************************************************************************
  22. * 函数名称:        DS1307_Init()
  23. * 功能说明: 用当前日期(yesr,month,day,hour,minute)初始化DS1307
  24. * 输    入: 无
  25. * 输    出: uint8_t        0=RET_OK 初始化成功        1=RET_ERR 初始化出错
  26. ******************************************************************************************/
  27. void DS1307_Init(void)
  28. {
  29.     uint8_t d=0;
  30.     bool i;

  31.     d = SI2C_8bitByteRead(DS1307_Addr,0);
  32.         if(d > 127){
  33.         year = 2024;
  34.         month = 9;
  35.         day = 23;
  36.         week = 1;
  37.         hour = 12;
  38.         minute = 30;
  39.         second = 0;
  40.         DS1307_WriteDate();
  41.     }
  42. }


  43. /******************************************************************************************
  44. * 函数名称:        DS1307_read_DS_Buffe()
  45. * 功能说明: 读取DS1307日期时间数据
  46. * 输    入: 无
  47. * 输    出: 无
  48. ******************************************************************************************/
  49. void DS1307_ReadDate(void)
  50. {
  51.     uint8_t d=0;
  52.     d = SI2C_8bitBuffRead(DS1307_Addr,0,8);
  53.    
  54.     second = ((DS_Buff[0]&0x70)>>4)*10 + (DS_Buff[0]&0x0F);//秒,屏蔽秒的第7位的标志
  55.     minute = ((DS_Buff[1]&0x70)>>4)*10 + (DS_Buff[1]&0x0F);//分(取低7位)
  56.     hour = ((DS_Buff[2]&0x30)>>4)*10 + (DS_Buff[2]&0x0F);  //时(取低6位)
  57.     week = (DS_Buff[3]&0x07);                              //周(取低3位)
  58.     day = ((DS_Buff[4]&0x30)>>4)*10 + (DS_Buff[4]&0x0F);   //日(取低6位)
  59.     month = ((DS_Buff[5]&0x10)>>4)*10 + (DS_Buff[5]&0x0F); //月(取低5位)
  60.     year = 2000 + (DS_Buff[6]>>4)*10 + (DS_Buff[6]&0x0F);  //年
  61. }


  62. /******************************************************************************************
  63. * 函数名称:        DS1307_write_DS_Buffe()
  64. * 功能说明: 读取DS1307日期时间数据
  65. * 输    入: 无
  66. * 输    出: 无
  67. ******************************************************************************************/
  68. void DS1307_WriteDate(void)
  69. {
  70.     uint8_t i, dat[8];
  71.     week = GetWeek(year,month,day);
  72.     dat[0] = 0;                                //秒
  73.     dat[1] = ((minute/10)<<4|(minute%10));     //分
  74.     dat[2] = ((hour/10)<<4|(hour%10));         //时
  75.     dat[3] = week;                             //星期
  76.     dat[4] = ((day/10)<<4|(day%10));           //日
  77.     dat[5] = ((month/10)<<4|(month%10));       //月
  78.     dat[6] = ((year%100)/10<<4|(year%10));     //年
  79.     dat[7] = 16;                               //0001 0000 = 允许按1Hz输出方波,低电平触发
  80.     i = SI2C_8bitBuffWrite(DS1307_Addr,0,8,dat);
  81. }
   至此,成功地完成了对DS1307的读写操作实验。
    软件模拟I2C比较灵活直观,不象使用硬件I2C那样有“隔靴搔痒”的感觉,而且还能够适应某些特殊的I2C设备,因此深受大家的喜爱。在上述的读写函数中扩充寄存器地址的长度,还可以适应16位甚至更高位寄存器的地址器件。




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
小灵通2018 发表于 2024-9-29 19:42 | 显示全部楼层
看着不错。比较好用。
21mengnan 发表于 2024-9-30 11:59 | 显示全部楼层
看着很好用。
单片小菜 发表于 2024-9-30 13:52 | 显示全部楼层
void DS1307_WriteDate(void)这个函数进不去
自己造声卡 发表于 2024-9-30 14:14 | 显示全部楼层
直接上函数,代码看的太累了。
 楼主| hu9jj 发表于 2024-9-30 17:28 | 显示全部楼层
  今天继续测试硬件I2C的写读函数。通过逻辑分析仪抓取的时序,发现问题出在开始的写寄存器地址上,其函数结构如下:
  1. bool I2C1_WriteRead(uint16_t address, uint8_t *writeData, size_t writeLength, uint8_t *readData, size_t readLength);
   第一个参数是器件地址,第二个参数是写入的数组,第三个参数是写入的长度,第四个参数是读出的数组,第五个参数是读出的长度。我因为只需要写入一个字节,所以开始就用一个变量来代替写入的数组:
  1. d = 0;
  2.     i = I2C1_Host_WriteRead(DS1307_Addr_7,&d,1,&DS_Buff,7);
   就是这里出了问题,变量不能代替数组。实际执行的结果写入的不是0地址,而是一个不确定的值,每次操作其值都不相同,所以最终读出的数据是错误的日期。下面就是逻辑分析仪抓取的数据:

    找出原因之后,我尝试修改代码,定义了两个数组,一个存放读出的数据,另一个存放要写入的数据:
  1.     dat[0] = 0;
  2.     i = I2C1_Host_WriteRead(DS1307_Addr_7,&dat,1,&DS_Buff,7);
   这次测试得到了正确的结果,从逻辑分析仪抓取的时序数据是正确的:

    通过串口获得的也是正确的日期:

    下面是DS1307的完整驱动代码:

  1. //#include "My_i2c.h"
  2. #include "ds1307.h"


  3. extern uint16_t year;              //年
  4. extern uint8_t week,month,day,hour,minute,second,DS_Buff[16]; //月日时分秒
  5. uint8_t dat[9]={0,0,0,0,0,0,0,0,0,};

  6. /******************************************************************************************
  7. * 函数名称:        GetWeek(Y,M,D)
  8. * 功能说明: 通过日期(yesr,month,day)计算星期
  9. * 输    入: Y,M,D
  10. * 输    出: W(周日=7)
  11. ******************************************************************************************/
  12. uint8_t GetWeek(uint16_t Y,uint8_t M,uint8_t D)
  13. {
  14.     uint8_t W;
  15.     if(M<3){
  16.         M += 12;
  17.         Y--;
  18.     }
  19.     W = (D+1+2*M+3*(M+1)/5+Y+Y/4-Y/100+Y/400)%7;
  20.     if(0==W)
  21.         W = 7;
  22.     return W;
  23. //    W = (Y-1) * 365.2425 + 0.5 + (M-1) * 30 +D;
  24.    
  25. }

  26. /******************************************************************************************
  27. * 函数名称:        DS1307_Init()
  28. * 功能说明: 用当前日期(yesr,month,day,hour,minute)初始化DS1307
  29. * 输    入: 无
  30. * 输    出: uint8_t        0=RET_OK 初始化成功        1=RET_ERR 初始化出错
  31. ******************************************************************************************/
  32. void DS1307_Init(void)
  33. {
  34.     bool i;
  35.     dat[0] = 0;
  36.     i = I2C1_Host_WriteRead(DS1307_Addr_7,&dat,1,&DS_Buff,7);
  37. //    d = SI2C_8bitByteRead(DS1307_Addr,0);
  38.     if(DS_Buff[0] > 127){
  39.         year = 2024;
  40.         month = 9;
  41.         day = 23;
  42.         week = 1;
  43.         hour = 12;
  44.         minute = 30;
  45.         second = 0;
  46.         DS1307_WriteDate();
  47.     }
  48. }


  49. /******************************************************************************************
  50. * 函数名称:        DS1307_read_DS_Buffe()
  51. * 功能说明: 读取DS1307日期时间数据
  52. * 输    入: 无
  53. * 输    出: 无
  54. ******************************************************************************************/
  55. void DS1307_ReadDate(void)
  56. {
  57.     bool i;
  58.     dat[0] = 0;
  59.     i = I2C1_Host_WriteRead(DS1307_Addr_7,&dat,1,&DS_Buff,7);
  60. //    d = SI2C_8bitBuffRead(DS1307_Addr,0,8);
  61.    
  62.     second = ((DS_Buff[0]&0x70)>>4)*10 + (DS_Buff[0]&0x0F);//秒,屏蔽秒的第7位的标志
  63.     minute = ((DS_Buff[1]&0x70)>>4)*10 + (DS_Buff[1]&0x0F);//分(取低7位)
  64.     hour = ((DS_Buff[2]&0x30)>>4)*10 + (DS_Buff[2]&0x0F);  //时(取低6位)
  65.     week = (DS_Buff[3]&0x07);                              //周(取低3位)
  66.     day = ((DS_Buff[4]&0x30)>>4)*10 + (DS_Buff[4]&0x0F);   //日(取低6位)
  67.     month = ((DS_Buff[5]&0x10)>>4)*10 + (DS_Buff[5]&0x0F); //月(取低5位)
  68.     year = 2000 + (DS_Buff[6]>>4)*10 + (DS_Buff[6]&0x0F);  //年
  69. }


  70. /******************************************************************************************
  71. * 函数名称:        DS1307_write_DS_Buffe()
  72. * 功能说明: 读取DS1307日期时间数据
  73. * 输    入: 无
  74. * 输    出: 无
  75. ******************************************************************************************/
  76. void DS1307_WriteDate(void)
  77. {
  78.     bool i;
  79.     week = GetWeek(year,month,day);
  80.     dat[0] = 0;                                //寄存器地址
  81.     dat[1] = 0;                                //秒
  82.     dat[2] = ((minute/10)<<4|(minute%10));     //分
  83.     dat[3] = ((hour/10)<<4|(hour%10));         //时
  84.     dat[4] = week;                             //星期
  85.     dat[5] = ((day/10)<<4|(day%10));           //日
  86.     dat[6] = ((month/10)<<4|(month%10));       //月
  87.     dat[7] = ((year%100)/10<<4|(year%10));     //年
  88.     dat[8] = 16;                               //0001 0000 = 允许按1Hz输出方波,低电平触发
  89.     i = I2C1_Host_Write(DS1307_Addr_7,&dat,9);
  90. //    i = SI2C_8bitBuffWrite(DS1307_Addr,0,8,dat);
  91. }






本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

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

本版积分规则

认证:Microchip
简介:让我们来为您提供帮助。我们可提供各种资源来帮助您解决一切问题。是否需要与我们的客户支持团队联系?您可以通过电话、在线聊天功能或电子邮件与他们联系。

151

主题

1063

帖子

11

粉丝
快速回复 在线客服 返回列表 返回顶部