打印
[其他ST产品]

STM32外部中断读取编码器计数值

[复制链接]
405|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
xiaoqi000|  楼主 | 2023-8-26 17:30 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
STM32外部中断读取编码器计数值:
stm32的定时器是可以直接读取编码器的正交信号的,这也说明定时器的强大之处,但是呢,stm32的定时器个数是有限的,当用一个定时器来读取编码器的,那这个定时器就不能再干其他任何事情了,驱动四个电机只需要一个定时器,但是读取编码器也直接用掉了一个定时器,这是对硬件资源极大的浪费
————————————————

使用特权

评论回复
评论
xiaoqi000 2023-8-26 17:30 回复TA
版权声明:本文为CSDN博主「GuiStar_李什么恩」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/hhhbdbfb/article/details/128694651 
沙发
xiaoqi000|  楼主 | 2023-8-26 17:30 | 只看该作者
定时器在一个项目中往往还有比读取编码器更重要的作用,因此,读取编码器这种简单的活就不要再让定时器做了,太浪费硬件资源了,所以,本文提供外部中断的方法来读取编码器的正交信号,毕竟stm32的每一个引脚都可以配置外部中断的。利用提供的三个函数
GuiStar_Encoder_Init,
GuiStar_Encoder_Read_Current_Value,
GuiStar_Encoder_GetSpeed。

使用特权

评论回复
板凳
xiaoqi000|  楼主 | 2023-8-26 17:30 | 只看该作者
代码中用到了IO.h和IO_Init,它们是初始化IO口为浮空输入模式的封装,源码如下:

提示:下面的两个文件的作用是stm32位带操作和stm32引脚初始化函数的封装,如果要深究位带操作,建议看野火哥的视频,本次的编码器读取只用到了IO_Init这个函数,没有涉及位带操作
其实位带操作也仅仅是原理很难理解而已,但是用起来是真的香啊,它把stm32单片机玩儿出了51单片机的感觉。

使用特权

评论回复
地板
xiaoqi000|  楼主 | 2023-8-26 17:31 | 只看该作者
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

使用特权

评论回复
5
xiaoqi000|  楼主 | 2023-8-26 17:31 | 只看该作者
IO.c:
#include "stm32f10x.h"

/**
  * [url=home.php?mod=space&uid=247401]@brief[/url]  完成指定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);
        }
}


使用特权

评论回复
6
xiaoqi000|  楼主 | 2023-8-26 17:31 | 只看该作者
接下来的两个文件中涉及外部中断的配置,

使用特权

评论回复
7
xiaoqi000|  楼主 | 2023-8-26 17:32 | 只看该作者
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

使用特权

评论回复
8
xiaoqi000|  楼主 | 2023-8-26 17:32 | 只看该作者
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反向,正向与反向是相对的)
  * [url=home.php?mod=space&uid=93590]@Attention[/url]  两个接口不能同时选择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;//返回锁存值
}


使用特权

评论回复
9
liuzaiy| | 2023-8-26 23:43 | 只看该作者
没有涉及位带操作

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

55

主题

755

帖子

0

粉丝