|
从昨天开始搞一个很简单的程序。就是一个按键控制灯的半亮与全亮与关。就是那么简单,预计半个小时的时间。可是花了我一天的时间,虽然现在样品可以出了,但是为什么出现这个问题,我还是不知道。有没有哪个给指点一下迷津。
程序如下:
#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》
|