#include "reg51.h"
#include "intrins.h"

#define MAIN_Fosc		22118400L	//定义主时钟

#define D_EN_WDT			(1<<5)
#define	WDT_PS_Set(n)	WDT_CONTR = (WDT_CONTR & ~0x07) | (n & 0x07)		/* 看门狗定时器时钟分频系数设置 */
#define D_CLR_WDT			(1<<4)	/* auto clear	*/

sfr AUXR = 0x8E;
sfr INT_CLKO = 0x8F;
sfr P_SW1 = 0xa2;

sfr     CCON    =   0xd8;
sbit    CF      =   CCON^7;
sbit    CR      =   CCON^6;
sbit    CCF2    =   CCON^2;
sbit    CCF1    =   CCON^1;
sbit    CCF0    =   CCON^0;

sfr     CMOD    =   0xd9;
sfr     CL      =   0xe9;
sfr     CH      =   0xf9;
sfr     CCAPM0  =   0xda;
sfr     CCAP0L  =   0xea;
sfr     CCAP0H  =   0xfa;
sfr     PCA_PWM0 =  0xf2;
sfr     CCAPM1  =   0xdb;
sfr     CCAP1L  =   0xeb;
sfr     CCAP1H  =   0xfb;
sfr     PCA_PWM1 =  0xf3;
sfr     CCAPM2  =   0xdc;
sfr     CCAP2L  =   0xec;
sfr     CCAP2H  =   0xfc;
sfr     PCA_PWM2 =  0xf4;

sfr     P0M1    =   0x93;
sfr     P0M0    =   0x94;
sfr     P1M1    =   0x91;
sfr     P1M0    =   0x92;
sfr     P2M1    =   0x95;
sfr     P2M0    =   0x96;
sfr     P3M1    =   0xb1;
sfr     P3M0    =   0xb2;
sfr     P4M1    =   0xb3;
sfr     P4M0    =   0xb4;
sfr     P5M1    =   0xc9;
sfr     P5M0    =   0xca;

sfr P4   = 0xC0;
sfr P5   = 0xC8;
sfr P6   = 0xE8;
sfr P7   = 0xF8;

sfr     IAP_DATA    =   0xC2;
sfr     IAP_ADDRH   =   0xC3;
sfr     IAP_ADDRL   =   0xC4;
sfr     IAP_CMD     =   0xC5;
sfr     IAP_TRIG    =   0xC6;
sfr     IAP_CONTR   =   0xC7;
sfr     IAP_TPS     =   0xF5;

sfr     WDT_CONTR   =   0xc1;

sbit    key1    =   P5^4;
sbit    key2    =   P3^0;
sbit    LorH    =   P3^1;

typedef     unsigned char   u8;
typedef     unsigned int    u16;
typedef     unsigned long   u32;

//#define     MAIN_Fosc       24000000UL  //定义主时钟

#define 	PCA0			0
#define 	PCA1			1
#define 	PCA2			2

#define		divisor		1

unsigned int brightness = 512;
unsigned int cw = 512;
unsigned int brightness_CUR = 0;
unsigned int cw_CUR = 0;
unsigned char key1_time = 0;
unsigned char key2_time = 0;
unsigned char led_time = 0;
unsigned int eeprom_time = 0;

bit LED_Flag;
bit cw_Flag;
bit KEY1_Flag;
bit KEY2_Flag;
bit eeprom_Flag;
bit eeprom_LED;
bit eeprom_cw;

