STM32F103C8T6 标准库,RTC时钟光唤醒小夜灯(无屏幕)

该项目基于正点原子精英板的模板更改配置再自行编写文件得来

适配C8T6的一个文章是STM32F103ZET6代码移植到F103C8T6上的方法及注意事项-CSDN博客

 尤其注意Systeminit();以及对应的三行取消注释,没有这个初始化计延时会不准

主要功能是

1.传统长按、短按长条形小夜灯的所有功能,但是还没找到合适的触摸按键或侧贴按钮

2.使用NEC协议来遥控灯的亮度(按键数字0到9),后续会加上定时

3.使用USB转TTL连接并使用上位机XCOM可以得到按键状态(短按、长按、按下次数)、当前设备时间并可以发送类似于20231113060900 年月日时分秒14位数据来修正设备时间,设备修正后每5s返回一次时间

4.可以使用按键来进行设置,设备的早晨光唤醒时间初始化设置为6.00,可以连按四下进入光唤醒时间设置模式,进入后红色LED灯会亮3s左右后熄灭,之后每次连按3次,(可以通过控制的灯条以及红色LED灯来判断按下的次数以及是否成功,可能是按键消抖没做好有时候会少计算一次如:连按四下实际上记录的是三次)光唤醒设定的时间就延迟10min(还没做提前的),并且每次加10min(连续按下三次)红色LED灯会对应亮灭,连按五下按下设置好时间后会自动写入Flash并退出编辑模式,之后哪怕断电也可以记住设置的时间(目前仅支持设置一个时间因为除了早上其余时间光唤醒似乎没用)

5.光唤醒亮后按一下按键就可以关掉,两个按键目前只做了一个按键的功能,两一个后续做一做或者哪位大佬改改开了也行

代码模板可以套用正点原子的红外遥控模板用来接收红外信号,代码写的很丑陋只是实现了功能,还没优化。
网盘链接宿舍小夜.zip - 蓝奏云 (lanzouj.com)icon-default.png?t=N7T8https://wwwq.lanzouj.com/i1Nmz1epo67i

// mian.c文件

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "pwm.h"
#include "remote.h"
#include "rtc.h"
#include "stmflash.h"

// PB9 红外输入 PB0 PB5 LED灯控制 PA1 PA2中断按键 PA10 PA9 串口输入 PC13 LED灯
 int main(void)
 {		
	u16 t, len, times=0; //串口接收   
	u8 key_event=1,key_preevent,key_state=0,keycnt=0,remote; //按键、红外信号存储
	u8 add=0,change_add=0;//加减亮度切换
 	u16 led0pwmval=450,autopwmval=900; //两种亮度控制
	u8 open_hour=6,open_min=0,open_sec=0,open_once=1,change_opentime=1;//灯开启的小时分钟 秒 是否开启 是否更换开始时间 
	u8 changeled=1, change_longled=0,to_change=0;
	u16 flashval = 600;
	SystemInit();
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 //串口初始化为115200
 	LED_Init();			     //LED端口初始化
	Key_Init();
	RTC_Init();
	Remote_Init();	
	TIM3_PWM_Init(899,0);	 //不分频。PWM频率=72000000/900=80Khz
	flashval=Flash_Read();
	delay_ms(500);
	if(flashval >= 500){
		//printf("mainflashval:%d \r\n",flashval);
		open_hour = flashval/100; 
		open_min =  flashval%100;
		printf("小时%d:分%d",open_hour,open_min);
	}		 


	 //flash读取光亮的时间
   	while(1)
	{	
		
		remote=Remote_Scan();
		key_state=Get_State();
		keycnt=Get_Cnt();		
		if(key_state==1){
			key_event=Get_Event();
			if(key_event==1){
				switch(add){
					case 0:	led0pwmval+=5;
							if(led0pwmval > 903 &&led0pwmval < 3000 ){ led0pwmval =  903; }
							LED_TIM_SetCompare2(change_longled,led0pwmval); //第一个参数时选择亮的灯,第二个参数是亮度			
							break;
					case 1:	led0pwmval-=5;
							if(led0pwmval <2 || led0pwmval > 4000 )	led0pwmval =  2;    
							LED_TIM_SetCompare2(change_longled,led0pwmval); //第一个参数时选择亮的灯,第二个参数是亮度
							break;				
				}
				delay_ms(10);
				change_add=1;
			}	
		}
		else if(key_state==0){
			if(key_event==1){ key_event=0;	if(change_add==1)	add=add==0?1:0;	}	//如果是长按,切换亮度增减
			else if( key_event==2){	//单独按下一次则切换灯,同时可以作为更改闹光时间的一个依据
				key_event=0;
				change_longled++;
				change_longled=change_longled==4?0:change_longled;
				LED_TIM_SetCompare2(change_longled,led0pwmval); //第一个参数时选择亮的灯,第二个参数是亮度
				printf("open_hour%d:open_min%d",open_hour,open_min);
			}
		}
		
		switch(remote) //红外接收
		{
			case 104:	TIM_SetCompare2(TIM3,0);	TIM_SetCompare3(TIM3,0);	LED0=0;		break;		  
			case 152:	TIM_SetCompare2(TIM3,100);	TIM_SetCompare3(TIM3,100);	LED0=0;		break;	   
			case 176:	TIM_SetCompare2(TIM3,200);	TIM_SetCompare3(TIM3,200);	LED0=0;		break;	    
			case 48:	TIM_SetCompare2(TIM3,300);	TIM_SetCompare3(TIM3,300);	LED0=0;		break;		    
			case 24:	TIM_SetCompare2(TIM3,400);	TIM_SetCompare3(TIM3,400);	LED0=0;		break;		    
			case 122:	TIM_SetCompare2(TIM3,500);	TIM_SetCompare3(TIM3,500);	LED0=0;		break;		  
			case 16:	TIM_SetCompare2(TIM3,600);	TIM_SetCompare3(TIM3,600);	LED0=0;		break;			   					
			case 56:	TIM_SetCompare2(TIM3,700);	TIM_SetCompare3(TIM3,700);	LED0=0;		break;	 
			case 90:	TIM_SetCompare2(TIM3,800);	TIM_SetCompare3(TIM3,800);	LED0=0;		break;
			case 66:	TIM_SetCompare2(TIM3,900);	TIM_SetCompare3(TIM3,900);	LED0=0;		break; 
		}

		if(USART_RX_STA&0x8000)
		{	
			oledcom=1;
			len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
			printf("\r\n您发送的消息为:\r\n\r\n");
			for(t=0;t<len;t++)
			{
				USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据		
				while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
			}
			flashTim=1;
			USART_RX_STA=0;
			Set_NewTime(USART_RX_BUF, len);//接收到串口信息修正时间
		}else{
			times++;					
			if(times%500==0)	printf("时间为%d %d %d %d %d %d\r\n",rtc_clock.year,rtc_clock.mon,rtc_clock.day,rtc_clock.hour,rtc_clock.min,rtc_clock.sec);	
			delay_ms(10);   
		}
		
		if(rtc_clock.hour==open_hour && rtc_clock.min == open_min && (rtc_clock.sec >=0 && rtc_clock.sec <= 2)) open_once=1; //到时间了可以开灯
		if(rtc_clock.hour==open_hour&&open_once==1){
			if(rtc_clock.min >= open_min && rtc_clock.min <= (open_min+1) ){
				if(open_once == 1){
					TIM_SetCompare2(TIM3,0);
					TIM_SetCompare3(TIM3,0);
				}
				if(key_state==1) key_event=Get_Event();//触发按键关灯	
				if(key_event==2||key_event==3){
					key_event=0;
					open_once=0;
					TIM_SetCompare2(TIM3,905);
					TIM_SetCompare3(TIM3,905);
				}
				if(rtc_clock.min==(open_min+1)&&(rtc_clock.sec>=0&&rtc_clock.sec<=5)){ //过时间了可以关灯
					TIM_SetCompare2(TIM3,905);
					TIM_SetCompare3(TIM3,905);
					open_once=0;
				}
			}	
		}

		if(keycnt==4 && change_opentime==1){
			printf("开始修改时间");	
			oledcom=1;
			change_opentime=0; //不再进入这个判断而是进入修改时间判断
			keycnt=Get_Cnt();
		}
		else if(change_opentime==0){	
			if(keycnt==3){ 	//按下三次时间加十分钟灯切换亮暗
				oledcom=oledcom==2?3:2;
				open_min+=10;
				if(open_min>=60){ open_min=0; open_hour++; }
			}
			if(keycnt==5){ 	//确认更改时间并退出3
				printf("当前狗叫时间为%d时%d分",open_hour,open_min);
				oledcom=1;//按下五次开灯6s后关闭
				change_opentime=1;//按下五次确认时间并关闭,进入按下四次判断是否修改时间
				Flash_Write(open_hour,open_min);//写入数据
				printf("写入数据\r\n");
				delay_ms(100);	
			}	
		}
		if(keycnt==7){
			printf("已经数据\r\n");
			Flash_Read();//读取数据
			delay_ms(100);	
		}
		LED_Command(oledcom,times);
	}
}
 
