当前位置:   article > 正文

大疆遥控控制M3508电机二(基于HAL库)_m3508电机控制

m3508电机控制

接上一篇文章,话不多说直接开始

一、打开我们创建的工程文件,先就建立一个文件夹用来存放我们写的子文件(不建立也行),然后建立pid.h,pid.c存入我们建立的文件夹中,并把它的源文件和头文件添加进去,最后记得编译一下。

二、遥控器部分

  1. 先在main.h 中定义一个遥控器接收数据的结构体,参考了官方的定义不过我删了一部分不需要的。

  1. typedef struct
  2. {
  3.     struct
  4.     {
  5.         signed short ch0;
  6.         signed short ch1;
  7.         signed short ch2;
  8.         signed short ch3;
  9.         unsigned char s1;
  10.         unsigned char s2;
  11.        
  12.         unsigned short sw;
  13.     }rc;
  14. }DBUS;
  1. 在main函数里初始化和中断使能DMA,注意这里我们是用了串口1来接收数据的

  1. HAL_UART_Receive_DMA(&huart1,dbus_resive,18);//初始化DMA
  2. __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//中断使能DMA
  1. 遥控接收到的数据需要进行拼接一下,如果大家不懂可以参考一下这位大佬下的这篇博客(https://blog.csdn.net/weixin_45850927/article/details/121299686

  1. uint8_t dbus_resive[18]; //用来储存接收到的数据的数组
  2. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
  3. {
  4. remoter.rc.ch0 = (dbus_resive[0]| (dbus_resive[1] << 8)) & 0x07ff;
  5. remoter.rc.ch0 -= 1024;
  6. remoter.rc.ch1 = ((dbus_resive[1] >> 3) | (dbus_resive[2] << 5)) & 0x07ff;
  7. remoter.rc.ch1 -= 1024;
  8. remoter.rc.ch2 = ((dbus_resive[2] >> 6) | (dbus_resive[3] << 2) | (dbus_resive[4] << 10)) & 0x07ff;
  9. remoter.rc.ch2 -= 1024;
  10. remoter.rc.ch3 = ((dbus_resive[4] >> 1) | (dbus_resive[5] << 7)) & 0x07ff;
  11. remoter.rc.ch3 -= 1024;
  12. remoter.rc.s1 = ((dbus_resive[5] >> 4)& 0x000C) >> 2;
  13. remoter.rc.s2 = ((dbus_resive[5] >> 4)& 0x0003);
  14. remoter.rc.sw = dbus_resive[16]|(dbus_resive[17]<<8);
  15. }

经过处理后我们接收到四个通道的数据范围是(-660~660)

三,PID部分(PID其实网上资料有很多,这里就不详细介绍,直接贴代码,我用的是最简单的【笑哭】)

  1. #ifndef __PID_H__
  2. #define __PID_H__
  3. #include "stdint.h"
  4. typedef float fp32;
  5. enum PID_MODE
  6. {
  7. PID_POSITION = 0,
  8. PID_DELTA
  9. };
  10. typedef struct
  11. {
  12. uint8_t mode;
  13. //PID 三参数
  14. fp32 Kp;
  15. fp32 Ki;
  16. fp32 Kd;
  17. fp32 max_out; //最大输出
  18. fp32 max_iout; //最大积分输出
  19. fp32 set;
  20. fp32 fdb;
  21. fp32 out;
  22. fp32 Pout;
  23. fp32 Iout;
  24. fp32 Dout;
  25. fp32 Dbuf[3]; //微分项 0最新 1上一次 2上上次
  26. fp32 error[3]; //误差项 0最新 1上一次 2上上次
  27. } PidTypeDef;
  28. fp32 PID_Calc(PidTypeDef *pid,fp32 ref,fp32 set);
  29. void PID_init(PidTypeDef *pid,uint8_t mode,const fp32 PID[3],fp32 max_out,fp32 max_iout);
  30. #endif
  1. #include "pid.h"
  2. #include "main.h"
  3. #define LimitMax(input, max) \
  4. { \
  5. if (input > max) \
  6. { \
  7. input = max; \
  8. } \
  9. else if (input < -max) \
  10. { \
  11. input = -max; \
  12. } \
  13. }
  14. void PID_init(PidTypeDef *pid,uint8_t mode,const fp32 PID[3],fp32 max_out,fp32 max_iout)
  15. {
  16. if(pid==NULL||PID==NULL)
  17. {
  18. return;
  19. }
  20. pid->mode=mode;
  21. pid->Kp=PID[0];
  22. pid->Ki=PID[1];
  23. pid->Kd=PID[2];
  24. pid->max_out=max_out;
  25. pid->max_iout=max_iout;
  26. pid->Dbuf[0]=pid->Dbuf[1]=pid->Dbuf[2]=0.0f;
  27. pid->error[0]=pid->error[1]=pid->error[2]=pid->Pout=pid->Iout=pid->Dout=pid->out=0.0f;
  28. }
  29. fp32 PID_Calc(PidTypeDef *pid, fp32 ref, fp32 set)
  30. {
  31. if (pid == NULL)
  32. {
  33. return 0.0f;
  34. }
  35. pid->error[2] = pid->error[1];
  36. pid->error[1] = pid->error[0];
  37. pid->set = set;
  38. pid->fdb = ref;
  39. pid->error[0] = set - ref;
  40. if (pid->mode == PID_POSITION)
  41. {
  42. pid->Pout = pid->Kp * pid->error[0];
  43. pid->Iout += pid->Ki * pid->error[0];
  44. pid->Dbuf[2] = pid->Dbuf[1];
  45. pid->Dbuf[1] = pid->Dbuf[0];
  46. pid->Dbuf[0] = (pid->error[0] - pid->error[1]);
  47. pid->Dout = pid->Kd * pid->Dbuf[0];
  48. LimitMax(pid->Iout, pid->max_iout);
  49. pid->out = pid->Pout + pid->Iout + pid->Dout;
  50. LimitMax(pid->out, pid->max_out);
  51. }
  52. else if (pid->mode == PID_DELTA)
  53. {
  54. pid->Pout = pid->Kp * (pid->error[0] - pid->error[1]);
  55. pid->Iout = pid->Ki * pid->error[0];
  56. pid->Dbuf[2] = pid->Dbuf[1];
  57. pid->Dbuf[1] = pid->Dbuf[0];
  58. pid->Dbuf[0] = (pid->error[0] - 2.0f * pid->error[1] + pid->error[2]);
  59. pid->Dout = pid->Kd * pid->Dbuf[0];
  60. pid->out += pid->Pout + pid->Iout + pid->Dout;
  61. LimitMax(pid->out, pid->max_out);
  62. }
  63. return pid->out;
  64. }

四,电机部分(因为我们只需要简单控个电机,所以就只需要在main.h中定义一些我们需要的就可以了)

  1. typedef enum
  2. {
  3. CAN_CHASSIS_ALL_ID = 0x200,
  4. CAN_AUXILIARY_ALL_ID = 0x1FF,
  5. motor1 = 0x201,
  6. motor2 = 0x202,
  7. motor3 = 0x203,
  8. motor4 = 0x204,
  9. }can_msg_id;
  10. typedef struct
  11. {
  12. uint16_t angle_value;
  13. int16_t speed_rpm;
  14. int16_t real_current;
  15. uint8_t temperate;
  16. int16_t real_angle;
  17. }motor_measure_t;

五,CAN

  1. CAN基础的配置CUBEMX已经配置好了

  1. 配置CAN的过滤器

  1. void can_filter_init(void)
  2. {
  3. CAN_FilterTypeDef can_filter_st;
  4. can_filter_st.FilterActivation = ENABLE;
  5. can_filter_st.FilterMode = CAN_FILTERMODE_IDMASK;
  6. can_filter_st.FilterScale = CAN_FILTERSCALE_32BIT;
  7. can_filter_st.FilterIdHigh = 0x0000;
  8. can_filter_st.FilterIdLow = 0x0000;
  9. can_filter_st.FilterMaskIdHigh = 0x0000;
  10. can_filter_st.FilterMaskIdLow = 0x0000;
  11. can_filter_st.FilterBank = 0;
  12. can_filter_st.FilterFIFOAssignment = CAN_RX_FIFO0;
  13. HAL_CAN_ConfigFilter(&hcan1, &can_filter_st);
  14. }

如果不太清楚可以参考一下这位大佬写的(https://blog.csdn.net/weixin_44663976/article/details/126138298

  1. 配置完过滤器就可以开启CAN的使用了(这两步很重要,不要漏了)

  1. HAL_CAN_Start(&hcan1);//启动CAN1
  2. HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);//使能中断

写到一个函数里防漏

  1. void can1_start(void)
  2. {
  3. can_filter_init();
  4. HAL_CAN_Start(&hcan1);//启动CAN1
  5. HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);//使能中断
  6. }
  1. 要让M3508电机转呢就要给电机发送电流

  1. uint8_t chassis_can_send_data[8]; //用于接收电机的原始数据
  2. CAN_TxHeaderTypeDef chassis_tx_message;
  3. /**
  4. * @brief send control current of motor (0x201, 0x202, 0x203, 0x204)
  5. * @param[in] motor1: (0x201) 3508 motor control current, range [-16384,16384]
  6. * @param[in] motor2: (0x202) 3508 motor control current, range [-16384,16384]
  7. * @param[in] motor3: (0x203) 3508 motor control current, range [-16384,16384]
  8. * @param[in] motor4: (0x204) 3508 motor control current, range [-16384,16384]
  9. * @retval none
  10. */
  11. void CAN_cmd_chassis(int16_t M1, int16_t M2, int16_t M3, int16_t M4)
  12. {
  13. uint32_t send_mail_box;
  14. chassis_tx_message.StdId=CAN_CHASSIS_ALL_ID;
  15. chassis_tx_message.IDE=CAN_ID_STD;
  16. chassis_tx_message.RTR=CAN_RTR_DATA;
  17. chassis_tx_message.DLC=0x08;
  18. chassis_can_send_data[0]=M1>>8;
  19. chassis_can_send_data[1]=M1;
  20. chassis_can_send_data[2]=M2>>8;
  21. chassis_can_send_data[3]=M2;
  22. chassis_can_send_data[4]=M3>>8;
  23. chassis_can_send_data[5]=M3;
  24. chassis_can_send_data[6]=M4>>8;
  25. chassis_can_send_data[7]=M4;
  26. HAL_CAN_AddTxMessage(&hcan1,&chassis_tx_message,chassis_can_send_data,&send_mail_box);
  27. }

