江协科技STM32学习- P14 示例程序(定时器定时中断和定时器外部时钟)

    🚀write in front🚀  
🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​ 

💬本系列哔哩哔哩江科大STM32的视频为主以及自己的总结梳理📚 

🚀Projeet source code🚀   

💾工程代码放在了本人的Gitee仓库:iPickCan (iPickCan) - Gitee.com

引用:

STM32入门教程-2023版 细致讲解 中文字幕_哔哩哔哩_bilibili

Keil5 MDK版 下载与安装教程(STM32单片机编程软件)_mdk528-CSDN博客

STM32之Keil5 MDK的安装与下载_keil5下载程序到单片机stm32-CSDN博客

0. 江协科技/江科大-STM32入门教程-各章节详细笔记-查阅传送门-STM32标准库开发_江协科技stm32笔记-CSDN博客

江科大STM32学习笔记(上)_stm32博客-CSDN博客

STM32学习笔记一(基于标准库学习)_电平输出推免-CSDN博客

STM32 MCU学习资源-CSDN博客

术语:

英文缩写描述
GPIO:General Purpose Input Onuput通用输入输出
AFIO:Alternate Function Input Output复用输入输出
AO:Analog Output模拟输出
DO:Digital Output数字输出
内部时钟源 CK_INT:Clock Internal内部时钟源
外部时钟源 ETR:External clock 时钟源 External clock 
外部时钟源 ETR:External clock mode 1外部时钟源 External 时钟模式1
外部时钟源 ETR:External clock mode 2外部时钟源 External 时钟模式2
外部时钟源 ITRx:Internal trigger inputs外部时钟源,ITRx (Internal trigger inputs)内部触发输入
外部时钟源 TIx:external input pin 外部时钟源 TIx (external input pin)外部输入引脚

正文:

0. 概述

从 2024/06/12 定下计划开始学习下江协科技STM32课程,接下来将会按照哔站上江协科技STM32的教学视频来学习入门STM32 开发,本文是视频教程 P2 STM32简介一讲的笔记。


定时器共四个部分,分为八个小节笔记。本小节为第一部分第一节。

🌳在第一部分,是定时器的基本定时的功能:定时中断功能、内外时钟源选择

🌳在第二部分,是定时器的输出比较功能,最常见的用途是产生PWM波形,用于驱动电机等设备

🌳在第三部分,是定时器的输入捕获功能和主从触发模式,来实现测量方波频率

🌳在第四部分,是定时器的编码器接口,能够更加方便读取正交编码器的输出波形,编码电机测速


🌵知识点get:

滤波器工作原理:可以滤掉信号的抖动干扰。在一个固定的时钟频率f下进行采样,如果连续N个采样点都为相同的电平,那就代表输入信号稳定了,就把这个采样值输出出去;如果这N个采样值不全都相同,那就说明信号有抖动,这时就保持上一次的输出,或者直接输出低电平也行,这样就能保证输出信号在一定程度上的滤波;这里的采样频率f和采样点数N都是滤波器的参数,频率越低,采样点数越多,滤波效果越好,不过相应的信号延迟就越大;采样频率f由内部时钟直接而来,也可以是由内部时钟加一个时钟分频而来,这个分频多少是由参数ClockDivision决定的,这个参数其实跟时基单元关系并不大,它的可选值可以选择1分频(也就是不分频),2分频和4分频

1.🔎定时中断和时钟源选择相关库函数使用 

定时器相关的库函数非常多,本节仅对将要使用的库函数和 亿些使用细节 进行说明(即使这样也还是很多)。

定时中断基本结构如下,便于理解下面的库函数及程序流程

 定时器初始化步骤如下,对应定时中断结构图

🌾第一步,RCC开启时钟,定时器的基准时钟和整个外设的工作时钟就都打开了
🌾第二步,选择时基单元的时钟源,对于定时中断就选择内部时钟源
🌾第三步,配置时基单元,包括预分频器、自动重装载器、计数模式等,参数用结构体配置
🌾第四步,配置输出中断控制,允许更新中断输出到NVIC
🌾第五步,配置NVIC,在NVIC中打开定时器中断通道并分配一个优先级
🌾第六步,运行控制,使能计数器,当定时器使能后,计数器就开始计数了,当计数器更新时,触发中断
🌾最后再写一个中断函数,中断函数每隔一段时间就能自动执行一次


 2.🔎定时器TIM的库函数

  • 基本配置、时基单元、中断输出控制、NVIC、运行控制函数