//#define KEY_NULL   0    //无事件
//#define KEY_LONG   1    //长按事件
//#define KEY_SHORT  2    //短按事件
//#define KEY_DOUBLE 3    //连按事件
//led.c文件
#include "led.h"

u8 oledcom=0;
//初始化PB5和PE5为输出口.并使能这两个口的时钟		    
//LED IO初始化
void LED_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);	 //使能PB,PE端口时钟
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;				 //LED0-->PB.5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOC, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB.5
 GPIO_SetBits(GPIOC,GPIO_Pin_13);						 //PB.5 输出高

// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;	    		 //LED1-->PE.5 端口配置, 推挽输出
// GPIO_Init(GPIOE, &GPIO_InitStructure);	  				 //推挽输出 ,IO口速度为50MHz
// GPIO_SetBits(GPIOE,GPIO_Pin_5); 						 //PE.5 输出高 
}
 
void LED_Command(u8 com,u8 times){
	if(com==1) 	 LED0=0;
	if(times%600 == 0 && LED0 == 0 && com==1){oledcom=0;	LED0=1;	}
	
	if(com==2)	 LED0=0;
	if(com==3) 	 LED0=1;
}


//led.h文件

#ifndef __LED_H
#define __LED_H	 
#include "sys.h"

#define LED0 PCout(13)// PB5
//#define LED1 PEout(5)// PE5	
extern u8 oledcom;
void LED_Init(void);//初始化

		 				    
#endif
//key.c文件
#include "stm32f10x.h"
#include "key.h"
#include "sys.h" 
#include "delay.h"
#include "timer.h"
#include "usart.h"
struct KEY
{
    u8 key_prevent;    //前一次按键事件
	u8 key_event;      //当前按键事件
	u8 key_state;      //按键状态 按下或松开
	u8 key_cnt;        //按键按下的次数
	u8 key_continue;   //按键按下的时间
	u8 key_idle;       //按键松手的时间
	u8 key_longflag;
	u8 key_flag;       //按键状态发生改变的标志
	u8 key_event_flag; //产生一次按键事件的标志
	u8 key_resault;
}key={KEY_NULL,KEY_NULL,KEY_NONE,0,0,0,1,0,0,0};//结构体初始化