这个函数几乎每个例程里面都有,笔者也是直接拿过来用

  1. CAN接收到数据后就会产生中断,进入中断回调函数,这个函数是需要我们自己编写的,我们需要在这里处理拼接接收到的数据,然后储存起来

  1. uint8_t rx_data[8]; //用于接收电机原始数据
  2. motor_measure_t motor_chassis[4]; //声明一个结构体数组来储存处理后电机的数据
  3. //用来拼接电机数据
  4. #define get_motor_measure(ptr,data)\
  5. {\
  6. (ptr)->angle_value=(uint16_t)((data)[0]<<8|(data)[1]);\
  7. (ptr)->speed_rpm=(uint16_t)((data)[2]<<8|(data)[3]);\
  8. (ptr)->real_current=(uint16_t)((data)[4]<<8|(data)[5]);\
  9. (ptr)->temperate=(data)[6];\
  10. (ptr)->real_angle=(ptr)->angle_value/8192.0f*360.0f;\
  11. }
  12. void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
  13. {
  14. CAN_RxHeaderTypeDef rx_header;
  15. HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rx_header, rx_data);
  16. switch(rx_header.StdId)
  17. {
  18. case motor1:
  19. case motor2:
  20. case motor3:
  21. case motor4:
  22. {
  23. static uint8_t i = 0;
  24. i = rx_header.StdId - motor1;
  25. get_motor_measure(&motor_chassis[i],rx_data);
  26. break;
  27. }
  28. default:
  29. {
  30. break;
  31. }
  32. }
  33. }

