找回密码
 立即注册

QQ登录

只需一步,快速开始

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

51单片机初学实践:用DS1302模块和LCD1602显示屏实现电子钟

[复制链接]
跳转到指定楼层
楼主
本帖最后由 ricebucket 于 2020-7-12 22:10 编辑

作为51单片机初学者,制作一个电子钟是不可缺少的实践内容。周末用STC89C52RC+DS1302模块+LCD1602显示屏+DS18B20数字温度传感器实现了一个电子钟,
显示效果如下:




可以用串口命令修改RTC时钟:



实践过程中的一些经验分享:
1、DS1302模块的RST,CLK和IO口最好加上4.7k以上的上拉电阻,提高RTC数据读写可靠性,这个问题让我花了不少时间,最后在Vcc2和IO引脚之间焊接了一个10k电阻解决问题,如图所示:



2、DS1302模块的读写接口类似于I2C,使用burst mode+结构体或者数值可以提高读写效率:

  1. typedef struct rtc_data
  2. {
  3.         uint8 SS;        //秒
  4.         uint8 MI;        //分
  5.         uint8 HH;        //小时
  6.         uint8 DD;        //日
  7.         uint8 MM;        //月
  8.         uint8 DOW;        //星期
  9.         uint8 YY;        //年
  10. } T_RTC_DATA;
复制代码
  1. void DS1302_Burst_Write(T_RTC_DATA *rtc_dat)
  2. {
  3.         uint8 i, *p = (uint8 *)rtc_dat;

  4.         DS1302_RST = 1;        //使能片选信号
  5.         _nop_();

  6.         DS1302_Write_Byte(DS1302_BURST_WRITE_ADDR);

  7.         for(i=0; i<sizeof(T_RTC_DATA); i++)
  8.         {
  9.                 DS1302_Write_Byte(*p++);
  10.         }

  11.         DS1302_RST = 0;
  12.         _nop_();
  13. }

  14. void DS1302_Burst_Read(T_RTC_DATA *rtc_dat)
  15. {
  16.         uint8 i, *p = (uint8 *)rtc_dat;

  17.         DS1302_RST = 1;        //使能片选信号
  18.         _nop_();

  19.         DS1302_Write_Byte(DS1302_BURST_READ_ADDR);

  20.         for(i=0; i<sizeof(T_RTC_DATA); i++)
  21.         {
  22.                 *p++ = DS1302_Read_Byte();
  23.         }

  24.         DS1302_RST = 0;
  25. }
复制代码


3、DS18B20温度传感器采用one wire协议,时序要求精确,读写问题不大,主要还是温度读取后的转换和显示,尤其是小数位的显示,使用长度16的lookup table,可以减少重复计算:

  1. bit DS18B20_Get_Temperature(int *temp, int *sign)
  2. {
  3.         bit ack;
  4.         uint8 LSB, MSB;

  5.         ack = DS18B20_Get_Ack();
  6.         if(ack == 0)
  7.         {
  8.                 DS18B20_Write_Byte(0xCC); //跳过ROM
  9.                 DS18B20_Write_Byte(0xBE); //跳过温度采集
  10.                 LSB = DS18B20_Read_Byte(); //读低字节温度值
  11.                 MSB = DS18B20_Read_Byte(); //读高字节温度值
  12.                 *temp = ((int)MSB<<8) + LSB;

  13.                 if(0 > *temp)
  14.                 {
  15.                         *temp -= 1;
  16.                         *temp  = ~*temp; //对负温度数据取补码
  17.                         *sign = -1; // 负数
  18.                 }
  19.                 else
  20.                 {
  21.                         *sign = 1; // 正数
  22.                 }
  23.         }
  24.         return ~ack;
  25. }
复制代码
  1.         // DS18B20的小数位四舍五入显示结果速查表,4bit=索引取值范围 0 - 15
  2.         uint8 code dect_lookup_tab[] = {0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9};
复制代码
  1.                         slen = 0;
  2.                         sbuf[slen++] = ds18b20_temp_sign_s < 0 ? '-' : '+';
  3.                         
  4.                         intT %= 100;
  5.                         
  6.                         if(intT > 10) sbuf[slen++] = '0' + intT / 10;
  7.                         sbuf[slen++] = '0' + intT % 10;
  8.                         sbuf[slen++] = '.';
  9.                         
  10.                         sbuf[slen++] = '0' + dect_lookup_tab[decT]; // 使用之前已经算好的四舍五入结果查表,速度更快。
  11.                         //sbuf[slen++] = '0' + (decT*10) / 16;  //二进制的小数部分转换为1位十进制位, 小数部分转换为可显示的数字字符
  12.                         
  13.                         sbuf[slen++] = '\0';                 //添加字符串结束符
  14.                         LCD1602_Show_Str(10, 0, sbuf);     //显示到液晶屏上
复制代码



4、uart命令部分,可以使用串口中断接收输入到UART_Rxd_Buf,然后选择一个合适的定时器间隔读取,解析并执行,详见附件代码。

  1. /* 串口动作函数,根据接收到的命令帧执行响应的动作
  2.    buf-接收到的命令帧指针,len-命令帧长度 */
  3. void Uart_Cmd_Handler(uint8 *buf)
  4. {
  5.     int8 slen = 0;

  6.         printf(">cmd recv: [%s]\r\n", buf);

  7.         if(0 == strncmp("rtc set ", buf, 8))
  8.         {
  9.                 if(0 != UART_Cmd_Exec_RTC_Set(buf+8))
  10.                 {
  11.                         printf(">cmd exec: failed.\r\n");
  12.                 }
  13.         }
  14.         else
  15.         {
  16.                 printf(">cmd unrecognized.\r\n");
  17.         }
  18. }
复制代码

5、main函数内容:
  1. void main()
  2. {
  3.     int8 slen = 0;
  4.         uint8 pdata uart_cmd_buf[64] = {0};

  5.     EA = 1;        //开总中断
  6.         DS18B20_Start();
  7.         UART_Config(9600);
  8.     ConfigTimer0(TIMER0_SLICE_MS);        //T0定时10ms

  9.     DS1302_Init();        //初始化RTC时钟
  10.     LCD1602_Init();        //初始化液晶

  11.         LCD1602_Show_Str(0, 0, "**:**:**");
  12.         LCD1602_Show_Str(0, 1, "20**#**#**# ???");
  13.         LCD1602_Show_Char(15, 0, 0);        //5x7字符 ℃
  14.         LCD1602_Show_Char(4, 1, 1);        //5x7字符 年
  15.         LCD1602_Show_Char(7, 1, 2);        //5x7字符 月
  16.         LCD1602_Show_Char(10, 1, 3);        //5x7字符 日

  17.     while(1)
  18.     {
  19.                 Uart_Cmd_Check(&uart_cmd_buf, sizeof(uart_cmd_buf)-1);

  20.         if (timer_flag_250ms)  //每250ms读取依次时间
  21.         {
  22.                         ReadAndShowRtc();
  23.             timer_flag_250ms = 0;
  24.         }
  25.         if (timer_flag_3s)  //每隔3s执行以下分支
  26.         {
  27.                         ReadAndShowTemperature();
  28.             timer_flag_3s = 0;
  29.                 }
  30.     }
  31. }
复制代码


附件文件列表如图所示:



以上代码使用C51开发板调试,接线简单,具体端口可参看config.h

初学单片机,难免有错漏之处,还请各位坛友不吝赐教。


DS1302_UART_LCD1602_STC89C52RC.7z (2.35 MB, 下载次数: 81)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

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

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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