小白STM32CAN控制3508大疆电机学习

小白博主最近在学习3508,以下是自己的学习笔记,欢迎大家给博主指出错误,博主感激不尽!!

大疆的代码运用了操作系统,暂时用不上。所以本文完全没提到。不需要使用的时候把大疆demo里面操作系统注释掉即可。

/*can.c注意!!!!!这是老的hal库!!!博主有个同学因为没发现这个问题改了七遍代码
*/
void MX_CAN1_Init(void)
{

  hcan1.Instance = CAN1;
  hcan1.Init.Prescaler = 5;/*设置分频系数*/
  hcan1.Init.Mode = CAN_MODE_NORMAL;/*普通模式,回环模式*/
  hcan1.Init.SJW = CAN_SJW_1TQ;/*此处可以决定can的波特率*/
  hcan1.Init.BS1 = CAN_BS1_3TQ;
  hcan1.Init.BS2 = CAN_BS2_5TQ;/*波特率计算公式=APB1总线HZ/[(TS1+TS2+1)*BRP]*/
  hcan1.Init.TTCM = DISABLE;/*非时间触发通信模式*/
  hcan1.Init.ABOM = ENABLE;
  hcan1.Init.AWUM = DISABLE;
  hcan1.Init.NART = DISABLE;
  hcan1.Init.RFLM = DISABLE;
  hcan1.Init.TXFP = DISABLE;
/*以上均为hal库初始化配置*/
  if (HAL_CAN_Init(&hcan1) != HAL_OK)
  {
    Error_Handler();
  }

}
/*首先先对can进行参数初始化设置
/*这个会被HAL_CAN-init调用*/
void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(canHandle->Instance==CAN1)/*用于判断是哪个can口~以免后面配置多个can*/
  {

     /*开时钟*/
    HAL_RCC_CAN1_CLK_ENABLED++;
    if(HAL_RCC_CAN1_CLK_ENABLED==1){
      __HAL_RCC_CAN1_CLK_ENABLE();
    }
   
    /*根据自己使用的板子改引脚*/
    GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    
    HAL_NVIC_SetPriority(CAN1_TX_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(CAN1_TX_IRQn);
    HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
    /*中断配置*/
   }
  
}



void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
{

  if(canHandle->Instance==CAN1)
  {
 
 
    HAL_RCC_CAN1_CLK_ENABLED--;
    if(HAL_RCC_CAN1_CLK_ENABLED==0){
      __HAL_RCC_CAN1_CLK_DISABLE();
   }
  
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);

    /* Peripheral interrupt Deinit*/
    HAL_NVIC_DisableIRQ(CAN1_TX_IRQn);

    HAL_NVIC_DisableIRQ(CAN1_RX0_IRQn);

   
}

以上,初始化的配置就结束啦!博主现在只会最最基础的,接下来是滤波器的配置

博主用的是FIFO0

简单地说它就是一个控制接收发送的邮箱

/*接下来我们看到can滤波器的配置*/

void my_can_filter_init_recv_all(CAN_HandleTypeDef* _hcan)
{

	CAN_FilterConfTypeDef		CAN_FilterConfigStructure;
	static CanTxMsgTypeDef		Tx1Message;
	static CanRxMsgTypeDef 		Rx1Message;
	
	CAN_FilterConfigStructure.FilterNumber = 0;
	CAN_FilterConfigStructure.FilterMode = CAN_FILTERMODE_IDMASK;
	CAN_FilterConfigStructure.FilterScale = CAN_FILTERSCALE_32BIT;
	CAN_FilterConfigStructure.FilterIdHigh = 0x0000;/*32位的ID值*/
	CAN_FilterConfigStructure.FilterIdLow = 0x0000;
	CAN_FilterConfigStructure.FilterMaskIdHigh = 0x0000;/*32位的MASK值*/
	CAN_FilterConfigStructure.FilterMaskIdLow = 0x0000;
	CAN_FilterConfigStructure.FilterFIFOAssignment = CAN_FilterFIFO0;
	CAN_FilterConfigStructure.BankNumber = 14;//can1(0-13)和can2(14-27)分别得到一半的filter
	CAN_FilterConfigStructure.FilterActivation = ENABLE;

	if(HAL_CAN_ConfigFilter(_hcan, &CAN_FilterConfigStructure) != HAL_OK)
	{
		//err_deadloop(); 
    }
//滤波器初始化

	CAN_FilterConfigStructure.FilterNumber = 14;
	if(HAL_CAN_ConfigFilter(_hcan, &CAN_FilterConfigStructure) != HAL_OK)
	{
	//err_deadloop();
	}

	if(_hcan == &hcan1){
		_hcan->pTxMsg = &Tx1Message;
/*注意这两个结构体所指向的ptxmsg和prxmsg!!!后面会经常用到*/
		_hcan->pRxMsg = &Rx1Message;
	}

	

}