六、现在我们可以开始写main函数了(直接上代码)

  1. /* USER CODE BEGIN Header */
  2. /**
  3. ******************************************************************************
  4. * @file : main.c
  5. * @brief : 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 "can.h"
  22. #include "dma.h"
  23. #include "usart.h"
  24. #include "gpio.h"
  25. /* Private includes ----------------------------------------------------------*/
  26. /* USER CODE BEGIN Includes */
  27. #include "pid.h"
  28. /* USER CODE END Includes */
  29. /* Private typedef -----------------------------------------------------------*/
  30. /* USER CODE BEGIN PTD */
  31. #define unit_speed 1000/660.0 //这里改最大速度只需要改前面的1000就可以了
  32. DBUS remoter;
  33. uint8_t dbus_resive[18];
  34. /* USER CODE END PTD */
  35. /* Private define ------------------------------------------------------------*/
  36. /* USER CODE BEGIN PD */
  37. extern motor_measure_t motor_chassis[4];
  38. PidTypeDef motor_pid;
  39. fp32 pid_motor[3]={3.0,0.1,0}; // 这三个参数也是参考了其他大佬的
  40. /* USER CODE END PD */
  41. /* Private macro -------------------------------------------------------------*/
  42. /* USER CODE BEGIN PM */
  43. /* USER CODE END PM */
  44. /* Private variables ---------------------------------------------------------*/
  45. /* USER CODE BEGIN PV */
  46. void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan);
  47. void CAN_cmd_chassis(int16_t M1, int16_t M2, int16_t M3, int16_t M4);
  48. fp32 PID_Calc(PidTypeDef *pid,fp32 ref,fp32 set);
  49. /* USER CODE END PV */
  50. /* Private function prototypes -----------------------------------------------*/
  51. void SystemClock_Config(void);
  52. /* USER CODE BEGIN PFP */
  53. /* USER CODE END PFP */
  54. /* Private user code ---------------------------------------------------------*/
  55. /* USER CODE BEGIN 0 */
  56. /* USER CODE END 0 */
  57. /**
  58. * @brief The application entry point.
  59. * @retval int
  60. */
  61. int main(void)
  62. {
  63. /* USER CODE BEGIN 1 */
  64. signed int set_speed;
  65. /* USER CODE END 1 */
  66. /* MCU Configuration--------------------------------------------------------*/
  67. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  68. HAL_Init();
  69. /* USER CODE BEGIN Init */
  70. /* USER CODE END Init */
  71. /* Configure the system clock */
  72. SystemClock_Config();
  73. /* USER CODE BEGIN SysInit */
  74. /* USER CODE END SysInit */
  75. /* Initialize all configured peripherals */
  76. MX_GPIO_Init();
  77. MX_DMA_Init();
  78. MX_CAN1_Init();
  79. MX_USART1_UART_Init();
  80. MX_USART6_UART_Init();
  81. /* USER CODE BEGIN 2 */
  82. HAL_UART_Receive_DMA(&huart1,dbus_resive,18);//初始化DMA
  83. __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//IDLE 中断使能
  84. can1_start();
  85. PID_init(&motor_pid,PID_POSITION,pid_motor,16000,16000); //PID初始化
  86. /* USER CODE END 2 */
  87. /* Infinite loop */
  88. /* USER CODE BEGIN WHILE */
  89. while (1)
  90. {
  91. /* USER CODE END WHILE */
  92. set_speed = remoter.rc.ch1 * unit_speed;
  93. PID_Calc(&motor_pid,motor_chassis[1].speed_rpm,set_speed);
  94. CAN_cmd_chassis(motor_pid.out,motor_pid.out,motor_pid.out,motor_pid.out);
  95. HAL_Delay(10);
  96. /* USER CODE BEGIN 3 */
  97. }
  98. /* USER CODE END 3 */
  99. }
  100. /**
  101. * @brief System Clock Configuration
  102. * @retval None
  103. */
  104. void SystemClock_Config(void)
  105. {
  106. RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  107. RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  108. /** Configure the main internal regulator output voltage
  109. */
  110. __HAL_RCC_PWR_CLK_ENABLE();
  111. __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  112. /** Initializes the RCC Oscillators according to the specified parameters
  113. * in the RCC_OscInitTypeDef structure.
  114. */
  115. RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  116. RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  117. RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  118. RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  119. RCC_OscInitStruct.PLL.PLLM = 6;
  120. RCC_OscInitStruct.PLL.PLLN = 168;
  121. RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  122. RCC_OscInitStruct.PLL.PLLQ = 4;
  123. if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  124. {
  125. Error_Handler();
  126. }
  127. /** Initializes the CPU, AHB and APB buses clocks
  128. */
  129. RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
  130. |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  131. RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  132. RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  133. RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  134. RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
  135. if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  136. {
  137. Error_Handler();
  138. }
  139. }
  140. /* USER CODE BEGIN 4 */
  141. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
  142. {
  143. remoter.rc.ch0 = (dbus_resive[0]| (dbus_resive[1] << 8)) & 0x07ff;
  144. remoter.rc.ch0 -= 1024;
  145. remoter.rc.ch1 = ((dbus_resive[1] >> 3) | (dbus_resive[2] << 5)) & 0x07ff;
  146. remoter.rc.ch1 -= 1024;
  147. remoter.rc.ch2 = ((dbus_resive[2] >> 6) | (dbus_resive[3] << 2) | (dbus_resive[4] << 10)) & 0x07ff;
  148. remoter.rc.ch2 -= 1024;
  149. remoter.rc.ch3 = ((dbus_resive[4] >> 1) | (dbus_resive[5] << 7)) & 0x07ff;
  150. remoter.rc.ch3 -= 1024;
  151. remoter.rc.s1 = ((dbus_resive[5] >> 4)& 0x000C) >> 2;
  152. remoter.rc.s2 = ((dbus_resive[5] >> 4)& 0x0003);
  153. remoter.rc.sw = dbus_resive[16]|(dbus_resive[17]<<8);
  154. }
  155. /* USER CODE END 4 */
  156. /**
  157. * @brief This function is executed in case of error occurrence.
  158. * @retval None
  159. */
  160. void Error_Handler(void)
  161. {
  162. /* USER CODE BEGIN Error_Handler_Debug */
  163. /* User can add his own implementation to report the HAL error return state */
  164. __disable_irq();
  165. while (1)
  166. {
  167. }
  168. /* USER CODE END Error_Handler_Debug */
  169. }
  170. #ifdef USE_FULL_ASSERT
  171. /**
  172. * @brief Reports the name of the source file and the source line number
  173. * where the assert_param error has occurred.
  174. * @param file: pointer to the source file name
  175. * @param line: assert_param error line source number
  176. * @retval None
  177. */
  178. void assert_failed(uint8_t *file, uint32_t line)
  179. {
  180. /* USER CODE BEGIN 6 */
  181. /* User can add his own implementation to report the file name and line number,
  182. ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  183. /* USER CODE END 6 */
  184. }
  185. #endif /* USE_FULL_ASSERT */

七,仿真

测试一下我们的代码

八,

到这功能就算完成啦。要完全看懂这些的也是需要有一定相关知识基础的,这篇主要写的就是怎么用这些函数,至于为什么要这么写或者说为什么要这么用就没有详细说,如果大家想学习的可以去网上搜,相关的资料有很多,有些地方笔者也为大家贴出来了。如果大家需要什么相关资料也可找笔者要哦,祝大家生活愉快。

“且将新火试新茶,诗酒趁年华”

-------------苏轼

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/335632
推荐阅读
相关标签
  

闽ICP备14008679号