u8 once=0;

void Key_Init(void)
{
	Exit_GPIO_Config();
	TIM2_Config();
	NVIC_Exit_GPIO_Config();
	NVIC_TIM2_Config();
	TIM_Cmd(TIM2, ENABLE);	//使能时钟
}	


void EXTI1_IRQHandler()
{
	delay_ms(20);                 //按键消抖

         if(KEY_PIN==0)                //KEY_PIN==0代表按键按下
		{
			key.key_flag=1;           //代表按键状态发生改变的标志
			key.key_resault=1;		  //可以在松开按键部分得出一次结果
			key.key_state=KEY_DOWN;   //按键状态
			key.key_continue=0;       //按键按下的时间清零
		}else                          //else的情况就是按键松手  
		{
			key.key_longflag=1;
			key.key_flag=1;            
			key.key_state=KEY_UP;
			key.key_idle=0;	// 按键松手的时间清零		
		}
        EXTI_ClearITPendingBit(EXTI_Line1);  // 清除中断标志位

}
void EXTI2_IRQHandler()
{
	delay_ms(20);                 //按键消抖

         if(KEY2_PIN==0)                //KEY_PIN==0代表按键按下
		{
			key.key_flag=1;           //代表按键状态发生改变的标志
			key.key_resault=1;		  //可以在松开按键部分得出一次结果
			key.key_state=KEY_DOWN;   //按键状态
			key.key_continue=0;       //按键按下的时间清零
		}else                          //else的情况就是按键松手  
		{
			key.key_longflag=1;
			key.key_flag=1;            
			key.key_state=KEY_UP;
			key.key_idle=0;	// 按键松手的时间清零		
		}
        EXTI_ClearITPendingBit(EXTI_Line2);  // 清除中断标志位

}
 
void TIM2_IRQHandler(void)        //定时器中断每隔10ms进入一次中断
{
	Key_Process();                //每隔10ms调用一次
	TIM_ClearITPendingBit(TIM2 , TIM_IT_Update);//清除中断标志位
}

u8 Get_Event(){ //其他函数调用按键事件
	return key.key_event;
}
u8 Get_State(){ //其他函数调用按键事件

	return key.key_state;
	once=0;
}
u8 Get_Cnt(){
	u8 tmp;
	if(key.key_cnt>=2 && key.key_idle >= KEY_IDLE && key.key_event == KEY_DOUBLE){
		tmp=key.key_cnt;
		key.key_cnt=1;
		return tmp;		
	}
	return 0;
}
void Key_Process(void)
{
		switch(key.key_state)
		{
			case KEY_DOWN://按键按下进入
			{
				if(key.key_continue<KEY_CONTINUE){key.key_continue++;}
                //每隔10ms,key.key_continue++,if判断防止它一直增加
                //当按下的时间超出了设定的值后,说明此次为长按
				if(key.key_continue>=KEY_CONTINUE)
				{
					if(key.key_cnt>1)//这个if判断是防止连按之后在长按会被判定为一次长按
					{
						printf("%s","1DOUBLE");//这个if判断把连按之后的长按判断为连按
						printf("Cnt:%d\r\n",key.key_cnt);//的一部分
                        key.key_event=KEY_DOUBLE;
                        key.key_prevent=key.key_event;
						once=1;
					}
					else if(key.key_cnt<=1 && key.key_longflag==1)//正常的长按
					{
						key.key_event=KEY_LONG;    //当按下的时间超出了设定的值后判断为长按
						key.key_longflag = 0;     //产生了一次按键事件
						key.key_prevent=key.key_event;
						printf("%s","2LONG  ");    //打印到OLED屏上观察
						once=1;
					}
 
				}
				if(key.key_flag)//按键按下就会进入这个if判断
				{
					key.key_flag=0;//清除按键状态发生改变的标志位
					if(key.key_idle<KEY_IDLE){key.key_cnt++;}
                    //判断松手的时间是否超时,没超时就说明是连按,按键按下次数加一
					else{key.key_cnt=1;}//否则已超时,此次只能为单机或长按
                    //单击和连击的判断放到下面松手的部分通过按键次数来判断
					key.key_event=KEY_SHORT;
				}
				break;
			}
            //按键松开
			case KEY_UP:
			{
              //清楚标志位,其实这一部分没有这个也一样
				if(key.key_idle<KEY_IDLE){key.key_idle++;}//松手时间小于设定值就增加
				if(key.key_idle>=KEY_IDLE&&key.key_flag==1)//大于松手时间,代表按键事件产生
				{
					key.key_flag=0;
					once=0;
                    //这个if判断防止长按被识别为单击,应为这一部分是靠按下次数来判断
                    //单击或长按,而单击和长按按下次数都为1,但长按的按键事件一定会判定为
                    //长按,通过key.key_event判断当前是长按还是单击
					if(key.key_cnt==1 && key.key_event!=KEY_LONG)
					{
						key.key_event=KEY_SHORT;
                        key.key_prevent=key.key_event;
						printf("%s","3SHORT ");
						printf("Cnt:%d\r\n",key.key_cnt);
						once=1;
					}
					else if(key.key_cnt>1)//按下次数为>1次的情况
					{
						if(key.key_prevent==KEY_LONG)
                        //这个if判断防止前一次的长按被当作连按的一部分
							key.key_cnt--;//减去长按的一次计数
						if(key.key_cnt==1)//一次说明为单击
						{
							key.key_event=KEY_SHORT;
                            key.key_prevent=key.key_event;
							printf("%s","4SHORT ");
							printf("Cnt:%d\r\n",key.key_cnt);
							once=1;
						}
						else//大于1次为连击
						{
							key.key_event=KEY_DOUBLE;
							printf("%s","5DOUBLE");
							printf("Cnt:%d\r\n",key.key_cnt);
							once=1;
						}
					}
				}
				//key.key_state=KEY_NONE;

			}
		}
		
	}

