/*
**
******************************************************************************
* @file : MESCinput.c
* @brief : Collect inputs like throttle/brake
******************************************************************************
* @attention
*
*
© Copyright (c) 2020 David Molony.
* 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
#include "MESCinput.h"
#include "MESCerror.h"
void MESCinput_Init(MESC_motor_typedef *_motor){
_motor->input_vars.max_request_Idq.d = 0.0f; //Not supporting d-axis input current for now
_motor->input_vars.min_request_Idq.d = 0.0f;
if(!_motor->input_vars.max_request_Idq.q){
_motor->input_vars.max_request_Idq.q = MAX_IQ_REQUEST;
_motor->input_vars.min_request_Idq.q = MIN_IQ_REQUEST; //ToDo, SETTING THESE ASSYMETRIC WILL CAUSE ISSUES WITH REVERSE..
}
_motor->input_vars.IC_pulse_MAX = IC_PULSE_MAX;
_motor->input_vars.IC_pulse_MIN = IC_PULSE_MIN;
_motor->input_vars.IC_pulse_MID = IC_PULSE_MID;
_motor->input_vars.IC_pulse_DEADZONE = IC_PULSE_DEADZONE;
_motor->input_vars.IC_duration_MAX = IC_DURATION_MAX;
_motor->input_vars.IC_duration_MIN = IC_DURATION_MIN;
if(!_motor->input_vars.adc1_MAX){
_motor->input_vars.adc1_MAX = ADC1MAX;
_motor->input_vars.adc1_MIN = ADC1MIN;
_motor->input_vars.adc1_OOR = ADC1OOR;
_motor->input_vars.ADC1_polarity = ADC1_POLARITY;
}
if(!_motor->input_vars.adc2_MAX){
_motor->input_vars.adc2_MAX = ADC2MAX;
_motor->input_vars.adc2_MIN = ADC2MIN;
_motor->input_vars.adc2_OOR = ADC2OOR;
_motor->input_vars.ADC2_polarity = ADC2_POLARITY;
}
_motor->input_vars.adc1_gain[0] = 1.0f/(_motor->input_vars.adc1_MAX-_motor->input_vars.adc1_MIN);
_motor->input_vars.adc1_gain[1] = 1.0f/(_motor->input_vars.adc1_MAX-_motor->input_vars.adc1_MIN);
_motor->input_vars.adc2_gain[0] = 1.0f/(_motor->input_vars.adc2_MAX-_motor->input_vars.adc2_MIN);
_motor->input_vars.adc2_gain[1] = 1.0f/(_motor->input_vars.adc2_MAX-_motor->input_vars.adc2_MIN);
//RCPWM forward gain//index [0][x] is used for Idq requests for now, might support asymmetric brake and throttle later
_motor->input_vars.RCPWM_gain[0][0] = 1.0f/((float)_motor->input_vars.IC_pulse_MAX - (float)_motor->input_vars.IC_pulse_MID - (float)_motor->input_vars.IC_pulse_DEADZONE);
_motor->input_vars.RCPWM_gain[0][1] = 1.0f/(((float)_motor->input_vars.IC_pulse_MID - (float)_motor->input_vars.IC_pulse_DEADZONE)-(float)_motor->input_vars.IC_pulse_MIN);
if(!_motor->input_vars.input_options){
_motor->input_vars.input_options = DEFAULT_INPUT;
}
_motor->input_vars.UART_req = 0.0f;
_motor->input_vars.RCPWM_req = 0.0f;
_motor->input_vars.ADC1_req = 0.0f;
_motor->input_vars.ADC2_req = 0.0f;
}
void MESCinput_Collect(MESC_motor_typedef *_motor){
//This function should be called from a strong and well defined periodic interrupt or thread.
//In many applications, this may be safety critical code, since it is the primary source of the signals that can generate torque/power.
// Expectation is that it is called with a typical period of 100Hz. This may need to be increased in case of servo control.
//Check if remote ADC timeouts. The remote ADC is set elsewhere from the CAN data received.
if(_motor->input_vars.remote_ADC_timeout > 0){
_motor->input_vars.remote_ADC_timeout--;
}else{
_motor->input_vars.remote_ADC1_req = 0.0f;
_motor->input_vars.remote_ADC2_req = 0.0f;
}
//Collect the requested throttle inputs
//Remote ADC1 input
if((_motor->input_vars.input_options & 0b100000)&&(_motor->input_vars.remote_ADC_can_id > 0)){
//Do nothing. Already set
}else{
_motor->input_vars.remote_ADC1_req = 0.0f;//Set the input variable to zero
}
//Remote ADC2 input
if((_motor->input_vars.input_options & 0b1000000)&&(_motor->input_vars.remote_ADC_can_id > 0)){
//Do nothing, already set
}else{
_motor->input_vars.remote_ADC2_req = 0.0f;//Set the input variable to zero
}
//Differential ADC12 input
if((_motor->input_vars.input_options & 0b10000000)){
//TBC, Math and logic required
//To be filled, as signal = ext1-ext2 with error check based on ext1+ext2
}else{
_motor->input_vars.ADC12_diff_req = 0.0f; //Set the input variable to zero
}
//UART input
if(0 == (_motor->input_vars.input_options & 0b1000)){
_motor->input_vars.UART_req = 0.0f;
}
//RCPWM input
if(_motor->input_vars.input_options & 0b0100){
if(_motor->input_vars.pulse_recieved){
if((_motor->input_vars.IC_duration > _motor->input_vars.IC_duration_MIN) && (_motor->input_vars.IC_duration < _motor->input_vars.IC_duration_MAX)){
if(_motor->input_vars.IC_pulse>(_motor->input_vars.IC_pulse_MID + _motor->input_vars.IC_pulse_DEADZONE)){
_motor->input_vars.RCPWM_req = (float)(_motor->input_vars.IC_pulse - (_motor->input_vars.IC_pulse_MID + _motor->input_vars.IC_pulse_DEADZONE))*_motor->input_vars.RCPWM_gain[0][1];
if(fabsf(_motor->input_vars.RCPWM_req>1.1f)){
handleError(_motor, ERROR_INPUT_OOR);
}
if(_motor->input_vars.RCPWM_req>1.0f){_motor->input_vars.RCPWM_req=1.0f;}
if(_motor->input_vars.RCPWM_req<-1.0f){_motor->input_vars.RCPWM_req=-1.0f;}
}
else if(_motor->input_vars.IC_pulse<(_motor->input_vars.IC_pulse_MID - _motor->input_vars.IC_pulse_DEADZONE)){
_motor->input_vars.RCPWM_req = ((float)_motor->input_vars.IC_pulse - (float)(_motor->input_vars.IC_pulse_MID - _motor->input_vars.IC_pulse_DEADZONE))*_motor->input_vars.RCPWM_gain[0][1];
if(fabsf(_motor->input_vars.RCPWM_req>1.1f)){
handleError(_motor, ERROR_INPUT_OOR);
}
if(_motor->input_vars.RCPWM_req>1.0f){_motor->input_vars.RCPWM_req=1.0f;}
if(_motor->input_vars.RCPWM_req<-1.0f){_motor->input_vars.RCPWM_req=-1.0f;}
} else{
_motor->input_vars.RCPWM_req = 0.0f;
}
} else {//The duration of the IC was wrong; trap it and write no current request
//Todo maybe want to implement a timeout on this, allowing spurious pulses to not wiggle the current?
_motor->input_vars.RCPWM_req = 0.0f;
}
} else {//No pulse received flag
_motor->input_vars.RCPWM_req = 0.0f;
}
} else{
_motor->input_vars.RCPWM_req = 0.0f;
}
//ADC2 input
if(_motor->input_vars.input_options & 0b0010){
if(_motor->Raw.ADC_in_ext2>_motor->input_vars.adc2_MIN){
_motor->input_vars.ADC2_req = ((float)_motor->Raw.ADC_in_ext2-(float)_motor->input_vars.adc2_MIN)*_motor->input_vars.adc2_gain[1]*_motor->input_vars.ADC2_polarity;
if(_motor->Raw.ADC_in_ext2>_motor->input_vars.adc2_OOR){
//input_vars.ADC2_req = 0.0f;
handleError(_motor, ERROR_INPUT_OOR);
}
}
else{
_motor->input_vars.ADC2_req = 0.0f;
}
if(_motor->input_vars.ADC2_req>1.0f){_motor->input_vars.ADC2_req=1.0f;}
if(_motor->input_vars.ADC2_req<-1.0f){_motor->input_vars.ADC2_req=-1.0f;}
}else{
_motor->input_vars.ADC2_req = 0.0f;
}
//ADC1 input
if(_motor->input_vars.input_options & 0b0001){
if(_motor->Raw.ADC_in_ext1>_motor->input_vars.adc1_MIN){
_motor->input_vars.ADC1_req = ((float)_motor->Raw.ADC_in_ext1-(float)_motor->input_vars.adc1_MIN)*_motor->input_vars.adc1_gain[1]*_motor->input_vars.ADC1_polarity;
if(_motor->Raw.ADC_in_ext1>_motor->input_vars.adc1_OOR){
//input_vars.ADC1_req = 0.0f;//If we set throttle to zero, it will immediately reset the error!
handleError(_motor, ERROR_INPUT_OOR);
}
}
else{
_motor->input_vars.ADC1_req = 0.0f;
}
if(_motor->input_vars.ADC1_req>1.0f){_motor->input_vars.ADC1_req=1.0f;}
if(_motor->input_vars.ADC1_req<-1.0f){_motor->input_vars.ADC1_req=-1.0f;}
}else{
_motor->input_vars.ADC1_req = 0.0f;
}
#ifdef KILLSWITCH_GPIO
if(_motor->input_vars.input_options & 0b10000){//Killswitch
if(KILLSWITCH_GPIO->IDR & (0x01<input_vars.nKillswitch = 1;
_motor->key_bits &= ~KILLSWITCH_KEY;
}else{
_motor->input_vars.nKillswitch = 0;
_motor->key_bits |= KILLSWITCH_KEY;
}
if(_motor->input_vars.invert_killswitch){
_motor->input_vars.nKillswitch = !_motor->input_vars.nKillswitch;
_motor->key_bits ^= KILLSWITCH_KEY;
}
}else{//If we are not using the killswitch, then it should be "on"
_motor->input_vars.nKillswitch = 1;
_motor->key_bits &= ~KILLSWITCH_KEY;
}
#else
_motor->input_vars.nKillswitch = 1;
_motor->key_bits &= ~KILLSWITCH_KEY;
#endif
}
int handbrakenow;
int MESCinput_isHandbrake(){
#ifdef HANDBRAKE_GPIO
handbrakenow = HANDBRAKE_GPIO->IDR & (0x01<IDR & (0x01<