基于stm32单片机的Grayhill编码器/开关软件设计

1、初识编码器,像示波器的旋转按钮,可左旋右旋,还可以按下,我们使用的是Grayhill编码器,如下图所示:

从图中可以看出,该编码器一共有6个IO,从1-6分别为GND,GND,C,B,A,VCC,。

其中VCC和GND接3.3V和GND,A、B对应旋转时电平的跳变IO,C对应按下时电平的跳变IO。

 

(1) 硬件电路设计上,为了方便代码编写与理解,最好把编码器的A、B接到单片机相邻的GPIO中,即使用同一个中断处理函数,比如本设计A、B分别接到PB12和PB11。C接任一个GPIO,当成中断使用。

(2) 顺时针和逆时针旋转编码器时,慢慢旋转一个单位(手指头会有明显旋转到位的感觉),用万用表测量A、B对应的IO电平,并记录下来。旋转一圈为止。如下表是我使用的编码器,在顺、逆时针旋转一圈时所记录下的IO电平(旋4次即满一圈):

(实际使用中,旋转了24小格才满一圈,这里只记录方法,不记录确切的值,需要自己测量)

 

 

旋转前

旋1/4圈

旋2/4圈

旋3/4圈

旋4/4圈

顺时针

A电平

0

1

1

0

0

B电平

0

0

1

1

0

逆时针

A电平

0

0

1

1

0

B电平

0

1

1

0

0

                                    表1         顺、逆时针旋转编码器时A、B对应IO电平

2、A、B对应的IO初始化成中断双边沿触发方式,如下所示:

void KeyA_Init(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

EXTI_InitTypeDef EXTI_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

 

/* Enable the EXTI_PB1 Clock */

RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB , ENABLE);

 

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;    //浮空输入   

GPIO_Init(GPIOB, &GPIO_InitStructure);

 

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource12);   //配置PB12管脚为外部中断线路用

  

EXTI_InitStructure.EXTI_Line = EXTI_Line12;      //配置为外部中断线2

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;        //配置为中断请求

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;      //输入线路下降沿为中断请求

EXTI_InitStructure.EXTI_LineCmd = ENABLE;   //使能中断

   EXTI_Init(&EXTI_InitStructure);

 

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    //NVIC_Group:先占优先级2位,从优先级2位  

NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;   //配置为外部中断2中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级为1

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;   //从优先级为2

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;      //使能中断通道

NVIC_Init(&NVIC_InitStructure);

}

void KeyB_Init(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

EXTI_InitTypeDef EXTI_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

 

/* Enable the EXTI_PB1 Clock */

RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB , ENABLE);

 

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;    //浮空输入   

GPIO_Init(GPIOB, &GPIO_InitStructure);

 

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11);   //配置PB11管脚为外部中断线路用

  

EXTI_InitStructure.EXTI_Line = EXTI_Line11;      //配置为外部中断线11

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;        //配置为中断请求

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;      //输入线路下降沿为中断请求

EXTI_InitStructure.EXTI_LineCmd = ENABLE;   //使能中断

   EXTI_Init(&EXTI_InitStructure);

 

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    //NVIC_Group:先占优先级2位,从优先级2位  

NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;   //配置为外部中断2中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级为1

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;   //从优先级为2

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;      //使能中断通道

NVIC_Init(&NVIC_InitStructure);

}

 

 

3、根据表1设计的中断处理函数如下所示,使用TurningDir变量来标识检测到的旋转方向,TurnLeft表示逆时针(对应数值可以理解为减),TurnRight表示顺时针(对应数值可以理解为加):

typedef enum {TurnNone=0,TurnRight , TurnLeft}TurnDir ;

TurnDir TurningDir=TurnNone;//default turning dir = none

void EXTI15_10_IRQHandler(void)

{

u8 KeyAValue=0;

u8 KeyBValue=0;

 

//key left & right  

if (EXTI_GetITStatus(EXTI_Line12) != RESET)

{

EXTI_ClearITPendingBit(EXTI_Line12);   

  KeyAValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12);

KeyBValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);

