STM32外部中断读取编码器计数值:
stm32的定时器是可以直接读取编码器的正交信号的,这也说明定时器的强大之处,但是呢,stm32的定时器个数是有限的,当用一个定时器来读取编码器的,那这个定时器就不能再干其他任何事情了,驱动四个电机只需要一个定时器,但是读取编码器也直接用掉了一个定时器,这是对硬件资源极大的浪费
定时器在一个项目中往往还有比读取编码器更重要的作用,因此,读取编码器这种简单的活就不要再让定时器做了,太浪费硬件资源了,所以,本文提供外部中断的方法来读取编码器的正交信号,毕竟stm32的每一个引脚都可以配置外部中断的。利用提供的三个函数
GuiStar_Encoder_Init,
GuiStar_Encoder_Read_Current_Value,
GuiStar_Encoder_GetSpeed。
代码中用到了IO.h和IO_Init,它们是初始化IO口为浮空输入模式的封装,源码如下:
提示:下面的两个文件的作用是stm32位带操作和stm32引脚初始化函数的封装,如果要深究位带操作,建议看野火哥的视频,本次的编码器读取只用到了IO_Init这个函数,没有涉及位带操作
其实位带操作也仅仅是原理很难理解而已,但是用起来是真的香啊,它把stm32单片机玩儿出了51单片机的感觉。哈哈
IO.h
#ifndef __IO_H
#define __IO_H
#include "stm32f10x.h"
#define SYSTEM_SUPPORT_OS
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
#define GPIOA_ODR_Addr (GPIOA_BASE+12)
#define GPIOB_ODR_Addr (GPIOB_BASE+12)
#define GPIOC_ODR_Addr (GPIOC_BASE+12)
#define GPIOD_ODR_Addr (GPIOD_BASE+12)
#define GPIOE_ODR_Addr (GPIOE_BASE+12)
#define GPIOF_ODR_Addr (GPIOF_BASE+12)
#define GPIOG_ODR_Addr (GPIOG_BASE+12)
#define GPIOA_IDR_Addr (GPIOA_BASE+8)
#define GPIOB_IDR_Addr (GPIOB_BASE+8)
#define GPIOC_IDR_Addr (GPIOC_BASE+8)
#define GPIOD_IDR_Addr (GPIOD_BASE+8)
#define GPIOE_IDR_Addr (GPIOE_BASE+8)
#define GPIOF_IDR_Addr (GPIOF_BASE+8)
#define GPIOG_IDR_Addr (GPIOG_BASE+8)
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n)
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n)
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n)
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n)
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n)
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n)
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n)
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n)
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n)
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n)
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n)
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n)
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n)
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n)
void IO_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin_x, GPIOMode_TypeDef GPIO_Mode);
#endif
IO.c:
#include "stm32f10x.h"
/**
* @brief 完成指定IO口的时钟开启,模式选择
* @param GPIOx 可选GPIOA,GPIOB,GPIOC,GPIOD
* @param GPIO_Pin_x 选择Pin
* @param GPIO_Mode 选择GPIO模式,有以下八种模式可供选择:
GPIO_Mode_AIN 模拟输入
GPIO_Mode_IN_FLOATING 浮空输入
GPIO_Mode_IPD 下拉输入
GPIO_Mode_IPU 上拉输入
GPIO_Mode_Out_OD 开漏输出(带上拉或者下拉)
GPIO_Mode_Out_PP 推挽输出(带上拉或者下拉
GPIO_Mode_AF_OD 复用开漏输出(带上拉或者下拉)
GPIO_Mode_AF_PP 复用推挽输出(带上拉或者下拉)
* @param
* @retval 无
*/
void IO_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin_x, GPIOMode_TypeDef GPIO_Mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_x;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
if(GPIOx==GPIOA)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
if(GPIOx==GPIOB)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
if(GPIOx==GPIOC)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
if(GPIOx==GPIOD)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
}
接下来的两个文件中涉及外部中断的配置,我之前封装过相关函数了,请看这里外部中断函数封装
GuiStar_Encoder.h:
#ifndef __GUISTAR_ENCODER_H__
#define __GUISTAR_ENCODER_H__
#include "stm32f10x.h" // Device header
#include "IO.h"
#include "GuiStar_EXTI.h"
void GuiStar_Encoder_Init(GPIO_TypeDef* INT1_Port, uint16_t INT1_Pin,GPIO_TypeDef* INT2_Port, uint16_t INT2_Pin, uint8_t Reversal);
int GuiStar_Encoder_Read_Current_Value(void);
int GuiStar_Encoder_GetSpeed(void);
#endif
GuiStar_Encoder.c:
#include "GuiStar_Encoder.h"
GPIO_TypeDef* INT2_port;
uint16_t INT2_pin;
uint8_t reversal;
int GuiStar_Encoder_Current_Value=0;
void Encoder_IQHeader(void)
{
if(GPIO_ReadInputDataBit(INT2_port,INT2_pin)==SET)
{
if(reversal==0)
GuiStar_Encoder_Current_Value++;
else GuiStar_Encoder_Current_Value--;
}
else if(GPIO_ReadInputDataBit(INT2_port,INT2_pin)==RESET)
{
if(reversal==0)
GuiStar_Encoder_Current_Value--;
else GuiStar_Encoder_Current_Value++;
}
}
/**
* @brief 外部中断读取正交编码器初始化函数
* @param INT1_Port 选择接口一的端口(GPIOA,GPIOB,GPIOC,GPIOD)
* @param INT1_Pin 选择接口一的引脚 GPIO_Pin_x(x从0到15)
* @param INT2_Port 选择接口二的端口(GPIOA,GPIOB,GPIOC,GPIOD)
* @param INT2_Pin 选择接口二的引脚 GPIO_Pin_x(x从0到15)
* @param Reversal 是否反向(给0正向,给1反向,正向与反向是相对的)
* @attention 两个接口不能同时选择9到10引脚,也不能同时选择10到15引脚!!!
* @retval
*/
void GuiStar_Encoder_Init(GPIO_TypeDef* INT1_Port, uint16_t INT1_Pin,GPIO_TypeDef* INT2_Port, uint16_t INT2_Pin, uint8_t Reversal)
{
u8 INT1_Num=0;
u8 INT2_Num=0;
INT2_port=INT2_Port;
INT2_pin=INT2_Pin;
reversal=Reversal;
for(;((1<<INT1_Num) & (INT1_Pin))==0;)
{
INT1_Num++;
};
for(;((1<<INT2_Num) & (INT2_Pin))==0;)
{
INT2_Num++;
};
IO_Init(INT1_Port,INT1_Pin,GPIO_Mode_IN_FLOATING);//初始化接口1引脚为浮空输入
IO_Init(INT2_Port,INT2_Pin,GPIO_Mode_IN_FLOATING);//初始化接口2引脚为浮空输入
GuiStar_EXTI_Init(INT1_Port,INT1_Pin,EXTI_Trigger_Rising);//配置接口1外部中断
if(INT1_Num==0)
{
GuiStar_EXTI_SetEXTI0_IRQHandler(Encoder_IQHeader);
}
else if(INT1_Num==1)
{
GuiStar_EXTI_SetEXTI1_IRQHandler(Encoder_IQHeader);
}
else if(INT1_Num==2)
{
GuiStar_EXTI_SetEXTI2_IRQHandler(Encoder_IQHeader);
}
else if(INT1_Num==3)
{
GuiStar_EXTI_SetEXTI3_IRQHandler(Encoder_IQHeader);
}
else if(INT1_Num==4)
{
GuiStar_EXTI_SetEXTI4_IRQHandler(Encoder_IQHeader);
}
else if(INT1_Num>=5&&INT1_Num<=9)
{
GuiStar_EXTI_SetEXTI9_5_IRQHandler(Encoder_IQHeader);
}
else if(INT1_Num>=10&&INT1_Num<=15)
{
GuiStar_EXTI_SetEXTI15_10_IRQHandler(Encoder_IQHeader);
}
}
/**
* @brief 返回正交编码器当前值
* @param 无
* @retval 正交编码器当前值
*/
int GuiStar_Encoder_Read_Current_Value(void)
{
return GuiStar_Encoder_Current_Value;
}
/**
* @brief 函数内部先将编码器当前计数值锁存,再清零计数变量(GuiStar_Encoder_Current_Value),最后返回锁存值
* @param 无
* @retval 无
*/
int GuiStar_Encoder_GetSpeed(void)
{
int temp;
temp=GuiStar_Encoder_Current_Value;//先将编码器当前计数值锁存
GuiStar_Encoder_Current_Value=0;//再清零计数变量(GuiStar_Encoder_Current_Value)
return temp;//返回锁存值
}
本文结束,感谢观看!