找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1265|回复: 10
收起左侧

单片机LED每过一段时间会出现频闪求大佬指点解决?

[复制链接]
ID:757219 发表于 2023-3-27 22:12 | 显示全部楼层 |阅读模式
1.png
1.png
回复

使用道具 举报

ID:757219 发表于 2023-3-27 22:13 | 显示全部楼层
#include <reg52.h>
#include <stdio.h>
#include <intrins.h>
#include "lcd016.h"

#define uchar unsigned char
#define uint unsigned int
sbit LCD_RS=P2^0;
sbit LCD_RW=P2^1;
sbit LCD_EN=P2^2;
#define LCD_Data_Port P0

sbit ADCS=P1^0;
sbit ADCLK=P1^1;
sbit ADDI=P1^2;
sbit ADDO=P1^2;
sbit LED=P1^4;

uint dat = 0x00;      //AD值
uchar adc=0;//存储adc0832值
uint voltage = 0;
uchar pwm=0;//PWM占空比参数
uchar time=0;
uchar LED_buffer1[16] = {"ADC:000LX"};//ADC显示缓冲
uchar LED_buffer2[16] = {"voltage:0.00V"};//电压显示缓冲
/*------------------------------------------------
延时函数,含有输入参数 unsigned int t,无返回值
unsigned int 是定义无符号整形变量,其值的范围是
0~65535
------------------------------------------------*/
void Delay(unsigned int t)
{
while(--t);
}



// 从ADC读取数据的函数
uchar ADC_read_data(unsigned char channel)
{
unsigned char i = 0;
    unsigned char j;
    unsigned int dat = 0;
    unsigned char ndat = 0;

    if (channel == 0) channel = 2;//通道1
    if (channel == 1) channel = 3;//通道2
    ADDI = 1;//输入引脚拉高
    _nop_();//延时1us
    _nop_();//延时1us
    ADCS = 0;//片选
    _nop_();//延时1us
    _nop_();//延时1us
    ADCLK = 1;//拉高时钟
    _nop_();//延时1us
    _nop_();//延时1us
    ADCLK = 0;//拉低时钟
    _nop_();//延时1us
    _nop_();//延时1us
    ADCLK = 1;//拉高时钟
    ADDI = channel & 0x1;
    _nop_();//延时1us
    _nop_();//延时1us
    ADCLK = 0;//拉低时钟
    _nop_();//延时1us
    _nop_();//延时1us
    ADCLK = 1;//拉高时钟
    ADDI = (channel >> 1) & 0x1;
    _nop_();//延时1us
    _nop_();//延时1us
    ADCLK = 0;//拉低时钟
    ADDI = 1;//输出1
    _nop_();//延时1us
    _nop_();//延时1us
    dat = 0; //存储清零
    for (i = 0; i < 8; i++)    //循环8次
    {
        dat |= ADDO;//读取引脚
        ADCLK = 1; //拉高时钟
        _nop_();//延时1us
        _nop_();//延时1us
        ADCLK = 0;//拉低时钟
        _nop_();//延时1us
        _nop_();//延时1us
        dat <<= 1;//左移   最低位从外面进去,最高位往左顶依旧是最高位  即0832数据最高位先进去
        if (i == 7) dat |= ADDO;//读取引脚
    }
    for (i = 0; i < 8; i++)//循环8次
    {
        j = 0;
        j = j | ADDO; //读取引脚
        ADCLK = 1;    //拉高时钟
        _nop_();//延时1us
        _nop_();//延时1us
        ADCLK = 0;    //拉低时钟
        _nop_();//延时1us
        _nop_();//延时1us
        j = j << 7;    //左移7位
        ndat = ndat | j; //合并数据
        if (i < 7) ndat >>= 1;//去掉符号
    }
    ADCS = 1; //清除片选
    ADCLK = 0;//拉低时钟
    ADDO = 1; //输出1
    dat <<= 8;//数据移位
    dat |= ndat;//合并数据
    return(ndat);
}


void init_timer1()//定时器1的初始化函数
{
TMOD|=0X11;  //设置计时器1为工作模式1,计时器0为工作模式1
    TH1=0X3c;    //设置计时器1的计数器高位为0x3C,让定时器1产生一个50ms的中断信号
    TL1=0XB0;    //设置计时器1的计数器低位为0xB0
    ET1=1;       //打开定时器1中断允许
    EA=1;        //打开总中断
    TR1=1;       //打开计时器1,若是定时器0则是TH0\TL0\ET0\TR0\TMOD=0X01
}

void main()
{
uchar i=0;
LCD_Init(); // 初始化LCD
init_timer1();
while (1)
{
        if(i<100) //输出PWM调光,将一个周期具体化为100等分,从而易得高电平多少分来计算pwm
            i++;//不断自增代表对一个周期100等分中每个等分进行检查此时是高电平还是低电平。在这100中方波越疏越暗,越密越亮。
        else
            i=0;
        if(i<84)
            LED=0;
        else
            LED=1;
}

}