void UpdatePcaPwm(u8 PCA_id, u16 pwm_value)
{
	if(PCA_id == PCA0)
	{
		PCA_PWM0 = (PCA_PWM0 & ~0x32) | (u8)((pwm_value & 0x0300) >> 4) | (u8)((pwm_value & 0x0400) >> 9);
		CCAP0H = (u8)pwm_value;
	}
	else if(PCA_id == PCA1)
	{
		PCA_PWM1 = (PCA_PWM1 & ~0x32) | (u8)((pwm_value & 0x0300) >> 4) | (u8)((pwm_value & 0x0400) >> 9);
		CCAP1H = (u8)pwm_value;
	}
	else if(PCA_id == PCA2)
	{
		PCA_PWM2 = (PCA_PWM2 & ~0x32) | (u8)((pwm_value & 0x0300) >> 4) | (u8)((pwm_value & 0x0400) >> 9);
		CCAP2H = (u8)pwm_value;
	}
}
/*
void IapIdle()
{
    IAP_CONTR = 0;                              //关闭IAP功能
    IAP_CMD = 0;                                //清除命令寄存器
    IAP_TRIG = 0;                               //清除触发寄存器
    IAP_ADDRH = 0x80;                           //将地址设置到非IAP区域
    IAP_ADDRL = 0;
}

char IapRead(int addr)
{
    char dat;

		EA = 0;
		_nop_();
	
    IAP_CONTR = 0x80;                           //使能IAP
    IAP_TPS = 22;                               //设置等待参数22.1184MHz
    IAP_CMD = 1;                                //设置IAP读命令
    IAP_ADDRL = addr;                           //设置IAP低地址
    IAP_ADDRH = addr >> 8;                      //设置IAP高地址
    IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
    _nop_();
		_nop_();
		_nop_();
		_nop_();
    dat = IAP_DATA;                             //读IAP数据
    IapIdle();                                  //关闭IAP功能

		EA = 1;
		_nop_();
	
	
    return dat;
}

// 从指定地址读取16位整数
unsigned int ReadData(unsigned int addr)
{
    unsigned int dat = 0;
    // 读取低字节
    dat = IapRead(addr);
    // 读取高字节
    dat |= ((u16)IapRead(addr + 1) << 8);
    return dat;
}

void IapProgram(int addr, char dat)
{
		EA = 0;
		_nop_();
	
    IAP_CONTR = 0x80;                           //使能IAP
    IAP_TPS = 22;                               //设置等待参数22.1184MHz
    IAP_CMD = 2;                                //设置IAP写命令
    IAP_ADDRL = addr;                           //设置IAP低地址
    IAP_ADDRH = addr >> 8;                      //设置IAP高地址
    IAP_DATA = dat;                             //写IAP数据
    IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
    _nop_();
		_nop_();
		_nop_();
		_nop_();
    IapIdle();                                  //关闭IAP功能
	
		EA = 1;
		_nop_();
	
}

// 存储16位整数到指定地址
void StoreData(unsigned int addr, unsigned int dat)
{
    // 写入低字节
    IapProgram(addr, dat & 0xFF);
    // 写入高字节
    IapProgram(addr + 1, (dat >> 8) & 0xFF);
}

void IapErase(int addr)
{
		EA = 0;
		_nop_();
	
	
    IAP_CONTR = 0x80;                           //使能IAP
    IAP_TPS = 22;                               //设置等待参数22.1184MHz
    IAP_CMD = 3;                                //设置IAP擦除命令
    IAP_ADDRL = addr;                           //设置IAP低地址
    IAP_ADDRH = addr >> 8;                      //设置IAP高地址
    IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
    _nop_();                                    //
		_nop_(); 
		_nop_(); 
		_nop_(); 
    IapIdle();                                  //关闭IAP功能
	
		EA = 1;
		_nop_();
	
}
*/
// 关闭IAP功能并设置安全地址
void IapIdle() 
{
    IAP_CONTR = 0;      // 禁止IAP操作
    IAP_CMD   = 0;      // 清除命令寄存器
    IAP_TRIG  = 0;      // 防止误触发
    IAP_ADDRH = 0x80;   // 设置非EEPROM区地址（安全地址）
    IAP_ADDRL = 0;      // 地址低字节
}

// 从指定地址读取单字节
u8 IapReadByte(u16 addr) 
{
    u8 dat;
    EA = 0;                         // 关中断保护
    IAP_CONTR = 0x80;               // 使能IAP（最高位使能）
    IAP_TPS = 22;                   // 等待参数（22.1184MHz时钟）
    IAP_CMD = 1;                    // 读命令
    IAP_ADDRL = addr;               // 地址低字节
    IAP_ADDRH = addr >> 8;          // 地址高字节
    
    IAP_TRIG = 0x5A;
    IAP_TRIG = 0xA5;
    _nop_(); 
		_nop_(); 
		_nop_(); 
		_nop_();
    
    dat = IAP_DATA;     // 读取数据
    IapIdle();          // 关闭IAP
    EA = 1;             // 开中断
    return dat;
}

