实时时钟+红外+温度传感器+LCD1602显示程序

来源:本站
导读:目前正在解读《实时时钟+红外+温度传感器+LCD1602显示程序》的相关信息,《实时时钟+红外+温度传感器+LCD1602显示程序》是由用户自行发布的知识型内容!下面请观看由(电工技术网 - www.9ddd.net)用户发布《实时时钟+红外+温度传感器+LCD1602显示程序》的详细说明。
简介:实时时钟+红外+温度传感器+LCD1602显示程序,此程序是在51hei单片机开发板上面做的,如需要移植到自己的电路上,修改相应的端口即可。

/**

****************************************************************************

* @file main.c

* @author xr

* @date 2014年4月10日22:21:56

* @version V1.2.3

* @brief 在LCD1602上显示时钟,当前温度值,和NEC协议的红外解码值

* @note 单片机STC89C52RC MCU 晶振 11.0592MHZ

****************************************************************************

*/

#include <reg52.h>

#include "mytype.h"

bit fack = 0;//接收到温度数据标志位

bit flag1s = 0;//1s时间标志位

bit flag200ms = 0;//200ms标志

uint8 thr0, tlr0;

uint8 counter = 0, i = 0;

//数码管编码

uint8 code LedTable[] = {

0xC0, //"0"

0xF9, //"1"

0xA4, //"2"

0xB0, //"3"

0x99, //"4"

0x92, //"5"

0x82, //"6"

0xF8, //"7"

0x80, //"8"

0x90, //"9"

0x88, //"A"

0x83, //"B"

0xC6, //"C"

0xA1, //"D"

0x86, //"E"

0x8E //"F"

};

extern void InitLcd1602();

extern void LcdShowStr(uint8 x, uint8 y, uint8 * str);

extern bit StartDs18B20();

extern bit ReadDs18B20Temp(int * temp);

extern void InitDS1302();

extern void DS1302BurstRead(uint8 * time);

extern void InitInfrared();

//外部变量只是声明,不可以重新赋值

extern bit Irflag;//红外遥控器解码数据接收完成标志

extern uint8 Ircode[4];//存放遥控器解码值(用户码, 用户码反码,键码,键码反码)

void ConfigTimer0(uint16 xms);

uint8 IntToString(uint8 * str, int dat);

void main(void)

{

uint8 buf[4];

uint8 len = 0;

int temp;//温度

int intT, decT;//整数部分,小数部分

uint8 str[20];

uint8 time[20];//保存时间(BCD码值)

uint8 psec = 0xFF;//sec最大是59,所以一定能刷新显示

InitLcd1602();

InitDS1302();

ConfigTimer0(10);//定时10ms

StartDs18B20();

InitInfrared();

while (1)

{

if (Irflag) //红外解码

{

Irflag = 0;

buf[0] = Ircode[2] / 10 % 10;//按键码的十位数

buf[1] = Ircode[2] % 10;//按键码的个位数

str[0] = buf[0] + '0';

str[1] = buf[1] + '0';

str[2] = '';

LcdShowStr(14, 1, str);

}

if (flag200ms) //200ms刷新时钟显示

{

flag200ms = 0;

DS1302BurstRead(time);

if (psec != time[0]) //time[0]保存的是sec寄存器的BCD码值

{

//刷新显示

//日期

str[0] = '2';

str[1] = '0';

str[2] = (time[6] >> 4) + '0';//取年十位

str[3] = (time[6] & 0x0F) + '0';//取年个位转字符

str[4] = '-';

str[5] = (time[4] >> 4) + '0'; //月十位

str[6] = (time[4] & 0x0F) + '0'; //月个位

str[7] = '-';

str[8] = (time[3] >> 4) + '0'; //日十位

str[9] = (time[3] & 0x0F) + '0'; //日个位

str[10] = '';

LcdShowStr(0, 0, str);

//时间

str[0] = (time[2] >> 4) + '0';//时十位

str[1] = (time[2] & 0x0F) + '0';//时个位

str[2] = ':';

str[3] = (time[1] >> 4) + '0';

str[4] = (time[1] & 0x0F) + '0';

str[5] = ':';

str[6] = (time[0] >> 4) + '0';

str[7] = (time[0] & 0x0F) + '0';

str[8] = '';

LcdShowStr(0, 1, str);

//星期

str[0] = (time[5] & 0x0F) + '0';//星期只有一位数字

str[1] = '';

LcdShowStr(11, 0, "Week");

LcdShowStr(15, 0, str);

psec = time[0];

}

}

if (flag1s) //1s刷新温度显示

{

flag1s = 0;

fack = ReadDs18B20Temp(&temp);//读取温度

if (fack) //读取成功

{

intT = (temp >> 4);//整数部分,将小数部分移出

decT = (temp & 0x000F);//小数部分

len = IntToString(str, intT);//将intT整数部分转换成字符存入str中,并返回有效字符个数

str[len++] = '.';//小数点

decT = decT * 10 / 16 % 10;//小数部分转换

str[len++] = decT + '0';

str[len] = '';

LcdShowStr(8, 1, "T:");

LcdShowStr(10, 1, str);

}

else

{

LcdShowStr(0, 0, "error!");

}

}

StartDs18B20();//重新启动温度转换

}

}

