需要做一个基于单片机的火灾检测系统。用的是STC89C52RC单片机+ +DS18B20+ADC0832+ MQ2烟雾传感器+4位共阴极数码管。4位数码管用npn管9013驱动,仿真显示基极电压接近5V,但实际电路中最高电压才有0.6V,亮度不够。且复位电路采用手动复位,仿真正常,但实际上烧录的时候需要先冷启动再按下复位键才烧录成功。跑程序的时候也需要按下复位键。百度说亮度和延时函数有关,但改了一下并没有亮度变化。在线请教这两种问题怎么解决?
#include <reg52.h>
#include <intrins.h>
#include "eeprom52.h"
#define uchar unsigned char
#define uint unsigned int
//共阴极数码管段选定义 0 1 2 3 4 5 6 7 8 9 -
uchar code duan[]={0x5f,0x41,0x9b,0xd3,0xc5,0xd6,0xde,0x43,0xdf,0xd7,0x80};
sbit CS=P3^2; //CS定义为P3口的第2位脚,连接ADC0832CS脚
sbit SCL=P3^4; //SCL定义为P3口的第4位脚,连接ADC0832SCL脚
sbit DO=P3^3; //DO定义为P3口的第3位脚,连接ADC0832DO脚
sbit DI=P3^3;
sbit ledy = P0^2; //烟雾报警指示灯
sbit ledr = P0^3; //温度报警指示灯
sbit beep = P0^1; //蜂鸣器IO口定义
sbit dq = P3^5; //ds18b20 IO口的定义
//按键定义
sbit K1 = P2^0; //设置
sbit K2 = P2^1; //加
sbit K3 = P2^2; //减
sbit K4 = P2^3; //手动报警
uint temp,wendu,smog,dengji; //温度 烟物等级
uint a_a,flag; //标志变量
/*****************************50ms延时函数*****************************/
void delay50ms(uint i)
{
unsigned int j;
for(i;i>0;i--)
for(j=50000;j>0;j--); //该处使用的为12MHz晶振,及一周期用时1us
}
/*****************************小延时函数*****************************/
void delay(uint t)
{
while (--t);
}
/***********************小延时函数*****************************/
void delay_uint(uint q)
{
while(q--);
}
/************************把数据保存到单片机内部eeprom中******************/
void write_eeprom()
{
SectorErase(0x2000);
byte_write(0x2000, temp);
byte_write(0x2001, smog);
byte_write(0x2060, a_a);
}
/************************把数据从单片机内部eeprom中读出来*****************/
void read_eeprom()
{
temp = byte_read(0x2000);
smog = byte_read(0x2001); //分别读取EEPROM第一扇区首地址0和第2地址1
a_a = byte_read(0x2060);
}
/**************************开机自检eeprom初始化*****************************/
void init_eeprom()
{
read_eeprom(); //先读
if(a_a != 2) //新的单片机初始单片机内访问eeprom
{
temp = 27;
smog = 3;
a_a = 2;
write_eeprom();
}
}
/***********************18b20初始化函数*****************************/
void init_18b20()
{
bit q;
dq = 1; //把总线拉高
delay_uint(1); //15us
dq = 0; //给复位脉冲
delay_uint(80); //750us
dq = 1; //把总线拉高 等待
delay_uint(10); //110us
q = dq; //读取18b20初始化信号
delay_uint(20); //200us
dq = 1; //把总线拉高 释放总线
}
/*************************写18b20内的数据***************************/
void write_18b20(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{ //写数据是低位开始
dq = 0; //把总线拉低写时间隙开始
dq = dat & 0x01; //向18b20总线写数据,dat中保存的数,按照二进制计算保留最低位,其余位取0,保存到变量dq中
delay_uint(5); // 60us
dq = 1; //释放总线
dat >>= 1; //dat中数按照二进制数右移一位,最高位补0,原来的最低位舍弃,然后保存回变量dat中
}
}
/*************************读取18b20内的数据***************************/
uchar read_18b20()
{
uchar i,value;
for(i=0;i<8;i++)
{
dq = 0; //把总线拉低读时间隙开始
value >>= 1; //读数据是低位开始
dq = 1; //释放总线
if(dq == 1) //开始读写数据
value |= 0x80; //将value的最高位置1,其余位不变
delay_uint(5); //60us 读一个时间隙最少要保持60us的时间
}
return value; //返回数据
}
/*******************读取温度的值 读出来的是小数*********************/
uint read_temp()
{
uint value;
uchar low; //在读取温度的时候如果中断的太频繁了,就应该把中断给关了,否则会影响到18b20的时序
init_18b20(); //初始化18b20
write_18b20(0xcc); //跳过64位ROM
write_18b20(0x44); //启动一次温度转换命令
delay_uint(50); //500us
init_18b20(); //初始化18b20
write_18b20(0xcc); //跳过64位ROM
write_18b20(0xbe); //发出读取暂存器命令
EA = 0;
low = read_18b20(); //读温度低字节
value = read_18b20(); //读温度高字节
EA = 1;
value <<= 8; //把温度的高位左移8位
value |= low; //把读出的温度低位放到value的低八位中
value = value*0.0625+0.5; //转换到温度值,四舍五入
return value; //返回读出的温度
}
/*****************读数模转换烟雾浓度等级数据************************************************/
uchar ADC0832(bit mode,bit channel) //AD转换,返回结果
{
uchar i,dat,ndat;
CS = 0;//拉低CS端
_nop_();
_nop_();
DI = 1; //第1个下降沿为高电平
SCL = 1;//拉高CLK端
_nop_();
_nop_();
SCL = 0;//拉低CLK端,形成下降沿1
_nop_();
_nop_();
DI = mode; //低电平为差分模式,高电平为单通道模式。
SCL = 1;//拉高CLK端
_nop_();
_nop_();
SCL = 0;//拉低CLK端,形成下降沿2
_nop_();
_nop_();
DI = channel; //低电平为CH0,高电平为CH1
SCL = 1;//拉高CLK端
_nop_();
_nop_();
SCL = 0;//拉低CLK端,形成下降沿3
DI = 1;//控制命令结束(经试验必需)
dat = 0;
//下面开始读取转换后的数据,从最高位开始依次输出(D7~D0)
for(i = 0;i < 8;i++)
{
dat <<= 1;
SCL=1;//拉高时钟端
_nop_();
_nop_();
SCL=0;//拉低时钟端形成一次时钟脉冲
_nop_();
_nop_();
dat |= DO;
}
ndat = 0; //记录D0
if(DO == 1)
ndat |= 0x80;
//下面开始继续读取反序的数据(从D1到D7)
for(i = 0;i < 7;i++)
{
ndat >>= 1;
SCL = 1;//拉高时钟端
_nop_();
_nop_();
SCL=0;//拉低时钟端形成一次时钟脉冲
_nop_();
_nop_();
if(DO==1)
ndat |= 0x80;
}
CS=1;//拉高CS端,结束转换
SCL=0;//拉低CLK端
DI=1;//拉高数据端,回到初始状态
if(dat==ndat)
return(dat);
else
return 0;
}
/**********************共阴极数码管显示函数*********************/
void display(uchar wei,num)
{
uchar i;
for(i=0;i<4;i++)
{
switch(wei)//位选
{
case 1:P2=0xbf;break;
case 2:P2=0xdf;break;
case 3:P2=0xef;break;
case 4:P2=0x7f;break;
}
P1=duan[num]; delay(200);
P1=0x00;//消隐
}
}
/**********************按键处理函数***************************/
void key() //按键程序 选择菜单模式
{
uint t;
if(!K1)
{ delay(100);
if(!K1)
{
flag++;
if(flag>=3) flag=0;
for(t=0;t<80;t++) //稳定显示
{
display(1,10);
display(2,10);
display(3,10);
display(4,10);
}
}
while(K1);
}
}
void set() //加减操作和手动报警
{
if(!K2)
{
delay(1000);
if(!K2)
{
if(flag==1)
{
temp+=1;
if(temp>100) temp=99; //温度上限加
}
if(flag==2)
{
smog+=1;
if(smog>9) smog=9; //烟雾浓度等级上限加
}
write_eeprom(); //保存数据
}
}
if(!K3)
{
delay(1000);
if(!K3)
{
if(flag==1)
{
temp-=1; //温度下限减
if(temp<0) temp=0;
}
if(flag==2)
{
smog-=1; //烟雾浓度等级下限减
if(smog<0) smog=0;
}
write_eeprom(); //保存数据
}
}
if(!K4)
{
delay(1000);
if(!K4) beep=1;
while(K4);
if(!K4)
{
delay(1000);
if(!K4) beep=0;
}
}
}
/**************************声光报警程序部分*************************/
void warn() //报警函数
{
if(wendu>=temp) //检测的温度高于设定值
{
beep=0; //报警
ledy=0;
}
if(dengji>=smog) //检测的烟雾浓度等级高于设定值
{
beep=0; //报警
ledr=0;
}
if( wendu<temp && dengji<smog )
{
beep=1; //停止报警
ledy=1;
ledr=1;
}
}
/*********************主函数*****************************/
void main()
{
uint t;
IT0=1; //外部中断0为下降沿触发
EX0=1; //开EX0中断
EA=1; //开总中断
init_eeprom(); //开始初始化保存的数据
while(1)
{
dengji =ADC0832(1,0);
dengji = dengji * 10 / 250;
wendu = read_temp(); //读取温度值
for(t=0;t<80;t++) //稳定显示
{
display(1,dengji);
display(2,10);
display(3,wendu/10%10);
display(4,wendu%10);
}
// warn();
}
}
/*********************按键外部中断函数*****************************/
void button() interrupt 0
{
uint t;
key();
if(flag==1) //设置温度
{
set();
for(t=0;t<80;t++) //稳定显示
{
display(1,10);
display(2,10);
display(3,temp/10%10);
display(4,temp%10);
}while(flag!=1);
}
if(flag==2) //设置烟雾浓度等级
{
set();
for(t=0;t<80;t++) //稳定显示
{
display(1,smog);
display(2,10);
display(3,10);
display(4,10);
}while(flag!=2);
}
}
电路需要这么改一下
三极管发射极电阻过大,略计算就知道1K5=5因此可推导点亮数码管电流不足5毫安,提供数码管电流越大亮度越高.
三极管发射极电阻过大,略计算就知道1K5=5因此可推导点亮数码管电流不足5毫安,提供数码管电流越大亮度越高.
从代码上略看按键释放代码无用while(K4);应该是while(!K4);数码管驱动写得不合逻辑,数码管驱动:消隐-送位值-送段值-延时。
引用 皆我百晓生 小程序回复内容作答:
对于数码管亮度不够的问题,您可以尝试以下几个解决方案:
对于单片机烧录需要手动复位的问题,您可以尝试以下几个解决方案:
如您在尝试以上解决方案后仍未解决问题,建议仔细检查您的电路和代码,并可能寻求技术人员的帮助。
【以下回答由 GPT 生成】
问题描述中提到,在实际电路中,数码管的最高电压只有0.6V,导致数码管亮度不足。已经尝试过修改延时函数,但并没有改变亮度。请问还有其他方法可以解决这个亮度问题吗?
// 声明数码管引脚连接
#define DIG1 P0_0
#define DIG2 P0_1
#define DIG3 P0_2
#define DIG4 P0_3
#define SEGMENT P1
// 定义数码管显示的数字模式,每一位的亮度由brightness数组控制
const unsigned char digits[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
const unsigned char brightness[4] = {0xFF, 0xE0, 0xC0, 0x80}; // 分别代表100%、75%、50%、25%的亮度
void displayDigit(int digit, int value) {
SEGMENT = digits[value] & brightness[digit];
// 设置当前位,例如DIG1表示第一位,DIG2表示第二位,以此类推
switch (digit) {
case 0:
DIG1 = 1; DIG2 = 0; DIG3 = 0; DIG4 = 0;
break;
case 1:
DIG1 = 0; DIG2 = 1; DIG3 = 0; DIG4 = 0;
break;
case 2:
DIG1 = 0; DIG2 = 0; DIG3 = 1; DIG4 = 0;
break;
case 3:
DIG1 = 0; DIG2 = 0; DIG3 = 0; DIG4 = 1;
break;
}
}
void main() {
while (1) {
// 循环显示数字0到9,每300毫秒更新一次
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 4; j++) {
displayDigit(j, i);
delay(10); // 延时时间根据具体需求调整
}
delay(300); // 切换数字的时间间隔,根据具体需求调整
}
}
}
上述代码通过控制亮度数组brightness
的值来改变数码管的亮度,可根据实际需求调整brightness
数组的值。
问题描述中提到,在仿真中,系统的复位电路正常工作,但在实际烧录时,需要先冷启动单片机,然后按下复位键才能成功烧录。另外,在运行程序时也需要手动按下复位键。请问如何解决这个手动复位的问题?
// 头文件引入
#include <stc89c52.h>
// 手动复位函数
void manualReset() {
EA = 0; // 关闭中断
RST=0; // 拉低复位引脚
RST=1; // 拉高复位引脚
EA = 1; // 打开中断
// 执行复位后续操作
}
void main() {
while (1) {
// 主循环代码
// 如果需要进行复位操作,调用manualReset函数
manualReset();
// 继续执行其他操作
}
}
在上述代码中,通过手动设置复位引脚的状态来模拟手动复位操作,可根据实际需求在合适的位置调用manualReset()
函数进行复位。
以上是针对问题一和问题二的解决方案,希望对您有所帮助。如果还有其他问题,请随时提问。