[应用相关] STM32 HAL库 EC11旋转编码器相位检测原理和程序

[复制链接]
 楼主| heimaojingzhang 发表于 2025-7-9 11:44 | 显示全部楼层 |阅读模式
常用的编码器A端与B端一个周期相位通常是相差90°的。

一个旋转周期内,同一时间A和B端的PIN读取输入得到以下

顺时针       —————————>

逆时针      <—————————

        A : 0        1        1        0

        B:  0        0        1        1

顺时针方向时,看A端变化 0->1是上升沿,1->0是下降沿,每次变化后的B的电平也会跟着改变,所以 (A = ↑,B = 0)或者(A = ↓,B = 1)代表顺时针方向。

从左到右



10541686b7d476d60e.png

逆时针方向时,看A端变化 0->1是上升沿,1->0是下降沿,每次变化后的B的电平也会跟着改变,所以 (A = ↑,B = 1)或者(A = ↓,B = 0)代表逆时针方向。

从右到左

15002686b7d4274772.png

事实上,将任意一端看作A端,另外一端看作B端,都可以得出以上规律。将A端看作时钟线,B端看作数据线,每次A端变化时读取B端的数据即可完成驱动,A端的变化的瞬时的,需要使用中断检测,才能快速响应处理。



EC11.h

#ifndef __EC11_H
#define __EC11_H

    #include "stm32f1xx_hal.h"

    #define EC_IDLE (0)     //编码器状态
    #define EC_CW   (1)     //顺时针
    #define EC_CCW  (2)     //逆时针

    //编码器方向如果相反了,修改宏定义只能 0 或者 1
    #if 1
        #define EC_DIR_1    EC_CW
        #define EC_DIR_2    EC_CCW
    #else
        #define EC_DIR_1    EC_CCW
        #define EC_DIR_2    EC_CW
    #endif

    #define EC11_A           HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15)
    #define EC11_B           HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2)


    extern char g_ec11_disalbe;
    extern char com_ec_result;

    void EC11_Init(void);

#endif


objectivec


EC11.c

#include "EC11.h"

//编码器反馈使能
char g_ec11_disalbe = 0;         //可用于临时禁用编码器
char com_ec_result = EC_IDLE;    //全局通用唯一可用接口,声明为外部变量

void EC11_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_GPIOA_CLK_ENABLE();

    //编码器B端
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    GPIO_InitStruct.Pin = GPIO_PIN_2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    //编码器A端(用于中断)
    GPIO_InitStruct.Pin = GPIO_PIN_15;
    //        GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
    //        GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; //采用上升沿和下降沿全检测
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    //配置PA15的中断优先级
    HAL_NVIC_SetPriority(EXTI15_10_IRQn, 3, 1);
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

}

//中断函数入口
void EXTI15_10_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
}


void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    /** 静态局部变量,记录旧状态 **/
    static char step = 0;                   //步进确定周期
    static char old_ec_state = EC_IDLE;     //旧编码器状态

    /** 临时局部变量 **/
    char cur_a = 1,cur_b = 1;               //当前A端和B端电平状态
    char temp_ec_state  = EC_IDLE;          //临时反馈编码器状态

    //用Switch的方式更方便移植
    switch(GPIO_Pin)
    {
        case GPIO_PIN_15:
        {
            if(g_ec11_disalbe)
            {
                com_encoder_e = EC_IDLE;
                return;
            }

            //读取AB端状态
            cur_a = EC11_A_PIN;
            cur_b = EC11_B_PIN;

            //判断方向状态
            temp_ec_state =  (cur_a) ? ((cur_b) ? EC_DIR_1 : EC_DIR_2) : ((!cur_b) ? EC_DIR_1 : EC_DIR_2);

            //判断是否需要更新状态,防止抖动
            if(old_ec_state != temp_ec_state)
            {
                step = 1;
                old_ec_state = temp_ec_state;
                return;
            }
            else
            {
                //防止抖动
                ++step;
                //两次中断(先上升沿,再下降沿)的步进为一个周期,合情合理
                if(step == 2)
                {       
                    step = 0;
                    com_ec_result = temp_ec_state;
                    old_ec_state        = EC_IDLE;                               
                }
            }
            break;
        }
        default:
        {
            break;
        }
    }
}


objectivec


#include "EC11.h"

int main(void)
{
    char c = '0';
    HAL_Init();
    RCC_HSE_PLL_72MHz_Clk_Init();
    delay_init_com();
    EC11_Init();               
    UART1_Init(115200);
    cur_huart_p = &huart1;

    for(;;)
    {
        switch(com_ec_result)
        {
            case EC_CCW:
            {
                c++;
                printf("%d -> %c \n",c,c);
                break;
            }
            case EC_CW:
            {
                c--;
                printf("%d -> %c \n",c,c);
                break;
            }
            default:
            {
              break;
            }
        }
        //切记,每次使用完编码器反馈的结果后,必须需重新置为空闲状态
        com_ec_result = EC_IDLE;
    }
}


objectivec


切记,每次使用完编码器反馈的结果后,必须需重新置为空闲状态,com_ec_result = EC_IDLE;
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_46079813/article/details/149062097

LOVEEVER 发表于 2025-7-29 23:39 | 显示全部楼层
HAL库 EC11旋转编码器相位检测原理
您需要登录后才可以回帖 登录 | 注册

本版积分规则

106

主题

4333

帖子

4

粉丝
快速回复 在线客服 返回列表 返回顶部