//定时器T0配置

void ConfigTimer0(uint16 xms)

{

uint16 tmp;

tmp = 65536-xms*11059200/12/1000;

thr0 = (uint8)(tmp >> 8);//取高字节

tlr0 = (uint8)(tmp & 0x00FF);//取低字节

TMOD &= 0xF0;//清零T0控制位

TMOD |= 0x01;//T0方式1

TH0 = thr0;

TL0 = tlr0;//装入定时初值

TR0 = 1;//启动T0定时器

EA = 1;//开总中断

ET0 = 1;//开定时器T0中断

//PT0 = 1;//设置T0中断优先级为最高级

}

//整数转换成str

unsigned char IntToString(unsigned char * str, signed int dat)

{

unsigned char len = 0;//统计有效字符的个数

signed char i = 0;//计数器

unsigned char buff[6];//数据分解缓冲区

if (dat < 0) //负数

{

dat = -dat;//取绝对值

*str++ = '-';//前面加上-

len++;//长度++

}

//分解整数dat到buff中

do

{

buff[i++] = dat % 10;

dat /= 10;

} while (dat > 0);//分解到dat==0为止

len += i;//长度+i,有效字符个数

while (i-- > 0) //拷贝转换后的ASIIC码字符到str接收指针中

{

*str++ = buff[i] + '0';//转换成ASCII字符

}

*str = '';//加上串结束符

return len;//返回有效字符个数

}

//定时器T0中断服务

void timer0_ISP() interrupt 1

{

TH0 = thr0;

TL0 = tlr0;

counter++;

if (counter >= 20)

{

counter = 0;

flag200ms = 1;

i++;

if (i >= 5)

{

i = 0;

flag1s = 1;

}

}

}

/**********自定义头文件************/

#ifndef _MYTYPE_H_H

#define _MYTYPE_H_H

typedef unsigned int uint16;

typedef unsigned char uint8;

typedef unsigned long uint32;

#endif //_MYTYPE_H_H

/***************LCD1602.c*********************/

#include <reg52.h>

#include "mytype.h"

//LCD1602

sbit LCD1602_RS = P1^0;

sbit LCD1602_RW = P1^1;

sbit LCD1602_EN = P1^5;

#define LCD1602_DB P0

//液晶忙碌等待

void WaitLcd1602()

{

uint8 sta;

LCD1602_DB = 0xFF;//拉高P0口

LCD1602_RS = 0;

LCD1602_RW = 1;

do

{

LCD1602_EN = 1;

sta = LCD1602_DB;

LCD1602_EN = 0;//关闭液晶的数据输出

} while (sta & 0x80);

}

//写命令

void WriteLcd1602Cmd(uint8 cmd)

{

WaitLcd1602();

LCD1602_RS = 0;

LCD1602_RW = 0;

LCD1602_DB = cmd;

LCD1602_EN = 1;//高脉冲

LCD1602_EN = 0;

}

//写数据

void WriteLcd1602Data(uint8 dat)

