STM32TIM时钟(1)

news/2024/7/24 10:02:18 标签: stm32, 单片机, 嵌入式硬件

文章目录

  • 前言
  • 一、介绍部分
    • TIM简介
    • 了解定时器类型
      • 基本定时器框图
      • 通用定时器框图
      • 高级定时器框图
      • 定时器级联关系
    • 所需简化定时器中断流程图
    • 时序部分
      • 预分频器时序
      • 计数器时序
        • 无影子寄存器计数器时序
        • 有影子寄存器计数器时序
    • 时钟树
  • 二、实例部分
    • 使用定时器计数
    • 使用对射红外传感器来控制计数器
      • 电路连接
      • 代码部分
  • 总结
    • 所使用函数总结
    • NVIC分组与优先级分配的关系


前言

简介STM32的时钟,主要连接通用定时器的用法,了解定时器中断的原理,以及如何基础的利用定时器中断


一、介绍部分

TIM简介

在这里插入图片描述

了解定时器类型

注意定时器的编号以及所在总线

在这里插入图片描述

基本定时器框图

计数器加到阈值选择中断会进入NVIC(即框图的向上的箭头),称更新中断,向下的箭头表示产生一个新的事件,触发内部其他电路工作,而不是进入中断,称更新事件。

在这里插入图片描述
主模式触发DAC:即通过更新时间,映射到TRGO,这样就可以不需要频繁中断来输出波形

通用定时器框图

在这里插入图片描述
基本定时器计数器只有向上计数
通用和高级定时器计数器有三种方法如下图:
在这里插入图片描述

高级定时器框图

在这里插入图片描述

定时器级联关系

在这里插入图片描述

所需简化定时器中断流程图

在这里插入图片描述

时序部分

预分频器时序

在这里插入图片描述

计数器时序

在这里插入图片描述

无影子寄存器计数器时序

在这里插入图片描述

有影子寄存器计数器时序

在这里插入图片描述

时钟树

在这里插入图片描述

二、实例部分

使用定时器计数

硬件电路只需连接STM32最小电路与OLED即可
封装定时器初始化函数Timer.c内容如下:

#include "stm32f10x.h"                  // Device header

void Timer_Init(void){
	// 初始化时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	// 使用内部时钟(默认)
	TIM_InternalClockConfig(TIM2);
	// 配置事间基础(时基单元)
	TIM_TimeBaseInitTypeDef TIM_InitStructure;
	// 时钟分频
	TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	// 计算模式
	TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	// 重载值(有1的偏差)<计数值> 10000/10000=1
	TIM_InitStructure.TIM_Period = 10000-1;
	// 预分频(有1的偏差)<频率> 72000000/7200=10000
	TIM_InitStructure.TIM_Prescaler = 7200-1;
	// 重复计数器(不使用)
	TIM_InitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2,&TIM_InitStructure);
	
	// 由于时基初始化后会立即进入中断一次,提前清除以下标志位
	TIM_ClearFlag(TIM2,TIM_IT_Update);
	
	// 中断使能,更新中断到NVIC
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	
	// NVIC分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	// 初始化NVIC
	NVIC_InitTypeDef NVIC_InitStructure;
	// 中断通道
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	// 中断通道使能
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	// 抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	// 响应优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStructure);
	
	// 启动定时器
	TIM_Cmd(TIM2,ENABLE);
}

uint16_t Num;

uint16_t GetNum(void){
	return Num;
}

//中断函数
void TIM2_IRQHandler(void){
	// 获取中断标志位
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){
		Num++;
		// 清除标志位
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}

主函数逻辑main.c内容如下:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"


int main(void)
{
	OLED_Init();
	Timer_Init();
	OLED_ShowString(1,1,"Num:");
	while (1)
	{
		OLED_ShowNum(2,1,GetNum(),4);
		// 查看计数器的变化
		OLED_ShowNum(3,1,TIM_GetCounter(TIM2),4);
	}
}

使用对射红外传感器来控制计数器

红外传感器每产生10次上升沿即Num+1

电路连接

在这里插入图片描述

代码部分

封装定时器内容Timer.c

#include "stm32f10x.h"                  // Device header

