STM32定时器中断显示时间

前言

利用STM32的定时器中断,实现时间的显示。我们知道利用定时器中断只能进行tick的计算,然而用来显示时间我们应该怎么办呢?经过项目的实际运用,我发现利用定时器中断配合串口通讯也能实现时间显示。
这种方法应用的前提是,我们的板件不能装电池无法保存时钟,我们的板件会与其他可以保存时钟的板件通讯。

原理介绍

(1)硬件资源:有通讯功能的STM32板件、可以保存时钟且能通讯的其他任意板件
(2)软件设计:定时器中断、通讯接收(串口接收)
(3)设计思路:利用定时器中断获得稳定的tick(假设tick为1ms,那么我们就1ms进入定时器中断计数一次),编写时钟进位函数,通过通讯获得当前时间。

功能实现

定时器中断实现

我们选用STM32的基本定时器,具体操作看代码。

#include "stm32f10x.h"
#define            BASIC_TIM                   TIM6
#define            BASIC_TIM_APBxClock_FUN     RCC_APB1PeriphClockCmd
#define            BASIC_TIM_CLK               RCC_APB1Periph_TIM6
#define            BASIC_TIM_IRQ               TIM6_IRQn 
#define            BASIC_TIM_IRQHandler        TIM6_IRQHandler
void BASIC_TIM_Config(void);
void BASIC_TIM_NVIC_Config(void) ;
void system_time_increase(void);
void BASIC_TIM_Config(void)
{
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure; 
  //使能定时器时钟,内部时钟48M
  BASIC_TIM_APBxClock_FUN(BASIC_TIM_CLK, ENABLE); 
  //自动重装载寄存器的值
  TIM_TimeBaseStructure.TIM_Period=1000;
  //时钟预分频数为47,则定时器时钟(47+1)/48=1M,每1ms进入中断一次
  TIM_TimeBaseStructure.TIM_Prescaler= 47; 
  //初始化定时器
  TIM_TimeBaseInit(BASIC_TIM, &TIM_TimeBaseStructure); 
  //清除计数器中断标志位
  TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update);
  //使能计数器1中断
  TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE);
  //定时器使能
  TIM_Cmd(BASIC_TIM, ENABLE);
  BASIC_TIM_NVIC_Config();
}
void BASIC_TIM_NVIC_Config(void) 
{
  NVIC_InitTypeDef NVIC_InitStructure; 
  //配置中断向量组
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
  //配置中断源
  NVIC_InitStructure.NVIC_IRQChannel = BASIC_TIM_IRQ ; 
  //配置中断优先级
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  //配置中断子优先级
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; 
  //中断使能
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
 }

以上为STM32定时器中断的配置部分代码,要想实现计数我们还要编写中断服务函数,代码如下:

void BASIC_TIM_IRQHandler(void)
{
  if ( TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET )
  {
    system_time_increase();//时钟进位函数  
    IM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update);
  }    
}

时钟进位函数

我们中断服务函数的关键就是时钟进位,这个函数的具体代码如下:

void system_time_increase(void)
{
    uint8_t month_day_tab[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};  //每月的天数
    TIME.msec++;
    if(TIME.msec >= 1000)  //1ms进入中断一次,1000次就是1ms
    {
        TIME.msec = 0L;
        TIME.second++;//秒进位
        if(TIME.second >= 60)
        {
            TIME.second = 0;
            TIME.minute++;//分钟进位
            if(TIME.minute >= 60)
            {
                TIME.minute = 0;
                TIME.hour++;//小时进位
                if(TIME.hour >= 24)
                {
                    TIME.hour = 0;
                    TIME.day++;//天进位
                    if(TIME.day > (((TIME.year%4 == 0) && (TIME.month == 2))?
                                        month_day_tab[TIME.month]+1:month_day_tab[TIME.month]) )
                    {
                        TIME.day = 1;
                        TIME.month++;//月进位
                        if(TIME.month > 12)
                        {
                            TIME.month = 1;
                            TIME.year++;//年进位
                            if(TIME.year > 99)
                            {
                                TIME.year = 0;
                            }
                        }
                    }
                }
            }
        }
    }
}

这里说明一下,我们做了一个TIME的结构体,我们可以通过访问操作这个结构体实现通讯,实现时间显示等后续功能。结构体如下:

typedef struct _TDateTime
{
    uint8 year;
    uint8 month;
    uint8 day;
    uint8 hour;
    uint8 minute;
    uint8 second;
    uint16  msec;
}TDateTime;

我们在定时器中断服务函数中实现时间进位功能,到这一步我们就能实现正确的计时了。但是我们每次开启的时候时间都会是00年00月00日00:00:00。我们要想实现实时显示时间还需要最后一步:通讯。

通讯获取当前时间

在这里我们利用串口通讯的方式获取其他板件的当前时间。我们使用私有协议报文的方式获得当前时间报文,当然也有其他的方式。大致的原理就是,时间板件把时间信息封装成一串十六进制报文,我们的程序进行解析获取当前时间,然后在进行进位。具体的代码量比较大,这里就不贴出来了。我们还可以把时间通过液晶屏显示出来,关于液晶屏的操作可以借鉴这篇博客STM32F103成功点亮12864点阵液晶屏

效果展示

最后给大家展示一下效果

在这里插入图片描述

发布了4 篇原创文章 · 获赞 2 · 访问量 200

猜你喜欢

转载自blog.csdn.net/qq_44703307/article/details/103869845