STM32 TIM编码器接口测速

embedded/2025/2/6 3:01:29/

编码器接口简介:

        Encoder Interface 编码器接口

        编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度

        每个高级定时器和通用定时器都拥有1个编码器接口

        两个输入引脚借用了输入捕获的通道1和通道2

  编码器接口基本结构:

工作模式:

正传的状态都向上计数,反转的状态都向下计数

接线图:

函数介绍:

定时器编码器接口配置,第一个参数选择定时器,第二个参数选择编码器模式,后面两个参数分别选择通道1和通道2的极性

void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);

代码配置:

        利用编码器接口进行测速

1.定义结构体变量

//定义结构体变量
GPIO_InitTypeDef GPIO_InitStructure;							 //定义GPIO结构体变量
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;      //定义TimeBase结构体变量
TIM_ICInitTypeDef TIM_ICInitStructure;						 //定义IC结构体变量  

2.RCC开启时钟

开启GPIO和定时器的时钟

//开启RCC时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//打开TIM3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//打开GPIO A族时钟

3.配置GPIO

这里需要把PA6和PA7配置成输入模式

//GPIO配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;    			 //选择上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //配置引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //速率GPIO_Init(GPIOA, &GPIO_InitStructure);//GPIO初始化

4.配置时基单元

这里预分频器我们选择不分频,自动重装一般给最大65536,只需要个CNT执行计数就行

//时基单元配置TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;	//时钟分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
TIM_TimeBaseInitStructure.TIM_Period = 65535 - 1;        //周期 自动重装器ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;         //预分频器 PSC的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;     //重复计数器的值
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);  //TimeBase初始化

5.配置输入捕获单元

这里输入捕获单元只有滤波器和极性这两个参数有用(极性在配置编码器接口模式也可以配置,所以这里就删掉了)

//输入捕获单元配置
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //TIM通道选择,这里选择的是TIM3的CH1通道
TIM_ICInitStructure.TIM_ICFilter = 0xF;          //选择输入捕获的滤波器TIM_ICInit(TIM3,&TIM_ICInitStructure);         //IC初始化TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; //TIM通道选择,这里选择的是TIM3的CH2通道
TIM_ICInitStructure.TIM_ICFilter = 0xF;          //选择输入捕获的滤波器TIM_ICInit(TIM3,&TIM_ICInitStructure);         //IC初始化

6.配置编码器接口模式

后面两个参数可以配置通道1与通道2的极性

//配置编码器接口
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);

最后调用TIM_Cmd,启动定时器

//开启定时器
TIM_Cmd(TIM3,ENABLE);

电路初始化完成之后,CNT就会随之编码器的旋转而自增自减,如果想要测量编码器的位置,直接读出CNT的值就行了,如果想测量编码器的速度和方向,那就需要每隔一段固定的闸门时间,取出一次CNT,然后再把CNT清零,这样就是测频法测量速度了

完整代码:

void Encoder_Config(void)
{//定义结构体变量GPIO_InitTypeDef GPIO_InitStructure;							 //定义GPIO结构体变量TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//定义TimeBase结构体变量TIM_ICInitTypeDef TIM_ICInitStructure;						 //定义IC结构体变量       //开启RCC时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//打开TIM3时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//打开GPIO A族时钟//GPIO配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;    			 //选择上拉输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //配置引脚GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //速率GPIO_Init(GPIOA, &GPIO_InitStructure);//GPIO初始化//时基单元配置TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;	//时钟分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数TIM_TimeBaseInitStructure.TIM_Period = 65535 - 1;        //周期 自动重装器ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;         //预分频器 PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;     //重复计数器的值TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);  //TimeBase初始化//输入捕获单元配置TIM_ICStructInit(&TIM_ICInitStructure);TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //TIM通道选择,这里选择的是TIM3的CH1通道TIM_ICInitStructure.TIM_ICFilter = 0xF;          //选择输入捕获的滤波器TIM_ICInit(TIM3,&TIM_ICInitStructure);         //IC初始化TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; //TIM通道选择,这里选择的是TIM3的CH2通道TIM_ICInitStructure.TIM_ICFilter = 0xF;          //选择输入捕获的滤波器TIM_ICInit(TIM3,&TIM_ICInitStructure);         //IC初始化//配置编码器接口TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//开启定时器TIM_Cmd(TIM3,ENABLE);
}

功能函数:

读取CNT的值,并读取一次便将CNT的值清零

int16_t Encoder_GetTime(void)
{int16_t temp;temp = TIM_GetCounter(TIM3);TIM_SetCounter(TIM3,0);return temp;}

Timer函数:

这里用定时中断的方法来测量,其他方法涉及到用delay函数的问题,会堵塞主程序,所以最好是用中断解决

