搜索
bottom↓
回复: 4

这个关于IF判断,是我的问题还是编译器的问题,到现在我也不知道

[复制链接]

出0入10汤圆

发表于 2020-8-1 13:57:25 | 显示全部楼层 |阅读模式
  从昨天开始搞一个很简单的程序。就是一个按键控制灯的半亮与全亮与关。就是那么简单,预计半个小时的时间。可是花了我一天的时间,虽然现在样品可以出了,但是为什么出现这个问题,我还是不知道。有没有哪个给指点一下迷津。
程序如下:
#include <ny8.h>
#include "ny8_constant.h"


unsigned int    R_Quarter_VDD_DATA;       
unsigned char   R_Quarter_VDD_DATA_LB;       

unsigned  char bit_status = 0;


volatile __sbit   flash_status   = bit_status : 0;   //SOS标志
volatile __sbit   sw_on          = bit_status : 1;   //按键按下了
volatile __sbit   duty_down      = bit_status : 2;   //开始下降
volatile __sbit   down_status    = bit_status : 3;   //开始下降
volatile __sbit   sw_off         = bit_status : 4;   //按键按下了

unsigned int    down_time        = 0;//下降计时
unsigned int    down_tim         = 0;//下降计时
unsigned int    down_speed       = 0;//下降速度

unsigned int    sw_time          = 0;      //SW键按下时间
volatile unsigned char  led_status   = 0;   //亮灯模式:0:关  1:小亮  2:大亮

unsigned int           pwm_duty  = 0;       //全亮最大占空比

unsigned int   vol_val           = 1899;      //电池电压     上次读取有电池电压

unsigned int   flash_time        = 0;      //闪烁模式时的闪烁计时
unsigned int   flash_tim         = 0;      //闪烁时计时频率


#define        sw           PORTBbits.PB3    //按键

unsigned int   adc_time          = 0;   //监测电压计时

unsigned int   on_time           = 0;   //开机计时
unsigned char  on_sec            = 0;   //开机秒

volatile unsigned char   time    = 0;  //延时
//输出

#define        led_out      PORTAbits.PA2


unsigned char  off_time = 0;
unsigned char  read_status = 0;   //读取电压

/*=========================需要的变量===========================*/



#define  open_pwm3  0x83
#define  off_pwm3   0x02
       

#define UPDATE_REG(x)        __asm__("MOVR _" #x ",F")      //寄存器更新数据


void F_Quarter_VDD_Convert(char);
void F_wait_eoc(void);
void delay(int);
void delay_ms(unsigned int);
void io_int();



/*中断*/
void F_Quarter_VDD_Convert(char count)//1/4VDD电压检测
{
          char i;
       
          ADMD  = 0x90 | C_Quarter_VDD;                        // Select internal 1/4VDD as ADC input
          //C_Quarter_VDD是定义的0X0B:0000 1011 | 1001 0000 ADMD = 1001 1011
          //bit7:1开启ADC  bit4:1开启所有ADC通道
          //后四位:1011是将内部1/4VDD为模拟输入通道
          for(i=1;i<=count;i++)   //读取,
          {                              
              ADMDbits.START = 1;                                        // 开始AD转换
              F_wait_eoc();                                           //等待转换完成
              R_Quarter_VDD_DATA_LB += ( 0x0F & ADR); //先将低四位取出,放到一个定义变量
              R_Quarter_VDD_DATA    += ADD;     //将高八位取出。累计到高八位
          }
}
void F_wait_eoc(void)
{
    while(ADMDbits.EOC==0)   //EOC = 1时,AD才转换完成
    ;
}

