STM32单片机第一周其二任务总结(一)sg90大致模拟时钟时分秒指针(RTC模块和舵机)

第一周学习任务(其二)实现思路和方法

2.利用舵机模拟时钟指针。用舵机模拟时钟的时分秒,设置一个按键让舵机模拟的指针可以在时分秒之间切换,时间设定可在代码内来修改。
注:舵机的限位作为时钟的0点(12点),由于舵机只能转180度,所以可以用1度来代表时钟上的2度,当时针指到12或分针秒针指到60时(舵机180度)需要转回0度,秒针要能体现出转动
实现思路
个人认为实现的方法是比较方便的,因为STM32F10x系列的芯片上面是带有RTC模块的,RTC模块在以后我也要自己再总结一遍,其实就是一个类似于DS1302的一个简单32位寄存器,但是一定要结合软件进行日历的配置,不然是没办法用上RTC的,因为不加上软件中断调控,RTC会一直自己增加,直到把所有位占满,看到百度上查找到的资料是132年…
所以我们需要做的就是,读取RTC寄存器的当前时间,然后自己计算得到时分秒和PWM方波的占空比之间的关系,把时分秒进行转化对应的占空比,然后把PWM方波输出即可。然后需要一个按键扫描部分,来对按键扫描,然后判断舵机需要模拟的时分秒中的哪一个指针。
我配置舵机的时候使用的利用20ms的PWM输出周期

主函数
初始化所需要的函数 延时 RTC 中断分组
设置PWM方波输出的时间
按键扫描 判断哪根指针
读取相应时分秒的数值
利用转换关系转换为占空比并输出

注:RTC模块里面的日历模块可以参考用,挺方便的

#include "stm32f10x.h"
#include "delay.h"
#include "timer.h"
#include "rtc.h" 
#include "led.h"
#include "key.h"

u8 KeyValue = 1;																	//时间的模式

int main()
{
	u8 t,key;																				//记录时间的值t,范围0-60,记录按键的值key
	delay_init();																		//延时函数初始化
	LED_Init();
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组为组2:2位抢占优先级,2位响应优先级      
	RTC_Init();	  																	//RTC初始化
	TIM3_PWM_Init(1999,719);		//这里的arr的值和psc的值可以修改,只要在不分频情况下为72MHZ,719对应0.01ms,1999对应2000*0.01 = 20ms						
	while(1)
	{
		key = KEY_Scan(1);
		if(key)
		{
			switch(key)
			{
				case KEY0_PRES:
						KeyValue=1;								//切换到读取s的模式;
						LED0 = 1;
						break;
					case KEY2_PRES:	
						KeyValue=2;								//切换到读取min的模式;
						LED0 = 1;
						break;
					case KEY1_PRES:	 
						KeyValue=3;								//切换到读取hour的模式;
						LED0 = 1;
						break;
			}
		}
		switch(KeyValue)							//判断此时改变的模式
		{	
			case 1:
					t=calendar.sec;								//更新s到t上
					break;
			case 2:
					t=calendar.min;								//更新min到t上
					break;
			case 3:
					t=calendar.hour;							//更新hour到t上		
					break;
		}
		
		//时钟指针存在一定的误差
		if(KeyValue == 1||KeyValue == 2)
		{
			if((1750 + t*3) < 1950)
					TIM_SetCompare2(TIM3, 1750 + t*3);  //60s共360度,就是每1s转过3度,每1秒要加2.7,让它每1s加3
		}
		else
		{
			if(t > 12)
				t -= 12;
			if((1750 + t*14) < 1950)
					TIM_SetCompare2(TIM3, 1750 + t*14);  //12h共360度,就是每1h转过15度,每1h要加13.5,让它每1h加14
		}
		LED0 = 0;
	}	           
}

#include "timer.h"


void TIM3_PWM_Init(u16 arr,u16 psc)
{
       
		GPIO_InitTypeDef GPIO_InitStructure;
		TIM_TimeBaseInitTypeDef TIM_TimeBaseInitstruct;
		TIM_OCInitTypeDef TIM_OCInitStructure;
		
		
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//改变相应的引脚号和时钟号,就能有不同的引脚PWM输出,
		//TIM3直接输出方波到PA7,此时没有重映射
		//RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); 		//是否启用重映射
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//使能IO口时钟
	 
		GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;			//复用推挽输出,可以输出强高低电平
		GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;						//输出引脚为7
		GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;		//设置输出速度
		GPIO_Init(GPIOA,&GPIO_InitStructure);							//初始化IO口,为
		//GPIO_SetBits(GPIOA,GPIO_Pin_1);									//初始为低电平,可有可无
	 
		//GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);//是否启用重映射
	 

		TIM_TimeBaseInitstruct.TIM_ClockDivision=TIM_CKD_DIV1;
		TIM_TimeBaseInitstruct.TIM_CounterMode=TIM_CounterMode_Up;
		TIM_TimeBaseInitstruct.TIM_Period=arr;
		TIM_TimeBaseInitstruct.TIM_Prescaler=psc;
		TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitstruct);
		//初始化TIM3时钟

		TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; 						//选择 PWM 模式 2
		TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
		TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; 		//输出极性高
		TIM_OC2Init(TIM3, &TIM_OCInitStructure); 											//初始化定时器 TIM3 OC1
	 
		TIM_Cmd(TIM3, ENABLE);																				//使能定时器
	 
		TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);							//使能预装载寄存器
}


#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "rtc.h" 		    
	   
_calendar_obj calendar;//时钟结构体 
 
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) != 0x5052)		//从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎,如果要重新配置初始化时间,需要在这里把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预分频的值,设置晶振是1Hz
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		RTC_Set(2020,7,17,19,50,55);  //设置时间	
		RTC_ExitConfigMode(); //退出配置模式  
		BKP_WriteBackupRegister(BKP_DR1, 0X5052);	//向指定的后备寄存器中写入用户程序数据,如果要重新配置初始化时间,需要在这里把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",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.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++;  
		}   
		calendar.w_year=temp1;//得到年份
		temp1=0;
		while(temp>=28)//超过了一个月
		{
			if(Is_Leap_Year(calendar.w_year)&&temp1==1)//当年是不是闰年/2月份
			{
				if(temp>=29)temp-=29;//闰年的秒钟数
				else break; 
			}
			else 
			{
				if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
				else break;
			}
			temp1++;  
		}
		calendar.w_month=temp1+1;	//得到月份
		calendar.w_date=temp+1;  	//得到日期 
	}
	temp=timecount%86400;     		//得到秒钟数   	   
	calendar.hour=temp/3600;     	//小时
	calendar.min=(temp%3600)/60; 	//分钟	
	calendar.sec=(temp%3600)%60; 	//秒钟
	calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期   
	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);
}			  

你可能感兴趣的:(STM32)