数码管亮度不够!单片机烧录需要手动复位

需要做一个基于单片机的火灾检测系统。用的是STC89C52RC单片机+ +DS18B20+ADC0832+ MQ2烟雾传感器+4位共阴极数码管。4位数码管用npn管9013驱动,仿真显示基极电压接近5V,但实际电路中最高电压才有0.6V,亮度不够。且复位电路采用手动复位,仿真正常,但实际上烧录的时候需要先冷启动再按下复位键才烧录成功。跑程序的时候也需要按下复位键。百度说亮度和延时函数有关,但改了一下并没有亮度变化。在线请教这两种问题怎么解决?

img

#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);
         }   
    
    
}


电路需要这么改一下

img

三极管发射极电阻过大,略计算就知道1K5=5因此可推导点亮数码管电流不足5毫安,提供数码管电流越大亮度越高.
三极管发射极电阻过大,略计算就知道1K
5=5因此可推导点亮数码管电流不足5毫安,提供数码管电流越大亮度越高.
从代码上略看按键释放代码无用while(K4);应该是while(!K4);数码管驱动写得不合逻辑,数码管驱动:消隐-送位值-送段值-延时。

引用 皆我百晓生 小程序回复内容作答:
对于数码管亮度不够的问题,您可以尝试以下几个解决方案:

  1. 检查代码中的数码管控制部分是否有误:确保使用正确的IO口进行控制,并检查是否配置了正确的IO口输出模式等参数。
  2. 检查数码管驱动电流是否足够:您可以尝试增大NPN管9013的基电流,或者使用更大的NPN或N-MOSFET来驱动数码管,以提供更大的电流。
  3. 检查电路连线是否正确:确保数码管的阴极引脚连接正确,例如共阴极数码管应将其阴极引脚连接到GND,确保没有接错引脚或短路等问题。

对于单片机烧录需要手动复位的问题,您可以尝试以下几个解决方案:

  1. 检查复位电路的连接是否正确:查看复位电路是否按照规定的接法进行连接,并确保与单片机的复位引脚连接正确。
  2. 检查是否存在其他影响复位的因素:例如供电电压是否稳定,是否存在其他电路中断复位等问题。
  3. 检查编程器设置是否正确:检查编程器的相关设置,例如设置是否为手动复位模式等。

如您在尝试以上解决方案后仍未解决问题,建议仔细检查您的电路和代码,并可能寻求技术人员的帮助。

【以下回答由 GPT 生成】

问题一:数码管亮度不足

问题描述中提到,在实际电路中,数码管的最高电压只有0.6V,导致数码管亮度不足。已经尝试过修改延时函数,但并没有改变亮度。请问还有其他方法可以解决这个亮度问题吗?

解决方案:

  1. 首先,确认驱动npn管9013的电路连接正确,包括引脚连接和电压接入。
  2. 确认npn管9013、数码管以及其他元件的额定电流和功率,以保证亮度不足问题不是由于电流或功率不足引起的。
  3. 考虑使用其他型号的驱动管、数码管或增加驱动电路以提供足够的电流,以增强数码管的亮度。例如,使用PNP型的数码管驱动器来驱动共阴极数码管。
  4. 检查数码管的共阴极连接是否正确。确保每个数码管的共阴极引脚都连接到单片机通过驱动管提供的地线。
  5. 优化单片机代码中与数码管驱动相关的部分。例如,可以使用软件PWM技术来改变数码管的亮度。可以根据具体的编程语言和单片机型号来搜索软件PWM技术的实现方法。以下是一个使用C语言的软件PWM驱动数码管的示例代码:
// 声明数码管引脚连接
#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数组的值。


问题二:烧录需要手动复位

问题描述中提到,在仿真中,系统的复位电路正常工作,但在实际烧录时,需要先冷启动单片机,然后按下复位键才能成功烧录。另外,在运行程序时也需要手动按下复位键。请问如何解决这个手动复位的问题?

解决方案:

  1. 确认烧录工具的设备、连接和烧录设置是否正确。确保相关的连接线路稳定可靠,烧录工具的驱动程序正确安装,并选择正确的单片机型号和烧录选项。
  2. 检查复位电路的连接和元件是否正确。确保从烧录工具到单片机的复位线连接正确,并且复位电路中的电阻、电容等元件的值符合设计要求。
  3. 检查单片机上的复位电路是否有设计或硬件问题。可以尝试使用另一个单片机或更换复位电路中的元件,以排除硬件故障。
  4. 检查单片机的复位电路是否有软件配置问题。检查是否需要将复位电路配置为硬件复位或软件复位,并根据需要进行配置。可以查阅单片机的资料手册来了解有关复位配置的详细信息和相应的配置寄存器。
  5. 在单片机的初始化代码中,添加复位相关的配置。如果手动复位是因为初始化未正确执行或初始化代码缺失导致的,可以在初始化代码中添加复位相关的配置,以确保在程序运行前进行复位操作。
  6. 尝试对单片机进行软件复位。在程序的合适位置(例如主循环的初始位置)添加一段代码,通过设置特定的寄存器或执行复位指令来模拟手动复位。以下是使用C语言进行软件复位的示例代码:
// 头文件引入
#include <stc89c52.h>

// 手动复位函数
void manualReset() {
    EA = 0; // 关闭中断
    RST=0;  // 拉低复位引脚
    RST=1;  // 拉高复位引脚
    EA = 1; // 打开中断
    // 执行复位后续操作
}

void main() {
    while (1) {
        // 主循环代码
        // 如果需要进行复位操作,调用manualReset函数
        manualReset();
        // 继续执行其他操作
    }
}

在上述代码中,通过手动设置复位引脚的状态来模拟手动复位操作,可根据实际需求在合适的位置调用manualReset()函数进行复位。


以上是针对问题一和问题二的解决方案,希望对您有所帮助。如果还有其他问题,请随时提问。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^