// 向指定地址写入单字节
void IapProgramByte(u16 addr, u8 dat) 
{
    EA = 0;                         // 关中断保护
    IAP_CONTR = 0x80;               // 使能IAP
    IAP_TPS = 22;                   // 等待参数
    IAP_CMD = 2;                    // 写命令
    IAP_ADDRL = addr;
    IAP_ADDRH = addr >> 8;
    IAP_DATA = dat;                 // 写入数据
    
    IAP_TRIG = 0x5A;
    IAP_TRIG = 0xA5;
    _nop_(); 
		_nop_(); 
		_nop_(); 
		_nop_();
    
    IapIdle();          // 关闭IAP
    EA = 1;             // 开中断
}

// 擦除指定扇区（512字节）
void IapEraseSector(u16 sectorAddr) 
{
    EA = 0;                         // 关中断保护
    IAP_CONTR = 0x80;               // 使能IAP
    IAP_TPS = 22;                   // 等待参数
    IAP_CMD = 3;                    // 擦除命令
    IAP_ADDRL = sectorAddr;         // 扇区内任意地址
    IAP_ADDRH = sectorAddr >> 8;
    
    IAP_TRIG = 0x5A;
    IAP_TRIG = 0xA5;
    _nop_(); 
		_nop_(); 
		_nop_(); 
		_nop_();
    
    IapIdle();          // 关闭IAP
    EA = 1;             // 开中断
}

// 从指定地址读取16位数据
u16 Read16BitData(u16 addr) 
{
    u16 lowByte = IapReadByte(addr);      // 读取低字节
    u16 highByte = IapReadByte(addr + 1); // 读取高字节
    return (highByte << 8) | lowByte;           // 合并为16位数据
}

// 向指定地址写入16位数据
void Store16BitData(u16 addr, u16 dta) 
{
    IapProgramByte(addr, dta & 0xFF);         // 写入低字节
    IapProgramByte(addr + 1, (dta >> 8) & 0xFF); // 写入高字节
}

void TM0_Isr() interrupt 1	//1ms
{
		if(!key1)
			key1_time++;
		
		if(LorH && !key2)
			key2_time++;
		
		led_time++;
		
		if(eeprom_Flag)
			eeprom_time++;
		
}

void Delay8ms(void)	//@22.1184MHz
{
	unsigned char data i, j;

	_nop_();
	_nop_();
	i = 230;
	j = 202;
	do
	{
		while (--j);
	} while (--i);
}


