前言
-------------------------------------------------------------------------------------------------------------------------------实验室留下一块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>© 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循环能不能这么用。
以上就是所有内容了