当前位置: 首页 > news >正文

STM32学习笔记5-TIM定时器-1

定时器的基本功能:定时中断功能、内外时钟源选择

定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断

16位计数器(执行计数定时的一个寄存器)、预分频器(对计数器的时钟进行分频)、自动重装寄存器(计数的目标值,就是我想要计多少个时钟申请中断)的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时,可以使定时器进行级联,达到指数爆炸的定时时间。

不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能

根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

  • STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4

基本定时器

基本定时器只能选择内部时钟(CK_INT,默认72Mz)

预分频器:输出=输入/自己配置个数;实际分频系数=预分频器的值+1。

CNT计数器:会根据分配后的数据进行记录,当达到目标值时,产生中断,完成定时的任务;默认是向上计数;在通用和高级定时器中还有向下计数,和中央对齐(就是从0一直加,加到目标值后,又递减)

自动重装载寄存器:存储目标值,当计数器的值==自动重装载寄存器的目标值时,会产生一个中断信号 ,并清零计数器,与CNT计数器相连接。

对于自动重装载寄存器的中断信号,一般叫更新中断,之后会通过NVIC,再配置好NVIC的定时器通道,那定时器的更新中断可以被CPU响应。

:产生一个事件,同理在自动重装载寄存器中,为更新事件


定时器的一大特色:DAC主从触发模式:可以让内部的硬件在不受程序的控制下实现自动运行


在使用DAC时,可能会用DAC输出一段波形:需要每隔一段时间来触发一次DAC,让它输出下一个电压点;

如果用中断,会不断的触发中断来响应触发输出(TRGO),会很频繁

如用主从触发模式,会触发事件,由事件映射到触发输出(TRGO),然后TRGO直接接到DAC的触发转换引脚上。整个过程不需要软件的参与,实现了硬件的自动化。

通用定时器:

高级定时器

定时中断基本结构

中断输出控制:是一个中断输出的允许位,用来处理有必要的中断和无必要的中断

预分频器时序

  • 计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)

计数器时序

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

                                          = CK_PSC / (PSC + 1) / (ARR + 1)

        实际分频系数=预分频器的值+1。

ARR:自动重装载寄存器

在电路中含有阴影的,都有缓冲机制

在计数器里的ARR中就有缓冲器;且是手动配置;

计数器无预装时序

计数器有预装时序

RCC时钟树:

时钟树是STM32中用来产生和配置时钟,并且把配置好的时钟发送到各个外设的系统;

时钟是所有外设运行的基础,所以必须优先配置

STM32F10xxx参考手册(中文).pdf-13-15章

接线图:

定时器定时中断

定时器不涉及外部设备,所以放入System的文件夹中

void TIM_DeInit(TIM_TypeDef* TIMx); //恢复初始

void TIM_TimeBaseInit(TIM_TypeDef* TIMx,       TIM_TimeBaseInitTypeDef*TIM_TimeBaseInitStruct); //时基单元初始化

void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct); //对结构体变量赋一个默认值

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState); 使能计数器——运行控制

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState); //用来使能中断信号的——中断输出控制

//以下6个函数,选择外部时钟:

void TIM_InternalClockConfig(TIM_TypeDef* TIMx);  //选择内部时钟

void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource); //选择ITRx其他定时器的的时钟

void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,

                                uint16_t TIM_ICPolarity, uint16_t ICFilter);//选择Tix捕获通道的时钟

void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,

                             uint16_t ExtTRGFilter);//选择ETR通过外部时钟模式1输入时钟

void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,

                             uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter); //选择ETR通过外部时钟模式2输入时钟

void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,

                   uint16_t ExtTRGFilter);//单独来配置ETR引脚的预分频器、极性、滤波器参数的

void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);//单独写预分频值的

void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);//用来改变计数器的计数模式

void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState); //自动重装载寄存器功能配置

void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);//给计数器写一个值

void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);//给自动重装载寄存器写一个值

uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);//获取当前计数器的值

uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);//获取当前预分频器的值

//以下四个函数,用来获取标志位和清除标志位的

FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);

void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);

ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);

void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Time.h"//我们想让定时器每秒自动帮我们加一下Num这个变量
uint16_t Num;
int main(void){OLED_Init();Time_Init();OLED_ShowString(1,3,"hello world!"); //如果超出16个字,会从头到尾重新输入OLED_ShowString(2,3 ,"Num:");while(1){OLED_ShowNum(2,7,Num,5);OLED_ShowNum(3,7,TIM_GetCounter(TIM2),5);}
}
void TIM2_IRQHandler(void){//判断标志位是否存在if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){Num++;TIM_ClearITPendingBit(TIM2,TIM_IT_Update);}
}Time.c
#include "stm32f10x.h"                  // Device header//1.RCC开启时钟//2.选择时基单元的时钟源,我们选择内部时钟源//3.配置时基单元,通过结构体配置//4.配置输出中断控制,允许更新中断输出到NVIC//5.配置NVIC,并打开定时器中断的通道,并分配一个优先级//6.运行控制,对定时器进行使能,配置中断函数
void Time_Init(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);  //开启APB1的时钟函数,TIM2在APB1总线中//选择时基单元TIM_InternalClockConfig(TIM2);//系统默认是内部时钟,不写也可以//配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //给输入的滤波器一个采样频率TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数方式//因为公式里CK_PSC / (PSC + 1) / (ARR + 1),所以两个值需要手动减1//设置1s,默认是72Mz,分频分7200,每一个波形就是10k,再设置ARR目标值为10000的时候为一个周期TIM_TimeBaseInitStructure.TIM_Period=10000-1;//ARR自动重装器的值TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1;//预分配器的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器的值,我们用的通用寄存器,所以直接写0就好了TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);//我们发现当我们按下复原键时,定时器是从1开始的,就说明复原的时候,自己走了一次,在此函数中发现它为了让值立刻起作用,所以它自己更新了中断’//想要从0开始,就需要把此时中断的标志值清空TIM_ClearFlag(TIM2,TIM_IT_Update);//使能更新中断TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//开启了更新中断到NVIC的通路//配置NVICNVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置NVIC的分组通道NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;//匹配NVIC的TIM2通道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);
//	}
//}
//由于我们打算在主文件上使用这个中断函数来改写Num的值,第一个方法:extern Num到Time.c中;第二个方法,把中断函数放到主文件里

