[其他ST产品] STM32外部中断读取编码器计数值

[复制链接]
 楼主| xiaoqi000 发表于 2023-8-26 17:30 | 显示全部楼层 |阅读模式
STM32外部中断读取编码器计数值:
stm32的定时器是可以直接读取编码器的正交信号的,这也说明定时器的强大之处,但是呢,stm32的定时器个数是有限的,当用一个定时器来读取编码器的,那这个定时器就不能再干其他任何事情了,驱动四个电机只需要一个定时器,但是读取编码器也直接用掉了一个定时器,这是对硬件资源极大的浪费
————————————————

评论

版权声明:本文为CSDN博主「GuiStar_李什么恩」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/hhhbdbfb/article/details/128694651  发表于 2023-8-26 17:30
 楼主| 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
  1. #ifndef __IO_H
  2. #define __IO_H


  3. #include "stm32f10x.h"

  4. #define SYSTEM_SUPPORT_OS
  5.                                                                                                                                             
  6.          

  7. #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
  8. #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))
  9. #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

  10. #define GPIOA_ODR_Addr    (GPIOA_BASE+12)
  11. #define GPIOB_ODR_Addr    (GPIOB_BASE+12)
  12. #define GPIOC_ODR_Addr    (GPIOC_BASE+12)
  13. #define GPIOD_ODR_Addr    (GPIOD_BASE+12)
  14. #define GPIOE_ODR_Addr    (GPIOE_BASE+12)
  15. #define GPIOF_ODR_Addr    (GPIOF_BASE+12)   
  16. #define GPIOG_ODR_Addr    (GPIOG_BASE+12)   

  17. #define GPIOA_IDR_Addr    (GPIOA_BASE+8)
  18. #define GPIOB_IDR_Addr    (GPIOB_BASE+8)
  19. #define GPIOC_IDR_Addr    (GPIOC_BASE+8)
  20. #define GPIOD_IDR_Addr    (GPIOD_BASE+8)
  21. #define GPIOE_IDR_Addr    (GPIOE_BASE+8)
  22. #define GPIOF_IDR_Addr    (GPIOF_BASE+8)
  23. #define GPIOG_IDR_Addr    (GPIOG_BASE+8)


  24. #define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  
  25. #define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  

  26. #define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  
  27. #define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  

  28. #define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  
  29. #define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  

  30. #define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  
  31. #define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  

  32. #define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  
  33. #define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  

  34. #define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  
  35. #define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  

  36. #define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  
  37. #define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  


  38. void IO_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin_x, GPIOMode_TypeDef GPIO_Mode);

  39. #endif

 楼主| xiaoqi000 发表于 2023-8-26 17:31 | 显示全部楼层
IO.c:
  1. #include "stm32f10x.h"

  2. /**
  3.   * [url=home.php?mod=space&uid=247401]@brief[/url]  完成指定IO口的时钟开启,模式选择
  4.   * @param  GPIOx                                可选GPIOA,GPIOB,GPIOC,GPIOD
  5.   * @param  GPIO_Pin_x                           选择Pin
  6.   * @param  GPIO_Mode                           选择GPIO模式,有以下八种模式可供选择:
  7.                           GPIO_Mode_AIN                 模拟输入
  8.                           GPIO_Mode_IN_FLOATING 浮空输入
  9.                           GPIO_Mode_IPD                 下拉输入
  10.                           GPIO_Mode_IPU                 上拉输入
  11.                           GPIO_Mode_Out_OD                 开漏输出(带上拉或者下拉)
  12.                           GPIO_Mode_Out_PP                 推挽输出(带上拉或者下拉
  13.                           GPIO_Mode_AF_OD                 复用开漏输出(带上拉或者下拉)
  14.                           GPIO_Mode_AF_PP                 复用推挽输出(带上拉或者下拉)
  15.   * @param
  16.   * @retval 无
  17.   */
  18. void IO_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin_x, GPIOMode_TypeDef GPIO_Mode)
  19. {
  20.         GPIO_InitTypeDef GPIO_InitStructure;
  21.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_x;
  22.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode;
  23.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  24.        
  25.         if(GPIOx==GPIOA)
  26.         {
  27.                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  28.                 GPIO_Init(GPIOA, &GPIO_InitStructure);
  29.         }
  30.        
  31.         if(GPIOx==GPIOB)
  32.         {
  33.                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  34.                 GPIO_Init(GPIOB, &GPIO_InitStructure);
  35.         }
  36.        
  37.         if(GPIOx==GPIOC)
  38.         {
  39.                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
  40.                 GPIO_Init(GPIOC, &GPIO_InitStructure);
  41.         }

  42.         if(GPIOx==GPIOD)
  43.         {
  44.                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
  45.                 GPIO_Init(GPIOD, &GPIO_InitStructure);
  46.         }
  47. }


 楼主| xiaoqi000 发表于 2023-8-26 17:31 | 显示全部楼层
接下来的两个文件中涉及外部中断的配置,
 楼主| xiaoqi000 发表于 2023-8-26 17:32 | 显示全部楼层
GuiStar_Encoder.h:
  1. #ifndef __GUISTAR_ENCODER_H__
  2. #define __GUISTAR_ENCODER_H__

  3. #include "stm32f10x.h"                  // Device header
  4. #include "IO.h"
  5. #include "GuiStar_EXTI.h"

  6. void GuiStar_Encoder_Init(GPIO_TypeDef* INT1_Port, uint16_t INT1_Pin,GPIO_TypeDef* INT2_Port, uint16_t INT2_Pin, uint8_t Reversal);
  7. int GuiStar_Encoder_Read_Current_Value(void);
  8. int GuiStar_Encoder_GetSpeed(void);

  9. #endif

 楼主| xiaoqi000 发表于 2023-8-26 17:32 | 显示全部楼层
