STM32定时器(TIM)

目录

一、概述

二、定时器的类型

三、时序

四、定时器中断基本结构

五、定时器定时中断代码

六、定时器外部时钟代码


一、概述

TIM(Timer)定时器

  • 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
  • 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHZ计数时钟下可以实现最大559.65s的定时(stm32级联两个16位计数器)
  • 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
  • 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

二、定时器的类型

  • 基本定时器:拥有定时中断、主模式触发DAC的功能

如上图,内部时钟的来自RCC的TIMxCLK,这里的频率值一般都是系统的主频72MHZ,也就是基准时钟自动重装载寄存器、PSC预分频器和CNT计数器组成叫做时基单元,通向时基单元的计数基准频率就是72MHZ。

预分频器:如果预分频器写0,就是1分频,输出频率=输入频率=72MHZ;如果预分频器写1,就是2分频,输出频率=输入频率/2=36MHZ;依次类推,所以预分频器的值和实际的分频系数相差了1,预分频系数=预分频器值+1。这个预分频器是16位,最大值就是65535,也就是65536分频。

CNT计数器:对预分频器后的时钟进行计数,计数时钟每来一个上升沿,计数器的值加1。这个计数器也是16位的。计数器从0开始加,加到65535时或目标值时就会从0开始重新加。

自动重装载寄存器:也是16位的,它存的就是我们写入的计数目标值。自动重装值是固定的目标值,当计数值等于自动重装值时,也就是计时时间到了,会产生一个中断信号,并且清零计数器,计数器自动开始下一次的计数计时。

向上的箭头UI:当计数值等于自动重装值时,产生的一个中断信号,我们叫做它更新中断,之后就会通往NVIC,我们再配置好NVIC的定时器通道,那定时器的更新中断就能得到CPU的响应了。

向下的箭头UI:代表会产生一个事件,这里对应的事件就叫做 "更新事件",更新事件不会触发中断,但可以触发内部其他电路的工作

  • 通用定时器:拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能。

如上图,通用定时器就比基本定时器复杂的多了。

基本定时器计数模式只有向上计数模式,通用和高级定时器计数模式:向上计数、向下计数、中央对齐。

以预分频器为分界线,最上面的部分就是内外时钟源选择和主从触发模式的结构了。内部时钟由主频72MHZ产生。

外部时钟输入:

TI1F_ED连接的是输入捕获单元的CH1引脚,ED(Edge)就是边沿的意思,上升沿和下降沿均有效。最后,这个时钟还能通过TI1FP1和TI2FP2获得。

编码器接口可以读取正交编码器的输出波形。

TRGO那部分电路,可以把内部的一些事件映射到这个TRGO引脚上,比如也可以把定时器内部的一些事件映射到这里来,用于触发其他定时器、DAC或ADC。

右下部分,捕获/比较寄存器、输出控制。

捕获/比较寄存器是输入捕获和输出比较共用的。

  • 高级定时器暂时不用。

三、时序

  • 预分频器时序

CNT_EN :计数器使能,高电平计数正常运行,低电平计数器停止。

CK_CNT:预分频器输出时钟,也是计数器时钟。

计数器寄存器:在计数器时钟的驱动下,下面的计数器寄存器也跟随时钟的上升沿不断自增。

预分频缓冲器:如果对正在计数的计数频率进行分频,会在计数完成之后,下一次计数才会改变。

计数器计数频率:CK_CNT=CK_PSC/(PSC+1);PSC是预分频的值。

  • 计数器时序

计数器溢出频率CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)

时间 t=1/CK_CNT_OV=(PSC + 1)x (ARR + 1)/CK_PSC;

在配置寄存器时PSC一般设置为7200-1;

t=7200*(ARR+1)/72000000=(ARR+1)/10000=(ARR+1)x0.1ms

ARR为自动重装寄存器

  • 计数器无预装时序

如图所示,原来目标值为FF,然后修改为36,然后到达36就会发生更新。

  • 计数器有预装时序

如图所示,多了一个影子寄存器。原来目标值为F5,然后加了影子寄存器,不会到达36发生更新,而到达F5发生更新,而是在下一次更新中断或事件才开始生效。加入影子寄存器的目的是让值得变化和更新事件同步发生,防止在运行途中更改造成错误。

四、定时器中断基本结构

如下图:

