STM32 TIM 编码器模式采集编码器信号

2024-05-18 18:32

本文主要是介绍STM32 TIM 编码器模式采集编码器信号,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


layout: post
tags: [STM32]
comments: true

文章目录

    • @[toc]
    • 什么是正交解码?
    • 编码器接口模式
      • 标准库接口
        • TIM_TimeBaseInitTypeDef
        • TIM_ICInitTypeDef
      • 寄存器接口
      • 检测方法
    • 标准库配置
      • 关于计数器溢出的情况
    • 总结

什么是正交解码?

对于常用增量式编码器,光学编码器,采用带槽圆盘,一侧是发射光线的发射端,而光电晶体管在相对的一侧。当圆盘转动时,光程被阻断,得到的脉冲指示轴的转动和方向。通常的说法是1000线的编码器,即转一圈会产生1000个方波脉冲,马盘上刻了1000个栅格,中间被镂了1000个空,举个例子,未免显得有点啰嗦,下面直奔主题,至于什么是编码器还是搜索引擎说的明明白白。
增量编码器通常有A,B两相信号,相位相差90°,所以也叫正交,还有一个复位信号是机械复位,即转了一圈,复位信号会有一个跳变沿。具体如下图所示:
编码器
所以,正交解码,就是把解码A,B两相的方波信号,检测相位,以及脉冲数和转向,当然也可以计算出转速,加速度,以及转动到相应的位置。

编码器接口模式

参考《STM32 参考手册中文版》,可以看到,对于TIM定时器中通用的功能,普遍支持编码器接口模式,下面配合手册和标准库进行配置。

标准库接口

首先看到标准库的代码stm32f10x_tim.h中的接口,先简单分析以下源码,找到以下四个数据类型:

  • TIM_TimeBaseInitTypeDef:时基单位,配置定时器预分频参数,计数器模式(上溢/下溢),周期频率以及分频系数;
  • TIM_OCInitTypeDef:振荡输出单元,可以用于产生PWM波形;
  • TIM_ICInitTypeDef:输入捕获单元,可以用于检测编码器信号的输入;
  • TIM_BDTRInitTypeDef:适用于TIM1TIM8作为插入死区时间配置的结构体;

所以,综合以上,只需要关注时基单元输入捕获单元即可,下面对于其成员的以及其注释做一下简单解释;

TIM_TimeBaseInitTypeDef
typedef struct
{uint16_t TIM_Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.This parameter can be a number between 0x0000 and 0xFFFF */uint16_t TIM_CounterMode;       /*!< Specifies the counter mode.This parameter can be a value of @ref TIM_Counter_Mode */uint16_t TIM_Period;            /*!< Specifies the period value to be loaded into the activeAuto-Reload Register at the next update event.This parameter must be a number between 0x0000 and 0xFFFF.  */ uint16_t TIM_ClockDivision;     /*!< Specifies the clock division.This parameter can be a value of @ref TIM_Clock_Division_CKD */uint8_t TIM_RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounterreaches zero, an update event is generated and counting restartsfrom the RCR value (N).This means in PWM mode that (N+1) corresponds to:- the number of PWM periods in edge-aligned mode- the number of half PWM period in center-aligned modeThis parameter must be a number between 0x00 and 0xFF. @note This parameter is valid only for TIM1 and TIM8. */
} TIM_TimeBaseInitTypeDef;       
TIM_ICInitTypeDef
typedef struct
{uint16_t TIM_Channel;      /*!< Specifies the TIM channel.This parameter can be a value of @ref TIM_Channel */uint16_t TIM_ICPolarity;   /*!< Specifies the active edge of the input signal.This parameter can be a value of @ref TIM_Input_Capture_Polarity */uint16_t TIM_ICSelection;  /*!< Specifies the input.This parameter can be a value of @ref TIM_Input_Capture_Selection */uint16_t TIM_ICPrescaler;  /*!< Specifies the Input Capture Prescaler.This parameter can be a value of @ref TIM_Input_Capture_Prescaler */uint16_t TIM_ICFilter;     /*!< Specifies the input capture filter.This parameter can be a number between 0x0 and 0xF */
} TIM_ICInitTypeDef;

寄存器接口

