新人写帖子,仅供内部人员参照,使用大疆A型板,2006电机,c610电调,4s电池
cubemx配置如图
基本配置
注意 HSE 设置为12MHz
配置can
注意将手动将PD0和PD1设置为can1
使用fifo0邮箱作为接收,所以使能RX0
配置工程名
即可生成
keil5代码部分
LSS_PID.c
#include "LSS_PID.h"
PID M3508_spid[8];
PID M3508_apid[8];
float abs_limit(float a, float ABS_MAX)
{
if(a > ABS_MAX)
a = ABS_MAX;
if(a < -ABS_MAX)
a = -ABS_MAX;
return a;
}
/**
三个用户自定义参数:
pp->Proportion比例
pp->Integral比例积分
pp->Derivative比例微分
**/
void PID_Position_Calc( PID *pp, float CurrentPoint, float NextPoint ) //位置式PID
{
pp->Error = NextPoint - CurrentPoint; //误差
pp->SumError += pp->Error; //积分项:误差累积
pp->DError = pp->Error - pp->LastError; //微分项:误差变化
pp->output = pp->Proportion * pp->Error + \
abs_limit(pp->Integral * pp->SumError, pp->Integralmax ) + \
pp->Derivative * pp->DError ; //pid输出
if(pp->output > pp->outputmax ) pp->output = pp->outputmax; //?
if(pp->output < - pp->outputmax ) pp->output = -pp->outputmax;//?
// pp->PrevError = pp->LastError; //上次误差保存
pp->LastError = pp->Error;//这次误差保存
}
void PID_Incremental_Calc( PID *pp, float CurrentPoint, float NextPoint ) //增量式PID
{
pp->Error = NextPoint - CurrentPoint;
pp->SumError += pp->Error;
pp->DError = pp->Error - pp->LastError;
pp->output += pp->Proportion * ( pp->Error - pp->LastError )+ \
abs_limit(pp->Integral * pp->Error, pp->Integralmax ) + \
pp->Derivative * ( pp->Error + pp->PrevError - 2*pp->LastError);
if(pp->output > pp->outputmax ) pp->output = pp->outputmax;
if(pp->output < - pp->outputmax ) pp->output = -pp->outputmax;
pp->PrevError = pp->LastError;
pp->LastError = pp->Error;
}
void PIDInit(PID *pp, float Kp , float Ki , float Kd , float outputmax, float Integralmax)
{
pp->Integralmax = Integralmax;//积分阈值?
pp->outputmax = outputmax;//输出阈值
pp->Proportion = Kp;
pp->Integral = Ki;
pp->Derivative = Kd;
pp->DError = pp->Error = pp->output = pp->LastError = pp->PrevError = 0;
}
LSS_PID.h
#ifndef __LSS_PID_H
#define __LSS_PID_H
typedef struct PID {
float Proportion; // Proportional Const
float Integral; // Integral Const
float Derivative; // Derivative Const
float PrevError; // Error[-2]
float LastError; // Error[-1]
float Error;
float DError;
float SumError; // Sums of Errors
float Integralmax;
float output;
float outputmax;
} PID;
extern PID M3508_spid[8];
extern PID M3508_apid[8];
float abs_limit(float a, float ABS_MAX);
void PID_Position_Calc( PID *pp, float CurrentPoint, float NextPoint);
void PID_Incremental_Calc( PID *pp, float CurrentPoint, float NextPoint);
void PIDInit(PID *pp, float Kp , float Ki , float Kd , float outputmax, float Integralmax);
#endif
LSS_3508.c
#include "LSS_3508.h"
//1-5 can1
//6-8 can2
//偏航角 ID 5
//俯仰角 ID 2
//翻转角 ID 3
//推进 ID 4
struct M3508_Fback_Inf m3508_fback_inf[1] =
{
{0x201,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
};
//初始化m3508控制接口
void init_m3508_can(void)
{
CAN_FilterTypeDef hCAN_Filter;
//CAN1滤波器
hCAN_Filter.FilterBank = 0;
hCAN_Filter.SlaveStartFilterBank = 0;
hCAN_Filter.FilterMode = CAN_FILTERMODE_IDMASK;
hCAN_Filter.FilterScale = CAN_FILTERSCALE_32BIT;
hCAN_Filter.FilterIdHigh = 0;
hCAN_Filter.FilterIdLow = 0;
hCAN_Filter.FilterMaskIdHigh = 0;
hCAN_Filter.FilterMaskIdLow = 0;
hCAN_Filter.FilterFIFOAssignment = CAN_RX_FIFO0;
hCAN_Filter.FilterActivation = ENABLE;
if(HAL_CAN_ConfigFilter(&hcan1, &hCAN_Filter) != HAL_OK){
Error_Handler();
}
if(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK){
Error_Handler();
}
if(HAL_CAN_Start(&hcan1) != HAL_OK){
Error_Handler();
}
}
//执行时间5us
//发送m3508控制电流
void send_m3508_control_current(void)
{
uint8_t can_tx_buffer[8];
uint32_t CAN_TX_BOX0;
CAN_TxHeaderTypeDef hCAN_TxHeader;//CAN发送消息
hCAN_TxHeader.StdId = 0x200;
hCAN_TxHeader.IDE = CAN_ID_STD;
hCAN_TxHeader.RTR = CAN_RTR_DATA;
hCAN_TxHeader.DLC = 8;
can_tx_buffer[0] = m3508_fback_inf[0].control_current >> 8;
can_tx_buffer[1] = m3508_fback_inf[0].control_current;
can_tx_buffer[2] = 0;
can_tx_buffer[3] = 0;
can_tx_buffer[4] = 0;
can_tx_buffer[5] = 0;
can_tx_buffer[6] = 0;
can_tx_buffer[7] = 0;
HAL_CAN_AddTxMessage(&hcan1, &hCAN_TxHeader, can_tx_buffer, &CAN_TX_BOX0);
}
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
uint8_t can_rx_buffer[8];
CAN_RxHeaderTypeDef hCAN_RxHeader;
if(hcan->Instance == hcan1.Instance)
{
HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &hCAN_RxHeader, can_rx_buffer);
switch(hCAN_RxHeader.StdId)
{
case 0x201 :
{
m3508_fback_inf[0].angle = can_rx_buffer[0] << 8 | can_rx_buffer[1];//电机角度
m3508_fback_inf[0].speed = can_rx_buffer[2] << 8 | can_rx_buffer[3];//电机速度
m3508_fback_inf[0].torque_current = (can_rx_buffer[4] << 8 | can_rx_buffer[5]);//反馈转矩电流
// m3508_fback_inf[0].temperature = can_rx_buffer[6];//电机温度
if(m3508_fback_inf[0].frist_flag == 1)//上电标志为1
{
if(m3508_fback_inf[0].frist_cnt ++ <= 150)//上电计数
{
m3508_fback_inf[0].fris_angle = m3508_fback_inf[0].angle;//上电角度等于电机角度值
m3508_fback_inf[0].frist_flag = 0;//上电标志为0
}
if(m3508_fback_inf[0].angle > 4096)//bug 初始值大于4096 会轮转一圈
m3508_fback_inf[0].round_cnt = 1;
}
else
{
if(m3508_fback_inf[0].angle - m3508_fback_inf[0].last_angle > 4096)//电机角度 - 上一次电机角度
m3508_fback_inf[0].round_cnt --;//电机旋转圈数自减
else if(m3508_fback_inf[0].angle - m3508_fback_inf[0].last_angle < -4096)
m3508_fback_inf[0].round_cnt ++;//电机旋转圈数自加
m3508_fback_inf[0].last_angle = m3508_fback_inf[0].angle;//上一次电机角度等于电机角度
m3508_fback_inf[0].total_angle = m3508_fback_inf[0].round_cnt * 8192 + m3508_fback_inf[0].angle - m3508_fback_inf[0].fris_angle;//电机总转角度
}
}break;
}
}
}
/***输入角度输出脉冲数****/
int32_t m3508_Ang2Cnt(float ang)
{
int32_t cnt = CNT_PER_ROUND_OUT / 360.0f * ang;
return cnt;
}
/****输入脉冲数输出角度****/
float m3508_Cnt2Ang(int32_t cnt)
{
return (float) ((cnt * 360.0f) / CNT_PER_ROUND_OUT);
}
LSS_3508.h
#ifndef _LSS_3508_H_
#define _LSS_3508_H_
#include "stm32f4xx_hal.h"
#include "can.h"
#define CNT_PER_ROUND 8192 //外转子转一圈的脉冲数
#define REDUCTION_RATIO 19.0f //减速比
#define CNT_PER_ROUND_OUT (CNT_PER_ROUND * REDUCTION_RATIO) //电机轴转一圈的脉冲数
#define CNT_PER_ROUND1 8192 //外转子转一圈的脉冲数
#define REDUCTION_RATIO1 36.0f //减速比
#define CNT_PER_ROUND_OUT1 (CNT_PER_ROUND1 * REDUCTION_RATIO1) //电机轴转一圈的脉冲数
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#define CLAMP(x, lower, upper) (MIN(upper, MAX(x, lower)))
#define lift_max 100
#define lift_min 0
#define close_max 90
#define close_min -90
struct M3508_Fback_Inf
{
uint32_t id;//电机id
uint8_t frist_flag;//上电标志
uint16_t frist_cnt;//上电计数
int16_t fris_angle;//上电角度
int16_t angle;//电机角度
int16_t last_angle;//上一次电机角度
int32_t round_cnt;//电机旋转圈数
int64_t total_angle;//电机总转角度
int64_t set_angle;//设置电机目标角度
uint8_t speed_cnt_time;
uint8_t speed_cnt;
int32_t last_total_angle[2];//上一次电机总转角度
int16_t speed;//电机速度
int16_t torque_current;//反馈转矩电流
int16_t torque_rated;//设定转矩电流
int16_t set_speed;//设置速度
int16_t control_current;//电机控制电流
uint8_t temperature;//电机温度
};
extern struct M3508_Fback_Inf m3508_fback_inf[];
//初始化m3508控制接口
void init_m3508_can(void);
//发送m3508控制电流
void send_m3508_control_current(void);
/***输入角度输出脉冲数****/
int32_t m3508_Ang2Cnt(float ang);
/****输入脉冲数输出角度****/
float m3508_Cnt2Ang(int32_t cnt);
#endif
main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2022 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 "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "LRJ_3508.h"
#include "LRJ_PID.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
int16_t ref_speed,inf_speed;
int16_t pid_output;
/* 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 */
/* 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();
/* USER CODE BEGIN 2 */
//ref_speed = 1000;
init_m3508_can();//开启过滤器
PIDInit(&M3508_spid[0],5,0,0,15000,15000);//速度环设置和限幅 spid:15 0.5 0
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
PID_Incremental_Calc(&M3508_spid[0], m3508_fback_inf[0].speed, ref_speed);//速度环
m3508_fback_inf[0].control_current = M3508_spid[0].output;
send_m3508_control_current();
HAL_Delay(5);//有bug必须搞个5ms延时,或者开个定时器中断丢中断里头
/* USER CODE END WHILE */
/* 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 = 180;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Activate the Over-Drive mode
*/
if (HAL_PWREx_EnableOverDrive() != 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 */
/* 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 */
结束,挺简单的,有代码直接往上套用即可,不需要配置其他乱七八糟的东西,为了凑字数,随便唠点开发事故吧。为什么文章标题叫做A板杀手,因为吾有一友,拿4s电池怼到12v输出接口,霎时间,烟雾弥漫在我的工位上..........顾不上手上窜着的火花,立马将电池从分电板上拔下,索性,A版的防反接保护起作用了,要不然就不只是烧两根电线这么简单的事了。一阵喧嚣过后,我那兄弟手还不断颤抖,嘴里不停念叨着,完了完了,昨天烧电机,今天烧板子(我当时绷不住了)。那兄弟接反这个A板的电源,倒是也情有可原,之前他用的都是c板,a板没怎么用过,c板上的xt30都是相互导通的,没有什么输入端输出端之分,相当于集成了一个分电板,所以也没这么多讲究,这原罪还是得怪在DJI头上,搞这么多的坑钱东西在板子上,A板和C板在差异化上又这么不明显,就像是A板是初代,C板才是改进过的版本一样。好在还有个保护电路的设计,这一点确实不错。好了就说这么些了,下一篇我就跟新速度环和位置环的串级PID,敬请期待