源码地址:GitHub源码
前言
实验基于STM32F103开发板,Keil4。这里仅作为记录实验过程,代码上传至GitHub,尽量优化代码,本人小白,有不当之处请指教
实验目标
1.实现秒表功能:
分、秒和毫秒计时
按K1键开始计时
按K2键计录一个时间,再按继续记录下一时间,共可记录五个时间
按K3键停止计时
停止后,K2,K3可以前后翻看五个时间
2.实现万能历功能
能显示年、月、日 或 时、分、秒,通过按键切换显示内容
按键可以调整和设置日期和时间
功能分析
1、先进行系统时钟、延时函数、LED的初始化,然后配置按键外部中断,通过两个按键外部中断实现计时及终止计时功能。 时间的记录则用一个二维数组分别记录时、分、秒,根据题目要求显示。
2、万年历功能主要是通过三个按键KEY3控制切换,KEY2和KEY1分别对年、月、日、时、分、秒或加或减操作,需要对闰年进行判断。减操作仅影响当前操作的数,不影响其他的。如 day–,当month==0,执行month=12。【这里尚未对当前操作位做特殊操作,比如年份闪烁表示正在对其进行操作,之后补充】
关键代码
秒表
/****************普通按键初始化函数********************
* 通用定时器中断初始化
* 这里时钟选择为APB1的2倍,而APB1为36M
* arr:自动重装值。
* psc:时钟预分频数
* 这里使用的是定时器3!
******************************************************/
void TimerxInit(u16 arr, u16 psc)
{
RCC->APB1ENR |= 1<<1; //TIM3时钟使能
TIM3->ARR = arr; //设定计数器自动重装值,10为1ms
TIM3->PSC = psc; //预分频器7200,得到10KHZ的计数时钟
TIM3->DIER |= 1<<0; //允许更新中断
TIM3->CR1 |= 0x01; //使能定时器3
MY_NVIC_Init(1, 3, TIM3_IRQChannel, 2); //抢占1,子优先级3,组2
RCC->APB2ENR|=1<<4; //使能PORTC时钟
GPIOC->CRL&=0XFFFFF000;//PC0-2设置成输入
GPIOC->CRL|=0X00000888;
Ex_NVIC_Config(GPIO_C,0,FTIR);//下降沿触发
Ex_NVIC_Config(GPIO_C,1,FTIR);//下降沿触发
Ex_NVIC_Config(GPIO_C,2,FTIR);//下降沿触发
MY_NVIC_Init(0,2,EXTI0_IRQChannel,2);//抢占2,子优先级2,组2
MY_NVIC_Init(2,2,EXTI2_IRQChannel,2);//抢占2,子优先级1,组2
}
void EXTI2_IRQHandler(void){
delay_ms(10);//消抖
if(key1==0){ //按键1
key1=1;
second = 0;
minute = 0;
hour = 0;
while(1){
DisplayDigitalClock(hour, minute, second);
delay_ms(10);//消抖
t=KeyScan();
if(t==2){
timeRec[0][count] = hour;
timeRec[1][count] = minute;
timeRec[2][count] = second;
count++; // 记录时间个数+1
actCount = count-1; //actCount表示实际记录的时间个数
EXTI->PR=1<<1; //清除LINE1上的中断标志位
}
}
}
EXTI->PR=1<<2; //清除LINE2上的中断标志位
}
void EXTI0_IRQHandler(void){
delay_ms(10);//消抖
if(key3==0){ //按键3
while(1){
DisplayDigitalClock(hour, minute, second); //计时停在当前计时时间
t=KeyScan();
if(t==2){ //先翻看前一个记录时间
while(1){
DisplayDigitalClock(timeRec[0][count], timeRec[1][count], timeRec[2][count]);
if(t==2){
if(count>0){ //判断记录下限
--count;
}
EXTI->PR=1<<1;//清除LINE1上的中断标志位
}
if(t==3){
if(count<actCount){ //判断记录上限
++count;
}
EXTI->PR=1<<0;//清除LINE0上的中断标志位
}
t=KeyScan();
}
}
}
}
EXTI->PR=1<<0; //清除LINE0上的中断标志位
}
万年历
/// 注释部分为功能2【万年历】部分的代码
// 判断是否为闰年
int IsLeapYear(int year){
if((year%4 == 0 && year%100 != 0) || year %400 == 0){
return 1;
}
return 0;
}
// 对day++的合法性做判断
void JudgeDay(){
int i;
for(i=0; i<12; i++){
if(i==0||i==2||i==4||i==6||i==7||i==9||i==11){ //31天的月份
if((day>31)&&(curMonth==i)){
day=1;
curMonth++;
if(curMonth==13){
curMonth=1;
year++;
}
}
}else if(i==1){
if(day>28&&(curMonth==2)){ //2月
if(IsLeapYear(year)==0){ //且不是闰年
day=1;
curMonth=3;
}
}
}else { // 30的月份
if((day>30)&&(curMonth==i+1)){
day=1;
curMonth++;
}
}
}
}
// 对day--的合法性的操作
void JudgeDay2(){
int i;
if(day==0){
for(i=0; i<12; i++){
if(i==0||i==2||i==4||i==6||i==7||i==9||i==11){
day=31;
}else if(i==1){
if(IsLeapYear(year)==1){
day=29;
}else {
day=28;
}
}else {
day=30;
}
}
}
}
void TIM3_IRQHandler( void )
{
if( TIM3->SR & 0x0001) //溢出中断
{
second++;
if( second>59 )
{
second = 0;
minute++;
if( minute>59 )
{
minute = 0;
hour++;
if( hour>23 )
{
hour = 0;
day++;
JudgeDay();
}
}
}
}
TIM3->SR &= ~(1<<0); //清除中断标志位
}
// 处理按键3切换时间和日期的终端函数
void EXTI0_IRQHandler(void){
u8 t , cnt = 0;
delay_ms(10);//消抖
if(key3==0){ //按键3
while(1){
t = KeyScan();
if(t==3){
cnt++;
if(cnt==7) break;
}
if(cnt>3) DisplayDigitalClock2();
else DisplayCalendar(year, curMonth, day);
if(t==2){// 加操作
if(cnt==1) year++;
else if(cnt==2){
curMonth++;
if(curMonth==13){
curMonth=1; year++;
}
}else if(cnt==3){
day++; JudgeDay();
}else if(cnt>3){
if(cnt==4){
hour++;
if( hour>23 ){
hour = 0;
day++;
JudgeDay();
}
}else if(cnt==5){
minute++;
if( minute>59 ){
minute = 0;
hour++;
if( hour>23 ){
hour = 0;
day++;
JudgeDay();
}
}
}else if(cnt==6){
second++;
if( second>59 ){
second = 0; minute++;
if( minute>59 ) {
minute = 0; hour++;
if( hour>23 ){
hour = 0;
day++;
JudgeDay();
}
}
}
}
}
}else if(t==1){// 减操作
if(cnt==1) year--;
else if(cnt==2) {
curMonth--;
if(curMonth==0) {curMonth=12;year--;}
}else if(cnt==3){
day--;JudgeDay2();
}else if(cnt>3){
if(cnt==4){
if( hour==0 ){hour=23; }
else { hour--;}
}else if(cnt==5){
if( minute==0 )minute=59;
else minute--;
}else if(cnt==6){
if(second==0){ second=59;}
else { second--; }
}
}
}
}
}
EXTI->PR=1<<0; //清除LINE0上的中断标志位
}