STM32学习笔记(6_8)- TIM定时器的编码器接口代码

无人问津也好,技不如人也罢,都应静下心来,去做该做的事。

最近在学STM32,所以也开贴记录一下主要内容,省的过目即忘。视频教程为江科大(改名江协科技),网站jiangxiekeji.com

现在开始上难度,STM32功能最强大、结构最复杂的外设——定时器,分四期介绍。

第一期介绍最基础的定时功能理论、定时器中断和定时器内外时钟源选择的代码。

第二期介绍定时器输出比较功能的代码,输出比较功能常用产生PWM波驱动电机。

上期介绍定时器输入捕获功能代码,常用测量方波频率。

最后介绍定时器的编码器接口代码,更方便读取正交编码器的输出波形,常用编码电机测速。

上下拉的选择原则:如果GPIO口接的外部模块默认输出低电平,我们配置下拉输入,默认输入低电平。和外部模块保持默认状态一致,防止默认电平打架。不过一般来说,默认高电平,这是一个习惯的状态。

接线图

这里使用TIM3的CH1、CH2来做定时器的编码器接口输入GPIO,所以A相接PA6、B相接PA7或者A相接PA7、B相接PA6,都是可以的,只是正反转的极性区别。

编码器接口程序测速现象

 在这里接了一个旋转编码器模块,这个代码和之前我们写的旋转编码器计次的代码,实现的功能基本都是一样的。OLED显示的是Speed速度,向右慢速旋转,数值为正,计次比较小。向右快速旋转,计次就会增大;然后向左慢速旋转,数值为负,计次比较小;然后停下来,速度就是0。

编码器接口常用函数

TIM_EncoderInterfaceConfig(哪个定时器,选择编码器模式,通道1的电平极性,通道2的电平极性):定时器编码器接口配置

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

初始化步骤

第一步,RCC开启时钟,开启GPIO和定时器的时钟

	/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟

第二步,配置GPIO,这里需要把PA6和PA7配置成输入模式

上下拉的选择原则:如果GPIO口接的外部模块默认输出低电平,我们配置下拉输入,默认输入低电平。和外部模块保持默认状态一致,防止默认电平打架。不过一般来说,默认高电平,这是一个习惯的状态。

	/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;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);							//将PA6和PA7引脚初始化为上拉输入

第三步,配置时基单元,这里预分频器我们一般选择不分频,自动重装一般给最大65535,
只需要一个CNT执行计数就行了

	/*时基单元初始化*/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;                //预分频器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元

第四步,配置输入捕获单元,不过这里输入捕获单元只有滤波器和极性这两个参数有用,后面的参数没有用到,与编码器无关。但是为避免出现奇奇怪怪的问题,这里先用TIM_ICStructInit()函数把输入捕获单元的结构体成员都赋一个初始值,再单独去修改滤波器和极性这两个参数。

	/*输入捕获初始化*/TIM_ICInitTypeDef TIM_ICInitStructure;							//定义结构体变量TIM_ICStructInit(&TIM_ICInitStructure);							//结构体初始化,若结构体没有完整赋值//则最好执行此函数,给结构体所有成员都赋一个默认值//避免结构体初值不确定的问题TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				//选择配置定时器通道1TIM_ICInitStructure.TIM_ICFilter = 0xF;							//输入滤波器参数,可以过滤信号抖动TIM_ICInit(TIM3, &TIM_ICInitStructure);							//将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;				//选择配置定时器通道2TIM_ICInitStructure.TIM_ICFilter = 0xF;							//输入滤波器参数,可以过滤信号抖动TIM_ICInit(TIM3, &TIM_ICInitStructure);							//将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道

第五步,配置编码器接口模式,这个直接调用一个库函数就可以了

	/*编码器接口配置*/TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//配置编码器模式以及两个输入通道是否反相//注意此时参数的Rising和Falling已经不代表上升沿和下降沿了,而是代表是否反相//此函数必须在输入捕获初始化之后进行,否则输入捕获的配置会覆盖此函数的部分配置

最后,调用TIM_Cmd,启动定时器,就完事了

	/*TIM使能*/TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定时器开始运行

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

代码展示

Hardware文件夹下新建编码器接口文件(Encoder),添加Encoder.h、.c文件,在Encoder_Init函数这里进行编码器接口的初始化。

main函数

#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更新事件的中断标志位//中断标志位必须清除//否则中断将连续不断地触发,导致主程序卡死}
}

Encoder.h文件

#ifndef __ENCODER_H
#define __ENCODER_Hvoid Encoder_Init(void);
int16_t Encoder_Get(void);#endif

Encoder.c文件