//key.h文件
#ifndef __KEY_H
#define __KEY_H
 
#include "sys.h"

 
#define KEY_NULL   0    //无事件
#define KEY_LONG   1    //长按事件
#define KEY_SHORT  2    //短按事件
#define KEY_DOUBLE 3    //连按事件
 
#define KEY_DOWN   1    //按键按下状态
#define KEY_UP     0    //按键松手状态
#define KEY_NONE   2    //按键初始状态
 
#define KEY_CONTINUE 50 //按下的最长时间
#define KEY_IDLE     40 //松手最长时间
#define KEY_PIN    PAin(1)//PB10用作按键输入
#define KEY2_PIN    PAin(2)//PB11用作按键输入
extern u8 once;
extern u8 resu;
void Key_Init(void);
//int Key_Scan(void);
void Key_Process(void);
 
#endif
//pwm.c文件
#include "pwm.h"
#include "led.h"
#include "usart.h"
#include "rtc.h"
//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 计数到5000为500ms
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
 
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断

	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

	TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设
							 
}
//定时器3中断服务程序
//void TIM3_IRQHandler(void)   //TIM3中断
//{
//	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
//	{
//		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
//		LED0=!LED0;
//	}
//}
// 

//TIM3 PWM部分初始化 
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//使能定时器3时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟
	
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5    
 
   //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形	GPIOB.5
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_0; //TIM_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
 
   //初始化TIM3
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	//初始化TIM3 Channel2 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2
	TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2
	
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器
 
 
	TIM_Cmd(TIM3, ENABLE);  //使能TIM3
}

void Open_LED(autopwmval){
	if(rtc_clock.hour>=17){
		if(rtc_clock.min>=12){
			TIM_SetCompare2(TIM3,autopwmval);
		}
	}
}	
void LED_TIM_SetCompare2(u8 c_led,u16 val){
	switch(c_led){
		printf("c_led:%d",c_led);
		case 0:
			TIM_SetCompare2(TIM3,900);	
			TIM_SetCompare3(TIM3,900);
			break;
		case 1:
			TIM_SetCompare2(TIM3,val);	
			TIM_SetCompare3(TIM3,900);
			break;
		case 2:
			TIM_SetCompare2(TIM3,900);	
			TIM_SetCompare3(TIM3,val);
			break;
		case 3:
			TIM_SetCompare2(TIM3,val);	
			TIM_SetCompare3(TIM3,val);
			break;		
	}
}


//pwm.h文件
#ifndef __PWM_H
#define __PWM_H
#include "sys.h"


void TIM3_Int_Init(u16 arr,u16 psc);
void TIM3_PWM_Init(u16 arr,u16 psc);
#endif
//timer.c文件
#include "timer.h"
#include "usart.h"
#include "key.h"

void Exit_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	EXTI_InitTypeDef EXTI_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);
 
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;						//配置为上拉输入
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);
	EXTI_DeInit();
	EXTI_InitStructure.EXTI_Line = EXTI_Line1;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; 
	EXTI_Init(&EXTI_InitStructure); 
    //上升下降沿中断,这样按下或松手就都能触发中断

	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource2);
 	EXTI_InitStructure.EXTI_Line = EXTI_Line2;
	EXTI_Init(&EXTI_InitStructure); 
}

void TIM2_Config(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	TIM_TimeBaseStructure.TIM_Prescaler= 71;//预分频					
    TIM_TimeBaseStructure.TIM_Period=9999; //相当于每10ms进入一次中断
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;	// 计数器计数模式,向上计数
	// 初始化定时器
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
 
	TIM_ARRPreloadConfig(TIM2, ENABLE);			//使能TIM重载寄存器ARR
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);		// 清除计数器中断标志位
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);	// 开启计数器中断
	TIM_Cmd(TIM2, DISABLE);						// 关闭定时器的时钟,等待使用
}
 
//配置中断
void NVIC_Exit_GPIO_Config(void)
{
	NVIC_InitTypeDef	NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}
 
 void NVIC_TIM2_Config(void)
{
	NVIC_InitTypeDef	NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn  ;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}



//timer.h文件
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"

#endif
//rtc.c文件
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "rtc.h" 	
#include "stm32f10x.h"
#include <stdbool.h>
	   
struct RTC_CLOCK rtc_clock;  //定义RTC标准结构体
 
u8 New_RTC_Index =0; //更改时间  

u8 flashTim=0; //是否赋值时间
u16 New_RTC[15]; //获取的时间
u16 t;  


