前言:
本次多功能液晶,是在宋雪松编撰的《手把手教你学单片机》第19章的多功能电子时钟基础上改的。为了符合学校的期末考核要求,日期、温度、闹钟等均是显示在液晶上的。这个项目所有代码,我都会放在文章结尾。如有错误,欢迎在评论区指出来。感谢!
设计要求:
1)DS1302保持时间和日期
2)DS18B20采集温度
3) 蜂鸣器提示闹钟
4)可以修改日期、闹钟设置
主要代码展示:
main.c
#define _MAIN_C
#include "config.h"
#include "Lcd1602.h"
#include "LedBuzzer.h"
#include "keyboard.h"
#include "DS1302.h"
#include "DS18B20.h"
#include "Time.h"
#include "main.h"
bit flag2s = 0; //2s 定时标志位
bit flag200ms = 0; //200ms 定时标志
bit staBuzzer = 0; //蜂鸣器状态控制位,1-鸣叫、0-关闭
uint8 T0RH = 0; //T0 重载值的高字节
uint8 T0RL = 0; //T0 重载值的低字节
enum eStaSystem staSystem = E_NORMAL; //系统运行状态
uint8 T2RH = 0;
uint8 T2RL = 0;
void main()
{
EA = 1; //开总中断
ConfigTimer0(1); //配置 T0 定时 1ms
InitDS1302(); //初始化实时时钟模块
InitLcd1602(); //初始化液晶模块
Start18B20(); //启动首次温度转换
while (!flag2s); //上电后延时 2 秒
flag2s = 0;
RefreshDate(1); //立即刷新日期显示
RefreshTemp(1); //立即刷新温度显示
RefreshAlarm();
while (1) //进入主循环
{
KeyDriver(); //执行按键驱动
if (flag200ms) //每隔 200ms 执行以下分支
{
flag200ms = 0;
AlarmMonitor(); //监控闹钟
if (staSystem == E_NORMAL) //正常运行时刷新日期显示
{
RefreshDate(1);
}
}
if (flag2s) //每隔 2s 执行以下分支
{
flag2s = 0;
if (staSystem == E_NORMAL) //正常运行时刷新温度显示
{
RefreshTemp(0);
}
}
}
}
/**************************温度部分代码*******************************************/
/* 整型数转换为字符串,str-字符串指针,dat-待转换数,返回值-字符串长度 */
unsigned char IntToString(unsigned char *str, int dat)
{
signed char i = 0;
unsigned char len = 0;
unsigned char buf[5];
if (dat < 0) //如果为负数,首先取绝对值,并在指针上添加负号
{
dat = -dat;
*str++ = '-';
len++;
}
do { //先转换为低位在前的十进制数组
buf[i++] = dat % 10;
dat /= 10;
} while (dat > 0);
len += i; //i 最后的值就是有效字符的个数
while (i-- > 0) //将数组值转换为 ASCII 码反向拷贝到接收指针上
{
*str++ = buf[i] + '0';
}
*str = '\0'; //添加字符串结束符
return len; //返回字符串长度
}
/* 温度刷新函数,读取当前温度并根据需要刷新液晶显示,
** ops-刷新选项:为 0 时只当温度变化才刷新,非 0 则立即刷新 */
void RefreshTemp(uint8 ops)
{
int16 temp;
int intT,decT;
bit res;
uint8 pdata str[8];
uint8 len;
static int16 backup = 0;
res = Get18B20Temp(&temp); //获取当前温度值
if(ops!=0) //按需要刷新液晶显示
{
if(res)
{
intT = temp >> 4;
decT = temp & 0xF;
len = IntToString(str,intT);
str[len++] = '.';
decT = (decT * 10) / 16;
str[len++] = decT + '0';
while(len<6)
{
str[len++] = ' ';
}
str[len] = '\0';
LcdShowStr(11, 0, str); //显示到液晶上
LcdShowStr(15, 0, "C"); //显示到液晶上
backup = temp; //刷新上次温度值
}
else
{
LcdShowStr(11, 0, "error");
}
}
Start18B20(); //启动下一次转换
}
/* 配置并启动 T0,ms-T0 定时时间 */
void ConfigTimer0(uint16 ms)
{
uint32 tmp;
tmp = (SYS_MCLK*ms)/1000; //计算所需的计数值
tmp = 65536 - tmp; //计算定时器重载值
tmp = tmp + 33; //补偿中断响应延时造成的误差
T0RH = (uint8)(tmp>>8); //定时器重载值拆分为高低字节
T0RL = (uint8)tmp;
TMOD &= 0xF0; //清零 T0 的控制位
TMOD |= 0x01; //配置 T0 为模式 1
TH0 = T0RH; //加载 T0 重载值
TL0 = T0RL;
ET0 = 1; //使能 T0 中断
TR0 = 1; //启动 T0
}
/* T0 中断服务函数,实现系统定时和按键扫描 */
void InterruptTimer0() interrupt 1
{
static uint8 tmr2s = 0;
static uint8 tmr200ms = 0;
TH0 = T0RH; //重新加载重载值
TL0 = T0RL;
tmr200ms++; //定时 200ms
if (tmr200ms >= 200)
{
tmr200ms = 0;
flag200ms = 1;
tmr2s++; //定时 2s
if (tmr2s >= 10)
{
tmr2s = 0;
flag2s = 1;
}
}
KeyScan(); //执行按键扫描
}
void interrupttimer2() interrupt 5
{
TH2 = T2RH;
TL2 = T2RL;
}
这个温度模块有点没弄好,就直接放main.c里面了,如果看不过去,你们可以自己放其他.c文件里面。
不足:
本次设计有两个不足的地方,代码没改好,如果不符合你们的要求,需要你们自己改啦。
第一个就是闹钟,没有加入EEPROM模块保存闹钟设置,即使改变了闹钟设置,但给单片机重新上电后,都会变成6:30。
第二个就是蜂鸣器设置,只要一重新设置了闹钟,那个时钟秒就会从原本的秒变成零,再开始向计数;蜂鸣器声音也超小声,滴滴的,不仔细听都听不见那种。
展示:
链接:https://pan.baidu.com/s/1yvydtKNk3WuDxZ9YWr3fvw
提取码:axhs