基于at89c51单片机的简单系统设计

qq_36708405 2017-06-07 03:03:35
我现在是想在单片机开发板上做一个简单的无控制应用系统,包括 蜂鸣器+红外遥控+数码管三个模块就好了,它主要功能就跟空调一样,遥控器可以调节数码管上的“温度”,待达到一定值蜂鸣器会响,调节回正常后,蜂鸣器再不响,不知道这个预想可不可行。我之前网上找的一段代码是将数码管模块换成lcd1602做的,并且增加温度传感器模块,而且代码特别长,但我把代码烧进去后并没有反应。但我不需要温度模块了,只要三个就可以,然后我把代码放在下面,希望大神给点解决措施,拜托拜托了
#include <reg52.h>
#include "mytype.h"
#include <intrins.h>
bit fack = 0;//接收到温度数据标志位
bit flag1s = 0;//1s时间标志位
bit flag200ms = 0;//200ms标志
#ifndef _MYTYPE_H_H
#define _MYTYPE_H_H
typedef unsigned int uint16;
typedef unsigned char uint8;
typedef unsigned long uint32;
#endif
sbit LCD1602_RS = P1^0;//LCD1602
sbit LCD1602_RW = P1^1;
sbit LCD1602_EN = P1^5;
#define LCD1602_DB P0
sbit IRD=P3^3;//红外通信接口位声明
bit Irflag=0;//红外遥控器解码数据接收完成标志
uint8 Ircode[4];
extern void LcdShowStr(uint8 x,uint8 y,uint8 *str);
sbit DS1302_SCK = P3^5;//DS1302时钟线,数据线,和使能引脚位声明
sbit DS1302_SIO = P3^4;
sbit DS1302_CE = P1^7;
sbit IO_DS18B20 = P3^2;//温度传感器引脚
uint8 thr0,tlr0;
uint8 counter = 0, i = 0;
uint8 code LedTable[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
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);//清屏
}
void LcdShowStr(uint8 x, uint8 y, uint8 * str)//写str到LCD1602
{
uint8 addr;
if (y == 0)
{ //第一行
addr = 0x00 + x;
}
else
{ //第二行
addr = 0x40 + x;
}
WriteLcd1602Cmd(0x80 | addr);
while (*str != '\0')
{
WriteLcd1602Data(*str++);
}
}
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;
}
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)
}
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计数值
}
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;
}
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;//再来一个下降沿,主机进行数据的输出
}
DS1302_SIO = 1;
}
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);
}
void WriteDS1302(uint8 reg, uint8 dat)
{DS1302_CE = 1;//使能DS1302
DS1302WriteByte((reg << 1) | 0x80);//寄存器的最高位固定位1第二位为RAM/CLK选择位,A5-A1为寄存器地址位
DS1302WriteByte(dat);//写入数据
DS1302_CE = 0;//关闭片选
DS1302_SIO = 0;//由于本版本的开发板上的DS1302的SIO口没有加上拉电阻,在释放SIO后SIO处于
}
uint8 ReadDS1302(uint8 reg)
{
uint8 dat = 0;
DS1302_CE = 1;//使能片选
DS1302WriteByte((reg << 1) | 0x81);//左移出一位存放读写位,这里选择读
dat = DS1302ReadByte();
DS1302_CE = 0;
DS1302_SIO = 0;
return (dat);
}
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,保持稳定
}
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口处于稳定
}
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);//突发模式写
}
}
void delayx10us(unsigned int xus)
{
do
{
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
} while (xus--); //8个_nop_()和函数的进栈出栈时间大约10us
}
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;//返回存在脉冲
}
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;//写入一个字节数据完毕,打开总中断
}
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;
}
bit StartDs18B20()
{
bit ack;
ack = ReadDs18B20Ack();//进行DS18B20复位,并读取DS18B20的存在脉冲
if (ack == 0)
{
WriteDs18B20(0xCC);//跳过ROM的器件地址寻址,因为只有一个DS18B20
WriteDs18B20(0x44);//启动温度转换
}
return ~ack;//ack==0则转换成功
}
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表示读取成功
}
//定时器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;
}
}
}
//主函数
void main(void)
{
uint8 buf[4];
uint8 len = 0;
int temp;//温度
int intT, decT;//整数部分,小数部分
uint8 str[20];
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 (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();//重新启动温度转换
}
}
...全文
269 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

3,881

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 其它技术问题
社区管理员
  • 其它技术问题社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