[其他ST产品] stm32编码器电机测速(hal库)

[复制链接]
 楼主| lxs0026 发表于 2023-7-29 16:50 | 显示全部楼层 |阅读模式
记录一下今天参考别人的代码实现了四个电机的测速。



编码器被广泛应用于电机测速,实现电机闭环控制。所以不论是自己做小车还是后续参加各种比赛,必须要学会编码器测速。

一.参数     
        编码电机其实就是一个带有编码器的电机,我的这个电机是一个带霍尔传感器的电机,型号是JGB37-520,然后我的电机减速比是30(一定要记住,买的时候也要看清电机减速比是多少,涉及到转速的计算),额定电压12V,然后就是编码器的参数了,见下图

1969364c4d2d97c22e.png

电机驱动模块我用的TB6612的四路的板子,就是下面这款,很好用,就是稍微有点贵。
3789164c4d2e80473a.png

 楼主| lxs0026 发表于 2023-7-29 16:51 | 显示全部楼层
二.常用测速方法
主要分为M法、T法和M/T法
 楼主| lxs0026 发表于 2023-7-29 16:51 | 显示全部楼层
三.CubeMX配置
首先是配置PWM输出定时器,我这里使用的是TIM8

4640364c4d3070fccf.png
 楼主| lxs0026 发表于 2023-7-29 16:51 | 显示全部楼层
然后再配置编码器输入定时器TIM2,TIM3\TIM4\TIM5按照相同的参数配置

5757164c4d31b8586a.png
 楼主| lxs0026 发表于 2023-7-29 16:51 | 显示全部楼层
这里开启了两个通道计数,就是倍频技术的4倍频

编码器模式下的定时器其实是个计数器,在编码器的脉冲到来时,Counter会相应地加和减,正转时加,反转时减,溢出后到达另一个极端值,比如说向上计数到达20001时会变成0
 楼主| lxs0026 发表于 2023-7-29 16:52 | 显示全部楼层
再设置每隔10ms读取定时器的值的定时器TIM6
5920264c4d33abe2f6.png
 楼主| lxs0026 发表于 2023-7-29 16:52 | 显示全部楼层
最后注意中断优先级TIM6要小于编码器计数的定时器。
8866664c4d34751df8.png
 楼主| lxs0026 发表于 2023-7-29 16:52 | 显示全部楼层
