STM32标准库——(9)TIM编码器接口

news2024/9/25 5:23:50

1.编码器接口简介

  • Encoder Interface 编码器接口
  • 编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度
  • 每个高级定时器和通用定时器都拥有1个编码器接口
  • 两个输入引脚借用了输入捕获的通道1和通道2

2.正交编码器

  1. 编码器接口 相当于一个带有方向控制的外部时钟 它同时控制着CNT的计数时钟和计数方向
  2. 旋转速度:当编码器的旋转轴转起来时 A相和B相就会输出这样的方波信号 转得越快 这个方波的频率就越高 所以方波的频率就代表了速度
  3. 旋转方向:当正转时A相提前B相90度 反转时A相滞后B相90度 当然这个正转是A相提前还是A相滞后 并不是绝对的 这只是一个极性问题 毕竟正转和反转的定义也是相对的 总之就是朝一个方向转 是A相提前 另一个方向是A相滞后

3.编码器基本结构

  1. TI1FP1和TI2FP2分别对应定时器的CH1、CH2这两个引脚
  2. 输入捕获的前两个通道 通过GPIO口接入编码器的A、B相 然后通过滤波器和边沿检测极性选择  产生TI1FP1和TI2FP2 通向编码器接口 编码器接口通过预分频器控制CNT计数器的时钟 同时 编码器接口还根据编码器的旋转方向 控制CNT的计数方向 编码器正转时 CNT自增 编码器反转时 CNT自减

  3. 这里ARR是有效的 一般我们会设置ARR为65535(最大量程) 这样的话 利用补码的特性 很容易得到负数 比如CNT初始为0 正转 CNT自增,0、 1、2、3、4、5、6、7等等  反转时CNT自减 0下一个数就是65535 接着是65534 65533等等 这里负数不应该是-1、-2吗  所以直接把这个16位的无符号数转换为16位的有符号数 根据补码的定义 这个65535就对应-1 65534就对应-2(有符号编码时负数按补码计算,2^16 的补码= -1)等等 这样就可以直接得到负数 非常方便 这就是我们读出数据得到负数的一个小技巧

4.工作模式

5.实例

5.1 均不反相

  • 红框内就是正交编码器抗噪声原理 由图可知 TI2没有发生变化 但TI1却跳变好几次 这不符合正交编码器的信号规律 然后通过上表逻辑 成功将这种毛刺信号滤掉 TI1为上升沿 TI2为低电平 查表可知此时为向上计数 即自增 接着看下一个状态 TI1为下降沿 TI2为低电平 查表可知此时为向下计数 即自减 所以这里一个引脚不变 另一个引脚来回跳动 计数器会来回加、减、加、减 尽管发生多次变化 但其值与原先值还是一样

5.2 TI1反相

  • TI1反相的计数频率与5.1中的类似 只不过TI1真正的信号是与上图相反 如红色划线部分 后续也是如此 将其电位翻转后 通过查询表可知其计数方式

6.相关API

TIM_EncoderInterfaceConfig

void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,
                                uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);
功能:
	定时器编码器接口配置
参数:
    TIMx:其中x可以为1、2、3、4、5或8,选择TIM外设  
    TIM_EncoderMode: TIMx编码器模式
    TIM_IC1Polarity: IC1极性
    TIM_IC2Polarity: IC2极性
//参数3、4可以配置成TIM_ICPolarity_Falling: IC下降沿(反相)、TIM_ICPolarity_Rising: IC上升沿(不反相)
返回值:
	无    

7.编码器接口测速

7.1 实现思路

  1. 第一步,RCC开启时钟,开启GPIO和定时器的时钟
  2. 第二步,配置GPIO,这里需要把PA6和PA7配置成输入模式
  3. 第三步,配置时基单元,这里预分频器我们一般选择不分频,自动重装一般给最大65535,只需要个CNT执行计数就行了
  4. 第四步,配置输入捕获单元。不过这里输入捕获单元只有滤波器和极性这两个参数有用,后面的参数没有用到,与编码器无关
  5. 第五步,配置编码器接口模式。这个直接调用一个库函数就可以了