static void RTC_NVIC_Config(void)
{	
  NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;		//RTC全局中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	//先占优先级1位,从优先级3位
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	//先占优先级0位,从优先级4位
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;		//使能该通道中断
	NVIC_Init(&NVIC_InitStructure);		//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}

//实时时钟配置
//初始化RTC时钟,同时检测时钟是否工作正常
//BKP->DR1用于保存是否第一次配置的设置
//返回0:正常
//其他:错误代码

u8 RTC_Init(void)
{
	//检查是不是第一次配置时钟
	u8 temp=0;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟   
	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问  
	if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050)		//从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
	{	 			 
		BKP_DeInit();	//复位备份区域 	
		RCC_LSEConfig(RCC_LSE_ON);	//设置外部低速晶振(LSE),使用外设低速晶振
		while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)	//检查指定的RCC标志位设置与否,等待低速晶振就绪
		{
			temp++;
			delay_ms(10);
		}
		if(temp>=250)return 1;//初始化时钟失败,晶振有问题	    
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    
		RCC_RTCCLKCmd(ENABLE);	//使能RTC时钟  
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		RTC_WaitForSynchro();		//等待RTC寄存器同步  
		RTC_ITConfig(RTC_IT_SEC, ENABLE);		//使能RTC秒中断
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		RTC_EnterConfigMode();/// 允许配置	
		RTC_SetPrescaler(32767); //设置RTC预分频的值
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		RTC_Set(2023,9,19,21,00,00);  //设置时间	
		RTC_ExitConfigMode(); //退出配置模式  
		BKP_WriteBackupRegister(BKP_DR1, 0X5050);	//向指定的后备寄存器中写入用户程序数据
	}
	else//系统继续计时
	{
		RTC_WaitForSynchro();	//等待最近一次对RTC寄存器的写操作完成
		RTC_ITConfig(RTC_IT_SEC, ENABLE);	//使能RTC秒中断
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
	}
	RTC_NVIC_Config();//RCT中断分组设置		    				     
	RTC_Get();//更新时间	
	return 0; //ok

}		 				    
//RTC时钟中断
//每秒触发一次  
//extern u16 tcnt; 
void RTC_IRQHandler(void)
{		 
	if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
	{							
		RTC_Get();//更新时间   
 	}
	if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
	{
		RTC_ClearITPendingBit(RTC_IT_ALR);		//清闹钟中断	  	
		RTC_Get();				//更新时间   
		printf("Alarm Time:%d-%d-%d %d:%d:%d\n",rtc_clock.year,rtc_clock.mon,rtc_clock.day,rtc_clock.hour,rtc_clock.min,rtc_clock.sec);//输出闹铃时间	
  	} 				  								 
	RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);		//清闹钟中断
	RTC_WaitForLastTask();	  	    						 	   	 
}
//判断是否是闰年函数
//月份   1  2  3  4  5  6  7  8  9  10 11 12
//闰年   31 29 31 30 31 30 31 31 30 31 30 31
//非闰年 31 28 31 30 31 30 31 31 30 31 30 31
//输入:年份
//输出:该年份是不是闰年.1,是.0,不是
u8 Is_Leap_Year(u16 year)
{			  
	if(year%4==0) //必须能被4整除
	{ 
		if(year%100==0) 
		{ 
			if(year%400==0)return 1;//如果以00结尾,还要能被400整除 	   
			else return 0;   
		}else return 1;   
	}else return 0;	
}	 			   
//设置时钟
//把输入的时钟转换为秒钟
//以1970年1月1日为基准
//1970~2099年为合法年份
//返回值:0,成功;其他:错误代码.
//月份数据表											 
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表	  
//平年的月份日期表
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
	u16 t;
	u32 seccount=0;
	if(syear<1970||syear>2099)return 1;	   
	for(t=1970;t<syear;t++)	//把所有年份的秒钟相加
	{
		if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
		else seccount+=31536000;			  //平年的秒钟数
	}
	smon-=1;
	for(t=0;t<smon;t++)	   //把前面月份的秒钟数相加
	{
		seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
		if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数	   
	}
	seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加 
	seccount+=(u32)hour*3600;//小时秒钟数
    seccount+=(u32)min*60;	 //分钟秒钟数
	seccount+=sec;//最后的秒钟加上去

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟  
	PWR_BackupAccessCmd(ENABLE);	//使能RTC和后备寄存器访问 
	RTC_SetCounter(seccount);	//设置RTC计数器的值

	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成  	
	return 0;	    
}

//初始化闹钟		  
//以1970年1月1日为基准
//1970~2099年为合法年份
//syear,smon,sday,hour,min,sec:闹钟的年月日时分秒   
//返回值:0,成功;其他:错误代码.
u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
	u16 t;
	u32 seccount=0;
	if(syear<1970||syear>2099)return 1;	   
	for(t=1970;t<syear;t++)	//把所有年份的秒钟相加
	{
		if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
		else seccount+=31536000;			  //平年的秒钟数
	}
	smon-=1;
	for(t=0;t<smon;t++)	   //把前面月份的秒钟数相加
	{
		seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
		if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数	   
	}
	seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加 
	seccount+=(u32)hour*3600;//小时秒钟数
    seccount+=(u32)min*60;	 //分钟秒钟数
	seccount+=sec;//最后的秒钟加上去 			    
	//设置时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟   
	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问  
	//上面三步是必须的!
	
	RTC_SetAlarm(seccount);
 
	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成  	
	
	return 0;	    
}


//得到当前的时间
//返回值:0,成功;其他:错误代码.
u8 RTC_Get(void)
{
	static u16 daycnt=0;
	u32 timecount=0; 
	u32 temp=0;
	u16 temp1=0;	  
    timecount=RTC_GetCounter();	 
 	temp=timecount/86400;   //得到天数(秒钟数对应的)
	if(daycnt!=temp)//超过一天了
	{	  
		daycnt=temp;
		temp1=1970;	//从1970年开始  这是一个默认规则
		while(temp>=365)
		{				 
			if(Is_Leap_Year(temp1))//是闰年
			{
				if(temp>=366)temp-=366;//闰年的秒钟数
				else {temp1++;break;}  
			}
			else temp-=365;	  //平年 
			temp1++;  
		}   
		rtc_clock.year=temp1;//得到年份
		temp1=0;
		while(temp>=28)//超过了一个月
		{
			if(Is_Leap_Year(rtc_clock.year)&&temp1==1)//当年是不是闰年/2月份
			{
				if(temp>=29)temp-=29;//闰年的秒钟数
				else break; 
			}
			else 
			{
				if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
				else break;
			}
			temp1++;  
		}
		rtc_clock.mon=temp1+1;	//得到月份
		rtc_clock.day=temp+1;  	//得到日期 
	}
	temp=timecount%86400;     		//得到秒钟数   	   
	rtc_clock.hour=temp/3600;     	//小时
	rtc_clock.min=(temp%3600)/60; 	//分钟	
	rtc_clock.sec=(temp%3600)%60; 	//秒钟
	rtc_clock.week=RTC_Get_Week(rtc_clock.year,rtc_clock.mon,rtc_clock.day);//获取星期   
	return 0;
}	 
//获得现在是星期几
//功能描述:输入公历日期得到星期(只允许1901-2099年)
//输入参数:公历年月日 
//返回值:星期号																						 
u8 RTC_Get_Week(u16 year,u8 month,u8 day)
{	
	u16 temp2;
	u8 yearH,yearL;
	
	yearH=year/100;	yearL=year%100; 
	// 如果为21世纪,年份数加100  
	if (yearH>19)yearL+=100;
	// 所过闰年数只算1900年之后的  
	temp2=yearL+yearL/4;
	temp2=temp2%7; 
	temp2=temp2+day+table_week[month-1];
	if (yearL%4==0&&month<3)temp2--;
	return(temp2%7);
}			  