void PWM_Server(void)
{
	u32 warm_duty = 0;	
	u32 cold_duty = 0;
	u16 temp_led  = 0;
	u16 temp_cw   = 0;
	
	if(led_time >= 8)
	{
		led_time = 0;
		// 亮度过渡
		if(brightness_CUR != brightness) 
		{
			if(brightness_CUR < brightness) 
			{
				brightness_CUR += divisor;
				if(brightness_CUR > brightness) 
					brightness_CUR = brightness;  
			} 
			else 
			{
				brightness_CUR -= divisor;
				if(brightness_CUR < brightness) 
					brightness_CUR = brightness;
			}
			
			if(LorH)
			{
				warm_duty = (u32)brightness_CUR * cw_CUR / 1023;
				cold_duty = brightness_CUR - warm_duty;
					
				// 更新PWM输出,PCA的PWM是左对齐
				UpdatePcaPwm(PCA0,1023 - (u16)cold_duty);
				UpdatePcaPwm(PCA1, 1023 - (u16)warm_duty);
			}
			else
			{
				UpdatePcaPwm(PCA1,1023 - brightness_CUR);
			}
			
			eeprom_LED = 1;
		}
			
		if(LorH)
		{
			// 色温过渡
			if (cw_CUR != cw) 
			{
				if(cw_CUR < cw) 
				{
					cw_CUR += divisor;
					if(cw_CUR > cw) 
						cw_CUR = cw;
				} 
				else 
				{
					cw_CUR -= divisor;
					if(cw_CUR < cw) 
						cw_CUR = cw;
				}
				
				warm_duty = (u32)brightness_CUR * cw_CUR / 1023;
				cold_duty = brightness_CUR - warm_duty;
					
				// 更新PWM输出,PCA的PWM是左对齐
				UpdatePcaPwm(PCA0,1023 - (u16)cold_duty);
				UpdatePcaPwm(PCA1, 1023 - (u16)warm_duty);
				eeprom_cw = 1;
			}
		}
		
		if(eeprom_time > 30000)
		{
			eeprom_time = 0;
			eeprom_Flag = 0;
			
			if(eeprom_LED)
			{
				eeprom_LED = 0;
				temp_led = Read16BitData(0x0000);
				if(temp_led != brightness)
				{
					IapEraseSector(0x0000);
					Delay8ms();
					Store16BitData(0x0000, brightness);
					
					temp_led = Read16BitData(0x0000);
						while(temp_led != brightness);
							;
				}
			}
			
			if(LorH)
			{
				if(eeprom_cw)
				{
					eeprom_cw = 0;
					temp_cw = Read16BitData(0x0200);
					if(temp_cw != cw)
					{
						IapEraseSector(0x0200);
						Delay8ms();
						Store16BitData(0x0200, cw);
						
						temp_cw = Read16BitData(0x0200);
						while(cw != temp_cw);
							;
					}
				}
			}
		}
	}
	
	/*
	warm_duty = (u32)brightness * cw / 1023;
  cold_duty = brightness - warm_duty;
    
  // 更新PWM输出,PCA的PWM是左对齐
  UpdatePcaPwm(PCA0,1023 - (u16)cold_duty);
  UpdatePcaPwm(PCA1, 1023 - (u16)warm_duty);
	*/
}

/********************* 看门狗初始化 *************************/
void WDT_Inilize(void)
{
	WDT_CONTR = D_EN_WDT;	//使能看门狗

	WDT_PS_Set(7);	//看门狗定时器时钟分频系数		WDT_SCALE_2,WDT_SCALE_4,WDT_SCALE_8,WDT_SCALE_16,WDT_SCALE_32,WDT_SCALE_64,WDT_SCALE_128,WDT_SCALE_256
	WDT_CONTR |= 0x08;	//IDLE模式继续计数
}

/********************* 清除看门狗函数 *************************/
void WDT_Clear(void)
{
	WDT_CONTR |= D_CLR_WDT;    // 喂狗
}