四.代码
encoder.c
  1. #include "encoder.h"

  2. Motor motor1;
  3. Motor motor2;
  4. Motor motor3;
  5. Motor motor4;
  6. int t1,t2,t3,t4,j1,j2,j3,j4;

  7. void Motor_Init(void)
  8. {
  9.     HAL_TIM_Encoder_Start(&ENCODER_TIM1, TIM_CHANNEL_ALL);      //开启编码器定时器
  10.                 HAL_TIM_Encoder_Start(&ENCODER_TIM2, TIM_CHANNEL_ALL);
  11.                 HAL_TIM_Encoder_Start(&ENCODER_TIM3, TIM_CHANNEL_ALL);
  12.                 HAL_TIM_Encoder_Start(&ENCODER_TIM4, TIM_CHANNEL_ALL);
  13.                
  14.     __HAL_TIM_ENABLE_IT(&ENCODER_TIM1,TIM_IT_UPDATE);           //开启编码器定时器更新中断,防溢出处理
  15.                 __HAL_TIM_ENABLE_IT(&ENCODER_TIM2,TIM_IT_UPDATE);
  16.                 __HAL_TIM_ENABLE_IT(&ENCODER_TIM3,TIM_IT_UPDATE);
  17.                 __HAL_TIM_ENABLE_IT(&ENCODER_TIM4,TIM_IT_UPDATE);
  18.        
  19.     HAL_TIM_Base_Start_IT(&GAP_TIM);                       //开启10ms定时器中断
  20.     __HAL_TIM_SET_COUNTER(&ENCODER_TIM1, 10000);                //编码器定时器初始值设定为10000
  21.                 __HAL_TIM_SET_COUNTER(&ENCODER_TIM2, 10000);
  22.                 __HAL_TIM_SET_COUNTER(&ENCODER_TIM3, 10000);
  23.                 __HAL_TIM_SET_COUNTER(&ENCODER_TIM4, 10000);
  24.        
  25.        
  26.     motor1.lastCount = 0;                                   //结构体内容初始化
  27.     motor1.totalCount = 0;
  28.     motor1.overflowNum = 0;                                 
  29.     motor1.speed = 0;
  30.     motor1.direct = 0;
  31.        
  32.                 motor2.lastCount = 0;                                   //结构体内容初始化
  33.     motor2.totalCount = 0;
  34.     motor2.overflowNum = 0;                                 
  35.     motor2.speed = 0;
  36.     motor2.direct = 0;
  37.                
  38.                 motor3.lastCount = 0;                                   //结构体内容初始化
  39.     motor3.totalCount = 0;
  40.     motor3.overflowNum = 0;                                 
  41.     motor3.speed = 0;
  42.     motor3.direct = 0;
  43.                
  44.                 motor4.lastCount = 0;                                   //结构体内容初始化
  45.     motor4.totalCount = 0;
  46.     motor4.overflowNum = 0;                                 
  47.     motor4.speed = 0;
  48.     motor4.direct = 0;
  49. }
  50. //M法测速度
  51. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//定时器回调函数,用于计算速度
  52. {
  53.     if(htim->Instance==ENCODER_TIM1.Instance)//编码器输入定时器溢出中断                    
  54.     {      
  55.         if(COUNTERNUM1 < 10000) motor1.overflowNum++;       //如果是向上溢出
  56.         else if(COUNTERNUM1 >= 10000) motor1.overflowNum--; //如果是向下溢出
  57.         __HAL_TIM_SetCounter(&ENCODER_TIM1, 10000);             //重新设定初始值
  58.                        
  59.                         if(COUNTERNUM2 < 10000) motor2.overflowNum++;       //如果是向上溢出
  60.         else if(COUNTERNUM2 >= 10000) motor2.overflowNum--; //如果是向下溢出
  61.         __HAL_TIM_SetCounter(&ENCODER_TIM2, 10000);             //重新设定初始值
  62.                        
  63.                         if(COUNTERNUM3 < 10000) motor3.overflowNum++;       //如果是向上溢出
  64.         else if(COUNTERNUM3 >= 10000) motor3.overflowNum--; //如果是向下溢出
  65.         __HAL_TIM_SetCounter(&ENCODER_TIM3, 10000);             //重新设定初始值
  66.                        
  67.                          if(COUNTERNUM4 < 10000) motor4.overflowNum++;       //如果是向上溢出
  68.         else if(COUNTERNUM4 >= 10000) motor4.overflowNum--; //如果是向下溢出
  69.         __HAL_TIM_SetCounter(&ENCODER_TIM4, 10000);             //重新设定初始值
  70.     }
  71.     else if(htim->Instance==GAP_TIM.Instance)//间隔定时器中断,是时候计算速度了
  72.     {
  73.         motor1.direct = __HAL_TIM_IS_TIM_COUNTING_DOWN(&ENCODER_TIM1);//如果向上计数(正转),返回值为0,否则返回值为1
  74.         motor1.totalCount = COUNTERNUM1 + motor1.overflowNum * RELOADVALUE1;//一个周期内的总计数值等于目前计数值加上溢出的计数值
  75.         motor1.speed = (float)(motor1.totalCount - motor1.lastCount) / (4 * MOTOR_SPEED_RERATIO * PULSE_PRE_ROUND) * 10;//算得每秒多少转
  76.                         if(motor1.direct==0)
  77.                         {
  78.                                 t1=motor1.speed/1;
  79.                                 j1=(motor1.speed-t1)*10000;
  80.                         }
  81.                         else
  82.                         {
  83.                                 t1=-motor1.speed/1;
  84.                                 j1=-(motor1.speed+t1)*10000;
  85.                         }
  86.                                
  87.                                  //motor1.speed = (float)(motor1.totalCount - motor1.lastCount) / (4 * MOTOR_SPEED_RERATIO * PULSE_PRE_ROUND) * 10 * LINE_SPEED_C//算得车轮线速度每秒多少毫米
  88.         motor1.lastCount = motor1.totalCount; //记录这一次的计数值       
  89.                                
  90.                                 motor2.direct = __HAL_TIM_IS_TIM_COUNTING_DOWN(&ENCODER_TIM2);//如果向上计数(正转),返回值为0,否则返回值为1
  91.         motor2.totalCount = COUNTERNUM2 + motor1.overflowNum * RELOADVALUE2;//一个周期内的总计数值等于目前计数值加上溢出的计数值
  92.         motor2.speed = (float)(motor2.totalCount - motor2.lastCount) / (4 * MOTOR_SPEED_RERATIO * PULSE_PRE_ROUND) * 10;//算得每秒多少转
  93.                         if(motor2.direct==0)
  94.                         {
  95.                                 t2=motor2.speed/1;
  96.                                 j2=(motor2.speed-t2)*10000;
  97.                         }
  98.                         else
  99.                         {
  100.                                 t2=-motor2.speed/1;
  101.                                 j2=-(motor2.speed+t2)*10000;
  102.                         }
  103.         //motor1.speed = (float)(motor1.totalCount - motor1.lastCount) / (4 * MOTOR_SPEED_RERATIO * PULSE_PRE_ROUND) * 10 * LINE_SPEED_C//算得车轮线速度每秒多少毫米
  104.         motor2.lastCount = motor2.totalCount; //记录这一次的计数值
  105.                                
  106.                                 motor3.direct = __HAL_TIM_IS_TIM_COUNTING_DOWN(&ENCODER_TIM3);//如果向上计数(正转),返回值为0,否则返回值为1
  107.         motor3.totalCount = COUNTERNUM3 + motor3.overflowNum * RELOADVALUE3;//一个周期内的总计数值等于目前计数值加上溢出的计数值
  108.         motor3.speed = (float)(motor3.totalCount - motor3.lastCount) / (4 * MOTOR_SPEED_RERATIO * PULSE_PRE_ROUND) * 10;//算得每秒多少转
  109.                         if(motor3.direct==0)
  110.                         {
  111.                                 t3=motor3.speed/1;
  112.                                 j3=(motor3.speed-t3)*10000;
  113.                         }
  114.                         else
  115.                         {
  116.                                 t3=-motor3.speed/1;
  117.                                 j3=-(motor3.speed+t3)*10000;
  118.                         }
  119.         //motor1.speed = (float)(motor1.totalCount - motor1.lastCount) / (4 * MOTOR_SPEED_RERATIO * PULSE_PRE_ROUND) * 10 * LINE_SPEED_C//算得车轮线速度每秒多少毫米
  120.         motor3.lastCount = motor3.totalCount; //记录这一次的计数值
  121.                                
  122.                                 motor4.direct = __HAL_TIM_IS_TIM_COUNTING_DOWN(&ENCODER_TIM4);//如果向上计数(正转),返回值为0,否则返回值为1
  123.         motor4.totalCount = COUNTERNUM4 + motor4.overflowNum * RELOADVALUE4;//一个周期内的总计数值等于目前计数值加上溢出的计数值
  124.         motor4.speed = (float)(motor4.totalCount - motor4.lastCount) / (4 * MOTOR_SPEED_RERATIO * PULSE_PRE_ROUND) * 10;//算得每秒多少转
  125.                         if(motor4.direct==0)
  126.                         {
  127.                                 t4=motor4.speed/1;
  128.                                 j4=(motor4.speed-t4)*10000;
  129.                         }
  130.                         else
  131.                         {
  132.                                 t4=-motor4.speed/1;
  133.                                 j4=-(motor4.speed+t4)*10000;
  134.                         }
  135.         //motor1.speed = (float)(motor1.totalCount - motor1.lastCount) / (4 * MOTOR_SPEED_RERATIO * PULSE_PRE_ROUND) * 10 * LINE_SPEED_C//算得车轮线速度每秒多少毫米
  136.         motor4.lastCount = motor4.totalCount; //记录这一次的计数值
  137.                         }      
  138. }                       
 楼主| lxs0026 发表于 2023-7-29 16:52 | 显示全部楼层
