历史上的今天
返回首页

历史上的今天

今天是:2024年12月03日(星期二)

正在发生

2018年12月03日 | STM32定时器相关介绍(主要是有UEV更新事件介绍)

2018-12-03 来源:eefocus

单片机的定时器的确很强大,参考说明书中就占了一百多页,占参考手册1/4 有多了。
STM32的定时器分了好几个类别,各个类别针对功能作用都不大相同。
分有: 一、高级定时器    二、通用定时器   三、基本定时器   四、看门狗定时器   五、SysTick定时器

      其中看门狗定时器和SysTick定时器本篇笔记阐述,这里主要记下对平时使用定时器作用的计时计数器的一些自己的理解。

按照参考手册中的定义 高级定时器 通用定时器 基本定时器,这三个定时器成上下级的关系,即基本定时器有的功能通用定时器都有,而且还增加了向下、向上/向下计数器、PWM生成、输出比较、输入捕获等等功能;而高级定时器又包含了通用定时器的所有功能,另外还增加了死区互补输出、刹车信号、加入重复计数器等等。(这里等等功能请参考《STM32参考手册》)

      所以学习STM32 定时器实际就是学习一下高级定时器,然后适当的删减后就是后面的两种定时器了。

假若不涉及输出输入,定时器的最基本用法就是计数定时作用了本篇笔记主要针对这部分的理解所写下的。


高级定时器中一共有20个寄存器:

TIMx_CR1、TIMx_CR2、TIMx_SMCR、TIMx_DIER、TIMx_SR、TIMx_EGR、TIMx_CCMR1、TIMx_CCMR2、
TIMx_CCER、TIMx_CNT、TIMx_PSC、TIMx_ARR、TIMx_RCR、TIMx_CCR1、TIMx_CCR2、TIMx_CCR3、
TIMx_CCR4、TIMx_BDTR、TIMx_DCR、TIMx_DMAR

好吧一堆寄存器光看都看到眼花缭乱了,当然不是所有寄存器都涉及到才能让定时器工作的,例如最基本的定时功能所涉及的只有几个与时基功能相关的寄存器,TIMx_CNT(计数器寄存器)、TIMx_PSC(预分频器寄存器)、TIMx_ARR(自动装载寄存器)、TIMx_RCR(重复次数寄存器)。参考手册中有那么 衣服定时器的框图。这几个寄存器的关系如图所示的:
 

CK_PSC这根时钟线上的时钟源的选择,即给定时器计数计时的时钟源的输入方式,有四种方式,分别是内部时钟,外部时钟模式1,外部时钟模式2,内部触发。这部分日后再说,这里暂且使用最常用的内部时钟方式,既是当内部时钟为72MHz 的内部时钟源。

如图所示的,时钟源首先进入预分频器,然后再进入预先装入自动重装载寄存器的计数器中,当计数器溢出时产生一次中断和一次事件更新。除了多了一个PSC,其他的基本和51单片机很相似,初次看参考手册中的功能描述中出现了好多次“更新事件(UEV)”。这究竟是怎么的一样东西呢? 在这里有个新概念叫“影子寄存器”,在上图中,可以看到PSC、ARR、REP(重复计数器中的低八位)这三个寄存器框框下都有个黑影,每次这三个寄存器就是影子寄存器,如果看到参考手册全图中还可以看到另外还有几个框框下也有阴影部分的,这几个寄存器也是影子寄存器。何谓影子寄存器呢,例如PSC寄存器可以理解为有两个,一个是用户可以访问到的寄存器,可读可写,另一部分就是客户访问不到的但其装载值和实际寄存器是密切关联的,当程序在运行中改写PSC 这时候影子寄存器的作用就体现了,因为立刻写入的值可能会大于或小于目前正在运行的寄存器中的数值,而真实在运行时候的正是这个影子寄存器中的值,而程序写入的是可访问的寄存器,只有当产生一个更新事件的时候影子寄存器才会读入访问寄存器中的值,这样就可以防止突然修改而产生的非正常中断或不会中断等异常问题。当然在控制器CR1中控制这个影子寄存器是否起作用,不起作用的话就是立即写入这个数值到寄存器中。下面两幅是参考手册中的相关时序图:
 
 


