STM32--编码器(E6B2-CWZ1X)

目录

前言

[一、E6B2-CWZ1X 核心工作原理](#一、E6B2-CWZ1X 核心工作原理)

[1. 编码器基础特性](#1. 编码器基础特性)

[2. 正交编码核心原理(A/B 相)](#2. 正交编码核心原理(A/B 相))

方向判断示意图

[3. Z 相(零位脉冲)作用](#3. Z 相(零位脉冲)作用)

[4. NPN 集电极开路输出特性](#4. NPN 集电极开路输出特性)

二、接线方式

三、软件程序

头文件(encoder_e6b2.h)

测试代码(main.c)

关键注意事项!!!!

总结

前言

最近用到了STM32驱动编码器(E6B2-CWZ1X)传感器模块,我这里用的STM32是STM32F407IGT6,这个模块作为编码器使用还是挺不错的,精度也够高,所以记录一下。

一、E6B2-CWZ1X 核心工作原理

E6B2-CWZ1X 是欧姆龙经典的增量式旋转编码器 ,核心用于检测旋转的角度、速度、方向,广泛应用于电机闭环控制、位置检测等场景。

1. 编码器基础特性

特性 说明
输出类型 NPN 集电极开路输出(需外接上拉电阻到 3.3V/5V,兼容 STM32 电平)
输出相 A 相、B 相(正交脉冲)+ Z 相(零位脉冲,每转 1 个)
分辨率 常见 100P/R、200P/R、360P/R、500P/R、1000P/R(每转脉冲数,可四倍频)
供电电压 DC5~12V(推荐 5V 供电,和 STM32 共地)
响应频率 最高 100kHz(满足大部分低速 / 中速旋转场景)

2. 正交编码核心原理(A/B 相)

E6B2-CWZ1X 的 A、B 相输出相位差 90° 的方波脉冲(正交信号),通过相位差判断旋转方向,通过脉冲数计算旋转角度 / 圈数:

  • 正转:A 相超前 B 相 90°(比如 A 相上升沿时,B 相为低电平);
  • 反转:B 相超前 A 相 90°(比如 A 相上升沿时,B 相为高电平);
  • 脉冲计数:每一个 A/B 相的跳变都代表旋转了一个最小角度(分辨率越高,角度精度越高)。
方向判断示意图

3. Z 相(零位脉冲)作用

Z 相每旋转一圈输出1 个窄脉冲,用于:

  • 清零计数寄存器,实现 "绝对零位" 校准;
  • 计算旋转的总圈数(每检测到 1 个 Z 相脉冲,圈数 + 1/-1)。

4. NPN 集电极开路输出特性

E6B2-CWZ1X 的输出引脚为 NPN 三极管集电极,默认悬空,需外接4.7KΩ 上拉电阻到 3.3V(STM32 电平):

  • 编码器输出低电平:三极管导通,引脚拉到 GND;
  • 编码器输出高电平:三极管截止,引脚通过上拉电阻到 3.3V;
  • 无外部上拉时,引脚电平不确定,无法检测脉冲。

二、接线方式

E6B2-CWZ1X 引脚 STM32 引脚 硬件说明
VCC DC5V 编码器供电(不能接 3.3V,需 5V)
GND STM32 GND 必须共地,否则脉冲信号不稳定
A 相 PA0(TIM2_CH1) 接 4.7KΩ 上拉电阻到 3.3V,NPN 输出需上拉
B 相 PA1(TIM2_CH2) 同上
Z 相 PA2(EXTI2) 同上(可选,用于零位校准)

三、软件程序

头文件(encoder_e6b2.h)
复制代码
#ifndef __ENCODER_E6B2_H
#define __ENCODER_E6B2_H

#include "stm32f4xx.h"

// ==================== 编码器参数配置 ====================
#define ENCODER_RESOLUTION  1000    // E6B2-CWZ1X分辨率(P/R,根据实际型号修改)
#define ENCODER_TIM         TIM2    // 选用的定时器(TIM2/TIM3/TIM4等支持编码器模式)
#define ENCODER_TIM_CLK     RCC_APB1Periph_TIM2
#define ENCODER_PPR         (ENCODER_RESOLUTION * 4) // 四倍频后每转脉冲数(提高精度)

// ==================== 引脚定义 ====================
// A相:TIM2_CH1 → PA0
#define ENCODER_A_GPIO_PORT  GPIOA
#define ENCODER_A_GPIO_PIN   GPIO_Pin_0
#define ENCODER_A_GPIO_CLK   RCC_AHB1Periph_GPIOA
#define ENCODER_A_GPIO_AF    GPIO_AF_TIM2
#define ENCODER_A_GPIO_SRC   GPIO_PinSource0

// B相:TIM2_CH2 → PA1
#define ENCODER_B_GPIO_PORT  GPIOA
#define ENCODER_B_GPIO_PIN   GPIO_Pin_1
#define ENCODER_B_GPIO_CLK   RCC_AHB1Periph_GPIOA
#define ENCODER_B_GPIO_AF    GPIO_AF_TIM2
#define ENCODER_B_GPIO_SRC   GPIO_PinSource1

// Z相:EXTI2 → PA2(可选)
#define ENCODER_Z_GPIO_PORT  GPIOA
#define ENCODER_Z_GPIO_PIN   GPIO_Pin_2
#define ENCODER_Z_GPIO_CLK   RCC_AHB1Periph_GPIOA
#define ENCODER_Z_EXTI_LINE  EXTI_Line2
#define ENCODER_Z_EXTI_PORT  EXTI_PortSourceGPIOA
#define ENCODER_Z_EXTI_PIN   EXTI_PinSource2
#define ENCODER_Z_IRQn       EXTI2_IRQn

// ==================== 函数声明 ====================
void Encoder_E6B2_Init(void);                // 编码器初始化(TIM编码器模式+Z相中断)
int32_t Encoder_Get_Count(void);             // 获取当前脉冲计数值
void Encoder_Clear_Count(void);              // 清零脉冲计数值
float Encoder_Get_Angle(void);               // 获取当前旋转角度(°)
int32_t Encoder_Get_Revolutions(void);       // 获取旋转圈数(带方向)
int8_t Encoder_Get_Direction(void);          // 获取旋转方向(1=正转,-1=反转,0=静止)

#endif

源文件(encoder_e6b2.c)

复制代码
#include "encoder_e6b2.h"
#include "stm32f4xx_tim.h"
#include "stm32f4xx_exti.h"
#include "stm32f4xx_syscfg.h"

// 全局变量:旋转圈数(Z相中断更新)
static int32_t g_encoder_revolutions = 0;

/**
 * @brief  配置编码器A/B相引脚(复用为TIM编码器接口)
 */
static void Encoder_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    
    // 1. 使能GPIO时钟
    RCC_AHB1PeriphClockCmd(ENCODER_A_GPIO_CLK | ENCODER_B_GPIO_CLK | ENCODER_Z_GPIO_CLK, ENABLE);
    
    // 2. 配置A/B相:复用功能输入(上拉,匹配NPN输出)
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;         // 复用功能
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;         // 上拉(匹配NPN集电极开路)
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;       // 推挽(复用模式无影响)
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;   // 高速(匹配100kHz脉冲)
    
    // 配置A相
    GPIO_InitStruct.GPIO_Pin = ENCODER_A_GPIO_PIN;
    GPIO_Init(ENCODER_A_GPIO_PORT, &GPIO_InitStruct);
    GPIO_PinAFConfig(ENCODER_A_GPIO_PORT, ENCODER_A_GPIO_SRC, ENCODER_A_GPIO_AF);
    
    // 配置B相
    GPIO_InitStruct.GPIO_Pin = ENCODER_B_GPIO_PIN;
    GPIO_Init(ENCODER_B_GPIO_PORT, &GPIO_InitStruct);
    GPIO_PinAFConfig(ENCODER_B_GPIO_PORT, ENCODER_B_GPIO_SRC, ENCODER_B_GPIO_AF);
    
    // 3. 配置Z相:外部中断输入(上拉)
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;         // 输入模式
    GPIO_InitStruct.GPIO_Pin = ENCODER_Z_GPIO_PIN;
    GPIO_Init(ENCODER_Z_GPIO_PORT, &GPIO_InitStruct);
}

/**
 * @brief  配置Z相外部中断(零位清零)
 */
static void Encoder_Z_EXTI_Config(void)
{
    EXTI_InitTypeDef EXTI_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;
    
    // 1. 使能SYSCFG时钟(外部中断必需)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
    
    // 2. 连接EXTI线到GPIO引脚
    SYSCFG_EXTILineConfig(ENCODER_Z_EXTI_PORT, ENCODER_Z_EXTI_PIN);
    
    // 3. 配置EXTI
    EXTI_InitStruct.EXTI_Line = ENCODER_Z_EXTI_LINE;
    EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;   // 中断模式
    EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;// 上升沿触发(Z相脉冲为高电平窄脉冲)
    EXTI_InitStruct.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStruct);
    
    // 4. 配置NVIC(中断优先级)
    NVIC_InitStruct.NVIC_IRQChannel = ENCODER_Z_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级1
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;        // 子优先级0
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
}

/**
 * @brief  配置TIM编码器模式
 */
static void Encoder_TIM_Config(void)
{
    TIM_EncoderInitTypeDef TIM_EncoderInitStruct;
    
    // 1. 使能定时器时钟
    RCC_APB1PeriphClockCmd(ENCODER_TIM_CLK, ENABLE);
    
    // 2. 复位定时器配置
    TIM_DeInit(ENCODER_TIM);
    
    // 3. 编码器模式配置
    TIM_EncoderInitStruct.TIM_EncoderMode = TIM_EncoderMode_TI12; // 同时检测TI1/TI2跳变(四倍频)
    TIM_EncoderInitStruct.TIM_IC1Polarity = TIM_IC1Polarity_Rising; // TI1上升沿有效
    TIM_EncoderInitStruct.TIM_IC1Selection = TIM_IC1Selection_DirectTI; // 直接连接到TI1
    TIM_EncoderInitStruct.TIM_IC1Prescaler = TIM_ICPSC_DIV1; // 不分频
    TIM_EncoderInitStruct.TIM_IC1Filter = 0x0F; // 滤波(减少干扰,0x0F为最大滤波)
    
    TIM_EncoderInitStruct.TIM_IC2Polarity = TIM_IC2Polarity_Rising; // TI2上升沿有效
    TIM_EncoderInitStruct.TIM_IC2Selection = TIM_IC2Selection_DirectTI;
    TIM_EncoderInitStruct.TIM_IC2Prescaler = TIM_ICPSC_DIV1;
    TIM_EncoderInitStruct.TIM_IC2Filter = 0x0F;
    
    TIM_EncoderInterfaceConfig(ENCODER_TIM, &TIM_EncoderInitStruct);
    
    // 4. 设置计数器范围(32位,无需溢出处理)
    TIM_SetAutoreload(ENCODER_TIM, 0xFFFFFFFF); // 最大计数值
    TIM_SetCounter(ENCODER_TIM, 0);             // 初始计数清零
    
    // 5. 使能定时器
    TIM_Cmd(ENCODER_TIM, ENABLE);
}

/**
 * @brief  E6B2-CWZ1X编码器初始化(整合所有配置)
 */
void Encoder_E6B2_Init(void)
{
    Encoder_GPIO_Config();      // 引脚配置
    Encoder_TIM_Config();       // 定时器编码器模式
    Encoder_Z_EXTI_Config();    // Z相中断配置
}

/**
 * @brief  获取当前脉冲计数值(带方向,正转+,反转-)
 * @retval 32位计数值(范围:-2^31 ~ 2^31-1)
 */
int32_t Encoder_Get_Count(void)
{
    // TIM2计数器为16位?F4的TIM2是32位,直接读取
    return (int32_t)TIM_GetCounter(ENCODER_TIM);
}

/**
 * @brief  清零脉冲计数值
 */
void Encoder_Clear_Count(void)
{
    TIM_SetCounter(ENCODER_TIM, 0);
}

/**
 * @brief  获取当前旋转角度(°)
 * @retval 角度值(范围:-360° ~ +∞,正转为正,反转为负)
 */
float Encoder_Get_Angle(void)
{
    int32_t count = Encoder_Get_Count();
    // 角度 = 计数值 / 每转脉冲数 * 360°
    return (float)count / ENCODER_PPR * 360.0f;
}

/**
 * @brief  获取旋转圈数(带方向)
 * @retval 圈数(正转+,反转-)
 */
int32_t Encoder_Get_Revolutions(void)
{
    return g_encoder_revolutions;
}

/**
 * @brief  获取旋转方向
 * @retval 1:正转,-1:反转,0:静止
 */
int8_t Encoder_Get_Direction(void)
{
    // TIM_GetFlagStatus:判断计数器方向(TIM_FLAG_DIR:方向标志位)
    if(TIM_GetFlagStatus(ENCODER_TIM, TIM_FLAG_DIR) != RESET)
    {
        return -1; // 反转(计数器递减)
    }
    else
    {
        // 检测是否静止(计数值无变化,可选)
        static int32_t last_count = 0;
        int32_t current_count = Encoder_Get_Count();
        if(current_count == last_count)
        {
            last_count = current_count;
            return 0; // 静止
        }
        last_count = current_count;
        return 1; // 正转(计数器递增)
    }
}

/**
 * @brief  Z相中断服务函数(零位清零,更新圈数)
 */
void EXTI2_IRQHandler(void)
{
    if(EXTI_GetITStatus(ENCODER_Z_EXTI_LINE) != RESET)
    {
        // 根据旋转方向更新圈数
        if(Encoder_Get_Direction() == 1)
        {
            g_encoder_revolutions++;
        }
        else if(Encoder_Get_Direction() == -1)
        {
            g_encoder_revolutions--;
        }
        
        // 清零脉冲计数(回到零位)
        Encoder_Clear_Count();
        
        // 清除中断标志位
        EXTI_ClearITPendingBit(ENCODER_Z_EXTI_LINE);
    }
}
测试代码(main.c)
复制代码
#include "stm32f4xx.h"
#include "encoder_e6b2.h"
#include "delay.h"
#include "stdio.h"

// 重定向printf到串口1(用于打印编码器数据)
int fputc(int ch, FILE *f)
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
    return ch;
}

int main(void)
{
    // 系统初始化
    SystemInit();
    // 延时初始化(SysTick)
    SysTime_Init();
    // 编码器初始化
    Encoder_E6B2_Init();
    
    // 清零初始计数
    Encoder_Clear_Count();
    
    while(1)
    {
        // 读取编码器数据
        int32_t count = Encoder_Get_Count();
        float angle = Encoder_Get_Angle();
        int32_t revolutions = Encoder_Get_Revolutions();
        int8_t dir = Encoder_Get_Direction();
        
        // 串口打印(波特率115200)
        printf("计数值:%d | 角度:%.2f° | 圈数:%d | 方向:%s\r\n",
               count, angle, revolutions,
               dir == 1 ? "正转" : (dir == -1 ? "反转" : "静止"));
        
        // 500ms打印一次
        Delay_Ms(500);
    }
}

关键注意事项!!!!

  1. 定时器选择
    • STM32F4 的 TIM2/TIM3/TIM4/TIM5 是 32 位定时器,适合长时间计数(无溢出);TIM1/TIM8 是 16 位,需注意溢出处理。
  2. 滤波配置
    • TIM_EncoderInitStruct.TIM_IC1Filter = 0x0F;:增加输入滤波,减少电磁干扰导致的误计数(工业场景必配)。
  3. 中断优先级
    • Z 相中断优先级需高于普通任务,避免零位脉冲丢失。
  4. 供电与共地
    • 编码器 VCC 接 5V,STM32 接 3.3V,必须共地,否则脉冲信号会有电平偏移。

总结

  1. E6B2-CWZ1X 核心原理:通过 A/B 相正交脉冲的相位差判断旋转方向,脉冲数计算角度 / 圈数,Z 相实现零位校准,NPN 集电极开路输出需上拉;
  2. STM32 驱动核心:使用定时器编码器接口自动解码正交脉冲,无需手动处理中断,CPU 占用低,精度高;
  3. 关键要点:引脚配置为上拉输入(适配 NPN 输出),开启四倍频提升精度,Z 相中断实现零位清零,32 位定时器避免计数溢出。
相关推荐
SUNNYSPY0011 小时前
2N7002K-ASEMI智能家居控制专用2N7002K
单片机
bai5459362 小时前
STM32 PWM驱动电机
stm32·单片机·嵌入式硬件
cooldream200910 小时前
小智 AI 智能音箱深度体验全解析:人设、音色、记忆与多场景玩法的全面指南
人工智能·嵌入式硬件·智能音箱
聚能芯罗1803804647612 小时前
Hi8001/Hi8002高集成升压芯片2.7-40V 宽输入电压智芯一级代理聚能芯半导体原厂技术支持
嵌入式硬件
传感器与混合集成电路12 小时前
PSOC 7020运算模块解析:从放大器到ADC的片上模拟集成之道
嵌入式硬件·物联网
bai54593614 小时前
STM32单片机光敏传感器控制蜂鸣器
stm32·单片机·嵌入式硬件
米羊12114 小时前
FAT32(上)
stm32·单片机·嵌入式硬件
MARIN_shen16 小时前
Marin说PCB之电容物料的替换经验总计--03
嵌入式硬件·硬件工程·信号处理·pcb工艺
d111111111d16 小时前
STM32低功耗学习-停止模式-(学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习