// 恢复定时器缺省配置
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);
  • 时钟源选择函数
//选择内部时钟
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);//选择ITRx其它定时器时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//选择TIx捕获通道时钟,对于外部引脚的波形一般都会有极性选择和滤波器
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,uint16_t TIM_ICPolarity, uint16_t ICFilter);/选择ETR通过外部时钟模式1输入的时钟
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);//选择ETR通过外部时钟模式2输入的时钟,如果不需触发输入功能本函数可与上面函数互换
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);//单独用来配置ETR引脚的预分频器、极性、滤波器这些参数
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);

3.🔎参数(PSC、ARR等)更改函数(在程序运行过程中修改

以下,单独的函数可以方便地更改PSC\ARR等参数

// 预分频值设置,TIM_PSCReloadMode为是否应用输入缓冲功能配置
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);

获取定时器中断标志位和清除定时器中断标志位,使用方法和EXTI外部中断相同。

//在主函数中获取定时器中断标志位
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);

4.🔎使用定时器库函数的一些细节

* 选择内部时钟函数:定时器上电后默认选择内部时钟,如果要选择内部时钟,这一句可以省略。

TIM_InternalClockConfig(TIM_TypeDef* TIMx);

时基单元初始化函数TIN_TimeBaseInit:在配置结构体变量时,会遇到以下几个细节问题 

  • 🌵1.TIM_TimeBaseInitStructure.TIM_ClockDivision (采样)时钟分频频率选择

  在定时器的外部信号输入引脚一般都有一个滤波器消除信号的抖动干扰,它的工作原理是:在一个固定的时钟频率f下进行采样,如果连续N个采样点都是相同的电平,就代表输入信号稳定了,就将采样值输出到下一级电路;如果N个采样点不全都相同,就说明信号有抖动,这时保持上一次的输出,或直接输出低电平。 这样就能保证输出信号在一定程度上的滤波。这里的采样频率f 和采样点数N,f和N是滤波器的参数,频率越低,采样点数越多,滤波效果就越好,不过相应的信号延迟就越大。
  采样频率f的来源可以是内部时钟直接提供,也可以是内部时钟加一个时钟分频而来。 分频是多少,就由参数TIM_ClockDivision决定。可见 TIM_ClockDivision与时基单元的关系并不大,它的可选值可以选择1分频,2分频和4分频。

  • 🌵2.TIM_TimeBaseInitStructure.TIM_CounterMode  计数器模式

        TIM_CounterMode_Up  (向上计数)

        TIM_CounterMode_CenterAligned1  (中央对齐计数)

  • 🌵3.TIM_TimeBaseInitStructure.TIM_Period     周期,ARR自动重装器的值
  • 🌵4.TIM_TimeBaseInitStructure.TIM_Prescaler  PSC预分频器的值

以上2-3-4参数就是时基单元里面每个关键寄存器的参数,在配置结构体变量时,并没有能直接操作计数器CNT的参数。如果需要,可以采用SetCounter和GetCounter两个函数来操作计数器。

  • 🌵5.TIM_TimeBaseInitStructure.TIM_RepetitionCounter 重复计数寄存器,通过这个参数可以设置重复计数寄存器。但是通用定时器中没有这一个寄存器,故可以直接设置为0。

  • 🌵6.定时时间的计算

决定定时时间的参数,是TIM_Period和TIM_Prescaler
  参考公式:

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

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

* 定时1s也就是定时频率为1Hz,定时频率=72M/ (PSC + 1) / (ARR + 1) = 1s =1Hz,那就可以PSC给7200,ARR给10000(1MHz等于10^6Hz),然后两个参数再减1。

* 注意PSC(TIM_Prescaler)和ARR(TIM_Period)的取值都要在0~65535之间。

* PSC和ARR的取值不是唯一的。可以预分频给少点,自动重装给多点,这样就是以一个比较高的频率计比较多的数;也可以预分配给多点,自动重装给少点,这样就是以一个比较低的频率计比较少的数;两种方法都可以达到目标的定时时间。

