layout: post

tags: [STM32]

comments: true

什么是正交解码?

对于常用增量式编码器,光学编码器,采用带槽圆盘,一侧是发射光线的发射端,而光电晶体管在相对的一侧。当圆盘转动时,光程被阻断,得到的脉冲指示轴的转动和方向。通常的说法是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 active
Auto-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 downcounter
reaches zero, an update event is generated and counting restarts
from 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 mode
This 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 interrupt
TIM_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 mode
if((TIM3->CR1 & flag) == flag){
N--;
}else{
//up mode
N++;
}
}
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 mode
if((TIM3->CR1 & flag) == flag){
N--;
}else{
//up mode
N++;
}
}
TIM3->SR&=~(TIM_FLAG_Update);
}

总结

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

STM32 TIM 编码器模式采集编码器信号的更多相关文章

  1. STM32输入捕获模式设置并用DMA接收数据

    参考: STM32的PWM输入模式设置并用DMA接收数据 Input capture mode The input stage samples the corresponding TIx input ...

  2. STM32 TIM高级定时器死区时间的计算

    STM32 TIM高级定时器的互补PWM支持插入死区时间,本文将介绍如何计算以及配置正确的死区时间. 文章目录 什么是死区时间? 数据手册的参数 如何计算合理的死区时间? STM32中配置死区时间 什 ...

  3. stm32之TIM+ADC+DMA采集50HZ交流信号

    http://cache.baiducontent.com/c?m=9d78d513d98207f04fece47f0d01d7174a02d1743ca6c76409c3e03984145b5637 ...

  4. STM32 TIM 多通道互补PWM波形输出配置快速入门

    platform:stm32f10xxx lib:STM32F10x_StdPeriph_Lib_V3.5.0 前言 在做三相逆变的时候,需要软件生成SVPWM波形,具体的算法需要产生三对互补的PWM ...

  5. STM32 TIM重映射

    复用功能 没有重映射 部分重映射 完全重映射 TIM3_CH1 PA6 PB4 PC6 CH2 PA7 PB5 PC7 CH3 PB0 PB0 PC8 CH4 PB1 PB1 PC9 /**重映射 t ...

  6. STM32 ~ STM32 TIM重映射

    复用功能 没有重映射 部分重映射 完全重映射 TIM3_CH1 PA6 PB4 PC6 CH2 PA7 PB5 PC7 CH3 PB0 PB0 PC8 CH4 PB1 PB1 PC9 /**重映射 t ...

  7. stm32定时器PWM模式和输出比较模式

    pwm模式是输出比较模式的一种特例,包含于输出比较模式中 /** @defgroup TIM_Output_Compare_and_PWM_modes * @{ */ #define TIM_OCMo ...

  8. stm32定时器主从模式

    TIM2作master:TIM3,TIM4作slave 定时器2事件更新被用作触发输出TRGO 从定时器TIM3,TIM4工作在从模式:门控模式 触发选择设为:ITR1,这样TIM2的TRGO就连到了 ...

  9. STM32中用 stop 模式 配合低功耗模式下的自动唤醒(AWU) 能否实现FreeRTOS tickless 模式

    已经实现  ,2018年11月17日11:56:42,具体 如下: 第一步 : 修改 void vPortSetupTimerInterrupt( void ) 函数 ,修改原来的 systick 定 ...

随机推荐

  1. python这门语言为什么要起这个名字

    我只是一只可爱的小虫 前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:Liz喵 PS:如有需要Python学习资料的小 ...

  2. A. Number Theory Problem

    题目大意:计算小于2^n,且满足2^k-1并且是7的倍数的个数 思路:优先打表,数据不大,1e5,然后求个前n项和 #include<bits/stdc++.h> using namesp ...

  3. 好用的mitmproxy代理抓包

    安装证书 浏览器输入 `mitm.it` 下载证书有时候打不开,可能是起的服务卡死了,回车下命令行,再再网页刷新下载证书就可以了. mitmweb Chrome浏览器代理设置 打开的话,记得保存点一下 ...

  4. Asp.Net Core 3.0 学习3、Web Api 文件上传 Ajax请求以及跨域问题

    1.创建Api项目 我用的是VS2019 Core3.1 .打开Vs2019 创建Asp.Net Core Web应用程序命名CoreWebApi 创建选择API 在Controller文件夹下面添加 ...

  5. 深度剖析前端JavaScript中的原型(JS的对象原型)

          这张图片有点劝退了,哈哈哈~    通过原型机制,JavaScript 中的对象从其他对象继承功能特性:这种继承机制与经典的面向对象编程语言的继承机制不同.本文将探讨这些差别,解释原型链如 ...

  6. es技术规划

    一.业务背景 es服务当前没有专门的部门负责维护和开发,交由各端自行负责维护,随着公司业务查询和统计需求非常多,会面临居多方面问题和挑战: 无人(专业RD或部门)负责 无专业的人进行维护,遇到问题几乎 ...

  7. JSP中引用CSS样式文件却无法显示的问题解决方案

    你也遇到过这种问题吗,CSS写好了,JSP写好了,在JSP中调用CSS文件,路径检查后也正确,但是无法显示渲染后的页面 原因:罪魁祸首就是过滤器响应数据的时候,响应头设置为了“text/html”,但 ...

  8. linq深入

    一.匿名类:[ C# 3.0/.NET 3.x 新增特性 ] 1.1 不好意思,我匿了 在开发中,我们有时会像下面的代码一样声明一个匿名类:可以看出,在匿名类的语法中并没有为其命名,而是直接的一个ne ...

  9. pytorch seq2seq模型训练测试

    num_sequence.py """ 数字序列化方法 """ class NumSequence: """ ...

  10. Spring Cloud微服务技术概览

    Spring Cloud 是一系列框架的有序集合.它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册.配置中心.消息总线.负载均衡.断路器.数据监控等,都 ...