encoder.h
  1. #ifndef _ENCODER_H_
  2. #define _ENCODER_H_

  3. #include "stm32f1xx.h"
  4. #include "tim.h"
  5.   

  6. //定时器号
  7. #define ENCODER_TIM1 htim2
  8. #define ENCODER_TIM2 htim3
  9. #define ENCODER_TIM3 htim4
  10. #define ENCODER_TIM4 htim5

  11. #define GAP_TIM     htim6
  12.   
  13. #define MOTOR_SPEED_RERATIO 30u    //电机减速比
  14. #define PULSE_PRE_ROUND 11 //一圈多少个脉冲
  15. #define RADIUS_OF_TYRE 40 //轮胎半径,单位毫米
  16. #define LINE_SPEED_C RADIUS_OF_TYRE * 2 * 3.14

  17. #define RELOADVALUE1 __HAL_TIM_GetAutoreload(&ENCODER_TIM1)    //获取自动装载值,本例中为20000
  18. #define COUNTERNUM1 __HAL_TIM_GetCounter(&ENCODER_TIM1)        //获取编码器定时器中的计数值

  19. #define RELOADVALUE2 __HAL_TIM_GetAutoreload(&ENCODER_TIM2)
  20. #define COUNTERNUM2 __HAL_TIM_GetCounter(&ENCODER_TIM2)

  21. #define RELOADVALUE3 __HAL_TIM_GetAutoreload(&ENCODER_TIM3)
  22. #define COUNTERNUM3 __HAL_TIM_GetCounter(&ENCODER_TIM3)

  23. #define RELOADVALUE4 __HAL_TIM_GetAutoreload(&ENCODER_TIM4)
  24. #define COUNTERNUM4 __HAL_TIM_GetCounter(&ENCODER_TIM4)

  25. typedef struct _Motor
  26. {
  27.     int32_t lastCount;   //上一次计数值
  28.     int32_t totalCount;  //总计数值
  29.     int16_t overflowNum; //溢出次数
  30.     float speed;         //电机转速
  31.     uint8_t direct;      //旋转方向
  32. }Motor;
  33. extern int t1,t2,t3,t4,j1,j2,j3,j4;
  34. void Motor_Init(void);
  35. void HAL_TIM_PeriodElapsedCallback1(TIM_HandleTypeDef *htim);
  36.   
  37. #endif
 楼主| lxs0026 发表于 2023-7-29 16:53 | 显示全部楼层