回头再说一下“更新事件(UEV)” ,当计数器溢出的时候产生一次UEV事件,另外还可以在事件寄存器TMx_EGR中的UG位软件写入产生一次事件更新,当UEV事件来临的时候所有影子寄存器均载入寄存器中的值,从而实现所有带影子寄存器的更新,而不启用影子寄存器的情况下只能实现,写那个寄存器更新那个寄存器而,这可能造成相关联的寄存器产生冲突矛盾,建议还是开启此功能,在下一个溢出周期后产生事件更新。

       (既然说到了影子寄存器也说点自己的猜测,了解了点STM32单片机的都知道几乎所有寄存器都是32位的,唯独TIM寄存器是16位的,是的如果是32位的计数器我们可能还能做更宽广的定时作用。但我们也还是发现即使加入了影子寄存器而整体的寄存器地址依然保持是连续的,这我猜测一种可能性寄存器本身其实还是32位的,但高位提供了影子寄存器的载入功能,所以依然能保持地址连续性,只要设定了高位禁止访问即可。官方资料和搜索中均未有任何确认说法,纯粹本人猜测未得到官方任何证实)

        另外高级定时器中还有RCR重复次数寄存器这个,也是比较简单的事件更新(UEV) 都是在RCR为0的情况下产生计数器溢出而产生的,当RCR中不为0的时候计数器溢出只会使得重复次数寄存器递减而不会产生UEV,这样就可以使得定时器的定时情况得以延长,而相当于有16位的分频器,16位的计数器,再加入16位的重复次数,一共48位的计数定时器。详细看参考手册,这个很好理解。

      基本的基时单元就是上面提及的这几个,下面看看3.0库是如何实习的基本使用。

TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;

       
      TIM_DeInit(TIM2);                                         //重新将Timer设置为缺省值
       
      TIM_InternalClockConfig(TIM2);                            //采用内部时钟给TIM2提供时钟源      
      TIM_TimeBaseStructure.TIM_Prescaler = 36000 -1;            //预分频系数为36000-1,这样计数器时钟为72MHz/36000 = 2kHz       
      TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1;     //设置时钟分割      
      TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//设置计数器模式为向上计数模式       
      TIM_TimeBaseStructure.TIM_Period = 2000 - 1;          //设置计数溢出大小,每计2000个数就产生一个更新事件
     TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);        //将配置应用到TIM2中
      TIM_ClearFlag(TIM2, TIM_FLAG_Update);                 //清除溢出中断标志  
       
      TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);               //开启TIM2的中断

以上是一个最基本的定时器配置的代码,载自网上被转载无数次的地方……
中断函数自己按照需求写,这里不多说。

在库中的初始化函数和初始化数据类型有3类,TIM_TimeBaseInitTypeDef、TIM_OCInitTypeDef、TIM_ICInitTypeDef与基时参数相关的数据类型是TIM_TimeBaseInitTypeDef

typedef struct
{
  uint16_t TIM_Prescaler;        /*!< Specifies the prescaler value used to divide the TIM clock.
                                     This parameter can be a number between 0x0000 and 0xFFFF */

  uint16_t TIM_CounterMode;       /*!                                     This parameter can be a value of @ref TIM_Counter_Mode */

  uint16_t TIM_Period;           /*!< Specifies the period value to be loaded into the active
                                     Auto-Reload Register at the next update event.
                                     This parameter must be a number between 0x0000 and0xFFFF.  */

  uint16_t TIM_ClockDivision;     /*!                                    This parameter can be a value of @ref TIM_Clock_Division_CKD*/

  uint8_t TIM_RepetitionCounter;  /*!< Specifies therepetition counter value. Each time the RCR downcounter
                                     reaches zero, an update event is generated and countingrestarts
                                     from the RCR value (N).
                                     This means in PWM mode that (N+1) corresponds to:
                                        - the number of PWM periods in edge-aligned mode
                                        - the number of half PWM period incenter-aligned mode
                                    This parameter must be a number between 0x00 and 0xFF. 
                                     @note This parameter is valid only for TIM1 and TIM8. */
} TIM_TimeBaseInitTypeDef;

以上是从库stm32f10x_tim.h中 截取的代码,整体的数据结构可以中这段注释中得知,不懂E文的要么翻字典要么翻库函数中文翻译版本(当然这个是2.0的库,有部分会和3.0后的版本很不相同),这部分的数据类型还是很一样的,不多说。 

接着就是TIM_TimeBaseInit()这个函数了,在stm32f10x_tim.c的224行中

voidTIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef*TIM_TimeBaseInitStruct)
{
  uint16_t tmpcr1 = 0;

  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx)); 
  assert_param(IS_TIM_COUNTER_MODE(TIM_TimeBaseInitStruct->TIM_CounterMode));
  assert_param(IS_TIM_CKD_DIV(TIM_TimeBaseInitStruct->TIM_ClockDivision));

  tmpcr1 = TIMx->CR1;  

  if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM2) || (TIMx ==TIM3)||
     (TIMx == TIM4) || (TIMx == TIM5)) 
  {
    /* Select the Counter Mode */
    tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR |TIM_CR1_CMS)));
    tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
  }
  
  if((TIMx != TIM6) && (TIMx != TIM7))
  {
    /* Set the clock division */
    tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));
    tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;
  }

  TIMx->CR1 = tmpcr1;

  /* Set the Autoreload value */
  TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ;
  
  /* Set the Prescaler value */
  TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;
     
  if ((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)|| (TIMx ==TIM16) || (TIMx == TIM17))  
  {
    /* Set the Repetition Counter value */
    TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
  }

  /* Generate an update event to reload the Prescaler and theRepetition counter
     values immediately */
  TIMx->EGR = TIM_PSCReloadMode_Immediate;          
}