关于滤波器的解释,以下取自正点原子:(就是上面所说的id值)

举个简单的例子,我们设置过滤器组 0 工作在:1 个 32 为位过滤器-标识符屏蔽模式,然 后设置 CAN_F0R1=0XFFFF0000,CAN_F0R2=0XFF00FF00。其中存放到 CAN_F0R1的值就是 期望收到的 ID,即我们希望收到的映像(STID+EXTID+IDE+RTR)最好是:0XFFFF0000。 而 0XFF00FF00 就是设置我们需要必须关心的 ID,表示收到的映像,其位[31:24]和位[15:8]这 16 个位的必须和 CAN_F0R1 中对应的位一模一样,而另外的 16 个位则不关心,可以一样,也 可以不一样,都认为是正确的 ID,即收到的映像必须是 0XFFxx00xx,才算是正确的(x 表示 不关心)。这里需要注意的是标识符选择位 IDE 和帧类型 RTR 需要一致

void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* _hcan)
{
	
    __HAL_CAN_ENABLE_IT(&hcan1, CAN_IT_FMP0); 
	switch(_hcan->pRxMsg->StdId){
		/*这里涉及到电调的调节*/
		case CAN_3510Moto2_ID:/*这个是电调2*/
		
			{
				static u8 i;
				i = _hcan->pRxMsg->StdId - CAN_3510Moto1_ID;
				/*i其实就是对应的电调值*/
				moto_chassis[i].msg_cnt++ <= 50	?	    get_moto_offset(&moto_chassis[i],_hcan) :get_moto_measure(&moto_chassis[i], _hcan);
/*这里应该是等待电流值稳定再调用get——moto——measure函数*/
				get_moto_measure(&moto_info, _hcan);
				//get_moto_measure(&moto_chassis[i], _hcan);
			}
			break;
		
		
	}
		

	/*再次使能去解决智能收到一次id的问题*/
	__HAL_CAN_ENABLE_IT(&hcan1, CAN_IT_FMP0);
}
	
void get_moto_measure(moto_measure_t *ptr, CAN_HandleTypeDef* hcan)
{

	ptr->last_angle = ptr->angle;
	ptr->angle = (uint16_t)(hcan->pRxMsg->Data[0]<<8 | hcan->pRxMsg->Data[1]) ;
	ptr->real_current  = (int16_t)(hcan->pRxMsg->Data[2]<<8 | hcan->pRxMsg->Data[3]);
	ptr->speed_rpm = ptr->real_current;	//这里是因为两种电调对应位不一样的信息
	ptr->given_current = (int16_t)(hcan->pRxMsg->Data[4]<<8 | hcan->pRxMsg->Data[5])/-5;
	ptr->hall = hcan->pRxMsg->Data[6];
	if(ptr->angle - ptr->last_angle > 4096)
		ptr->round_cnt --;
	else if (ptr->angle - ptr->last_angle < -4096)
		ptr->round_cnt ++;
	ptr->total_angle = ptr->round_cnt * 8192 + ptr->angle - ptr->offset_angle;
}

通过以上的代码,可以获取到电机现在的各种值

其中ptr就是这个结构体

typedef struct{
	int16_t	 	speed_rpm;
    int16_t  	real_current;
    int16_t  	given_current;
    uint8_t  	hall;
	uint16_t 	angle;				//abs angle range:[0,8191]
	uint16_t 	last_angle;	//abs angle range:[0,8191]
	uint16_t	offset_angle;
	int32_t		round_cnt;
	int32_t		total_angle;
	u8			buf_idx;
	u16			angle_buf[FILTER_BUF_LEN];
	u16			fited_angle;
	u32			msg_cnt;
}moto_measure_t;

以下是用于设定电机的电流值通过can发送过去的东西,这里用到了四个电流参数形成了一个pid环

关于这里的stdId值,这是0x202对应的就是第二个电调。

要改变这里的电调值来控制不同的电调

