一、学习前提:编码器接口简介

编码器接口是STM32微控制器中用于处理增量式(正交)编码器信号的重要功能。它能够接收来自编码器的A相和B相信号,并根据这些信号自动控制计数器(CNT)的增减,从而确定编码器的位置、旋转方向和旋转速度。每个高级定时器和通用定时器都配备了一个编码器接口,且两个输入引脚通常借用输入捕获的通道1和通道2(即CH1和CH2引脚)。

工作原理

编码器接口的工作原理基于正交信号的特性。正交信号的A相和B相信号之间存在90°的相位差,这种相位差使得可以通过解析A相和B相信号来确定旋转的方向。当编码器旋转时,A相和B相信号的边沿变化会导致定时器计数器(CNT)的值相应增加或减少,从而实现对旋转运动的精确测量。

功能特点

  1. 四倍频技术:STM32的编码器接口支持四倍频技术,这意味着它能够在每个周期内检测到更多的脉冲,从而提高计数精度。

  2. 方向检测:通过分析A相和B相信号的相位关系,编码器接口能够自动检测旋转方向,即正转或反转。

  3. 灵活的软件配置:编码器接口允许通过软件配置来适应不同的编码器类型和应用需求,例如设置编码器模式为2倍频或4倍频,以及选择输入信号的极性和通道。

  4. 输入滤波:为了减少噪声对信号的影响,编码器接口还支持输入滤波功能,如果一相不变,另一相一直变化,则检测为噪声,不进行反应。

应用场景

编码器接口在需要精确测量和控制旋转运动的场合中非常有用,例如:

  • 电机控制:通过编码器接口,可以精确测量电机的转速和位置,实现精确的速度和位置控制。

  • 机器人技术:在机器人关节的控制中,编码器接口可以帮助确定关节的精确位置和运动状态。

  • 自动化设备:在各种自动化设备中,编码器接口可以用于监控和控制机械部件的旋转运动。

初始化步骤

编码器接口的初始化通常包括以下步骤:

  1. 开启时钟:开启GPIO和定时器的时钟。

  2. 配置GPIO:将相关的GPIO引脚配置为输入模式。

  3. 配置时基单元:设置预分频器、自动重装值等参数。

  4. 配置输入捕获单元:设置滤波器和极性等参数。

  5. 配置编码器接口模式:根据需要配置编码器接口的工作模式。

  6. 启动定时器:最后,启动定时器并进行必要的软件设计,如设置定时器的中断优先级和开启中断。

通过合理配置和编程,STM32微控制器的编码器接口可以显著提升各种应用的性能和可靠性,实现对旋转运动的精确控制和测量。

二、正交编码器接口实现简介

正交编码器接口是一种用于处理正交编码信号的接口技术。正交编码信号由两个相位差为90度的脉冲信号组成,通常称为A相和B相。通过对A相和B相信号的边缘检测和比较,可以确定旋转方向和计算旋转脉冲数,从而实现对旋转角度和速度的精确测量。

图中展示了正交编码器在正转和反转情况下的信号波形。图中的波形清晰地显示了A相和B相信号的相位关系,以及它们如何通过边沿变化来指示旋转方向。

正转情况:

  • 在正转时,A相的上升沿通常先于B相的上升沿出现,这意味着当A相信号从低电平变为高电平时,B相信号仍然是低电平。随后,B相信号上升,当B相信号从低电平变为高电平时,A相信号已经处于高电平状态。

反转情况:

  • 在反转时,B相的上升沿先于A相的上升沿出现,这与正转的情况相反。即当B相信号从低电平变为高电平时,A相信号仍然是低电平。随后,A相信号上升,当A相信号从低电平变为高电平时,B相信号已经处于高电平状态。

这种相位关系使得可以通过检测A相和B相信号的边沿变化来确定旋转的方向。

三、旋转编码器基本结构