void main()
{
	u16 temp_led  = 0;
	u16 temp_cw   = 0;
	
	P0M0 = 0x00;
	P0M1 = 0x00;
	P1M0 = 0x00;
	P1M1 = 0x00;
	P2M0 = 0x00;
	P2M1 = 0x00;
	P3M0 = 0xe0;
	P3M1 = 0x00;
	P4M0 = 0x00;
	P4M1 = 0x00;
	P5M0 = 0x00;
	P5M1 = 0x00;
	
	//定时器0
	TMOD = 0x00;                                //模式0
  TL0  = 0xcd;                                 //[TH0,TL0]=65536-22.1184M*1000/12*1(1MS)
  TH0  = 0xf8;
  TR0  = 1;                                    //启动定时器
  ET0  = 1;                                    //使能定时器中断
  EA  = 1;
	
	P3 |= 0x02; 
	P5 &= ~0x10;

	P3 &= ~0x08; 
	P3 &= ~0x04;
	

  P3M0 |= 0x08; P3M1 &= ~0x08; //P3.3推挽模式
	if(LorH)
		P3M0 |= 0x04; P3M1 &= ~0x04; //P3.2推挽模式

	
  P_SW1 = 0x00; //ECI/P1.2, CCP0/P1.1, CCP1/P1.0, CCP2/P3.7
	//P_SW1 = 0x10; //ECI_2/P3.4, CCP0_2/P3.5, CCP1_2/P3.6, CCP2_2/P3.7
	//P_SW1 = 0x20; //ECI_3/P2.4, CCP0_3/P2.5, CCP1_3/P2.6, CCP2_3/P2.7
	
	CCON = 0x00;
	//CMOD = 0x04; //PCA 时钟为定时器0的溢出脉冲
	CMOD = 0x08; //PCA 时钟为系统时钟
	CL = 0x00;
	CH = 0x00;

	//－－ 10 位 PWM－－	
	if(LorH)
	{
		CCAPM0 = 0x02; 	 //PCA 模块 0 为 PWM 工作模式
		PCA_PWM0 = 0xc0; //PCA 模块 0 输出 10 位 PWM
		CCAP0L = 0;
		CCAP0H = 0;
	}
	
	CCAPM1 = 0x02; 	 //PCA 模块 1 为 PWM 工作模式
	PCA_PWM1 = 0xc0; //PCA 模块 1 输出 10 位 PWM
	CCAP1L = 0;
	CCAP1H = 0;
	//CR = 1; //启动 PCA 计时器

	P5M0 &= ~0x10; P5M1 &= ~0x10;	//使能P5.4输入脚
	if(LorH)
		P3M0 &= ~0x01; P3M1 &= ~0x01; //使能P3.0输入脚

	UpdatePcaPwm(PCA1,1023);
	if(LorH)
		UpdatePcaPwm(PCA0,1023); 
	
	CR = 1; //启动 PCA 计时器
	
	P3M0 &= ~0x02; P3M1 &= ~0x02; //使能P3.1输入脚

	brightness = 0;
	cw = 0;
	brightness_CUR = 0;
  cw_CUR = 0;
	LED_Flag = 0;
	KEY1_Flag = 0;
	cw_Flag = 0;
	KEY2_Flag = 0;
	key1_time = 0;
	key2_time = 0;
	led_time = 0;
	eeprom_time = 0;
	eeprom_Flag = 0;
	eeprom_LED = 0;
  eeprom_cw = 0;
	
	temp_led = Read16BitData(0x0000);
	temp_cw = Read16BitData(0x0200);
	
	if((temp_led < 0) || (temp_led > 1023))
		brightness = 512;
	else
		brightness = temp_led;
	
	if((temp_cw < 0) || (temp_cw > 1023))
		cw = 512;
	else
		cw = temp_cw;
	
	WDT_Inilize();
	
  while (1)
	{
		if(!key1)
		{
			if(key1_time >= 8)
			{
				KEY1_Flag = 1;
				key1_time = 0;
				if(LED_Flag && (brightness < 1023))
				{
					brightness += divisor;
					if(brightness >= 1023)
						brightness = 1023;
				}
				else if(!LED_Flag && (brightness > 51))
				{
					brightness -= divisor;
					if(brightness <= 51)
						brightness = 51;
				}
				
				eeprom_time = 0;
				eeprom_Flag = 1;				
			}
		}
		else
		{
			if(LED_Flag && KEY1_Flag)
				LED_Flag = 0;
			else if(KEY1_Flag && !LED_Flag)
				LED_Flag = 1;
			
			KEY1_Flag = 0;
			key1_time = 0;
		}
		
		if(LorH)
		{
			if(!key2)
			{
				if(key2_time >= 8)
				{
					KEY2_Flag = 1;
					key2_time = 0;		
					if(cw_Flag && (cw < 1023))
					{
						cw += divisor;
						if(cw >= 1023)
							cw = 1023;
					}
					else if(!cw_Flag && (cw > 0))
					{
						cw -= divisor;
						if(cw <= 0)
							cw = 0;
					}
					
					eeprom_time = 0;
					eeprom_Flag = 1;				
				}
			}
			else
			{
				if(cw_Flag && KEY2_Flag)
					cw_Flag = 0;
				else if(KEY2_Flag && !cw_Flag)
					cw_Flag = 1;
				
				KEY2_Flag = 0;
				key2_time = 0;				
			}
		}
			
		PWM_Server();
		WDT_Clear();
	}	
}