void Set_NewTime(u8* USART_RX,u16 len)
{		   
	for(t=0;t<len;t++)
	{
		switch(USART_RX[t]){
			case '0': New_RTC[t]=0; break;
			case '1': New_RTC[t]=1; break;
			case '2': New_RTC[t]=2; break;
			case '3': New_RTC[t]=3; break;
			case '4': New_RTC[t]=4; break;
			case '5': New_RTC[t]=5; break;
			case '6': New_RTC[t]=6; break;
			case '7': New_RTC[t]=7; break;
			case '8': New_RTC[t]=8; break;
			case '9': New_RTC[t]=9; break;			
		}	
	}					
	if(flashTim==1){
		if( New_RTC[0]<4000){
			New_RTC[0]=New_RTC[0]*1000+New_RTC[1]*100+New_RTC[2]*10+New_RTC[3];
			New_RTC[1]=New_RTC[4]*10+New_RTC[5];
			New_RTC[2]=New_RTC[6]*10+New_RTC[7];
			New_RTC[3]=New_RTC[8]*10+New_RTC[9];
			New_RTC[4]=New_RTC[10]*10+New_RTC[11];
			New_RTC[5]=New_RTC[12]*10+New_RTC[13];
			RTC_Set(New_RTC[0],New_RTC[1],New_RTC[2],New_RTC[3],New_RTC[4],New_RTC[5]);
			flashTim=0;
		}	
	}
}


//rtc.h文件

#ifndef __RTC_H
#define __RTC_H	    
//ALIENTEK 精英STM32开发板
//RTC实时时钟 驱动代码			 
//正点原子@ALIENTEK
//2010/6/6

//时间结构体
struct RTC_CLOCK
{
		u32 year;
	  u32 mon;
	  u32 day;
		u32 hour;
		u32 min;
	  u32 sec;
		u32 week;
};				
extern struct RTC_CLOCK rtc_clock; 
extern u8 Open_Auto_Air;
extern u8 flashTim;
extern u8 const mon_table[12];	//月份日期数据表
void Disp_Time(u8 x,u8 y,u8 size);//在制定位置开始显示时间
void Disp_Week(u8 x,u8 y,u8 size,u8 lang);//在指定位置显示星期
void NewTime(u8 Value);
u8 RTC_Init(void);        //初始化RTC,返回0,失败;1,成功;
u8 Is_Leap_Year(u16 year);//平年,闰年判断
u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);
u8 RTC_Get(void);         //更新时间   
u8 RTC_Get_Week(u16 year,u8 month,u8 day);
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);//设置时间			 
#endif
//remote.c
#include "remote.h"
#include "delay.h"
#include "usart.h"

//红外遥控初始化
//设置IO以及定时器4的输入捕获
void Remote_Init(void)    			  
{  
	GPIO_InitTypeDef GPIO_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_ICInitTypeDef  TIM_ICInitStructure;  
 
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //使能PORTB时钟 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);	//TIM4 时钟使能 

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;				 //PB9 输入 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; 		//上拉输入 
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOB,GPIO_Pin_9);	//初始化GPIOB.9
	
						  
 	TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出  
	TIM_TimeBaseStructure.TIM_Prescaler =(72-1); 	//预分频器,1M的计数频率,1us加1.	   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式

	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx

  TIM_ICInitStructure.TIM_Channel = TIM_Channel_4;  // 选择输入端 IC4映射到TI4上
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置输入分频,不分频 
  TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波
  TIM_ICInit(TIM4, &TIM_ICInitStructure);//初始化定时器输入捕获通道

  TIM_Cmd(TIM4,ENABLE ); 	//使能定时器4
 
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器	

	TIM_ITConfig( TIM4,TIM_IT_Update|TIM_IT_CC4,ENABLE);//允许更新中断 ,允许CC4IE捕获中断								 
}

