STM32入门开发操作记录(七)——旋转编码器计次

目录

  • 一、项目准备
  • 1. 工程模板
  • 2. 器件接线
  • 二、旋转编码器模块
  • Encoder.c
  • Encoder.h
  • 三、增量计数
  • 一、项目准备

    1. 工程模板

      本篇项目所用模板包含以下模块,声明函数见头文件,模块添加和函数功能详见往期记录。
    请添加图片描述

    2. 器件接线

      主装置:ST-Link仿真器,STM32系统板,MB102面包板,OLED显示屏(接线详见往期记录)

      除了A15B3B4JLINK的调试端口,其他端口可随意使用。各类器件默认接线方式如下。

    器件 端口/电源
    3.3/VCC +
    GND
    Encoder-A B0
    Encoder-B B1
    OLED-SCL B8
    OLED-SDA B9

      增加模块:旋转编码器A相 – B0端口,B相 – B1端口)
      注:可能需要对旋转编码器的引脚稍作调整才能插入面包板。

      旋转编码器往不同方向旋转时输出的电平变化如上所示:顺时针旋转时,B相的下降沿处,A相输出低电平逆时针旋转时,A相的下降沿处,B相输出低电平
      

    二、旋转编码器模块

      与上篇同理,利用上述波形特点作为中断源,触发中断实现计数。

    Encoder.c

      其中:若有多个中断,可以把优先级分组配置放在main函数内,while循环之前。取增量值的设计可以提高代码的可移植性,以便于后续的功能迁移,如测量转速等。

    #include "stm32f10x.h"
    
    int16_t Encoder_Count;		// 增量
    
    void Encoder_Init(void)
    {
    	// 开启外设时钟:外设EXTI、NVIC除外,内核外设不受RCC控制
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	// 外设GPIOB
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);	// 外设AFIO
    	
    	// 配置结构体参数
    	GPIO_InitTypeDef GPIO_InitStructure;
     	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;			// 上拉输入模式:默认高电平
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		// 最大速度50MHz
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;	// 中断引脚:PB0和PB1
    	// 初始化B0和B1端口
     	GPIO_Init(GPIOB, &GPIO_InitStructure);
    	
    	
    	// 配置中断线路:0号,1号
    	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
    	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
    	
     	// 配置结构体参数
    	EXTI_InitTypeDef EXTI_InitStructure;
     	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;		// 0号和1号中断线路
    	EXTI_InitStructure.EXTI_LineCmd = ENABLE;					// 中断线状态:使能
    	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			// 中断模式	
    	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		// 下降沿触发
    	// 初始化外部中断事件控制器
     	EXTI_Init(&EXTI_InitStructure);
    	
    	// 配置中断优先级:分组2
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				// 抢占/响应优先级范围:0~3
    	
    	// 定义结构体
    	NVIC_InitTypeDef NVIC_InitStructure;
    	
    	// 配置结构体参数
     	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;			// 中断请求通道:0号
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				// 中断请求通道状态:使能
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	// 抢占优先级:1	
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			// 响应优先级:1
    	// 初始化嵌套中断向量控制器:0号中断请求通道
     	NVIC_Init(&NVIC_InitStructure);
    	
    	// 配置结构体参数
     	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;			// 中断请求通道:1号
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				// 中断请求通道状态:使能
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	// 抢占优先级:1	
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;			// 响应优先级:2
    	// 初始化嵌套中断向量控制器:1号中断请求通道
     	NVIC_Init(&NVIC_InitStructure);
    }
    
    // 0号中断函数
    void EXTI0_IRQHandler(void)
    {
    	// 检查0号中断线请求状态寄存器的标志位
    	if(EXTI_GetITStatus(EXTI_Line0) == SET)					// 0号中断线:中断源触发
    	{
    		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)	// B1端口输入低电平
    		{
    			Encoder_Count --;								// 反转减数
    		}
    		EXTI_ClearITPendingBit(EXTI_Line0);					// 重置标志位
    	}
    }
    
    // 1号中断函数
    void EXTI1_IRQHandler(void)
    {
    	// 检查中断线请求状态寄存器的标志位
    	if(EXTI_GetITStatus(EXTI_Line1) == SET)					// 1号中断线:中断源触发
    	{
    		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)	// B0端口输入低电平
    		{
    			Encoder_Count ++;								// 正转加数
    		}
    		EXTI_ClearITPendingBit(EXTI_Line1);					// 重置标志位
    	}
    }
    
    
    // 获取增量值
    int16_t Encoder_Get(void)
    {
    	int16_t Temp;			// 初始化中继变量
    	Temp = Encoder_Count;	// 传递增量
    	Encoder_Count = 0;		// 重置计数
    	return Temp;
    }
    

      

    Encoder.h

    #ifndef __ENCODER_H
    #define __ENCODER_H
    
    void Encoder_Init(void);
    int16_t Encoder_Get(void);
    
    #endif
    
    

      

    三、增量计数

      建议:尽量不要在主函数和中断函数里调用同一硬件或变量,减少代码耦合性。

    #include "stm32f10x.h"		// 器件模块
    #include "OLED.h"			// OLED模块
    #include "Encoder.h"		// 旋转编码器模块
    
    int16_t Num;	// 增量
    
    int main(void)
    {	
    	// 初始化
    	OLED_Init();
    	Encoder_Init();
    	
    	// 显示计数
    	OLED_ShowString(1, 1, "Num:");
    	
    	while(1)
    	{
    		Num += Encoder_Get();
    		OLED_ShowSignedNum(1, 5, Num, 5);
    	}
    }
    
    

    作者:dandellion_

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32入门开发操作记录(七)——旋转编码器计次

    发表回复