配置寄存器,可以直接参考《STM32 参考手册中文版》的十三章的编码器接口模式,详细内容可以参考一下手册,这里结合前面标准库的结构体,将重点的内容做一下提炼,编码器接口大概需要进行以下几项的配置:

  • 编码器接口模式的配置:
    • 上升沿触发
    • 下降沿触发
    • 跳变沿触发
  • 极性配置
  • 滤波器配置

以下是官方给出的配置方案:

● CC1S=01(TIMx_CCMR1寄存器, IC1FP1映射到TI1)
● CC2S=01(TIMx_CCMR2寄存器, IC2FP2映射到TI2)
● CC1P=0(TIMx_CCER寄存器, IC1FP1不反相, IC1FP1=TI1)
● CC2P=0(TIMx_CCER寄存器, IC2FP2不反相, IC2FP2=TI2)
● SMS=011(TIMx_SMCR寄存器,所有的输入均在上升沿和下降沿有效).
● CEN=1(TIMx_CR1寄存器,计数器使能)

这意味着计数器TIMx_CNT寄存器只在0到TIMx_ARR寄存器的自动装载值之间连续计数(根据方向,或是0到ARR计数,或是ARR到0计
数)。具体如下图所示;

官方正交解码时序

检测方法

综上所述,如果想得到转速,和方向:

  • 在间隔固定时间Ts,读取TIMx_CNT寄存器的值,假设是1000线的编码器,转速:n = 1/Ts*TIMx_CNT*1000;
  • 根据TIMx_CNT的计数方向判断转向,不同极性,TIMx_CNT增长方向也不同,这里要加以区分;

标准库配置

下面是基于标准库V3.5的代码,基于STM32F103系列的单片机,硬件接口:

  • TIM3通道1,Pin6和Pin7;
  • 机械复位信号;

可以通过encoder_get_signal_cnt接口读取当前编码的脉冲数,采用M法测速;

关于计数器溢出的情况

TIM3_IRQHandler中断通过判断SR寄存器中的上溢和下溢标志位,检测定时器可能溢出的方向,通过N做一个补偿,encoder_get_signal_cnt中未考虑到定时器溢出的情况;