void set_moto_current(CAN_HandleTypeDef* hcan, s16 iq1, s16 iq2, s16 iq3, s16 iq4){

	hcan->pTxMsg->StdId = 0x202;
	hcan->pTxMsg->IDE = CAN_ID_STD;
	hcan->pTxMsg->RTR = CAN_RTR_DATA;
	hcan->pTxMsg->DLC = 0x08;
	hcan->pTxMsg->Data[0] = iq1 >> 8;
	hcan->pTxMsg->Data[1] = iq1;
	hcan->pTxMsg->Data[2] = iq2 >> 8;
	hcan->pTxMsg->Data[3] = iq2;
	hcan->pTxMsg->Data[4] = iq3 >> 8;
	hcan->pTxMsg->Data[5] = iq3;
	hcan->pTxMsg->Data[6] = iq4 >> 8;
	hcan->pTxMsg->Data[7] = iq4;
	
	HAL_CAN_Transmit(hcan, 1000);
}	
static HAL_StatusTypeDef CAN_Receive_IT(CAN_HandleTypeDef* hcan, uint8_t FIFONumber)
{
  /* Get the Id */
  hcan->pRxMsg->IDE = (uint8_t)0x04U & hcan->Instance->sFIFOMailBox[FIFONumber].RIR;
  if (hcan->pRxMsg->IDE == CAN_ID_STD)
  {
    hcan->pRxMsg->StdId = (uint32_t)0x000007FFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RIR >> 21U);
  }
  else
  {
    hcan->pRxMsg->ExtId = (uint32_t)0x1FFFFFFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RIR >> 3U);
  }
  
  hcan->pRxMsg->RTR = (uint8_t)0x02U & hcan->Instance->sFIFOMailBox[FIFONumber].RIR;
  /* Get the DLC */
  hcan->pRxMsg->DLC = (uint8_t)0x0FU & hcan->Instance->sFIFOMailBox[FIFONumber].RDTR;
  /* Get the FMI */
  hcan->pRxMsg->FMI = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDTR >> 8U);
  /* Get the data field */
  hcan->pRxMsg->Data[0U] = (uint8_t)0xFFU & hcan->Instance->sFIFOMailBox[FIFONumber].RDLR;
  hcan->pRxMsg->Data[1U] = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDLR >> 8U);
  hcan->pRxMsg->Data[2U] = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDLR >> 16U);
  hcan->pRxMsg->Data[3U] = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDLR >> 24U);
  hcan->pRxMsg->Data[4U] = (uint8_t)0xFFU & hcan->Instance->sFIFOMailBox[FIFONumber].RDHR;
  hcan->pRxMsg->Data[5U] = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDHR >> 8U);
  hcan->pRxMsg->Data[6U] = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDHR >> 16U);
  hcan->pRxMsg->Data[7U] = (uint8_t)0xFFU & (hcan->Instance->sFIFOMailBox[FIFONumber].RDHR >> 24U);
  /* Release the FIFO */
  /* Release FIFO0 */
  if (FIFONumber == CAN_FIFO0)
  {
    __HAL_CAN_FIFO_RELEASE(hcan, CAN_FIFO0);
    
    /* Disable FIFO 0 message pending Interrupt */
    __HAL_CAN_DISABLE_IT(hcan, CAN_IT_FMP0);
  }
  /* Release FIFO1 */
  else /* FIFONumber == CAN_FIFO1 */
  {
    __HAL_CAN_FIFO_RELEASE(hcan, CAN_FIFO1);
    
    /* Disable FIFO 1 message pending Interrupt */
    __HAL_CAN_DISABLE_IT(hcan, CAN_IT_FMP1);
  }
  
  if(hcan->State == HAL_CAN_STATE_BUSY_RX)
  {   
    /* Disable Error warning Interrupt */
    __HAL_CAN_DISABLE_IT(hcan, CAN_IT_EWG);
    
    /* Disable Error passive Interrupt */
    __HAL_CAN_DISABLE_IT(hcan, CAN_IT_EPV);
    
    /* Disable Bus-off Interrupt */
    __HAL_CAN_DISABLE_IT(hcan, CAN_IT_BOF);
    
    /* Disable Last error code Interrupt */
    __HAL_CAN_DISABLE_IT(hcan, CAN_IT_LEC);
    
    /* Disable Error Interrupt */
    __HAL_CAN_DISABLE_IT(hcan, CAN_IT_ERR);
  }
  
  if(hcan->State == HAL_CAN_STATE_BUSY_TX_RX) 
  {
    /* Disable CAN state */
    hcan->State = HAL_CAN_STATE_BUSY_TX;
  }
  else
  {
    /* Change CAN state */
    hcan->State = HAL_CAN_STATE_READY;
  }

  /* Receive complete callback */ 
  HAL_CAN_RxCpltCallback(hcan);/*注意这里!!!!!!!调用了我们上面定义的callback函数*/

  /* Return function status */
  return HAL_OK;
}