五、定时器定时中断代码

  • 配置时钟外设
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
  • 配置内部时钟
TIM_InternalClockConfig(TIM2);
  • 配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;     //滤波频率
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //计数方式
TIM_TimeBaseInitStruct.TIM_Period=10000-1;                 //自动重装载寄存器ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=7200-1;               //预分频器
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
  • 配置中断输出控制
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //启动中断
  • 配置NVIC
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;    //从启动文件找后缀为md.s
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitStructure);
  • 启动定时器
TIM_Cmd(TIM2,ENABLE);
  • 中断服务函数
void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)   //更新中断就是产生一个中断标志位{Num++;TIM_ClearITPendingBit(TIM2,TIM_IT_Update); }}

完整代码,如下:

Timer.c:

#include "stm32f10x.h"                  // Device headerextern uint16_t Num;
void Timer_Init(void)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;NVIC_InitTypeDef NVIC_InitStructure;//1.配置时钟,用那个外设RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//2.内部时钟配置TIM_InternalClockConfig(TIM2);//3.配置时基单元TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;     //滤波频率TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //计数方式TIM_TimeBaseInitStruct.TIM_Period=10000-1;                 //自动重装载寄存器ARR,定时1sTIM_TimeBaseInitStruct.TIM_Prescaler=7200-1;               //预分频器TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;            //这个是高级定时器才用的,这里不用,给0TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);TIM_ClearFlag(TIM2,TIM_FLAG_Update);//4.配置中断输出控制,打开中断TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//5.NVIC配置NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitStructure);//6.启动定时器TIM_Cmd(TIM2,ENABLE);
}void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){Num++;TIM_ClearITPendingBit(TIM2,TIM_IT_Update);  //更新中断就是产生一个中断标志位}}

Timer.h:

#ifndef _TIMER_H
#define _TIMER_Hvoid Timer_Init(void);#endif

main.c:

#include  "stm32f10x.h"                  // Device header
#include  "OLED.h"
#include  "delay.h"
#include  "Timer.h"uint16_t Num=0;int main(void)
{OLED_Init();Timer_Init();OLED_ShowString(1,1,"Num:");while(1) {OLED_ShowNum(1,5,Num,4);OLED_ShowNum(2,5,TIM_GetCounter(TIM2),4);   //TIM_GetCounte()用来获得计数器的值}}

运行起来,会有一点问题,就是每次复位Num的值都是从1开始,而不是从0开始。这是因为啥呢?

可以查看时基单元函数TIM_TimeBaseInit(),最后有这么一句话,如下图:绿字意思位:会立即产生一个更新事件去重装载预分频器和重复计数器。

更新时事的同时,也会更新中断,产生中断让预分频器的值立即生效。这是由于预分频缓冲器的存在,我们写入的值不会立即生效,需要在下一计数开始才生效。而系统是想让预分频器的值立即生效,所以会产生一次中断,产生了一个中断标志位,我们需要在中断初始化之前要进行清除。需要用到清除标志位函数,在启动中断之前进行初始化:

TIM_ClearFlag(TIM2,TIM_FLAG_Update);  //清除中断标志位
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //启动中断

这样每次复位Num的值都是从0开始。

六、定时器外部时钟代码

把内部时钟变为外部时钟输入,其他部分基本不变。然后利用对射式红外传感器模拟外部时钟输入。

  • 配置时钟外设

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  • GPIO初始化
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;  //PA0口
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);
  • 外部时钟配置
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);
  • 配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;     //滤波频率
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //计数方式,向上计数
TIM_TimeBaseInitStruct.TIM_Period=10-1;                 //自动重装载寄存器ARR,需要手动模拟,值不要太大
TIM_TimeBaseInitStruct.TIM_Prescaler=1-1;               //预分频器
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
  • 配置中断输出控制,打开中断
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
  • NVIC配置
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitStructure);
  • 启动定时器
TIM_Cmd(TIM2,ENABLE);
  • 中断服务函数和内部时钟的服务函数共用