编码器接口的基本结构,它主要由以下几个部分组成:

  1. GPIO(通用输入输出端口)

    • 图中显示了两个GPIO端口,每个端口都连接到一个滤波器。

    • GPIO端口用于接收来自编码器的A相和B相信号。

  2. 滤波器

    • 每个GPIO端口后都有一个滤波器,用于滤除输入信号中的噪声,以确保信号的稳定性和准确性。

    • 滤波器有助于提高信号的质量,减少由于电磁干扰或其他噪声源引起的误触发。

  3. 边沿检测极性选择

    • 滤波器后的信号会经过边沿检测极性选择电路,用于确定信号的边沿类型(上升沿或下降沿)。

    • 这个选择电路可以根据应用需求来设置,以适应不同的编码器信号特性。

  4. 编码器接口

    • 编码器接口是整个结构的核心部分,它接收经过处理的A相和B相信号。

    • 编码器接口负责解析这些信号,并根据信号的变化来控制计数器的增减。

  5. 时基单元

    • 时基单元包括预分频器(PSC)和计数器(CNT)。

    • 预分频器用于调整计数器的计数频率,以匹配编码器的信号频率。

    • 计数器用于记录编码器的脉冲数,从而确定旋转的角度和速度。

  6. ARR(自动重装载器)

    • ARR用于设置计数器的最大值,当计数器达到这个值时,会自动重置为0,重新开始计数。

    • 这个功能可以用于实现周期性测量,或者在达到某个计数值时触发特定的操作。

 

四、可实现功能

  1. 信号接收

    接口通过GPIO端口接收来自编码器的A相和B相信号。
  2. 信号处理

    滤波器用于去除信号中的噪声,保证信号清晰。边沿检测极性选择电路识别信号的上升沿或下降沿,确定编码器的旋转方向。
  3. 计数与测量

    编码器接口解析信号,控制内部计数器(CNT)根据信号变化进行计数。计数器记录脉冲数,用于计算编码器的旋转角度和速度。
  4. 周期性测量

    自动重装载器(ARR)设定计数器的最大值,实现周期性计数和测量。
  5. 方向检测

    通过分析A相和B相信号的相位关系,接口能够自动检测编码器的旋转方向 

 五、现象和代码

最终实现顺时针旋转为正速度,逆时针旋转为负速度。

代码:encorder.c

#include "stm32f10x.h"                  // Device header

int16_t Encoder_Count; // 定义一个全局变量,用于存储编码器的计数值

// 编码器初始化函数
void Encoder_Init(void)
{
    // 使能GPIOB和AFIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
    
    // 配置GPIO
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; // 设置为输入上拉模式
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1; // 配置PB0和PB1引脚
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; // 设置速度为50MHz
    GPIO_Init(GPIOB,&GPIO_InitStructure);
    
    // 配置AFIO,用于引脚重映射
    // 配置GPIOB的14引脚为外设中断线路。
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
    
    // 配置EXTI(外部中断/事件控制器)
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line=EXTI_Line0|EXTI_Line1; // 配置PB0和PB1的外部中断线路
    EXTI_InitStructure.EXTI_LineCmd=ENABLE; // 使能中断线路
    EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; // 设置为中断模式
    EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling; // 设置触发方式为下降沿触发
    EXTI_Init(&EXTI_InitStructure);
    
    // 配置NVIC(嵌套向量中断控制器)
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn; // EXTI0中断通道
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; // 使能中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; // 设置抢占优先级为1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=1; // 设置子优先级为1
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置中断优先级分组
    NVIC_Init(&NVIC_InitStructure);
    
    NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn; // EXTI1中断通道
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; // 使能中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; // 设置抢占优先级为1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=2; // 设置子优先级为2
    NVIC_Init(&NVIC_InitStructure);
}

// 获取编码器计数值的函数
int16_t Encoder_Get(void)
{
    int16_t Temp;
    Temp=Encoder_Count; // 读取计数值
    Encoder_Count=0; // 清零计数值
    return Temp; // 返回计数值
}

// EXTI0中断处理函数
void EXTI0_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line0)==SET) // 检查中断标志位
    {
        if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0) // 读取PB1引脚的状态
        {
            Encoder_Count--; // 如果PB1为低电平,则计数器减1
        }
        EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志位
    }
}

// EXTI1中断处理函数
void EXTI1_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line1)==SET) // 检查中断标志位
    {
        if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0) // 读取PB0引脚的状态
        {
            Encoder_Count++; // 如果PB0为低电平,则计数器加1
        }
        EXTI_ClearITPendingBit(EXTI_Line1); // 清除中断标志位
    }
}

 main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.H"
#include "ENCODER.H"
int16_t Num;
int main()
{
	OLED_Init();
	Encoder_Init();
	OLED_ShowString(1,3,"chenxi");
	while(1)
	{
		Num+=Encoder_Get();
		OLED_ShowSignedNum(2,1,Num,5);//显示速度
	}
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