sathishkumar
Published © Apache-2.0

I Built a Drone Where the PCB Frame IS the Circuit Board

Designing a quadcopter where the PCB arms are the airframe - what I learned about rigidity, PID tuning, and flying something I built myself.

IntermediateFull instructions providedOver 4 days38
I Built a Drone Where the PCB Frame IS the Circuit Board

Things used in this project

Hardware components

ESP32
Espressif ESP32
×1
Anu PCB Drone
×1
Inertial Measurement Unit (IMU) (6 deg of freedom)
Inertial Measurement Unit (IMU) (6 deg of freedom)
×1
Core le
×1
Battery, 3.7 V
Battery, 3.7 V
×1

Software apps and online services

Arduino IDE
Arduino IDE
Edge Impulse Studio
Edge Impulse Studio

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Mastech MS8217 Autorange Digital Multimeter
Digilent Mastech MS8217 Autorange Digital Multimeter

Story

Read more

Schematics

schematics

Code

code

C/C++
#include <FU68xx.h>
#include "foc.h"
#include "foc-cfg.h"
#include "adc.h"
#include "q15-math.h"
#include "pu-quantities.h"

static struct q15_ramp_s m_vq_ramp_s;
static struct q15_ramp_s m_theta_comp_s;
static int16_t m_current_offset = 0;
static int16_t m_theta_comp;

void
foc_init(void)
{
  q15_ramp_init(&m_vq_ramp_s,_Q15(0.3),_Q15(0.000156));
  q15_ramp_init(&m_theta_comp_s,_Q15(0.0),1);
  m_theta_comp = THETA_COMP;
	/* deadtime set */
	TIM0_DTR = PWM_LOAD_DEADTIME;
	
	/*enable FOC register set*/	
	FOCEN	= 1;
  FOC_SET1 = (1<<FOCFR);
	
	CLR(FOC_SR,FUIF);
	SET(FOC_IER,FUIE);
		
	PFOC1 = 0;
	PFOC0 = 1;
		
	/* set FOC register */
	FOC_CR1 = 0;
  FOC_CR2 = 0;
	FOC_IDREF = 0;
	FOC_IQREF = 0;

	FOC_THETA = 0;
	FOC_RTHEACC = 0;
	FOC_RTHESTEP = 0;
	FOC_RTHECNT = 0;
	FOC_THECOMP = 0;
	FOC_THECOR = 2;

	FOC_ARR = PWM_VALUE_LOAD;
	FOC_POWKLPF = _Q15(0.00392699);
	
	/* current loop parameter set */
	FOC_DKP = D_KP;
	FOC_DKI = D_KI;
	FOC_DMAX = DOUTMAX;
	FOC_DMIN = DOUTMIN;
	
	FOC_QKP = Q_KP;
	FOC_QKI = Q_KI;
	FOC_QMAX = QOUTMAX;
	FOC_QMIN = QOUTMIN;
		
	/* estimate parameter set */
	FOC_EK1 = OBS_K1T;
	FOC_EK2 = OBS_K2T;
	FOC_EK3 = OBS_K3T;
	FOC_EK4 = OBS_K4T;

	FOC_FBASE = OBS_FBASE;
	FOC_OMEKLPF = SPEED_KLPF;
	FOC_EBMFK = OBS_KLPF;
	FOC_EKP = OBSW_KP_GAIN;
	FOC_EKI = OBSW_KI_GAIN;

  /* driver mode and active level set*/
	SET_BIT(FOC_CMR,CCPH,0);
	SET_BIT(FOC_CMR,CCPL,0);

	/* Estimate Algorithm PLL */
	FOC_KSLIDE = OBSE_PLLKP_GAIN;
  FOC_EKLPFMIN = OBSE_PLLKI_GAIN;
	
  /* single shunt mode */
	SET_BIT(FOC_CR1,CSM,0);
	FOC_TSMIN = PWM_TS_LOAD;
	FOC_TRGDLY = 2;
	SET_BIT(FOC_CR2,F5SEG,0);

  if (m_current_offset)
  {
    CLR(FOC_CHC,CSOC1);
    CLR(FOC_CHC,CSOC0);
    FOC_CSO = m_current_offset;
  }

  /* Enable PWM 6 channel output */
	SET_BIT(FOC_CMR,CC3EH,1);
	SET_BIT(FOC_CMR,CC3EL,1);
	SET_BIT(FOC_CMR,CC2EH,1);
	SET_BIT(FOC_CMR,CC2EL,1);
	SET_BIT(FOC_CMR,CC1EH,1);
  SET_BIT(FOC_CMR,CC1EL,1);
	
	/* enable I/O port output */
	P3_AN |= 0xC0;
	MOE = 1;
}

void
foc_motor_windmill_detect_init(void)
{
  FOCEN = 0;
  foc_init();

  FOC_IDREF = 0;
  FOC_IQREF = 0;

  FOC_EKP = OBSW_KP_GAIN_FREEWHEEL;
  FOC_EKI = OBSW_KI_GAIN_FREEWHEEL;

  FOC_CR2 |= (1<<ESCMS);
  /*sample shunt resister mode set*/
  FOC_CR1 	= 0x05;
  /*Estimate Algorithm set*/
  SET_BIT(FOC_CR1,ESEL,1);
}