* 在这里预分频是对72M进行7200分频,得到的就是10k的计数频率,在10k的频率下,计10000个数,就是1s的时间。

  • 🌵7.在TIM_TimeBaseInit函数的最后,会立刻生成一个更新事件,来重新装载预分频器和重复计数器的值。预分频器有缓冲寄存器,我们写入的PSC和ARR只有在更新事件时才会起作用。但是更新事件和更新中断是同时发生的,更新中断会置更新中断标志位,手动生成一个更新事件,就相当于在初始化时立刻进入更新函数执行一次,在开启中断之前手动清除一次更新中断标志位,就可以避免刚初始化完成就进入中断函数的问题。

* 使能中断函数TIM_ITConfig

TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //开启更新中断到NVIC的通道 

启动定时器 

TIM_Cmd(TIM2,ENABLE);//当产生更新时,就会触发中断 

* 外部时钟配置函数TIM_ETRClockMode2Config

🌵1.TIM_ExtTRGPrescaler外部时钟预分频器:可以选择外部时钟分频关闭(1分频)、2分频、4分频、8分频。


🌵2.TIM_ExtTRGPolarity 外部触发的极性TIM_ExtTRGPolarity_Inverted为反向极性,即低电平和下降沿有效;TIM_ExtTRGPolarity_NonInverted为不反向,即高电平和上升沿有效。


🌵3.ExtTRGFilter 外部输入滤波器:工作原理与内部时钟的滤波器相似,它的值可以是0x00到0x0F之间的一个值,其决定了采样的f和N,具体的对应关系在手册中有对应表:

🌵4.GPIO配置:因为是使用外部接口输入时钟,故在使用该函数之前还需要配置GPIO端口。对于定时器,手册中给的推荐配置是浮空输入。但是浮空输入会导致引脚的输入电平极易受干扰,所以输入信号的功率不小时一般选择上拉或下拉输入。当外部的输入信号功率很小,内部的上拉/下拉电阻(较大)可能会影响到这个输入信号,这时就需要用浮空输入,防止影响外部输入的电平。

5. 🔎STM32F103 本节使用到的TIM定时器函数介绍

打开Keil5中文件 stm32f10x_tim.h ,在文件的尾部位置就是 stm32f103 TIM 定时器的操作函数原型,可以看到定时器操作的函数还是比较多的,我们先学习本节要使用到的函数后面的在使用的时候再慢慢学习。

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);定时器中断配置,允许产生定时器中断
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配置,预分频,极性,过滤器

5.1 ➡️定时器使用的步骤

按照如下stm32f10x 定时器的框图,使用stm32 定时器基本按照如下的基本步骤

  1. 🌾启用RCC内部时钟,这样stm32外设和定时器才有时钟源输入。
  2. 🌾定时钟时钟源选择,可以选择内部时钟源,外部时钟源ETR,外部ITRx,外部TIx捕获通道。
  3. 🌾时基单元配置,配置时钟预分频器,自动重装载器
  4. 🌾定时器中断输出控制,允许定时器更新中断进入NVIC
  5. 🌾NVIC中配置定时器中断的优先级
  6. 🌾定时器运行控制,启动定时器

5.2 ➡️定时器时基单元配置

在stm32f10x_tim.h中定时器时基单元配置函数原型如下

void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);定时器“时基单元”配置

‘TIM_TimeBaseInit()’接收两个输入参数,一个是定时器TIMx,一个是时基单元配置结构体,该函数配置定时器框图中的如下红框部分。

5.3 ➡️定时器时钟源选择

5.3.1 定时器选择内部时钟源,函数原型如下

void TIM_InternalClockConfig(TIM_TypeDef* TIMx);定时器选择内部时钟源

该函数选择stm32定时器的时钟源为内部时钟源,也就是如下图中箭头所示从内部时钟源进入时基单元。

5.3.2 定时器选择外部时钟源ITRx,函数原型如下

void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);定时器选择外部时钟源 ITRx

该函数选择stm32定时器的时钟源为外部时钟源ITRx,也就是如下图中箭头所示从外部时钟源ITRx进入时基单元。

5.3.3 定时器选择外部时钟源TIx,函数原型如下

void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter);
定时器选择外部时钟源 TIx,参数预分频,极性,过滤器