//遥控器接收状态
//[7]:收到了引导码标志
//[6]:得到了一个按键的所有信息
//[5]:保留	
//[4]:标记上升沿是否已经被捕获								   
//[3:0]:溢出计时器
u8 	RmtSta=0;	  	  
u16 Dval;		//下降沿时计数器的值
u32 RmtRec=0;	//红外接收到的数据	   		    
u8  RmtCnt=0;	//按键按下的次数	  
//定时器4中断服务程序	 
void TIM4_IRQHandler(void)
{ 		    	 
 
	if(TIM_GetITStatus(TIM4,TIM_IT_Update)!=RESET)
	{
		if(RmtSta&0x80)								//上次有数据被接收到了
		{	
			RmtSta&=~0X10;							//取消上升沿已经被捕获标记
			if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;	//标记已经完成一次按键的键值信息采集
			if((RmtSta&0X0F)<14)RmtSta++;
			else
			{
				RmtSta&=~(1<<7);					//清空引导标识
				RmtSta&=0XF0;						//清空计数器	
			}								 	   	
		}							    
	}
	if(TIM_GetITStatus(TIM4,TIM_IT_CC4)!=RESET)
	{	  
		if(RDATA)//上升沿捕获
		{
  			TIM_OC4PolarityConfig(TIM4,TIM_ICPolarity_Falling);						//CC4P=1	设置为下降沿捕获
			TIM_SetCounter(TIM4,0);							//清空定时器值
			RmtSta|=0X10;							//标记上升沿已经被捕获
		}else //下降沿捕获
		{
			Dval=TIM_GetCapture4(TIM4);					//读取CCR4也可以清CC4IF标志位
  		TIM_OC4PolarityConfig(TIM4,TIM_ICPolarity_Rising);				//CC4P=0	设置为上升沿捕获
			if(RmtSta&0X10)							//完成一次高电平捕获 
			{
 				if(RmtSta&0X80)//接收到了引导码
				{
					
					if(Dval>300&&Dval<800)			//560为标准值,560us
					{
						RmtRec<<=1;					//左移一位.
						RmtRec|=0;					//接收到0	   
					}else if(Dval>1400&&Dval<1800)	//1680为标准值,1680us
					{
						RmtRec<<=1;					//左移一位.
						RmtRec|=1;					//接收到1
					}else if(Dval>2200&&Dval<2600)	//得到按键键值增加的信息 2500为标准值2.5ms
					{
						RmtCnt++; 					//按键次数增加1次
						RmtSta&=0XF0;				//清空计时器		
					}
 				}else if(Dval>4200&&Dval<4700)		//4500为标准值4.5ms
				{
					RmtSta|=1<<7;					//标记成功接收到了引导码
					RmtCnt=0;						//清除按键次数计数器
				}						 
			}
			RmtSta&=~(1<<4);
		}				 		     	    					   
	}
	TIM_ClearITPendingBit(TIM4,TIM_IT_Update|TIM_IT_CC4);	 	    
}

//处理红外键盘
//返回值:
//	 0,没有任何按键按下
//其他,按下的按键键值.
u8 Remote_Scan(void)
{        
	u8 sta=0;       
    u8 t1,t2;  
	if(RmtSta&(1<<6))//得到一个按键的所有信息了
	{ 
	    t1=RmtRec>>24;			//得到地址码
	    t2=(RmtRec>>16)&0xff;	//得到地址反码 
 	    if((t1==(u8)~t2)&&t1==REMOTE_ID)//检验遥控识别码(ID)及地址 
	    { 
	        t1=RmtRec>>8;
	        t2=RmtRec; 	
	        if(t1==(u8)~t2)sta=t1;//键值正确	 
		}   
		if((sta==0)||((RmtSta&0X80)==0))//按键数据错误/遥控已经没有按下了
		{
		 	RmtSta&=~(1<<6);//清除接收到有效按键标识
			RmtCnt=0;		//清除按键次数计数器
		}
	}  
    return sta;
}

//remote.h
#ifndef __RED_H
#define __RED_H 
#include "sys.h"   

#define RDATA 	PBin(9)	 	//红外数据输入脚

//红外遥控识别码(ID),每款遥控器的该值基本都不一样,但也有一样的.
//我们选用的遥控器识别码为0
#define REMOTE_ID 0      		   

extern u8 RmtCnt;			//按键按下的次数

void Remote_Init(void);    	//红外传感器接收头引脚初始化
u8 Remote_Scan(void);	    
#endif
//stmflash.c
#include "stmflash.h"
#include "delay.h"
#include "usart.h"
#include "sys.h"
u16 A_Parameter[10],B_Parameter[10],Flash_Parameter[10];  //Flash相关数组

//		读取的数据	写入的数据 
//解锁STM32的FLASH
void STMFLASH_Unlock(void)
{
  FLASH->KEYR=FLASH_KEY1;//写入解锁序列.
  FLASH->KEYR=FLASH_KEY2;
}
//flash上锁
void STMFLASH_Lock(void)
{
  FLASH->CR|=1<<7;//上锁
}
//得到FLASH状态
u8 STMFLASH_GetStatus(void)
{	
	u32 res;		
	res=FLASH->SR; 
	if(res&(1<<0))return 1;		    //忙
	else if(res&(1<<2))return 2;	//编程错误
	else if(res&(1<<4))return 3;	//写保护错误
	return 0;						//操作完成
}
//等待操作完成
//time:要延时的长短
//返回值:状态.
u8 STMFLASH_WaitDone(u16 time)
{
	u8 res;
	do
	{
		res=STMFLASH_GetStatus();
		if(res!=1)break;//非忙,无需等待了,直接退出.
		delay_us(1);
		time--;
	 }while(time);
	 if(time==0)res=0xff;//TIMEOUT
	 return res;
}
//擦除页
//paddr:页地址
//返回值:执行情况
u8 STMFLASH_ErasePage(u32 paddr)
{
	u8 res=0;
	res=STMFLASH_WaitDone(0X5FFF);//等待上次操作结束,>20ms    
	if(res==0)
	{ 
		FLASH->CR|=1<<1;//页擦除
		FLASH->AR=paddr;//设置页地址 
		FLASH->CR|=1<<6;//开始擦除		  
		res=STMFLASH_WaitDone(0X5FFF);//等待操作结束,>20ms  
		if(res!=1)//非忙
		{
			FLASH->CR&=~(1<<1);//清除页擦除标志.
		}
	}
	return res;
}

