Bootstrap

大疆A板STM32427用CAN通信进行M2006/M3508位置闭环和往复转动

前言       

-------------------------------------------------------------------------------------------------------------------------------实验室留下一块M2006和一块大疆A板,想着就拿来给我做的机械本体做个工作空间的测试,所以实现两个功能:

1、旋转固定转角,即位置闭环

2、实现往复匀速运动,即速度闭环

由于外设有限,而且A板因为年久失修,唯一的按键已经坏了,所以调试过程异常艰难……

硬件部分

        首先还是不得不骂一句RM真是坑钱玩意儿,逮着学生就使劲薅,所有的硬件接口都被高度封装,连接起外设来十分的麻烦,甚至下载线都得是专用的,不得不买RM自己做的线……然而这怎么难得了我呢,作为勤俭节约的好少年,当然是自己动手丰衣足食,自制的SWD接口。

这里说一下,这种接口只要用淘宝拍照功能,照一下板子上的插座,一般就能在相关链接里找到公头,包括其他的4口的插头也能这么买到,我记得是管脚间距1.25mm的。

        然后就是A板引出来的最大电压是12V,C610电调的额定电压是24V,不过很神奇的是就算是12V够给一个电调供电进行闭环控制了;当然,你接两个电调进行闭环的话绝对是带不动的,开环的话,四个电机也能带动,因为不需要电调进行控制。

最后给个硬件接线图:

可以一直用电池供电,Jlink的电源线拔掉就行了

 程序部分

        由于不是科班,本人C语言很烂,写程序就是像裁缝一样,东女票西女票,所以肯定有更好的实现逻辑,大家当我的逻辑是最下等的就行了。

        首先是配置CAN通信接口,这里可以看B站这个up的视频,M2006与M3508大疆电机pid程序控制_哔哩哔哩_bilibili步骤非常简单,说明书里也有,就是用CubeMX进行配置,时钟树大多数人都是这么配置的,不过427的性能是能到180MHz的,给168也行

然后改引脚,AC板都是一个引脚,之后使能中断

由于电调的接受波特率是1000000,所以这样分频,然后就可以生成代码了。

之后用这个up主的GitHub代码随便写的M3508电机位置闭环_哔哩哔哩_bilibili

 keil里的代码替换添加我就不赘述了,这个比较基础,注意这个Up用的单片机型号可能跟大伙的不一样,所有还是要以上面的CubeMX为主体进行代码添加,把这个Up的代码移植到自己的程序中去,直接下CAN通信不了可别赖我哈。移植完后的程序树应该有这些.C文件,这里除了tim都是必要的,tim是我自己搞着玩的。

 然后就是把随便写的M3508电机位置闭环_哔哩哔哩_bilibili的主函数进行修改,这里我直接粘贴我自己的

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "can.h"
#include "gpio.h"
#include "math.h"
#include "tim.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "bsp_can.h"
#include "CAN_receive.h"
#include "pid.h"


/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
pid_t pid3508v,pid3508pos;
const motor_measure_t *hahaha3508;
int16_t iref_inner;
float pos_now,pos_temp,pos_set=0,vel_set=192;
float angle_set=2000*19.2*8;
float max_speed = 400;
#define mode 1

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* 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_CAN1_Init();
	MX_TIM2_Init();
  /* USER CODE BEGIN 2 */


    can_filter_init();
		hahaha3508 = get_chassis_motor_measure_point(0);
		PID_struct_init(&pid3508v ,POSITION_PID,10000.f,10000.f,10.f,6.f,20.0f);// float kp20,float ki,float kd
    PID_struct_init(&pid3508pos ,POSITION_PID,10000.f,10000.f,0.155f,0,0);// float kp20,float ki,float kd
  /* USER CODE END 2 */

  /* Infinite loop */
int time;
	
  /* USER CODE BEGIN WHILE */
  while (1)
  {

		
		
		pos_temp+=hahaha3508->speed_rpm;
		pos_set=angle_set;
		if(mode == 0)
		{
			pos_set=angle_set;
		vel_set=pid_calc(&pid3508pos,pos_temp,pos_set);//通过位置偏差计算速度大小,偏差大速度快
		iref_inner=pid_calc(&pid3508v,hahaha3508->speed_rpm,vel_set);//将实际速度跟踪上面求到的速度
    CAN_cmd_chassis(iref_inner, 0, 0, 0);//
		HAL_Delay(2);
		}
			if(mode == 1)
			{				
				for(time=0;time<=1000;time++)
		{
	
		iref_inner=pid_calc(&pid3508v,hahaha3508->speed_rpm,0);//将实际速度跟踪上面求到的速度
    CAN_cmd_chassis(iref_inner, 0, 0, 0);//
		HAL_Delay(2);	
		}	
		for(time=0;time<=1000;time++)
		{
	
		iref_inner=pid_calc(&pid3508v,hahaha3508->speed_rpm,max_speed);//将实际速度跟踪上面求到的速度
    CAN_cmd_chassis(iref_inner, 0, 0, 0);//
		HAL_Delay(2);
		
		}
		time = 0;
				for(time=0;time<=1000;time++)
		{
	
		iref_inner=pid_calc(&pid3508v,hahaha3508->speed_rpm,0);//将实际速度跟踪上面求到的速度
    CAN_cmd_chassis(iref_inner, 0, 0, 0);//
		HAL_Delay(2);
		}	
		time = 0;
		for(time=0;time<=1000;time++)
		{
	
		iref_inner=pid_calc(&pid3508v,hahaha3508->speed_rpm,-max_speed);//将实际速度跟踪上面求到的速度
    CAN_cmd_chassis(iref_inner, 0, 0, 0);//
		HAL_Delay(2);	
		
		}
		
				time = 0;
		for(time=0;time<=1000;time++)
		{
	
		iref_inner=pid_calc(&pid3508v,hahaha3508->speed_rpm,0);//将实际速度跟踪上面求到的速度
    CAN_cmd_chassis(iref_inner, 0, 0, 0);//
		HAL_Delay(2);	
		}	
		}
		

  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 6;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

这里简单解释一下,mode就是转换模式,mode设置成0就是位置闭环,1就是往复旋转。位置闭环是上面那个up写的,往复运动是我写的。

我本来想的是用计时器中断来控制正反转,在中断里放一个标志位,让这个标志位指导正反转,但是我太菜了,逻辑写不出来,因为一次中断0.5s,我不知道怎么才能让这个标志位持续几秒不变,然后持续几秒又变化,用中断是更精确的办法了。。。。

所以我就用的for循环,个人感觉HAL的delay是不精确延时,每次都不太一样,用for来延时好像还挺准的?不知道有没有大佬告诉我for循环能不能这么用。

以上就是所有内容了

;