void delay(int count)
{
        int i;
        for(i=1;i<=count;i++)
        ;
}
void delay_ms(unsigned int time)
{
    unsigned int i;
        for(i = 0;i < 1000;i++)
        {
                for(;time > 0;time--);
        }
}
void get_vddval()            //读取VDD电压
{
        ADMD  = C_ADC_En | C_ADC_CH_Dis | C_Quarter_VDD ;   //C_ADC_En:    0X80:1000 0000
        //C_ADC_CH_Dis:0X00;                              //C_Quarter_VDD 0X0B:0000 1011
    //即是开启ADC,并打开VDD/4通道
        ADVREFH = C_Vrefh_2V;           //内部2V基准  C_Vrefh_2V:0X00,后两BIT 00为内部2V基准
        ADR          = C_Ckl_Div1;        //250K         C_Ckl_Div8:0X10; ADR的BIT4/5为时钟选择,01:ADC时钟 = FCPU/8.2M/8 = 250K
    ADCR  = C_Sample_1clk | C_12BIT;        //C_Sample_1clk :0X00;
    //C_12BIT:0X03,ADCR = 0000 0011  BIT为1:ADC为12位
    //bit3/2为00:采样时钟是1个ADC时钟
   
        delay(25);        //delay 25个
       
        R_Quarter_VDD_DATA=R_Quarter_VDD_DATA_LB=0x00;  //选将存值清零
       
        F_Quarter_VDD_Convert(8);                        // 读取8次 1/4VDD
    R_Quarter_VDD_DATA <<= 4;                        //高八位左移四位,即是将高八位对位
    R_Quarter_VDD_DATA_LB &= 0xF0;                // 低四位值读出(读8次,,此句等下测试为什么要舍弃后四位的值
   
   
    R_Quarter_VDD_DATA += R_Quarter_VDD_DATA_LB; //// R_Quarter_VDD_DATA + R_Quarter_VDD_DATA_LB
    R_Quarter_VDD_DATA >>=3;    //读了八次,除以八
        vol_val =  R_Quarter_VDD_DATA;//将值取出
}
void isr(void) __interrupt(0)
{
//-------------T0中断--------------------------
    unsigned int  jg_time = 0;
    if(INTFbits.T0IF)
    {
                time++;
            if(time >= 200){time = 0;}
                if(sw_on)       //按键按下了
                {
                        sw_time++;  //计时累加
                        if(sw)//检测到按键松开了
                        {
                                if(sw_time < 2000)     //如果按下时间小于3秒,代表换档
                                {
                                        sw_time = 0;
                                        if(flash_status){flash_status = 0;}
                                        //如果刚才是SOS,关掉SOS
                                        else //不然的话就是换档
                                        {
                                            
                                            led_status++;
                                            if(led_status < 3)
                                            {
                                                T3CR1 = 0X83;
                                                on_time = 0;on_sec = 0;
                                                        duty_down = 0;
                                                        pwm_duty = 1023;
                                            }
                                               
                                                else if(led_status >= 3)
                                                {
                                                     led_status = 0;
                                                     T3CR1         = 0x00;led_out = 0;
                                                }//0:OFF 1:小亮。2.大亮。
                                        }
                                }
                                sw_on = 0;
                        }
                        if(sw_time >= 2000)   //一直没有松开达到3秒,那么,就是开SOS
                        {
                                sw_off = 0;
                                sw_time = 0;
                                if(flash_status == 0)//如果是SOS模式就啥也不干
                                {
                                        led_status = 0;
                                        flash_status = 1;
                                        flash_time   = 0;
                                        T3CR1  = 0x02;
                                        led_out = 1;
                                }
                sw_on = 0;                               
                        }
                }
/*==============================================================
功能:在需要亮灯的时候处理,电压检测时间。
变更:
编写:孙可友
日期:2020.06.01儿童节快乐
几个关键的数:
1.电压变化范围:2.8V~4.22V(1484~2159 = 675)
2.PWM变化范围:10~100%(25~250 = 225)
3.相对变化:实时PWM与电压对应关系应该为 PWM = 25+ ((电压-1433)*10/32)
==============================================================*/
        if(led_status > 0)   //亮灯
                {
                        if(read_status == 0) //5秒检测一次电压
                        {
                                adc_time++;
                            if(adc_time >= 5000)
                                {
                                    adc_time = 0;
                                        read_status = 1;
                            }                                       
                        }
                    if(duty_down == 0)
                        {
                                on_time++;
                                if(on_time >= 998)
                                {
                                        on_time = 0;
                                        on_sec++;
                                        if(on_sec >= 60)
                                        {
                                                duty_down = 1;
                                                down_status = 1;
                                                on_sec = 0;
                                        }
                                }
                        }
                        else if(duty_down)
                        {
                                if(down_status == 0)
                                {
                                        down_tim++;
                                        if(down_tim >= 998)
                                        {
                                            down_tim = 0;
                                            down_time++;
                                            if(down_time >= down_speed)
                                            {
                                                    down_time = 0;
                                                    down_status = 1;
                                            }
                                        }
                                       
                                }
                        }                               
                }
        if(flash_status)  //SOS
                {
                        flash_time++;
                        if(flash_time >= 100)    //5H爆闪
                        {
                                flash_time = 0;
                                led_out =! led_out;
                        }
                }
        INTFbits.T0IF = 0;                                                                //清T0中断标志
                   TMR0 = 5;               
            
        }
        if(INTFbits.T2IF)
        {
            INTFbits.T2IF = 0;
        }
       
//-------------PB变化中断--------------------------   
    if(INTFbits.PABIF)
    {
               
                if(sw == 0)    //如果是按下按键了
                {
                    delay_ms(1);
                    if(sw == 0)
                    {
                       sw_on = 1;sw_off = 0; sw_time = 0;  //如果不是其它中断引起的检测,那么证明是按键
                    }
                }     
                INTFbits.PABIF = 0;                //清PB口变化中断标志
    }
//-------------外部中断--------------------------   
    if(INTFbits.INT0IF)
    {
                   INTFbits.INT0IF = 0;                                                                //清外部中断标志
    }
//-------------T1中断--------------------------   
    if(INTFbits.INT1IF)
    {
                   INTFbits.INT1IF = 0;                                                            //清T1中断标志               
    }
   //INTF = 0x0;
}
void io_int(void)
{
     /*先设置输入还是输出*/
    IOSTA = 0X00;     //先将PA全部设为;输出
    //IOSTA |= (1<<4);  //为输入:PA5开关,PA7,充电
     
    IOSTB = 0X00;     //先将PB全部设为;输出
    IOSTB |= (1<<3);  // PB3:按键
     
   
     
     /*再设置上拉*/
   // PCON &= 0XEF; // APHCON = 0XDf;                               //打开PA5上拉   
    BPHCON &= 0XF7;                               //打开PB3上拉   
     
   //PORTB = 0;
    PORTA = 0;                                   // PortB Data Register = 0x00
     /*再设置管脚中断*/
         /*ADC*/
    /*=========================PWM===================================*/
        //TMRH  =   C_TMR1_Data_b9 | C_TMR1_Data_b8 ;//| C_PWM1_Duty_b9 | C_PWM1_Duty_b8 ;
        TM3RH  = 0XfC;//11001100(TM3 高1,DUTY3高
    TMR3   = 255;// 100%占空比时1023 ( TMR3[9:0]=3FFH )
        T3CR2  = 0b00001000;       
        // C_PS1_Dis:0X08  C_TMR3_ClkSrc_Inst:0X00: T3CR2 = 0000 1000:bit7/6:NC
        //bit5:0:指令时钟...bit4:0  EX_CKI0 脚上升沿时定时器 1 减一。
        //bit3:0 开启预分频
        //000:1/2分频
        //4分频   250K 除以周期250 = 1K的PWM
        //4M/4T  1/4分频:1M,除以周期1023 = 977HZ的PWM
       
        T3CR1         = 0X02 ;//| C_TMR3_En;
        //C_PWM1_Active_Hi :0X00;C_TMR3_Reload:0X02
        //T1CR1  = XXXX XX1X       
        //定时器 1 从重载的数值下数到 0x00。当下溢发生,定时器 1 从TMR1[9:0]重新载入数值并继续下数
       
    AWUCON   = 0x00;   
   // AWUCONbits.WUPA1 = 0;
    BWUCON   = 0x08;   //PB3
     
    INTEbits.PABIE = 1;       //打开端口
    INTFbits.PABIF = 0;      //先清除中断标志
    ENI();                                                                        //开总中断
     
}
void tm_int()
{
        INTEbits.T0IE = 0;                  //设置前先关掉中断
        T0MD = 0X01;                                                                //BIT7=0 选择指令时钟BIT6 = 0;是没用的。不开外部中断无影响
                                                                                                //BIT5=0 指令时钟。
                                                                                                //BIT0~BIT2=0    2分频  4000000/4/4 =一个指令4us,要用1mS的话,需要250个
        TMR0 = 5;                                                                //255-5 = 250
        INTEbits.T0IE = 1;INTFbits.T0IF = 0; PCON1 |= (1<<0);                                                 //开启T0;         //开T0中断       
/*
    INTEbits.T2IE  = 0;    //先将T2使能关闭
    T2CR2 = 0X00;  //0000 0001:T2选指令时钟(2M/2T),开启预分频,选择2分频。2000000/2/2 = 一个指令2US,需要100us的话,需要50个
    TMRH &=0X3f;
        TMR2 =   50;   //下数计时
        INTEbits.T2IE = 1;INTFbits.T2IF = 0;//使能中断
    T2CR1 = 0X03;//重装。并开启       
*/
}
/*
void time2_delay_01ms(unsigned char time_ms)//用Time2 做延时
{
    INTEbits.T2IE  = 0;    //先将T2使能关闭
    T2CR2 = 0X00;  //0000 0001:T2选指令时钟(2M/2T),开启预分频,选择4分频130。2000000/2/2 = 一个指令2US,需要100us的话,需要50个
    INTEbits.T2IE = 1;INTFbits.T2IF = 0;//使能中断
    TMRH &=0X3f;
        TMR2 =   5;   //下数计时
    T2CR1 = 0X03;//重装。并开启
    while(time_ms != 0)
    {
                TMRH &=0X3f;
        TMR2 = 5;   //下数计时
        while(INTFbits.T2IF != 1);//等待中断
        INTFbits.T2IF = 0;
        time_ms--;     
    }
    INTEbits.T2IE  = 0;
    T2CR1 = 0X00; //关闭Time2
}*/
/*=============================================
电量显示程序:参数为电量标志,0:为25,1:50,2:75,3:100
编写日期:2020.5.29
程序编写:孙可友
*/


