欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32定时器中断显示时间

程序员文章站 2022-03-13 17:18:23
...

前言

利用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点阵液晶屏

效果展示

最后给大家展示一下效果

STM32定时器中断显示时间