STM32驱动旋转编码器(标准库)

一、机械式增量式旋转编码器工作原理

KY-040是一种机械式增量编码器,通过CLK和DT两个相位差90°的方波信号判断旋转方向:

  • 顺时针旋转:CLK引脚先变化,DT随后变化

  • 逆时针旋转:DT引脚先变化,CLK随后变化

  • 它有5个引脚:CLK、DT、SW、VCC、GND

  • 编程思想:通过中断I/O不断轮询的方式:取一个周期2个引脚的波形图,TI1对应CLK,TI2对应DT,把两个脚配置为上升沿触发,当CLK引脚上升沿到来触发中断,此时进入中断服务函数判断DT引脚电平是否为低电平,若为则编码器正转,反之,几乎同时,当DT引脚上升沿到来触发中断,进入中断服务函数判断CLK引脚是否为低电平,若为则编码器反转。(下面提供的代码会有详细的注释,没看懂可以翻到下面源码部分我这里通过数据的加减来模拟旋转编码器的正反转,并输出到串口上。

  • 源码下载链接:https://share.weiyun.com/zlszrbGj

  • 下面图片参考作者@百里与司空https://blog.youkuaiyun.com/hjlkklk/article/details/142960567?ops_request_misc=&request_id=&biz_id=102&utm_term=STM32%E2%80%94%E6%97%8B%E8%BD%AC%E7%BC%96%E7%A0%81%E5%99%A8&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-2-142960567.142^v101^pc_search_result_base5&spm=1018.2226.3001.4187

  • 驱动源码encoder.c:

  • #include "encoder.h"
    #include "stm32f10x.h"
    #include "Delay.h"
    
    /*通过I/O口的中断不断检测来驱动旋转编码器*/
    
    /*********************************
    
       @brief:    旋转编码器的初始化
       @param:    void
       @retval:   void
      
     *********************************/
     
    int16_t Encoder_Count; //定义一个变量的值用来模拟旋转编码器的正反转
     
    void Encoder_Init(void)
    {
    
        GPIO_InitTypeDef GPIO_InitStructure;
        RCC_APB2PeriphClockCmd(Encoder_GPIO_CLK, ENABLE);		    //开启GPIOB的时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//开启AFIO的时钟,外部中断必须开启AFIO的时钟
        
        
        //CLK和DT和SW三个引脚对应GPIOA的引脚的配置
        GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;    //上拉输入,因为旋转编码器空闲时为高电平
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2;
        GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(Encoder_GPIO_PORT, &GPIO_InitStructure);
        
        /*AFIO选择中断引脚*/
        GPIO_EXTILineConfig(Encoder_GPIO_PORT_Source, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择CLK的PA0为外部中断引脚
        GPIO_EXTILineConfig(Encoder_GPIO_PORT_Source, GPIO_PinSource1);//将外部中断的1号线映射到GPIOB,即选择DT的PA1为外部中断引脚
        
        /*EXTI的初始化*/
        
        EXTI_InitTypeDef EXTI_InitStructure;
        EXTI_InitStructure.EXTI_Line=EXTI_Line0|EXTI_Line1;  //选择配置外部中断的0号线和1号线
        EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;    //指定外部中断线为中断模式
        EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising; //指定外部中断线为上升沿触发
        EXTI_InitStructure.EXTI_LineCmd=ENABLE;              //指定外部中断线使能
        EXTI_Init(&EXTI_InitStructure);
        
        /*NVIC中断分组*/
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	  //0~4都可以
        
        /*NVIC配置*/
        //EXTI0线的配置
        NVIC_InitTypeDef NVIC_InitStructure;
        NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;              //选择配置NVIC的EXTI0线
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;     //指定NVIC线路的抢占优先级为1
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;            //指定NVIC线路的响应优先级为1
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;               //NVIC使能
        NVIC_Init(&NVIC_InitStructure);                             //将配置好的结构体变量的置赋给NVIC_Init,配置NVIC外设
        
        
        //EXTI1线的配置
        NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn;              //选择配置NVIC的EXTI1线
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;     //指定NVIC线路的抢占优先级为1
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;            //指定NVIC线路的响应优先级为2
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;               //NVIC使能
        NVIC_Init(&NVIC_InitStructure);                             //将配置好的结构体变量的置赋给NVIC_Init,配置NVIC外设
        
    }
    
    
    /*编写EXTI0的中断服务函数*/
    void EXTI0_IRQHandler(void)
    {
      if(EXTI_GetITStatus(EXTI_Line0) == SET)   //先判断中断是否发生,即使不需要这步,但也要保持逻辑的严密性
      {
          if(GPIO_ReadInputDataBit(Encoder_GPIO_PORT, Encoder_GPIO_PIN_CLK) == 1)  //校验CLK引脚上升沿对应引脚的电平
          {
             if(GPIO_ReadInputDataBit(Encoder_GPIO_PORT, Encoder_GPIO_PIN_DT) == 0)  //在判断DT的引脚是否为低电平
             {
                
               Encoder_Count++;  //自加来模拟正转
               EXTI_ClearITPendingBit(EXTI_Line0); //注意:自加完后一定清楚中断标志位
             }
          
          }
      }
    }
    
    
    /*编写EXTI1的中断服务函数*/  //与上面的逻辑是一样的
    void EXTI1_IRQHandler(void)
    {
    
       if(EXTI_GetITStatus(EXTI_Line1) == SET)   //先判断中断是否发生,即使不需要这步,但也要保持逻辑的严密性
      {
          if(GPIO_ReadInputDataBit(Encoder_GPIO_PORT, Encoder_GPIO_PIN_DT) == 1)  //校验DT引脚上升沿对应引脚的电平
          {
             if(GPIO_ReadInputDataBit(Encoder_GPIO_PORT, Encoder_GPIO_PIN_CLK) == 0)  //在判断CLK的引脚是否为低电平
             {
                
               Encoder_Count--;  //自减来模拟反转
               EXTI_ClearITPendingBit(EXTI_Line1);  //注意:自加完后一定清楚中断标志位
             }
          
          }
      }   
    
    }
    
    
    /*获取Encoder_Count的值,和按键清0*/
    
    int16_t Encoder_Get_Count(void)
    {
       
       int16_t temp;
       temp=Encoder_Count;
       if(GPIO_ReadInputDataBit(Encoder_GPIO_PORT, Encoder_GPIO_PIN_CLK) == 1)   //检测SW引脚按下,则对Encoder_Count进行清0
       {
            Delay_us(10);  //延时按键消抖
            Encoder_Count=0;
       }
       return temp;
    
    }
    
    
    

    encoder.h头文件:

  • #ifndef __ENCODER_H
    #define __ENCODER_H
    
    #include "stm32f10x.h"
    
    //宏定义部分,方便用户修改引脚
    #define Encoder_GPIO_CLK              RCC_APB2Periph_GPIOA
    #define Encoder_GPIO_PORT             GPIOA
    #define Encoder_GPIO_PIN_CLK          GPIO_Pin_0
    #define Encoder_GPIO_PIN_DT           GPIO_Pin_1
    #define Encoder_GPIO_PIN_SW           GPIO_Pin_2
    #define Encoder_GPIO_PORT_Source      GPIO_PortSourceGPIOA
    
    void Encoder_Init(void);
    int16_t Encoder_Get_Count(void);
    
    
    #endif

  • 主函数main:

  • #include "stm32f10x.h"                  // Device header
    #include "Delay.h"
    #include "usart.h"
    #include "encoder.h"
    #include <stdio.h>
    
    int16_t Value;
    
    int main(void)
    {
    
        USART1_Config();
        Encoder_Init();
    
    	while(1)
    	{
          printf("\r\ntest\n");
          Value=Encoder_Get_Count();   //获取Encoder_Count的加减来模拟旋转编码器正反转
          printf("\r\nValue=%d\n",Value);   //打印在串口上
    	}
        
    }
    

    最后看效果:

  • ​​

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值