#ifndef ENCODER_H
#define ENCODER_H
#include <stdint.h>
/*
QPEA--->PA6/TIM3C1
QPEB--->PA7/TIM3C1
---------------------------
TIM3_UPDATE_IRQ
EXTI_PA5
---------------------------
*/
typedef enum{FORWARD = 0,BACK
}MOTO_DIR;/*** 	@brief init encoder pin for pha phb and zero*		   and interrpts		*/
void encoder_init(void);/***	@brief get encoder capture signal counts*/
int32_t encoder_get_signal_cnt(void);/***	@brief get encoder running direction*/
MOTO_DIR encoder_get_motor_dir(void); #endif
#include "encoder.h"
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_exti.h"
#include "misc.h"#define SAMPLE_FRQ 	10000L
#define SYS_FRQ		72000000L/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
volatile int32_t N = 0;
volatile uint32_t EncCnt = 0;/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/static void encoder_pin_init(void){GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);
}static void encoder_rcc_init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	
}static void encoder_tim_init(void){TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;TIM_ICInitTypeDef TIM_ICInitStructure; 		TIM_TimeBaseStructure.TIM_Period = ENCODER_MAX_CNT;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseStructure.TIM_Prescaler = 0;TIM_TimeBaseStructure.TIM_ClockDivision = 0;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//must clear it flag before enabe interruptTIM_ClearFlag(TIM3,TIM_FLAG_Update);TIM_ITConfig(TIM3,TIM_IT_Update, ENABLE);	//TIM_ITConfig(TIM3, TIM_IT_CC2, ENABLE);TIM_SetCounter(TIM3,ENCODER_ZERO_VAL);TIM_ICInit(TIM3, &TIM_ICInitStructure);TIM_Cmd(TIM3, ENABLE);//	TIM3->CCMR1 |= 0x0001;
//	TIM3->CCMR2 |= 0x0001;
//	TIM3->CCER &= ~(0x0001<<1);
//	TIM3->CCER &= ~(0x0001<<5);
//	TIM3->SMCR |= 0x0003;
//	TIM3->CR1 |= 0x0001;}/*** @brief  Configure the nested vectored interrupt controller.* @param  None* @retval None*/
static void encoder_irq_init(void)
{NVIC_InitTypeDef NVIC_InitStructure;EXTI_InitTypeDef EXTI_InitStructure;/* Enable the TIM3 global Interrupt */NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource5);EXTI_InitStructure.EXTI_Line = EXTI_Line5;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;EXTI_Init(&EXTI_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);}void encoder_init(void){encoder_rcc_init();encoder_pin_init();encoder_irq_init();encoder_tim_init();
}
// 机械复位信号
void EXTI9_5_IRQHandler(void){if(EXTI_GetITStatus(EXTI_Line5) == SET){ }EXTI_ClearITPendingBit(EXTI_Line5);
}MOTO_DIR encoder_get_motor_dir(void)
{if((TIM3->CR1 & 0x0010) == 0x0010){return FORWARD;}else{return BACK;}
}int32_t encoder_get_signal_cnt(void){int32_t cnt = 0;if(TIM3->CNT > ENCODER_ZERO_VAL){EncCnt = cnt = TIM3->CNT - ENCODER_ZERO_VAL;	}else{EncCnt = cnt = ENCODER_ZERO_VAL - TIM3->CNT;	}TIM_SetCounter(TIM3,ENCODER_ZERO_VAL);return cnt;
}/******************************************************************************/
/*            STM32F10x Peripherals Interrupt Handlers                        */
/******************************************************************************/
/*** @brief  This function handles TIM3 global interrupt request.* @param  None* @retval None*/
void TIM3_IRQHandler(void)
{ uint16_t flag = 0x0001 << 4;if(TIM3->SR&(TIM_FLAG_Update)){		//down modeif((TIM3->CR1 & flag) == flag){N--;}else{//up modeN++;}}	TIM3->SR&=~(TIM_FLAG_Update);		
}TIM3 global interrupt request.* @param  None* @retval None*/
void TIM3_IRQHandler(void)
{ uint16_t flag = 0x0001 << 4;if(TIM3->SR&(TIM_FLAG_Update)){		//down modeif((TIM3->CR1 & flag) == flag){N--;}else{//up modeN++;}}	TIM3->SR&=~(TIM_FLAG_Update);		
}

总结

本文实现了STM32编码器接口模式的配置以及编码器的M法测速,如果配合机械复位信号,可以通过编码器的脉冲数得到位置信息,转过多少度,但当前并未实现。

这篇关于STM32 TIM 编码器模式采集编码器信号的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

无线路由器哪个品牌好用信号强? 口碑最好的三个路由器大比拼

《无线路由器哪个品牌好用信号强?口碑最好的三个路由器大比拼》不同品牌在信号覆盖、稳定性和易用性等方面各有特色,如何在众多选择中找到最适合自己的那款无线路由器呢?今天推荐三款路由器让你的网速起飞... 今天我们来聊聊那些让网速飞起来的路由器。在这个信息爆炸的时代,一个好路由器简直就是家庭网编程络的心脏。无论你

电脑显示hdmi无信号怎么办? 电脑显示器无信号的终极解决指南

《电脑显示hdmi无信号怎么办?电脑显示器无信号的终极解决指南》HDMI无信号的问题却让人头疼不已,遇到这种情况该怎么办?针对这种情况,我们可以采取一系列步骤来逐一排查并解决问题,以下是详细的方法... 无论你是试图为笔记本电脑设置多个显示器还是使用外部显示器,都可能会弹出“无HDMI信号”错误。此消息可能

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

模版方法模式template method

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

迭代器模式iterator

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD

STM32内部闪存FLASH(内部ROM)、IAP

1 FLASH简介  1 利用程序存储器的剩余空间来保存掉电不丢失的用户数据 2 通过在程序中编程(IAP)实现程序的自我更新 (OTA) 3在线编程(ICP把整个程序都更新掉) 1 系统的Bootloader写死了,只能用串口下载到指定的位置,启动方式也不方便需要配置BOOT引脚触发启动  4 IAP(自己写的Bootloader,实现程序升级) 1 比如蓝牙转串口,