该函数选择stm32定时器的时钟源为外部时钟源TIx,也就是如下图中箭头所示从外部时钟源TIx进入时基单元。

5.3.4 定时器选择外部时钟源ETR 外部时钟模式2,函数原型如下

void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                             uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
定时器选择外部时钟源 ETR时钟模式2,参数预分频,极性,过滤器

该函数选择stm32定时器的时钟源为外部时钟源ETR 外部时钟模式2,也就是如下图中箭头所示从外部时钟源进入时基单元。

5.3.5 定时器选择外部时钟源ETR 外部时钟模式1,函数原型如下

 该函数选择stm32定时器的时钟源为外部时钟源ETR 外部时钟模式1,也就是如下图中箭头所示从外部时钟源进入时基单元。

5.4 ➡️定时器除上面的时基单元配置,内/外时钟源选择,本节可能用到的函数

定时器除上面的时基单元配置,内/外时钟源选择外,本节可能用到的函数还有如下:

void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);定时器预分频值设置,函数参数处预分频值外,还有预分频值Reload重加载模式设置是否使用预分频值“影子寄存器”,是预分频值设置立即生效,还是预分频值先写入“影子寄存器”等到“更新事件”时再生效
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);定时器计数器计数模式,向上计数,向下计数
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);定时器ARR重装载寄存器,预装载功能是否启用
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);获取预分频器的值

6.🔎实验1-定时器定时中断实例 

本次实验要完成的现象是:定义一个 uint16_t 的 Num 变量,使其每秒+1。器件连接图和程序源码如下所示:

timer.c

