stm32读取编码器的两种方式

该文详细介绍了如何在STM32微控制器中使用外部中断和定时器来读取编码器信号。首先,通过初始化GPIO、NVIC和EXTI,实现了编码器的外部中断读取,根据下降沿触发事件更新计数和方向。其次,利用TIM初始化为编码器接口模式,配置PB6和PB7作为输入,并在定时器中断服务函数中处理编码器计数。文章提供了完整的代码示例,涵盖了中断和定时器两种读取方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.使用外部中断 读取

#include "spin.h"


#define encoder_port    GPIOG
#define encoder_pin     (GPIO_Pin_3|GPIO_Pin_5)
#define encoder_pin_A   GPIO_Pin_3  //外部中断引脚
#define encoder_pin_B   GPIO_Pin_5



void Encoder_Init_Exit()
{
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel                   = EXTI3_IRQn; //选择触发的通道 EXTI3_IRQn 与 PG3 EXTI_Line3 一致
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;     // 使能
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;          //抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;          //响应优先级
    NVIC_Init(&NVIC_InitStructure);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE);
    
    GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IN_FLOATING;// GPIO_Mode_IPU; //配置为上拉模式
    GPIO_InitStructure.GPIO_Pin   = encoder_pin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(encoder_port, &GPIO_InitStructure);

    // GPIO_PinRemaConfig() 可以用来引脚重映射
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOG, GPIO_PinSource3); //配置AFIO的数据选择器,来选择我们要的引脚,指定外部中断线

    EXTI_InitStructure.EXTI_Line    = EXTI_Line3;                  //需要我们配置中断线,我们用PG3所在的 EXTI_Line3
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;                      //开启中断
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;            //中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;        //设置触发方式 下降沿触发
    EXTI_Init(&EXTI_InitStructure);                                //调用这个参数,就可以根据这个结构体

}
 
void EXTI3_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line3) == SET) //确认是 EXTI_Line3引发的中断
    {    // encoder_pin_A 设置了外部中断,下降沿触发

        delay_ms(1); //很重要,必不可少, 软件滤波

        if (0 == GPIO_ReadInputDataBit(encoder_port, encoder_pin_A)) //判断 encoder_pin_A是低电平, 经历了下降沿
        {
            if (1 == GPIO_ReadInputDataBit(encoder_port, encoder_pin_B)) //判断 encoder_pin_B 电平状态,
            {
                ++encoder_g.counter;
                encoder_g.dir= 10; //正转标志
            }    
            else
            {
                --encoder_g.counter;
                encoder_g.dir = 20; //反转标志
            }

            printf("count %d\n", Encoder_Readnum_Exit(0));
            printf("dir %d\n", Encoder_ReadDir_Exit());
            
        }
        EXTI_ClearITPendingBit(EXTI_Line3); //手动清除外部中断标志位 
    }
}
/**
 * @brief 正转返回1 反转返回-1 静止返回0
 * 
 */
int Encoder_ReadDir_Exit(void) 
{
    if( 10 == encoder_g.dir ){
        encoder_g.dir= 0;
        return 1;
    }
    else if (20 == encoder_g.dir ){
        encoder_g.dir = 0;
        return -1;
    }
    else{
        encoder_g.dir = 0;
        return 0;
    }
}

/**
 * @brief flag=0 读数cnt不清零  flag=0 读数cnt清零
 *
 */
int Encoder_Readnum_Exit(u8 flag)
{
    int result = encoder_g.counter;

    if (flag){
        encoder_g.counter = 0;
     }

     return result;
}
#ifndef _SPIN_H
#define _SPIN_H

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "delay.h"
#include "usart.h"

typedef struct E_g{
    int counter;   //计数    
    u8 dir;        //旋转方向 正为+  反为- 静为 0
    u8 sw;         //按键状态 按下置1

}Encoder_g;

static Encoder_g  encoder_g={
    .counter=0,
    .dir=0,
    .sw=0
};

void Encoder_Init_Exit();
int Encoder_Readnum_Exit(u8 flag);
int Encoder_ReadDir_Exit(void);

#endif

2.定时器作编码电机模式读取


#include "encoder.h"
 
 
/**************************************************************************
函数功能:单位时间读取编码器B计数
入口参数:无
返回  值:计数值
**************************************************************************/
int Read_Encoder(TIM_TypeDef *TIMx)
{
    return (short)TIMx->CNT;
}
/**************************************************************************
函数功能:把TIM初始化为编码器接口模式
端口配置: PB6 PB7
入口参数:无
返回  值:无
**************************************************************************/
 void Encoder_Init_TIMx(TIM_TypeDef* TIMx )
 {
     TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
     TIM_ICInitTypeDef TIM_ICInitStructure;
     GPIO_InitTypeDef GPIO_InitStructure;

    // NVIC_InitTypeDef NVIC_InitStructure;
    //  NVIC_InitStructure.NVIC_IRQChannel                   = TIM3_IRQn;
    //  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;      //抢占优先级3
    //  NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;      //子优先级3
    //  NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE; // IRQ通道使能
    //  NVIC_Init(&NVIC_InitStructure);                                //根据指定的参数初始化VIC寄存器

     /*----------------------------不同定时器 修改这里 以及中断函数配置--------------------------------*/

     // GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);  //重映射配置  引脚冲突时配置

     RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);  //使能定时器3的时钟
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PB端口

     GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_6 | GPIO_Pin_7; //端口配置
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   //浮空输入
     GPIO_Init(GPIOA, &GPIO_InitStructure);                  //初始化PB端口

     ////////////////////////////////////////////////////////////////////////////////////

     TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
     TIM_TimeBaseStructure.TIM_Prescaler     = 0x0;                // 预分频器
     TIM_TimeBaseStructure.TIM_Period        = ENCODER_TIM_PERIOD; //设定计数器自动重装值
     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;       //选择时钟分频:不分频
     TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up; //边沿计数模式
     TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);               //初始化定时器3

     TIM_EncoderInterfaceConfig(TIMx, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); //使用编码器模式3(TIM_ICPolarity_Rising或者TIM_ICPolarity_Falling效果相同,都是4倍频)

     TIM_ICStructInit(&TIM_ICInitStructure);  //把TIM_ICInitStruct 中的每一个参数按缺省值填入
     TIM_ICInitStructure.TIM_ICFilter = 10;   //设置滤波器长度
     TIM_ICInit(TIMx, &TIM_ICInitStructure);  //根据 TIM_ICInitStruct 的参数初始化外设	TIMx

     TIM_ClearFlag(TIMx, TIM_FLAG_Update);        //清除TIM的更新标志位
     TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);   //使能定时器中断
     TIM_SetCounter(TIMx, 0);                     //计数初值设定
     TIM_Cmd(TIMx, ENABLE);                       //使能定时器
}

