From 6064103cd2cd286233d54b23c77032eecc25513b Mon Sep 17 00:00:00 2001 From: Nathael Pajani Date: Thu, 13 Jun 2019 20:27:13 +0200 Subject: [PATCH] First release of app for v08 of Scialys module --- v08/Makefile | 21 ++ v08/config.c | 249 ++++++++++++++++++ v08/config.h | 95 +++++++ v08/interface.c | 657 ++++++++++++++++++++++++++++++++++++++++++++++++ v08/interface.h | 69 +++++ v08/main.c | 549 ++++++++++++++++++++++++++++++++++++++++ v08/time.c | 98 ++++++++ v08/time.h | 34 +++ v08/uSD.c | 132 ++++++++++ v08/uSD.h | 42 ++++ 10 files changed, 1946 insertions(+) create mode 100644 v08/Makefile create mode 100644 v08/config.c create mode 100644 v08/config.h create mode 100644 v08/interface.c create mode 100644 v08/interface.h create mode 100644 v08/main.c create mode 100644 v08/time.c create mode 100644 v08/time.h create mode 100644 v08/uSD.c create mode 100644 v08/uSD.h diff --git a/v08/Makefile b/v08/Makefile new file mode 100644 index 0000000..41ae555 --- /dev/null +++ b/v08/Makefile @@ -0,0 +1,21 @@ +# Makefile for apps + +MODULE = $(shell basename $(shell cd .. && pwd && cd -)) +NAME = $(shell basename $(CURDIR)) + +# Add this to your ~/.vimrc in order to get proper function of :make in vim : +# let $COMPILE_FROM_IDE = 1 +ifeq ($(strip $(COMPILE_FROM_IDE)),) + PRINT_DIRECTORY = --no-print-directory +else + PRINT_DIRECTORY = + LANG = C +endif + +.PHONY: $(NAME).bin +$(NAME).bin: + @make -C ../../.. ${PRINT_DIRECTORY} NAME=$(NAME) MODULE=$(MODULE) apps/$(MODULE)/$(NAME)/$@ + +clean mrproper: + @make -C ../../.. ${PRINT_DIRECTORY} $@ + diff --git a/v08/config.c b/v08/config.c new file mode 100644 index 0000000..7e321e2 --- /dev/null +++ b/v08/config.c @@ -0,0 +1,249 @@ +/**************************************************************************** + * apps/scialys/v07/config.c + * + * Copyright 2018 Nathael Pajani + * + * + * 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 "core/system.h" +#include "core/systick.h" +#include "core/pio.h" + +#include "drivers/serial.h" +#include "drivers/gpio.h" +#include "drivers/i2c.h" +#include "drivers/adc.h" +#include "drivers/ssp.h" +#include "drivers/timers.h" + + +#include "extdrv/status_led.h" +#include "extdrv/max31855_thermocouple.h" + +#include "lib/stdio.h" + +#include "config.h" +#include "interface.h" +#include "time.h" +#include "uSD.h" + +#define SELECTED_FREQ FREQ_SEL_48MHz + + +uint8_t config_got_interface = 1; + +/***************************************************************************** */ +/* Pins configuration */ +/* pins blocks are passed to set_pins() for pins configuration. + * Unused pin blocks can be removed safely with the corresponding set_pins() call + * All pins blocks may be safelly merged in a single block for single set_pins() call.. + */ +const struct pio_config common_pins[] = { + /* UART 0 */ + { LPC_UART0_RX_PIO_0_1, LPC_IO_DIGITAL }, + { LPC_UART0_TX_PIO_0_2, LPC_IO_DIGITAL }, + /* UART 1 */ + { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL }, + { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL }, + /* I2C 0 */ + { LPC_I2C0_SCL_PIO_0_10, (LPC_IO_DIGITAL | LPC_IO_OPEN_DRAIN_ENABLE) }, + { LPC_I2C0_SDA_PIO_0_11, (LPC_IO_DIGITAL | LPC_IO_OPEN_DRAIN_ENABLE) }, + /* SPI */ + { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL }, + { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL }, + { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL }, + /* ADC */ + { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* Home */ + { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* Production */ + { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* Load */ + /* GPIO */ + { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* External switch input */ + { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */ + { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Fan control */ + { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */ + { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Thermocouple chip select */ + { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */ + { LPC_GPIO_0_18, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */ + { LPC_GPIO_0_20, LPC_IO_DIGITAL }, /* RGB Leds */ + { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* Oled Reset */ + { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */ + { LPC_GPIO_1_2, LPC_IO_DIGITAL }, /* Button 4 */ + { LPC_GPIO_1_3, LPC_IO_DIGITAL }, /* Button 3 */ + { LPC_GPIO_1_4, LPC_IO_DIGITAL }, /* Button 2 */ + { LPC_GPIO_1_5, LPC_IO_DIGITAL }, /* Button 1 */ + ARRAY_LAST_PIO, +}; + +/* Internal status leds */ +const struct pio status_light_green = LPC_GPIO_0_26; +const struct pio status_light_red = LPC_GPIO_0_27; + +/* Inputs */ +/* External signals */ +const struct pio zero_cross_in_pin = LPC_GPIO_0_4; +const struct pio ext_disable_in_pin = LPC_GPIO_0_3; + +/* Outputs */ +/* AC output control (Mosfet) */ +const struct pio ac_ctrl = LPC_GPIO_0_6; +const struct pio fan_ctrl = LPC_GPIO_0_5; +/* Chip selects */ +const struct pio uSD_cs = LPC_GPIO_1_1; /* uSD card */ +const struct pio th_cs = LPC_GPIO_0_7; /* Thermocouple */ +const struct pio uext_cs = LPC_GPIO_0_18; /* UEXT module (optionnal) */ + + + +/***************************************************************************** */ +/* Basic system init and configuration */ +static volatile int got_wdt_int = 0; +void wdt_callback(void) +{ + got_wdt_int = 1; +} + +const struct wdt_config wdconf = { + .clk_sel = WDT_CLK_IRC, + .intr_mode_only = 0, + .callback = wdt_callback, + .locks = 0, + .nb_clk = 0x03FFFFFF, /* 0x3FF to 0x03FFFFFF */ + .wdt_window = 0, + .wdt_warn = 0x3FF, +}; + +void system_init() +{ + /* Configure the Watchdog */ + watchdog_config(&wdconf); + system_set_default_power_state(); + clock_config(SELECTED_FREQ); + set_pins(common_pins); + gpio_on(); + status_led_config(&status_light_green, &status_light_red); + /* System tick timer MUST be configured and running in order to use the sleeping + * functions */ + systick_timer_on(1); /* 1ms */ + systick_start(); +} + +/* Define our fault handler. This one is not mandatory, the dummy fault handler + * will be used when it's not overridden here. + * Note : The default one does a simple infinite loop. If the watchdog is deactivated + * the system will hang. + */ +void fault_info(const char* name, uint32_t len) +{ + uprintf(UART0, name); + while (1); +} + + +/***************************************************************************** */ +/* Internal modules */ +const struct lpc_tc_config ac_timer_conf_zc = { + .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH, + .match_control = { LPC_TIMER_INT_RESET_ON_MATCH, 0, 0, 0, }, +}; +const struct lpc_tc_config ac_timer_conf_delay = { + .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH, + .match_control = { LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, 0, }, +}; +const struct lpc_tc_config ac_timer_conf_power_track = { + .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH, + .match_control = { LPC_TIMER_INT_RESET_ON_MATCH, 0, 0, 0, }, +}; + +extern void config_rx(uint8_t c); +extern void comm_rx(uint8_t c); +extern void ac_switch_on(uint32_t flags); +extern void zero_cross(uint32_t flags); +extern void power_track(uint32_t flags); + +void modules_config(void) +{ + uart_on(UART0, 115200, config_rx); + uart_on(UART1, 115200, comm_rx); + i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER); + ssp_master_on(SSP_BUS_0, LPC_SSP_FRAME_SPI, 8, 4*1000*1000); + adc_on(NULL); + adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2), LPC_ADC_SEQ(0)); + timer_on(LPC_TIMER_32B0, 0, zero_cross); + timer_counter_config(LPC_TIMER_32B0, &ac_timer_conf_zc); + timer_on(LPC_TIMER_32B1, 0, ac_switch_on); + timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf_delay); + timer_on(LPC_TIMER_16B0, 0, power_track); + timer_counter_config(LPC_TIMER_16B0, &ac_timer_conf_power_track); +} + + +/***************************************************************************** */ +extern void zero_cross_detect(uint32_t gpio); +void board_io_config(void) +{ + /* Immediatly turn off Mosfet / Triac */ + config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 1); + /* Start with FAN ON */ + config_gpio(&fan_ctrl, 0, GPIO_DIR_OUT, 1); + + /* Configure Input GPIO */ + config_gpio(&ext_disable_in_pin, 0, GPIO_DIR_IN, 0); + + /* Zero-crossing detection */ + set_gpio_callback(zero_cross_detect, &zero_cross_in_pin, EDGE_FALLING); +} + + + +/***************************************************************************** */ +/* Configure external components */ + +/* Thermocouple reading */ +const struct max31855_sensor_config thermocouple = { + .ssp_bus_num = 0, + .chip_select = LPC_GPIO_0_7, +}; + +/* RTC and time */ +#define RTC_ADDR 0xA2 + + +int external_config(uint32_t uart) +{ + int ret = 0; + + /* Configure GPIO for specific board usage */ + board_io_config(); + + /* uSD card */ + uSD_config((struct pio *)&uSD_cs, SSP_BUS_0); + uSD_detect(uart); + + /* RTC */ + time_config(I2C0, RTC_ADDR); + time_init_check(uart); + + /* Thermocouple configuration */ + max31855_sensor_config(&thermocouple); + + /* Configure interface board */ + ret = interface_config(uart); + if (ret != 0) { + return -1; + } + return 0; +} diff --git a/v08/config.h b/v08/config.h new file mode 100644 index 0000000..7a2a307 --- /dev/null +++ b/v08/config.h @@ -0,0 +1,95 @@ +/**************************************************************************** + * apps/scialys/v07/config.h + * + * Copyright 2018 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include "core/system.h" +#include "core/systick.h" +#include "core/pio.h" + +#include "drivers/serial.h" +#include "drivers/gpio.h" +#include "drivers/i2c.h" +#include "drivers/adc.h" +#include "drivers/ssp.h" +#include "drivers/timers.h" + +#include "extdrv/status_led.h" +#include "extdrv/max31855_thermocouple.h" +#include "extdrv/ws2812.h" + +#include "lib/stdio.h" + + +#define SELECTED_FREQ FREQ_SEL_48MHz + +/***************************************************************************** */ +/* Pins configuration */ +extern const struct pio status_led_green; +extern const struct pio status_led_red; + +/* Inputs */ +/* External signals */ +extern const struct pio zero_cross_in_pin; +extern const struct pio ext_disable_in_pin; + +/* Outputs */ +/* AC output control (Mosfet) */ +extern const struct pio ac_ctrl; +extern const struct pio fan_ctrl; +/* Chip selects */ +extern const struct pio uSD_cs; /* uSD card */ +extern const struct pio th_cs; /* Thermocouple */ +extern const struct pio uext_cs; /* UEXT module (optionnal) */ + + + +/***************************************************************************** */ +extern const struct max31855_sensor_config thermocouple; + + +/***************************************************************************** */ +/* Configuration */ + +/* Configure the watchdog, clocks, systick, power and common pins */ +void system_init(); + +/* Define our fault handler. This one is not mandatory, the dummy fault handler + * will be used when it's not overridden here. + * Note : The default one does a simple infinite loop. If the watchdog is deactivated + * the system will hang. + */ +void fault_info(const char* name, uint32_t len); + + +/* Configure modules and main functions */ +void modules_config(void); + +/* Configure GPIO for specific board usage */ +void board_io_config(void); + +/* Configure external components */ +int external_config(uint32_t uart); + + +#endif /* CONFIG_H */ + diff --git a/v08/interface.c b/v08/interface.c new file mode 100644 index 0000000..289c062 --- /dev/null +++ b/v08/interface.c @@ -0,0 +1,657 @@ +/**************************************************************************** + * apps/scialys/v07/interface.c + * + * Copyright 2018 Nathael Pajani + * + * + * 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 "core/system.h" +#include "core/systick.h" +#include "core/pio.h" + +#include "drivers/serial.h" +#include "drivers/gpio.h" +#include "drivers/i2c.h" +#include "drivers/adc.h" + +#include "extdrv/status_led.h" +#include "extdrv/ws2812.h" +#include "extdrv/tmp101_temp_sensor.h" +#include "extdrv/ssd130x_oled_driver.h" +#include "extdrv/ssd130x_oled_buffer.h" + +#include "lib/stdio.h" +#include "lib/font.h" +#include "lib/time.h" + +#include "interface.h" +#include "time.h" + +/***************************************************************************** */ +/* Buttons inputs on front panel */ +const struct pio button_up = LPC_GPIO_1_5; +#define GPIO_BUTTON_UP 5 +const struct pio button_right = LPC_GPIO_1_3; +#define GPIO_BUTTON_RIGHT 3 +const struct pio button_left = LPC_GPIO_1_4; +#define GPIO_BUTTON_LEFT 4 +const struct pio button_down = LPC_GPIO_1_2; +#define GPIO_BUTTON_DOWN 2 +/* ISP /User button OK */ +const struct pio button_ok = LPC_GPIO_0_12; +#define GPIO_BUTTON_OK 12 + + +/* Led control data pin */ +const struct pio ws2812_data_out_pin = LPC_GPIO_0_20; + /* Oled Reset */ +const struct pio oled_reset = LPC_GPIO_0_21; + + +static uint8_t interface_board_present = 0; +static uint8_t power_board_present = 0; + +/***************************************************************************** */ +/* TMP101 I2C temperature sensor on Power board */ +#define TMP101_ADDR1 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */ +struct tmp101_sensor_config tmp101_sensor_power = { + .bus_num = I2C0, + .addr = TMP101_ADDR1, + .resolution = TMP_RES_ELEVEN_BITS, +}; + +/* TMP101 I2C temperature sensor on display board */ +#define TMP101_ADDR0 0x90 /* Pin Addr0 (pin5 of tmp101) connected to GND */ +struct tmp101_sensor_config tmp101_sensor_display = { + .bus_num = I2C0, + .addr = TMP101_ADDR0, + .resolution = TMP_RES_ELEVEN_BITS, +}; + +/***************************************************************************** */ +/* Oled Display */ +static uint8_t gddram[ 4 + GDDRAM_SIZE ]; + +#define DISPLAY_ADDR 0x78 +struct oled_display display = { + .bus_type = SSD130x_BUS_I2C, + .address = DISPLAY_ADDR, + .bus_num = I2C0, + .charge_pump = SSD130x_INTERNAL_PUMP, + .video_mode = SSD130x_DISP_NORMAL, + .contrast = 128, + .scan_dir = SSD130x_SCAN_BOTTOM_TOP, + .read_dir = SSD130x_RIGHT_TO_LEFT, + .display_offset_dir = SSD130x_MOVE_TOP, + .display_offset = 4, + .gddram = gddram, +}; + +#define ROW(x) VERTICAL_REV(x) +DECLARE_FONT(font); + +void display_char(uint8_t line, uint8_t col, uint8_t c) +{ + uint8_t tile = (c > FIRST_FONT_CHAR) ? (c - FIRST_FONT_CHAR) : 0; + uint8_t* tile_data = (uint8_t*)(&font[tile]); + ssd130x_buffer_set_tile(gddram, col, line, tile_data); +} +int display_line(uint8_t line, uint8_t col, const char* text) +{ + int len = strlen((char*)text); + int i = 0; + + if (interface_board_present == 0) { + return -1; + } + + for (i = 0; i < len; i++) { + uint8_t tile = (text[i] > FIRST_FONT_CHAR) ? (text[i] - FIRST_FONT_CHAR) : 0; + uint8_t* tile_data = (uint8_t*)(&font[tile]); + ssd130x_buffer_set_tile(gddram, col++, line, tile_data); + if (col >= (OLED_LINE_CHAR_LENGTH)) { + col = 0; + line++; + if (line >= SSD130x_NB_PAGES) { + len = i; + break; + } + } + } + return len; +} + +int erase_line(uint8_t line) +{ + uint8_t* tile_data = (uint8_t*)(&font[0]); + uint8_t col = 0; + int i = 0; + + if (interface_board_present == 0) { + return -1; + } + + for (i = 0; i < OLED_LINE_CHAR_LENGTH; i++) { + ssd130x_buffer_set_tile(gddram, col++, line, tile_data); + } + /* Update Oled display */ + return ssd130x_display_full_screen(&display); +} + +void erase_screen_content(void) +{ + /* Erase screen data ram */ + ssd130x_buffer_set(gddram, 0x00); +} + + +int temp_read(uint32_t uart, int* deci_degrees_disp, int* deci_degrees_power) +{ + int ret = 0; + int conv = 0; + + if (interface_board_present != 0) { + ret = tmp101_sensor_read(&tmp101_sensor_display, NULL, deci_degrees_disp); + if (ret == 0) { + conv |= CONV_DISPLAY_OK; + } else { + uprintf(uart, "TMP101 read error on display board : %d\n", ret); + } + } + if (power_board_present != 0) { + ret = tmp101_sensor_read(&tmp101_sensor_power, NULL, deci_degrees_power); + if (ret == 0) { + conv |= CONV_POWER_OK; + } else { + uprintf(uart, "TMP101 read error on power board : %d\n", ret); + } + } + return conv; +} + + +/***************************************************************************** */ +volatile uint8_t button_pressed = 0; +void button_callback(uint32_t gpio) +{ + switch (gpio) { + case GPIO_BUTTON_OK: + button_pressed |= BUTTON_OK; + break; + case GPIO_BUTTON_UP: + button_pressed |= BUTTON_UP; + break; + case GPIO_BUTTON_DOWN: + button_pressed |= BUTTON_DOWN; + break; + case GPIO_BUTTON_LEFT: + button_pressed |= BUTTON_LEFT; + break; + case GPIO_BUTTON_RIGHT: + button_pressed |= BUTTON_RIGHT; + break; + } +} + +int interface_config(uint32_t uart) +{ + int ret = 0; + + /* TMP101 sensor config on power board */ + power_board_present = 1; + ret = tmp101_sensor_config(&tmp101_sensor_power); + if (ret != 0) { + uprintf(uart, "Temp config error on power board: %d\n", ret); + power_board_present = 0; + } else { + ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_power); + if (ret != 0) { + uprintf(uart, "Temp config error on power board: %d\n", ret); + power_board_present = 0; + } + } + + /* TMP101 sensor config on display board */ + ret = tmp101_sensor_config(&tmp101_sensor_display); + if (ret != 0) { + uprintf(uart, "Temp config error on display board: %d\n", ret); + interface_board_present = 0; + return -1; + } + ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_display); + if (ret != 0) { + uprintf(uart, "Temp config error on display board: %d\n", ret); + interface_board_present = 0; + return -2; + } + interface_board_present = 1; + + /* Buttons inputs on front panel */ + /* Activate on Rising edge (button release) */ + set_gpio_callback(button_callback, &button_up, EDGE_RISING); + set_gpio_callback(button_callback, &button_left, EDGE_RISING); + set_gpio_callback(button_callback, &button_right, EDGE_RISING); + set_gpio_callback(button_callback, &button_down, EDGE_RISING); + set_gpio_callback(button_callback, &button_ok, EDGE_RISING); + + /* WS2812B Leds on display board */ + ws2812_config(&ws2812_data_out_pin); + + /* Configure and start display */ + config_gpio(&oled_reset, 0, GPIO_DIR_OUT, 1); /* Release reset signal */ + ssd130x_display_on(&display); + erase_screen_content(); + ssd130x_display_full_screen(&display); + + ws2812_set_pixel(0, 0x05, 0x15, 0x08); + ws2812_send_frame(0); + + uprintf(uart, "Config OK\n"); + + return 0; +} + + + +/***************************************************************************** */ + /* Menu part */ + +enum interface_modes { + MODE_RUN = 0, + MODE_CONFIG, + MODE_DISPLAY, +}; +static int interface_mode = MODE_RUN; +volatile int manual_activation_request = 0; + +enum menu_list { + MAIN_MENU, + MANUAL_MODE, + DATE_CONFIG, + LIMITS, + FUNCTIONS, + TEST_MODE, + SAVE_CONFIG, + NB_MENU, /* This one must be the last */ +}; +static const char* menu_titles[] = { + [MAIN_MENU] = "Menu principal", + [MANUAL_MODE] = "Marche Forcee", + [DATE_CONFIG] = "Regl. Heure", + [LIMITS] = "Limites", + [FUNCTIONS] = "Fonctions", + [TEST_MODE] = "Test mode", + [SAVE_CONFIG] = "Save config", +}; +static uint8_t current_menu = MAIN_MENU; +static uint8_t current_entry = MANUAL_MODE; + +enum func_menu_list { + FUNC_LOAD_TYPE, + FUNC_CMD_TYPE, + FUNC_NB_MENU, /* This one must be the last */ +}; +static const char* func_modes_titles[] = { + [FUNC_LOAD_TYPE] = "Load Type", + [FUNC_CMD_TYPE] = "CMD type", +}; +static uint8_t func_cur_menu = FUNC_LOAD_TYPE; +static uint8_t func_cur_entry = FUNC_LOAD_TYPE; + +enum limits_menu_list { + TEMP_FORCED_IN, + TEMP_FORCED_TARGET, + FORCED_MODE_VAL, + SUNNY_DAY_VALUE, + LIM_NB_MENU, /* This one must be the last */ +}; +static const char* limits_modes_titles[] = { + [TEMP_FORCED_IN] = "Water Min", + [TEMP_FORCED_TARGET] = "Water Target", + [FORCED_MODE_VAL] = "Forced cmd", + [SUNNY_DAY_VALUE] = "Sun Prod", +}; +static uint8_t limits_cur_menu = TEMP_FORCED_IN; +static uint8_t limits_cur_entry = TEMP_FORCED_IN; + +enum test_menu_list { + TEST_FAN, + TEST_12V, + TEST_CMDV, + TEST_NB_MENU, /* This one must be the last */ +}; +static const char* test_modes_titles[] = { + [TEST_FAN] = "Fan", + [TEST_12V] = "DC 12V", + [TEST_CMDV] = "CMD Val", +}; +extern uint8_t force_fan; +extern int8_t force_cmd; +extern uint32_t zc_cnt; +extern void zero_cross_detect(uint32_t gpio); +static uint8_t test_cur_entry = TEST_FAN; + + +/* Current sub-menu level. main menu is level 0 */ +int sub_menu_level = 0; + + +void config_interface_handle(void) +{ + char line[DISP_LLEN]; + uint8_t button = 0; + + if (current_menu >= NB_MENU) { + current_menu = MAIN_MENU; + } + button = button_pressed; + button_pressed = 0; + /* Start config with a blank screen */ + erase_screen_content(); + + /* Display current menu title */ + display_line(0, 1, menu_titles[current_menu]); + + switch (current_menu) { + case MAIN_MENU: { + int i = 0; + for (i = 1; i < NB_MENU; i++) { + if (i != current_entry) { + display_line(i + 1, 3, menu_titles[i]); + } else { + snprintf(line, DISP_LLEN, " ->%s", menu_titles[i]); + display_line(i + 1, 0, line); + } + } + if (button & BUTTON_UP) { + current_entry -= 1; + } + if (button & BUTTON_DOWN) { + current_entry += 1; + } + if (current_entry >= NB_MENU) { + current_entry = 1; + } else if (current_entry == 0) { + current_entry = NB_MENU - 1; + } + if (button & (BUTTON_OK | BUTTON_RIGHT)) { + current_menu = current_entry; + sub_menu_level = 1; + } + if (button & BUTTON_LEFT) { + interface_mode = MODE_RUN; + } + } + break; + case DATE_CONFIG: { + static uint8_t date_idx = 0; + struct rtc_time now; + snprintf(line, DISP_LLEN, "^"); + display_line(3, 6 + (date_idx * 3), line); + rtc_pcf85363_time_read(&rtc_conf, &now); + snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec); + display_line(4, 6, line); + snprintf(line, DISP_LLEN, "V"); + display_line(5, 6 + (date_idx * 3), line); + if ((button & BUTTON_RIGHT) && (date_idx < 2)) { + date_idx++; + } + if ((button & BUTTON_LEFT) && (date_idx > 0)) { + date_idx--; + } + if (button & (BUTTON_UP | BUTTON_DOWN)) { + uint32_t seconds = rtc_pcf85363_daytime_to_seconds(&now); + uint32_t add[6] = { + 3600, 60, 1, + (23 * 3600), (23* 3600 + 59 * 60), (23 * 3600 + 59 * 60 + 59), + }; + if (button & BUTTON_UP) { + if (seconds < add[date_idx + 3]) { + seconds += add[date_idx]; + } else { + seconds -= add[date_idx + 3]; + } + } else { + if (seconds < add[date_idx]) { + seconds += add[date_idx + 3]; + } else { + seconds -= add[date_idx]; + } + } + rtc_pcf85363_seconds_to_daytime(&now, seconds); + rtc_pcf85363_time_write(&rtc_conf, &now); + } + if (button & BUTTON_OK) { + sub_menu_level = 0; + current_menu = MAIN_MENU; + } + } + break; + case LIMITS: { + if (sub_menu_level == 1) { + int i = 0; + for (i = 0; i < LIM_NB_MENU; i++) { + if (i != limits_cur_entry) { + display_line(i + 2, 3, limits_modes_titles[i]); + } else { + snprintf(line, DISP_LLEN, " ->%s", limits_modes_titles[i]); + display_line(i + 2, 0, line); + } + } + if (button & BUTTON_UP) { + limits_cur_entry -= 1; + } + if (button & BUTTON_DOWN) { + limits_cur_entry += 1; + } + if (limits_cur_entry >= LIM_NB_MENU) { + limits_cur_entry = 0; + } else if (limits_cur_entry == 0xFF) { + limits_cur_entry = LIM_NB_MENU - 1; + } + if (button & (BUTTON_OK | BUTTON_RIGHT)) { + limits_cur_menu = limits_cur_entry; + sub_menu_level = 2; + } + if (button & BUTTON_LEFT) { + sub_menu_level = 0; + current_menu = MAIN_MENU; + } + } else { + } + } + break; + case FUNCTIONS: { + if (sub_menu_level == 1) { + int i = 0; + for (i = 0; i < FUNC_NB_MENU; i++) { + if (i != func_cur_entry) { + display_line(i + 2, 3, func_modes_titles[i]); + } else { + snprintf(line, DISP_LLEN, " ->%s", func_modes_titles[i]); + display_line(i + 2, 0, line); + } + } + if (button & BUTTON_UP) { + func_cur_entry -= 1; + } + if (button & BUTTON_DOWN) { + func_cur_entry += 1; + } + if (func_cur_entry >= FUNC_NB_MENU) { + func_cur_entry = 0; + } else if (func_cur_entry == 0xFF) { + func_cur_entry = FUNC_NB_MENU - 1; + } + if (button & (BUTTON_OK | BUTTON_RIGHT)) { + func_cur_menu = func_cur_entry; + sub_menu_level = 2; + } + if (button & BUTTON_LEFT) { + sub_menu_level = 0; + current_menu = MAIN_MENU; + } + } else { + } + } + break; + case TEST_MODE: { + int i = 0; + uint8_t states[3]; + + states[TEST_FAN] = force_fan; + states[TEST_12V] = (zc_cnt != 0) ? 1 : 0; + states[TEST_CMDV] = force_cmd; + + for (i = 0; i < TEST_NB_MENU; i++) { + if (i != test_cur_entry) { + display_line(i + 2, 3, test_modes_titles[i]); + } else { + snprintf(line, DISP_LLEN, " ->%s : %d", test_modes_titles[i], states[i]); + display_line(i + 2, 0, line); + } + } + if (button & BUTTON_UP) { + test_cur_entry -= 1; + } + if (button & BUTTON_DOWN) { + test_cur_entry += 1; + } + if (test_cur_entry >= TEST_NB_MENU) { + test_cur_entry = 0; + } else if (test_cur_entry == 0xFF) { + test_cur_entry = TEST_NB_MENU - 1; + } + if (button & (BUTTON_LEFT | BUTTON_RIGHT)) { + switch (test_cur_entry) { + case TEST_FAN: + force_fan = !force_fan; + break; + case TEST_12V: + /* Generate a fake zero-cross detect by calling the handler directly */ + zero_cross_detect(0); + break; + case TEST_CMDV: + if (force_cmd >= 0) { + force_cmd = -1; + } else { + force_cmd = 0; + } + default: + break; + } + } + if (button & BUTTON_OK) { + sub_menu_level = 0; + current_menu = MAIN_MENU; + } + } + break; + case SAVE_CONFIG: { + } + break; + case MANUAL_MODE: + manual_activation_request = -1; + /* Fallback */ + default: + interface_mode = MODE_RUN; + current_menu = MAIN_MENU; + break; + } + /* Update Oled display */ + ssd130x_display_full_screen(&display); +} + + +/***************************************************************************** */ + /* usual menu and switch to config menu */ + +extern int water_centi_degrees; +extern uint32_t moyenne_solar; +extern uint32_t moyenne_home; +extern volatile uint8_t command_val; + +void interface_update(char heat_mode) +{ + int abs_centi = water_centi_degrees; + + if (water_centi_degrees < 0) { + abs_centi = -water_centi_degrees; + } + + /* Debug */ + if (button_pressed != 0) { + uprintf(UART0, "Button : 0x%02x\n", button_pressed); + } + + if (interface_mode == MODE_RUN) { + if (button_pressed != 0) { + if (button_pressed & BUTTON_OK) { + manual_activation_request = -1; + } + if (button_pressed & BUTTON_UP) { + interface_mode = MODE_CONFIG; + } + if (force_cmd >= 0) { + if ((button_pressed & BUTTON_RIGHT) && (force_cmd <= 95)) { + force_cmd += 5; + } + if ((button_pressed & BUTTON_LEFT) && (force_cmd >= 5)) { + force_cmd -= 5; + } + } + button_pressed = 0; + } + + if (interface_board_present != 0) { + char line[DISP_LLEN]; + + /* Start with a blank screen */ + erase_screen_content(); + + /* Update time and time display on internal memory */ + rtc_pcf85363_time_read(&rtc_conf, &now); + snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec); + display_line(0, 0, line); + + /* Display info */ + snprintf(line, DISP_LLEN, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F); + display_line(2, 0, line); + snprintf(line, DISP_LLEN, "Prod :% 2d,%03dA", (moyenne_solar / 1000), ((moyenne_solar % 1000) / 10)); + display_line(3, 0, line); + snprintf(line, DISP_LLEN, "Conso:% 2d,%03dA", (moyenne_home / 1000), ((moyenne_home % 1000) / 10)); + display_line(4, 0, line); + snprintf(line, DISP_LLEN, "Command: %d%%", command_val); + display_line(5, 0, line); + snprintf(line, DISP_LLEN, "Mode: %c", heat_mode); + display_line(6, 0, line); + /* Update Oled display */ + ssd130x_display_full_screen(&display); + + /* Update RGB leds */ + /* FIXME : use for error signal */ + ws2812_set_pixel(0, (moyenne_home / 2000), (moyenne_solar / 2000), 0); + ws2812_set_pixel(1, 0, 0, command_val); + ws2812_send_frame(0); + } + } else { + /* Config mode. Mode entered by button action, which implies that interface board is present. */ + config_interface_handle(); + } +} + + diff --git a/v08/interface.h b/v08/interface.h new file mode 100644 index 0000000..59518a8 --- /dev/null +++ b/v08/interface.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * apps/blyes/relay_lights/interface.h + * + * Copyright 2018 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#ifndef INTERFACE_H +#define INTERFACE_H + +#include "core/system.h" +#include "core/pio.h" + +#include "extdrv/ssd130x_oled_driver.h" + +#define OLED_LINE_CHAR_LENGTH (SSD130x_NB_COL / 8) +#define DISP_LLEN (OLED_LINE_CHAR_LENGTH + 1) + + +/***************************************************************************** */ +/* Pins configuration */ +/* Buttons inputs on front panel */ +extern const struct pio button_up; +extern const struct pio button_left; +extern const struct pio button_right; +extern const struct pio button_down; +extern const struct pio button_ok; + +extern volatile int manual_activation_request; + +/* Exported vars */ +extern volatile uint8_t button_pressed; +#define BUTTON_OK (0x01 << 0) +#define BUTTON_UP (0x01 << 1) +#define BUTTON_DOWN (0x01 << 2) +#define BUTTON_RIGHT (0x01 << 3) +#define BUTTON_LEFT (0x01 << 4) + +/***************************************************************************** */ +/* Configuration */ +/* Configure interface board */ +int interface_config(uint32_t uart); + + +/* Internal Temperature sensors */ +#define CONV_DISPLAY_OK (0x01 << 0) +#define CONV_POWER_OK (0x01 << 1) +#define CONV_OK (CONV_POWER_OK | CONV_DISPLAY_OK) +int temp_read(uint32_t uart, int* deci_degrees_disp, int* deci_degrees_power); + +/* Interface content update */ +void interface_update(char mode); + +#endif /* INTERFACE_H */ + diff --git a/v08/main.c b/v08/main.c new file mode 100644 index 0000000..e46038a --- /dev/null +++ b/v08/main.c @@ -0,0 +1,549 @@ +/**************************************************************************** + * apps/scialys/v07/main.c + * + * Scialys system for solar-panel power generation tracking and fair use. + * + * Copyright 2016 Nathael Pajani + * + * + * 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 "config.h" +#include "interface.h" +#include "time.h" +#include "uSD.h" + + +#define MODULE_VERSION 0x05 +#define MODULE_NAME "Scialys uC" + + + +/***************************************************************************** */ +/* System configuration */ + +/* Period (in ms) of the decrementer handler from the systick interrupt */ +#define DEC_PERIOD 100 +/* Period (in ms) of the handler for the command value update */ +#define CMD_UPD_PERIOD 200 + +/* NOTE : All temperatures are stored as decidegrees */ +/* Max water temperature. Internal protection which cannot be overriden by configuration */ +#define MAX_WATER_TEMP 900 +/* Internal system max temperature : turn off heating when reached */ +#define MAX_INTERNAL_TEMP 800 /* FIXME */ + +#define DEFAULT_INTERNAL_TEMP 600 +#define FAN_ON_INTERNAL_TEMP_POWER 400 +#define FAN_OFF_INTERNAL_TEMP_POWER 350 +#define FAN_ON_INTERNAL_TEMP 380 +#define FAN_OFF_INTERNAL_TEMP 320 + +/* Most of the defines in here should go to configuration setting in user flash */ +/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until + * TARGET_FORCED_HEATER_TEMP is reached. + * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which + * is between 0 and 100. + */ +#define FORCE_HEATER_TEMP 300 +#define TARGET_FORCED_HEATER_TEMP 500 +#define FORCED_MODE_VALUE 75 /* A fraction of 100 */ +/* mA prod value above which the system will not enter forced mode, waiting for home + * to stop using power to start automatic heating */ +#define SUNNY_DAYS_PROD_VALUE 3000 + +/* Delay before automatic forced heating */ +#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD) +/* Duration of automatic forced heating */ +#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) + +/* Duration of manual forced heating */ +#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */ + + +uint32_t forced_heater_mode = 0; +uint32_t forced_heater_delay = 0; +uint32_t forced_heater_time = 0; + +uint8_t error_shutdown = 0; + +uint8_t never_force = 0; +uint32_t sunny_days_prod_value_config = 0; + +#define EXTERNAL_DISABLE_FORCE 0 /* Input is pulled low when external disable is ON */ +int external_disable = 0; + +enum modes { + heat = 'C', + ext_disable = 'E', + delayed_heat_prod = 'P', + forced = 'F', + temp_OK = 'T', + manual = 'M', + idle_heat = 'L', + full_heat = 'F', +}; + +/* Water and internaltemperature */ +int water_centi_degrees = 0; +int deci_degrees_power = 0; +int deci_degrees_disp = 0; + +/* RTC and time */ +struct rtc_time now; + + +/***************************************************************************** */ +/* Rx interrupt handler for system configuration over USB */ +void config_rx(uint8_t c) +{ + /* FAN control */ + if (c == 'f') { + gpio_set(fan_ctrl); + } else { + gpio_clear(fan_ctrl); + } +} + + +/* Communication with slave modules */ +void comm_rx(uint8_t c) +{ +} + + + +/***************************************************************************** */ +/* System communication over UART1 */ +void cmd_rx(uint8_t c) +{ +} + + +/***************************************************************************** */ +/* Decrementer for heating timers */ +void handle_dec_request(uint32_t curent_tick) { + if (manual_activation_request > 0) { + manual_activation_request--; + } + if (forced_heater_mode == 1) { + if (forced_heater_delay > 0) { + forced_heater_delay--; + } + if (forced_heater_time > 0) { + forced_heater_time--; + } + } +} + + +/***************************************************************************** */ +/* Track power production and usage */ + +/* Average value computed on last 10 ADC values */ +uint32_t moyenne_solar = 0; +uint32_t moyenne_home = 0; +/* Last ADC values (snapshot) */ +static uint16_t snapval_solar = 0; +static uint16_t snapval_home = 0; + +#define NB_VAL 20 +static void track_isnail_values(void) +{ + static uint16_t isnail_solar_values[NB_VAL]; + static uint16_t isnail_home_values[NB_VAL]; + static uint8_t idx = 0; + + /* Get new values */ + adc_get_value(&snapval_solar, LPC_ADC(1)); + adc_get_value(&snapval_home, LPC_ADC(0)); + + /* Convert to mA value. + * ADC range is 0 to 1024 - approx 3.2mV / ADC step + * Coil convertion is 1000mA -> 50mV : multily mV value by 20 to get mA value + * x * 3.2 * 20 == x * 32 * 2 == x << 6 + * Increment is 64mA / ADC step */ + snapval_solar = snapval_solar << 6; + snapval_home = snapval_home << 6; + + /* Store value */ + isnail_solar_values[idx] = snapval_solar; + isnail_home_values[idx++] = snapval_home; + if (idx == NB_VAL) { + idx = 0; + } + + /* Compute average once we sampled enough values */ + if ((idx == 0) || (idx == (NB_VAL / 2))) { + int i = 0; + moyenne_solar = 0; + moyenne_home = 0; + for (i = 0; i < NB_VAL; i++) { + moyenne_solar += isnail_solar_values[i]; + moyenne_home += isnail_home_values[i]; + } + moyenne_solar = moyenne_solar / NB_VAL; + moyenne_home = moyenne_home / NB_VAL; + } +} + +/***************************************************************************** */ +/* AC control */ + +volatile uint8_t command_val = 0; +volatile int act_cmd = 100; /* Start off */ +int8_t force_cmd = -1; /* Forced command value, for tests */ +static int fan_on = 0; +int force_fan = 0; /* Request to force fan ON from test menu */ + + +void set_ctrl_duty_cycle(uint8_t value) +{ + act_cmd = value; + /* Below 3% and above 97% there are triggering problems which + * lead to 50% instead of desired command */ + if (act_cmd >= 97) { + act_cmd = 100; + } else if (act_cmd <= 2) { + act_cmd = 0; + } +} + +void ac_switch_on(uint32_t flags) +{ + gpio_set(ac_ctrl); +} + +static uint32_t clk_cycles_ac_zc = 0; +volatile uint32_t zc_cnt = 0; +void zero_cross(uint32_t gpio) +{ + uint32_t delay = 0; + + zc_cnt++; + if (act_cmd == 100) { + gpio_set(ac_ctrl); + return; + } + gpio_clear(ac_ctrl); + if (act_cmd == 0) { + return; + } + /* Set timer to trigger ac out ON at given delay */ + delay = clk_cycles_ac_zc * (100 - act_cmd); + timer_set_match(LPC_TIMER_32B1, CHAN0, delay); + timer_restart(LPC_TIMER_32B1); +} + +/* This one is used to synchronize to the real zero-crossing detection, if any. */ +void zero_cross_detect(uint32_t gpio) +{ + timer_restart(LPC_TIMER_32B0); + zero_cross(0); +} + +/* Handle the power command */ +#define CMD_UP_DELAY_RESET_VALUE 5 +void handle_cmd_update(uint32_t curent_tick) +{ + static uint8_t cmd = 0; + static uint8_t cmd_up_delay = CMD_UP_DELAY_RESET_VALUE; + + /* Unable to read internal temperature : turn off heating */ + if (error_shutdown == 1) { + cmd = 0; + goto cmd_update_end; + } + + /* Water max temperature protection */ + if (water_centi_degrees > (MAX_WATER_TEMP * 10)) { + cmd = 0; + goto cmd_update_end; + } + + /* Forced test mode */ + if (force_cmd != -1) { + cmd = force_cmd; + goto cmd_update_end; + } + + /* Which is the current mode ? */ + if (forced_heater_mode == 1) { + /* Forced heating mode */ + if ((forced_heater_delay == 0) && (forced_heater_time > 0)) { + cmd = FORCED_MODE_VALUE; + } + /* Entering forced heating mode from temperature getting below threshold, + * wait some delay before effective forced heating */ + if (forced_heater_time == 0) { + forced_heater_delay = FORCED_HEATER_DELAY; + forced_heater_time = FORCED_HEATER_DELAY + FORCED_HEATER_DURATION; + } + } else if (moyenne_solar < (moyenne_home - 64)) { + /* Low production mode */ + if (cmd > 0) { + cmd--; + } + } else if (moyenne_solar > (moyenne_home + 1280)) { + if (cmd < 100) { + cmd++; + } + } else if (moyenne_solar > (moyenne_home + 192)) { + /* High production mode */ + if (cmd_up_delay > 0) { + cmd_up_delay--; + } else { + cmd_up_delay = CMD_UP_DELAY_RESET_VALUE; + if (cmd < 100) { + cmd++; + } + } + } + +cmd_update_end: + command_val = cmd; + /* Set Control Output duty cycle */ + set_ctrl_duty_cycle(100 - cmd); +} + + +/***************************************************************************** */ +/* Power tracking - load part */ + +/* ADC values are between 0 and 1023, no load value is at about 520 */ +static volatile uint16_t load_power_highest = 520; +static volatile uint16_t load_power_lowest = 520; +void power_track(uint32_t flags) +{ + /* Start tracking the higest value */ + static int tracking = 1; + static uint16_t last_value = 0; + uint16_t val = 0; + + /* Get load power usage */ + adc_get_value(&val, LPC_ADC(2)); + + /* Four possibilities : + * tracking high and value increasing : nothing to do + * tracking high and value decreasing : we found the max, store it and change tracking + * tracking low and value decreasing : nothing to do + * tracking low and value increasing : we found the min, store it and change tracking + */ + if (tracking == 1) { + if (last_value > val) { + /* Found max */ + load_power_highest = last_value; + tracking = 0; + } + } else { + if (last_value < val) { + /* Found min */ + load_power_lowest = last_value; + tracking = 1; + } + } + last_value = val; +} + +/***************************************************************************** */ +void scialys_config(void) +{ + /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */ + clk_cycles_ac_zc = get_main_clock() / (100 * 100); + + /* Configure the fake zero-cross generation timer to 100Hz. + * Do not start it now, it will be started by the first real zero-cross detected in order to + * generate the other half zc (not seen by system due to hardware design) + * Also sarted by config when DC mode is set */ + timer_set_match(LPC_TIMER_32B0, CHAN0, (clk_cycles_ac_zc * 100)); + + /* Configure the power tracking timer. + * 20 points per half sin wave should be enough to get the min and max */ + timer_set_match(LPC_TIMER_16B0, CHAN0, (clk_cycles_ac_zc * 5)); + timer_start(LPC_TIMER_16B0); + + /* Read parameters from memory */ + if (1) { + never_force = 0; + sunny_days_prod_value_config = SUNNY_DAYS_PROD_VALUE; + } + + /* Add a systick callback to handle command value update */ + add_systick_callback(handle_cmd_update, CMD_UPD_PERIOD); + + /* Add a systick callback to handle time counting */ + add_systick_callback(handle_dec_request, DEC_PERIOD); +} + +/* Add a false zero-cross, but make sure to do it only once and only if we are not AC powered */ +void DC_switch_start(void) +{ + static int already_done = 0; + if ((already_done == 0) && (zc_cnt == 0)) { + int ret = add_systick_callback(zero_cross, 10); /* 2 zero-crossing at 50Hz -> 100Hz -> 10ms */ + uprintf(UART0, "Entering forced DC mode on systick callback %d\n", ret); + already_done = 1; + } else { + uprintf(UART0, "DC forced mode already ON: %d/%d\n", already_done, zc_cnt); + } +} + +/***************************************************************************** */ +int main(void) +{ + uint32_t loop = 0; + char mode = heat; /* Debug info */ + int ret = 0; + + system_init(); + board_io_config(); + modules_config(); + + external_config(UART0); + scialys_config(); + + msleep(500); + status_led(green_only); + + while (1) { + + /* Default mode : try to heat the water tank */ + mode = heat; + + /* Always track power consumption and production */ + track_isnail_values(); + + /* Feed the dog */ + if ((moyenne_solar != 0) && (moyenne_home != 0)) { + watchdog_feed(); + } + + /* Get internal temperature */ + ret = temp_read(UART0, &deci_degrees_disp, &deci_degrees_power); + if (ret != CONV_OK) { + /* Set internal temperature to 60°C if there's a temp error, as this will trigger the + * internal fan */ + if ((ret & CONV_POWER_OK) == 0) { + deci_degrees_power = DEFAULT_INTERNAL_TEMP; + } + if ((ret & CONV_DISPLAY_OK) == 0) { + deci_degrees_disp = DEFAULT_INTERNAL_TEMP; + } + } + + /* Internal temperature protection */ + if ((deci_degrees_power > MAX_INTERNAL_TEMP) || (deci_degrees_disp > MAX_INTERNAL_TEMP)) { + error_shutdown = 1; + } else if ((deci_degrees_power < ((MAX_INTERNAL_TEMP) / 2)) && + (deci_degrees_disp < ((MAX_INTERNAL_TEMP) / 2))) { + error_shutdown = 0; + } + /* If internal temperature is above 32°C, then turn on fan. Turn off when back to under 28°C */ + if ((deci_degrees_power > FAN_ON_INTERNAL_TEMP_POWER) || (deci_degrees_disp > FAN_ON_INTERNAL_TEMP) || + (force_fan != 0)) { + fan_on = 1; + gpio_set(fan_ctrl); + } else if ((deci_degrees_power < FAN_OFF_INTERNAL_TEMP_POWER) && (deci_degrees_disp < FAN_OFF_INTERNAL_TEMP)) { + fan_on = 0; + gpio_clear(fan_ctrl); + } + + /* Get thermocouple value */ + if (1) { + int ret = 0, old_water_temp = 0; + old_water_temp = water_centi_degrees; + ret = max31855_sensor_read(&thermocouple, NULL, &water_centi_degrees); + if (ret != 0) { + uprintf(UART0, "Water Temp read error : %d\n", ret); + /* Do not act upon iinvalid temp value */ + water_centi_degrees = old_water_temp; + } + } + + /* Need to enter Forced heating mode ? */ + if (water_centi_degrees < (FORCE_HEATER_TEMP * 10)) { + if (forced_heater_mode == 0) { + uprintf(UART0, "Water temp low, entering forced mode\n"); + forced_heater_mode = 1; + } + status_led(red_on); + mode = forced; + } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 10)) && (forced_heater_mode == 1)) { + status_led(red_off); + uprintf(UART0, "Water temp OK, forced mode exit\n"); + forced_heater_mode = 0; + mode = temp_OK; + } + + /* Do not force if there is a lot of sun, it may be enough to heat again soon */ + if (moyenne_solar > sunny_days_prod_value_config) { + mode = delayed_heat_prod; + forced_heater_mode = 0; + } + + /* Do not force heating if disabled by external command */ + external_disable = gpio_read(ext_disable_in_pin); + if ((external_disable == EXTERNAL_DISABLE_FORCE) && (forced_heater_mode != 0)) { + forced_heater_mode = 0; + mode = ext_disable; + uprintf(UART0, "Forced mode disabled by external input\n"); + } + + if (never_force == 1) { + forced_heater_mode = 0; + } + + /* Did the user request a forced heating ? */ + if (manual_activation_request != 0) { + forced_heater_mode = 1; + mode = manual; + if (manual_activation_request == -1) { + uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request); + manual_activation_request = MANUAL_ACTIVATION_DURATION; + forced_heater_time = FORCED_HEATER_DURATION; + } + if (manual_activation_request < 10) { + uprintf(UART0, "Leaving manual forced mode\n"); + manual_activation_request = 0; + } + } + + /* Display */ + interface_update(mode); + + /* Debug */ + if (1) { + uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++, + (moyenne_solar / 1000), (moyenne_solar % 1000), + (moyenne_home / 1000), (moyenne_home % 1000)); + uprintf(UART0, "Water Temp : %d\n", water_centi_degrees); + uprintf(UART0, "Internal Temp power : %d\n", deci_degrees_power); + uprintf(UART0, "Internal Temp display : %d\n", deci_degrees_disp); + uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA\n", snapval_solar, snapval_home); + uprintf(UART0, "Load: %d - %d\n", load_power_lowest, load_power_highest); + uprintf(UART0, "Zc: %d\n", zc_cnt); + uprintf(UART0, "CMD: %d/%d, Fan: %d/%d\n\n", command_val, (100 - act_cmd), fan_on, force_fan); + } + + /* Wait approx 10ms between each loop */ + //msleep(10); + } + return 0; +} + + + diff --git a/v08/time.c b/v08/time.c new file mode 100644 index 0000000..257c1fe --- /dev/null +++ b/v08/time.c @@ -0,0 +1,98 @@ +/**************************************************************************** + * apps/blyes/inter_lights/time.c + * + * Copyright 2018 Nathael Pajani + * + * + * 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 "core/system.h" + +#include "drivers/serial.h" +#include "drivers/i2c.h" + +#include "lib/stdio.h" +#include "lib/errno.h" + +#include "time.h" + + +/***************************************************************************** */ +/* RTC */ +struct rtc_pcf85363a_config rtc_conf = { + .mode = PCF85363A_MODE_RTC, + .config_marker = PCF85363A_CONFIGURED_1, + .batt_ctrl = PCF85363A_CONF_BATT_TH_2_8V, +}; +/* Oldest acceptable time in RTC. BCD coded. */ +const struct rtc_time oldest = { + .year = 0x18, + .month = 0x08, + .day = 0x09, + .hour = 0x13, + .min = 0x37, +}; +int time_valid = 0; +int rtc_conf_ok = 0; + +int time_init_check(uint32_t uart) +{ + int ret = 0; + ret = rtc_pcf85363a_config(&rtc_conf); + if (ret < 0) { + uprintf(uart, "RTC config error: %d\n", ret); + return -1; + } + rtc_conf_ok = 1; + + ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest); + if (ret == 1) { + /* Time is valid (newer than source code time) */ + char buff[30]; + rtc_pcf85363_time_read(&rtc_conf, &now); + rtc_pcf85363_time_to_str(&now, buff, 30); + /* Debug */ + uprintf(uart, "Using time from RTC: %s\n", buff); + time_valid = 1; + } else if (ret == -EFAULT) { + /* Time is older than source code time */ + time_valid = 0; + uprintf(uart, "RTC time too old, provide valid time.\n"); + /* FIXME: remove this, time should come from control master */ + memcpy(&now, &oldest, sizeof(struct rtc_time)); + rtc_pcf85363_time_write(&rtc_conf, &now); + } else { + /* RTC config error */ + rtc_conf_ok = 0; + } + return ret; +} + +int time_update(uint32_t uart, struct rtc_time* now) +{ + rtc_pcf85363_time_write(&rtc_conf, now); + return 0; +} + + +/***************************************************************************** */ +/* RTC init */ +void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr) +{ + rtc_conf.bus_num = i2c_bus_num; + rtc_conf.addr = rtc_addr; +} + diff --git a/v08/time.h b/v08/time.h new file mode 100644 index 0000000..a1533f8 --- /dev/null +++ b/v08/time.h @@ -0,0 +1,34 @@ +/**************************************************************************** + * apps/blyes/inter_lights/time.h + * + * Copyright 2018 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#ifndef RTC_TIME_H +#define RTC_TIME_H + +#include "extdrv/rtc_pcf85363a.h" + +extern struct rtc_pcf85363a_config rtc_conf; +extern struct rtc_time now; + +int time_init_check(uint32_t uart); +void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr); + +#endif /* RTC_TIME_H */ + diff --git a/v08/uSD.c b/v08/uSD.c new file mode 100644 index 0000000..30022cf --- /dev/null +++ b/v08/uSD.c @@ -0,0 +1,132 @@ +/**************************************************************************** + * apps/blyes/inter_lights/uSD.c + * + * Copyright 2018 Nathael Pajani + * + * + * 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 "core/system.h" + +#include "drivers/serial.h" +#include "drivers/ssp.h" + +#include "lib/stdio.h" + +#include "uSD.h" + + +/***************************************************************************** */ +/* SD/MMC Card */ +struct sdmmc_card micro_sd = { + .card_type = MMC_CARDTYPE_UNKNOWN, + .block_size = 64, +}; + +static uint8_t got_uSD = 0; + +/* Buffer to read / write uSD card */ +uint8_t mmc_data[MMC_BUF_SIZE]; + +/* Read up to 4 blocks of 16 bytes each */ +int uSD_read(uint32_t offset, uint8_t nb_blocks) +{ + int i = 0, ret = 0; + if (got_uSD == 0) { + return -1; + } + if (nb_blocks > 4) { + return -2; + } + memset(mmc_data, 0, MMC_BUF_SIZE); + for (i = 0; i < nb_blocks; i++) { + ret = sdmmc_read_block(µ_sd, (offset + i), (mmc_data + (i * 16))); + /* FIXME : check return value */ + } + return 0; +} + + +/* Write up to 4 blocks of 16 bytes each */ +int uSD_write(uint32_t offset, uint8_t nb_blocks) +{ + int i = 0, ret = 0; + if (got_uSD == 0) { + return -1; + } + if (nb_blocks > 4) { + return -1; + } + for (i = 0; i < nb_blocks; i++) { + ret = sdmmc_write_block(µ_sd, (offset + i), (mmc_data + (i * 16))); + /* FIXME : check return value */ + } + return 0; +} + +/* microSD card init */ +int uSD_detect(int uart) +{ + int i = 0, ret = 0, step = 0; + do { + step = 0; + ret = sdmmc_init(µ_sd); + if (ret == 0) { + step = 1; + msleep(10); + ret = sdmmc_init_wait_card_ready(µ_sd); + if (ret <= 1) { + step = 2; + ret = sdmmc_init_end(µ_sd); + } + } + uprintf(uart, "uSD init(%d): step:%d, ret: %d, type: %d, bs: %d\n", + i, step, ret, micro_sd.card_type, micro_sd.block_size); + i++; + } while (((ret != 0) || (micro_sd.card_type == MMC_CARDTYPE_UNKNOWN)) && (i < 10)); + + /* Got uSD ? */ + if (i >= 10) { + uprintf(uart, "uSD init failed, no uSD card present.\n"); + got_uSD = 0; + return -1; + } + + /* uSD card detected */ + got_uSD = 1; + /* got_uSD MUST be set to 1 from here on ! */ + uSD_read(0, 4); /* Read 4 blocks at start of card */ + /* FIXME : check that the card magic is present */ + uprintf(uart, "uSD read: %s\n", mmc_data); + + return 0; +} + + +int uSD_logs_init(int uart) +{ + /* FIXME : read 4 blocks at offset FIXME to get the last block used info */ + return 0; +} + +/***************************************************************************** */ +/* microSD card init */ +void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num) +{ + memcpy(&(micro_sd.chip_select), uSD_cs, sizeof(struct pio)); + micro_sd.ssp_bus_num = ssp_bus_num; +} + diff --git a/v08/uSD.h b/v08/uSD.h new file mode 100644 index 0000000..f0c10c0 --- /dev/null +++ b/v08/uSD.h @@ -0,0 +1,42 @@ +/**************************************************************************** + * apps/blyes/inter_lights/usSD.h + * + * Copyright 2018 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#ifndef USD_H +#define USD_H + +#include "extdrv/sdmmc.h" + +#define MMC_BUF_SIZE 64 + +extern struct sdmmc_card micro_sd; +extern uint8_t mmc_data[MMC_BUF_SIZE]; + +int uSD_read(uint32_t offset, uint8_t nb_blocks); +int uSD_write(uint32_t offset, uint8_t nb_blocks); + +/***************************************************************************** */ +/* microSD card init */ +int uSD_detect(int uart); +void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num); + + +#endif /* USD_H */ + -- 2.43.0