{

WaitLcd1602();

LCD1602_RS = 1;

LCD1602_RW = 0;

LCD1602_DB = dat;

LCD1602_EN = 1;//高脉冲

LCD1602_EN = 0;

}

//液晶初始化

void InitLcd1602()

{

WriteLcd1602Cmd(0x38);//设置16*2显示 5*7点阵 8位数据口

WriteLcd1602Cmd(0x0C);//开显示不显示光标

WriteLcd1602Cmd(0x06);//写入一个字符时字符指针++且地址++

WriteLcd1602Cmd(0x01);//清屏

}

//写str到LCD1602

void LcdShowStr(uint8 x, uint8 y, uint8 * str)

{

uint8 addr;

if (y == 0)

{ //第一行

addr = 0x00 + x;

}

else

{ //第二行

addr = 0x40 + x;

}

WriteLcd1602Cmd(0x80 | addr);

while (*str != '')

{

WriteLcd1602Data(*str++);

}

}

/**

*********************************************************************

* @file infrared.c

* @author xr

* @date 2014年4月11日08:25:04

* @version V1.2.3

* @brief 红外通信-NEC协议红外遥控器解码驱动

* @note 单片机STC89C52RC MCU晶振 11.0592MHZ

*********************************************************************

*/

#include <reg52.h>

#include "mytype.h"

//红外通信接口位声明

sbit IRD = P3^3;

bit Irflag = 0;//红外遥控器解码数据接收完成标志

uint8 Ircode[4];//存放遥控器解码值(用户码, 用户码反码,键码,键码反码)

extern void LcdShowStr(uint8 x, uint8 y, uint8 * str);

/**

* @brief 红外通信配置

* @param 无

* @retval 无

*/

void InitInfrared()

{

//使用定时器T1作为计数,不定时也不进入T1中断

TMOD &= 0x0F;//清零T1控制位

TMOD |= 0x10;//设置T1方式1

ET1 = 0;//禁止T1中断

TR1 = 0;//在外部中断引脚即红外通信引脚没有载波(低电平)和空闲(高电平)时,先关闭T1计数

TH1 = 0;

TL1 = 0;//清零T1计数值

//使能外部中断1

IT1 = 1;//设置INT1中断触发方式为负边沿触发

EX1 = 1;//开启外部中断1

EA = 1;//开启总中断

PX1= 1;

}

/**

* @brief 获取红外通信引脚载波(低电平)计数值

* @param 无

* @retval 载波计数值(即T1计数值)

*/

uint16 GetLowCounter() //计数值*(12/11059200)即是载波时间,计数值=TH1*256+TL1

{

IRD = 1;//在检测IRD引脚电平时要将此引脚拉高释放

TH1 = 0;

TL1 = 0;//清零T1计数值

TR1 = 1;//开启T1计数

while (!IRD) //若IRD=0,T1开始计数

{

//超时判断,因为引导码的载波时间是9ms,所以不能等待太长时间

if (TH1 > 0x40) //超过20ms就强制退出 (TH1*256*(12/11059200)s)

{

break;

}

}

TR1 = 0;//关闭T1计数

return (TH1*256 + TL1);//返回计数值(TL1加满到256向TH1进1)

}

/**

* @brief 获取空闲时间计数值(高电平)

* @param 无

* @retval 空闲计数值

*/

uint16 GetHeighCounter()

{

IRD = 1;//拉高释放引脚,以检测IRD引脚的电平

TH1 = 0;

TL1 = 0;//清零T1计数值

TR1 = 1;//开启计数

while (IRD)

{

if (TH1 > 0x40) //等待超出20ms,则认为是错误,强制退出

{

break;

}

}

TR1 = 0;//关闭T1计数

return (TH1*256 + TL1);//返回T1计数值

}

/**

* @brief 外部中断1服务程序(判断解码)

* @param 无

* @retval 无

*/

void EXINT1_ISP() interrupt 2 //INT1中断标号:2