GuiStar_Encoder.c:
  1. #include "GuiStar_Encoder.h"
  2. GPIO_TypeDef*                         INT2_port;
  3. uint16_t                                 INT2_pin;
  4. uint8_t                                 reversal;
  5. int GuiStar_Encoder_Current_Value=0;

  6. void Encoder_IQHeader(void)
  7. {
  8.         if(GPIO_ReadInputDataBit(INT2_port,INT2_pin)==SET)
  9.         {
  10.                 if(reversal==0)
  11.                         GuiStar_Encoder_Current_Value++;
  12.                 else GuiStar_Encoder_Current_Value--;
  13.         }
  14.         else if(GPIO_ReadInputDataBit(INT2_port,INT2_pin)==RESET)
  15.         {
  16.                 if(reversal==0)
  17.                         GuiStar_Encoder_Current_Value--;
  18.                 else GuiStar_Encoder_Current_Value++;
  19.         }
  20. }

  21. /**       
  22.   * @brief          外部中断读取正交编码器初始化函数
  23.   * @param          INT1_Port 选择接口一的端口(GPIOA,GPIOB,GPIOC,GPIOD)
  24.   * @param          INT1_Pin  选择接口一的引脚 GPIO_Pin_x(x从0到15)
  25.   * @param          INT2_Port 选择接口二的端口(GPIOA,GPIOB,GPIOC,GPIOD)
  26.   * @param          INT2_Pin  选择接口二的引脚 GPIO_Pin_x(x从0到15)
  27.   * @param          Reversal  是否反向(给0正向,给1反向,正向与反向是相对的)
  28.   * [url=home.php?mod=space&uid=93590]@Attention[/url]  两个接口不能同时选择9到10引脚,也不能同时选择10到15引脚!!!
  29.   * @retval
  30.   */
  31. void GuiStar_Encoder_Init(GPIO_TypeDef* INT1_Port, uint16_t INT1_Pin,GPIO_TypeDef* INT2_Port, uint16_t INT2_Pin, uint8_t Reversal)
  32. {
  33.         u8 INT1_Num=0;
  34.         u8 INT2_Num=0;
  35.         INT2_port=INT2_Port;
  36.         INT2_pin=INT2_Pin;
  37.         reversal=Reversal;
  38.         for(;((1<<INT1_Num) & (INT1_Pin))==0;)
  39.         {
  40.                 INT1_Num++;
  41.         };
  42.         for(;((1<<INT2_Num) & (INT2_Pin))==0;)
  43.         {
  44.                 INT2_Num++;
  45.         };
  46.        
  47.         IO_Init(INT1_Port,INT1_Pin,GPIO_Mode_IN_FLOATING);//初始化接口1引脚为浮空输入
  48.         IO_Init(INT2_Port,INT2_Pin,GPIO_Mode_IN_FLOATING);//初始化接口2引脚为浮空输入
  49.         GuiStar_EXTI_Init(INT1_Port,INT1_Pin,EXTI_Trigger_Rising);//配置接口1外部中断
  50.        
  51.         if(INT1_Num==0)
  52.         {
  53.                 GuiStar_EXTI_SetEXTI0_IRQHandler(Encoder_IQHeader);
  54.         }
  55.         else if(INT1_Num==1)
  56.         {
  57.                 GuiStar_EXTI_SetEXTI1_IRQHandler(Encoder_IQHeader);
  58.         }
  59.         else if(INT1_Num==2)
  60.         {
  61.                 GuiStar_EXTI_SetEXTI2_IRQHandler(Encoder_IQHeader);
  62.         }
  63.         else if(INT1_Num==3)
  64.         {
  65.                 GuiStar_EXTI_SetEXTI3_IRQHandler(Encoder_IQHeader);
  66.         }
  67.         else if(INT1_Num==4)
  68.         {
  69.                 GuiStar_EXTI_SetEXTI4_IRQHandler(Encoder_IQHeader);
  70.         }
  71.         else if(INT1_Num>=5&&INT1_Num<=9)
  72.         {
  73.                 GuiStar_EXTI_SetEXTI9_5_IRQHandler(Encoder_IQHeader);
  74.         }
  75.         else if(INT1_Num>=10&&INT1_Num<=15)
  76.         {
  77.                 GuiStar_EXTI_SetEXTI15_10_IRQHandler(Encoder_IQHeader);
  78.         }
  79. }

  80. /**
  81.   * @brief  返回正交编码器当前值
  82.   * @param  无
  83.   * @retval 正交编码器当前值
  84.   */
  85. int GuiStar_Encoder_Read_Current_Value(void)
  86. {
  87.         return GuiStar_Encoder_Current_Value;
  88. }

  89. /**
  90.   * @brief  函数内部先将编码器当前计数值锁存,再清零计数变量(GuiStar_Encoder_Current_Value),最后返回锁存值
  91.   * @param  无
  92.   * @retval 无
  93.   */
  94. int GuiStar_Encoder_GetSpeed(void)
  95. {
  96.         int temp;
  97.         temp=GuiStar_Encoder_Current_Value;//先将编码器当前计数值锁存
  98.         GuiStar_Encoder_Current_Value=0;//再清零计数变量(GuiStar_Encoder_Current_Value)
  99.         return temp;//返回锁存值
  100. }


liuzaiy 发表于 2023-8-26 23:43 | 显示全部楼层
没有涉及位带操作
您需要登录后才可以回帖 登录 | 注册

本版积分规则

65

主题

819

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部

65

主题

819

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部