void Timer_Init(void)
{//---------------------------定义结构体变量-------------------------------TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//定义TIM结构体变量NVIC_InitTypeDef NVIC_InitStructure;							 //定义NVIC结构体变量//---------------------------定义结构体变量-------------------------------RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//打开TIM2的外设时钟//-----------------------------配置时基单元---------------------------------TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;		 //时钟分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式  这里选择向上计数TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;							 //周期 就是ARR自动重装器的值TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;						 //是PSC预分频器的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;						 //重复计数器的值(这个是高级寄存器才有的,这里不需要用直接给0)TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);						 //TIM初始化//-----------------------------配置时基单元---------------------------------TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);						//开启更新中断到NVIC通路TIM_ClearITPendingBit(TIM2,TIM_IT_Update);        		//清除标志位//-----------------------------NVIC配置-------------------------------------NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//选择中断分组2NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//选择中断通道NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			 //响应优先级NVIC_Init(&NVIC_InitStructure);//NVIC初始化//-----------------------------NVIC配置-------------------------------------TIM_Cmd(TIM2,ENABLE);//启动定时器
}

中断函数:

int16_t Speed = 0;
//中断函数void TIM2_IRQHandler(void)
{//获取中断标志位,判断是否触发中断if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){Speed = Encoder_GetTime();TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除中断标志位}}

主函数代码:

#include "Encoder.h"
#include "timer.h"
int main(void)
{OLED_Init();Timer_Init();Encoder_Config();OLED_ShowString(1, 1, "Speed:");						while(1){OLED_ShowSignedNum(1, 7, Speed, 5);}}


http://www.ppmy.cn/embedded/159908.html

相关文章

汇编基础语法及其示例

1.汇编指令 1.1汇编指令的基本格式 <opcode>{<cond>}{s} <Rd> , <Rn> , <shifter_operand> <功能码>{<条件码>}{cpsr影响位} <目标寄存器> , <第一操作寄存器> , <第二操作数> 注&#xff1a;第一操作寄存器…

【PHP】基于 PHP 的图片管理系统(源码+论文+数据库+图集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、测试技术。 【PHP】基于 PHP 的图片管理系统&#xff08;源码论…

【C语言篇】深入探究 C 语言指针:揭开指针变量与地址的神秘面纱

我的个人主页 我的专栏&#xff1a;C语言&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 目录 引言指针的基础概念 2.1 什么是指针2.2 指针的声明与初始化2.3 指针的存储模型与内存布局 指针的操作 3.1 获取地址与解引用操作3.2 指针的算术…

Vue 3.0打造响应式用户界面的新方式

1 简介 Vue.js 是一个用于构建用户界面的渐进式框架。Vue 3.0 是其最新版本,引入了许多新特性和改进,使得开发者能够更高效地构建响应式的Web应用程序。本文将带你深入了解如何使用Vue 3.0来打造响应式用户界面,并通过实际案例和代码示例帮助你快速上手。 2 环境搭建 要开…

内核定时器1-普通定时器

定时器与中断关系 软件意义上的定时器最终依赖硬件定时器来实现&#xff0c;内核在时钟中断发生后检测各定时器是否到期&#xff0c;到期后的定时器处理函数将作为软中断在底半部执行。实质上&#xff0c;时钟中断处理程序会唤起TIMER_SOFTIRQ 软中断&#xff0c;运行当前处理…

【探索篇】探索部署离线AI在Android的实际体验

【探索篇】探索离线AI在Android的实际体验 文章目录 【探索篇】探索离线AI在Android的实际体验一、离线AI的核心优势1.1 隐私保护与低延迟1.2 无网络持续服务1.3 典型应用场景 二、Android端的技术实现2.1 框架支持对比2.2 性能优化策略 三、真实体验报告3.1 测试环境配置3.2 功…

Python从零构建macOS状态栏应用(仿ollama)并集成AI同款流式聊天 API 服务(含打包为独立应用)

在本教程中,我们将一步步构建一个 macOS 状态栏应用程序,并集成一个 Flask 服务器,提供流式响应的 API 服务。 如果你手中正好持有一台 MacBook Pro,又怀揣着搭建 AI 聊天服务的想法,却不知从何处迈出第一步,那么这篇文章绝对是你的及时雨。 最终,我们将实现以下功能: …

一个 windows 自动语音识别案列

一个 windows 自动语音识别案列 之前给写过一段很有意思的代码,今天分享给大家 ! 文章目录 一个 windows 自动语音识别案列前言一、需要安装一些python 库二、代码如下三,测试总结下前言 一、需要安装一些python 库 speech_recognition:这是一个用于语音识别的库。它可以…