TIM时钟中断——输出捕获、输入捕获、编码器接口测速

2024-06-08 08:28

本文主要是介绍TIM时钟中断——输出捕获、输入捕获、编码器接口测速,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

输出捕获

在这里插入图片描述

通道与DMA

计算机中的通道是一种专用于输入/输出(I/O)操作的控制器,它充当了主机(包括CPU和内存)与外部设备之间数据传输的桥梁。通道的主要目的是提高系统的并行处理能力,允许CPU与I/O设备同时工作,从而提升整体效率。

通道的基本特点和功能:
独立性:通道是一个相对独立的处理单元,拥有自己的指令集(通道指令),可以独立于CPU执行I/O操作。这意味着CPU可以启动通道后继续执行其他任务,不必等待I/O操作完成。

直接内存访问 (DMA):通道能够直接与内存交互,进行数据传输,无需CPU介入每个数据传输步骤,这大大减少了CPU的负担。

并行处理:通过使用通道,计算机系统能够实现CPU处理计算任务与通道处理I/O操作的同时进行,即CPU与I/O并行工作,提高了系统性能。

多种类型:常见的通道类型有字节多路通道、数组多路通道和选择通道,每种类型适用于不同场景下的I/O需求。例如,字节多路通道适合连接大量低速设备,而数组多路通道适合处理高速数据传输的设备。

中断机制:一旦通道完成指定的I/O任务,它会向CPU发送一个中断信号,通知CPU任务已完成,这样CPU可以适时地处理后续操作或结果。

与DMA的关系:
通道技术通常与直接内存访问(DMA)结合使用,但两者有所区别。DMA主要关注数据在内存与外设间的直接传输,减少CPU在数据传输中的参与,而通道技术更侧重于对外部设备的管理和控制,是一种更高级别的I/O处理机制。

总的来说,计算机中的通道是提高系统效率的关键组件之一,特别是在需要频繁进行大量I/O操作的大型系统或特定应用环境中。

频率测量

在这里插入图片描述

测周法: 一个标准频率用时= 1/Fc, 两个上升沿为一个周期,T= (1/Fc) * N , 所以 Freq= Fc/N

高频率用测频法,低频率用测周法

PWMI模式: 波形上升沿统计频率,下降沿用于统计占空比

主从触发模式(主动发出信号和被动接收信号,进行相关操作的)

在这里插入图片描述

输入捕获结构(基本定时器TIM1、2都可以选择从模式让CNT自动清零,TIM3、4则需要通过捕获中断来实现,消耗资源)

在这里插入图片描述 - 标准频率(Fc)= 72MHz / 预分频系数

  • 频率太低的时候,周期变长,CNT的值可能会溢出

PWMI基本结构

在这里插入图片描述

  • CCR2 / CCR1 = 占空比

输入捕获连线图

在这里插入图片描述

输入捕获结构

在这里插入图片描述

//测频率
#include "stm32f10x.h"
#include "PWM.h"
//输入捕获初始化
void IC_Init(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);   //输入捕获,开启时钟3RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);    //配置GPIOGPIO_InitTypeDef GI;GI.GPIO_Mode = GPIO_Mode_IPU;    //引脚复用GI.GPIO_Pin = GPIO_Pin_6;         //TIM3专用引脚之一GI.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GI);TIM_InternalClockConfig(TIM2);TIM_TimeBaseInitTypeDef TI;     //配置时钟单元TI.TIM_ClockDivision = TIM_CKD_DIV2;TI.TIM_CounterMode = TIM_CounterMode_Up;TI.TIM_Period = 65536-1;  //ARR满减TI.TIM_Prescaler = 72-1;    //PSC,决定计数频率(72MHz/psc)TI.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM3,&TI);  //初始化输入捕获单元TIM_ICInitTypeDef TIC;TIC.TIM_Channel = TIM_Channel_1;TIC.TIM_ICFilter = 0xF;   //滤波器,避免干扰信号,数值越大,采样效果越好TIC.TIM_ICPolarity = TIM_ICPolarity_Rising;TIC.TIM_ICPrescaler =  TIM_ICPSC_DIV1;    //每次更新事件,都捕获一次输入TIC.TIM_ICSelection = TIM_ICSelection_DirectTI;TIM_ICInit(TIM3,&TIC);//从模式,触发源以及模式选择(触发复位)TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);//启动定时器TIM_Cmd(TIM3,ENABLE);
}//自定义计算频率函数,这里用测周法:Freq = Fc/N  , 由于PSC为72-1,所以Fc=1MHz=(72MHz / PSC)
uint32_t IC_GetFreq(void){return 1000000  / (TIM_GetCapture1(TIM3)+1);  
}#ifndef IC_H
#define IC_H
//输入捕获初始化
void IC_Init(void);
//自定义计算频率函数,这里用测周法:Freq = Fc/N  , 由于PSC为72-1,所以Fc=1MHz=(72MHz / PSC)
uint32_t IC_GetFreq(void);
#endif