HAL库里面的函数。

在HAL_CAN_recieve_IT里面没有调用callback函数,但是在can_recieve_it里面调用了。

方法2:正常的方法,不需自己在hal库里面开启中断:

使用公共处理函数

void CAN1_RX0_IRQHandler(void)
{
  /* USER CODE BEGIN CAN1_RX0_IRQn 0 */

  /* USER CODE END CAN1_RX0_IRQn 0 */
  HAL_CAN_IRQHandler(&hcan1);
  /* USER CODE BEGIN CAN1_RX0_IRQn 1 */

  /* USER CODE END CAN1_RX0_IRQn 1 */
}

后面就是讲解pid的部分,pid算法用于稳定电机输出的电流值和转动的速度。简单来说就是把参数丢进去它能帮你计算,让你的电机稳定的转动。

static void pid_param_init(
    pid_t *pid, 
    uint32_t mode,
    uint32_t maxout,
    uint32_t intergral_limit,
    float 	kp, 
    float 	ki, 
    float 	kd)

kp,ki,kd三个参数分别对应比例常数,微分常数,积分常数

float pid_calc(pid_t* pid, float get, float set){
    pid->get[NOW] = get;
    pid->set[NOW] = set;
    pid->err[NOW] = set - get;	//set - measure
    if (pid->max_err != 0 && ABS(pid->err[NOW]) >  pid->max_err  )
		return 0;
	if (pid->deadband != 0 && ABS(pid->err[NOW]) < pid->deadband)
		return 0;
    
    if(pid->pid_mode == POSITION_PID) //位置式p
    {
        pid->pout = pid->p * pid->err[NOW];
        pid->iout += pid->i * pid->err[NOW];
        pid->dout = pid->d * (pid->err[NOW] - pid->err[LAST] );
        abs_limit(&(pid->iout), pid->IntegralLimit);
        pid->pos_out = pid->pout + pid->iout + pid->dout;
        abs_limit(&(pid->pos_out), pid->MaxOutput);
        pid->last_pos_out = pid->pos_out;	//update last time 
    }
    else if(pid->pid_mode == DELTA_PID)//增量式P
    {
        pid->pout = pid->p * (pid->err[NOW] - pid->err[LAST]);
        pid->iout = pid->i * pid->err[NOW];
        pid->dout = pid->d * (pid->err[NOW] - 2*pid->err[LAST] + pid->err[LLAST]);
        
        abs_limit(&(pid->iout), pid->IntegralLimit);
        pid->delta_u = pid->pout + pid->iout + pid->dout;
        pid->delta_out = pid->last_delta_out + pid->delta_u;
        abs_limit(&(pid->delta_out), pid->MaxOutput);
        pid->last_delta_out = pid->delta_out;	//update last time
    }
    
    pid->err[LLAST] = pid->err[LAST];
    pid->err[LAST] = pid->err[NOW];
    pid->get[LLAST] = pid->get[LAST];
    pid->get[LAST] = pid->get[NOW];
    pid->set[LLAST] = pid->set[LAST];
    pid->set[LAST] = pid->set[NOW];
    return pid->pid_mode==POSITION_PID ? pid->pos_out : pid->delta_out;
//	
}

通过pid计算前后值,设置两个-结构体用于存储pid速度和电流值,用于pid_calc中调用

pid_t pid_speed[4];		   //电机速度PID环
pid_t pid_position[4];	   //电机电流PID环
moto_measure_t moto_chassis[4] = {0};

上面这个值会被HAL_CAN_RxCpltCallback调用获取电机的参数值,再传入pid中用于计算,最后得到设置电机的值

需要注意的几点:

1.不同板子代码的移植,比如这个时钟在stm32f429中就无法正常使用(暂时没去找原因)。

2.canreceive_it和hal_receive_it的区别

3.电调id值的设定

4.新老hal库的冲突
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值