stm32使用外部中断写旋转编码器(包教包会)
链接:https://pan.baidu.com/s/1yBKbngf5VsZEIwxoJ4WNNA?pwd=ulmi
提取码:ulmi
一、编码器原理介绍
旋转编码器也称为轴编码器,增量式旋转编码器拥有A、B、C三个输出通道,其中A、B为两个正交信号,相位差为90°,C相输出0脉冲信号,用于标识位置。当编码器正转时,A相的输出信号超前B相90°;当编码器反转时,A相滞后90°,在程序中可以根据A、B两相信号输出的先后顺序来判断编码器是正转还是反转.
以上呢,是旋转编码器正反转的A、B相波形图,程序中采用的是下降沿触发,由波形图,当A相为下降沿,B相为低电平时,反转;当B相为下降沿,A为低电平时,正转。根据该原理逻辑可实现编码器计数。
二、引脚连接
旋转编码器 |
stm32f103c8t6 |
VCC | 3.3V |
GND | GND |
A | PB0 |
B | PB1 |
C | GND |
三、代码解读
代码解读总思路就是配置中断服务函数,这里采用的是外部中断。
首先定义IO口
然后将PB0和PB1的总线映射到PB口上
然后再配置中断模式(这里选择的是下降沿触发)
然后配置抢占优先级和响应优先级,开启中断(这里PB0和PB1和抢占优先级一样,PB1的响应优先级大于PB0的响应优先级,就是再PB0和PB1的下降沿同时到来时,优先触发PB1的中断)
最后再配置中断服务函数,就可以使用了(在PB1的下降沿到来时,触发PB1的中断,如果此时PB0为低电平,计数++,后面同理)
#include "stm32f10x.h" // Device header
int16_t EncoderCount;//全局变量
void Encoder_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE );//APB2_GPIOB的外设,开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE );//选择AFIO这个外设,开启时钟,通常用于启用或禁用 AFIO 外设的时钟,以便进行相应的配置操作。
//配置GPIO 定义结构体
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//上拉输入,默认为高电平的输入方式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;//旋转编码器分两相,PB0 为A相,PB1为B相
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init (GPIOB ,&GPIO_InitStructure);//初始化GPIOB外设
//配置AFIO 通过 AFIO 外设将 PB0 和 PB1 引脚映射到 EXTI 外设上,以便用于外部中断。
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
//配置EXTI 将第 0 条和第 1 条线路都初始化为中断模式,并指定下降沿触发中断
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_Rising_Falling;//上升沿和下降沿都触发,遮挡和离开都+1
//EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;//上升沿触发,遮挡就+1
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿触发,离开就+1
EXTI_Init(&EXTI_InitStructure);
//配置NVIC中断 置了抢占优先级和响应优先级,并使能了 EXTI0 和 EXTI1 的中断。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//选两位抢占两位响应
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;//外部中断0
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//指定中断是使能还是失能(这里选使能)
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//响应优先级
NVIC_Init (&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn;//外部中断1
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//指定中断是使能还是失能(这里选使能)
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;//响应优先级2大于1
NVIC_Init (&NVIC_InitStructure);//调用NVIC_Init函数
//PB0和PB1为同一抢占优先级,但PB1的响应优先级比PB0大,所以PB0可以被PB1打断
}
int8_t Encoder_Get(void)//获取中断服务函数中改变的数
{
int8_t num;
num=EncoderCount;
return num;
}
void EXTI0_IRQHandler(void)//这个函数只有0这个引脚可以触发
{
if(EXTI_GetFlagStatus (EXTI_Line0 )==SET)//外部中断0的线被触发,进入中断
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)//A下降沿,B低电平,反转
{
EncoderCount --;//计数--
}
}
EXTI_ClearITPendingBit(EXTI_Line0);//清除中断标志位,跳出中断
}
}
void EXTI1_IRQHandler(void)//这个函数只有1这个引脚可以触发
{
if(EXTI_GetFlagStatus (EXTI_Line1)==SET)//外部中断1的线被触发,进入中断
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0)//B下降沿,A低电平正转,
{
EncoderCount ++;//计数++
}
}
EXTI_ClearITPendingBit(EXTI_Line1);//清除中断标志位
}
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "encoder.h"
int main(void)
{
int8_t num;
OLED_Init();
Encoder_Init();
OLED_ShowString(1,1,"Num:");//第一行第一列显示字符串
while(1)
{
num=Encoder_Get();
OLED_ShowSignedNum(1,5,num,5);//第一行第5列显示5位数
}
}
通过触发中断,改变num的值,可以直观感受外部中断和旋转编码器的原理及用途。完整的工程源码已经放在百度网盘中了,想要的可自取,有哪里不懂的或者解释有错误的欢迎在评论区留言。
作者:不就if else嘛