测PWM占空比(两通道同时捕获同一引脚)

#ifndef IC_H
#define IC_H
//输入捕获初始化
void IC_Init(void);
//自定义计算频率函数,这里用测周法:Freq = Fc/N  , 由于PSC为72-1,所以Fc=1MHz=(72MHz / PSC)
uint32_t IC_GetFreq(void);
//获取占空比,上升沿次数记录在CCR1,下降沿次数记录在CCR2
uint32_t IC_GetDuty(void);
#endif#include "stm32f10x.h"                  // Device header
//初始化PWM
void PWM_Init(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);   //时钟中断RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);  //为呼吸灯指定引脚PA0//法二:为了提高引脚使用率,重映射端口,打开AFIO,重映射引脚,解除调试端口
//  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//  GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
//  GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //禁用JTAG调试,这里用的是ST-LinkGPIO_InitTypeDef GI;GI.GPIO_Mode = GPIO_Mode_AF_PP;   //(外设)控制引脚,PA2要用复用推挽输出GI.GPIO_Pin = GPIO_Pin_0;GI.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GI);TIM_InternalClockConfig(TIM2);     //内部时钟TIM_TimeBaseInitTypeDef TI;     //时钟中断配置TI.TIM_ClockDivision = TIM_CKD_DIV1 ;TI.TIM_CounterMode = TIM_CounterMode_Up;TI.TIM_Period = 100-1;    // ARRTI.TIM_Prescaler = 720-1;      // PSC ,CK_PSC=72MHZTI.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM2,&TI);//时钟中断的输出比较模块定义: PWM波形,频率1KHZ,占空比50%,分辨率为 1%,TIM_OCInitTypeDef OCI;TIM_OCStructInit(&OCI); //先初始化输出比较模块,后续修改某个属性,其余属性默认OCI.TIM_OCMode = TIM_OCMode_PWM1;OCI.TIM_OCPolarity = TIM_OCPolarity_High;   //极性,高电平有效电平OCI.TIM_OutputState = TIM_OutputState_Enable;OCI.TIM_Pulse = 0;    //CCR捕获寄存器,可以通过改变CCR的值,调制PWM波形的占空比,从而改变LED灯的亮度    TIM_OC1Init(TIM2,&OCI);  //pwm波形初始化 ,将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1,端口PA0TIM_Cmd(TIM2,ENABLE);  //时钟中断使能}//动态改变CCR
void PWM_SetCompare1(uint16_t Compare){TIM_SetCompare1(TIM2,Compare); //该函数用于设置CRR的值}//动态修改PSC ,  PSC的频率=更新频率= 72MHZ/(PSC+1)/(ARR+1)
void PWM_SetPrescaler_TIM2(uint16_t PSC) {TIM_PrescalerConfig(TIM2,PSC,TIM_PSCReloadMode_Immediate);     //先把修改写进影子寄存器,触发更新事件再修改}#include "stm32f10x.h"    // Device header
#include "MyDelay.h"   //自定义延时函数
#include "Delay.h"     //官方延迟函数
#include "Button.h"   //按键Led驱动
#include "stdio.h"
#include "OLED.h"
#include "Servos.h"
#include "DCmotors.h"
#include "PWM.h"
#include "IC.h"int main(void){//环境配置OLED_Init();PWM_Init();IC_Init();/*显示静态字符串*/OLED_ShowString(1, 1, "Freq:00000Hz");		//1行1列显示字符串Freq:00000HzOLED_ShowString(2, 1, "Duty:00%");			//2行1列显示字符串Duty:00%PWM_SetPrescaler_TIM2(7200-1); //Freq = 72MHz / (PSC+1) / (ARR+1),这里的频率是1000Hz  PWM_SetCompare1(70);    //Duty = CCR/100 ,这里的占空比是50%while(1){OLED_ShowNum(1, 6, IC_GetFreq(), 5);	//不断刷新显示输入捕获测得的频率OLED_ShowNum(2, 6, IC_GetDuty(), 2);	//不断刷新显示输入捕获测得的占空比      }return 0;
}

编码器(常见应用:使用PWM驱动电机,再用编码器测电机速度,接着用PID算法进行闭环控制)

在这里插入图片描述

正交编码器(抗噪声,可根据正交编码规律,对不符合规律的噪声状态,不予计次)

在这里插入图片描述

编码器接口基本结构

在这里插入图片描述

编码器工作模式(与正反转的正交波形对应)

在这里插入图片描述

实例(对应通道给上升沿,不反相)

在这里插入图片描述

实例(对应通道给下降沿,反相)

在这里插入图片描述

接线图

在这里插入图片描述

TIM_RepetitionCounter

它是STM32定时器的一个寄存器位,它的主要作用是控制定时器中断的触发频率。具体来说,TIM_RepetitionCounter定义了在产生一个更新(溢出)中断之前,定时器的计数器需要重复溢出的次数。

当你设置TIM_RepetitionCounter的值大于0时,定时器的计数器会在每次达到自动重载值后,不是立即产生中断,而是需要再重复计数指定的次数(TIM_RepetitionCounter的值),之后才会产生一个更新中断。这样一来,你就可以通过这个参数来延长中断产生的周期,而不需要改变基础的计数配置(如预分频和计数周期)。

例如,如果你将TIM_RepetitionCounter设置为3,那么定时器会在计数器溢出4次(初始溢出加3次重复溢出)后才触发一次中断,从而实现了中断周期的扩展。

如果TIM_RepetitionCounter没有被显式配置,其值可能是随机的,这可能会导致定时或中断行为不符合预期,特别是在需要精确定时的应用场景中。因此,在初始化定时器时,明确设置TIM_RepetitionCounter是非常重要的,尤其是当你依赖于定时器中断来进行精确控制时。

编码器测速代码

#include "stm32f10x.h"                  // Device header
#include "MyDelay.h"   //自定义延时函数
#include "Delay.h"     //官方延迟函数
#include "Button.h"   //按键Led驱动
#include "stdio.h"
#include "MyClock.h"
#include "OLED.h"
#include "Encoder.h"int16_t Speed;      
int main(void){//环境配置OLED_Init();  Timer_Init();Encoder_Init();while(1){OLED_ShowSignedNum(1,5,Speed,5);   //现象:旋转一次计数器加4,因为上升沿、下降沿各两次//Delay_ms(1000);                      //测完旋转编码器的速度后,延时读秒,之后下一个循环CNT清零,可以通过时钟中断取代延时函数}    return 0;
}
//时钟中断,中断触发频率由TImer_Init中的ARR、PSC的值决定,频率执行配置
void TIM2_IRQHandler(void){if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){Speed = Encoder_GetCounter();TIM_ClearITPendingBit(TIM2,TIM_IT_Update);}
}#ifndef Encode_H
#define Encode_H
//编码器配置初始化
void Encoder_Init();
//获取计数值
int16_t Encoder_GetCounter();
#endif#include "stm32f10x.h"
#include "OLED.h"
int16_t Encoder_Count;  //用于记录编码器旋转次数
//编码器配置初始化(旋转计数)
void Encoder_Init(){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GI;GI.GPIO_Mode = GPIO_Mode_IPU;GI.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GI.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GI);//内部时钟TIM_InternalClockConfig(TIM3);//时钟属性配置TIM_TimeBaseInitTypeDef TI;TI.TIM_ClockDivision = TIM_CKD_DIV1;TI.TIM_CounterMode = TIM_CounterMode_Up;TI.TIM_Period = 65536-1;   //ARR计数范围最大,方便换成负数TI.TIM_Prescaler = 1-1;   //1-1,不分频TI.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM3,&TI);//时钟的输入捕获通道配置(两个)TIM_ICInitTypeDef TIC;TIM_ICStructInit(&TIC);   //避免输入捕获某些属性配置失败,可以先配置默认属性TIC.TIM_Channel = TIM_Channel_1;  //通道1TIC.TIM_ICFilter = 0xF;TIC.TIM_ICSelection = TIM_ICSelection_DirectTI;TIM_ICInit(TIM3,&TIC); TIM_ICStructInit(&TIC);   //避免输入捕获某些属性配置失败,可以先配置默认属性TIC.TIM_Channel = TIM_Channel_2;   //通道2TIC.TIM_ICFilter = 0xF;TIC.TIM_ICSelection = TIM_ICSelection_DirectTI;TIM_ICInit(TIM3,&TIC); //编码器接口,配置为T1,T2都计数,非反相,配置极性TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);//时钟使能TIM_Cmd(TIM3,ENABLE);       }
//获取计数值
int16_t Encoder_GetCounter(){uint16_t Counter = TIM_GetCounter(TIM3);TIM_SetCounter(TIM3,0);  //获取CNT值后,清零return Counter;
}//端口0中断处理函数
void EXTI0_IRQHandler(void){//获取中断标志位if(EXTI_GetITStatus(EXTI_Line0) == SET){ //触发中断后,端口为高电平if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0){ //B下降沿,A低电平,正向旋转Encoder_Count++;OLED_ShowNum(2,1,Encoder_Count,16);}//清除标志位EXTI_ClearITPendingBit(EXTI_Line0);}
}//端口1中断处理函数
void EXTI1_IRQHandler(void){//获取中断标志位if(EXTI_GetITStatus(EXTI_Line1) == SET){ //触发中断后,端口为高电平if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0){  //A下降沿,B低电平,逆向旋转Encoder_Count--;OLED_ShowNum(2,1,Encoder_Count,16);}//清除标志位EXTI_ClearITPendingBit(EXTI_Line1);}
}//定时器初始化,时钟中断更新频率设置为1s
//定时器初始化
void Timer_Init(void){// Enables or disables the Low Speed APB (APB1) peripheral clock.(外部时钟)RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//(内部时钟)TIM_InternalClockConfig(TIM2);//基本定时器配置TIM_TimeBaseInitTypeDef BaseTI;BaseTI.TIM_ClockDivision = TIM_CKD_DIV1;BaseTI.TIM_CounterMode =   TIM_CounterMode_Up; //向上计数法BaseTI.TIM_Prescaler = 7200-1;   //PSC,计数器(PSC大,ARR小,则定时器频率低,OV=C_PSC/PSC+1/ARR+1)BaseTI.TIM_Period = 10000-1;   //ARR,重定位值 ,(PSC小,ARR大,则定时器频率高) BaseTI.TIM_RepetitionCounter = 0;    //Specifies the repetition counter value. Each time the RCR downcounterreaches zero, an update event is generated and counting restarts from the RCR valueTIM_TimeBaseInit(TIM2,&BaseTI); //由于时钟在初始化的时候,触发了一次更新事件使预分频器的值有效,所以要清除掉更新事件,避免后续计时器数值从一开始TIM_ClearFlag(TIM2,TIM_FLAG_Update);  //中断配置,由更新事件触发中断TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//中断控制器NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NI;NI.NVIC_IRQChannel = TIM2_IRQn;NI.NVIC_IRQChannelCmd = ENABLE;NI.NVIC_IRQChannelPreemptionPriority = 2;NI.NVIC_IRQChannelSubPriority = 1;  NVIC_Init(&NI);//启动定时器TIM_Cmd(TIM2,ENABLE);
}

