赞
踩
接上一篇文章,话不多说直接开始
一、打开我们创建的工程文件,先就建立一个文件夹用来存放我们写的子文件(不建立也行),然后建立pid.h,pid.c存入我们建立的文件夹中,并把它的源文件和头文件添加进去,最后记得编译一下。
二、遥控器部分
先在main.h 中定义一个遥控器接收数据的结构体,参考了官方的定义不过我删了一部分不需要的。
- typedef struct
- {
- struct
- {
- signed short ch0;
- signed short ch1;
- signed short ch2;
- signed short ch3;
- unsigned char s1;
- unsigned char s2;
-
- unsigned short sw;
- }rc;
- }DBUS;
在main函数里初始化和中断使能DMA,注意这里我们是用了串口1来接收数据的
- HAL_UART_Receive_DMA(&huart1,dbus_resive,18);//初始化DMA
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//中断使能DMA
遥控接收到的数据需要进行拼接一下,如果大家不懂可以参考一下这位大佬下的这篇博客(https://blog.csdn.net/weixin_45850927/article/details/121299686)
- uint8_t dbus_resive[18]; //用来储存接收到的数据的数组
-
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
- {
- remoter.rc.ch0 = (dbus_resive[0]| (dbus_resive[1] << 8)) & 0x07ff;
- remoter.rc.ch0 -= 1024;
- remoter.rc.ch1 = ((dbus_resive[1] >> 3) | (dbus_resive[2] << 5)) & 0x07ff;
- remoter.rc.ch1 -= 1024;
- remoter.rc.ch2 = ((dbus_resive[2] >> 6) | (dbus_resive[3] << 2) | (dbus_resive[4] << 10)) & 0x07ff;
- remoter.rc.ch2 -= 1024;
- remoter.rc.ch3 = ((dbus_resive[4] >> 1) | (dbus_resive[5] << 7)) & 0x07ff;
- remoter.rc.ch3 -= 1024;
- remoter.rc.s1 = ((dbus_resive[5] >> 4)& 0x000C) >> 2;
- remoter.rc.s2 = ((dbus_resive[5] >> 4)& 0x0003);
-
- remoter.rc.sw = dbus_resive[16]|(dbus_resive[17]<<8);
- }
经过处理后我们接收到四个通道的数据范围是(-660~660)
三,PID部分(PID其实网上资料有很多,这里就不详细介绍,直接贴代码,我用的是最简单的【笑哭】)
- #ifndef __PID_H__
- #define __PID_H__
-
- #include "stdint.h"
-
- typedef float fp32;
-
- enum PID_MODE
- {
- PID_POSITION = 0,
- PID_DELTA
- };
-
- typedef struct
- {
- uint8_t mode;
- //PID 三参数
- fp32 Kp;
- fp32 Ki;
- fp32 Kd;
-
- fp32 max_out; //最大输出
- fp32 max_iout; //最大积分输出
-
- fp32 set;
- fp32 fdb;
-
- fp32 out;
- fp32 Pout;
- fp32 Iout;
- fp32 Dout;
- fp32 Dbuf[3]; //微分项 0最新 1上一次 2上上次
- fp32 error[3]; //误差项 0最新 1上一次 2上上次
-
- } PidTypeDef;
-
- fp32 PID_Calc(PidTypeDef *pid,fp32 ref,fp32 set);
- void PID_init(PidTypeDef *pid,uint8_t mode,const fp32 PID[3],fp32 max_out,fp32 max_iout);
-
- #endif
- #include "pid.h"
- #include "main.h"
-
- #define LimitMax(input, max) \
- { \
- if (input > max) \
- { \
- input = max; \
- } \
- else if (input < -max) \
- { \
- input = -max; \
- } \
- }
-
- void PID_init(PidTypeDef *pid,uint8_t mode,const fp32 PID[3],fp32 max_out,fp32 max_iout)
- {
- if(pid==NULL||PID==NULL)
- {
- return;
- }
- pid->mode=mode;
- pid->Kp=PID[0];
- pid->Ki=PID[1];
- pid->Kd=PID[2];
- pid->max_out=max_out;
- pid->max_iout=max_iout;
- pid->Dbuf[0]=pid->Dbuf[1]=pid->Dbuf[2]=0.0f;
- pid->error[0]=pid->error[1]=pid->error[2]=pid->Pout=pid->Iout=pid->Dout=pid->out=0.0f;
- }
-
- fp32 PID_Calc(PidTypeDef *pid, fp32 ref, fp32 set)
- {
- if (pid == NULL)
- {
- return 0.0f;
- }
-
- pid->error[2] = pid->error[1];
- pid->error[1] = pid->error[0];
- pid->set = set;
- pid->fdb = ref;
- pid->error[0] = set - ref;
- if (pid->mode == PID_POSITION)
- {
- pid->Pout = pid->Kp * pid->error[0];
- pid->Iout += pid->Ki * pid->error[0];
- pid->Dbuf[2] = pid->Dbuf[1];
- pid->Dbuf[1] = pid->Dbuf[0];
- pid->Dbuf[0] = (pid->error[0] - pid->error[1]);
- pid->Dout = pid->Kd * pid->Dbuf[0];
- LimitMax(pid->Iout, pid->max_iout);
- pid->out = pid->Pout + pid->Iout + pid->Dout;
- LimitMax(pid->out, pid->max_out);
- }
- else if (pid->mode == PID_DELTA)
- {
- pid->Pout = pid->Kp * (pid->error[0] - pid->error[1]);
- pid->Iout = pid->Ki * pid->error[0];
- pid->Dbuf[2] = pid->Dbuf[1];
- pid->Dbuf[1] = pid->Dbuf[0];
- pid->Dbuf[0] = (pid->error[0] - 2.0f * pid->error[1] + pid->error[2]);
- pid->Dout = pid->Kd * pid->Dbuf[0];
- pid->out += pid->Pout + pid->Iout + pid->Dout;
- LimitMax(pid->out, pid->max_out);
- }
- return pid->out;
- }
四,电机部分(因为我们只需要简单控个电机,所以就只需要在main.h中定义一些我们需要的就可以了)
- typedef enum
- {
- CAN_CHASSIS_ALL_ID = 0x200,
- CAN_AUXILIARY_ALL_ID = 0x1FF,
- motor1 = 0x201,
- motor2 = 0x202,
- motor3 = 0x203,
- motor4 = 0x204,
- }can_msg_id;
-
- typedef struct
- {
- uint16_t angle_value;
- int16_t speed_rpm;
- int16_t real_current;
- uint8_t temperate;
- int16_t real_angle;
- }motor_measure_t;
五,CAN
CAN基础的配置CUBEMX已经配置好了
配置CAN的过滤器
- void can_filter_init(void)
- {
-
- CAN_FilterTypeDef can_filter_st;
-
- can_filter_st.FilterActivation = ENABLE;
- can_filter_st.FilterMode = CAN_FILTERMODE_IDMASK;
- can_filter_st.FilterScale = CAN_FILTERSCALE_32BIT;
- can_filter_st.FilterIdHigh = 0x0000;
- can_filter_st.FilterIdLow = 0x0000;
- can_filter_st.FilterMaskIdHigh = 0x0000;
- can_filter_st.FilterMaskIdLow = 0x0000;
- can_filter_st.FilterBank = 0;
- can_filter_st.FilterFIFOAssignment = CAN_RX_FIFO0;
- HAL_CAN_ConfigFilter(&hcan1, &can_filter_st);
- }
如果不太清楚可以参考一下这位大佬写的(https://blog.csdn.net/weixin_44663976/article/details/126138298)
配置完过滤器就可以开启CAN的使用了(这两步很重要,不要漏了)
- HAL_CAN_Start(&hcan1);//启动CAN1
- HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);//使能中断
写到一个函数里防漏
- void can1_start(void)
- {
- can_filter_init();
- HAL_CAN_Start(&hcan1);//启动CAN1
- HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);//使能中断
- }
要让M3508电机转呢就要给电机发送电流
- uint8_t chassis_can_send_data[8]; //用于接收电机的原始数据
- CAN_TxHeaderTypeDef chassis_tx_message;
-
-
- /**
- * @brief send control current of motor (0x201, 0x202, 0x203, 0x204)
- * @param[in] motor1: (0x201) 3508 motor control current, range [-16384,16384]
- * @param[in] motor2: (0x202) 3508 motor control current, range [-16384,16384]
- * @param[in] motor3: (0x203) 3508 motor control current, range [-16384,16384]
- * @param[in] motor4: (0x204) 3508 motor control current, range [-16384,16384]
- * @retval none
- */
- void CAN_cmd_chassis(int16_t M1, int16_t M2, int16_t M3, int16_t M4)
- {
- uint32_t send_mail_box;
- chassis_tx_message.StdId=CAN_CHASSIS_ALL_ID;
- chassis_tx_message.IDE=CAN_ID_STD;
- chassis_tx_message.RTR=CAN_RTR_DATA;
- chassis_tx_message.DLC=0x08;
- chassis_can_send_data[0]=M1>>8;
- chassis_can_send_data[1]=M1;
- chassis_can_send_data[2]=M2>>8;
- chassis_can_send_data[3]=M2;
- chassis_can_send_data[4]=M3>>8;
- chassis_can_send_data[5]=M3;
- chassis_can_send_data[6]=M4>>8;
- chassis_can_send_data[7]=M4;
-
- HAL_CAN_AddTxMessage(&hcan1,&chassis_tx_message,chassis_can_send_data,&send_mail_box);
- }
这个函数几乎每个例程里面都有,笔者也是直接拿过来用
CAN接收到数据后就会产生中断,进入中断回调函数,这个函数是需要我们自己编写的,我们需要在这里处理拼接接收到的数据,然后储存起来
- uint8_t rx_data[8]; //用于接收电机原始数据
- motor_measure_t motor_chassis[4]; //声明一个结构体数组来储存处理后电机的数据
- //用来拼接电机数据
- #define get_motor_measure(ptr,data)\
- {\
- (ptr)->angle_value=(uint16_t)((data)[0]<<8|(data)[1]);\
- (ptr)->speed_rpm=(uint16_t)((data)[2]<<8|(data)[3]);\
- (ptr)->real_current=(uint16_t)((data)[4]<<8|(data)[5]);\
- (ptr)->temperate=(data)[6];\
- (ptr)->real_angle=(ptr)->angle_value/8192.0f*360.0f;\
- }
-
- void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
- {
-
- CAN_RxHeaderTypeDef rx_header;
- HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rx_header, rx_data);
- switch(rx_header.StdId)
- {
- case motor1:
- case motor2:
- case motor3:
- case motor4:
- {
- static uint8_t i = 0;
- i = rx_header.StdId - motor1;
- get_motor_measure(&motor_chassis[i],rx_data);
- break;
- }
-
- default:
- {
- break;
- }
- }
-
- }
六、现在我们可以开始写main函数了(直接上代码)
- /* USER CODE BEGIN Header */
- /**
- ******************************************************************************
- * @file : main.c
- * @brief : Main program body
- ******************************************************************************
- * @attention
- *
- * Copyright (c) 2023 STMicroelectronics.
- * All rights reserved.
- *
- * This software is licensed under terms that can be found in the LICENSE file
- * in the root directory of this software component.
- * If no LICENSE file comes with this software, it is provided AS-IS.
- *
- ******************************************************************************
- */
- /* USER CODE END Header */
- /* Includes ------------------------------------------------------------------*/
- #include "main.h"
- #include "can.h"
- #include "dma.h"
- #include "usart.h"
- #include "gpio.h"
-
- /* Private includes ----------------------------------------------------------*/
- /* USER CODE BEGIN Includes */
- #include "pid.h"
- /* USER CODE END Includes */
-
- /* Private typedef -----------------------------------------------------------*/
- /* USER CODE BEGIN PTD */
- #define unit_speed 1000/660.0 //这里改最大速度只需要改前面的1000就可以了
-
- DBUS remoter;
- uint8_t dbus_resive[18];
- /* USER CODE END PTD */
-
- /* Private define ------------------------------------------------------------*/
- /* USER CODE BEGIN PD */
- extern motor_measure_t motor_chassis[4];
- PidTypeDef motor_pid;
- fp32 pid_motor[3]={3.0,0.1,0}; // 这三个参数也是参考了其他大佬的
- /* USER CODE END PD */
-
- /* Private macro -------------------------------------------------------------*/
- /* USER CODE BEGIN PM */
-
- /* USER CODE END PM */
-
- /* Private variables ---------------------------------------------------------*/
-
- /* USER CODE BEGIN PV */
- void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan);
- void CAN_cmd_chassis(int16_t M1, int16_t M2, int16_t M3, int16_t M4);
- fp32 PID_Calc(PidTypeDef *pid,fp32 ref,fp32 set);
- /* 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 */
-
- signed int set_speed;
- /* 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_DMA_Init();
- MX_CAN1_Init();
- MX_USART1_UART_Init();
- MX_USART6_UART_Init();
- /* USER CODE BEGIN 2 */
- HAL_UART_Receive_DMA(&huart1,dbus_resive,18);//初始化DMA
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//IDLE 中断使能
- can1_start();
- PID_init(&motor_pid,PID_POSITION,pid_motor,16000,16000); //PID初始化
-
- /* USER CODE END 2 */
-
- /* Infinite loop */
- /* USER CODE BEGIN WHILE */
- while (1)
- {
- /* USER CODE END WHILE */
- set_speed = remoter.rc.ch1 * unit_speed;
- PID_Calc(&motor_pid,motor_chassis[1].speed_rpm,set_speed);
- CAN_cmd_chassis(motor_pid.out,motor_pid.out,motor_pid.out,motor_pid.out);
- HAL_Delay(10);
-
- /* USER CODE BEGIN 3 */
- }
- /* 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 RCC Oscillators according to the specified parameters
- * in the RCC_OscInitTypeDef structure.
- */
- 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 buses 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 */
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
- {
- remoter.rc.ch0 = (dbus_resive[0]| (dbus_resive[1] << 8)) & 0x07ff;
- remoter.rc.ch0 -= 1024;
- remoter.rc.ch1 = ((dbus_resive[1] >> 3) | (dbus_resive[2] << 5)) & 0x07ff;
- remoter.rc.ch1 -= 1024;
- remoter.rc.ch2 = ((dbus_resive[2] >> 6) | (dbus_resive[3] << 2) | (dbus_resive[4] << 10)) & 0x07ff;
- remoter.rc.ch2 -= 1024;
- remoter.rc.ch3 = ((dbus_resive[4] >> 1) | (dbus_resive[5] << 7)) & 0x07ff;
- remoter.rc.ch3 -= 1024;
- remoter.rc.s1 = ((dbus_resive[5] >> 4)& 0x000C) >> 2;
- remoter.rc.s2 = ((dbus_resive[5] >> 4)& 0x0003);
-
- remoter.rc.sw = dbus_resive[16]|(dbus_resive[17]<<8);
- }
- /* 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 */
- __disable_irq();
- while (1)
- {
- }
- /* 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,
- ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
- /* USER CODE END 6 */
- }
- #endif /* USE_FULL_ASSERT */
-
七,仿真
测试一下我们的代码
八,
到这功能就算完成啦。要完全看懂这些的也是需要有一定相关知识基础的,这篇主要写的就是怎么用这些函数,至于为什么要这么写或者说为什么要这么用就没有详细说,如果大家想学习的可以去网上搜,相关的资料有很多,有些地方笔者也为大家贴出来了。如果大家需要什么相关资料也可找笔者要哦,祝大家生活愉快。
“且将新火试新茶,诗酒趁年华”
-------------苏轼
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。