void led_work()
{
        unsigned char i;
        unsigned char full_duty_hi = 0,full_duty_lo = 0,half_duty_hi = 0,half_duty_lo = 0; //PWM的高两位与低八位独立出来

    if(read_status == 1)
        {
                get_vddval();
                //vol_val = 2149;
                i = (vol_val - 1535 ) / 50;
                down_speed = i * i;
                read_status  = 0;
                adc_time = 0;
        }   //读一次VDD
       
        half_duty_hi = ((pwm_duty / 3) / 256);
        half_duty_lo = ((pwm_duty / 3) % 256);
        full_duty_hi = (pwm_duty  / 256);
        full_duty_lo = (pwm_duty % 256);
        if(led_status == 1)       {TM3RH &= 0XFC; TM3RH |= half_duty_hi; PWM3DUTY = half_duty_lo;}
        else if(led_status == 2)  {TM3RH &= 0XFC; TM3RH |= full_duty_hi; PWM3DUTY = full_duty_lo;}   
                //4.2V时应该为100%,3V时对应PWM为:25%,即:ADC:2149时为1023,1535时为257。
                //ADC变化范围:2149- 1383 = 766   DUTY变化范围:1023- 257 = 766
        if(duty_down)
        {
                if(down_status)
                {
                        if(pwm_duty > ((vol_val - 1383)+257)){pwm_duty -= 15;}
                        down_status = 0;down_time = 0;down_tim = 0;
                }
        }       
}