这篇关于TIM时钟中断——输出捕获、输入捕获、编码器接口测速的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1041662

相关文章

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

顺序表之创建,判满,插入,输出

文章目录 🍊自我介绍🍊创建一个空的顺序表,为结构体在堆区分配空间🍊插入数据🍊输出数据🍊判断顺序表是否满了,满了返回值1,否则返回0🍊main函数 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以:点赞+关注+评论+收藏(一键四连)哦~ 🍊自我介绍   Hello,大家好,我是小珑也要变强(也是小珑),我是易编程·终身成长社群的一名“创始团队·嘉宾”

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出 在数字化时代,文本到语音(Text-to-Speech, TTS)技术已成为人机交互的关键桥梁,无论是为视障人士提供辅助阅读,还是为智能助手注入声音的灵魂,TTS 技术都扮演着至关重要的角色。从最初的拼接式方法到参数化技术,再到现今的深度学习解决方案,TTS 技术经历了一段长足的进步。这篇文章将带您穿越时

Java 后端接口入参 - 联合前端VUE 使用AES完成入参出参加密解密

加密效果: 解密后的数据就是正常数据: 后端:使用的是spring-cloud框架,在gateway模块进行操作 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.0-jre</version></dependency> 编写一个AES加密

java线程深度解析(一)——java new 接口?匿名内部类给你答案