//定时器1中断函数
void Timer1() interrupt 3
{   
    if(time<8)
        time++;
    else
    {
    adc = ADC_read_data(0);//读取adc值
    voltage = adc * 500.0 / 255;//将adc值换算成电压
    LED_buffer1[4] = adc / 100 + '0';        //取adc百位
    LED_buffer1[5] = adc % 100 / 10 + '0';    //取adc十位
    LED_buffer1[6] = adc % 10 + '0';        //取adc个位
    LED_buffer2[8] = voltage / 100 + '0';        //取电压个位
    LED_buffer2[9] = '.';//小数点
    LED_buffer2[10] = voltage / 10 % 10 + '0';    //取电压十分位
    LED_buffer2[11] = voltage % 10 + '0';    //取电压百分位
    LCD_ShowString(1, 1, &LED_buffer1[0]);    //显示字符串
    LCD_ShowString(2, 1, &LED_buffer2[0]);    //显示字符串
    }
    TH1 = 0x3C;     // 设置定时器1计数初值,用于控制LED亮度的PWM值
    TL1 = 0xB0;     // 设置定时器1计数初值,用于控制LED亮度的PWM值
}

不看其他只看P14口的led,正常应该经过pwm调制后led灯是暗的(图3),但图2是闪一下暗的然后常亮。
发现是正常的p14口高低电平交替输出,不正常的是一直是低电平。
可是我用的代码是一模一样的,不知道哪里出问题了
回复

使用道具 举报

ID:757219 发表于 2023-3-27 22:14 | 显示全部楼层
每过一段时间会频闪,代码已贴上求指点如何更改
回复

使用道具 举报

ID:1004920 发表于 2023-3-28 13:09 | 显示全部楼层
这程序写的臃肿,AD采集最好不要放在中断里。低电平点亮LED,调试先看看单一功能:AD的值对不对,定时器运行时间,LED点亮等。都没问题了再合起来。
回复

使用道具 举报

ID:757219 发表于 2023-3-28 17:54 | 显示全部楼层
zch5200 发表于 2023-3-28 13:09
这程序写的臃肿,AD采集最好不要放在中断里。低电平点亮LED,调试先看看单一功能:AD的值对不对,定时器运 ...

ad采集放在主函数里led的频闪会更严重直接出现频闪
回复

使用道具 举报

ID:757219 发表于 2023-3-28 20:02 | 显示全部楼层
有没有大神帮忙修改一下,如何解决频闪,led亮度随adc控制的pwm变化
回复

使用道具 举报

ID:401564 发表于 2023-3-29 00:40 | 显示全部楼层
1:LED显示的延时一定要均匀,2mS和500uS的延时几乎是没有区别的
2:不能有一个突变的延时,比如,你LED的延时一直是1mS,ADC的时候,延时突然变成了2mS,这个时候是一定会闪的
解决建议:
LED显示用定时器中断扫描显示,这样就不会被ADC打断了
回复

使用道具 举报

ID:757219 发表于 2023-3-29 16:02 | 显示全部楼层
Y_G_G 发表于 2023-3-29 00:40
1:LED显示的延时一定要均匀,2mS和500uS的延时几乎是没有区别的
2:不能有一个突变的延时,比如,你LED的延时 ...

我把led程序和adc读取显示两个程序调换,即led显示程序放入中断后,led直接常亮了
回复

使用道具 举报

ID:757219 发表于 2023-3-29 16:05 | 显示全部楼层
#include <reg52.h>
#include <stdio.h>
#include <intrins.h>
#include "lcd016.h"

#define uchar unsigned char
#define uint unsigned int
sbit LCD_RS=P2^0;
sbit LCD_RW=P2^1;
sbit LCD_EN=P2^2;
#define LCD_Data_Port P0

sbit ADCS=P1^0;
sbit ADCLK=P1^1;
sbit ADDI=P1^2;
sbit ADDO=P1^2;
sbit LED=P1^4;

uint dat = 0x00;      //AD值
uchar adc=0;//存储adc0832值
uint voltage = 0;
uchar pwm=0;//PWM占空比参数
uchar time=0;
uchar LED_buffer1[16] = {"ADC:000LX"};//ADC显示缓冲
uchar LED_buffer2[16] = {"voltage:0.00V"};//电压显示缓冲
uchar adc2[4];
uchar voltage2[4];
/*------------------------------------------------
延时函数,含有输入参数 unsigned int t,无返回值
unsigned int 是定义无符号整形变量,其值的范围是
0~65535
------------------------------------------------*/
void Delay(unsigned int t)
{
while(--t);
}



