本文主要是介绍STM32 TIM 编码器模式采集编码器信号,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
layout: post
tags: [STM32]
comments: true
文章目录
- @[toc]
- 什么是正交解码?
- 编码器接口模式
- 标准库接口
- TIM_TimeBaseInitTypeDef
- TIM_ICInitTypeDef
- 寄存器接口
- 检测方法
- 标准库配置
- 关于计数器溢出的情况
- 总结
文章目录
- @[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
:适用于TIM1
和TIM8
作为插入死区时间配置的结构体;
所以,综合以上,只需要关注时基单元
和输入捕获单元
即可,下面对于其成员的以及其注释做一下简单解释;
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 编码器模式采集编码器信号的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!