自学笔记3

这个仅仅是零碎的知识点,还没有总结,总结将会在二月二十五号开始,到时候所有的自学笔记我会完全整理成步骤,到时候会系统的学习,另外这个笔记是学习利用cube使用stm32,而且学习的主要目的是robomasrter,所以出现的大部分历程都是RM的

1月13日

1 机器人状态.h文件

本代码用于定义机器人状态量
#ifndef _ROBOT_H_
#define _ROBOT_H_
//以上一般定义时很有用


#define CHASSIS     //底盘

#define PREPARE_TIME 10   //准备时间

//  include "pid.h"     //pid function
//这个以后补上

//工作及控制状态
enum WorkState_e
{
   STOP,                    //停止状态	
   PREPARE,                 //准备状态
   RemoteControl,           //遥控器控制状态
   MouseKeyControl,         //鼠标键盘控制状态
   AutoControl              //自动控制状态
};


//底盘结构体
struct Chassis_t  
{
     int FBSpeed;
     int LRSoeed;
     int RoboteAngle;
};


//角度结构体
struct Gimbal_t
{
   int PitchAngle;           //俯仰角
   int PitchBiasAngle;       //
   int YawAngle;             // 后面的注有说到
   int YawBiasAngle;         //
};


//射击状态
enum ShootState_e
{
   StopShoot,          //停止设计
   SingleShoot,        //点射
   ContinuousShoot     //连发
};


//射击具体参数结构体
struct Shoot_t
{
   int ShootSpeed;                   //射击速度
   int FireRate;                     //开火频率
   enum ShootState_e shootstate;     //射击状态
};



//总的机器人结构体,把之前的用结构体涵盖
struct Robot_t
{
   enum WorkState_e workstate;                       //定义工作状态
   enum WorkState_e last_workstate;                  //定义最后时刻工作状态
   struct Chassis_t chassis ;                        //定义底盘状态
   struct Gimbal_t gimbal;                           //定义角度状态
   struct Shoot_t shoot;                             //定义设计状态 
};



void RobotParamInit(void);
//此处加这个函数的目的是在引用该.h文件时可以直接用这个函数,类似于给它一个接口
#endif


注:
来源于百度

2.PID各变量定义(pid.h)

#ifndef _PID_H_
#define _PID_H_


struct PID_t
{
  float KP;
	float KI;
	float KD;
	int error[2];
	int error_sum;
	int error_max;
	int fdb; // feedback
	int ref;
	int output;
	int outputMax;
};

#define DEFUALT_PID \
{0,0,0,{0,0},0,0,0,0,0,0}
//此处的“\”用于行,因为有些句子太长需要换行,而且在\前面需要有个空格

void PID_Cale (struct PID_t *pid);

#endif

3.具体pid代码(pid.c)


#include "pid.h"
//将所需要的结构体定义包含在该.c文件中

//定义PID计算函数
void PID_Calc(struct PID_t *pid)
{
      pid->error[0] = pid->error[1];
	  pid->error[1] = pid->ref-pid->fdb;
	  pid->error_sum+=pid->error[1];
	  //将偏差求和,用于求积分I
	
	//积分上限,防止积分累计过大导致失去控制
	if(pid->error_sum > pid->error_max ) pid->error_sum = pid->error_max;
	if(pid->error_sum < -pid->error_max) pid->error_sum = -pid->error_max;
	
	//具体积分式子
	pid->output = pid->KP * pid->error[1] + pid->KI * pid->error_sum + pid->KD * (pid->error[1] - pid->error[0]);
	
	//输出上下限,防止过量输出
	if(pid->output > pid->outputMax)  pid->output  = pid->outputMax ;
	if(pid->output <-pid->outputMax)  pid->output = -pid->outputMax ;
}

4.can通讯具体操作步骤

1.查看单片机引脚信息,找到can通讯相关引脚,例如RM开发板(以STM32F427II6) 取自RM开发手册
2.然后在cub中找到相应的引脚,并使能
[图片]
3.接下来就要开始配置时钟了
这里一般保证不过载问题就不大(由于我学识有限,还不知道如何配置最合理,就暂时随便配,等以后熟系了会更新)
在这里插入图片描述
对这个图,我们设置can通讯应该注意APB1,因为这个时钟包含了can通讯,为42MHZ
4.然后选择中断在这里插入图片描述
根据需求选择中断,由于我们只需要在接收到数据的时候触发一个中断,所以这里只需要一个RX中断即可
5.然后需要根据所需的波特率配置波特率,其中波特率信息一般在你用can通信的原件说明书上有,这里的例子是RM3510的电机的说明书在这里插入图片描述
这里的要求是需要can总线的比特率为1Mbps,
在这里插入图片描述
如图所示,prescarler是预分频
42/((1+4+9)*3)=1

接下里就到keil中
6.先是电机相关数据的结构体定义(can_motor.h)

#ifndef _CAN_MOTOR_H_
#define _CAN_MOTOR_H_

#include "can.h"
#include "pid.h"
#include "robot.h"

#define CAN_MOTOR1_ID 0x205


#define CAN_BUF_SIZE 6
struct CAN_Motor
{
  int fdbPosition;           //电机的编码器的反馈值
  int last_fdbPosition;      //电机上次的编码器的反馈值
  int bias_position;         //机器人初始状态电机位置环设定值
  int fdbSpeed;              //电机反馈的转速
	int round;               //电机转过的圈数
  int real_position;         //过零处理后的电机转子
  int diff;                  //本次与上次电机过零处理后的编码器反馈值之差
  int velocity;              //根据电机编码器计算得到的速度
  int can_buf[CAN_BUF_SIZE]; //电机编码器值缓存数组
  int buf_count;             //缓存数组计数,用于后面计算电机平均速度
  struct PID_t position_pid; //电机位置环PID
  struct PID_t speed_pid;    //电机速度环PID
};