// 从ADC读取数据的函数
uchar ADC_read_data(unsigned char channel)
{
unsigned char i = 0;
        unsigned char j;
        unsigned int dat = 0;
        unsigned char ndat = 0;

        if (channel == 0) channel = 2;//通道1
        if (channel == 1) channel = 3;//通道2
        ADDI = 1;//输入引脚拉高
        _nop_();//延时1us
        _nop_();//延时1us
        ADCS = 0;//片选
        _nop_();//延时1us
        _nop_();//延时1us
        ADCLK = 1;//拉高时钟
        _nop_();//延时1us
        _nop_();//延时1us
        ADCLK = 0;//拉低时钟
        _nop_();//延时1us
        _nop_();//延时1us
        ADCLK = 1;//拉高时钟
        ADDI = channel & 0x1;
        _nop_();//延时1us
        _nop_();//延时1us
        ADCLK = 0;//拉低时钟
        _nop_();//延时1us
        _nop_();//延时1us
        ADCLK = 1;//拉高时钟
        ADDI = (channel >> 1) & 0x1;
        _nop_();//延时1us
        _nop_();//延时1us
        ADCLK = 0;//拉低时钟
        ADDI = 1;//输出1
        _nop_();//延时1us
        _nop_();//延时1us
        dat = 0; //存储清零
        for (i = 0; i < 8; i++)        //循环8次
        {
                dat |= ADDO;//读取引脚
                ADCLK = 1; //拉高时钟
                _nop_();//延时1us
                _nop_();//延时1us
                ADCLK = 0;//拉低时钟
                _nop_();//延时1us
                _nop_();//延时1us
                dat <<= 1;//左移   最低位从外面进去,最高位往左顶依旧是最高位  即0832数据最高位先进去
                if (i == 7) dat |= ADDO;//读取引脚
        }
        for (i = 0; i < 8; i++)//循环8次
        {
                j = 0;
                j = j | ADDO; //读取引脚
                ADCLK = 1;        //拉高时钟
                _nop_();//延时1us
                _nop_();//延时1us
                ADCLK = 0;        //拉低时钟
                _nop_();//延时1us
                _nop_();//延时1us
                j = j << 7;        //左移7位
                ndat = ndat | j; //合并数据
                if (i < 7) ndat >>= 1;//去掉符号
        }
        ADCS = 1; //清除片选
        ADCLK = 0;//拉低时钟
        ADDO = 1; //输出1
        dat <<= 8;//数据移位
        dat |= ndat;//合并数据
        return(ndat);
}


void init_timer1()//定时器1的初始化函数
{
TMOD|=0X01;  //设置计时器1为工作模式1,计时器0为工作模式1
    TH0=0X3c;    //设置计时器1的计数器高位为0x3C,让定时器1产生一个50ms的中断信号
    TL0=0XB0;    //设置计时器1的计数器低位为0xB0
    ET0=1;       //打开定时器1中断允许
    EA=1;        //打开总中断
    TR0=1;       //打开计时器1,若是定时器0则是TH0\TL0\ET0\TR0\TMOD=0X01
}

void main()
{

LCD_Init(); // 初始化LCD
init_timer1();
while (1)
{
                adc = ADC_read_data(0);//读取adc值
        voltage = adc * 500.0 / 255;//将adc值换算成电压
        LED_buffer1[4] = adc / 100 + '0';                //取adc百位
        LED_buffer1[5] = adc % 100 / 10 + '0';        //取adc十位
        LED_buffer1[6] = adc % 10 + '0';                //取adc个位
        LED_buffer2[8] = voltage / 100 + '0';                //取电压个位
        LED_buffer2[9] = '.';//小数点
        LED_buffer2[10] = voltage / 10 % 10 + '0';        //取电压十分位
        LED_buffer2[11] = voltage % 10 + '0';        //取电压百分位
        LCD_ShowString(1, 1, LED_buffer1);        //显示字符串
        LCD_ShowString(2, 1, LED_buffer2);        //显示字符串

}

}

//定时器1中断函数
void Timer1() interrupt 1
{       
        uchar i;
          pwm=adc/2;
                if(i<100) //输出PWM调光,将一个周期具体化为100等分,从而易得高电平多少分来计算pwm
                        i++;//不断自增代表对一个周期100等分中每个等分进行检查此时是高电平还是低电平。在这100中方波越疏越暗,越密越亮。
                else
                        i=0;
                if(i<84)
                        LED=0;
                else
                        LED=1;

    TH0 = 0x3C;     // 设置定时器1计数初值,用于控制LED亮度的PWM值
    TL0 = 0xB0;     // 设置定时器1计数初值,用于控制LED亮度的PWM值
}


这样改似乎程序进入不了中断了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表