#include "stm32f10x.h"                  // Device header
#include "Timer.h"void Timer_Init(void)
{//Setp 1.//RCC APB1的外设时钟控制,因为TIM2在STM32的APB1外设总线上RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//Setp 2.//选择时基单元的时钟,使用内部RCC时钟 CLK_INT (Clock_Internal)TIM_InternalClockConfig(TIM2);//Setp 3.//配置时基单元TIM_TimeBaseInitTypeDef TimeBaseInitStruct;TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟信号滤波使用,滤波的采样频率,采样点数TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;	//计数器向上计数TimeBaseInitStruct.TIM_Period = 10000 - 1;					//Auto-Reload Register 自动重装载寄存器的值,记得需要减一TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;				//预分频器的值,记得需要减一TimeBaseInitStruct.TIM_RepetitionCounter = 0;				//重复计数器的值	TIM_TimeBaseInit(TIM2, &TimeBaseInitStruct);//Setp 4.//使能定时中断TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//Setp 5.//NVIC配置NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//NVIC优先级分组NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStruct);					//NVIC配置定时器中断优先级//Setp 6.//定时器启动TIM_Cmd(TIM2, ENABLE);
}uint16_t Num;//定时器TIM2,中断处理函数
void TIM2_IRQHandler(void)
{//检查中断标志位if(SET == TIM_GetITStatus(TIM2, TIM_IT_Update)){Num++;//清除中断标志位TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}

timer.h

#ifndef __TIMER_H__
#define __TIMER_H__
void Timer_Init(void);
#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "oled.h"
#include "Countersensor.h"
#include "Encoder.h"
#include "Timer.h"extern uint16_t Num;int main(int argc, char *argv[])
{	OLED_Init();OLED_ShowString(1, 1, "Cnt:");Timer_Init();while(1){OLED_ShowNum(1,5,Num,5);}return 1;
}

6.1 😛实验结果

实验结果如下

6.1 😛实验一个小问题,按下Reset键之后Cnt值从1开始增加

江科大STM32本节教程里提到,本TIM定时器中断实验一个小问题,按下Reset键之后Cnt值从1开始增加。本地测试结果的确也是按下开发板上Reset按键之后,OLED显示屏上Cnt的值从1开始增加。

江科大STM32本节教程讲解了出现这个问题的原因,在stm32f10x_timeer.c 中找到 'TIM_TimeBaseInit()'的函数实现。

在函数的结尾

😎😎为什么要加这个,我们知道预分频器是有一个缓冲寄存器的,我们写的值只有在更新事件时才真正起作用,所以这里为了让值立即起作用,就在这里最后手动生成了一个更新事件,这样预分频器的值就有效了,但同时它的副作用是更新事件更新中断是同时发生的,更新中断会置更新中断标志位,当我们之后一旦初始化晚了,更新中断就会立刻进入。这就是我们刚一上电,就立刻进中断的原因。😎😎

🤓🤓那解决方法也非常简单。

就是在Time_BaseInit()的后面,在开启中断的前面,手动调用一下 Time_CleanFlag(TIME2, X) 将更新中断标志位清除一下,就能避免刚初始化我那侧灰姑娘就进中断的问题了。

 Timer.c 

#include "stm32f10x.h"                  // Device header
#include "Timer.h"void Timer_Init(void)
{//Setp 1.//RCC APB1的外设时钟控制,因为TIM2在STM32的APB1外设总线上RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//Setp 2.//选择时基单元的时钟,使用内部RCC时钟 CLK_INT (Clock_Internal)TIM_InternalClockConfig(TIM2);//Setp 3.//配置时基单元TIM_TimeBaseInitTypeDef TimeBaseInitStruct;TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟信号滤波使用,滤波的采样频率,采样点数TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;	//计数器向上计数TimeBaseInitStruct.TIM_Period = 10000 - 1;					//Auto-Reload Register 自动重装载寄存器的值,记得需要减一TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;				//预分频器的值,记得需要减一TimeBaseInitStruct.TIM_RepetitionCounter = 0;				//重复计数器的值	TIM_TimeBaseInit(TIM2, &TimeBaseInitStruct);//在TIM_TimeBaseInit()之后,在启用中断之前,手动清除一下定时器更新中断标志位,//就能避免初始化之后就立即进入中断的问题了。TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//Setp 4.//使能定时中断TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//Setp 5.//NVIC配置NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//NVIC优先级分组NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStruct);					//NVIC配置定时器中断优先级//Setp 6.//定时器启动TIM_Cmd(TIM2, ENABLE);
}

此时再使用Keil5 MDK编译,STLink下载到开发板测试一下,按下 Reset 按钮之后,OLED屏幕上Cnt值就从 0 开始增加了,问题解决。

 

7.🔎实验2-定时器外部时钟选择

本实验使用定时器外部时钟源 EXT mode2 (External Trigger mode2)。

*🌵 可以在引脚定义图里找TIMx的ETR引脚是哪个

* 🌵在上一个定时中断实例程序基础上进行更改;基本任务仍然是定时中断,时钟部分就不使用内部时钟了

本次实验要完成的现象是:用光敏传感器手动模拟一个外部时钟,定义一个 uint16_t 的 Num 变量,当外部时钟触发10次(预分频之后的脉冲)后Num + 1。器件连接图和程序源码如下所示:

timer.c

#include "stm32f10x.h"                  // Device header
#include "Timer.h"void Timer_Init(void)
{//Setp 1.//RCC APB1的外设时钟控制,因为TIM2在STM32的APB1外设总线上RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//GPIO初始化//开启GPIO外设时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;		//上拉输入GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStruct);//Setp 2.//选择时基单元的时钟,使用内部RCC时钟 CLK_INT (Clock_Internal)//TIM_InternalClockConfig(TIM2);//选择时基单元的时钟,使用外部时钟ETR mode2TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0f);//Setp 3.//配置时基单元TIM_TimeBaseInitTypeDef TimeBaseInitStruct;TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟信号滤波使用,滤波的采样频率,采样点数TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;	//计数器向上计数TimeBaseInitStruct.TIM_Period = 10 - 1;						//Auto-Reload Register 自动重装载寄存器的值,记得需要减一TimeBaseInitStruct.TIM_Prescaler = 1 - 1;					//预分频器的值,记得需要减一TimeBaseInitStruct.TIM_RepetitionCounter = 0;				//重复计数器的值	TIM_TimeBaseInit(TIM2, &TimeBaseInitStruct);//在TIM_TimeBaseInit()之后,在启用中断之前,手动清除一下定时器更新中断标志位,//就能避免初始化之后就立即进入中断的问题了。TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//Setp 4.//使能定时中断TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//Setp 5.//NVIC配置NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//NVIC优先级分组NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStruct);					//NVIC配置定时器中断优先级//Setp 6.//定时器启动TIM_Cmd(TIM2, ENABLE);
}uint16_t Num;//定时器TIM2,中断处理函数
void TIM2_IRQHandler(void)
{//检查中断标志位if(SET == TIM_GetITStatus(TIM2, TIM_IT_Update)){Num++;//清除中断标志位TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}

 

7.1 😛实验结果

7.2😛 实验注意和经验总结

1.初始化GPIOA PA0的时候,需要注意使用的是

    RCC_APB2PeriphClockCmd()

   别错误的写成了 RCC_APB1PeriphClockCmd(),编译器并不会报错,但是GPIOA却没有启用RCC时钟,是因为 GPIOA 是挂载 APB2上的外设,不能使用RCC_APB1开启它的时钟。

2. 在做定时器外部时钟源选择 ETR mode2的实验的时候,需要注意调小时钟源的预分频器值和重装载器的值。因为手动使用红外对射传感器的方式来产生方波信号的频率很低,如果有一个很大的预分频值,则需要产生很多的预分频方波信号之后才能产生一个计数器时钟信号让计数器加一。

8. 🔎结束

本章至此结束

 

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

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

相关文章

Leetcode990.等式方程的可满足性

题目 原题链接 等式方程的可满足性 思路 定义一个长度为26(变量为小写字母)的数组充当并查集,并将数组中的元素初始化为 -1判断“”并合并元素,将相等的放在一个集合中判断“!”;不等的如果在一个集合中,则…

应用密码学第一次作业(9.23)

一、Please briefly describe the objectives of information and network security,such as confidentiality, integrity, availability , authenticity , and accountability The objectives of information and network security include: Confidentiality: Protecting se…

【小白向】怎么去除视频水印?HitPaw帮你轻松解决

序言 HitPaw是一款优秀的去除视频水印的工具。 特点:不仅仅能够去除图片、视频里的固定水印,还能去除移动水印。 尤其是它的AI去水印功能,效果非常好。 极简使用教程 下载安装 HitPaw需要在电脑上安装软件才能使用。 支持Windows系统和…

MySQL和SQL的区别简单了解和分析使用以及个人总结

MySQL的基本了解 运行环境,这是一种后台运行的服务,想要使用必须打开后台服务,这个后台服务启动的名字是在安装中定义的如下图(个人定义MySQL88)区分大小写图片来源 可以使用命令net start/stop 服务名,开…

浮动静态路由

浮动静态路由 首先我们知道静态路由的默认优先级是60&#xff0c;然后手动添加一条静态路由优先级为80的路由作为备份路由。当主路由失效的备份路由就会启动。 一、拓扑图 二、基本配置 1.R1: <Huawei>system-view [Huawei]sysname R1 [R1]interface GigabitEthernet…

怎么开通GitHub Copilot?不会开通GitHub Copilot?一文看懂

GitHub Copilot 简介 GitHub Copilot 是由 GitHub 推出的一种人工智能编程助手&#xff0c;旨在帮助开发者更快速、更高效地编写代码。GitHub Copilot 是基于 OpenAI 的 GPT&#xff08;Generative Pre-trained Transformer&#xff09;模型开发的&#xff0c;它能够通过理解编…

[大语言模型] LINFUSION:1个GPU,1分钟,16K图像

1. 文章 2409.02097 (arxiv.org)https://arxiv.org/pdf/2409.02097 LINFUSION: 1 GPU, 1 MINUTE, 16K IMAGE 摘要 本文介绍了一种新型的扩散模型LINFUSION&#xff0c;它能够在保持高分辨率图像生成性能的同时显著降低时间和内存复杂度。该模型采用了基于Transformer的UNet进…

解决IDEA出现:java: 程序包javax.servlet不存在的问题

问题截图&#xff1a; 解决如下&#xff1a; 1. 点击文件——>项目结构 2. 点击库——>点击——>点击java 3. 找到Tomcat的文件夹&#xff0c;找到lib文件夹中的servlet-api.jar&#xff0c;点击确定 4. 选择要添加的模块 5. 点击应用——>确定

828华为云征文 | 将Vue项目部署到Flexus云服务器X实例并实现公网访问

一、Flexus云服务器X实例简介 1.1 概述 华为云Flexus X实例是华为云推出的一款创新云服务器产品&#xff0c;它主要面向中小企业和开发者&#xff0c;旨在解决传统云服务中的痛点&#xff0c;提供更加灵活、高效的云服务体验。 华为深刻洞察了中小企业和开发者在云服务应用中遇…

分享几种方式获取免费精致的Live2d模型

文章目录 1、 Live2D官方示例数据集&#xff08;可免费下载&#xff09;2、模之屋3、unity商店4、直接b站搜索5、youtube6、BOOTH完结 1、 Live2D官方示例数据集&#xff08;可免费下载&#xff09; 官方提供了一些 Live2D实例模型给大家下载使用 地址&#xff1a;https://ww…

Unity webgl跨域问题 unity使用nginx设置跨域 ,修改请求头

跨域 什么是跨域 跨域是指浏览器因安全策略限制&#xff0c;阻止一个域下的网页访问另一个域下的资源。 一些常见的跨域情况&#xff1a; 协议不同 从 http://example.com 请求 https://example.com。域名不同 从 http://example.com 请求 http://anotherdomain.com。端口不…

高效高质量SCI论文撰写及投稿

第一章、论文写作准备即为最关键 1、科技论文写作前期的重要性及其分类 2、AI工具如何助力学术论文 3、研究主题确定及提高创新性 兴趣与背景&#xff1a;选择一个您感兴趣且有背景知识的研究领域。 创新性&#xff1a;选题和研究设计阶段如何提高学术创新性的方法。 研究缺…

Python3 爬虫教程 - Web 网页基础

Web网页基础 1&#xff0c;网页的组成HTMLcssJavaScript2&#xff0c;网页的结构 3&#xff0c;节点树及节点间的关系4&#xff0c;选择器开头代表选择 id&#xff0c;其后紧跟 id 的名称。如&#xff1a;div 节点的 id 为 container&#xff0c;那么就可以表示为 #container 1…

CentOS7更换阿里云yum更新源

目前CentOS内置的更新安装源经常报错无法更新&#xff0c;或者速度不够理想&#xff0c;这个时候更换国内的镜像源就是一个不错的选择。 备份内置更新源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 下载阿里云repo源&#xff08;需要系统…

9.23-部署项目

部署项目 一、先部署mariadb [rootk8s-master ~]# mkdir aaa [rootk8s-master ~]# cd aaa/ [rootk8s-master aaa]# # 先部署mariadb [rootk8s-master aaa]# # configmap [rootk8s-master aaa]# vim mariadb-configmap.yaml apiVersion: v1 kind: ConfigMap metadata:name: ma…

工业机器视觉中的常见需求

目录 学习目的 熟系 Halcon的原因 专业性强&#xff1a; 高性能&#xff1a; 丰富的功能库 学习 OpenCV 的原因 开源与免费&#xff1a; 灵活性与可扩展性&#xff1a; 广泛的应用&#xff1a; 学习资源丰富&#xff1a; 总结 学习背景 工业视觉检测中常见分类 一、定…

D. Minimize the Difference (Codeforces Round 973 Div. 2)

D. Minimize the Difference 思路&#xff1a; 发现操作是单向的从左往右取高补低&#xff0c;最终目标是尽可能趋于平均&#xff0c;使最大值最小和使最小值最大。可以用二分答案法分别找到两个最值&#xff0c;然后做差即可。 关于这种算法的正确性没有做严格的证明&#x…

基于单片机的无线宠物自动喂食系统设计

本设计研究了一种无线宠物自动喂食器&#xff0c;其功能是先将宠物饲料放入其中&#xff0c;通过设定喂食时间点&#xff0c;当到达这一时间点后&#xff0c;系统开始播报语音同时控制步进电机转动&#xff0c;自动进行喂食。本设计主要研究怎么设定时间并进行投喂&#xff0c;…

npm 安装 与 切换 淘宝镜像

一、镜像源 npm默认镜像源是国外的&#xff0c;安装依赖速度较慢&#xff0c;使用国内的镜像源速度会快一些。 1、设置淘宝镜像源&#xff1a; #最新地址 淘宝 NPM 镜像站喊你切换新域名啦! npm config set registry https://registry.npm.taobao.org&#xff08;弃用了&…

C++11新特性和扩展(1)

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 C11新特性和扩展 收录于专栏 [C进阶学习] 本专栏旨在分享学习C的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1.C11简介 2. 列表初始…