void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){Num++;TIM_ClearITPendingBit(TIM2,TIM_IT_Update);  //更新中断就是产生一个中断标志位}}

完整代码,如下:

Timer.c

#include "stm32f10x.h"                  // Device headerextern uint16_t Num;
void Timer_Init(void)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;//1.配置时钟,用那个外设RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);//2.外部时钟配置TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);//3.配置时基单元TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;     //滤波频率TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //计数方式TIM_TimeBaseInitStruct.TIM_Period=10-1;                 //自动重装载寄存器ARR,需要手动模拟,值不要太大TIM_TimeBaseInitStruct.TIM_Prescaler=1-1;               //预分频器TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);TIM_ClearFlag(TIM2,TIM_FLAG_Update);//4.配置中断输出控制,打开中断TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//5.NVIC配置NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitStructure);//6.启动定时器TIM_Cmd(TIM2,ENABLE);
}void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){Num++;TIM_ClearITPendingBit(TIM2,TIM_IT_Update);  //更新中断就是产生一个中断标志位}}

Timer.h

#ifndef _TIMER_H
#define _TIMER_Hvoid Timer_Init(void);#endif

main.c

#include  "stm32f10x.h"                  // Device header
#include  "OLED.h"
#include  "delay.h"
#include  "Timer.h"uint16_t Num=0;int main(void)
{OLED_Init();Timer_Init();OLED_ShowString(1,1,"Num:");OLED_ShowString(2,1,"CNT:");while(1) {OLED_ShowNum(1,5,Num,4);OLED_ShowNum(2,5,TIM_GetCounter(TIM2),4); //获取计数器CNT中计数值}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/439898.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣刷题 | 两数之和

目前主要分为三个专栏,后续还会添加: 专栏如下: C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读! 初来乍到,如有错误请指出,感谢! 给定一个整数数组 nums 和…

网站建设完成后,切勿让公司官网成为摆设

在当今这个数字化时代,公司官网已经成为企业展示形象、传递信息、吸引客户的重要平台。然而,许多企业在网站建设完成后,往往忽视了对官网的持续运营和维护,导致官网逐渐沦为摆设,无法发挥其应有的作用。为了确保公司官…

15分钟学 Python 第40天:Python 爬虫入门(六)第一篇

Day40 :Python 爬取豆瓣网前一百的电影信息 1. 项目背景 在这个项目中,我们将学习如何利用 Python 爬虫技术从豆瓣网抓取前一百部电影的信息。通过这一练习,您将掌握网页抓取的基本流程,包括发送请求、解析HTML、存储数据等核心…

jvisualvm学习

系列文章目录 JavaSE基础知识、数据类型学习万年历项目代码逻辑训练习题代码逻辑训练习题方法、数组学习图书管理系统项目面向对象编程:封装、继承、多态学习封装继承多态习题常用类、包装类、异常处理机制学习集合学习IO流、多线程学习仓库管理系统JavaSE项目员工…

ssm服装店销售管理系统

系统包含:源码论文 所用技术:SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习,获取源码请私聊我 需要定制请私聊 目 录 摘 要 I Abstract II 第1章 绪论 1 1.1研究背景 1 1.2研究意义 1 1.3国内外研究现状 2 1.3.1国外研…

提高顾客满意度,餐饮业如何开展客户调研?

餐饮行业需明确调研目的,选择合适工具,设计问卷,收集并分析数据,持续追踪优化。通过客户调研,提升服务质量、顾客满意度和竞争力,利用ZohoSurvey等工具实现高效调研。 一、明确调研目的 进行客户调研前&am…

【hot100-java】【将有序数组转换为二叉搜索树】

二叉树篇 中序遍历实现 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right…

yolo自动化项目实例解析(七)自建UI--工具栏选项

在上一章我们基本实现了关于预览窗口的显示&#xff0c;现在我们主要完善一下工具栏菜单按键 一、添加任务ui 先加个ui页面&#xff0c;不想看ui的复制完这个文件到ui目录下转下py直接从第二步开始看 vi ui/formpy.ui <?xml version"1.0" encoding"UTF-8&q…

Mysql数据库--删除和备份、约束类型

目录 1.删除操作 1.1表的删除操作 1.2数据库备份 2.约束 2.1基本概况 2.2not null约束演示 2.3unique约束演示 2.4default约束演示 2.5primary key约束演示 2.6foreign key约束演示 2.7check约束演示 1.删除操作 1.1表的删除操作 delete from 表名 where 条件…

kubeadm部署k8s

1.1 安装Docker [rootk8s-all ~]# wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.huaweicloud.com/docker-ce/linux/centos/docker-ce.repo [rootk8s-all ~]# sed -i sdownload.docker.commirrors.huaweicloud.com/docker-ce /etc/yum.repos.d/docker-ce.repo [ro…

数据结构双向链表和循环链表

目录 一、循环链表二、双向链表三、循环双向链表 一、循环链表 循环链表就是首尾相接的的链表&#xff0c;就是尾节点的指针域指向头节点使整个链表形成一个循环&#xff0c;这就弥补了以前单链表无法在后面某个节点找到前面的节点&#xff0c;可以从任意一个节点找到目标节点…

用HTML5+CSS+JavaScript庆祝国庆

用HTML5CSSJavaScript庆祝国庆 中华人民共和国的国庆日是每年的10月1日。 1949年10月1日&#xff0c;中华人民共和国中央人民政府成立&#xff0c;在首都北京天安门广场举行了开国大典&#xff0c;中央人民政府主席毛泽东庄严宣告中华人民共和国成立&#xff0c;并亲手升起了…

[单master节点k8s部署]31.ceph分布式存储(二)

Ceph配置 Ceph集群通常是一个独立的存储集群&#xff0c;可以部署在 Kubernetes 集群之外。Ceph 提供分布式存储服务&#xff0c;能够通过 RADOS、CephFS、RBD&#xff08;块存储&#xff09;、和 RGW&#xff08;对象存储&#xff09;等方式与 Kubernetes 集成。即使 Ceph 部…

王者农药更新版

一、启动文件配置 二、GPIO使用 2.1基本步骤 1.配置GPIO&#xff0c;所以RCC开启APB2时钟 2.GPIO初始化&#xff08;结构体&#xff09; 3.给GPIO引脚设置高/低电平&#xff08;WriteBit&#xff09; 2.2Led循环点亮&#xff08;GPIO输出&#xff09; 1.RCC开启APB2时钟。…

CSS | 响应式布局之媒体查询(media-query)详解

media type(媒体类型)是CSS 2中的一个非常有用的属性&#xff0c;通过media type我们可以对不同的设备指定特定的样式&#xff0c;从而实现更丰富的界面。media query(媒体查询)是对media type的一种增强&#xff0c;是CSS 3的重要内容之一。随着移动互联网的发展&#xff0c;m…

如何在算家云搭建CosyVoice(文生音频)

一、CosyVoice简介 CosyVoice 是一个开源的超强 TTS&#xff08;‌文本转语音&#xff09;‌模型&#xff0c;‌它支持多种生成模式&#xff0c;‌具有极强的语音自然可控性。‌ 具有以下特点&#xff1a; 语音合成 &#xff1a;能够将文本转换为自然流畅的语音输出。多语种…

CSS 盒子属性

1. 盒子模型组成 1.1 边框属性 1.1.1 四边分开写 1.1.2 合并线框 1.1.3 边框影响盒子大小 1.2 内边距 注意&#xff1a; 1.3 外边距 1.3.1 嵌套块元素垂直外边距的塌陷 1.4 清除内外边距 1.5 总结

k8s-集群部署1

k8s-集群部署1 一、基础环境准备二、docker环境准备三、k8s集群部署1.kubeadm创建集群2.使用kubeadm引导集群 总结 一、基础环境准备 首先&#xff0c;需要准备三个服务器实例&#xff0c;这里我使用了阿里云创建了三个实例&#xff0c;如果不想花钱&#xff0c;也可以在VM上创…

多输入多输出预测 | NGO-BP北方苍鹰算法优化BP神经网络多输入多输出预测(Matlab)

多输入多输出预测 | NGO-BP北方苍鹰算法优化BP神经网络多输入多输出预测&#xff08;Matlab&#xff09; 目录 多输入多输出预测 | NGO-BP北方苍鹰算法优化BP神经网络多输入多输出预测&#xff08;Matlab&#xff09;预测效果基本介绍程序设计往期精彩参考资料 预测效果 基本介…

SCUC博客摘录「 储能参与电能市场联合出清:SCUC和SCED模型应用于辅助服务调频市场(IEEE39节点系统)」2024年10月6日

2.1 SCUC模型在本方法中&#xff0c;首先利用SCUC模型确定机组出力计划和储能充放电计划。SCUC模型是电力系统经济调度的重要工具&#xff0c;通过优化发电机组出力计划和调度&#xff0c;实现电力系统的经济性和可靠性。在考虑储能的情况下&#xff0c;SCUC模型需要考虑储能的…