可以看3.0后的函数里把所有的TIMx都加入一个函数里面做判断了,不需要和2.0的区分TIM1和TIM 两类函数,比较其基本操作都一样无非就是多了一个两个寄存器而已。

程序中可以看到这一段:

  if((TIMx == TIM1)|| (TIMx == TIM8)|| (TIMx == TIM2) || (TIMx == TIM3)||
     (TIMx == TIM4) || (TIMx == TIM5))
  {
    /* Select the Counter Mode */
    tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR |TIM_CR1_CMS)));
    tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
  }
if((TIMx != TIM6) && (TIMx != TIM7))
{     
    /* Set the clock division */    
    tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));    
    tmpcr1 |=(uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;   
}
TIMx->CR1 = tmpcr1;
    

高级定时器和通用定时器拥有向上计数、向下技术、向上/向下模式,三种,而基本计时器只有向上计数一种,因此TIM6和TIM7木有的设置计数模式,tmpcr1首先装入了控制寄存器CR1d 值,然后把其中DIR(位4)和CMS(位6:5)清除,然后或运算上载入数据结构中的值,这里代码再跳转到stm32f10x_tim.h中第362行


#define TIM_CounterMode_Up                ((uint16_t)0x0000)
#define TIM_CounterMode_Down              ((uint16_t)0x0010)
#define TIM_CounterMode_CenterAligned1    ((uint16_t)0x0020)
#define TIM_CounterMode_CenterAligned2    ((uint16_t)0x0040)
#define TIM_CounterMode_CenterAligned3    ((uint16_t)0x0060) 定义的是TIM_CounterMode的几种情况,即控制计数器向上技术或向下计数,或是向上向下交替计数,当设置的是交替计数的情况DIR位为只读。

TIM_ClockDivision为时钟分配因子,其中有 0、2x、4x ,在AHB低时钟时为提高定时精度而实用的倍频器,位于CR1寄存器中的位9:8。这段程序便配置好了CR1寄存器,然后再看下面的程序。 载入了PSC寄存器和ARR寄存器值,如果是高级定时器还有RCR寄存器值,在这一步便基本配置得差不多了,但还是发现函数最后还有一句 TIMx->EGR = TIM_PSCReloadMode_Immediate; 这里在事件寄存器中做了一次软件的事件更新触发 使得其UG位置1。

#define TIM_PSCReloadMode_Immediate       ((uint16_t)0x0001)

基本定时器的工作原理便如本文所述,如有错误非常感谢您的指出。

推荐阅读

史海拾趣

Harwin公司的发展小趣事

1994年,Harbour Industries成为Marmon集团的一员。Marmon集团是一个由多个业务部门组成的国际组织,涵盖了多个行业领域。加入Marmon后,Harbour获得了更多的资源和支持,加速了其全球化布局的步伐。公司不仅在北美和欧洲建立了多个生产基地和销售网络,还积极拓展亚洲市场。这一战略调整使得Harbour能够更好地服务全球客户,进一步提升其市场份额和品牌影响力。

DYMO公司的发展小趣事

在XX世纪XX年代,DYMO公司推出了一款具有划时代意义的标签打印机。这款打印机采用了全新的打印技术,能够打印出清晰、耐用的标签,并且支持多种字体和图案。这一创新技术让DYMO公司在标签打印领域取得了领先地位,并为其后续的产品线奠定了坚实的基础。

Crameda Intersys公司的发展小趣事

作为一家有社会责任感的企业,Crameda Intersys公司不仅关注自身的经济效益,还积极履行社会责任。公司积极参与公益事业,支持教育、环保等领域的发展。同时,公司还注重环保和可持续发展,通过采用环保材料和节能技术,降低生产过程中的能耗和排放。这些举措不仅提升了公司的社会形象,也为社会的可持续发展做出了积极贡献。

这五个故事只是Crameda Intersys公司发展历程中的一部分,但它们足以展现出公司在电子行业中的成长轨迹和不懈追求。在未来的发展中,Crameda Intersys公司将继续以技术创新为引领,积极拓展市场渠道,培养更多优秀人才,加强质量管理和社会责任履行,为电子行业的发展贡献更多的力量。

Datasensor公司的发展小趣事