/**************************************************************************
 *  函数功能:TIM中断服务函数
 *
 *  功能:防止定时器cnt计数溢出
 * 
 *  入口参数:无
 *  
 *  返 回 值:无
 **************************************************************************/
// void TIM3_IRQHandler(void) ////////////
// {
//     if (_TIM3_->SR & 0X0001) //溢出中断
//     {
//     }
//     _TIM3_->SR &= ~(1 << 0); //清除中断标志位
//  }
#ifndef _encoder_H_
#define _encoder_H_

#include "stm32f10x.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_gpio.h"
#define ENCODER_TIM_PERIOD (u16)(65535)    

typedef struct bianmadianji 
{
    int coder;
    u8 mode;  

}G_ENCODER;

static G_ENCODER g_ENCODER={0,0};

void Encoder_Init_TIMx(TIM_TypeDef* TIMx);

int Read_Encoder(TIM_TypeDef* TIMx);

#endif
 

### STM32 读取编码器示例代码及使用教程 #### 配置环境准备 为了能够顺利运行STM32读取编码器的程序,需先准备好开发环境。这包括安装必要的IDE(如Keil MDK或STMCubeIDE),并获取相应的库文件和支持包。 #### 初始化GPIO和TIM外设 在开始编写具体的编码器读取逻辑之前,要初始化用于连接编码器信号线的GPIO端口以及负责计数的时间管理模块(Timer)。这里假设采用的是通用定时器作为增量型编码器接口: ```c // 定义使用的Timer编号及其通道分配给A/B相位输入引脚 #define ENCODER_TIMx TIM1 #define ENCODER_CHANNEL_A GPIO_PIN_8 #define ENCODER_CHANNEL_B GPIO_PIN_9 #define ENCODER_GPIO_PORT GPIOA ``` 接着,在`main.c`中完成基本设置: ```c MX_GPIO_Init(); // 初始化所有已配置的GPIO引脚 MX_TIM1_Init(); // 对应于ENCODER_TIMx定义的选择合适的定时器实例进行初始化 __HAL_TIM_ENABLE(&htim1); // 启动指定的定时器使能寄存器 ``` 以上部分确保了硬件资源被正确设定好以便后续操作[^4]。 #### 设置编码器模式 针对所选定时器开启编码器模式,允许它根据外部施加到两个不同管脚上的电平变化自动更新内部计数值。具体来说就是修改定时器高级控制寄存器(TIMx_CR2),使其工作于特定类型的编码器接口下: ```c static void MX_TIM1_Encoder_Init(void){ TIM_Encoder_InitTypeDef sConfig = {0}; htim1.Instance = ENCODER_TIMx; htim1.Init.Prescaler = 0; /* 不分频 */ htim1.Init.CounterMode = TIM_COUNTERMODE_UP; /* 计数向上 */ htim1.Init.Period = 65535; /* 自由运行模式下的最大周期值 */ htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; sConfig.EncoderMode = TIM_ENCODERMODE_TI12; /* A/B两路都参与计算 */ sConfig.IC1Polarity = TIM_ICPOLARITY_RISING; /* 上升沿触发 */ sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;/* TI1FP1映射至IC1 */ sConfig.IC1Prescaler = TIM_ICPSC_DIV1; /* 输入不分频 */ sConfig.IC1Filter = 0xF; /* 数字滤波系数 */ HAL_TIM_Encoder_ConfigChannel(&htim1, &sConfig, TIM_CHANNEL_ALL); } ``` 这段代码片段展示了如何利用官方提供的API函数来简化对复杂结构体成员赋初值的过程,并最终调用`HAL_TIM_Encoder_ConfigChannel()`完成整个配置过程[^2]。 #### 获取当前的位置信息 一旦完成了前面提到的各项准备工作之后,就可以很方便地从定时器处取得最新的位置反馈了。下面给出了一种简单的方式来进行这一操作: ```c int32_t GetEncoderPosition(void){ int32_t position = __HAL_TIM_GET_COUNTER(&htim1); return position; } ``` 此功能通过访问定时器的状态寄存器获得自上次重载以来累计发生过的事件次数,即代表了旋转的角度偏移量[^1]。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值