#include "stm32f10x.h"                  // Device header/*** 函    数:编码器初始化* 参    数:无* 返 回 值:无*/
void Encoder_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;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);							//将PA6和PA7引脚初始化为上拉输入/*时基单元初始化*/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;                //预分频器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元/*输入捕获初始化*/TIM_ICInitTypeDef TIM_ICInitStructure;							//定义结构体变量TIM_ICStructInit(&TIM_ICInitStructure);							//结构体初始化,若结构体没有完整赋值//则最好执行此函数,给结构体所有成员都赋一个默认值//避免结构体初值不确定的问题TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				//选择配置定时器通道1TIM_ICInitStructure.TIM_ICFilter = 0xF;							//输入滤波器参数,可以过滤信号抖动TIM_ICInit(TIM3, &TIM_ICInitStructure);							//将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;				//选择配置定时器通道2TIM_ICInitStructure.TIM_ICFilter = 0xF;							//输入滤波器参数,可以过滤信号抖动TIM_ICInit(TIM3, &TIM_ICInitStructure);							//将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道/*编码器接口配置*/TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//配置编码器模式以及两个输入通道是否反相//注意此时参数的Rising和Falling已经不代表上升沿和下降沿了,而是代表是否反相//此函数必须在输入捕获初始化之后进行,否则输入捕获的配置会覆盖此函数的部分配置/*TIM使能*/TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定时器开始运行
}/*** 函    数:获取编码器的增量值* 参    数:无* 返 回 值:自上此调用此函数后,编码器的增量值*/
int16_t Encoder_Get(void)
{/*使用Temp变量作为中继,目的是返回CNT后将其清零*/int16_t Temp;Temp = TIM_GetCounter(TIM3);TIM_SetCounter(TIM3, 0);return Temp;
}

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

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

相关文章

低代码平台与自动化软件开发的关系

引言 随着信息技术的不断发展,软件开发领域也在不断演进。在追求更高效、更快速的软件开发过程中,低代码平台和自动化软件开发技术日益受到关注。低代码平台以其可视化开发界面和快速构建应用的能力,为非专业开发人员提供了参与软件开发的机会…

有内置泵电源垂直输出电路,偏转电流可达1.7Ap-pp,可用于中小型显示器的国产芯片 78040

78040是一款场扫描电路,偏转电流可达1.7Ap-p,可用于中小型显示器。 二 特 点 1、有内置泵电源 2、垂直输出电路 3、热保护电路 4、偏转电流可达1.7Ap-p 三 基本参数 四 应用电路图 1、应用线路 2、PIN5脚输出波形如下:

windows允许指定IP段访问本地端口

虚拟机内部应用有时候需要访问windows的一些端口,例如数据库或Redis等,默认情况下,需关闭windows上的防火墙才可正常访问。本文通过在防火墙设置允许指定IP段进行访问来处理,不用每次操作都关闭防火墙。 入站规则-》新建规则 完成…

【jenkins+cmake+svn管理c++项目】Windows环境安装以及工具配置

一、目标和环境 目标:搭建一个jenkins环境,实现jenkins调用cmake和svn和VS编译c项目,并将生成的库上传svn。 环境:win10虚拟机(练习流程用,正式用的话还是放到服务器),VS2017. 二、…

linux忘记mysql的root密码,强制修改

1、登录linux后编辑mysql的配置文件:vi /etc/my.cnf 2、添加如下代码,表示跳过授权表登录mysql 编辑完成后,按Esc键,":wq"退出编辑并保存修改内容。 3、使用命令:service mysqld restart 重启mysql服务. …

[蓝桥杯 2021 省 B] 杨辉三角形

这道二分fix了半年 我还是那么&#x1f96c;..... 注意边界 先找规律&#xff0c;再二分 还学到了暴力算C&#xff08;i,j&#xff09;的超级暴力手段... #include<bits/stdc.h> using namespace std; using ll long long; using pii pair<int,int>; #define in…

神经网络代码实现(用手写数字识别数据集实验)

目录 一、前言 二、神经网络架构 三、算法实现 1、导入包 2、实现类 3、训练函数 4、权重参数矩阵初始化 5、参数矩阵变换向量 6、向量变换权重参数矩阵 7、进行梯度下降 7.1、损失函数 7.1.1、前向传播 7.2、反向传播 8、预测函数 四、完整代码 五、手写数字识别 一、前言 …

HarmonyOS网格布局:List组件和Grid组件的使用

简介 在我们常用的手机应用中&#xff0c;经常会见到一些数据列表&#xff0c;如设置页面、通讯录、商品列表等。下图中两个页面都包含列表&#xff0c;“首页”页面中包含两个网格布局&#xff0c;“商城”页面中包含一个商品列表。 上图中的列表中都包含一系列相同宽度的列表…

【Linux系统】进程概念创建进程进程标示符

