#include "main.h" #include "usb_device.h" #include "usbd_cdc_if.h" #include "LCD_I2C_Driver.h" #include "InitSequence.h" #include "button_input.h" #include "Menu.h" #include "Menu_Entry_Type_Checkable.h" #include "Menu_Entry_Type_Numeric.h" #include "Menu_Entry_Type_Percent.h" #include "Menu_Entry_Type_ReadOnly.h" #include "Menu_Entry_Type_Time.h" #include "Config_Store.h" #include "Menu_Controller.h" #include "PressureChannel.h" #include "RelayChannel.h" #include "GPIChannel.h" #include "Menu_Entry_Type_Execute.h" #define SLAVE_ADDRESS_LCD 0x4e ADC_HandleTypeDef hadc1; I2C_HandleTypeDef hi2c1; RTC_HandleTypeDef hrtc; TIM_HandleTypeDef htim2; UART_HandleTypeDef huart1; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_RTC_Init(void); static void MX_ADC1_Init(void); static void MX_I2C1_Init(void); static void MX_TIM2_Init(void); static void MX_USART1_UART_Init(void); extern int16_t rot_counter; extern bool rot_button; using namespace floatpump; void CheckTankConditions(Config_Store &cfg, io::PressureChannel &tankLevel, io::RelayChannel &tankPump) { //Check if config says relay works inverted tankPump.setInverted(cfg.TankPumpInvert.getValue()); //First check if Tank has enough water and disable pump if necessary if(tankLevel.getPercent() < cfg.TankMinLevel.getValue()) { tankPump.switchRelay(io::RelayChannel::state::OFF); } else if(tankLevel.getPercent() > cfg.TankMinLevel.getValue() + cfg.TankHysteresis.getValue()) { tankPump.switchRelay(io::RelayChannel::state::ON); } } void CheckRefillConditions(Config_Store &cfg, io::PressureChannel &tankLevel, io::GPIChannel &refillBlock, io::RelayChannel &refillPump) { if(cfg.RefillEnable.getValue()) { //Check whether refilling is necessary if(tankLevel.getPercent() < cfg.RefillBelow.getValue()) { if(cfg.RefillBlockEnable.getValue() && !refillBlock.getStateBool()) { refillPump.switchRelay(io::RelayChannel::state::ON); } else { refillPump.switchRelay(io::RelayChannel::state::ON); } } else if (tankLevel.getPercent() > cfg.RefillBelow.getValue() + cfg.RefillHysteresis.getValue()) { refillPump.switchRelay(io::RelayChannel::state::OFF); } } else { refillPump.switchRelay(io::RelayChannel::state::OFF); } } int main(void) { Config_Store globalConfig; // Step 1: Initialize HAL HAL_Init(); //Step 2: Configure Clock and RCC SystemClock_Config(); //Step 3: Configure Peripherals MX_GPIO_Init(); MX_RTC_Init(); MX_TIM2_Init(); MX_USB_DEVICE_Init(); MX_ADC1_Init(); MX_I2C1_Init(); MX_USART1_UART_Init(); //Disable Interrupt for Debouncing timer during display initialisation (exact timings are necessary) HAL_NVIC_DisableIRQ(TIM2_IRQn); LCD_I2C_Driver &display = floatpump::LCD_I2C_Driver::getInstance(hi2c1, SLAVE_ADDRESS_LCD); HAL_NVIC_EnableIRQ(TIM2_IRQn); //Run init Sequence InitSequence initializer(display); initializer.runInitSequence(); //Enable green led HAL_GPIO_WritePin(LED5_GPIO_Port, LED5_Pin, GPIO_PIN_RESET); /// /// /// EXPERIMENTAL CODE BEGIN /// /// /// using namespace floatpump; menu::Menu tankmenu("Tank"); bool a_caliblow, a_calibhigh = false; menu::Menu_Entry_Type_Execute t_exCalibLow; menu::Menu_Entry_Type_Execute t_exCalibHigh; t_exCalibLow.linkConfig(&a_caliblow); t_exCalibHigh.linkConfig(&a_calibhigh); menu::Menu_Entry exCalibLow(t_exCalibLow, "Kal. Unt. Punkt"); menu::Menu_Entry exCalibHigh(t_exCalibHigh, "Kal. Ob. Punkt"); menu::Menu_Entry_Type_Numeric t_CalibLow(0); menu::Menu_Entry_Type_Numeric t_CalibHigh(65535); t_CalibLow.linkConfig(globalConfig.TankCalibLow.getLink()); t_CalibHigh.linkConfig(globalConfig.TankCalibHigh.getLink()); menu::Menu_Entry CalibLow(t_CalibLow, "K Unten"); menu::Menu_Entry CalibHigh(t_CalibHigh, "K Oben"); menu::Menu_Entry_Type_Percent t_tankLevel(25); t_tankLevel.linkConfig(globalConfig.TankMinLevel.getLink()); menu::Menu_Entry TankLevel(t_tankLevel, "Min. Fuellst."); menu::Menu_Entry_Type_Percent t_tankHysteresis(5); t_tankHysteresis.linkConfig(globalConfig.TankHysteresis.getLink()); menu::Menu_Entry TankHysteresis(t_tankHysteresis, "Hysterese"); menu::Menu_Entry_Type_Checkable t_tankInvert(false); t_tankInvert.linkConfig(globalConfig.TankPumpInvert.getLink()); menu::Menu_Entry TankInvert(t_tankInvert, "Inv. Relais"); tankmenu.addEntry(exCalibLow); tankmenu.addEntry(exCalibHigh); tankmenu.addEntry(CalibLow); tankmenu.addEntry(CalibHigh); tankmenu.addEntry(TankLevel); tankmenu.addEntry(TankHysteresis); tankmenu.addEntry(TankInvert); menu::Menu refillmenu("Nachspeisung"); menu::Menu_Entry_Type_Checkable t_refillEnable(false); menu::Menu_Entry_Type_Checkable t_refillBlock(true); menu::Menu_Entry_Type_Checkable t_refillBlockInvert(false); t_refillEnable.linkConfig(globalConfig.RefillEnable.getLink()); t_refillBlock.linkConfig(globalConfig.RefillBlockEnable.getLink()); t_refillBlockInvert.linkConfig(globalConfig.RefillBlockInvert.getLink()); menu::Menu_Entry refillEnable(t_refillEnable, "Nachsp. akt."); menu::Menu_Entry refillBlock(t_refillBlock, "Notabschalt."); menu::Menu_Entry refillBlockInvert(t_refillBlockInvert, "NotAbs. Inv."); menu::Menu_Entry_Type_Percent t_RefillBelow(20); menu::Menu_Entry_Type_Percent t_RefillHysteresis(5); t_RefillBelow.linkConfig(globalConfig.RefillBelow.getLink()); t_RefillHysteresis.linkConfig(globalConfig.RefillHysteresis.getLink()); menu::Menu_Entry refillBelow(t_RefillBelow, "Auff. bis"); menu::Menu_Entry refillHysteresis(t_RefillHysteresis, "Hysterese"); refillmenu.addEntry(refillEnable); refillmenu.addEntry(refillBlock); refillmenu.addEntry(refillBlockInvert); refillmenu.addEntry(refillBelow); refillmenu.addEntry(refillHysteresis); menu::Menu mainmenu("Hauptmenu"); mainmenu.addSubmenu(&tankmenu); mainmenu.addSubmenu(&refillmenu); menu::Menu_Controller controller(&mainmenu, display); static int old_pos = 0; //Instantiate Input and Output modules io::PressureChannel tankLevel0(&hadc1, MPWR0_GPIO_Port, MPWR0_Pin, 50); io::GPIChannel refillBlocker0(GPI0_GPIO_Port, GPI0_Pin); io::RelayChannel tankPump(OCHAN0_GPIO_Port, OCHAN0_Pin, true, io::RelayChannel::state::OFF); io::RelayChannel refillPump(OCHAN1_GPIO_Port, OCHAN1_Pin, false, io::RelayChannel::state::OFF); uint32_t last_menu_retrigger = 0; bool f_store = false, f_restore = false; menu::Menu_Entry_Type_Execute t_MStore; menu::Menu_Entry_Type_Execute t_MRestore; t_MStore.linkConfig(&f_store); t_MRestore.linkConfig(&f_restore); menu::Menu_Entry MStore(t_MStore, "Speichern"); menu::Menu_Entry MRestore(t_MRestore, "Laden"); mainmenu.addEntry(MStore); mainmenu.addEntry(MRestore); while (1) { display.LCDSetCursor(0,0); display.LCDSendCString(const_cast(std::string("Tank: " + std::to_string(tankLevel0.getPercent()) + " %").c_str())); display.LCDSetCursor(0, 1); if(tankLevel0.getPercent() < globalConfig.TankMinLevel.getValue()) { display.LCDSendCString(const_cast(std::string("Tank Wassermangel").c_str())); } else { display.LCDSendCString(const_cast(std::string("Tank Normal").c_str())); } if(rot_button) { rot_button = false; last_menu_retrigger = HAL_GetTick(); while(true) { //Display menu until timeout controller.execute(); if (rot_button) { rot_button = false; controller.pushEvent(menu::Menu_Controller::Event::Push); last_menu_retrigger = HAL_GetTick(); } if (old_pos < rot_counter) { controller.pushEvent(menu::Menu_Controller::Event::Increase); old_pos = rot_counter; last_menu_retrigger = HAL_GetTick(); } else if (old_pos > rot_counter) { controller.pushEvent(menu::Menu_Controller::Event::Decrease); old_pos = rot_counter; last_menu_retrigger = HAL_GetTick(); } HAL_Delay(100); //Execute Calibrations if necessary if(a_caliblow) { tankLevel0.calibrateLow(); controller.execute(); HAL_Delay(2000); a_caliblow = false; } else if (a_calibhigh) { tankLevel0.calibrateHigh(); controller.execute(); HAL_Delay(2000); a_calibhigh = false; } //Store or restore if necessary if(f_store) { globalConfig.saveToFlash(); f_store = false; } else if (f_restore) { globalConfig.loadFromFlash(); f_restore = false; } if(HAL_GetTick() > last_menu_retrigger + 10000) { display.LCDClearDisplay(); break; } } } //Poll Sensors tankLevel0.poll(); refillBlocker0.poll(); //Check conditions CheckTankConditions(globalConfig, tankLevel0, tankPump); CheckRefillConditions(globalConfig, tankLevel0, refillBlocker0, refillPump); HAL_Delay(1000); } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.LSEState = RCC_LSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 12; RCC_OscInitStruct.PLL.PLLN = 144; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV6; RCC_OscInitStruct.PLL.PLLQ = 3; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } static void MX_ADC1_Init(void) { /* USER CODE BEGIN ADC1_Init 0 */ /* USER CODE END ADC1_Init 0 */ ADC_ChannelConfTypeDef sConfig = {0}; /* USER CODE BEGIN ADC1_Init 1 */ /* USER CODE END ADC1_Init 1 */ /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) */ hadc1.Instance = ADC1; hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; hadc1.Init.Resolution = ADC_RESOLUTION_12B; hadc1.Init.ScanConvMode = DISABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1; hadc1.Init.DMAContinuousRequests = DISABLE; hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ sConfig.Channel = ADC_CHANNEL_8; sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN ADC1_Init 2 */ /* USER CODE END ADC1_Init 2 */ } static void MX_I2C1_Init(void) { /* USER CODE BEGIN I2C1_Init 0 */ /* USER CODE END I2C1_Init 0 */ /* USER CODE BEGIN I2C1_Init 1 */ /* USER CODE END I2C1_Init 1 */ hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN I2C1_Init 2 */ /* USER CODE END I2C1_Init 2 */ } static void MX_RTC_Init(void) { /* USER CODE BEGIN RTC_Init 0 */ /* USER CODE END RTC_Init 0 */ RTC_TimeTypeDef sTime = {0}; RTC_DateTypeDef sDate = {0}; /* USER CODE BEGIN RTC_Init 1 */ /* USER CODE END RTC_Init 1 */ /** Initialize RTC Only */ hrtc.Instance = RTC; hrtc.Init.HourFormat = RTC_HOURFORMAT_24; hrtc.Init.AsynchPrediv = 127; hrtc.Init.SynchPrediv = 255; hrtc.Init.OutPut = RTC_OUTPUT_DISABLE; hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; if (HAL_RTC_Init(&hrtc) != HAL_OK) { Error_Handler(); } /** Initialize RTC and set the Time and Date */ sTime.Hours = 0; sTime.Minutes = 0; sTime.Seconds = 0; sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; sTime.StoreOperation = RTC_STOREOPERATION_RESET; if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } sDate.WeekDay = RTC_WEEKDAY_MONDAY; sDate.Month = RTC_MONTH_JANUARY; sDate.Date = 1; sDate.Year = 0; if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN RTC_Init 2 */ /* USER CODE END RTC_Init 2 */ } /** * @brief TIM2 Initialization Function * @param None * @retval None */ static void MX_TIM2_Init(void) { //Timer has input of 24MHZ //Scale down with prescaler to 10kHz //Auto reload each ms /* USER CODE BEGIN TIM2_Init 0 */ /* USER CODE END TIM2_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; /* USER CODE BEGIN TIM2_Init 1 */ /* USER CODE END TIM2_Init 1 */ htim2.Instance = TIM2; htim2.Init.Prescaler = 23; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 99; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } //HAL_TIM_Base_Start(&htim2); //Directly start timer base generation in interrupt mode HAL_TIM_Base_Start_IT(&htim2); /* USER CODE BEGIN TIM2_Init 2 */ /* USER CODE END TIM2_Init 2 */ } /** * @brief USART1 Initialization Function * @param None * @retval None */ static void MX_USART1_UART_Init(void) { /* USER CODE BEGIN USART1_Init 0 */ /* USER CODE END USART1_Init 0 */ /* USER CODE BEGIN USART1_Init 1 */ /* USER CODE END USART1_Init 1 */ huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN USART1_Init 2 */ /* USER CODE END USART1_Init 2 */ } static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(XXUNUSED_GPIO_Port, XXUNUSED_Pin, GPIO_PIN_RESET); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOA, OCHAN0_Pin | OCHAN1_Pin | OCHAN2_Pin | BEEP_Pin, GPIO_PIN_RESET); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOA, LED2_Pin | LED3_Pin | LED4_Pin | LED5_Pin, GPIO_PIN_SET); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOB, LED0_Pin | LED1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, MPWR0_Pin | MPWR1_Pin | MPWR2_Pin, GPIO_PIN_RESET); /*Configure GPIO pin : XXUNUSED_Pin */ GPIO_InitStruct.Pin = XXUNUSED_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(XXUNUSED_GPIO_Port, &GPIO_InitStruct); /*Configure GPIO pins : OCHAN0_Pin OCHAN1_Pin OCHAN2_Pin LED2_Pin LED3_Pin LED4_Pin LED5_Pin BEEP_Pin */ GPIO_InitStruct.Pin = OCHAN0_Pin | OCHAN1_Pin | OCHAN2_Pin | LED2_Pin | LED3_Pin | LED4_Pin | LED5_Pin | BEEP_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /*Configure GPIO pins : RRDT_Pin RRSW_pin RRCLK_Pin */ GPIO_InitStruct.Pin = RRDT_Pin | RRSW_Pin | RRCLK_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /*Configure GPIO pin : ADC1_2_Pin */ GPIO_InitStruct.Pin = ADC1_2_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(ADC1_2_GPIO_Port, &GPIO_InitStruct); /*Configure GPIO pins : MPWR0_Pin MPWR1_Pin MPWR2_Pin LED0_Pin LED1_Pin */ GPIO_InitStruct.Pin = MPWR0_Pin | MPWR1_Pin | MPWR2_Pin | LED0_Pin | LED1_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /*Configure GPIO pins : GPI1_Pin GPI2_Pin GPI0_Pin */ GPIO_InitStruct.Pin = GPI1_Pin | GPI2_Pin | GPI0_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLDOWN; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */