当前位置:   article > 正文

51单片机——多功能电子钟_51单片机多功能电子钟

51单片机多功能电子钟

单片机——多功能电子钟


宗旨:技术的学习是有限的,分享的精神是无限的。


实现的功能有:走 时、校时、闹钟、温度、遥控这几个功能。要想实现这几个功能,其中走时所需要的就是时 钟芯片,即 DS1302;时间需要显示给人看,就需要显示器件,我们用到了点阵、数码管、 独立 LED、液晶;再来看校时,校时需要输入器件。

 

注重模块化思想:

  1. // 工程配置头文件config.h:
  2. #ifndef _CONFIG_H
  3. #define _CONFIG_H
  4. /* 通用头文件 */
  5. #include <reg52.h>
  6. #include <intrins.h>
  7. /* 数据类型定义 */
  8. typedef signed char int8; // 8位有符号整型数
  9. typedef signed int int16; //16位有符号整型数
  10. typedef signed long int32; //32位有符号整型数
  11. typedef unsigned char uint8; // 8位无符号整型数
  12. typedef unsigned int uint16; //16位无符号整型数
  13. typedef unsigned long uint32; //32位无符号整型数
  14. /* 全局运行参数定义 */
  15. #define SYS_MCLK (11059200/12) //系统主时钟频率,即振荡器频率÷12
  16. /* IO引脚分配定义 */
  17. sbit KEY_IN_1 = P2 ^ 4; //矩阵按键的扫描输入引脚1
  18. sbit KEY_IN_2 = P2 ^ 5; //矩阵按键的扫描输入引脚2
  19. sbit KEY_IN_3 = P2 ^ 6; //矩阵按键的扫描输入引脚3
  20. sbit KEY_IN_4 = P2 ^ 7; //矩阵按键的扫描输入引脚4
  21. sbit KEY_OUT_1 = P2 ^ 3; //矩阵按键的扫描输出引脚1
  22. sbit KEY_OUT_2 = P2 ^ 2; //矩阵按键的扫描输出引脚2
  23. sbit KEY_OUT_3 = P2 ^ 1; //矩阵按键的扫描输出引脚3
  24. sbit KEY_OUT_4 = P2 ^ 0; //矩阵按键的扫描输出引脚4
  25. sbit ADDR0 = P1 ^ 0; //LED位选译码地址引脚0
  26. sbit ADDR1 = P1 ^ 1; //LED位选译码地址引脚1
  27. sbit ADDR2 = P1 ^ 2; //LED位选译码地址引脚2
  28. sbit ADDR3 = P1 ^ 3; //LED位选译码地址引脚3
  29. sbit ENLED = P1 ^ 4; //LED显示部件的总使能引脚
  30. #define LCD1602_DB P0 //1602液晶数据端口
  31. sbit LCD1602_RS = P1 ^ 0; //1602液晶指令/数据选择引脚
  32. sbit LCD1602_RW = P1 ^ 1; //1602液晶读写引脚
  33. sbit LCD1602_E = P1 ^ 5; //1602液晶使能引脚
  34. sbit DS1302_CE = P1 ^ 7; //DS1302片选引脚
  35. sbit DS1302_CK = P3 ^ 5; //DS1302通信时钟引脚
  36. sbit DS1302_IO = P3 ^ 4; //DS1302通信数据引脚
  37. sbit I2C_SCL = P3 ^ 7; //I2C总线时钟引脚
  38. sbit I2C_SDA = P3 ^ 6; //I2C总线数据引脚
  39. sbit BUZZER = P1 ^ 6; //蜂鸣器控制引脚
  40. sbit IO_18B20 = P3 ^ 2; //DS18B20通信引脚
  41. sbit IR_INPUT = P3 ^ 3; //红外接收引脚
  42. #endif /* _CONFIG_H */<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
  1. // 头文件Lcd1602.h:
  2. #ifndef _LCD1602_H
  3. #define _LCD1602_H
  4. #ifndef _LCD1602_C
  5. #endif
  6. void InitLcd1602();
  7. void LcdClearScreen();
  8. void LcdOpenCursor();
  9. void LcdCloseCursor();
  10. void LcdSetCursor(uint8 x, uint8 y);
  11. void LcdShowStr(uint8 x, uint8 y, uint8*str);
  12. void LcdShowChar(uint8 x, uint8 y, uint8chr);
  13. #endif /* _LCD1602_H */
  14. // 实时时钟芯片DS1302驱动模块的头文件DS1302.h:
  15. #ifndef _DS1302_H
  16. #define _DS1302_H
  17. struct sTime //日期时间结构
  18. {
  19. uint16 year; //年
  20. uint8 mon; //月
  21. uint8 day; //日
  22. uint8 hour; //时
  23. uint8 min; //分
  24. uint8 sec; //秒
  25. uint8 week; //星期
  26. };
  27. #ifndef _DS1302_C
  28. #endif
  29. void InitDS1302();
  30. void GetRealTime(struct sTime *time);
  31. void SetRealTime(struct sTime *time);
  32. #endif /* _DS1302_H */
  1. // 温度传感器DS18B20驱动模块的头文件
  2. #ifndef _DS18B20_H
  3. #define _DS18B20_H
  4. #ifndef _DS18B20_C
  5. #endif
  6. bit Start18B20();
  7. bit Get18B20Temp(int16 *temp);
  8. #endif /* _DS18B20_H */

  1. // 多功能电子钟主要功能文件的头文件Time.h:
  2. #ifndef _TIME_H
  3. #define _TIME_H
  4. #ifndef _TIME_C
  5. #endif
  6. void RefreshTime();
  7. void RefreshDate(uint8 ops);
  8. void RefreshAlarm();
  9. void AlarmMonitor();
  10. void KeyAction(uint8 keycode);
  11. #endif /* _TIME_H */

  1. // 4*4矩阵按键驱动模块的头文件keyboard.h:
  2. #ifndef _KEY_BOARD_H
  3. #define _KEY_BOARD_H
  4. #ifndef _KEY_BOARD_C
  5. #endif
  6. void KeyScan();
  7. void KeyDriver();
  8. #endif /* _KEY_BOARD_H */<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>

  1. // 点阵LED、数码管、独立LED和无源蜂鸣器的驱动模块头文件:LedBuzzer.h:
  2. #ifndef _LED_BUZZER_H
  3. #define _LED_BUZZER_H
  4. struct sLedBuff //LED显示缓冲区结构
  5. {
  6. uint8 array[8]; //点阵缓冲区
  7. uint8 number[6]; //数码管缓冲区
  8. uint8 alone; //独立LED缓冲区
  9. };
  10. #ifndef _LED_BUZZER_C
  11. extern bit staBuzzer;
  12. extern struct sLedBuff ledBuff;
  13. #endif
  14. void InitLed();
  15. void FlowingLight();
  16. void ShowLedNumber(uint8 index, uint8num, uint8 point);
  17. void ShowLedArray(uint8 *ptr);
  18. #endif /* _LED_BUZZER_H */<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>

  1. // 主文件的头文件main.h:
  2. #ifndef _MAIN_H
  3. #define _MAIN_H
  4. enum eStaSystem //系统运行状态枚举
  5. {
  6. E_NORMAL, E_SET_TIME, E_SET_ALARM
  7. };
  8. #ifndef _MAIN_C
  9. extern enum eStaSystem staSystem;
  10. #endif
  11. void RefreshTemp(uint8 ops);
  12. void ConfigTimer0(uint16 ms);
  13. #endif /* _MAIN_H */