//读取指定地址的半字(16位数据)
//faddr:读地址(此地址必须为2的倍数!!)
//返回值:对应数据.
u16 STMFLASH_ReadHalfWord(u32 faddr)
{
	return *(vu16*)faddr; 
}
#if STM32_FLASH_WREN	//如果使能了写   
//不检查的写入
//WriteAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数   
void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
{ 			 		 
	u16 i;
	for(i=0;i<NumToWrite;i++)
	{
		FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
	    WriteAddr+=2;//地址增加2.
	}  
} 
//从指定地址开始写入指定长度的数据
//WriteAddr:起始地址(此地址必须为2的倍数!!)
//pBuffer:数据指针
//NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
#if STM32_FLASH_SIZE<256
#define STM_SECTOR_SIZE 1024 //字节
#else 
#define STM_SECTOR_SIZE	2048
#endif		 
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)	
{
	u32 secpos;	   //扇区地址
	u16 secoff;	   //扇区内偏移地址(16位字计算)
	u16 secremain; //扇区内剩余地址(16位字计算)	   
 	u16 i;    
	u32 offaddr;   //去掉0X08000000后的地址
	if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
	FLASH_Unlock();						//解锁
	offaddr=WriteAddr-STM32_FLASH_BASE;		//实际偏移地址.
	secpos=offaddr/STM_SECTOR_SIZE;			//扇区地址  0~127 for STM32F103RBT6
	secoff=(offaddr%STM_SECTOR_SIZE)/2;		//在扇区内的偏移(2个字节为基本单位.)
	secremain=STM_SECTOR_SIZE/2-secoff;		//扇区剩余空间大小   
	if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围
	while(1) 
	{	
		STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
		for(i=0;i<secremain;i++)//校验数据
		{
			if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除  	  
		}
		if(i<secremain)//需要擦除
		{
			FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
			for(i=0;i<secremain;i++)//复制
			{
				STMFLASH_BUF[i+secoff]=pBuffer[i];	  
			}
			STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区  
		}else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. 				   
		if(NumToWrite==secremain)break;//写入结束了
		else//写入未结束
		{
			secpos++;				//扇区地址增1
			secoff=0;				//偏移位置为0 	 
		   	pBuffer+=secremain;  	//指针偏移
			WriteAddr+=secremain;	//写地址偏移	   
		   	NumToWrite-=secremain;	//字节(16位)数递减
			if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
			else secremain=NumToWrite;//下一个扇区可以写完了
		}	 
	};	
	FLASH_Lock();//上锁
}
#endif

//从指定地址开始读出指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)   	
{
	u16 i;
	for(i=0;i<NumToRead;i++)
	{
		pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
		ReadAddr+=2;//偏移2个字节.	
	}
}

//
//WriteAddr:起始地址
//WriteData:要写入的数据
void Test_Write(u32 WriteAddr,u16 WriteData)   	
{
	STMFLASH_Write(WriteAddr,&WriteData,1);//写入一个字 
}


/**************************************************************************
函数功能:从Flash读取指定数据
入口参数:无
返回  值:无
**************************************************************************/
u16 Flash_Read(void)
{
	STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)A_Parameter,10);
	//Flash未写入数据的时候,数据为65535
	if(A_Parameter[0]==65535&&A_Parameter[1]==65535)
	{
		printf("FLASH暂时为空"); 

	}
	else//Flash内有数据,进行读取
	{		
		printf("%d,%d\r\n",A_Parameter[0],A_Parameter[1]);
		return A_Parameter[0]*100+A_Parameter[1];
	}

}	

/**************************************************************************
函数功能:向Flash写入数据
入口参数:无
返回  值:无
**************************************************************************/
void Flash_Write(u16 hour,u8 min)
{
	if(hour>=5) B_Parameter[0]=hour;		
	B_Parameter[1]=min;		
	STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)B_Parameter,4);	
}

//stemflash.h
#ifndef __STMFLASH_H__
#define __STMFLASH_H__
#include "sys.h"  
 
extern u16 A_Parameter[10],B_Parameter[10],Flash_Parameter[10];               //Flash相关数组
//用户根据自己的需要设置
#define STM32_FLASH_SIZE 64 	 		//所选STM32的FLASH容量大小(单位为K)
#define STM32_FLASH_WREN 1              //使能FLASH写入(0,不是能;1,使能)

//FLASH起始地址
#define STM32_FLASH_BASE 0x08000000 	//STM32 FLASH的起始地址
#define FLASH_SAVE_ADDR  0X0800E000 	//设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000)
 
 //FLASH解锁键值
#define FLASH_KEY1               0X45670123
#define FLASH_KEY2               0XCDEF89AB

void STMFLASH_Unlock(void);					  	//FLASH解锁
void STMFLASH_Lock(void);					  	//FLASH上锁
u8 STMFLASH_GetStatus(void);				  	//获得状态
u8 STMFLASH_WaitDone(u16 time);				  	//等待操作结束
u8 STMFLASH_ErasePage(u32 paddr);			  	//擦除页
u8 STMFLASH_WriteHalfWord(u32 faddr, u16 dat);	//写入半字
u16 STMFLASH_ReadHalfWord(u32 faddr);		    //读出半字  
void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len);	//指定地址开始写入指定长度的数据
u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len);						//指定地址开始读取指定长度数据
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite);		//从指定地址开始写入指定长度的数据
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead);   		//从指定地址开始读出指定长度的数据


void Test_Write(u32 WriteAddr,u16 WriteData);//测试写入
u16 Flash_Read(void);	//读取Flash数据
void Flash_Write(u16 hour,u8 min);//写入Flash数据

#endif

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值