• 直流有刷电机编码器测速基于STM32F302R8+X-NUCLEO-IHM07M1



    前言

    主控板STM32F302R8+驱动板X-NUCLEO-IHM07M1+直流减速电机37GB3530(带HALL编码器),实现电机的测速。

    一、编码器测速原理

    HALL编码器:利用霍尔效应,将位移转换成计数脉冲,用脉冲的个数计算位移和速度。
    在这里插入图片描述
    直流有刷电机HALL编码器有A、B两相,会输出两个相位差为90°的脉冲信号。当电机正转时,A相脉冲在前;当电机反转时,B相脉冲在前。
    常用的测速方法有:M法测速;T法测速;M/T法测速
    本次试验采用M法测速,即在一定时间内测取旋转编码器输出的脉冲个数,用以计算这段时间内的转速。

    二、STM32F302R8+X-NUCLEO-IHM07M1直流电机编码器测速

    2.1.功能需求

    直流减速电机37GB3530测速

    2.2.硬件设计

    控制板:STM32F302R8
    驱动板:X-NUCLEO-IHM07M1
    直流电机:37GB3530,额定功率10W,额定电压12V,额定电流0.3A,带HALL编码器
    在这里插入图片描述

    2.3.软件设计

    2.3.1.底层配置

    1、RCC设置为外部时钟,72MHz
    2、PC13设置为输入,无上下拉电阻;PC10,PC11设置为输出,无上下拉电阻,高速,初值为0;PB13设置为输出,下拉电阻,高速,初值为0
    3、PA8设置为TIM1_CH1,PA9设置为TIM1_CH2;TIM1时钟源设置为内部时钟,两通道均设置为PWM输出;TIM1时钟分频值设置为36-1,向上计数,ARR设置为100-1,PWM输出的周期为1/(72000000/36)100=510^-5s,也即20KHz,其余值保持默认即可
    4、PA15设置为TIM2_CH1,PB3设置为TIM2_CH2;TIM2设置为编码器模式,TIM2时钟不分频,向上计数,ARR值为65536-1;编码器模式设置为TI1 and TI2;使能TIM2中断,中断优先级设置为2,0
    5、TIM6激活,TIM6时钟分频值设置为72-1,向上计数,ARR设置为1000-1,计数周期为(1/(72000000/72))*1000=0.001s=1ms;使能TIM6中断,中断优先级设置为1,0
    6、USART2设置为异步;波特率115200,8位数据位,1位停止位,无奇偶检验位
    7、IDE设置为Keil,在Keil环境中进行应用层编程
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    2.3.2.应用层开发

    按键扫描函数:

    uint8_t KEY_Scany(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    {
    	if(HAL_GPIO_ReadPin(GPIOx, GPIO_Pin)==0)
    	{
    		while(HAL_GPIO_ReadPin(GPIOx, GPIO_Pin)==0);
    		return 1;
    	}
    	else
    		return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    主函数:

    int main(void)
    {
      /* USER CODE BEGIN 1 */
      uint8_t count=0;
      /* USER CODE END 1 */
    
      /* MCU Configuration--------------------------------------------------------*/
    
      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
      HAL_Init();
    
      /* USER CODE BEGIN Init */
    
      /* USER CODE END Init */
    
      /* Configure the system clock */
      SystemClock_Config();
    
      /* USER CODE BEGIN SysInit */
    
      /* USER CODE END SysInit */
    
      /* Initialize all configured peripherals */
      MX_GPIO_Init();
      MX_TIM1_Init();
      MX_TIM2_Init();
      MX_TIM6_Init();
      MX_USART2_UART_Init();
      /* USER CODE BEGIN 2 */
      //使能桥臂1和桥臂2
      HAL_GPIO_WritePin(GPIOC, EN1_Pin, GPIO_PIN_SET);
      HAL_GPIO_WritePin(GPIOC, EN2_Pin, GPIO_PIN_SET);
      
      //使能TIM1_CH1和TIM1_CH2两通道PWM输出,输出占空比默认为0
      //PWM2输出0,控制PWM1输出的占空比,即可实现开环调速
      HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
      HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);
      
      //使能TIM2_CH1和TIM2_CH2编码器
      HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_1);
      HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_2);
      
      //使能更新中断,清除中断标志位
      __HAL_TIM_ENABLE_IT(&htim2,TIM_IT_UPDATE);
      __HAL_TIM_CLEAR_FLAG(&htim2,TIM_IT_UPDATE);
      
      //使能TIM6中断,清除中断标志位
      HAL_TIM_Base_Start_IT(&htim6);
      __HAL_TIM_CLEAR_IT(&htim6,TIM_IT_UPDATE);
      
      debug_init();
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
    	  if(KEY_Scany(KEY_GPIO_Port,KEY_Pin)==1)
    	  {
    		  Duty+=5;
    		  if(Duty>=100)
    			  Duty=100;
    		  
    		  TIM1->CCR1=Duty;
    	  }
    	  
    	  HAL_Delay(10);
    	  if(count%50==0)  
    	  {
    		  HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);  //0.5s程序运行指示灯
    		  debug_send_wave_data(1,Motor_Speed);         //发送数据到上位机
    		  debug_send_wave_data(2,Motor_Direction);
    		  debug_send_wave_data(3,Duty);
    		  
    		  count=0;
    	  }
    	  count++;
      }
      /* USER CODE END 3 */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    中断处理函数:

    /* USER CODE BEGIN 1 */
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
    	int Encode_now;
    	if(htim->Instance==TIM2)  //TIM2更新中断
    	{
    		if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim2))   //记录溢出次数,向下计数减1,向上计数加1
    		{
    			Encode_Count--;
    		}
    		else
    		{
    			Encode_Count++;
    		}	
    	}
    	else if(htim->Instance==TIM6)
    	{
    		Motor_Direction=__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim2);  //电机方向
    		Encode_now=Get_Encode();  //获取当前脉冲计数总值
    		Speed_Computer(Encode_now,50);  //50ms更新一次速度
    	}
    }
    /* USER CODE END 1 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    Get_Encode函数,该函数获得当前编码器计数总值:

    int Get_Encode(void)
    {
    	return (int32_t) __HAL_TIM_GET_COUNTER(&htim2)+Encode_Count*65536; //计数总值=当前计数值+溢出次数*65536
    }
    
    • 1
    • 2
    • 3
    • 4

    速度计算函数,采用M法测速:
    注:此处借鉴正点原子参考程序,引入冒泡排序及一阶滤波

    void Speed_Computer(int32_t Encode_now,uint8_t time)  //采用M法测速
    {
    	uint8_t i=0,j=0;
    	float temp=0.0;
    	static uint8_t sp_count=0,k=0;
    	static float speed_arr[10]={0.0};
    	
    	if(sp_count==time)  //50ms计算一次速度
    	{
    		g_encode.encode_now=Encode_now;  //获取当前编码器计数总值
    		g_encode.speed=(g_encode.encode_now-g_encode.encode_old);  //计算50ms内编码器计数的变化量
    		speed_arr[k++]=(float)(g_encode.speed*((1000/time)*60)/(uint16_t)(16*4*30));  //M法测速计算电机转速,累计10次转速
    		g_encode.encode_old=g_encode.encode_now;  //保存当前计数值
    		
    		if(k>=10)  //冒泡排序
    		{
    			for(i=10;i>=1;i--)
    			{
    				for(j=0;j<(i-1);j++)
    				{
    					if(speed_arr[j]>speed_arr[j+1])
    					{
    						temp=speed_arr[j];
    						speed_arr[j]=speed_arr[j+1];
    						speed_arr[j+1]=temp;
    					}
    				}
    			}
    			temp=0.0;
    			for(i=2;i<8;i++)  //去掉一个最大和最小值,取平均
    			{
    				temp+=speed_arr[i];
    			}
    			temp=(float)temp/6;
    			Motor_Speed=(float)((Motor_Speed*(float)0.52)+((float)0.48*temp)); //一阶滤波
    			k=0;
    		}
    		sp_count=0;
    	}
    	sp_count++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    上位机采用正点原子上位机,观察测速波形。

    2.4.下载验证

    编译下载到控制器,观察实验现象
    在这里插入图片描述

    总结

    主控板STM32F302R8+驱动板X-NUCLEO-IHM07M1+直流减速电机37GB3530,实现了直流电机的编码器测速,为后续章节的分析奠定基础

  • 相关阅读:
    力扣1482.制作m束花所需的最少时间
    如何在Rocky Linux 8上安装LAMP栈
    DCU上如何运行大模型以及用到的docker命令
    学习笔记二十三:Deployment入门到企业实战应用
    重学 JavaSE 高阶
    python中namedtuple函数用法详解
    android adb工具命令大全
    java计算机毕业设计springboot+vue小微企业人才推荐系统
    基于springboot的校园跑腿系统
    latex给文字添加阴影--链接中转站
  • 原文地址:https://blog.csdn.net/weixin_42650162/article/details/126897363