7.2 接线图

7.3 相关代码

Encoder.c
#include "stm32f10x.h"                  // Device header

void Encoder_Init(void)
{
    /*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
    /*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//将PA6引脚初始化为上拉输入
	//外部模块空闲默认输出高电平 选择上拉输入 默认输入高电平 外部模块空闲默认输出低电平 选择下拉输入 默认输入低电平 不过一般默认高电平 若不确定外部模块输出的默认状态或者外部模块输出功率非常小 这时就选择浮空输入模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;//这里TIM2要用来输出PWM 所以输入捕获的定时器需要换一个 暂时换到TIM3 由引脚定义表可以看到TIM3的通道1对应PA6 通道2对应PA7 通道3对应PB0 通道4对应PB1 这里选择TIM3的通道1 所以选择PA6引脚 这里需要配置上拉输入模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
    /*配置时钟源*/
//	TIM_InternalClockConfig(TIM3);//选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	//编码器接口是一个带方向控制的外部时钟 所以这个内部时钟就没用 
	
    /*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//在这里无作用 因为计数方向受编码器接口托管
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;//ARR的值 设置最大是防止计数溢出 满量程计数 计数范围大
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;//预分频给0 就是不分频
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	
    /*PWMI模式初始化*/
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICStructInit(&TIM_ICInitStructure);//该结构体最后两个参数在此无用处 因为这里只需配置滤波器和极性选择 但删除其他两个参数后可能显示结构体不完整 所以调用此函数给结构体赋一个初始值
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//配置通道参数 这里选择通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;//用来配置输入捕获的滤波器 若信号有毛刺或噪声 可以增大滤波器参数 可以有效避免干扰
//	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//对应图里的边沿选择、极性选择部分
	//这里并不是上升沿有效 编码器接口始终都是上升沿、下降沿都有效 这里上升沿参数代表的是高低电平极性不反转
	TIM_ICInit(TIM3,&TIM_ICInitStructure);//调用此函数后 上面结构体的配置就写入寄存器中 接着再配置通道2
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;//配置通道参数 这里选择通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;//用来配置输入捕获的滤波器 若信号有毛刺或噪声 可以增大滤波器参数 可以有效避免干扰
//	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//对应图里的边沿选择、极性选择部分
	TIM_ICInit(TIM3,&TIM_ICInitStructure);//将第二个结构体参数配置写入寄存器中 配置通道2
	
	TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);//编码器接口配置 第二个参数选择TI1和TI2都计数
	//这里第三个参数和第四个参数与上面的极性选择配置重复 其功能一样 后定义的函数会覆盖前面的 所以前面的可以注释掉

	TIM_Cmd(TIM3,ENABLE);
}

/**
  * 函    数:获取编码器的增量值
  * 参    数:无
  * 返 回 值:自上此调用此函数后,编码器的增量值
  */
int16_t Encoder_Get(void)
{
	/*使用Temp变量作为中继,目的是返回CNT后将其清零*/
	int16_t Temp;
	Temp = TIM_GetCounter(TIM3);
	TIM_SetCounter(TIM3, 0);
	return Temp;
}
Encoder.h
#ifndef __ENCODER_H
#define __ENCODER_H

void Encoder_Init(void);
int16_t Encoder_Get(void);

#endif
main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "Encoder.h"

int16_t Speed;//定义速度变量

int main(void)
{
	OLED_Init();//OLED初始化
	Timer_Init();//定时器初始化
	Encoder_Init();//编码器初始化
	
	OLED_ShowString(1,1,"Speed:");//1行1列显示字符串Speed:
	
	while (1)
	{
		OLED_ShowSignedNum(1,7,Speed,5);//不断刷新显示编码器测得的最新速度
	}
}