#define DEFAULT_CAN_NOTOR {0,0,0,0,0,0,0,0,{0},0,DEFUALT_PID,DEFAULT_PID}

#ifdef CHASSIS
void CanDataEncoderProcess(struct CAN_Motor *motor);
void CanDataReveive (int motor_index);
void CanTransmit_1234(CAN_HandleTypeDef *hcanx,int16_t cml_iq, int16_t cm2_iq, int16_t cm3_iq, int16_t cm4_iq);
extern struct CAN_Motor m3510_1;//定义m3501电机的数据结构体
#endif
HAL_StatusTypeDef CanFilterInit(CAN_HandleTypeDef* hcan);//给.c文件中写的过滤器一个接口,这样在main函数中好引入
#endif

7.然后是电机数据的处理以及can通讯的收发和初始化(can_motor.c)

#include "can_motor.h"         //将.h中的结构体引入到该.c中

uint8_t CanReceiveData[8];  //can接收的电机反馈信息

#ifdef CHASS    //如果定义了底盘,则定义底盘电机的参数结构体

struct CAN_Motor m3510_1 = DEFAULT_CAN_MOTOR;

#endif


/**
* @brief 根据电机信息的ID号进行对应的数据解析
* @param 电机ID号
* @retval None
*/
//且该函数是中断触发的
void CanDataReveive (int motor_index)
{
switch(motor_index)
{
     #ifdef CHASSIS
     case CAN_MOTOR1_ID: 
  	CanDataEncoderProcess(&m3510_1); //电机具体解析函数
  	 break;
     
     #endif
 }     
}



/**
* @brief CAN通信电机的反馈数据具体解析函数
* @param 电机数据结构体
* @retval None
*/
void CanDataEncoderprocess(struct CAN_Motor *motor)
{
  int i=0;
  int32_t temp_sum = 0;
  
  motor->last_fdbPosition = motor->fdbPosition ;
  motor->fdbPosition = CanReceiveData [0]<<8|CanReceiveData [1];
  motor->fdbSpeed = CanReceiveData[2]<<8|CanReceiveData [3];
//此处的移位操作在电机说明书里明确标出,具体会写在后面
  
  /*电机位置数据过零处理,避免出现位置突变,电机频率为1000hz,也就是周期是1ms,但它每个周期最多转800左右(相对数据)
  所以发生了跃变,一定是转过一圈*/
  if(motor->fdbPosition - motor->last_fdbPosition >4096)
  {
  	motor->round --;
  	motor->diff = motor->fdbPosition  - motor->last_fdbPosition - 8192;
  }else if (motor -> fdbPosition - motor->last_fdbPosition < -4096)
  {
  	motor->round ++;
  motor->diff = motor->fdbPosition - motor->last_fdbPosition + 8192;
  }
  else
  {
  motor->diff = motor->fdbPosition - motor->last_fdbPosition;
  }
      motor->real_position = motor->fdbPosition + motor->round * 8192;
      motor->can_buf[motor->buf_count++] = motor->diff;//先算括号里的


//用于如果数据存满了就使之清零

  if(motor->buf_count == CAN_BUF_SIZE)
  {
  	motor->buf_count = 0;
  }

//计算编码器值好在后面求平均
  for(i =0; i<CAN_BUF_SIZE; i++)
  {
  	temp_sum +=motor->can_buf[i];
  }
  motor->velocity = temp_sum/CAN_BUF_SIZE ;
  
  //将电机速度反馈值有无符号整型转变为有符号整形
  if(motor->fdbPosition >32768)
  {
  	motor->fdbSpeed -=65536;
  }
}


/**
* @brief ID为1~4的电机信号发送函数
* @param ID为1~4的各个电机的电流数值
* @retval None
*/
void CanTransmit_1234(CAN_HandleTypeDef *hcanx,int16_t cml_iq, int16_t cm2_iq, int16_t cm3_iq, int16_t cm4_iq)
{
  CAN_TxHeaderTypeDef  TxMessage;
  TxMessage.DLC=0x08;
  TxMessage.StdId=0x200;
  TxMessage.IDE=CAN_ID_STD;
  TxMessage.RTR=CAN_RTR_DATA;
  uint8_t TxData[8];
  
  TxData[0] = (uint8_t)(cml_iq >> 8);//电压给定高八位
  TxData[1] = (uint8_t)cml_iq;//电压给定低八位
if(HAL_CAN_AddTxMessage(hcanx,&TxMessage,TxData,(uint32_t*)CAN_TX_MAILBOX0)!=HAL_OK)
  {
  	 Error_Handler();       //如果CAN信息发送失败则进入死循环
  }
}

/**
* @brief CAN外设过滤器初始化
* @param can结构体
* @retval None
*/
HAL_StatusTypeDef CanFilterInit(CAN_HandleTypeDef* hcan)
{
CAN_FilterTypeDef  sFilterConfig;

sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000;
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.SlaveStartFilterBank = 14;

  if(hcan == &hcan1)
  {
  	sFilterConfig.FilterBank = 0;
  }
/*	if(hcan == &hcan2)
  {
  	sFilterConfig.FilterBank = 14;
  }
  */
if(HAL_CAN_ConfigFilter(hcan, &sFilterConfig) != HAL_OK)
{
  Error_Handler();
}

if (HAL_CAN_Start(hcan) != HAL_OK)
{
  Error_Handler();
}
  
if (HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
{
  Error_Handler();
}

  return HAL_OK;
}




猜你喜欢

转载自blog.csdn.net/weixin_44109556/article/details/86408072
今日推荐