http://blog.csdn.net/daybreak1209/article/details/51305477 一、内部类 1、内部类初识 一般,一个类里主要包含类的方法和属性,但在Java中还提出在类中继续定义类(内部类)的概念。 内部类的定义:类的内部定义类 先来看一个实例 [html]  view plain copy pu

解决Office Word不能切换中文输入

我们在使用WORD的时可能会经常碰到WORD中无法输入中文的情况。因为,虽然我们安装了搜狗输入法,但是到我们在WORD中使用搜狗的输入法的切换中英文的按键的时候会发现根本没有效果,无法将输入法切换成中文的。下面我就介绍一下如何在WORD中把搜狗输入法切换到中文。

模拟实现vector中的常见接口

insert void insert(iterator pos, const T& x){if (_finish == _endofstorage){int n = pos - _start;size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;reserve(newcapacity);pos = _start + n;//防止迭代

当你输入一个网址后都发生什么

原文:http://igoro.com/archive/what-really-happens-when-you-navigate-to-a-url/  作为一个软件开发者,你一定会对网络应用如何工作有一个完整的层次化的认知,同样这里也包括这些应用所用到的技术:像浏览器,HTTP,HTML,网络服务器,需求处理等等。 本文将更深入的研究当你输入一个网址的时候,后台到底发生了一件件什么样的事~

如何将一个文件里不包含某个字符的行输出到另一个文件?

第一种: grep -v 'string' filename > newfilenamegrep -v 'string' filename >> newfilename 第二种: sed -n '/string/!'p filename > newfilenamesed -n '/string/!'p filename >> newfilename