/* Reflow Oven Controller * * Copyright (C) 2020 Mario Hüttel * * This file is part of the Reflow Oven Controller Project. * * The reflow oven controller is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * The Reflow Oven Control Firmware 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 the reflow oven controller project. * If not, see . */ /** * @file main.c * @brief Main file for firmware */ #include #include #include #include /* #include */ #include #include #include #include #include #include #include #include #include "fatfs/shimatta_sdio_driver/shimatta_sdio.h" #include #include #include #include #include #include #include #include #include #include #include static void setup_nvic_priorities() { /* No sub priorities */ NVIC_SetPriorityGrouping(2); /* Setup Priorities */ NVIC_SetPriority(ADC_IRQn, 2); NVIC_SetPriority(DMA2_Stream0_IRQn, 1); NVIC_SetPriority(DMA2_Stream7_IRQn, 3); } /* Process parameters are defined static globally to be watched in debugger from any context */ static float pt1000_value; static volatile int pt1000_value_status; static uint32_t rot; static float target_temperature; static struct pid_controller pid; static volatile enum button_state button; FATFS fs; FATFS *fs_ptr = &fs; static inline void uart_gpio_config() { #ifdef DEBUGBUILD rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(SHELL_UART_PORT_RCC_MASK)); SHELL_UART_PORT->MODER &= MODER_DELETE(SHELL_UART_TX_PIN) & MODER_DELETE(SHELL_UART_RX_PIN); SHELL_UART_PORT->MODER |= ALTFUNC(SHELL_UART_RX_PIN) | ALTFUNC(SHELL_UART_TX_PIN); SETAF(SHELL_UART_PORT, SHELL_UART_RX_PIN, SHELL_UART_RX_PIN_ALTFUNC); SETAF(SHELL_UART_PORT, SHELL_UART_TX_PIN, SHELL_UART_TX_PIN_ALTFUNC); #endif } static char shell_uart_tx_buff[128]; static char shell_uart_rx_buff[48]; struct stm_uart shell_uart; static shellmatta_retCode_t write_shell_callback(const char *data, uint32_t len) { uart_send_array_with_dma(&shell_uart, data, len); return SHELLMATTA_OK; } static inline void setup_sell_uart(struct stm_uart *uart) { uart->rx = 1; uart->tx = 1; uart->brr_val = SHELL_UART_BRR_REG_VALUE; uart->rcc_reg = &SHELL_UART_RCC_REG; uart->rcc_bit_no = BITMASK_TO_BITNO(SHELL_UART_RCC_MASK); uart->uart_dev = SHELL_UART_PERIPH; uart->dma_rx_buff = shell_uart_rx_buff; uart->dma_tx_buff = shell_uart_tx_buff; uart->rx_buff_count = sizeof(shell_uart_rx_buff); uart->tx_buff_count = sizeof(shell_uart_tx_buff); uart->base_dma_num = 2; uart->dma_rx_stream = SHELL_UART_RECEIVE_DMA_STREAM; uart->dma_tx_stream = SHELL_UART_SEND_DMA_STREAM; uart->dma_rx_trigger_channel = SHELL_UART_RX_DMA_TRIGGER; uart->dma_tx_trigger_channel = SHELL_UART_TX_DMA_TRIGGER; uart_init(uart); NVIC_EnableIRQ(DMA2_Stream7_IRQn); } static bool mount_sd_card_if_avail(bool mounted) { FRESULT res; if (sdio_check_inserted() && mounted) { memset(fs_ptr, 0, sizeof(FATFS)); return false; } if (!sdio_check_inserted() && !mounted) { res = f_mount(fs_ptr, "0:/", 1); if (res == FR_OK) { return true; } else { return false; } } return mounted; } static inline int32_t handle_pid_controller(struct pid_controller *pid, float target_temperature, float current_pt1000_resistance) { float current_temperature; int32_t pid_out; (void)temp_converter_convert_resistance_to_temp(current_pt1000_resistance, (float *)¤t_temperature); pid_out = (int32_t)pid_sample(pid, target_temperature - current_temperature); /* Blink green LED */ led_set(1, !led_get(1)); return pid_out; } const char *oven_controller_hello_world = "Hello world :)\n"; static inline void setup_system() { setup_nvic_priorities(); systick_setup(); adc_pt1000_setup_meas(); oven_driver_init(); digio_setup_default_all(); led_setup(); loudspeaker_setup(); rotary_encoder_setup(); button_init(); lcd_init(); uart_gpio_config(); setup_sell_uart(&shell_uart); } int main() { bool sd_card_mounted = false; FIL test_file; const char *uart_input; size_t uart_input_len; shellmatta_handle_t shell_handle; int uart_receive_status; uint64_t pid_timestamp = 0ULL; bool pid_controller_active = false; int32_t pid_controller_output; uint64_t display_timestamp = 0ULL; char disp[4][21] = {0}; enum lcd_fsm_ret lcd_ret = LCD_FSM_NOP; target_temperature = 25.0f; setup_system(); shell_handle = shell_init(write_shell_callback); shell_print_motd(shell_handle); if (f_mount(fs_ptr, "0:/", 1) == FR_OK) { sd_card_mounted = true; f_open(&test_file, "hello-world.txt", FA_OPEN_APPEND | FA_WRITE); f_write(&test_file, oven_controller_hello_world, strlen(oven_controller_hello_world), NULL); f_close(&test_file); } pid_init(&pid, 0.1, 0.1, 4.0, 0.0, 100.0, 40.0); pid_zero(&pid); while (1) { sd_card_mounted = mount_sd_card_if_avail(sd_card_mounted); snprintf(&disp[0][0], 17, "SD %smounted", sd_card_mounted ? "" : "un"); pt1000_value_status = adc_pt1000_get_current_resistance(&pt1000_value); if (systick_ticks_have_passed(pid_timestamp, 250)) { pid_timestamp = systick_get_global_tick(); if (pt1000_value_status >= 0 && pid_controller_active) pid_controller_output = handle_pid_controller(&pid, target_temperature, pt1000_value); /* Blink red led in case of temp error */ if (pt1000_value_status < 0) led_set(0, !led_get(0)); } /* Handle error in case PID controller should be active, but temperature measurement failed */ if (pid_controller_active && pt1000_value_status < 0) { /* Disable the oven controller */ oven_driver_set_power(0U); /* Activate loundspeaker permanently */ loudspeaker_set(1); } else if (pid_controller_active) { /* In case temperature measuremnt is okay and controlelr is working, write output power */ oven_driver_set_power(pid_controller_output < 0 ? 0U : (uint8_t)pid_controller_output); } button = button_read_event(); rot = rotary_encoder_get_abs_val(); oven_driver_set_power(rot > 100U ? 100U : rot); /* TODO: handle gui */ snprintf(&disp[1][0], 17, "Rotary: %u", (unsigned int)rot); snprintf(&disp[2][0], 17, "Button: %s", (button == BUTTON_SHORT ? "SHORT" : (button == BUTTON_LONG ? "LONG" : ""))); /* Handle uart input for shell */ uart_receive_status = uart_receive_data_with_dma(&shell_uart, &uart_input, &uart_input_len); if (uart_receive_status >= 0) shell_handle_input(shell_handle, uart_input, uart_input_len); if (systick_ticks_have_passed(display_timestamp, 2) || lcd_ret == LCD_FSM_CALL_AGAIN) { lcd_ret = lcd_fsm_write_buffer(disp); display_timestamp = systick_get_global_tick(); } if (lcd_ret == LCD_FSM_CALL_AGAIN) { /* Nothing */ } else { __WFI(); } } } void sdio_wait_ms(uint32_t ms) { systick_wait_ms(ms); } void DMA2_Stream7_IRQHandler() { uint32_t hisr = DMA2->HISR; DMA2->HIFCR = hisr; if (hisr & DMA_HISR_TCIF7) { uart_tx_dma_complete_int_callback(&shell_uart); } }