{

//NEC协议:引导码(9ms载波+4.5ms的空闲) 用户码-用户反码-键码-键码反码-停止位

//比特值0:(560us的载波+560us的空闲) 比特值1:(560us的载波+1.68ms的空闲)

uint8 i, j;

uint8 byte;//接收字节

uint16 time;//获取时间

//首先判断引导码

time = GetLowCounter();//time*12/11059200*1000ms

if ((time < 7834) || (time > 8755)) //如果不在8.5ms-9.5ms的范围就清零外部中断1标志位,退出中断

{

IE1 = 0;//清零中断标志

return;//退出中断函数

}

time = GetHeighCounter();//空闲

if ((time < 3686) || (time > 4608)) //如果不在4ms-5ms的范围就退出中断

{

IE1 = 0;//中断标志硬件置位,软件清零

return;

}

//引导码正确,开始接收解码数据

for (i = 0; i < 4; i++) //接收4个字节数据

{

for (j = 0; j < 8; j++) //每个字节8bit

{

time = GetLowCounter();//载波时间计数

if ((time < 424) || (time > 608)) //如果载波时间不在460-660us的范围则认为是误码,退出中断

{

IE1 = 0;

return;

}

//检测到560us的载波,开始判断是560us的空闲还是1.68ms的空闲

time = GetHeighCounter();//空闲

if ((time > 424) && (time < 608)) //560us的空闲(即比特0)

{

//低位在先,逐位右移

byte >>= 1;//右移一位0

}

else if ((time > 1198) && (time < 1659)) //1.68ms的空闲 1.3-1.8ms

{

byte >>= 1;//低位在先,先右移出一位

byte |= 0x80;//再将这一位置1

}

else

{

//误码

IE1 = 0;

return;

}

}

Ircode[i] = byte;//循环接收解码字节数据

}

//4个字节全部接收完成

Irflag = 1;

IE1 = 0;

}

/**

*******************************************************************

* @file ds1302.c

* @author xr

* @date 2014年4月10日17:31:07

* @version V1.2.3

* @brief DS1302底层驱动 单片机STC89C52RC MCU 晶振11.0592MHZ

*******************************************************************

*/

#include <reg52.h>

#include "mytype.h"

//DS1302时钟线,数据线,和使能引脚位声明

sbit DS1302_SCK = P3^5;

sbit DS1302_SIO = P3^4;

sbit DS1302_CE = P1^7;

/**

* @brief DS1302写一个字节数据

* @param 待写入的字节

* @retval 无

*/

void DS1302WriteByte(uint8 byte)

{

uint8 mask = 0x01;//发送掩码(低位在前,逐位发送)

DS1302_SCK = 0;

for (mask = 0x01; mask != 0; mask <<= 1) ////发送掩码(低位在前,逐位发送)

{

if ((mask & byte) == 0)

{

DS1302_SIO = 0;

}

else

{

DS1302_SIO = 1;

} //将数据准备好

DS1302_SCK = 1;//先来一个上升沿,从机DS1302进行数据的采样锁存

DS1302_SCK = 0;//再来一个下降沿,主机进行数据的输出

}

//当一个字节的数据发送完成后,主机(即单片机)释放SIO数据总线,由DS1302进行数据的发送

DS1302_SIO = 1;

}

/**

* @brief DS1302读一个字节的数据

* @param 无

* @retval 返回读取到的数据

*/

uint8 DS1302ReadByte()

{

uint8 byte = 0;//存放待读取的字节

uint8 mask = 0x01;//接收掩码(低位在先,逐位接收)

DS1302_SCK = 0;

for (mask = 0x01; mask != 0; mask <<= 1) //(低位在先,逐位接收)

{

if (DS1302_SIO == 1)

{

byte |= mask;//字节byte的相应位置1

}

else

{

byte &= ~mask;//字节byte的相应位清零

}

DS1302_SCK = 1;//先来一个上升沿,主机进行数据采样接收

DS1302_SCK = 0;//再来一个下降沿,从机DS1302进行数据输出

}

return (byte);

}

/**

* @brief 向DS1302寄存器中写入数据

* @param 待写入数据的寄存器地址reg和待写入的数据dat

* @retval 无

*/

void WriteDS1302(uint8 reg, uint8 dat)