//=============================================================================

  1. // Lcd1602.c:
  2. #define _LCD1602_C
  3. #include "config.h"
  4. #include "Lcd1602.h"
  5. uint8 tmpP0; //暂存P0口的值
  6. bit tmpADDR0; //暂存LED位选译码地址0的值
  7. bit tmpADDR1; //暂存LED位选译码地址1的值
  8. /* 暂停LED动态扫描,暂存相关引脚的值 */
  9. void LedScanPause()
  10. {
  11. ENLED = 1;
  12. tmpP0 = P0;
  13. tmpADDR0 = ADDR0;
  14. tmpADDR1 = ADDR1;
  15. }
  16. /* 恢复LED动态扫描,恢复相关引脚的值 */
  17. void LedScanContinue()
  18. {
  19. ADDR0 = tmpADDR0;
  20. ADDR1 = tmpADDR1;
  21. P0 = tmpP0;
  22. ENLED = 0;
  23. }
  24. /* 等待液晶准备好 */
  25. void LcdWaitReady()
  26. {
  27. uint8 sta;
  28. LCD1602_DB = 0xFF;
  29. LCD1602_RS = 0;
  30. LCD1602_RW = 1;
  31. do
  32. {
  33. LCD1602_E = 1;
  34. sta = LCD1602_DB; //读取状态字
  35. LCD1602_E = 0;
  36. }
  37. while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止
  38. }
  39. /* 向LCD1602液晶写入一字节命令,cmd-待写入命令值 */
  40. void LcdWriteCmd(uint8 cmd)
  41. {
  42. LedScanPause();
  43. LcdWaitReady();
  44. LCD1602_RS = 0;
  45. LCD1602_RW = 0;
  46. LCD1602_DB = cmd;
  47. LCD1602_E = 1;
  48. LCD1602_E = 0;
  49. LedScanContinue();
  50. }
  51. /* 向LCD1602液晶写入一字节数据,dat-待写入数据值 */
  52. void LcdWriteDat(uint8 dat)
  53. {
  54. LedScanPause();
  55. LcdWaitReady();
  56. LCD1602_RS = 1;
  57. LCD1602_RW = 0;
  58. LCD1602_DB = dat;
  59. LCD1602_E = 1;
  60. LCD1602_E = 0;
  61. LedScanContinue();
  62. }
  63. /* 清屏 */
  64. void LcdClearScreen()
  65. {
  66. LcdWriteCmd(0x01);
  67. }
  68. /* 打开光标的闪烁效果 */
  69. void LcdOpenCursor()
  70. {
  71. LcdWriteCmd(0x0F);
  72. }
  73. /* 关闭光标显示 */
  74. void LcdCloseCursor()
  75. {
  76. LcdWriteCmd(0x0C);
  77. }
  78. /* 设置显示RAM起始地址,亦即光标位置,(x,y)-对应屏幕上的字符坐标 */
  79. void LcdSetCursor(uint8 x, uint8 y)
  80. {
  81. uint8 addr;
  82. if (y == 0) //由输入的屏幕坐标计算显示RAM的地址
  83. {
  84. addr = 0x00 + x; //第一行字符地址从0x00起始
  85. }
  86. else
  87. {
  88. addr = 0x40 + x; //第二行字符地址从0x40起始
  89. }
  90. LcdWriteCmd(addr | 0x80); //设置RAM地址
  91. }
  92. /* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,str-字符串指针 */
  93. void LcdShowStr(uint8 x, uint8 y, uint8*str)
  94. {
  95. LcdSetCursor(x, y); //设置起始地址
  96. while (*str != '\0') //连续写入字符串数据,直到检测到结束符
  97. {
  98. LcdWriteDat(*str++);
  99. }
  100. }
  101. /* 在液晶上显示一个字符,(x,y)-对应屏幕上的起始坐标,chr-字符ASCII码 */
  102. void LcdShowChar(uint8 x, uint8 y, uint8chr)
  103. {
  104. LcdSetCursor(x, y); //设置起始地址
  105. LcdWriteDat(chr); //写入ASCII字符
  106. }
  107. /* 初始化1602液晶 */
  108. void InitLcd1602()
  109. {
  110. LcdWriteCmd(0x38); //16*2显示,5*7点阵,8位数据接口
  111. LcdWriteCmd(0x0C); //显示器开,光标关闭
  112. LcdWriteCmd(0x06); //文字不动,地址自动+1
  113. LcdWriteCmd(0x01); //清屏
  114. }<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>

  1. // 实时时钟芯片DS1302驱动模块DS1302.c:
  2. #define _DS1302_C
  3. #include "config.h"
  4. #include "DS1302.h"
  5. /* 发送一个字节到DS1302通信总线上 */
  6. void DS1302ByteWrite(uint8 dat)
  7. {
  8. uint8 mask;
  9. for (mask = 0x01; mask != 0; mask <<= 1) //低位在前,逐位移出
  10. {
  11. if ((mask & dat) != 0) //首先输出该位数据
  12. {
  13. DS1302_IO = 1;
  14. }
  15. else
  16. {
  17. DS1302_IO = 0;
  18. }
  19. DS1302_CK = 1; //然后拉高时钟
  20. DS1302_CK = 0; //再拉低时钟,完成一个位的操作
  21. }
  22. DS1302_IO = 1; //最后确保释放IO引脚
  23. }
  24. /* 由DS1302通信总线上读取一个字节 */
  25. uint8 DS1302ByteRead()
  26. {
  27. uint8 mask;
  28. uint8 dat = 0;
  29. for (mask = 0x01; mask != 0; mask <<= 1) //低位在前,逐位读取
  30. {
  31. if (DS1302_IO != 0) //首先读取此时的IO引脚,并设置dat中的对应位
  32. {
  33. dat |= mask;
  34. }
  35. DS1302_CK = 1; //然后拉高时钟
  36. DS1302_CK = 0; //再拉低时钟,完成一个位的操作
  37. }
  38. return dat; //最后返回读到的字节数据
  39. }
  40. /* 用单次写操作向某一寄存器写入一个字节,reg-寄存器地址,dat-待写入字节 */
  41. void DS1302SingleWrite(uint8 reg, uint8dat)
  42. {
  43. DS1302_CE = 1; //使能片选信号
  44. DS1302ByteWrite((reg << 1) | 0x80); //发送写寄存器指令
  45. DS1302ByteWrite(dat); //写入字节数据
  46. DS1302_CE = 0; //除能片选信号
  47. }
  48. /* 用单次读操作从某一寄存器读取一个字节,reg-寄存器地址,返回值-读到的字节 */
  49. uint8 DS1302SingleRead(uint8 reg)
  50. {
  51. uint8 dat;
  52. DS1302_CE = 1; //使能片选信号
  53. DS1302ByteWrite((reg << 1) | 0x81); //发送读寄存器指令
  54. dat = DS1302ByteRead(); //读取字节数据
  55. DS1302_CE = 0; //除能片选信号
  56. return dat;
  57. }
  58. /* 用突发模式连续写入8个寄存器数据,dat-待写入数据指针 */
  59. void DS1302BurstWrite(uint8 *dat)
  60. {
  61. uint8 i;
  62. DS1302_CE = 1;
  63. DS1302ByteWrite(0xBE); //发送突发写寄存器指令
  64. for (i = 0; i < 8; i++) //连续写入8字节数据
  65. {
  66. DS1302ByteWrite(dat[i]);
  67. }
  68. DS1302_CE = 0;
  69. }
  70. /* 用突发模式连续读取8个寄存器的数据,dat-读取数据的接收指针 */
  71. void DS1302BurstRead(uint8 *dat)
  72. {
  73. uint8 i;
  74. DS1302_CE = 1;
  75. DS1302ByteWrite(0xBF); //发送突发读寄存器指令
  76. for (i = 0; i < 8; i++) //连续读取8个字节
  77. {
  78. dat[i] = DS1302ByteRead();
  79. }
  80. DS1302_CE = 0;
  81. }
  82. /* 获取实时时间,即读取DS1302当前时间并转换为时间结构体格式 */
  83. void GetRealTime(struct sTime *time)
  84. {
  85. uint8 buf[8];
  86. DS1302BurstRead(buf);
  87. time->year = buf[6] + 0x2000;
  88. time->mon = buf[4];
  89. time->day = buf[3];
  90. time->hour = buf[2];
  91. time->min = buf[1];
  92. time->sec = buf[0];
  93. time->week = buf[5];
  94. }
  95. /* 设定实时时间,时间结构体格式的设定时间转换为数组并写入DS1302 */
  96. void SetRealTime(struct sTime *time)
  97. {
  98. uint8 buf[8];
  99. buf[7] = 0;
  100. buf[6] = time->year;
  101. buf[5] = time->week;
  102. buf[4] = time->mon;
  103. buf[3] = time->day;
  104. buf[2] = time->hour;
  105. buf[1] = time->min;
  106. buf[0] = time->sec;
  107. DS1302BurstWrite(buf);
  108. }
  109. /* DS1302初始化,如发生掉电则重新设置初始时间 */
  110. void InitDS1302()
  111. {
  112. uint8 dat;
  113. struct sTime code InitTime[] = //默认初始值:2014-01-0112:30:00 星期3
  114. {
  115. 0x2014, 0x01, 0x01, 0x12, 0x30, 0x00, 0x03
  116. };
  117. DS1302_CE = 0; //初始化DS1302通信引脚
  118. DS1302_CK = 0;
  119. dat = DS1302SingleRead(0); //读取秒寄存器
  120. if ((dat & 0x80) != 0) //由秒寄存器最高位CH的值判断DS1302是否已停止
  121. {
  122. DS1302SingleWrite(7, 0x00); //撤销写保护以允许写入数据
  123. SetRealTime(&InitTime); //设置DS1302为默认的初始时间
  124. }
  125. }<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>

  1. // 温度传感器DS18B20驱动模块DS18B20.c:DS18B20.cs
  2. #define _DS18B20_C
  3. #include "config.h"
  4. #include "DS18B20.h"
  5. /* 软件延时函数,延时时间(t*10)us */
  6. void DelayX10us(uint8 t)
  7. {
  8. do
  9. {
  10. _nop_();
  11. _nop_();
  12. _nop_();
  13. _nop_();
  14. _nop_();
  15. _nop_();
  16. _nop_();
  17. _nop_();
  18. }
  19. while (--t);
  20. }
  21. /* 复位总线,获取存在脉冲,以启动一次读写操作 */
  22. bit Get18B20Ack()
  23. {
  24. bit ack;
  25. EA = 0; //禁止总中断
  26. IO_18B20 = 0; //产生500us复位脉冲
  27. DelayX10us(50);
  28. IO_18B20 = 1;
  29. DelayX10us(6); //延时60us
  30. ack = IO_18B20; //读取存在脉冲
  31. while(!IO_18B20); //等待存在脉冲结束
  32. EA = 1; //重新使能总中断
  33. return ack;
  34. }
  35. /* 向DS18B20写入一个字节,dat-待写入字节 */
  36. void Write18B20(uint8 dat)
  37. {
  38. uint8 mask;
  39. EA = 0; //禁止总中断
  40. for (mask = 0x01; mask != 0; mask <<= 1) //低位在先,依次移出8个bit
  41. {
  42. IO_18B20 = 0; //产生2us低电平脉冲
  43. _nop_();
  44. _nop_();
  45. if ((mask & dat) == 0) //输出该bit值
  46. {
  47. IO_18B20 = 0;
  48. }
  49. else
  50. {
  51. IO_18B20 = 1;
  52. }
  53. DelayX10us(6); //延时60us
  54. IO_18B20 = 1; //拉高通信引脚
  55. }
  56. EA = 1; //重新使能总中断
  57. }
  58. /* 从DS18B20读取一个字节,返回值-读到的字节 */
  59. uint8 Read18B20()
  60. {
  61. uint8 dat;
  62. uint8 mask;
  63. EA = 0; //禁止总中断
  64. for (mask = 0x01; mask != 0; mask <<= 1) //低位在先,依次采集8个bit
  65. {
  66. IO_18B20 = 0; //产生2us低电平脉冲
  67. _nop_();
  68. _nop_();
  69. IO_18B20 = 1; //结束低电平脉冲,等待18B20输出数据
  70. _nop_(); //延时2us
  71. _nop_();
  72. if (!IO_18B20) //读取通信引脚上的值
  73. {
  74. dat &= ~mask;
  75. }
  76. else
  77. {
  78. dat |= mask;
  79. }
  80. DelayX10us(6); //再延时60us
  81. }
  82. EA = 1; //重新使能总中断
  83. return dat;
  84. }
  85. /* 启动一次18B20温度转换,返回值-表示是否启动成功 */
  86. bit Start18B20()
  87. {
  88. bit ack;
  89. ack = Get18B20Ack(); //执行总线复位,并获取18B20应答
  90. if (ack == 0) //如18B20正确应答,则启动一次转换
  91. {
  92. Write18B20(0xCC); //跳过ROM操作
  93. Write18B20(0x44); //启动一次温度转换
  94. }
  95. return ~ack; //ack==0表示操作成功,所以返回值对其取反
  96. }
  97. /* 读取DS18B20转换的温度值,返回值-表示是否读取成功 */
  98. bit Get18B20Temp(int16 *temp)
  99. {
  100. bit ack;
  101. uint8 LSB, MSB; //16bit温度值的低字节和高字节
  102. ack = Get18B20Ack(); //执行总线复位,并获取18B20应答
  103. if (ack == 0) //如18B20正确应答,则读取温度值
  104. {
  105. Write18B20(0xCC); //跳过ROM操作
  106. Write18B20(0xBE); //发送读命令
  107. LSB = Read18B20(); //读温度值的低字节
  108. MSB = Read18B20(); //读温度值的高字节
  109. *temp = ((int16)MSB << 8) + LSB; //合成为16bit整型数
  110. }
  111. return~ack; //ack==0表示操作应答,所以返回值为其取反值
  112. }

  1. // 多功能电子钟主要功能文件Time.c:
  2. #define _TIME_C
  3. #include "config.h"
  4. #include "DS1302.h"
  5. #include "LedBuzzer.h"
  6. #include "Lcd1602.h"
  7. #include "Time.h"
  8. #include "main.h"
  9. uint8 code WeekMod[] = //星期X字符图片表
  10. {
  11. 0xFF, 0x99, 0x00, 0x00, 0x00, 0x81, 0xC3, 0xE7, //星期日(红心)
  12. 0xEF, 0xE7, 0xE3, 0xE7, 0xE7, 0xE7, 0xE7, 0xC3, //星期1
  13. 0xC3, 0x81, 0x9D, 0x87, 0xC3, 0xF9, 0xC1, 0x81, //星期2
  14. 0xC3, 0x81, 0x9D, 0xC7, 0xC7, 0x9D, 0x81, 0xC3, //星期3
  15. 0xCF, 0xC7, 0xC3, 0xC9, 0xC9, 0x81, 0xCF, 0xCF, //星期4
  16. 0x81, 0xC1, 0xF9, 0xC3, 0x87, 0x9D, 0x81, 0xC3, //星期5
  17. 0xC3, 0x81, 0xF9, 0xC3, 0x81, 0x99, 0x81, 0xC3, //星期6
  18. };
  19. bit staMute = 0; //静音标志位
  20. uint8 AlarmHour = 0x07; //闹钟时间的小时数
  21. uint8 AlarmMin = 0x30; //闹钟时间的分钟数
  22. struct sTime CurTime; //当前日期时间
  23. uint8 SetIndex = 0; //设置位索引
  24. uint8 pdata SetAlarmHour; //闹钟小时数设置缓冲
  25. uint8 pdata SetAlarmMin; //闹钟分钟数设置缓冲
  26. struct sTime pdata SetTime; //日期时间设置缓冲区
  27. /* 获取当前日期时间,并刷新时间和星期的显示 */
  28. void RefreshTime()
  29. {
  30. GetRealTime(&CurTime); //获取当前日期时间
  31. ShowLedNumber(5, CurTime.hour >> 4, 0); //时
  32. ShowLedNumber(4, CurTime.hour & 0xF, 1);
  33. ShowLedNumber(3, CurTime.min >> 4, 0); //分
  34. ShowLedNumber(2, CurTime.min & 0xF, 1);
  35. ShowLedNumber(1, CurTime.sec >> 4, 0); //秒
  36. ShowLedNumber(0, CurTime.sec & 0xF, 0);
  37. ShowLedArray(WeekMod + CurTime.week * 8); //星期
  38. }
  39. /* 日期刷新函数,ops-刷新选项:为0时只当日期变化才刷新,非0则立即刷新 */
  40. void RefreshDate(uint8 ops)
  41. {
  42. uint8 pdata str[12];
  43. static uint8 backup = 0;
  44. if ((backup != CurTime.day) || (ops != 0))
  45. {
  46. str[0] = ((CurTime.year >> 12) & 0xF) + '0'; //4位数年份
  47. str[1] = ((CurTime.year >> 8) & 0xF) + '0';
  48. str[2] = ((CurTime.year >> 4) & 0xF) + '0';
  49. str[3] = (CurTime.year & 0xF) + '0';
  50. str[4] = '-'; //分隔符
  51. str[5] = (CurTime.mon >> 4) + '0'; //月份
  52. str[6] = (CurTime.mon & 0xF) + '0';
  53. str[7] = '-'; //分隔符
  54. str[8] = (CurTime.day >> 4) + '0'; //日期
  55. str[9] = (CurTime.day & 0xF) + '0';
  56. str[10] = '\0'; //字符串结束符
  57. LcdShowStr(0, 0, str); //显示到液晶上
  58. backup = CurTime.day; //刷新上次日期值
  59. }
  60. }
  61. /* 刷新闹钟时间的显示 */
  62. void RefreshAlarm()
  63. {
  64. uint8 pdata str[8];
  65. LcdShowStr(0, 1, "Alarm at "); //显示提示标题
  66. str[0] = (AlarmHour >> 4) + '0'; //闹钟小时数
  67. str[1] = (AlarmHour & 0xF) + '0';
  68. str[2] = ':'; //分隔符
  69. str[3] = (AlarmMin >> 4) + '0'; //闹钟分钟数
  70. str[4] = (AlarmMin & 0xF) + '0';
  71. str[5] = '\0'; //字符串结束符
  72. LcdShowStr(9, 1, str); //显示到液晶上
  73. }
  74. /* 闹钟监控函数,抵达设定的闹钟时间时执行闹铃 */
  75. void AlarmMonitor()
  76. {
  77. if ((CurTime.hour == AlarmHour) && (CurTime.min == AlarmMin)) //检查时间匹配
  78. {
  79. if (!staMute) //检查是否静音
  80. {
  81. staBuzzer = ~staBuzzer; //实现蜂鸣器断续鸣叫
  82. }
  83. else
  84. {
  85. staBuzzer = 0;
  86. }
  87. }
  88. else
  89. {
  90. staMute = 0;
  91. staBuzzer = 0;
  92. }
  93. }
  94. /* 将设置时间及标题提示显示到液晶上 */
  95. void ShowSetTime()
  96. {
  97. uint8 pdata str[18];
  98. str[0] = ((SetTime.year >> 4) & 0xF) + '0'; //2位数年份
  99. str[1] = (SetTime.year & 0xF) + '0';
  100. str[2] = '-';
  101. str[3] = (SetTime.mon >> 4) + '0'; //月份
  102. str[4] = (SetTime.mon & 0xF) + '0';
  103. str[5] = '-';
  104. str[6] = (SetTime.day >> 4) + '0'; //日期
  105. str[7] = (SetTime.day & 0xF) + '0';
  106. str[8] = '-';
  107. str[9] = (SetTime.week & 0xF) + '0'; //星期
  108. str[10] = ' ';
  109. str[11] = (SetTime.hour >> 4) + '0'; //小时
  110. str[12] = (SetTime.hour & 0xF) + '0';
  111. str[13] = ':';
  112. str[14] = (SetTime.min >> 4) + '0'; //分钟
  113. str[15] = (SetTime.min & 0xF) + '0';
  114. str[16] = '\0';
  115. LcdShowStr(0, 0, "Set Date Time"); //显示提示标题
  116. LcdShowStr(0, 1, str); //显示设置时间值
  117. }
  118. /* 将设置闹钟及标题提示显示到液晶上 */
  119. void ShowSetAlarm()
  120. {
  121. uint8 pdata str[8];
  122. str[0] = (SetAlarmHour >> 4) + '0'; //小时
  123. str[1] = (SetAlarmHour & 0xF) + '0';
  124. str[2] = ':';
  125. str[3] = (SetAlarmMin >> 4) + '0'; //分钟
  126. str[4] = (SetAlarmMin & 0xF) + '0';
  127. str[5] = '\0';
  128. LcdShowStr(0, 0, "Set Alarm"); //显示提示标题
  129. LcdShowStr(0, 1, str); //显示设定闹钟值
  130. }
  131. /* 取消当前设置,返回正常运行状态 */
  132. void CancelCurSet()
  133. {
  134. staSystem = E_NORMAL;
  135. LcdCloseCursor(); //关闭光标
  136. LcdClearScreen(); //液晶清屏
  137. RefreshTime(); //刷新当前时间
  138. RefreshDate(1); //立即刷新日期显示
  139. RefreshTemp(1); //立即刷新温度显示
  140. RefreshAlarm(); //闹钟设定值显示
  141. }
  142. /* 时间或闹钟设置时,设置位右移一位,到头后折回 */
  143. void SetRightShift()
  144. {
  145. if (staSystem == E_SET_TIME)
  146. {
  147. switch (SetIndex)
  148. {
  149. case 0:
  150. SetIndex = 1;
  151. LcdSetCursor(1, 1);
  152. break;
  153. case 1:
  154. SetIndex = 2;
  155. LcdSetCursor(3, 1);
  156. break;
  157. case 2:
  158. SetIndex = 3;
  159. LcdSetCursor(4, 1);
  160. break;
  161. case 3:
  162. SetIndex = 4;
  163. LcdSetCursor(6, 1);
  164. break;
  165. case 4:
  166. SetIndex = 5;
  167. LcdSetCursor(7, 1);
  168. break;
  169. case 5:
  170. SetIndex = 6;
  171. LcdSetCursor(9, 1);
  172. break;
  173. case 6:
  174. SetIndex = 7;
  175. LcdSetCursor(11, 1);
  176. break;
  177. case 7:
  178. SetIndex = 8;
  179. LcdSetCursor(12, 1);
  180. break;
  181. case 8:
  182. SetIndex = 9;
  183. LcdSetCursor(14, 1);
  184. break;
  185. case 9:
  186. SetIndex = 10;
  187. LcdSetCursor(15, 1);
  188. break;
  189. default:
  190. SetIndex = 0;
  191. LcdSetCursor(0, 1);
  192. break;
  193. }
  194. }
  195. else if (staSystem == E_SET_ALARM)
  196. {
  197. switch (SetIndex)
  198. {
  199. case 0:
  200. SetIndex = 1;
  201. LcdSetCursor(1, 1);
  202. break;
  203. case 1:
  204. SetIndex = 2;
  205. LcdSetCursor(3, 1);
  206. break;
  207. case 2:
  208. SetIndex = 3;
  209. LcdSetCursor(4, 1);
  210. break;
  211. default:
  212. SetIndex = 0;
  213. LcdSetCursor(0, 1);
  214. break;
  215. }
  216. }
  217. }
  218. /* 时间或闹钟设置时,设置位左移一位,到头后折回 */
  219. void SetLeftShift()
  220. {
  221. if (staSystem == E_SET_TIME)
  222. {
  223. switch (SetIndex)
  224. {
  225. case 0:
  226. SetIndex = 10;
  227. LcdSetCursor(15, 1);
  228. break;
  229. case 1:
  230. SetIndex = 0;
  231. LcdSetCursor(0, 1);
  232. break;
  233. case 2:
  234. SetIndex = 1;
  235. LcdSetCursor(1, 1);
  236. break;
  237. case 3:
  238. SetIndex = 2;
  239. LcdSetCursor(3, 1);
  240. break;
  241. case 4:
  242. SetIndex = 3;
  243. LcdSetCursor(4, 1);
  244. break;
  245. case 5:
  246. SetIndex = 4;
  247. LcdSetCursor(6, 1);
  248. break;
  249. case 6:
  250. SetIndex = 5;
  251. LcdSetCursor(7, 1);
  252. break;
  253. case 7:
  254. SetIndex = 6;
  255. LcdSetCursor(9, 1);
  256. break;
  257. case 8:
  258. SetIndex = 7;
  259. LcdSetCursor(11, 1);
  260. break;
  261. case 9:
  262. SetIndex = 8;
  263. LcdSetCursor(12, 1);
  264. break;
  265. default:
  266. SetIndex = 9;
  267. LcdSetCursor(14, 1);
  268. break;
  269. }
  270. }
  271. else if (staSystem == E_SET_ALARM)
  272. {
  273. switch (SetIndex)
  274. {
  275. case 0:
  276. SetIndex = 3;
  277. LcdSetCursor(4, 1);
  278. break;
  279. case 1:
  280. SetIndex = 0;
  281. LcdSetCursor(0, 1);
  282. break;
  283. case 2:
  284. SetIndex = 1;
  285. LcdSetCursor(1, 1);
  286. break;
  287. default:
  288. SetIndex = 2;
  289. LcdSetCursor(3, 1);
  290. break;
  291. }
  292. }
  293. }
  294. /* 输入设置数字,修改对应的设置位,并显示该数字,ascii-输入数字的ASCII码 */
  295. void InputSetNumber(uint8 ascii)
  296. {
  297. uint8 num;
  298. num = ascii - '0';
  299. if (num <= 9) //只响应0~9的数字
  300. {
  301. if (staSystem == E_SET_TIME)
  302. {
  303. switch (SetIndex)
  304. {
  305. case 0:
  306. SetTime.year = (SetTime.year & 0xFF0F) | (num << 4);
  307. LcdShowChar(0, 1, ascii);
  308. break; //年份高位数字
  309. case 1:
  310. SetTime.year = (SetTime.year & 0xFFF0) | (num);
  311. LcdShowChar(1, 1, ascii);
  312. break; //年份低位数字
  313. case 2:
  314. SetTime.mon = (SetTime.mon & 0x0F) | (num << 4);
  315. LcdShowChar(3, 1, ascii);
  316. break; //月份高位数字
  317. case 3:
  318. SetTime.mon = (SetTime.mon & 0xF0) | (num);
  319. LcdShowChar(4, 1, ascii);
  320. break; //月份低位数字
  321. case 4:
  322. SetTime.day = (SetTime.day & 0x0F) | (num << 4);
  323. LcdShowChar(6, 1, ascii);
  324. break; //日期高位数字
  325. case 5:
  326. SetTime.day = (SetTime.day & 0xF0) | (num);
  327. LcdShowChar(7, 1, ascii);
  328. break; //日期低位数字
  329. case 6:
  330. SetTime.week = (SetTime.week & 0xF0) | (num);
  331. LcdShowChar(9, 1, ascii);
  332. break; //星期数字
  333. case 7:
  334. SetTime.hour = (SetTime.hour & 0x0F) | (num << 4);
  335. LcdShowChar(11, 1, ascii);
  336. break; //小时高位数字
  337. case 8:
  338. SetTime.hour = (SetTime.hour & 0xF0) | (num);
  339. LcdShowChar(12, 1, ascii);
  340. break; //小时低位数字
  341. case 9:
  342. SetTime.min = (SetTime.min & 0x0F) | (num << 4);
  343. LcdShowChar(14, 1, ascii);
  344. break; //分钟高位数字
  345. default:
  346. SetTime.min = (SetTime.min & 0xF0) | (num);
  347. LcdShowChar(15, 1, ascii);
  348. break; //分钟低位数字
  349. }
  350. SetRightShift(); //完成该位设置后自动右移
  351. }
  352. else if (staSystem == E_SET_ALARM)
  353. {
  354. switch (SetIndex)
  355. {
  356. case 0:
  357. SetAlarmHour = (SetAlarmHour & 0x0F) | (num << 4);
  358. LcdShowChar(0, 1, ascii);
  359. break; //小时高位数字
  360. case 1:
  361. SetAlarmHour = (SetAlarmHour & 0xF0) | (num);
  362. LcdShowChar(1, 1, ascii);
  363. break; //小时低位数字
  364. case 2:
  365. SetAlarmMin = (SetAlarmMin & 0x0F) | (num << 4);
  366. LcdShowChar(3, 1, ascii);
  367. break; //分钟高位数字
  368. default:
  369. SetAlarmMin = (SetAlarmMin & 0xF0) | (num);
  370. LcdShowChar(4, 1, ascii);
  371. break; //分钟低位数字
  372. }
  373. SetRightShift(); //完成该位设置后自动右移
  374. }
  375. }
  376. }
  377. /* 切换系统运行状态 */
  378. void SwitchSystemSta()
  379. {
  380. if (staSystem == E_NORMAL) //正常运行切换到时间设置
  381. {
  382. staSystem = E_SET_TIME;
  383. SetTime.year = CurTime.year; //当前时间拷贝到时间设置缓冲区中
  384. SetTime.mon = CurTime.mon;
  385. SetTime.day = CurTime.day;
  386. SetTime.hour = CurTime.hour;
  387. SetTime.min = CurTime.min;
  388. SetTime.sec = CurTime.sec;
  389. SetTime.week = CurTime.week;
  390. LcdClearScreen(); //液晶清屏
  391. ShowSetTime(); //显示设置时间
  392. SetIndex = 255; //与接下来的右移一起将光标设在最左边的位置上
  393. SetRightShift();
  394. LcdOpenCursor(); //开启光标
  395. }
  396. else if (staSystem == E_SET_TIME) //时间设置切换到闹钟设置
  397. {
  398. staSystem = E_SET_ALARM;
  399. SetTime.sec = 0; //秒清零,即当设置时间后从0秒开始走时
  400. SetRealTime(&SetTime); //设定时间写入实时时钟
  401. SetAlarmHour = AlarmHour; //当前闹钟值拷贝到设置缓冲区
  402. SetAlarmMin = AlarmMin;
  403. LcdClearScreen(); //液晶清屏
  404. ShowSetAlarm(); //显示设置闹钟
  405. SetIndex = 255; //与接下来的右移一起将光标设在最左边的位置上
  406. SetRightShift();
  407. }
  408. else //闹钟设置切换会正常运行
  409. {
  410. staSystem = E_NORMAL;
  411. AlarmHour = SetAlarmHour; //设定的闹钟值写入闹钟时间
  412. AlarmMin = SetAlarmMin;
  413. LcdCloseCursor(); //关闭光标
  414. LcdClearScreen(); //液晶清屏
  415. RefreshTime(); //刷新当前时间
  416. RefreshDate(1); //立即刷新日期显示
  417. RefreshTemp(1); //立即刷新温度显示
  418. RefreshAlarm(); //闹钟设定值显示
  419. }
  420. }
  421. /* 按键动作函数,根据键码执行相应的操作,keycode-按键键码 */
  422. void KeyAction(uint8 keycode)
  423. {
  424. if ((keycode >= '0') && (keycode <= '9')) //数字键输入当前位设定值
  425. {
  426. InputSetNumber(keycode);
  427. }
  428. else if (keycode == 0x25) //向左键,向左切换设置位
  429. {
  430. SetLeftShift();
  431. }
  432. else if (keycode == 0x27) //向右键,向右切换设置位
  433. {
  434. SetRightShift();
  435. }
  436. else if (keycode == 0x0D) //回车键,切换运行状态/保存设置
  437. {
  438. SwitchSystemSta();
  439. }
  440. else if (keycode == 0x1B) //Esc键,静音/取消当前设置
  441. {
  442. if (staSystem == E_NORMAL) //处于正常运行状态时闹铃静音
  443. {
  444. staMute = 1;
  445. }
  446. else //处于设置状态时退出设置
  447. {
  448. CancelCurSet();
  449. }
  450. }
  451. }
  1. // 4*4矩阵按键驱动模块keyboard.c:
  2. #define _KEY_BOARD_C
  3. #include "config.h"
  4. #include "keyboard.h"
  5. #include "Time.h"
  6. const uint8 code KeyCodeMap[4][4] = //矩阵按键到标准键码的映射表
  7. {
  8. { '1', '2', '3', 0x26 }, //数字键1、数字键2、数字键3、向上键
  9. { '4', '5', '6', 0x25 }, //数字键4、数字键5、数字键6、向左键
  10. { '7', '8', '9', 0x28 }, //数字键7、数字键8、数字键9、向下键
  11. { '0', 0x1B, 0x0D, 0x27 } //数字键0、ESC键、 回车键、 向右键
  12. };
  13. uint8 pdata KeySta[4][4] = //全部矩阵按键的当前状态
  14. {
  15. {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
  16. };
  17. /* 按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用 */
  18. void KeyDriver()
  19. {
  20. uint8 i, j;
  21. static uint8 pdata backup[4][4] = //按键值备份,保存前一次的值
  22. {
  23. {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
  24. };
  25. for (i = 0; i < 4; i++) //循环检测4*4的矩阵按键
  26. {
  27. for (j = 0; j < 4; j++)
  28. {
  29. if (backup[i][j] != KeySta[i][j]) //检测按键动作
  30. {
  31. if (backup[i][j] != 0) //按键按下时执行动作
  32. {
  33. KeyAction(KeyCodeMap[i][j]); //调用按键动作函数
  34. }
  35. backup[i][j] = KeySta[i][j]; //刷新前一次的备份值
  36. }
  37. }
  38. }
  39. }
  40. /* 按键扫描函数,需在定时中断中调用,推荐调用间隔1ms */
  41. void KeyScan()
  42. {
  43. uint8 i;
  44. static uint8 keyout = 0; //矩阵按键扫描输出索引
  45. static uint8 keybuf[4][4] = //矩阵按键扫描缓冲区
  46. {
  47. {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF},
  48. {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}
  49. };
  50. //将一行的4个按键值移入缓冲区
  51. keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;
  52. keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;
  53. keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;
  54. keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;
  55. //消抖后更新按键状态
  56. for (i = 0; i < 4; i++) //每行4个按键,所以循环4次
  57. {
  58. if ((keybuf[keyout][i] & 0x0F) == 0x00)
  59. {
  60. //连续4次扫描值为0,即4*4ms内都是按下状态时,可认为按键已稳定的按下
  61. KeySta[keyout][i] = 0;
  62. }
  63. else if ((keybuf[keyout][i] & 0x0F) == 0x0F)
  64. {
  65. //连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起
  66. KeySta[keyout][i] = 1;
  67. }
  68. }
  69. //执行下一次的扫描输出
  70. keyout++; //输出索引递增
  71. keyout &= 0x03; //索引值加到4即归零
  72. switch (keyout) //根据索引值,释放当前输出引脚,拉低下次的输出引脚
  73. {
  74. case 0:
  75. KEY_OUT_4 = 1;
  76. KEY_OUT_1 = 0;
  77. break;
  78. case 1:
  79. KEY_OUT_1 = 1;
  80. KEY_OUT_2 = 0;
  81. break;
  82. case 2:
  83. KEY_OUT_2 = 1;
  84. KEY_OUT_3 = 0;
  85. break;
  86. case 3:
  87. KEY_OUT_3 = 1;
  88. KEY_OUT_4 = 0;
  89. break;
  90. default:
  91. break;
  92. }
  93. }
  1. // 点阵LED、数码管、独立LED和无源蜂鸣器的驱动模块LedBuzzer.c:
  2. #define _LED_BUZZER_C
  3. #include "config.h"
  4. #include "LedBuzzer.h"
  5. uint8 code LedChar[] = //数码管显示字符转换表
  6. {
  7. 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
  8. 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
  9. };
  10. bit staBuzzer = 0; //蜂鸣器状态控制位,1-鸣叫、0-关闭
  11. struct sLedBuff ledBuff; //LED显示缓冲区,默认初值全0,正好达到上电全亮的效果
  12. /* LED初始化函数,初始化IO、配置定时器 */
  13. void InitLed()
  14. {
  15. //初始化IO口
  16. P0 = 0xFF;
  17. ENLED = 0;
  18. //配置T2作为动态扫描定时
  19. T2CON = 0x00; //配置T2工作在16位自动重载定时器模式
  20. RCAP2H = ((65536 - SYS_MCLK / 1500) >> 8); //配置重载值,每秒产生1500次中断,
  21. RCAP2L = (65536 - SYS_MCLK / 1500); //以使刷新率达到100Hz无闪烁的效果
  22. TH2 = RCAP2H; //设置初值等于重载值
  23. TL2 = RCAP2L;
  24. ET2 = 1; //使能T2中断
  25. PT2 = 1; //设置T2中断为高优先级
  26. TR2 = 1; //启动T2
  27. }
  28. /* 流水灯实现函数,间隔调用实现流动效果 */
  29. void FlowingLight()
  30. {
  31. static uint8 i = 0;
  32. const uint8 code tab[] = //流动表
  33. {
  34. 0x7F, 0x3F, 0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF
  35. };
  36. ledBuff.alone = tab[i]; //表中对应值送到独立LED的显示缓冲区
  37. if (i < (sizeof(tab) - 1)) //索引递增循环,遍历整个流动表
  38. {
  39. i++;
  40. }
  41. else
  42. {
  43. i = 0;
  44. }
  45. }
  46. /* 数码管上显示一位数字,index-数码管位索引(从右到左对应0~5),
  47. ** num-待显示的数字,point-代表是否显示此位上的小数点 */
  48. void ShowLedNumber(uint8 index, uint8num, uint8 point)
  49. {
  50. ledBuff.number[index] = LedChar[num]; //输入数字转换为数码管字符0~F
  51. if (point != 0)
  52. {
  53. ledBuff.number[index] &= 0x7F; //point不为0时点亮当前位的小数点
  54. }
  55. }
  56. /* 点阵上显示一帧图片,ptr-待显示图片指针 */
  57. void ShowLedArray(uint8 *ptr)
  58. {
  59. uint8 i;
  60. for (i = 0; i < sizeof(ledBuff.array); i++)
  61. {
  62. ledBuff.array[i] = *ptr++;
  63. }
  64. }
  65. /* T2中断服务函数,LED动态扫描、蜂鸣器控制 */
  66. void InterruptTimer2() interrupt 5
  67. {
  68. static uint8 i = 0; //LED位选索引
  69. TF2 = 0; //清零T2中断标志
  70. //全部LED动态扫描显示
  71. if (ENLED == 0) //LED使能时才进行动态扫描
  72. {
  73. P0 = 0xFF; //关闭所有段选位,显示消隐
  74. P1 = (P1 & 0xF0) | i; //位选索引值赋值到P1口低4位
  75. P0 = *((uint8 data*)&ledBuff + i); //缓冲区中索引位置的数据送到P0口
  76. if (i < (sizeof(ledBuff) - 1)) //索引递增循环,遍历整个缓冲区
  77. {
  78. i++;
  79. }
  80. else
  81. {
  82. i = 0;
  83. }
  84. }
  85. //由蜂鸣器状态位控制蜂鸣器
  86. if (staBuzzer == 1)
  87. {
  88. BUZZER = ~BUZZER; //蜂鸣器鸣叫
  89. }
  90. else
  91. { BUZZER = 1; } //蜂鸣器静音
  92. }

  1. // 多功能电子钟工程主文件main.c:
  2. #define _MAIN_C
  3. #include "config.h"
  4. #include "Lcd1602.h"
  5. #include "LedBuzzer.h"
  6. #include "keyboard.h"
  7. #include "DS1302.h"
  8. #include "DS18B20.h"
  9. #include "Infrared.h"
  10. #include "Time.h"
  11. #include "main.h"
  12. bit flag2s = 0; //2s定时标志位
  13. bit flag200ms = 0; //200ms定时标志
  14. uint8 T0RH = 0; //T0重载值的高字节
  15. uint8 T0RL = 0; //T0重载值的低字节
  16. enum eStaSystem staSystem = E_NORMAL; //系统运行状态
  17. void main()
  18. {
  19. EA = 1; //开总中断
  20. ConfigTimer0(1); //配置T0定时1ms
  21. InitLed(); //初始化LED模块
  22. InitDS1302(); //初始化实时时钟模块
  23. InitLcd1602(); //初始化液晶模块
  24. Start18B20(); //启动首次温度转换
  25. while (!flag2s); //上电后延时2秒
  26. flag2s = 0;
  27. RefreshTime(); //刷新当前时间
  28. RefreshDate(1); //立即刷新日期显示
  29. RefreshTemp(1); //立即刷新温度显示
  30. RefreshAlarm(); //闹钟设定值显示
  31. while (1) //进入主循环
  32. {
  33. KeyDriver(); //执行按键驱动
  34. if (flag200ms) //每隔200ms执行以下分支
  35. {
  36. flag200ms = 0;
  37. FlowingLight(); //流水灯效果实现
  38. RefreshTime(); //刷新当前时间
  39. AlarmMonitor(); //监控闹钟
  40. if (staSystem == E_NORMAL) //正常运行时刷新日期显示
  41. {
  42. RefreshDate(0);
  43. }
  44. }
  45. if (flag2s) //每隔2s执行以下分支
  46. {
  47. flag2s = 0;
  48. if (staSystem == E_NORMAL) //正常运行时刷新温度显示
  49. {
  50. RefreshTemp(0);
  51. }
  52. }
  53. }
  54. }
  55. /* 温度刷新函数,读取当前温度并根据需要刷新液晶显示,
  56. ** ops-刷新选项:为0时只当温度变化才刷新,非0则立即刷新 */
  57. void RefreshTemp(uint8 ops)
  58. {
  59. int16 temp;
  60. uint8 pdata str[8];
  61. static int16 backup = 0;
  62. Get18B20Temp(&temp); //获取当前温度值
  63. Start18B20(); //启动下一次转换
  64. temp >>= 4; //舍弃4bit小数位
  65. if ((backup != temp) || (ops != 0)) //按需要刷新液晶显示
  66. {
  67. str[0] = (temp / 10) + '0'; //十位转为ASCII码
  68. str[1] = (temp % 10) + '0'; //个位转为ASCII码
  69. str[2] = '\''; //用'C代替℃
  70. str[3] = 'C';
  71. str[4] = '\0'; //字符串结束符
  72. LcdShowStr(12, 0, str); //显示到液晶上
  73. backup = temp; //刷新上次温度值
  74. }
  75. }
  76. /* 配置并启动T0,ms-T0定时时间 */
  77. void ConfigTimer0(uint16 ms)
  78. {
  79. uint32 tmp;
  80. tmp = (SYS_MCLK * ms) / 1000; //计算所需的计数值
  81. tmp = 65536 - tmp; //计算定时器重载值
  82. tmp = tmp + 33; //补偿中断响应延时造成的误差
  83. T0RH = (uint8)(tmp >> 8); //定时器重载值拆分为高低字节
  84. T0RL = (uint8)tmp;
  85. TMOD &= 0xF0; //清零T0的控制位
  86. TMOD |= 0x01; //配置T0为模式1
  87. TH0 = T0RH; //加载T0重载值
  88. TL0 = T0RL;
  89. ET0 = 1; //使能T0中断
  90. TR0 = 1; //启动T0
  91. }
  92. /* T0中断服务函数,实现系统定时和按键扫描 */
  93. void InterruptTimer0() interrupt 1
  94. {
  95. static uint8 tmr2s = 0;
  96. static uint8 tmr200ms = 0;
  97. TH0 = T0RH; //重新加载重载值
  98. TL0 = T0RL;
  99. tmr200ms++; //定时200ms
  100. if (tmr200ms >= 200)
  101. {
  102. tmr200ms = 0;
  103. flag200ms = 1;
  104. tmr2s++; //定时2s
  105. if (tmr2s >= 10)
  106. {
  107. tmr2s = 0;
  108. flag2s = 1;
  109. }
  110. }
  111. KeyScan(); //执行按键扫描
  112. }


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/408630
推荐阅读
相关标签
  

闽ICP备14008679号