/* * dbw.c * * Created on: May 5, 2021 * Author: Dmitrijs */ #include "main.h" volatile config_t config_ram; volatile var_t var; volatile config_t *config; volatile uint32_t ac_timer = 100, ac_mode = 0; const config_t config_flash __attribute__((section(".config"), used)) = { PPS2_CALC_OPTION_LINEAR, TPS2_CALC_OPTION_LINEAR, 100, 3990, 100, 2048, 500, 3990, 100, 3990, 100, 2048, 500, 3990, PPS2TPS_OPTION_CURVE, IDLE_OPTION_NO_IDLE_INPUT, { 0, 67, 132, 198, 264, 330, 396, 462, 528, 594, 660, 726, 792, 858, 924, 1000 }, { 0, 67, 132, 198, 264, 330, 396, 462, 528, 594, 660, 726, 792, 858, 924, 1000 }, // Idle pps modifier curve. when Idle input is used. it modifies tps target { 0, 142, 284, 426, 568, 710, 852, 1000 }, //input pwm duty [0.1%] { 0, 8, 17, 25, 34, 42, 51, 60 }, 1000, // frequency in Hz 10, // ctl_teriod in [100uS] MOTOR_PID_OPTION_STATIC,// PID static coefs, static coef, but different for forward and backward moving // 3D table // PID COEFICIENTS for forward and reward 700, 50, 10, 700, 50, 10, { 0, 67, 132, 198, 264, 330, 396, 462, 528, 594, 660, 726, 792, 858, 924, 1000 }, { -325, -281, -239, -196, -153, -110, -67, -24, 18, 61, 104, 146, 189, 232, 275, 325 }, { { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, { 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700 }, }, //PID limmits 100, 10000, -10000, //DC offset curve { 0, 67, 132, 198, 264, 330, 396, 462, 528, 594, 660, 726, 792, 858, 924, 1000 }, // tps bins [0.1%] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // DC //Battery voltage correction curve { 0, 585, 1170, 1755, 2340, 2925, 3510, 4095 }, // battery voltage ADC bins [] { 0, 0, 0, 0, 0, 0, 0, 0 }, // motor pwm correction [0.1%] 30, 0x100, 0x5e8, 0x00, //limmits 30, 30, 15, 30, 30, 15, 100, 500, 10, 5000, 5, 7, // my MS CAN ID 1, 0, 100, 0x300 }; volatile unsigned short ctl_period; volatile pwm_t ttl1_pwm; volatile float pps1_gain, pps1_offset, pps2_gain, pps2_offset, tps1_gain, tps1_offset, tps2_gain, tps2_offset; volatile uint16_t dbw_fast_process_timer, dbw_slow_process_timer; volatile int32_t vbat_corr, tps_slow_t, p_comp, i_comp, d_comp, tps_error_t, can_target, spring_preload, idle_adder; void Apply_Sensor_Calibration(void) { pps1_gain = (1000.0F / (float) (config->pps1_max - config->pps1_min)); pps1_offset = (config->pps1_min * 1000.0F / (config->pps1_min - config->pps1_max)); pps2_gain = (1000.0F / (float) (config->pps2_max - config->pps2_min)); pps2_offset = (config->pps2_min * 1000.0F / (config->pps2_min - config->pps2_max)); tps1_gain = (1000.0F / (float) (config->tps1_max - config->tps1_min)); tps1_offset = (short) (config->tps1_min * 1000.0F / (config->tps1_min - config->tps1_max)); tps2_gain = (1000.0F / (float) (config->tps2_max - config->tps2_min)); tps2_offset = (config->tps2_min * 1000.0F / (config->tps2_min - config->tps2_max)); } void DBW_Init(void) { dbw_fast_process_timer = 1; dbw_slow_process_timer = config->motor_ctl_period; vbat_corr = 0; tps_slow_t = var.tps; p_comp = 0; i_comp = 0; d_comp = 0; tps_error_t = var.tps_error; can_target = 0; spring_preload = 0; idle_adder = 0; } int DBW_Process(void) { int32_t pwm_output = 0; int32_t pct = 0; if (dbw_fast_process_timer == 0) { //process DBW every 1 mS dbw_fast_process_timer = 1; // Read all sensors and calculate error // calculate PPS sensor reading var.pps1_adc = LPF(700, Adc_Read(0), var.pps1_adc); var.pps1 = var.pps1_adc * pps1_gain + pps1_offset; var.pps2_adc = LPF(700, Adc_Read(3), var.pps2_adc); //orig 1 var.pps2 = var.pps2_adc * pps2_gain + pps2_offset; var.pps_delta = var.pps1 - var.pps2; var.pps = (var.pps1 + var.pps2) >> 1; //Calculate TPS sensor reading var.tps1_adc = LPF(700, Adc_Read(2), var.tps1_adc); var.tps1 = var.tps1_adc * tps1_gain + tps1_offset; var.tps2_adc = LPF(700, Adc_Read(1), var.tps2_adc); //orig 3 var.tps2 = var.tps2_adc * tps2_gain + tps2_offset; // calculate tps and pps delta and values var.tps_delta = var.tps1 - var.tps2; var.tps = (var.tps1 + var.tps2) >> 1; var.pps_delta = var.pps1 - var.pps2; var.pps = (var.pps1 + var.pps2) >> 1; // set tps target if (config->pps2tps_option == PPS2TPS_OPTION_CURVE) { var.tps_target = intrp_1d_ss_table(var.pps, 16, (signed short*) config->pps_bins, 1, (signed short*) config->tps_bins); } // if MS3 DBW protocol is used else if (config->pps2tps_option == PPS2TPS_OPTION_MS3_CAN) { // if no RX errors //if(var.status0 & DBW_STATUS0_CAN_MSDBW_F) if (config->pps2tps_option == PPS2TPS_OPTION_MS3_CAN) { // use MS3 TPS TARGET var.tps_target = can_target; } else { // on CAN error fall back to internal curve var.tps_target = intrp_1d_ss_table(var.pps, 16, (signed short*) config->pps_bins, 1, (signed short*) config->tps_bins); } } else { // All other TPS target options are considered as fault Error_Handler(); } // add idle TPS target adder var.tps_target += idle_adder; //limit TPS target if (var.tps_target > 1000) var.tps_target = 1000; if (var.tps_target < 0) var.tps_target = 0; //calculate regulation error var.tps_error = var.tps_target - var.tps; // Proportional regulator p_comp = (((int32_t) var.tps_error) * (int32_t) config->motor_fw_p) / 10; // calculate spring preload compensation using interpolation spring_preload = intrp_1d_ss_table(var.tps_target, 16, (signed short*) config->tps_dc_tps_bins, 1, (signed short*) config->tps_dc_offset_bins); if (dbw_slow_process_timer > 0) { dbw_slow_process_timer--; if (dbw_slow_process_timer == 0) { dbw_slow_process_timer = config->motor_ctl_period; //calculate slow changing thinks //read Battery voltage and apply corrections var.vbat_adc = LPF(700, Adc_Read(5), var.vbat_adc); pct = intrp_1d_ss_table(var.vbat_adc, 8, (signed short*) config->vbat_bins, 1, (signed short*) config->motor_pwm_corr_bins); //read motor current var.motor_current_adc = LPF(700, Adc_Read(4), var.motor_current_adc); // calculate I component i_comp += ((int32_t) var.tps_error * (int32_t) config->motor_fw_i) / 100; if (i_comp > config->i_limmit) i_comp = config->i_limmit; if (i_comp < (-config->i_limmit)) i_comp = (-config->i_limmit); // calculate D component d_comp = (((int32_t) var.tps_error - tps_error_t) * (int32_t) config->motor_fw_d) / 10; tps_error_t = var.tps_error; // calculate single shot //single_shot = var.tps_error * // Calculate Idle adder if (config->idle_input_option == IDLE_OPTION_NO_IDLE_INPUT) idle_adder = 0; else if (config->idle_input_option == IDLE_OPTION_MS_CAN) { idle_adder = intrp_1d_ss_table(var.idle_dc, 8, (signed short*) config->idle_input_bins, 1, (signed short*) config->idle_tps_adder_bins); } else if (config->idle_input_option == IDLE_OPTION_PWM_INPUT1) { idle_adder = intrp_1d_ss_table(var.idle_dc, 8, (signed short*) config->idle_input_bins, 1, (signed short*) config->idle_tps_adder_bins) - (var.pps / 5); if (idle_adder < 0) { idle_adder = 0; } } } } pwm_output = p_comp + i_comp + d_comp + spring_preload; vbat_corr = (int16_t) (((int32_t) var.motor_pwm) * vbat_corr / 1000); pwm_output += pct; //limmit pwm if (pwm_output > config->motor_dc_max) pwm_output = config->motor_dc_max; if (pwm_output < config->motor_dc_min) pwm_output = config->motor_dc_min; //apply pwm DBW_Pwm_Set_Duty((signed short) (pwm_output), (pwm_t*) &ttl1_pwm); } return 0; } void DBW_Pwm_Init(void) { // Setup PWM ports ttl1_pwm.pos_port = GPIOB; ttl1_pwm.pos_pin = 10; ttl1_pwm.neg_port = GPIOB; ttl1_pwm.neg_pin = 11; // setup pwm status default -> no drive ttl1_pwm.status = PWM_STATUS_DEFAULT; // calculate how many tick are in given freq period ttl1_pwm.period_ticks = (unsigned int) 1000000UL / config->motor_pwm_fq; // set initian pwm from current settings ttl1_pwm.pwm = var.motor_pwm; ttl1_pwm.pwm_t = var.motor_pwm; ttl1_pwm.single_pulse = 0; //tmp = (float)ttl1_pwm.period_ticks * (float)ttl1_pwm.pwm/10000.0F; //ttl1_pwm.duty_ticks = (unsigned int) tmp; DBW_Pwm_Set_Duty(var.motor_pwm, (pwm_t*) &ttl1_pwm); if (var.motor_pwm < 0) ttl1_pwm.state = PWM_STATE_NEGATIVE_INACTIVE; else if (var.motor_pwm > 0) ttl1_pwm.state = PWM_STATE_POSITIVE_INACTIVE; else ttl1_pwm.state = PWM_STATE_POSITIVE_INACTIVE; ttl1_pwm.status = PWM_STATUS_IDLE; DBW_Stop(); ttl1_pwm.pos_port->BSRR = (1 << (16 + ttl1_pwm.pos_pin)); ttl1_pwm.neg_port->BSRR = (1 << (16 + ttl1_pwm.neg_pin)); // configure TIM2 to 1uS / tick timer // setup CCR interrupt happen after sturtup delay is over RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; TIM2->CNT = 0xFFFFFFFE; TIM2->PSC = 48; //2400; // (1uS precision) TIM2->ARR = 0xFFFFFFFF; TIM2->CR1 = (TIM_CR1_URS | TIM_CR1_CEN); TIM2->CCER = 0x0000; TIM2->CCMR1 = 0x0000; TIM2->CCR1 = (unsigned int) 20000; // startup delay TIM2->CCR2 = 0; TIM2->SR &= ~TIM_SR_CC1IF; TIM2->DIER |= TIM_DIER_CC1IE; NVIC_EnableIRQ(TIM2_IRQn); } void TIM2_IRQHandler(void) { if (TIM2->SR & TIM_SR_CC1IF) { // capture compare intrrupt TIM2->SR &= ~TIM_SR_CC1IF; if (ttl1_pwm.status == PWM_STATUS_DEFAULT) { // here ends startup delay // enable drive here ttl1_pwm.status = PWM_STATUS_STARTED; } //Set duty if (ttl1_pwm.pwm > 0) { // pwm > 0 if ((ttl1_pwm.pwm > 0) && (ttl1_pwm.pwm_t < 0)) { // if changeing PWM polarity insert dead time ttl1_pwm.state = PWM_STATE_POSITIVE_INACTIVE; ttl1_pwm.pos_port->BSRR = (1 << (16 + ttl1_pwm.pos_pin)); ttl1_pwm.neg_port->BSRR = (1 << (16 + ttl1_pwm.neg_pin)); TIM2->CCR1 = TIM2->CNT + config->pwm_deadtime; } else if (ttl1_pwm.state == PWM_STATE_POSITIVE_INACTIVE) { ttl1_pwm.state = PWM_STATE_POSITIVE_ACTIVE; ttl1_pwm.pos_port->BSRR = (1 << ttl1_pwm.pos_pin); ttl1_pwm.neg_port->BSRR = (1 << (16 + ttl1_pwm.neg_pin)); TIM2->CCR1 = TIM2->CNT + ttl1_pwm.duty_ticks; } else { ttl1_pwm.state = PWM_STATE_POSITIVE_INACTIVE; ttl1_pwm.pos_port->BSRR = (1 << (16 + ttl1_pwm.pos_pin)); ttl1_pwm.neg_port->BSRR = (1 << (16 + ttl1_pwm.neg_pin)); TIM2->CCR1 = TIM2->CNT + (ttl1_pwm.period_ticks - ttl1_pwm.duty_ticks); } } else if (ttl1_pwm.pwm < 0) { //pwm < 0 //if((ttl1_pwm.state == PWM_STATE_POSITIVE_INACTIVE) && (ttl1_pwm.state == PWM_STATE_POSITIVE_ACTIVE)) if ((ttl1_pwm.pwm < 0) && (ttl1_pwm.pwm_t > 0)) { // if changeing PWM polarity insert dead time ttl1_pwm.state = PWM_STATE_NEGATIVE_INACTIVE; ttl1_pwm.pos_port->BSRR = (1 << (16 + ttl1_pwm.pos_pin)); ttl1_pwm.neg_port->BSRR = (1 << (16 + ttl1_pwm.neg_pin)); TIM2->CCR1 = TIM2->CNT + config->pwm_deadtime; } else if (ttl1_pwm.state == PWM_STATE_NEGATIVE_INACTIVE) { ttl1_pwm.state = PWM_STATE_NEGATIVE_ACTIVE; ttl1_pwm.pos_port->BSRR = (1 << (16 + ttl1_pwm.pos_pin)); ttl1_pwm.neg_port->BSRR = (1 << ttl1_pwm.neg_pin); TIM2->CCR1 = TIM2->CNT + ttl1_pwm.duty_ticks; } else { ttl1_pwm.state = PWM_STATE_NEGATIVE_INACTIVE; ttl1_pwm.pos_port->BSRR = (1 << (16 + ttl1_pwm.pos_pin)); ttl1_pwm.neg_port->BSRR = (1 << (16 + ttl1_pwm.neg_pin)); TIM2->CCR1 = TIM2->CNT + (ttl1_pwm.period_ticks - ttl1_pwm.duty_ticks); } } else { // if pwm == 0 ttl1_pwm.pos_port->BSRR = (1 << (16 + ttl1_pwm.pos_pin)); ttl1_pwm.neg_port->BSRR = (1 << (16 + ttl1_pwm.neg_pin)); TIM2->CCR1 = TIM2->CNT + ttl1_pwm.period_ticks; if (ttl1_pwm.state == PWM_STATE_POSITIVE_ACTIVE || ttl1_pwm.state == PWM_STATE_POSITIVE_INACTIVE) { ttl1_pwm.state = PWM_STATE_POSITIVE_INACTIVE; } else { ttl1_pwm.state = PWM_STATE_NEGATIVE_INACTIVE; } } // store new pwm value as old pwm value for the nex iteration ttl1_pwm.pwm_t = ttl1_pwm.pwm; } if (TIM2->SR & TIM_SR_UIF) { //TIM2 overflow interrupt jaust clear update interrupt flag TIM2->SR &= ~TIM_SR_UIF; } } void DBW_Pwm_Set_Duty(signed int duty, pwm_t *ttl) { float tmp; unsigned int period_ticks; //check limmits if (duty > 10000) duty = 10000; else if (duty < -10000) duty = -10000; //calculate period - needed to change frequency on-the-fly period_ticks = (unsigned int) 1000000UL / config->motor_pwm_fq; if (duty > 0) tmp = (float) period_ticks * (float) duty / 10000.0F; else if (duty < 0) tmp = (float) period_ticks * (0 - ((float) duty)) / 10000.0F; else tmp = 20; // check minimum duty period; // do not allow duty cylce <20uS if ((period_ticks - (unsigned int) tmp) < 20) { tmp = period_ticks - 20; if (duty > 0) duty = (signed int) tmp * 10000.0F / ttl->period_ticks; else if (duty < 0) duty = 0 - (signed int) tmp * 10000.0F / ttl->period_ticks; } else if (tmp < 20) { tmp = 20; duty = 0; } //update variables var.motor_pwm = duty; //apply new settings __disable_irq(); ttl->duty_ticks = (unsigned int) tmp; ttl->period_ticks = period_ticks; ttl->pwm = duty; __enable_irq(); } unsigned int intrp_1d_uitable(unsigned int x, unsigned char nx, unsigned int *x_table, unsigned int *z_table) { int ix; long interp, interp3; // bound input arguments if (x > x_table[nx - 1]) return (z_table[nx - 1]); else if (x < x_table[0]) return (z_table[0]); // Find bounding indiex in table for (ix = nx - 2; ix > -1; ix--) { // Start w highest index // because will generally have least time for calculations at hi y if (x > x_table[ix]) { break; } } if (ix < 0) ix = 0; // do 1D interpolate interp = (long) (x_table[ix + 1] - x_table[ix]); if (interp != 0) { interp3 = (long) (x - x_table[ix]); interp3 = (100 * interp3); interp = interp3 / interp; } return ((unsigned int) (z_table[ix] + interp * (int) (z_table[ix + 1] - z_table[ix]) / 100)); } signed short intrp_1d_ss_table(signed short x, unsigned char n, signed short *x_table, char sgn, signed short *z_table) { int ix; int interp, interp3; // bound input arguments if (x > x_table[n - 1]) return z_table[n - 1]; if (x < x_table[0]) return z_table[0]; for (ix = n - 2; ix > -1; ix--) { if (x > x_table[ix]) { break; } } if (ix < 0) ix = 0; interp = x_table[ix + 1] - x_table[ix]; if (interp != 0) { interp3 = (x - x_table[ix]); interp3 = (100 * interp3); interp = interp3 / interp; } return ((short) ((int) z_table[ix] + interp * ((int) z_table[ix + 1] - (int) z_table[ix]) / 100)); } int intrp_1dstable(signed short x, unsigned char n, signed short *x_table, char sgn, signed short *z_table) { int ix; long interp, interp3; // bound input arguments if (x > x_table[n - 1]) { if (!sgn) return ((int) z_table[n - 1]); else return ((signed int) ((signed short) z_table[n - 1])); } if (x < x_table[0]) { if (!sgn) return ((signed int) z_table[0]); else return ((signed int) ((signed short) z_table[0])); } for (ix = n - 2; ix > -1; ix--) { if (x > x_table[ix]) { break; } } if (ix < 0) ix = 0; interp = x_table[ix + 1] - x_table[ix]; if (interp != 0) { interp3 = (x - x_table[ix]); interp3 = (100 * interp3); interp = interp3 / interp; } if (!sgn) return ((signed int) (z_table[ix] + interp * (z_table[ix + 1] - z_table[ix]) / 100)); else return ((int) ((signed short) z_table[ix] + interp * ((signed short) z_table[ix + 1] - (signed short) z_table[ix]) / 100)); } void DBW_Start(void) { GPIOB->ODR &= ~D1_Pin; GPIOB->ODR |= D2_Pin; } void DBW_Stop(void) { GPIOB->ODR |= D1_Pin; GPIOB->ODR &= ~D2_Pin; } void DBW_TPS_AutoCal(void) { // if time to process autocal if ((ac_timer == 0) && (ac_mode != 0)) { if (ac_mode == 1) { var.status0 |= DBW_STATUS0_PPSTPS_CAL_F; DBW_Pwm_Set_Duty((signed short) (-6000), (pwm_t*) &ttl1_pwm); DBW_Start(); ac_timer = 1500; ac_mode = 2; } else if (ac_mode == 2) { uint32_t tmp = 0; for (int i = 0; i < 16; i++) tmp += Adc_Read(2); config->tps1_min = tmp >> 4; tmp = 0; for (int i = 0; i < 16; i++) tmp += Adc_Read(3); config->tps2_min = tmp >> 4; DBW_Stop(); ac_timer = 2000; ac_mode = 3; } else if (ac_mode == 3) { DBW_Pwm_Set_Duty((signed short) (9000), (pwm_t*) &ttl1_pwm); DBW_Start(); ac_timer = 1500; ac_mode = 4; } else if (ac_mode == 4) { uint32_t tmp = 0; for (int i = 0; i < 16; i++) tmp += Adc_Read(2); config->tps1_max = tmp >> 4; tmp = 0; for (int i = 0; i < 16; i++) tmp += Adc_Read(3); config->tps2_max = tmp >> 4; DBW_Stop(); DBW_Pwm_Set_Duty((signed short) (0), (pwm_t*) &ttl1_pwm); Apply_Sensor_Calibration(); Write_Config(); ac_timer = 10000; ac_mode = 5; Comms_Reset(&RX); Comms_Reset(&TX); USART1->ICR &= ~USART_CR1_RXNEIE; NVIC_DisableIRQ(USART1_IRQn); } else if (ac_mode == 5) { ac_mode = 0; var.status0 &= ~DBW_STATUS0_PPSTPS_CAL_F; USART1->ICR |= USART_CR1_RXNEIE; NVIC_EnableIRQ(USART1_IRQn); } } } void DBW_Read_sensors(void) { var.pps1_adc = LPF(700, Adc_Read(0), var.pps1_adc); var.pps1 = var.pps1_adc * pps1_gain + pps1_offset; var.pps2_adc = LPF(700, Adc_Read(3), var.pps2_adc); // 1 is orig var.pps2 = var.pps2_adc * pps2_gain + pps2_offset; var.pps_delta = var.pps1 - var.pps2; var.pps = (var.pps1 + var.pps2) >> 1; //Calculate TPS sensor reading var.tps1_adc = LPF(700, Adc_Read(2), var.tps1_adc); var.tps1 = var.tps1_adc * tps1_gain + tps1_offset; var.tps2_adc = LPF(700, Adc_Read(1), var.tps2_adc); //3 is orig var.tps2 = var.tps2_adc * tps2_gain + tps2_offset; // calculate tps and pps delta and values var.tps_delta = var.tps1 - var.tps2; var.tps = (var.tps1 + var.tps2) >> 1; var.pps_delta = var.pps1 - var.pps2; var.pps = (var.pps1 + var.pps2) >> 1; }