{

DS1302_CE = 1;//使能DS1302

DS1302WriteByte((reg << 1) | 0x80);//寄存器的最高位固定位1第二位为RAM/CLK选择位,A5-A1为寄存器地址位

//且有效位为A3-A1,A0位为读写方向位

DS1302WriteByte(dat);//写入数据

DS1302_CE = 0;//关闭片选

DS1302_SIO = 0;//由于本版本的开发板上的DS1302的SIO口没有加上拉电阻,在释放SIO后SIO处于

//非0非1的不稳定状态,由于IO口的内部输出通过反相器输出,给SIO=0则SIO口

//会输出一个高电平1,从而保持SIO数据口的稳定!

}

/**

* @brief 读DS1302寄存器

* @param 待读取的寄存器地址reg

* @retval 读取到的数据dat

*/

uint8 ReadDS1302(uint8 reg)

{

uint8 dat = 0;

DS1302_CE = 1;//使能片选

DS1302WriteByte((reg << 1) | 0x81);//左移出一位存放读写位,这里选择读

dat = DS1302ReadByte();

DS1302_CE = 0;

DS1302_SIO = 0;

return (dat);

}

/**

* @brief DS1302 Burst触发模式连续写八个寄存器

* @param 待写入数据指针*dat

* @retval 无

*/

void DS1302BurstWrite(uint8 * dat)

{

uint8 i = 0;

DS1302_CE = 1;//使能片选

DS1302WriteByte(0xBE);//A5-A1写入1,触发DS1302的突发模式连续读写八次

for (i = 0; i < 8; i++)

{

DS1302WriteByte(dat[i]);//写Sec-WP八个寄存器

}

DS1302_CE = 0;//关闭片选

DS1302_SIO = 0;//通过反相器SIO口输出1,保持稳定

}

/**

* @brief DS1302 Burst模式连续读取八个寄存器

* @param 待接收读取的数据的指针*time

* @retval 无

*/

void DS1302BurstRead(uint8 * time)

{

uint8 i = 0;

DS1302_CE = 1;//使能片选

DS1302WriteByte(0xBF);//触发突发模式读操作

for (i = 0; i < 8; i++)

{

time[i] = DS1302ReadByte();//读取Sec-WP八个寄存器

}

DS1302_CE = 0;//关闭片选

DS1302_SIO = 0;//使SIO口处于稳定

}

/**

* @brief DS1302初始化

* @param 无

* @retval 无

*/

void InitDS1302()

{

uint8 ch = 0;//用于检测DS1302停止状态

uint8 InitTime[] = {0x56, 0x59, 0x23, 0x10, 0x04, 0x04, 0x14, 0x00};//2014年4月10日23:59:56 WP=0

//读取秒寄存器

ch = ReadDS1302(0);

if ((ch & 0x80) != 0) //检测秒寄存器的最高位CH,CH为1标志DS1302已经停止运行,要去除写保护,写入初始时间

{

DS1302BurstWrite(InitTime);//突发模式写

}

}

/**

******************************************************************

* @file ds18b20.c

* @author xr

* @date 2014年4月1日21:03:47

* @version V1.2.3

* @brief 温度传感器DS18B20检测温度值程序

* @note 单片机STC89C52RC MCU 晶振 11.0592MHZ

******************************************************************

*/

#include <reg52.h>

#include <intrins.h>

//温度传感器引脚

sbit IO_DS18B20 = P3^2;

/**

* @brief 软件延时xus

* @param 无

* @retval 无

*/

void delayx10us(unsigned int xus)

{

do

{

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

} while (xus--); //8个_nop_()和函数的进栈出栈时间大约10us

}

/**

* @brief DS18B20温度传感器初始化检测存在脉冲

* @param 无

* @retval 无

*/

bit ReadDs18B20Ack()

{

bit ack; //存放检测到的DS18B20的存在脉冲

EA = 0;//由于DS18B20的时序时间的要求很高,进出中断需要十几个us会影响时序时间

IO_DS18B20 = 0;//复位脉冲

delayx10us(60);//持续600us

IO_DS18B20 = 1;//释放总线,来检测DS18B20的存在脉冲

delayx10us(6);//延时60us后来读取DS18B20的存在脉冲,这个时候肯定可以读取到

ack = IO_DS18B20;

while (!IO_DS18B20);//等待存在脉冲的结束

EA = 1;//重新开启中断

return ack;//返回存在脉冲

}