main.c
  1. /* USER CODE BEGIN Header */
  2. /**
  3.   ******************************************************************************
  4.   * [url=home.php?mod=space&uid=288409]@file[/url]           : main.c
  5.   * [url=home.php?mod=space&uid=247401]@brief[/url]          : Main program body
  6.   ******************************************************************************
  7.   * @attention
  8.   *
  9.   * Copyright (c) 2023 STMicroelectronics.
  10.   * All rights reserved.
  11.   *
  12.   * This software is licensed under terms that can be found in the LICENSE file
  13.   * in the root directory of this software component.
  14.   * If no LICENSE file comes with this software, it is provided AS-IS.
  15.   *
  16.   ******************************************************************************
  17.   */
  18. /* USER CODE END Header */
  19. /* Includes ------------------------------------------------------------------*/
  20. #include "main.h"
  21. #include "i2c.h"
  22. #include "tim.h"
  23. #include "usart.h"
  24. #include "gpio.h"

  25. /* Private includes ----------------------------------------------------------*/
  26. /* USER CODE BEGIN Includes */
  27. #include "bluetooth.h"
  28. #include "Control.h"
  29. #include "oled.h"
  30. #include "encoder.h"
  31. /* USER CODE END Includes */

  32. /* Private typedef -----------------------------------------------------------*/
  33. /* USER CODE BEGIN PTD */

  34. /* USER CODE END PTD */

  35. /* Private define ------------------------------------------------------------*/
  36. /* USER CODE BEGIN PD */

  37. /* USER CODE END PD */

  38. /* Private macro -------------------------------------------------------------*/
  39. /* USER CODE BEGIN PM */

  40. /* USER CODE END PM */

  41. /* Private variables ---------------------------------------------------------*/

  42. /* USER CODE BEGIN PV */

  43. /* USER CODE END PV */

  44. /* Private function prototypes -----------------------------------------------*/
  45. void SystemClock_Config(void);
  46. /* USER CODE BEGIN PFP */

  47. /* USER CODE END PFP */

  48. /* Private user code ---------------------------------------------------------*/
  49. /* USER CODE BEGIN 0 */

  50. /* USER CODE END 0 */

  51. /**
  52.   * @brief  The application entry point.
  53.   * @retval int
  54.   */
  55. int main(void)
  56. {
  57.   /* USER CODE BEGIN 1 */

  58.   /* USER CODE END 1 */

  59.   /* MCU Configuration--------------------------------------------------------*/

  60.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  61.   HAL_Init();

  62.   /* USER CODE BEGIN Init */
  63.        
  64.   /* USER CODE END Init */

  65.   /* Configure the system clock */
  66.   SystemClock_Config();

  67.   /* USER CODE BEGIN SysInit */
  68.        
  69.   /* USER CODE END SysInit */

  70.   /* Initialize all configured peripherals */
  71.   MX_GPIO_Init();
  72.   MX_TIM8_Init();
  73.   MX_USART1_UART_Init();
  74.   MX_I2C2_Init();
  75.   MX_TIM2_Init();
  76.   MX_TIM6_Init();
  77.   MX_TIM3_Init();
  78.   MX_TIM4_Init();
  79.   MX_TIM5_Init();
  80.   /* USER CODE BEGIN 2 */
  81.         HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1);
  82.   /* USER CODE END 2 */
  83.         OLED_Init();
  84.         OLED_CLS();
  85.         OLED_ShowChar(14,1,'.',15);
  86.         OLED_ShowChar(14,2,'.',15);
  87.         OLED_ShowChar(14,3,'.',15);
  88.         OLED_ShowChar(14,4,'.',15);
  89.         OLED_ShowStr(50,5,"By Whelve",2);
  90.         OLED_ShowStr(60,1,"r/s",1);
  91.         OLED_ShowStr(60,2,"r/s",1);
  92.         OLED_ShowStr(60,3,"r/s",1);
  93.         OLED_ShowStr(60,4,"r/s",1);
  94.         Motor_Init();
  95.   /* Infinite loop */
  96.   /* USER CODE BEGIN WHILE */
  97.   while (1)
  98.   {
  99.     /* USER CODE END WHILE */
  100.                 Control();
  101.                 OLED_ShowNum(0,1,t1,2,15);
  102.                 OLED_ShowNum(18,1,j1,5,15);
  103.                
  104.                 OLED_ShowNum(0,2,t2,2,15);
  105.                 OLED_ShowNum(18,2,j2,5,15);
  106.                
  107.                 OLED_ShowNum(0,3,t3,2,15);
  108.                 OLED_ShowNum(18,3,j3,5,15);
  109.                
  110.                 OLED_ShowNum(0,4,t4,2,15);
  111.                 OLED_ShowNum(18,4,j4,5,15);
  112.                
  113.     /* USER CODE BEGIN 3 */
  114.   }
  115.   /* USER CODE END 3 */
  116. }

  117. /**
  118.   * @brief System Clock Configuration
  119.   * @retval None
  120.   */
  121. void SystemClock_Config(void)
  122. {
  123.   RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  124.   RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  125.   /** Initializes the RCC Oscillators according to the specified parameters
  126.   * in the RCC_OscInitTypeDef structure.
  127.   */
  128.   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  129.   RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  130.   RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  131.   RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  132.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  133.   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  134.   RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  135.   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  136.   {
  137.     Error_Handler();
  138.   }

  139.   /** Initializes the CPU, AHB and APB buses clocks
  140.   */
  141.   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
  142.                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  143.   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  144.   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  145.   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  146.   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  147.   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  148.   {
  149.     Error_Handler();
  150.   }
  151. }

  152. /* USER CODE BEGIN 4 */

  153. /* USER CODE END 4 */

  154. /**
  155.   * @brief  This function is executed in case of error occurrence.
  156.   * @retval None
  157.   */
  158. void Error_Handler(void)
  159. {
  160.   /* USER CODE BEGIN Error_Handler_Debug */
  161.   /* User can add his own implementation to report the HAL error return state */
  162.   __disable_irq();
  163.   while (1)
  164.   {
  165.   }
  166.   /* USER CODE END Error_Handler_Debug */
  167. }

  168. #ifdef  USE_FULL_ASSERT
  169. /**
  170.   * @brief  Reports the name of the source file and the source line number
  171.   *         where the assert_param error has occurred.
  172.   * @param  file: pointer to the source file name
  173.   * @param  line: assert_param error line source number
  174.   * @retval None
  175.   */
  176. void assert_failed(uint8_t *file, uint32_t line)
  177. {
  178.   /* USER CODE BEGIN 6 */
  179.   /* User can add his own implementation to report the file name and line number,
  180.      ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  181.   /* USER CODE END 6 */
  182. }
  183. #endif /* USE_FULL_ASSERT */
 楼主| lxs0026 发表于 2023-7-29 16:53 | 显示全部楼层
2075164c4d38f6b85d.png 最后效果不错

您需要登录后才可以回帖 登录 | 注册

本版积分规则

93

主题

1161

帖子

1

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