/* Copyright 2018 Benjamin Vedder benjamin@vedder.se This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "hw.h" #include "ch.h" #include "hal.h" #include "stm32f4xx_conf.h" #include "utils.h" #include "utils_math.h" #include "terminal.h" #include "commands.h" #include "mc_interface.h" #include "stdio.h" #include "timeout.h" #include "mcpwm.h" #include "mcpwm_foc.h" //#include "gpdrive.h" #include "app.h" #include "mempools.h" // Variables static volatile bool i2c_running = false; static mutex_t shutdown_mutex; static float bt_diff = 0.0; //private functions static void terminal_cmd_doublepulse(int argc, const char **argv); // I2C configuration static const I2CConfig i2cfg = { OPMODE_I2C, 100000, STD_DUTY_CYCLE }; static void terminal_shutdown_now(int argc, const char **argv); static void terminal_button_test(int argc, const char **argv); void hw_init_gpio(void) { chMtxObjectInit(&shutdown_mutex); // GPIO clock enable RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); // LEDs palSetPadMode(LED_GREEN_GPIO, LED_GREEN_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); palSetPadMode(LED_RED_GPIO, LED_RED_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); // GPIOA Configuration: Channel 1 to 3 as alternate function push-pull palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING); palSetPadMode(GPIOA, 9, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING); palSetPadMode(GPIOA, 10, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING); palSetPadMode(GPIOB, 13, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING); palSetPadMode(GPIOB, 14, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING); palSetPadMode(GPIOB, 15, PAL_MODE_ALTERNATE(GPIO_AF_TIM1) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUDR_FLOATING); // Hall sensors palSetPadMode(HW_HALL_ENC_GPIO1, HW_HALL_ENC_PIN1, PAL_MODE_INPUT_PULLUP); palSetPadMode(HW_HALL_ENC_GPIO2, HW_HALL_ENC_PIN2, PAL_MODE_INPUT_PULLUP); palSetPadMode(HW_HALL_ENC_GPIO3, HW_HALL_ENC_PIN3, PAL_MODE_INPUT_PULLUP); // Phase filters palSetPadMode(PHASE_FILTER_GPIO, PHASE_FILTER_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); PHASE_FILTER_OFF(); // Current filter palSetPadMode(GPIOD, 2, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); CURRENT_FILTER_OFF(); /* // AUX pin AUX_OFF(); palSetPadMode(AUX_GPIO, AUX_PIN, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); */ // ADC Pins palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG); palSetPadMode(GPIOA, 1, PAL_MODE_INPUT_ANALOG); palSetPadMode(GPIOA, 2, PAL_MODE_INPUT_ANALOG); palSetPadMode(GPIOA, 3, PAL_MODE_INPUT_ANALOG); palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG); palSetPadMode(GPIOA, 6, PAL_MODE_INPUT_ANALOG); palSetPadMode(GPIOB, 0, PAL_MODE_INPUT_ANALOG); palSetPadMode(GPIOB, 1, PAL_MODE_INPUT_ANALOG); palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG); palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG); palSetPadMode(GPIOC, 2, PAL_MODE_INPUT_ANALOG); palSetPadMode(GPIOC, 3, PAL_MODE_INPUT_ANALOG); palSetPadMode(GPIOC, 4, PAL_MODE_INPUT_ANALOG); //palSetPadMode(GPIOC, 5, PAL_MODE_INPUT_ANALOG); terminal_register_command_callback( "shutdown", "Shutdown VESC now.", 0, terminal_shutdown_now); terminal_register_command_callback( "test_button", "Try sampling the shutdown button", 0, terminal_button_test); terminal_register_command_callback( "double_pulse", "Double pulse test", 0, terminal_cmd_doublepulse); } void hw_setup_adc_channels(void) { // ADC1 regular channels ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 3, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 4, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 5, ADC_SampleTime_15Cycles); //ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 6, ADC_SampleTime_15Cycles); // ADC2 regular channels ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 3, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC2, ADC_Channel_15, 4, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC2, ADC_Channel_0, 5, ADC_SampleTime_15Cycles); //ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 6, ADC_SampleTime_15Cycles); // ADC3 regular channels ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 1, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 3, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC3, ADC_Channel_13, 4, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC3, ADC_Channel_1, 5, ADC_SampleTime_15Cycles); //ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 6, ADC_SampleTime_15Cycles); // Injected channels ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_15Cycles); ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 1, ADC_SampleTime_15Cycles); ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_15Cycles); ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 2, ADC_SampleTime_15Cycles); ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_15Cycles); ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 2, ADC_SampleTime_15Cycles); ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 3, ADC_SampleTime_15Cycles); ADC_InjectedChannelConfig(ADC2, ADC_Channel_11, 3, ADC_SampleTime_15Cycles); ADC_InjectedChannelConfig(ADC3, ADC_Channel_12, 3, ADC_SampleTime_15Cycles); } void hw_start_i2c(void) { i2cAcquireBus(&HW_I2C_DEV); if (!i2c_running) { palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN, PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_OSPEED_MID1 | PAL_STM32_PUDR_PULLUP); palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN, PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_OSPEED_MID1 | PAL_STM32_PUDR_PULLUP); i2cStart(&HW_I2C_DEV, &i2cfg); i2c_running = true; } i2cReleaseBus(&HW_I2C_DEV); } void hw_stop_i2c(void) { i2cAcquireBus(&HW_I2C_DEV); if (i2c_running) { palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN, PAL_MODE_INPUT); palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN, PAL_MODE_INPUT); i2cStop(&HW_I2C_DEV); i2c_running = false; } i2cReleaseBus(&HW_I2C_DEV); } /** * Try to restore the i2c bus */ void hw_try_restore_i2c(void) { if (i2c_running) { i2cAcquireBus(&HW_I2C_DEV); palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN, PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_OSPEED_MID1 | PAL_STM32_PUDR_PULLUP); palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN, PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_OSPEED_MID1 | PAL_STM32_PUDR_PULLUP); palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN); palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN); chThdSleep(1); for(int i = 0;i < 16;i++) { palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN); chThdSleep(1); palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN); chThdSleep(1); } // Generate start then stop condition palClearPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN); chThdSleep(1); palClearPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN); chThdSleep(1); palSetPad(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN); chThdSleep(1); palSetPad(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN); palSetPadMode(HW_I2C_SCL_PORT, HW_I2C_SCL_PIN, PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_OSPEED_MID1 | PAL_STM32_PUDR_PULLUP); palSetPadMode(HW_I2C_SDA_PORT, HW_I2C_SDA_PIN, PAL_MODE_ALTERNATE(HW_I2C_GPIO_AF) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_OSPEED_MID1 | PAL_STM32_PUDR_PULLUP); HW_I2C_DEV.state = I2C_STOP; i2cStart(&HW_I2C_DEV, &i2cfg); i2cReleaseBus(&HW_I2C_DEV); } } bool hw_sample_shutdown_button(void) { chMtxLock(&shutdown_mutex); bt_diff = 0.0; for (int i = 0;i < 3;i++) { palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_INPUT_ANALOG); chThdSleep(5); float val1 = ADC_VOLTS(ADC_IND_SHUTDOWN); chThdSleepMilliseconds(1); float val2 = ADC_VOLTS(ADC_IND_SHUTDOWN); palSetPadMode(HW_SHUTDOWN_GPIO, HW_SHUTDOWN_PIN, PAL_MODE_OUTPUT_PUSHPULL); chThdSleepMilliseconds(1); bt_diff += (val1 - val2); } chMtxUnlock(&shutdown_mutex); //return lonpress; return (bt_diff > 0.5); } static void terminal_shutdown_now(int argc, const char **argv) { (void)argc; (void)argv; DISABLE_GATE(); HW_SHUTDOWN_HOLD_OFF(); } static void terminal_button_test(int argc, const char **argv) { (void)argc; (void)argv; for (int i = 0;i < 40;i++) { commands_printf("BT: %d %.2f", HW_SAMPLE_SHUTDOWN(), (double)bt_diff); chThdSleepMilliseconds(100); } } /* float hw_ENNOID_100_125_get_temp(void) { float t1 = (1.0 / ((logf(NTC_RES(ADC_Value[ADC_IND_TEMP_MOS]) / 10000.0) / 3380.0) + (1.0 / 298.15)) - 273.15); float t2 = (1.0 / ((logf(NTC_RES(ADC_Value[ADC_IND_TEMP_MOS_2]) / 10000.0) / 3380.0) + (1.0 / 298.15)) - 273.15); float t3 = (1.0 / ((logf(NTC_RES(ADC_Value[ADC_IND_TEMP_MOS_3]) / 10000.0) / 3380.0) + (1.0 / 298.15)) - 273.15); float res = 0.0; if (t1 > t2 && t1 > t3) { res = t1; } else if (t2 > t1 && t2 > t3) { res = t2; } else { res = t3; } return res; } */ static void terminal_cmd_doublepulse(int argc, const char **argv) { (void)argc; (void)argv; int preface, pulse1, breaktime, pulse2; int utick; int deadtime = -1; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; if (argc < 5) { commands_printf("Usage: double_pulse [deadtime]"); commands_printf(" preface: idle time in µs"); commands_printf(" pulse1: high time of pulse 1 in µs"); commands_printf(" break: break between pulses in µs\n"); commands_printf(" pulse2: high time of pulse 2 in µs"); commands_printf(" deadtime: overwrite deadtime, in ns"); return; } sscanf(argv[1], "%d", &preface); sscanf(argv[2], "%d", &pulse1); sscanf(argv[3], "%d", &breaktime); sscanf(argv[4], "%d", &pulse2); if (argc == 6) { sscanf(argv[5], "%d", &deadtime); } timeout_configure_IWDT_slowest(); utick = (int)(SYSTEM_CORE_CLOCK / 1000000); mcpwm_deinit(); mcpwm_foc_deinit(); //gpdrive_deinit(); TIM_Cmd(TIM1, DISABLE); TIM_Cmd(TIM4, DISABLE); //TIM4 als Trigger Timer RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); TIM_TimeBaseStructure.TIM_Period = (SYSTEM_CORE_CLOCK / 20000); TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable); TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_Enable); TIM4->CNT = 0; // TIM1 // TIM1 clock enable RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); // Time Base configuration TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = (preface + pulse1) * utick; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); // Channel 1, 2 and 3 Configuration in PWM mode TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_Pulse = preface * utick; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set; TIM_OC1Init(TIM1, &TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_OC2Init(TIM1, &TIM_OCInitStructure); TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_OC3Init(TIM1, &TIM_OCInitStructure); TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_SelectOCxM(TIM1, TIM_Channel_1, TIM_OCMode_PWM2); TIM_CCxCmd(TIM1, TIM_Channel_1, TIM_CCx_Enable); TIM_CCxNCmd(TIM1, TIM_Channel_1, TIM_CCxN_Enable); TIM_SelectOCxM(TIM1, TIM_Channel_2, TIM_OCMode_Inactive); TIM_CCxCmd(TIM1, TIM_Channel_2, TIM_CCx_Enable); TIM_CCxNCmd(TIM1, TIM_Channel_2, TIM_CCxN_Enable); TIM_SelectOCxM(TIM1, TIM_Channel_3, TIM_OCMode_Inactive); TIM_CCxCmd(TIM1, TIM_Channel_3, TIM_CCx_Enable); TIM_CCxNCmd(TIM1, TIM_Channel_3, TIM_CCxN_Enable); TIM_GenerateEvent(TIM1, TIM_EventSource_COM); // Automatic Output enable, Break, dead time and lock configuration TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable; TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF; if (deadtime < 0) { TIM_BDTRInitStructure.TIM_DeadTime = conf_general_calculate_deadtime(HW_DEAD_TIME_NSEC, SYSTEM_CORE_CLOCK); } else { TIM_BDTRInitStructure.TIM_DeadTime = conf_general_calculate_deadtime(deadtime, SYSTEM_CORE_CLOCK); } TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable; TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High; TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable; TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure); TIM_CCPreloadControl(TIM1, ENABLE); TIM_ARRPreloadConfig(TIM1, ENABLE); TIM1->CNT = 0; TIM1->EGR = TIM_EGR_UG; TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Trigger); TIM_SelectInputTrigger(TIM1, TIM_TS_ITR3); TIM_SelectOnePulseMode(TIM1, TIM_OPMode_Single); TIM_CtrlPWMOutputs(TIM1, ENABLE); TIM_Cmd(TIM1, ENABLE); //Timer 4 triggert Timer 1 TIM_Cmd(TIM4, ENABLE); TIM_Cmd(TIM4, DISABLE); TIM1->ARR = (breaktime + pulse2) * utick; TIM1->CCR1 = breaktime * utick; while (TIM1->CNT != 0); TIM_Cmd(TIM4, ENABLE); chThdSleepMilliseconds(1); TIM_CtrlPWMOutputs(TIM1, DISABLE); mc_configuration* mcconf = mempools_alloc_mcconf(); *mcconf = *mc_interface_get_configuration(); switch (mcconf->motor_type) { case MOTOR_TYPE_BLDC: case MOTOR_TYPE_DC: mcpwm_init(mcconf); break; case MOTOR_TYPE_FOC: mcpwm_foc_init(mcconf, mcconf); break; /*case MOTOR_TYPE_GPD: //gpdrive_init(mcconf); break; */ default: break; } commands_printf("Done"); return; }