/**

* @brief DS18B20写一个字节数据

* @param 待写入数据 dat

* @retval 无

*/

void WriteDs18B20(unsigned char dat)

{

unsigned char mask = 0x01;//低位在前,逐位发送

EA = 0;//在发送每一位时必须将总中断关闭,避免进出中断时间影响DS18B20的读写

for (mask = 0x01; mask != 0; mask <<= 1)

{

IO_DS18B20 = 0;//主机拉低

_nop_();

_nop_();//延时2us,写1或0时延时>1us

if ((mask & dat) == 0)

{

IO_DS18B20 = 0;

}

else

{

IO_DS18B20 = 1;

}

delayx10us(6); //延时60-120us等待DS18B20来读取数据

IO_DS18B20 = 1;//释放总线,这时DS18B20会来采样数据进行写入

}

EA = 1;//写入一个字节数据完毕,打开总中断

}

/**

* @brief 读DS18B20一个字节

* @param 无

* @retval 读取的数据

*/

unsigned char ReadDs18B20()

{

unsigned char mask = 0x01;//低位在前,逐位接收

unsigned char dat = 0;

EA = 0;//DS18B20在读取数据前要关闭中断,避免进出中断时间,影响读取

for (mask = 0x01; mask != 0; mask <<= 1)

{

IO_DS18B20 = 0;//读0或读1时主机要先拉低>1us再拉高释放>1us时间,然后主机对DS18B20进行数据的采样读取

_nop_();

_nop_();

IO_DS18B20 = 1;

_nop_();

_nop_();

if (IO_DS18B20 == 0)

{

dat &= ~mask;//相应位置1

}

else

{

dat |= mask;//相应位清零

}

delayx10us(6);//延时60-120us,等待主机数据的采样

}

EA = 1;//开总中断

return dat;

}

/**

* @brief 启动DS18B20温度转换,启动成功则返回应答ack

* @param 无

* @retval 返回复位存在脉冲

*/

bit StartDs18B20()

{

bit ack;

ack = ReadDs18B20Ack();//进行DS18B20复位,并读取DS18B20的存在脉冲

if (ack == 0)

{

WriteDs18B20(0xCC);//跳过ROM的器件地址寻址,因为只有一个DS18B20

WriteDs18B20(0x44);//启动温度转换

}

return ~ack;//ack==0则转换成功

}

/**

* @brief 读取DS18B20的温度值

* @param 无

* @retval 返回复位存在脉冲的取反值

*/

bit ReadDs18B20Temp(int * temp) //温度值保存到temp指针指向的变量中

{

bit ack;//保存复位的存在脉冲

unsigned char lsb, msb;//温度值的低字节和高字节

ack = ReadDs18B20Ack();//DS18B20复位,并读取DS18B20的存在脉冲

if (ack == 0) //复位成功

{

WriteDs18B20(0xCC);//跳过ROM多个DS18B20的寻址

WriteDs18B20(0xBE);//读暂存寄存器中的数据

lsb = ReadDs18B20();//因为读取是从低位向高位读取的,所以先读取的是低字节数据

msb = ReadDs18B20();//读取高字节数据

//将温度值的低字节和高字节数据进行合并成16bit数据,易于计算

*temp = ((int)(msb) << 8) + lsb;//先将高字节强制转换成16为数据,将高字节移到高八位,再将低字节放到低八位即可

}

return ~ack;//ack == 0表示读取成功

}

提醒:《实时时钟+红外+温度传感器+LCD1602显示程序》最后刷新时间 2024-03-14 01:09:35,本站为公益型个人网站,仅供个人学习和记录信息,不进行任何商业性质的盈利。如果内容、图片资源失效或内容涉及侵权,请反馈至,我们会及时处理。本站只保证内容的可读性,无法保证真实性,《实时时钟+红外+温度传感器+LCD1602显示程序》该内容的真实性请自行鉴别。