/**
  * 函    数:TIM2中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)		//判断是否是TIM2的更新事件触发的中断
	{
		Speed = Encoder_Get();								//每隔固定时间段读取一次编码器计数增量值,即为速度值
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);			//清除TIM2更新事件的中断标志位
															//中断标志位必须清除
															//否则中断将连续不断地触发,导致主程序卡死
	}
}

现象:接上电源后OLED显示屏显示Speed:00000 旋转编码器 这里以顺时针转为正向 Speed的值会发生改变 旋转的越快数值越大 停止旋转则数值为0  逆时针旋转则为反方向 旋转越快数值越大 往负方向增大


注:若想改变旋转方向 即逆时针转为正向 顺时针转为反向

方法一:可以将A、B相接口接线互换位置

方法二:在Encoder.c的编码器配置函数中 第三个参数或者第四个参数选择反相即可 即一个选择Falling 另一个选择Rising 若两个选择一样则还是同向输出 类似于乘法(正正得正 正负得负 负负得正)

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

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

相关文章

异步编程Completablefuture使用详解----进阶篇

JDK版本:jdk17 IDEA版本:IntelliJ IDEA 2022.1.3 文章目录 前言一、异步任务的交互1.1 applyToEither1.2 acceptEither1.3 runAfterEither 二、get() 和 join() 区别三、ParallelStream VS CompletableFuture3.1 使用串行流执行并统计总耗时3.2 使用并行…

单元/集成测试服务

服务概述 单元/集成测试旨在证明被测软件实现其单元/架构设计规范、证明被测软件不包含非预期功能。经纬恒润测试团队拥有丰富的研发经验、严格的流程管控,依据ISO26262/ASPICE等开展符合要求的单元测试/集成测试工作。 在ISO 26262 - part6 部分产品开发&#xff…

荣耀手机如何录屏?在线分享3个录屏方法

荣耀手机如何录屏?荣耀手机录屏是一项非常实用的功能,它可以帮助我们轻松记录手机屏幕上的内容,无论是游戏攻略、教育学习还是工作演示,都能够方便地进行录制。通过录屏,我们可以随时随地记录和分享自己的操作和见解。…

C++ 动态规划 线性DP 最长上升子序列

给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。 输入格式 第一行包含整数 N 。 第二行包含 N 个整数,表示完整序列。 输出格式 输出一个整数,表示最大长度。 数据范围 1≤N≤1000 , −109≤数列中的数…

学成在线: 新增/修改课程计划

新增/修改课程计划(同接口) 界面原型 第一步: 在课程计划界面,点击添加章新增第一级课程计划,点击添加小节可以向某个第一级课程计划下添加小节 新增章/节成功后会自动发起请求刷新课程计划列表并且把新增的课程计划信息添加到数据库当中,新增的课程计划自动排序到最后 第二…

2024年第三届能源与环境工程国际会议(CFEEE 2024) | Ei&Scopus双检索

会议简介 Brief Introduction 2024年第三届能源与环境工程国际会议(CFEEE 2024) 会议时间:2024年12月12日-14日 召开地点:澳大利亚凯恩斯 大会官网:CFEEE 2024-2024 International Conference on Frontiers of Energy and Environment Engine…

【python】python爱心代码

一、实现效果: 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 二、准备工作 (1)、导入必要的模块: 代码首先导入了需要使用的模块:requests、lxml和csv。 import requests from lxml import etree import csv 如果出现…

不同状态角 θz 下的 K2eff 和 Kav 计算结果

不同状态角 θ z 下的 K 2 eff 和 K av 计算结果 clear clc close all syms theta for theta_z[pi/5,pi/3,pi/2,2*pi/3,4*pi/5,pi] % theta_zdtheta_z*180/pi theta_1-theta_z/2; theta_2theta_z/2; fthetacos(theta); ftheta2cos(theta)^2; Keff2int(ftheta2,theta_1,…

png,jpg,bmp等格式图像转.npy文件(附代码)

目录 一、npy文件二、代码三、转后效果 一、npy文件 .npy 文件是 NumPy 库用于存储数组的二进制文件格式。这种文件可以存储一个或多个 NumPy 数组对象。.npy 文件在保存和加载 NumPy 数组时非常有用,因为它们可以用于在磁盘上高效地存储数据,并且在加载…

MySQL 安装配置 windows

一、下载 去官网MySQL :: MySQL Downloads 下载社区版 然后根据自己的系统选择 直接下载。 二、安装 点击安装程序 这边看样子缺少东西。 去这边下载 Latest supported Visual C Redistributable downloads | Microsoft Learn 然后再一次安装mysql 三、配置 安装完成后&a…

PPT母版页码设置

PPT母版页码设置 一、需求介绍二、达到效果三、具体操作1. 插入页码2. 设置起始页码为03. 进入母版编辑页面4. 内容格式调整5. 删去最后一个板式的三个模块信息6. 尾页处理7. 最终效果 一、需求介绍 PPT的母版可以设定PPT的基调,且在非母版页面不会误改PPT中的固定…

参考数据集INRIA Holidays dataset

Download datasets 很贴心,MATLAB访问代码: % This function reads a siftgeo binary file % % Usage: [v, meta] = siftgeo_read (filename, maxdes) % filename the input filename % maxdes maximum number of descriptors to be loaded % (default=unlimit…

力扣刷题之旅:启程篇(二)

力扣(LeetCode)是一个在线编程平台,主要用于帮助程序员提升算法和数据结构方面的能力。以下是一些力扣上的入门题目,以及它们的解题代码。 --点击进入刷题地址 1.最后得到的余数 题目描述: 给定两个非空字符串 nu…

2024/2/3

一.选择题 1、适宜采用inline定义函数情况是(C) A. 函数体含有循环语句 B. 函数体含有递归语句‘、考科一 ’ C. 函数代码少、频繁调用 D. 函数代码多、不常调用 2、假定一个函数为A(int i4, int j0) {;}, 则执行“A (1);”语句后&#xff0c…

C++之std::tuple(一) : 使用

相关系列文章 C之std::tuple(一) : 使用 C三剑客之std::variant(一) : 使用 C三剑客之std::variant(二):深入剖析 目录 1.简介 2.创建元组 2.1.直接初始化方式 2.2.使用花括号初始化列表方式(C11及以上版本) 2.3.make_tuple方式 2.4.使…

03.PostgreSQL排序和分页

当我们使用SELECT语句查询表中数据的时候,PostgreSQL不确保按照一定的顺序返回结果。如果想要将查询的结果按照某些规则排序显示,需要使用ORDER BY子句。 1. 排序规则 使用ORDER BY 子句排序 ASC: 升序 DESC:降序 ORDER BY 子句在SELECT语句的结尾 1.1 单列排序 是指按…

手机gif动图怎么操作?这个方法分分钟解决

手机上怎么制作gif动画?Gif动图是当下非常流行的一种表达方式,通过简单的画面循环就能够传达您的想法。但是,想要在手机上制作gif动画的时候应该怎么办呢?通过会用GIF动图在线编辑(https://www.gif.cn/)工具…

AutoCAD .NET 层次结构介绍

AutoCAD .NET API 提供了一种面向对象的编程接口,通过它可以与AutoCAD进行深度集成和自定义功能开发。以下是基于.NET框架下AutoCAD对象层次结构的基本介绍: Autodesk.AutoCAD.ApplicationServices 命名空间 根对象,代表运行中的AutoCAD应用程…

牛客寒假训练营H题

思路&#xff1a;找出所有m的子集&#xff0c;加到价值中&#xff0c;找出最大价值即可。 代码&#xff1a; void solve(){int n, m;cin >> n >> m;vector<pii>a(n 1);for(int i 1;i < n;i )cin >> a[i].first >> a[i].second;int ans 0…

Servlet(未完结~)

文章目录 前言1 Servlet简介2 Servlet初识2.1 Servlet开发流程2.2 配置欢迎页 3 Servlet案例开发!3.1 开发登录页3.2 开发后台Servlet3.3 配置Servlet 4 HttpServletRequest4.1 回顾http请求4.2 自定义servlet流程图4.3 HttpServletRequest4.4获取请求行信息4.5获取请求头信息4…