void main(void)
{
        DISI();                                                                        //关闭总中断
        PCON &= 0X7F;
    tm_int();       //定时器0初始化
    io_int();       //IO设定初始化,并设置中断,管脚中断,PWM
   
        //SLEEP();    //进入睡眠
    while(1)
    {
                if((led_status == 0)&&(flash_status == 0)&&(sw_on == 0)){if(led_status == 0){T3CR1 = 0x00;led_out = 0;SLEEP();}}
                if(led_status > 0){led_work();}
        }
}

因为需要读电池电压,所以选了九齐的带AD的NY8B062D。

以上程序是出样品的程序。测试是可以的,但是大家注意到没有:
死循环里:if(A && B && C){if(A){}}这种写法是不是太沙雕。。。
我一开始的程序是在动作里没有再次判断led_status ,竟然出现了led_status = 1时,不亮灯。并且会睡眠的情况(偶尔出现)。

我就去仿真,不让他睡眠,改成:
if((led_status == 0)&&(flash_status == 0)&&(sw_on == 0)){T3CR1 = 0x00;led_out = 0;}
else if(led_status > 0){led_work();}

此时,在防真时,还中偶尔出现此情况,虽然不睡眠了,还是不输出PWM,查看寄存器T3CR1,发现是0;
是谁动我的T3CR1。。我翻遍了整个程序中有可能动到T3CR1的地方,设置断点,啥都没用。

