STM32入门开发操作记录(七)——旋转编码器计次
目录
一、项目准备
1. 工程模板
本篇项目所用模板包含以下模块,声明函数见头文件,模块添加和函数功能详见往期记录。
2. 器件接线
主装置:ST-Link
仿真器,STM32
系统板,MB102
面包板,OLED
显示屏(接线详见往期记录)
除了A15
,B3
,B4
是JLINK
的调试端口,其他端口可随意使用。各类器件默认接线方式如下。
器件 | 端口/电源 |
---|---|
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_