void
foc_motor_start_align(int16_t angle)
{
  /* reset the foc module to clear the observer */
  FOCEN	= 0;
  foc_init();

  FOC_EKP = OBSW_KP_GAIN;
  FOC_EKI = OBSW_KI_GAIN;

  FOC_DKP = D_KP;
  FOC_DKI = D_KI;
  FOC_QKP = Q_KP;
  FOC_QKI = Q_KI;

  FOC_RTHEACC = 0x0000;
  FOC_RTHESTEP = 0x0000;
  FOC_THETA = angle;
  FOC_RTHECNT = 5;
  SET_BIT(FOC_CR1,EFAE,0);
  SET_BIT(FOC_CR1,RFAE,1);
  SET_BIT(FOC_CR1,ANGM,0);

  /*sample shunt resister mode set*/
  FOC_CR1 	= 0x05;

  /* exit wind milling detect mode */
  FOC_CR2 = 0x00;

  /*Estimate Algorithm set*/
  SET_BIT(FOC_CR1,ESEL,1);
}

void
foc_motor_start_open_loop(void)
{
  FOC_RTHEACC = OPEN_LOOP_THETA_ACC;
  FOC_RTHESTEP = OPEN_LOOP_START_SPEED;
  FOC_RTHECNT = THETA_OPEN_LOOP_CNT;

  FOC_EFREQACC = OPEN_LOOP_THETA_ACC;
  FOC_EFREQMIN = OPEN_LOOP_CUTIN_SPEED;
  FOC_EFREQHOLD = OPEN_LOOP_CUTIN_HOLD_SPEED;

  SET_BIT(FOC_CR1,EFAE,1);
  SET_BIT(FOC_CR1,RFAE,1);
  SET_BIT(FOC_CR1,ANGM,1);
}

void
foc_continue_open_loop(void)
{
  SET_BIT(FOC_CR1,EFAE,1);
  SET_BIT(FOC_CR1,RFAE,1);
  SET_BIT(FOC_CR1,ANGM,1);
}

void
foc_motor_windmill_start(void)
{
  q15_ramp_init(&m_vq_ramp_s,_Q15(0.9),_Q15(0.000156));
  FOC_EFREQACC = OPEN_LOOP_THETA_ACC;
  FOC_EFREQMIN = OPEN_LOOP_CUTIN_SPEED;
  FOC_EFREQHOLD = OPEN_LOOP_CUTIN_HOLD_SPEED;
  SET_BIT(FOC_CR1,EFAE,1);
  SET_BIT(FOC_CR1,RFAE,0);
  SET_BIT(FOC_CR1,ANGM,1);
}

uint8_t
foc_motor_open_loop_complete(void)
{
  if(!GET_BIT(FOC_CR1,RFAE))
  {
    return (1);
  }
  return (0);
}

uint8_t
foc_motor_closed_loop_lock(void)
{
  if (FOC_EOME > FOC_EFREQMIN)
  {
    return (1);
  }
  return (0);
}

void
foc_motor_stop(void)
{
	FOC_CR1 = 0x00;
	FOCEN = 0;
	MOE = 0;
}

int16_t
foc_get_motor_speed(void)
{
  return ((int16_t)FOC_EOME);
}

void
foc_set_param(struct foc_idq_s ref_s)
{
  FOC_IDREF = ref_s.id;
  FOC_IQREF = ref_s.iq;
}

#ifndef EVENT_LOG_DISABLE
void
foc_set_theta_compensation(int16_t compensation)
{
  m_theta_comp = compensation;
}
#endif

void
foc_theta_compensation(void)
{
  FOC_THECOMP = q15_ramp(&m_theta_comp_s,m_theta_comp);
}

void
foc_motor_brake(void)
{
  FOC_SWDUTY = FOC_ARR;
  FOC_CR1 = (1 << FCE) | (1 << PWMSS);
}

int16_t
foc_get_backemf(void)
{
  return (FOC_ESQU);
}

int16_t
foc_get_power(void)
{
  return (FOC_POW);
}

void
foc_disable(void)
{
  FOCEN = 0;
}

void
foc_software_control_enable(void)
{
  DRV_OUT = 0x00;
}

void
foc_software_control(enum foc_sw_control_e active_vector)
{
  switch(active_vector)
  {
    case rt_yb_xx_e:
    {
      DRV_OUT = 0x09;
      break;
    }
    case rb_yt_xx_e:
    {
      DRV_OUT = 0x06;
      break;
    }
    case rt_xx_bb_e:
    {
      DRV_OUT = 0x21;
      break;
    }
    case rb_xx_bt_e:
    {
      DRV_OUT = 0x12;
      break;
    }
    case xx_yt_bb_e:
    {
      DRV_OUT = 0x24;
      break;
    }
    case xx_yb_bt_e:
    {
      DRV_OUT = 0x18;
      break;
    }
    case xx_xx_xx_e:
    {
      DRV_OUT = 0x00;
      break;
    }
  }
}

void
foc_current_offset_update(int16_t current_offset)
{
  m_current_offset = current_offset;
}

void
foc_vq_ramp(void)
{
  int16_t vq_ramp = q15_ramp(&m_vq_ramp_s,QOUTMAX);
  FOC_QMAX = vq_ramp;
}

Credits

sathishkumar
8 projects • 18 followers
Successful Kickstarter, 2 patents, 10+ yrs R&D at IITM & HCL, EV, IoT, CAN, HVAC, product design & dev expert. Contact: 9952093701

Comments