// printf("AB=%d,%d\r\n",KeyAValue,KeyBValue);

 

 

if(KeyAValue==1)

{

if(KeyBValue==0)

{

TurningDir=TurnLeft;

}

else

{

TurningDir=TurnRight;

}

}

else if(KeyAValue==0)

{

if(KeyBValue==1)

{

TurningDir=TurnLeft;

}

else

{

TurningDir=TurnRight;

}

}

}

 

if (EXTI_GetITStatus(EXTI_Line11) != RESET)

{

EXTI_ClearITPendingBit(EXTI_Line11);

 

  KeyAValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12);

KeyBValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);

if(KeyBValue==1)

{

if(KeyAValue==0)

{

TurningDir=TurnRight;

}

else

{

TurningDir=TurnLeft;

}

}

else if(KeyBValue==0)

{

if(KeyAValue==1)

{

TurningDir=TurnRight;

}

else

{

TurningDir=TurnLeft;

}

}

  }

}

 

编码器旋钮的按下功能,即C,可以把它当成一个普通的按键,软件设置成上拉输入,按下时为低电平,弹起时为高,同样也使用中断操作,这里就不再描述。

 

至此,编码器软件设计完成。经过实测,能完美检测左旋右旋。

如果你有什么问题,欢迎留言。

我们还建了一个QQ群,可以讨论相关内容,群号是:597254771

 

好的,对于这个型号的旋转编码开关,可以按照以下步骤进行STM32的编程: 1. 配置STM32的引脚,将旋转编码开关的输出引脚连接到STM32的输入引脚; 2. 初始化GPIO引脚,将输入引脚配置为输入模式,并开启上拉电阻; 3. 在主循环中,读取旋转编码开关的输出状态,包括旋转角度和按键状态; 4. 根据旋转角度的变化,判断旋转方向和旋转的角度值,并执行相应的操作; 5. 根据按键状态的变化,判断按键是否按下,并执行相应的操作。 下面是一个简单的STM32程序的示例代码,你可以根据自己的需求进行修改和完善: ``` #include <stm32f10x.h> // 定义旋转编码开关引脚 #define CLK_PIN GPIO_Pin_0 #define DT_PIN GPIO_Pin_1 #define SW_PIN GPIO_Pin_2 // 定义旋转角度和按键状态变量 int angle = 0; int lastAngle = 0; int buttonState = 0; int lastButtonState = 0; int main(void) { // 初始化时钟 SystemInit(); // 初始化GPIO引脚 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = CLK_PIN | DT_PIN | SW_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化USART串口 USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); while (1) { // 读取旋转编码开关状态 int clkState = GPIO_ReadInputDataBit(GPIOA, CLK_PIN); int dtState = GPIO_ReadInputDataBit(GPIOA, DT_PIN); int swState = GPIO_ReadInputDataBit(GPIOA, SW_PIN); // 计算旋转角度 if (clkState != lastAngle) { if (dtState != clkState) { angle++; } else { angle--; } USART_SendString("Angle: "); USART_SendInt(angle); USART_SendString("\r\n"); lastAngle = clkState; } // 检测按键状态 if (swState != lastButtonState) { if (swState == RESET) { USART_SendString("Button pressed!\r\n"); // 执行按键按下后的操作 } lastButtonState = swState; } // 执行旋转角度变化后的操作 if (angle != lastAngle) { // 根据旋转角度的变化,执行相应的操作 if (angle > lastAngle) { USART_SendString("Clockwise rotation!\r\n"); // 执行顺时针旋转后的操作 } else { USART_SendString("Counterclockwise rotation!\r\n"); // 执行逆时针旋转后的操作 } lastAngle = angle; } } } ``` 这是一个简单的示例程序,你可以根据自己的需求进行修改和完善。重要的是要理解程序的流程和实现方法,这样才能更好地为你提供帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

masterbee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值