定时器外部时钟

main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Time.h"//我们想让定时器每秒自动帮我们加一下Num这个变量
uint16_t Num;
int main(void){OLED_Init();Time_Init();OLED_ShowString(1,3,"hello world!"); //如果超出16个字,会从头到尾重新输入OLED_ShowString(2,1 ,"Num:");OLED_ShowString(3,1 ,"Count:");while(1){OLED_ShowNum(2,7,Num,5);OLED_ShowNum(3,7,Timer_GetCount(),5);}
}
void TIM2_IRQHandler(void){//判断标志位是否存在if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){Num++;TIM_ClearITPendingBit(TIM2,TIM_IT_Update);}
}Time.c
#include "stm32f10x.h"                  // Device header//1.RCC开启时钟//2.选择时基单元的时钟源,我们选择内部时钟源//3.配置时基单元,通过结构体配置//4.配置输出中断控制,允许更新中断输出到NVIC//5.配置NVIC,并打开定时器中断的通道,并分配一个优先级//6.运行控制,对定时器进行使能,配置中断函数
void Time_Init(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);  //开启APB1的时钟函数,TIM2在APB1总线中//初始化GPIO引脚RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;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);//选择Ext时基单元TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);//配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //给输入的滤波器一个采样频率TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数方式//因为公式里CK_PSC / (PSC + 1) / (ARR + 1),所以两个值需要手动减1//不需要分频的原因是我们是使用的手动拨频,是有一定的限制的,很慢,不像机器TIM_TimeBaseInitStructure.TIM_Period=10-1;//ARR自动重装器的值TIM_TimeBaseInitStructure.TIM_Prescaler=1-1;//预分配器的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器的值,我们用的通用寄存器,所以直接写0就好了TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);//我们发现当我们按下复原键时,定时器是从1开始的,就说明复原的时候,自己走了一次,在此函数中发现它为了让值立刻起作用,所以它自己更新了中断’//想要从0开始,就需要把此时中断的标志值清空TIM_ClearFlag(TIM2,TIM_IT_Update);//使能更新中断TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//开启了更新中断到NVIC的通路//配置NVICNVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置NVIC的分组通道NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;//匹配NVIC的TIM2通道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);
//	}
//}
//由于我们打算在主文件上使用这个中断函数来改写Num的值,第一个方法:extern Num到Time.c中;第二个方法,把中断函数放到主文件里//实时查看CNT计数器的值
uint16_t Timer_GetCount(void){return TIM_GetCounter(TIM2);
}

http://www.dtcms.com/a/321412.html

相关文章:

  • windows安装Docker Desktop报错One prerequisite is not fulfilled.
  • CVPR2025-DEIM改进项目介绍
  • Linux 运维与优化的系统化思维:从内核到生产环境的全链路管理
  • 景区车辆监控调度管理平台建设方案
  • Jetpack Compose 常用控件
  • 电子EDI:MaxLinear EDI 需求分析
  • Go 的错误处理方式深度解析—— error vs panic vs recover:机制原理与实战取舍
  • vue3 遍历 map 用法
  • 密码学安全模型(Security Model):用形式化框架定义“安全“
  • Microsoft Dynamics AX 性能优化解决方案
  • 网络资源模板--基于Android Studio 实现的麻雀笔记App
  • CSS:BFC
  • 五种IO模型 阻塞IO 多路转接之select 多路转接之poll
  • 灰狼算法+四模型对比!GWO-CNN-LSTM-Attention系列四模型多变量时序预测
  • VIOO IQOO7手机 解锁BL ROOT教程
  • 光猫、路由器和交换机
  • 如何使用 pg_rman 进行 PostgreSQL 的备份与恢复
  • 解决 vscode 编辑 markdown 文件时退格键/backspace 删除卡顿问题
  • 【普中STM32精灵开发攻略】--第 14 章 动态数码管实验
  • PyQt 中 pyqtSignal 的使用
  • Orangepi5-RK3588安装ffmpeg硬编码版本
  • UE4/UE5 Android 超大(视频)文件打包/防拷贝方案
  • 【07】OpenCV C++实战篇——鼠标在图片上绘制矩形,计算矩形区域内灰度值的累加值显示在图片上,支持连续多次框选,快速计算结果,快速刷新画面不卡顿
  • Atto Round 1 (Codeforces Round 1041, Div. 1 + Div. 2) A-C
  • 【身心健康】能量管理——为你的情绪和身体注入积极力量
  • LVS高可靠
  • [激光原理与应用-184]:光学器件 - 光学器件中晶体的用途、分类、特性及示例
  • CSS--后端也有自己的CSS要学
  • 化工厂安全升级:分布式光纤传感的 “实时监测 + 精准预警” 方案
  • 【mongoose】E11000 duplicate key error collection: test.counters