/**
****************************************************************************
* @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] = '\0';
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] = '\0';
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] = '\0';
LcdShowStr(0, 1, str);
//星期
str[0] = (time[5] & 0x0F) + '0';//星期只有一位数字
str[1] = '\0';
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] = '\0';
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 = '\0';//加上串结束符
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 != '\0')
{
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表示读取成功
}