江协科技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.mzph.cn/pingmian/54501.shtml

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

相关文章

传输层协议 —— TCP协议(上篇)

目录 1.认识TCP 2.TCP协议段格式 3.可靠性保证的机制 确认应答机制 超时重传机制 连接管理机制 三次握手 四次挥手 1.认识TCP 在网络通信模型中,传输层有两个经典的协议,分别是UDP协议和TCP协议。其中TCP协议全称为传输控制协议(Tra…

后台数据管理系统 - 项目架构设计-Vue3+axios+Element-plus(0920)

十三、文章分类页面 - [element-plus 表格] Git仓库:https://gitee.com/msyycn/vue3-hei-ma.git 基本架子 - PageContainer 功能需求说明: 基本架子-PageContainer封装文章分类渲染 & loading处理文章分类添加编辑[element-plus弹层]文章分类删除…

pg入门3—详解tablespaces—下

pg默认的tablespace的location为空,那么如果表设置了默认的tablespace,数据实际上是存哪个目录的呢? 在 PostgreSQL 中,如果你创建了一个表并且没有显式指定表空间(tablespace),或者表空间的 location 为…

OpenCV运动分析和目标跟踪(4)创建汉宁窗函数createHanningWindow()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 此函数计算二维的汉宁窗系数。 createHanningWindow是OpenCV中的一个函数,用于创建汉宁窗(Hann window)。汉宁…

肾癌的多模态预测模型-临床-组织学-基因组

目录 摘要 技术路线 ① lncRNA的预测模型 ②病理 WSI 的分类器 ③临床病理分类器 模型结果 与别的模型比较 同行评审学习 1)使用lncRNA的原因 2)模型临床使用意义 3)关于截止值的使用 摘要 A multi-classifier system integrated…

STC89C52定时器与中断 详细介绍 0基础入门

STC89C52定时器与中断 前言定时器/计数器定时器/计数器 功能选择定时器/计数器 模式选择使用寄存器进行功能选择与模式选择 中断使用寄存器进行中断配置中断执行操作 总结完整程序 前言 对于定时器与中断,这是两个完全不同的概念,在单片机中它们也对应着…

【HTTP】认识 URL 和 URL encode

文章目录 认识 URLURL 基本格式**带层次的文件路径****查询字符串****片段标识符** URL encode 认识 URL 计算机中非常重要的概念,并不仅仅是在 HTTP 中使用。用来描述一个网络资源所处的位置,全称“唯一资源定位符” URI 是“唯一资源标识符“严格的说…

mac命令行分卷压缩与合并

对当前目录内的文件压缩的同时分卷 //语法:zip -r -s 1m 压缩文件名.zip 当前路径 zip -r -s 1m split.zip . //解压 zip -s 0 split.zip --out unsplit.zip unzip unsplit.zip 将一个zip文件进行分卷 一个900k的压缩包名为hello.zip,将其分割为每500K一个zip zip - hello.…

Microsoft Edge 五个好用的插件

🐣个人主页 可惜已不在 🐤这篇在这个专栏 插件_可惜已不在的博客-CSDN博客 🐥有用的话就留下一个三连吧😼 目录 Microsoft Edge 一.安装游览器 ​编辑 二.找到插件商店 1.打开游览器后,点击右上角的设置&#…

第十四章:html和css做一个心在跳动,为你而动的表白动画

💖 让心跳加速,传递爱意 💖 在这个特别的时刻,让爱在跳动中绽放!🌟 无论是初次相遇的心动,还是陪伴多年的默契,我们的心总在为彼此跳动。就像这颗炙热的爱心,随着每一次的跳动,传递着满满的温暖与期待。 在这个浪漫的季节,让我们一同感受爱的律动!无论你是在…

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-19

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-19 1. SAM4MLLM: Enhance Multi-Modal Large Language Model for Referring Expression Segmentation Authors: Yi-Chia Chen, Wei-Hua Li, Cheng Sun, Yu-Chiang Frank Wang, Chu-Song Chen SAM4MLLM: 增强多模…

防火墙详解(三)华为防火墙基础安全策略配置(命令行配置)

实验要求 根据实验要求配置防火墙: 合理部署防火墙安全策略以及安全区域实现内网用户可以访问外网用户,反之不能访问内网用户和外网用户均可以访问公司服务器 实验配置 步骤一:配置各个终端、防火墙端口IP地址 终端以服务器为例&#xff…

离散制造 vs 流程制造:锚定精准制造未来,从装配线到化学反应,实时数据集成在制造业案例中的多维应用

使用 TapData,化繁为简,摆脱手动搭建、维护数据管道的诸多烦扰,轻量替代 OGG, Kettle 等同步工具,以及基于 Kafka 的 ETL 解决方案,「CDC 流处理 数据集成」组合拳,加速仓内数据流转,帮助企业…

Android IME输入法启动显示隐藏流程梳理

阅读Android AOSP 12版本代码,对输入法IME整体框架模块进行学习梳理,内容包含输入法框架三部分IMM、IMMS、IMS的启动流程、点击弹出流程、显示/隐藏流程,以及常见问题和调试技巧。 1. IME整体框架​​​​​​​ IME整体分为三个部分&#xf…

股指期货的持仓量指标如何分析?有哪些作用?

股指期货市的持仓量是一个极其重要的指标,它就像市场的“晴雨表”,能反映出投资者的信心、市场的热度以及潜在的趋势。下面,我们就用大白话的方式来详细解读一下股指期货持仓量指标的分析方法及其作用。 一、什么是股指期货持仓量&#xff1…

用CPU训练机器学习模型

人工智能最近的成功通常归功于 GPU 的出现和发展。GPU 的架构通常包括数千个多处理器、高速内存、专用张量核心等,特别适合满足人工智能/机器学习工作负载的密集需求。 不幸的是,人工智能开发的快速增长导致对 GPU 的需求激增,使得 GPU 难以…

ESP32/ESP8266开发板单向一对多ESP-NOW无线通信

目录 简介读取ESP32/ESP8266接收方Receiver的MAC地址ESP32发送方Sender程序ESP32/ESP8266接收方Receiver程序ESP-NOW通信验证总结 简介 本实验通过ESP-NOW无线通信协议实现ESP32开发板向多个ESP32/ESP 8266开发板发送数据。 读取ESP32/ESP8266接收方Receiver的MAC地址 读取…

Nginx反向代理出现502 Bad Gateway问题的解决方案

🎉 前言 前一阵子写了一篇“关于解决调用百度翻译API问题”的博客,近日在调用其他API时又遇到一些棘手的问题,于是写下这篇博客作为记录。 🎉 问题描述 在代理的遇到过很多错误码,其中出现频率最高的就是502&#x…

LabVIEW提高开发效率技巧----代码规范与文档记录

良好的代码规范与文档记录在LabVIEW开发中至关重要。它不仅能够大幅提升开发效率,还为后续的维护和项目交接提供便利。下面将从命名规则、注释标准、功能说明等多个角度,介绍如何通过规范化开发提高项目的可维护性与协作性。 1. 保持一致的命名规则 在L…

Flutter局域网广播(UDP通信)与TCP通信

前言 现在有一个需求,手机和ESP32通过WIFI进行通信。流程如下: 手机创建TCP服务器手机向192.168.0.255的1002端口广播自己的ip地址以及TCP服务器的端口号ESP32监听到1002的广播内容后,连接手机的TCP服务器。最后就是ESP32硬件和TCP服务器进…