/*
**
******************************************************************************
* @file : MESCinterface.c
* @brief : Initializing RTOS system and parameters
******************************************************************************
* @attention
*
*
© Copyright (c) 2022 Jens Kerrinnes.
* All rights reserved.
*
* This software component is licensed under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
******************************************************************************
*In addition to the usual 3 BSD clauses, it is explicitly noted that you
*do NOT have the right to take sections of this code for other projects
*without attribution and credit to the source. Specifically, if you copy into
*copyleft licenced code without attribution and retention of the permissive BSD
*3 clause licence, you grant a perpetual licence to do the same regarding turning sections of your code
*permissive, and lose any rights to use of this code previously granted or assumed.
*
*This code is intended to remain permissively licensed wherever it goes,
*maintaining the freedom to distribute compiled binaries WITHOUT a requirement to supply source.
*
*This is to ensure this code can at any point be used commercially, on products that may require
*such restriction to meet regulatory requirements, or to avoid damage to hardware, or to ensure
*warranties can reasonably be honoured.
******************************************************************************/
#include "main.h"
#include "Tasks/init.h"
#include "TTerm/Core/include/TTerm.h"
#include "Tasks/task_cli.h"
#include "Tasks/task_overlay.h"
#include "MESCmotor_state.h"
#include "MESCmotor.h"
#include
#include
#include
#include
#include
#include "MESCmeasure.h"
void handleEscape(TERMINAL_HANDLE *handle){
MESC_motor_typedef * motor_curr = &mtr[0];
motor_curr->input_vars.UART_req = 0;
motor_curr->input_vars.UART_dreq = 0;
}
const char TERM_startupText1[] = "\r\n";
const char TERM_startupText2[] = "\r\n[M]olony [E]lectronic [S]peed [C]ontroller";
const char TERM_startupText3[] = "\r\n";
uint8_t CMD_error(TERMINAL_HANDLE * handle, uint8_t argCount, char ** args){
bool has_error=false;
uint32_t mask = 0;
for(uint8_t i=0;i<32;i++){
mask = (1 << i);
if(MESC_errors & mask){
ttprintf("Error bit[%u]: %s\r\n", i+1, error_string[i]);
has_error = true;
}
}
if(has_error == false){
ttprintf("No errors :)\r\n");
}
return TERM_CMD_EXIT_SUCCESS;
}
uint8_t CMD_measure(TERMINAL_HANDLE * handle, uint8_t argCount, char ** args){
MESC_motor_typedef * motor_curr = &mtr[0];
port_str * port = handle->port;
bool measure_RL = false;
bool measure_res = false;
bool measure_ind = false;
bool measure_kv = false;
bool measure_linkage = false;
bool measure_hfi = false;
bool measure_dt = false;
if(argCount==0){
measure_RL = true;
measure_kv = true;
}
for(int i=0;iMotorState = MOTOR_STATE_MEASURING;
ttprintf("Measuring resistance and inductance\r\nWaiting for result");
mtr[0].meas.PWM_cycles = 0;
while(motor_curr->MotorState == MOTOR_STATE_MEASURING){
xSemaphoreGive(port->term_block);
vTaskDelay(200);
xQueueSemaphoreTake(port->term_block, portMAX_DELAY);
ttprintf(".");
}
TERM_sendVT100Code(handle,_VT100_ERASE_LINE, 0);
TERM_sendVT100Code(handle,_VT100_CURSOR_SET_COLUMN, 0);
float R, Lq, Ld;
char* Runit;
char* Lunit;
if(motor_curr->m.R > 0){
R = motor_curr->m.R;
Runit = "Ohm";
}else{
R = motor_curr->m.R*1000.0f;
Runit = "mOhm";
}
if(motor_curr->m.L_Q > 0.001f){
Ld = motor_curr->m.L_D*1000.0f;
Lq = motor_curr->m.L_Q*1000.0f;
Lunit = "mH";
}else{
Ld = motor_curr->m.L_D*1000.0f*1000.0f;
Lq = motor_curr->m.L_Q*1000.0f*1000.0f;
Lunit = "uH";
}
ttprintf("R = %f %s\r\nLd = %f %s\r\nLq = %f %s\r\n\r\n", (double)R, Runit, (double)Ld, Lunit, (double)Lq, Lunit);
calculateGains(motor_curr);
vTaskDelay(1000);
}
if(measure_res){
//Measure resistance
float old_L_D = motor_curr->m.L_D;
motor_curr->m.R = 0.0001f;//0.1mohm, really low
motor_curr->m.L_D = 0.000001f;//1uH, really low
calculateGains(motor_curr);
calculateVoltageGain(motor_curr);
motor_curr->MotorState = MOTOR_STATE_RUN;
motor_curr->MotorSensorMode = MOTOR_SENSOR_MODE_OPENLOOP;
motor_curr->FOC.openloop_step = 0;
ttprintf("Measuring resistance \r\nWaiting for result");
int a=200;
float Itop = 0.0f;
float Ibot = 0.0f;
float Vtop = 0.0f;
float Vbot = 0.0f;
motor_curr->input_vars.UART_req = 0.45f*motor_curr->m.Imax;
while(a){
xSemaphoreGive(port->term_block);
vTaskDelay(5);
xQueueSemaphoreTake(port->term_block, portMAX_DELAY);
ttprintf(".");
Ibot = Ibot+motor_curr->FOC.Idq.q;
Vbot = Vbot+motor_curr->FOC.Vdq.q;
a--;
motor_curr->FOC.FOCAngle +=300;
}
a=200;
motor_curr->input_vars.UART_req = 0.55f*motor_curr->m.Imax;
while(a){
xSemaphoreGive(port->term_block);
vTaskDelay(5);
xQueueSemaphoreTake(port->term_block, portMAX_DELAY);
ttprintf(".");
Itop = Itop+motor_curr->FOC.Idq.q;
Vtop = Vtop+motor_curr->FOC.Vdq.q;
a--;
motor_curr->FOC.FOCAngle +=300;
}
motor_curr->m.R = (Vtop-Vbot)/((Itop-Ibot)); //Calculate the resistance
motor_curr->input_vars.UART_req = 0.0f;
motor_curr->MotorState = MOTOR_STATE_TRACKING;
motor_curr->MotorSensorMode = MOTOR_SENSOR_MODE_SENSORLESS;
TERM_sendVT100Code(handle,_VT100_ERASE_LINE, 0);
TERM_sendVT100Code(handle,_VT100_CURSOR_SET_COLUMN, 0);
float R;
char* Runit;
if(motor_curr->m.R > 0){
R = motor_curr->m.R;
Runit = "Ohm";
}else{
R = motor_curr->m.R*1000.0f;
Runit = "mOhm";
}
ttprintf("R = %f %s\r\n\r\n", (double)R, Runit);
motor_curr->m.L_D =old_L_D;
calculateGains(motor_curr);
vTaskDelay(1000);
}
if(measure_ind){
//Measure inductance, second method
ttprintf("Measuring Inductance\r\nWaiting for result");
int a=200;
float Loffset[3];
float Lqoffset[3];
//set things up to do the L measurement
motor_curr->MotorState = MOTOR_STATE_RUN;
motor_curr->input_vars.UART_req = 0.25f; //Stop it going into tracking mode
motor_curr->HFI.Type = HFI_TYPE_SPECIAL;
motor_curr->HFI.special_injectionVd = 0.2f;
motor_curr->HFI.special_injectionVq = 0.0f;
motor_curr->MotorSensorMode = MOTOR_SENSOR_MODE_OPENLOOP;
motor_curr->FOC.openloop_step = 0;
motor_curr->FOC.FOCAngle = 0;
motor_curr->input_vars.UART_dreq = -5.0f;
motor_curr->FOC.didq.d = 0.0f;
vTaskDelay(10);
//Determine the voltage required
while(a){
if(fabsf(motor_curr->FOC.didq.d)<5.0f){
motor_curr->HFI.special_injectionVd *=1.05f;
if(motor_curr->HFI.special_injectionVd > (0.5f * motor_curr->Conv.Vbus))
{
motor_curr->HFI.special_injectionVd = 0.5f * motor_curr->Conv.Vbus;
}
}
xSemaphoreGive(port->term_block);
vTaskDelay(5);
xQueueSemaphoreTake(port->term_block, portMAX_DELAY);
ttprintf(".");
a--;
}
int b;
//Measure the Ld
for(b=0;b<3; b++){
Loffset[b] = 0.0f;
a=200;
motor_curr->input_vars.UART_dreq = -motor_curr->m.Imax * 0.25f * (float)b;
while(a){
Loffset[b] = Loffset[b] + motor_curr->FOC.didq.d;
xSemaphoreGive(port->term_block);
vTaskDelay(5);
xQueueSemaphoreTake(port->term_block, portMAX_DELAY);
ttprintf(".");
a--;
}
Loffset[b] = Loffset[b]/200;
Loffset[b] = motor_curr->FOC.pwm_period * motor_curr->HFI.special_injectionVd/Loffset[b];
}
TERM_sendVT100Code(handle,_VT100_ERASE_LINE, 0);
TERM_sendVT100Code(handle,_VT100_CURSOR_SET_COLUMN, 0);
ttprintf("D-Inductance = %f , %f , %f H\r\n voltage was %f \r\n", (double)Loffset[0], (double)Loffset[1], (double)Loffset[2], (double)motor_curr->HFI.special_injectionVd);
//Now do Lq
motor_curr->HFI.special_injectionVq = motor_curr->HFI.special_injectionVd;
float c = motor_curr->HFI.special_injectionVq;
motor_curr->HFI.special_injectionVd = 0.0f;
for(b=0;b<3; b++){
Lqoffset[b] = 0.0f;
a=200;
motor_curr->input_vars.UART_dreq = -10.0f ;
motor_curr->HFI.special_injectionVq = c * (1+(float)b);
if(motor_curr->HFI.special_injectionVq > motor_curr->Conv.Vbus*0.5f){motor_curr->HFI.special_injectionVq = motor_curr->Conv.Vbus*0.5f;}
while(a){
Lqoffset[b] = Lqoffset[b] + motor_curr->FOC.didq.q;
xSemaphoreGive(port->term_block);
vTaskDelay(5);
xQueueSemaphoreTake(port->term_block, portMAX_DELAY);
ttprintf(".");
a--;
}
Lqoffset[b] = Lqoffset[b]/200;
Lqoffset[b] = motor_curr->FOC.pwm_period * motor_curr->HFI.special_injectionVq/Lqoffset[b];
}
//Put things back to runable
motor_curr->HFI.Type = HFI_TYPE_NONE;
motor_curr->MotorSensorMode = MOTOR_SENSOR_MODE_SENSORLESS;
motor_curr->input_vars.UART_req = 0.0f; //Turn it off.
motor_curr->input_vars.UART_dreq = 0.0f;
motor_curr->MotorState = MOTOR_STATE_TRACKING;
TERM_sendVT100Code(handle,_VT100_ERASE_LINE, 0);
TERM_sendVT100Code(handle,_VT100_CURSOR_SET_COLUMN, 0);
ttprintf("Q-Inductance = %f , %f , %f H\r\n voltage was %f \r\n", (double)Lqoffset[0], (double)Lqoffset[1], (double)Lqoffset[2], (double)motor_curr->HFI.special_injectionVq);
motor_curr->HFI.special_injectionVd = 0.0f;
motor_curr->HFI.special_injectionVq = 0.0f;
vTaskDelay(200);
}
if(measure_kv){
//Measure kV
motor_curr->MotorState = MOTOR_STATE_GET_KV;
ttprintf("Measuring flux linkage\r\nWaiting for result");
while(motor_curr->MotorState == MOTOR_STATE_GET_KV){
xSemaphoreGive(port->term_block);
vTaskDelay(200);
xQueueSemaphoreTake(port->term_block, portMAX_DELAY);
ttprintf(".");
}
TERM_sendVT100Code(handle,_VT100_ERASE_LINE, 0);
TERM_sendVT100Code(handle,_VT100_CURSOR_SET_COLUMN, 0);
ttprintf("Flux linkage = %f mWb\r\n\r\n", (double)(motor_curr->m.flux_linkage * 1000.0f));
vTaskDelay(2000);
}
if(measure_linkage){
//Measure kV
motor_curr->MotorState = MOTOR_STATE_RUN;
ttprintf("Measuring flux linkage\r\nWaiting for result");
motor_curr->m.flux_linkage_max = 0.1001f;//Start it low
motor_curr->m.flux_linkage_min = 0.00005f;//Start it low
motor_curr->HFI.Type = HFI_TYPE_NONE;
motor_curr->FOC.FW_curr_max = 0.1f;
motor_curr->input_vars.UART_req = 10.0f; //Parametise later, closedloop current
while((motor_curr->m.flux_linkage_max > 0.0001f) && (motor_curr->FOC.eHz<100)){
xSemaphoreGive(port->term_block);
vTaskDelay(10);
xQueueSemaphoreTake(port->term_block, portMAX_DELAY);
ttprintf(".");
motor_curr->m.flux_linkage_max = motor_curr->m.flux_linkage_max*0.997f;// + 0.0001f;
motor_curr->FOC.flux_a = motor_curr->FOC.flux_a + 0.01*motor_curr->FOC.flux_b;
motor_curr->FOC.flux_b = motor_curr->FOC.flux_b - 0.01*motor_curr->FOC.flux_a;//Since the two fluxes are derivatives of each other, this kicks them around and avoids stalls
if(motor_curr->MotorState == MOTOR_STATE_ERROR){
break;
}
}
int a=200;
motor_curr->m.flux_linkage_max = motor_curr->m.flux_linkage_max*1.5f;
while(a){
xSemaphoreGive(port->term_block);
vTaskDelay(10);
xQueueSemaphoreTake(port->term_block, portMAX_DELAY);
ttprintf(".");
a--;
}
motor_curr->m.flux_linkage_max = motor_curr->FOC.flux_observed*1.5f;
motor_curr->m.flux_linkage_min = motor_curr->FOC.flux_observed*0.5f;
motor_curr->m.flux_linkage = motor_curr->FOC.flux_observed;
TERM_sendVT100Code(handle,_VT100_ERASE_LINE, 0);
TERM_sendVT100Code(handle,_VT100_CURSOR_SET_COLUMN, 0);
ttprintf("Flux linkage = %f mWb\r\n\r\n", (double)(motor_curr->m.flux_linkage * 1000.0f));
ttprintf("Did the motor spin for >2seconds?");
vTaskDelay(200);
motor_curr->input_vars.UART_req = 0.0f;
motor_curr->MotorState = MOTOR_STATE_TRACKING;
}
if(measure_hfi){
ttprintf("Measuring HFI threshold\r\n");
float HFI_Threshold = MESCmeasure_DetectHFI(motor_curr);
ttprintf("HFI threshold: %f\r\n", (double)HFI_Threshold);
vTaskDelay(500);
}
if(measure_dt){
ttprintf("Measuring deadtime compensation\r\nWaiting for result");
motor_curr->MotorState = MOTOR_STATE_TEST;
while(motor_curr->MotorState == MOTOR_STATE_TEST){
xSemaphoreGive(port->term_block);
vTaskDelay(200);
xQueueSemaphoreTake(port->term_block, portMAX_DELAY);
ttprintf(".");
}
TERM_sendVT100Code(handle,_VT100_ERASE_LINE, 0);
TERM_sendVT100Code(handle,_VT100_CURSOR_SET_COLUMN, 0);
ttprintf("Deadtime register: %d\r\n", motor_curr->FOC.deadtime_comp);
vTaskDelay(500);
}
return TERM_CMD_EXIT_SUCCESS;
}
void callback(TermVariableDescriptor * var){
calculateFlux(&mtr[0]);
calculateGains(&mtr[0]);
calculateVoltageGain(&mtr[0]);
MESCinput_Init(&mtr[0]);
}
void populate_vars(){
// | Variable | MIN | MAX | NAME | DESCRIPTION | RW | CALLBACK | VAR LIST HANDLE
TERM_addVar(mtr[0].m.Pmax , 0.0f , 50000.0f , "par_p_max" , "Max power" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].m.IBatmax , 0.0f , 1000.0f , "par_ibat_max", "Max battery current power" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].m.direction , 0 , 1 , "par_dir" , "Motor direction" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].m.pole_pairs , 0 , 255 , "par_pp" , "Motor pole pairs" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].m.RPMmax , 0 , 300000 , "par_rpm_max" , "Max RPM" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].m.flux_linkage , 0.0f , 100.0f , "par_flux" , "Flux linkage" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].m.flux_linkage_gain , 0.0f , 100.0f , "FOC_flux_gain" , "Flux linkage gain" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].m.non_linear_centering_gain , 0.0f , 10000.0f , "FOC_flux_nlin" , "Flux centering gain" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].m.flux_linkage_gain , 0.0f , 100.0f , "FOC_flux_gain" , "Flux linkage gain" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].FOC.ortega_gain , 1.0f , 100000000.0f , "FOC_ortega_gain" , "Ortega gain, typically 1M" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].options.observer_type , 0 , 4 , "FOC_obs_type", "Observer type, 0=None, 1=MXLLambda, 2MXL, 3=OrtegaOrig, 4=PLL" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].m.R , 0.0f , 10.0f , "par_r" , "Phase resistance" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].m.L_D , 0.0f , 10.0f , "par_ld" , "Phase inductance" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].m.L_Q , 0.0f , 10.0f , "par_lq" , "Phase inductance" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].FOC.Current_bandwidth , 200.0f , 10000.0f , "FOC_curr_BW" , "Current Controller Bandwidth" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].HFI.Type , 0 , 3 , "FOC_hfi_type" , "HFI type [0=None, 1=45deg, 2=d axis]" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].meas.hfi_voltage , 0.0f , 50.0f , "FOC_hfi_volt" , "HFI voltage" , VAR_ACCESS_RW , NULL , &TERM_varList);
//TERM_addVar(mtr[0].HFI.mod_didq , 0.0f , 2.0f , "FOC_hfi_gain" , "HFI gain" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].HFI.toggle_eHz , 0.0f , 2000.0f , "FOC_hfi_eHz" , "HFI Max Frequency" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].FOC.FW_curr_max , 0.0f , 300.0f , "par_fw_curr" , "Max field weakenning current" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].meas.measure_current , 0.5f , 100.0f , "meas_curr" , "Measuring current" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].meas.measure_closedloop_current, 0.5f , 100.0f , "meas_cl_curr", "Measuring q closed loop current" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].meas.measure_voltage , 0.5f , 100.0f , "meas_volt" , "Measuring voltage" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].input_vars.adc1_MAX , 0 , 4096 , "adc1_max" , "ADC1 max val" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].input_vars.adc1_MIN , 0 , 4096 , "adc1_min" , "ADC1 min val" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].input_vars.ADC1_polarity , -1.0f , 1.0f , "adc1_pol" , "ADC1 polarity" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].input_vars.adc2_MAX , 0 , 4096 , "adc2_max" , "ADC2 max val" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].input_vars.adc2_MIN , 0 , 4096 , "adc2_min" , "ADC2 min val" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].input_vars.ADC2_polarity , -1.0f , 1.0f , "adc2_pol" , "ADC2 polarity" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].input_vars.max_request_Idq.q , 0.0f , 1000.0f , "par_i_max" , "Max motor current" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].input_vars.min_request_Idq.q , -1000.0f , 0.0f , "par_i_min" , "Min motor current" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].FOC.pwm_frequency , 0.0f , 100000.0f , "FOC_fpwm" , "PWM frequency" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].FOC.Modulation_max , 0.1f , 1.12f , "FOC_Max_Mod" , "Max modulation index; typically 0.95, can over modulate to 1.12" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].input_vars.UART_req , -1000.0f , 1000.0f , "uart_req" , "Uart input" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].input_vars.UART_dreq , -1000.0f , 1000.0f , "uart_dreq" , "Uart input" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].input_vars.input_options , 0 , 128 , "input_opt" , "Inputs [1=ADC1 2=ADC2 4=PPM 8=UART 16=Killswitch 32=CANADC1 64=CANADC2 128=ADC12DIFF]" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].safe_start[0] , 0 , 1000 , "safe_start" , "Countdown before allowing throttle" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].safe_start[1] , 0 , 1000 , "safe_count" , "Live count before allowing throttle" , VAR_ACCESS_R , NULL , &TERM_varList);
TERM_addVar(mtr[0].FOC.enc_offset , 0 , 65535 , "FOC_enc_oset", "Encoder alignment angle" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].FOC.FOCAngle , 0 , 65535 , "FOC_angle" , "FOC angle now" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].FOC.enc_angle , 0 , 65535 , "FOC_enc_ang" , "Encoder angle now" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].m.enc_counts , 0 , 65535 , "FOC_enc_PPR" , "Encoder ABI PPR" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].FOC.encoder_polarity_invert , 0 , 1 , "FOC_enc_pol" , "Encoder polarity" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].MotorSensorMode , 0 , 30 , "par_motor_sensor", "0=SL, 1=Hall, 2=OL, 3=ABSENC, 4=INC_ENC, 5=HFI" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].SLStartupSensor , 0 , 30 , "par_SL_sensor" , "0=OL, 1=Hall, 2=PWMENC, 3=HFI" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].FOC.openloop_step , 0.0f , 6000.0f , "FOC_ol_step" , "Angle per PWM period openloop (65535 per erev)" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].FOC.FW_ehz_max , 0.0f , 6000.0f , "FOC_fw_ehz" , "max eHz under field weakenning" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].FOC.park_current , 0.0f , 300.0f , "par_i_park" , "Max current for handbrake" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].FOC.hall_IIR , 0.0f , 1.0f , "FOC_hall_iir", "Decay constant for hall preload (0-1.0)" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].FOC.hall_transition_V , 0.0f , 100.0f , "FOC_hall_Vt" , "Hall transition voltage" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].FOC.hall_initialised , 0 , 1 , "FOC_hall_array_ok" , "Hall array OK flag (set to 0 to restart live hall cal process)" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(MESC_all_errors , -HUGE_VAL , HUGE_VAL , "error_all" , "All errors encountered" , VAR_ACCESS_R , NULL , &TERM_varList);
TERM_addVar(mtr[0].options.field_weakening , 0 , 2 , "opt_fw" , "Field weakening [0=OFF, 1=ON, 2=ON V2]" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].options.sqrt_circle_lim , 0 , 2 , "opt_circ_lim", "Circle limiter [0=OFF, 1=ON, 2=ON Vd]" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].options.pwm_type , 0 , 3 , "opt_pwm_type", "Modulator [0=SVPWM, 1=sinusoidal, 2=Bottom clamp, 3=Sin/bottom combo]" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].options.MTPA_mode , 0 , 3 , "opt_mtpa" , "MTPA type = 0=none, 1=setpoint, 2=magnitude, 3=iq" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].options.use_hall_start , 0 , 1 , "opt_hall_start", "Use hall start" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].options.use_phase_balancing , 0 , 1 , "opt_phase_bal", "Use highhopes phase balancing" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].options.use_lr_observer , 0 , 1 , "opt_lr_obs" , "Use LR observer" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].options.has_motor_temp_sensor, 0 , 1 , "opt_motor_temp" , "Motor has temperature sensor" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].options.app_type , 0 , 3 , "opt_app_type" , "App type, 0=none, 1=Vehicle, 2,3 = undefined" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].ControlMode , 0 , 4 , "opt_cont_type" , "Cont type: 0=Torque, 1=Speed, 2=Duty, 3=Position, 4=Measuring, 5=Handbrake" , VAR_ACCESS_RW , NULL , &TERM_varList);
TERM_addVar(mtr[0].FOC.FOC_advance , -10.0f , 10.0f , "FOC_Advance" , "FOC advance, proportion of 1 PWM cycle" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].FOC.speed_kp , 0.0f , 6000000.0f, "speed_kp" , "amps/Hz proportional gain" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].FOC.speed_ki , 0.0f , 6000000.0f, "speed_ki" , "amps/Hz integral gain" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].FOC.speed_req , 0.0f , 5000.0f , "speed_req" , "Hz" , VAR_ACCESS_RW , callback , &TERM_varList);
// _motor->FOC.FOC_advance
TERM_addVarArrayFloat(mtr[0].m.hall_flux, sizeof(mtr[0].m.hall_flux), -10.0f, 10.0f, "Hall_flux", "hall start table", VAR_ACCESS_RW, NULL, &TERM_varList);
#ifdef HAL_CAN_MODULE_ENABLED
TERM_addVar(can1.node_id , 1 , 254 , "node_id" , "Node ID" , VAR_ACCESS_RW , callback , &TERM_varList);
TERM_addVar(mtr[0].input_vars.remote_ADC_can_id , 0 , 254 , "can_adc" , "CAN ADC ID 0=disabled" , VAR_ACCESS_RW , callback , &TERM_varList);
#endif
TermVariableDescriptor * desc;
desc = TERM_addVar(mtr[0].Conv.Vbus , 0.0f , HUGE_VAL , "vbus" , "Read input voltage" , VAR_ACCESS_TR , NULL , &TERM_varList);
TERM_setFlag(desc, FLAG_TELEMETRY_ON);
desc = TERM_addVar(mtr[0].FOC.eHz , -HUGE_VAL , HUGE_VAL , "ehz" , "Motor electrical hz" , VAR_ACCESS_TR , NULL , &TERM_varList);
TERM_setFlag(desc, FLAG_TELEMETRY_ON);
desc = TERM_addVar(mtr[0].FOC.Idq_smoothed.d , -HUGE_VAL , HUGE_VAL , "id" , "Phase Idq_d smoothed" , VAR_ACCESS_TR , NULL , &TERM_varList);
TERM_setFlag(desc, FLAG_TELEMETRY_ON);
desc = TERM_addVar(mtr[0].FOC.Idq_smoothed.q , -HUGE_VAL , HUGE_VAL , "iq" , "Phase Idq_q smoothed" , VAR_ACCESS_TR , NULL , &TERM_varList);
TERM_setFlag(desc, FLAG_TELEMETRY_ON);
desc = TERM_addVar(mtr[0].Raw.ADC_in_ext1 , 0 , 4096 , "adc1" , "Raw ADC throttle" , VAR_ACCESS_TR , NULL , &TERM_varList);
TERM_setFlag(desc, FLAG_TELEMETRY_ON);
desc = TERM_addVar(mtr[0].Conv.MOSu_T , 0.0f , 4096.0f , "TMOS" , "MOSFET temp, kelvin" , VAR_ACCESS_TR , NULL , &TERM_varList);
TERM_setFlag(desc, FLAG_TELEMETRY_ON);
desc = TERM_addVar(mtr[0].Conv.Motor_T , 0.0f , 4096.0f , "TMOT" , "Motor temp, kelvin" , VAR_ACCESS_TR , NULL , &TERM_varList);
TERM_setFlag(desc, FLAG_TELEMETRY_ON);
desc = TERM_addVar(MESC_errors , -HUGE_VAL , HUGE_VAL , "error" , "System errors now" , VAR_ACCESS_TR , NULL , &TERM_varList);
TERM_setFlag(desc, FLAG_TELEMETRY_ON);
desc = TERM_addVar(mtr[0].FOC.Vdq.q , -4096.0f , 4096.0f , "Vq" , "FOC_Vdq_q" , VAR_ACCESS_TR , NULL , &TERM_varList);
TERM_setFlag(desc, FLAG_TELEMETRY_ON);
desc = TERM_addVar(mtr[0].FOC.Vdq.d , -4096.0f , 4096.0f , "Vd" , "FOC_Vdq_d" , VAR_ACCESS_TR , NULL , &TERM_varList);
TERM_setFlag(desc, FLAG_TELEMETRY_ON);
desc = TERM_addVar(mtr[0].FOC.Idq_req.q , -4096.0f , 4096.0f , "iqreq" , "mtr[0].FOC.Idq_req.q" , VAR_ACCESS_TR , NULL , &TERM_varList);
TERM_setFlag(desc, FLAG_TELEMETRY_ON);
}
#ifdef HAL_CAN_MODULE_ENABLED
#define REMOTE_ADC_TIMEOUT 1000
void TASK_CAN_packet_cb(TASK_CAN_handle * handle, uint32_t id, uint8_t sender, uint8_t receiver, uint8_t* data, uint32_t len){
MESC_motor_typedef * motor_curr = &mtr[0];
switch(id){
case CAN_ID_IQREQ:{
float req = PACK_buf_to_float(data);
if(req > 0){
motor_curr->input_vars.UART_req = req * motor_curr->input_vars.max_request_Idq.q;
}else{
motor_curr->input_vars.UART_req = req * motor_curr->input_vars.min_request_Idq.q * -1.0;
}
break;
}
case CAN_ID_SAMPLE_NOW:
motor_curr->logging.sample_no_auto_send = true;
motor_curr->logging.sample_now = true;
break;
case CAN_ID_SAMPLE_SEND:
motor_curr->logging.sample_no_auto_send = false;
break;
case CAN_ID_ADC1_2_REQ:{
if(sender == motor_curr->input_vars.remote_ADC_can_id && motor_curr->input_vars.remote_ADC_can_id > 0){
motor_curr->input_vars.remote_ADC_timeout = REMOTE_ADC_TIMEOUT;
motor_curr->input_vars.remote_ADC1_req = PACK_buf_to_float(data);
motor_curr->input_vars.remote_ADC2_req = PACK_buf_to_float(data+4);
}
break;
}
default:
break;
}
}
void TASK_CAN_telemetry_fast(TASK_CAN_handle * handle){
MESC_motor_typedef * motor_curr = &mtr[0];
TASK_CAN_add_float(handle , CAN_ID_ADC1_2_REQ , CAN_BROADCAST, motor_curr->input_vars.ADC1_req , motor_curr->input_vars.ADC2_req , 0);
TASK_CAN_add_float(handle , CAN_ID_SPEED , CAN_BROADCAST, motor_curr->FOC.eHz , 0.0f , 0);
TASK_CAN_add_float(handle , CAN_ID_BUS_VOLT_CURR , CAN_BROADCAST, motor_curr->Conv.Vbus , motor_curr->FOC.Ibus , 0);
TASK_CAN_add_uint32(handle , CAN_ID_STATUS , CAN_BROADCAST, motor_curr->MotorState , 0 , 0);
TASK_CAN_add_float(handle , CAN_ID_MOTOR_CURRENT , CAN_BROADCAST, motor_curr->FOC.Idq.q , motor_curr->FOC.Idq.d , 0);
TASK_CAN_add_float(handle , CAN_ID_MOTOR_VOLTAGE , CAN_BROADCAST, motor_curr->FOC.Vdq.q , motor_curr->FOC.Vdq.d , 0);
}
void TASK_CAN_telemetry_slow(TASK_CAN_handle * handle){
MESC_motor_typedef * motor_curr = &mtr[0];
TASK_CAN_add_float(handle , CAN_ID_TEMP_MOT_MOS1 , CAN_BROADCAST, motor_curr->Conv.Motor_T , motor_curr->Conv.MOSu_T , 0);
TASK_CAN_add_float(handle , CAN_ID_TEMP_MOS2_MOS3 , CAN_BROADCAST, motor_curr->Conv.MOSv_T , motor_curr->Conv.MOSw_T , 0);
TASK_CAN_add_uint32(handle , CAN_ID_FOC_HYPER , CAN_BROADCAST, motor_curr->FOC.cycles_fastloop , motor_curr->FOC.cycles_pwmloop , 0);
}
#define POST_ERROR_SAMPLES LOGLENGTH/2
void TASK_CAN_aux_data(TASK_CAN_handle * handle){
static int samples_sent=-1;
static int current_pos=0;
static float timestamp;
MESC_motor_typedef * motor_curr = &mtr[0];
if(motor_curr->logging.print_samples_now && motor_curr->logging.sample_no_auto_send == false){
if(samples_sent == -1){
current_pos = motor_curr->logging.current_sample;
TASK_CAN_add_sample(handle, CAN_ID_SAMPLE, 0, 0, 0, CAN_SAMPLE_FLAG_START, (float)LOGLENGTH, 100);
samples_sent=0;
timestamp = motor_curr->FOC.pwm_period * (float)POST_ERROR_SAMPLES * -1.0f;
return;
}
timestamp += motor_curr->FOC.pwm_period;
TASK_CAN_add_sample(handle, CAN_ID_SAMPLE, 0, samples_sent, 0, 0, timestamp, 100);
TASK_CAN_add_sample(handle, CAN_ID_SAMPLE, 0, samples_sent, 1, 0, motor_curr->logging.Vbus[current_pos], 100);
TASK_CAN_add_sample(handle, CAN_ID_SAMPLE, 0, samples_sent, 2, 0, motor_curr->logging.Iu[current_pos], 100);
TASK_CAN_add_sample(handle, CAN_ID_SAMPLE, 0, samples_sent, 3, 0, motor_curr->logging.Iv[current_pos], 100);
TASK_CAN_add_sample(handle, CAN_ID_SAMPLE, 0, samples_sent, 4, 0, motor_curr->logging.Iw[current_pos], 100);
TASK_CAN_add_sample(handle, CAN_ID_SAMPLE, 0, samples_sent, 5, 0, motor_curr->logging.Vd[current_pos], 100);
TASK_CAN_add_sample(handle, CAN_ID_SAMPLE, 0, samples_sent, 6, 0, motor_curr->logging.Vq[current_pos], 100);
TASK_CAN_add_sample(handle, CAN_ID_SAMPLE, 0, samples_sent, 7, 0, motor_curr->logging.angle[current_pos], 100);
samples_sent++;
current_pos++;
if(current_pos == LOGLENGTH){
current_pos = 0;
}
if(samples_sent == LOGLENGTH){
timestamp = 0;
samples_sent = -2;
motor_curr->logging.print_samples_now = 0;
motor_curr->logging.lognow = 1;
return;
}
}
if(samples_sent == -2){
samples_sent = -1;
TASK_CAN_add_sample(handle, CAN_ID_SAMPLE, 0, 0, 0, CAN_SAMPLE_FLAG_END, 0.0f, 100);
}
}
#endif
void MESCinterface_init(TERMINAL_HANDLE * handle){
static volatile bool is_init = false;
if(is_init) return;
is_init = true;
populate_vars();
if(CMD_varLoad(&null_handle, 0, NULL) == TERM_CMD_EXIT_ERROR){
for(int i = 0; ivarListHead);
REGISTER_apps(&TERM_defaultList);
}