后来,我将T3CR1 = 0X83写到按键中断里去。我只要按下按键就开PWM后,才发现,是主程序 的while(1)里动了我的T3CR1,可是,
我判断led_status = 0时才进这个赋值的啊。。。于是,我在赋值前再判断一次,如果led_status确定等于0, 就让T3CR1的值清零。然后再加上掉电:
if((led_status == 0)&&(flash_status == 0)&&(sw_on == 0)){if(led_status == 0){T3CR1 = 0x00;led_out = 0;}SLEEP();}
这样后,就发现问题:仿真时,偶尔出现led_status = 1时不输出PWM,并且此时在睡眠,因仿真器在SLEEP下看不到各寄存器的状态,所以直接烧录芯片出来看,发现在睡眠时,T3CR1的值 并没有清零。(此现象可以通过灯珠在LEd_status = 2时,亮100%的状看出,是从33%慢慢亮上去的。
此时可以断定在主程序时,即使led_status = 1;那么程序也会进入到
if((led_status == 0)&&(flash_status == 0)&&(sw_on == 0)){T3CR1 = 0x00;led_out = 0;}执行。。。
是if里我判断太多了?还是什么情况?我真的第一次遇到这。。。

阿莫论坛20周年了!感谢大家的支持与爱护!!

如果想吃一顿饺子,就得从冰箱里取出肉,剁馅儿,倒面粉、揉面、醒面,擀成皮儿,下锅……
一整个繁琐流程,就是为了出锅时那一嘴滚烫流油的热饺子。

如果这个过程,禁不住饿,零食下肚了,饺子出锅时也就不香了……《非诚勿扰3》

出0入442汤圆

发表于 2020-8-1 15:21:55 来自手机 | 显示全部楼层
你这是中断和应用程序没有做好同步。比如=0时执行操作,那么cpu要先取数,然后随机进了中断,然后=1了。然后回到用户线程,往下走,一看是1,于是认为状态错误挂了。

要想写出良好的程序,切记一定要把中断的坑填好。中断里面尽量不要改动和主线程关系密切的变量,否则很容易出问题。取变量时可以先复制出来,再做判断,避免变量被意外修改。

出0入10汤圆

 楼主| 发表于 2020-8-1 15:58:34 | 显示全部楼层
wye11083 发表于 2020-8-1 15:21
你这是中断和应用程序没有做好同步。比如=0时执行操作,那么cpu要先取数,然后随机进了中断,然后=1了。 ...

OK,我再多多看看测试测试。一个端口中断,一个定时器中断,里面我也再看看,找找具体原因。多谢提醒

出0入0汤圆

发表于 2020-8-3 11:12:20 | 显示全部楼层
把这个变量unsigned  char bit_status = 0改为volatile bdata unsigned  char bit_status = 0试试?

出0入0汤圆

发表于 2020-8-6 00:26:40 | 显示全部楼层
可能是这个变量倍编译器优化了,每次取值都从缓存取,而不是变量对应的内存空间取值,可加个volatile在定义变量前 试试
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-28 23:05

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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