void Timer_Init(void){
	// 初始化时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	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);
	
	// 使用外部时钟模式2,不分频,中断方式,滤波
	/*
		@arg TIM_ExtTRGPolarity_Inverted: active low or falling edge active.
        @arg TIM_ExtTRGPolarity_NonInverted: active high or rising edge active.
	*/
	// 使用红外传感器的上升沿作为计数器+1
	TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x0f);
	
	// 配置事间基础(时基单元)
	TIM_TimeBaseInitTypeDef TIM_InitStructure;
	// 时钟分频
	TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	// 计算模式
	TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	// 重载值(有1的偏差)<计数值>
	TIM_InitStructure.TIM_Period = 9-1;
	// 预分频(有1的偏差)<频率>
	TIM_InitStructure.TIM_Prescaler = 1-1;
	// 重复计数器(不使用)
	TIM_InitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2,&TIM_InitStructure);
	
	// 由于时基初始化后会立即进入中断一次,提前清除以下标志位
	TIM_ClearFlag(TIM2,TIM_IT_Update);
	
	// 中断使能,更新中断到NVIC
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	
	// NVIC分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	// 初始化NVIC
	NVIC_InitTypeDef NVIC_InitStructure;
	// 中断通道
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	// 中断通道使能
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	// 抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	// 响应优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStructure);
	
	// 启动定时器
	TIM_Cmd(TIM2,ENABLE);
}

uint16_t Num;

uint16_t GetNum(void){
	return Num;
}

//中断函数
void TIM2_IRQHandler(void){
	// 获取中断标志位
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){
		Num++;
		// 清除标志位
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}

主函数main.c内容:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"


int main(void)
{
	OLED_Init();
	Timer_Init();
	OLED_ShowString(1,1,"Num:");
	OLED_ShowString(2,1,"CNT:");
	while (1)
	{
		OLED_ShowNum(1,6,GetNum(),4);
		// 查看计数器的变化
		OLED_ShowNum(2,6,TIM_GetCounter(TIM2),4);
	}
}


总结

所使用函数总结

// 时钟初始化(时钟分频、计数模式、重载值、分频值、重复计数器值)
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
// 启动定时器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
// 设置外部定时器模式2
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                             uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
//  使中断更新到NVIC                          
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
// 获取定时器计数器的值
uint16_t TIM_GetCounter(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);

NVIC分组与优先级分配的关系

在这里插入图片描述


http://www.niftyadmin.cn/n/5364253.html

相关文章

LiveData 迁移到 Kotlin Flow详解

LiveData ,是Android 2017推出的一个东西,配合MVVM使用。观察者模式,的确简化了我们的工作方式,但 RxJava 等选项,对于当时的初学者来说实在是太复杂了。因此 Architecture Components 团队创建了 LiveData :这是个非常 “有主见的” 可观察数据持有者类,并且是专门为 A…

浅谈——开源软件的影响力

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 ✨特色专栏&#xff1a…

SpringMVC-组件解析

一、引子 我们在上一篇文章Spring MVC-基本概念中&#xff0c;为读者解释了如何使用SpringMVC框架&#xff0c;将承接客户端请求的工作从原生的Servlet转移到我们熟知的Controller中。那么我们不禁会好奇&#xff0c;SpringMVC框架到底做了什么&#xff0c;是怎么把请求分发给…

典型数据结构的模板实现

栈和数组 1.使用类模板实现数组结构定长数组 &#xff08;未完待续..&#xff09;可变数组 2.使用类模板实现栈结构 在我们初步了解编写模板类后&#xff0c;应当做一下代码练习。这节我们就做一个编写代码的补充&#xff0c;方便大家继续学习模板类的嵌套。作为新手而言&#…

ubuntu 安装 ffmpeg 6.0

# ubuntu 22 sudo add-apt-repository ppa:ubuntuhandbook1/ffmpeg6 sudo apt update sudo apt install ffmpeg ffmpeg --version# 删除源 sudo add-apt-repository --remove ppa:ubuntuhandbook1/ffmpeg6 参考&#xff1a; FFmpeg 6.0 Released! How to Install in Ubuntu 22…

新版MQL语言程序设计:代理模式的原理、应用及代码实现

文章目录 一、什么代理模式二、代理模式的实现原理三、代理模式应用场景四、代理模式的代码实现 一、什么代理模式 代理模式是一种结构型设计模式&#xff0c;它允许通过创建一个代理对象来控制对另一个对象的访问。代理对象充当了客户端和目标对象之间的中介&#xff0c;可以在…

【MySQL】学习并使用DQL实现排序查询和分页查询

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-SP91zTA41FlGU0Ce {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

docker入门问题二

一、什么是Dockerfile&#xff0c;它的作用是什么&#xff1f; Dockerfile是一个文本文件&#xff0c;其中包含了一组用于构建Docker镜像的指令和参数。这些指令应用于一个基础镜像&#xff0c;并通过层叠的修改步骤来创建新的镜像。Dockerfile提供了一种可重复且自动化的方式…