什么是进程&#xff1f; 操作系统中&#xff0c; 进程可以同时存在非常多的。根据我们之前谈的操作系统具有“管理”的特性&#xff0c; 那么就有&#xff0c;既然要管理&#xff0c;就要 --- 先描述&#xff0c;在组织&#xff01;&#xff01;&#xff01; 由冯诺依曼体系结…

并发VS并行

参考文章 面试必考的&#xff1a;并发和并行有什么区别&#xff1f; 并发&#xff1a;一个人同时做多件事&#xff08;射击游戏队友抢装备&#xff09; 并行&#xff1a;多人同时处理同一件事&#xff08;射击游戏敌人同时射击对方&#xff09;

关于hook ntdll 代码详解

UNHOOK ntdll DWORD unhook() {//创建该结构体用于获取该dll的信息 将所有成员变量初始化为零MODULEINFO mi {};//获取当前内存的ntdll的句柄HMODULE ntdllModule GetModuleHandleA("ntdll.dll");//HANDLE(-1)表示获取当前进程的句柄 该函数用于获取该进程的信息G…

在项目中数据库如何优化?【MySQL主从复制(创建一个从节点复制备份数据)】【数据库读写分离ShardingJDBC(主库写,从库读)】

MySQL主从复制 MySQL主从复制介绍MySQL复制过程分成三步&#xff1a;1). MySQL master 将数据变更写入二进制日志( binary log)2). slave将master的binary log拷贝到它的中继日志&#xff08;relay log&#xff09;3). slave重做中继日志中的事件&#xff0c;将数据变更反映它自…

学点儿数据库_Day11_数据库简介、Sql单表、GROUP BY答疑

1 数据库简介 数据在内存&#xff1a; 优点&#xff1a;读写速度快 缺点&#xff1a;程序结束后数据丢失 保存到文件 优点&#xff1a;数据可以永久保存 缺点&#xff1a; 1、频繁的IO操作&#xff0c;效率不高 2、数据的管理非常不方便&#xff0c;需要把所有的数据整体都读…

云原生靶场kebernetesGoat、Metarget

靶场 文章目录 靶场kebernetesGoat靶场安装Docker in DockerSSRF漏洞容器逃逸到主系统Docker CIS 基线分析Kubernetes CIS 安全基线分析分析被部署挖矿软件的容器镜像获取环境信息Hidden in layersRBAC最低权限配置错误使用 Sysdig Falco 进行运行时安全监控和检测 Metarget ke…

C++类和对象、面向对象编程 (OOP)

文章目录 一、封装1.抽象、封装2.类和对象(0)学习视频(1)类的构成(2)三种访问权限(3)struct和class的区别(4)私有的成员变量、共有的成员函数(5)类内可以直接访问私有成员&#xff0c;不需要经过对象 二、继承三、多态1.概念2.多态的满足条件3.多态的使用条件4.多态原理剖析5.纯…

负氧离子监测站:创造健康生活环境

TH-FZ5在蓝天白云之下&#xff0c;那一座座高耸的全彩屏负氧离子监测站&#xff0c;如同一支支科技的绿芽&#xff0c;静静破土而出&#xff0c;为这片土地带来了新的生命力。这些现代化的设备不仅美化了环境&#xff0c;更是我们呼吸健康守护者&#xff0c;它们的存在让我们的…

初识云原生、虚拟化、DevOps

文章目录 K8S虚拟化DevOpsdevops平台搭建工具大数据架构 K8S master 主节点&#xff0c;控制平台&#xff0c;Master节点负责核心的调度、管理和运维&#xff0c;不需要很高性能&#xff0c;不跑任务&#xff0c;通常一个就行了&#xff0c;也可以开多个主节点来提高集群可用度…

Arduino+ESP8266+华为云物联网平台实现智能开关

前言 最近在做一个物联网项目&#xff0c;涉及到智能开关的开发。目前已经实现简单的TCP通信远程控制&#xff0c;但是考虑到后期的设备管理以及设备通信所需要的技术和服务器的维护成本&#xff0c;我决定将设备接入云平台。本文将详细阐述如何利用华为云的物联网平台&#x…

CI/CD实战-jenkins流水线 6

现最新版本没有该问题的出现 基于RBAC的身份授权&#xff1a; 安装插件&#xff1a; 新建测试用户 修改默认授权策略 新建的用户就没有任何权限 新建角色并授权 添加用户角色身份 pipeline 安装ssh agent插件 由于最新版的插件是有问题的&#xff0c;会有以下报错&#xff…

Memcached分布式内存对象数据库

一 Memcached 概念 Memcached 是一个高性能的分布式内存对象缓存系统&#xff0c;用于动态 Web 应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数&#xff0c;从而提高动态、数据库驱动网站的速度。 二 在架构中的位置 Memcached 处于前端或中间件后…