1972年,意大利的Romano Volta博士凭借对光电技术的深厚理解和前瞻性眼光,创立了DATALOGIC公司,并致力于自动化机器的光电传感器和控制产品的开发。这是Datasensor公司的起点,也是其日后成为全球工业自动化光电检测领域领导者的基石。在初创期,公司面临技术挑战、市场竞争和资金压力等多重困难,但Romano Volta博士的坚定信念和团队的共同努力,使公司逐渐站稳脚跟,并开始赢得市场的认可。

ANSHAN [Anshan Suly Electronics]公司的发展小趣事

近年来,随着电子行业的竞争日益激烈和市场环境的不断变化,ANSHAN公司面临着前所未有的挑战。然而,公司始终保持着清醒的头脑和坚定的信心,积极应对各种挑战。公司不断调整战略方向,优化产品结构,加大创新力度,推出了一系列具有竞争力的新产品。同时,公司还积极拓展新的市场领域和合作伙伴,为公司的持续发展注入了新的动力。

这五个故事展示了ANSHAN公司在电子行业发展中不断克服困难、取得成就的历程。从创业初期的艰辛与坚持,到技术突破与产品升级,再到市场拓展与品牌建设、人才培养与团队建设以及应对挑战与持续创新,ANSHAN公司始终保持着坚定的信念和不懈的努力,在电子行业中谱写了一曲曲壮丽的篇章。

Hpc Technology Inc公司的发展小趣事

ANSHAN公司成立于上世纪末,当时正值电子行业的蓬勃发展期。创始人李先生凭借对电子技术的深厚理解和敏锐的市场洞察力,决定投身于这个充满机遇的领域。初创时期的ANSHAN面临资金短缺、人才匮乏和技术壁垒等多重挑战。然而,李先生带领团队日夜兼程,不断研发新产品,拓展市场渠道。他们坚持自主创新,不断攻克技术难题,逐渐在电子行业中崭露头角。

问答坊 | AI 解惑

我的DSP之路-关于中段向量的一些心得与问题

中断向量表的编写 .ref _bad_trap  ; 这个标号外部没有定义,为什么还用.ref,而不是用..global? .ref _c_int0 ; entry point to the code .sect "vectors" ;自定义 ...…

查看全部问答>

关于谐振电路的品质因素(Q值)

许多电子初学者对于谐振电路的品质因素(Q值)理解的不是很透彻,近日在网上发现这篇文章,讲得比较详细.   在研究各种谐振电路时,常常涉及到电路的品质因素Q值的问题,那末什么是Q值呢?下面我们作详细的论述。   图1是一串 ...…

查看全部问答>

今天是5月最后一天,新的6月即将来临

希望各位工控坛友,多提问题,多回答问题,我们一起成长!…

查看全部问答>

定制的ce5,如何掉中文的哪个小面板

ce5中调用软输入面板双拼,但是输入中文时,软面板显示,同时中文小面板也显示,如何去掉这个小面板呢, 谢谢,在线…

查看全部问答>

人才,你在哪儿??

昔日,未行桃李之事,今遇一通达之事,却无良才平复此役,苦求未果!心中不甚纷扰,叹,吾,何以解?唉!唉!.......…

查看全部问答>

WinCe Wifi配置

大家好, 关于wifi的配置, 下面黑体部分: g_dwOutput & INTF_BSSID == 0????为什么啊?望高人指教! 部分打印信息: In flags used       = [0x7fffffff] dwOutput 打印信息   =  [0x000fffff] ...…

查看全部问答>

CCeCommandBar的工具条的问题

CCeCommandBar的工具条怎么设置才能够使得按钮可以有下压的状态,以表示当前正在进行某项操作。 谢谢…

查看全部问答>

RTX嵌入在Lotus的问题

在Lotus代理中的Initialize中这样写道Dim objRTXCApi As RTXClient.RTXAPI 但objRTXApi出错,定义不了objRTXCApi,在VB中只要添加应用就可以了,请问在Lotus该怎样声明objRTXApi?请高人帮忙解决!谢谢…

查看全部问答>

主题:庭审Sitara, 你是怎样启动的?

TI的Sitara系列ARM器件提供了丰富多彩的启动模式, 可以说东邪西毒南帝北丐,你能想到的启动方式, 它基本上都有了.那么就让我们拷问一下Sitara, 究竟你是怎么实现这么多的启动的? 手段合不合** 是否损害了最广大的人民群众的根本利益?Sitara:尊敬的各 ...…

查看全部问答>

远程水位监测系统

关键词:远程水位监测系统、河道水位监测系统、水库水位监测系统、远程水位监控系统、地下水位监测系统 一 概述 济南惠驰电子科技有限公司基于移动通讯网络开发的水位远程监测系统,支持用户通过手机即可远程无线查看高位水池、水塔的具体水位值 ...…

查看全部问答>