From: Nathael Pajani Date: Wed, 8 Feb 2023 23:37:17 +0000 (+0100) Subject: Moving old soft versions related to old boards to "old" directory X-Git-Url: http://git.techno-innov.fr/?a=commitdiff_plain;h=fb1f0b67b08309489e17f2a806710ef086b17919;p=soft%2Flpc122x%2Fscialys Moving old soft versions related to old boards to "old" directory to prevent recompilation when re-building all apps. These may not build anymore (though I updated some today to allow re-compilation) --- diff --git a/alpha/Makefile b/alpha/Makefile deleted file mode 100644 index 41ae555..0000000 --- a/alpha/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# 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/alpha/README b/alpha/README deleted file mode 100644 index 2eebdad..0000000 --- a/alpha/README +++ /dev/null @@ -1,24 +0,0 @@ -DMX module for solar-panel power generation tracking and fair use. - -Copyright 2015 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 . - * - *************************************************************************** */ - -First prototype version of the Scialys module. -Used for validation of feasability - diff --git a/alpha/main.c b/alpha/main.c deleted file mode 100644 index 24a7030..0000000 --- a/alpha/main.c +++ /dev/null @@ -1,462 +0,0 @@ -/**************************************************************************** - * apps/scialys/alpha/main.c - * - * DMX module for solar-panel power generation tracking and fair use. - * - * Copyright 2015-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 . - * - *************************************************************************** */ - - -/* DMX informations : - * https://en.wikipedia.org/wiki/DMX512 - * - * DMX uses an RS485 line with a few glitches (the break and start pulse). - * The DMX module uses an ADM2482 isolated RS485 bridge from analog devices. - */ - -#include "core/system.h" -#include "core/pio.h" -#include "core/systick.h" -#include "lib/stdio.h" -#include "drivers/serial.h" -#include "drivers/gpio.h" -#include "extdrv/status_led.h" -#include "drivers/adc.h" -#include "drivers/ssp.h" - -#include "extdrv/max31855_thermocouple.h" - - -#define MODULE_VERSION 0x02 -#define MODULE_NAME "DMX Module" - - -#define SELECTED_FREQ FREQ_SEL_48MHz - -/* Period of the decrementer handler from the systick interrupt */ -#define DEC_PERIOD 100 - -/* 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 heated is controlled to heat at FORCED_MODE_VALUE which - * is between 0 and 255. - */ -#define FORCE_HEATER_TEMP 40 -#define TARGET_FORCED_HEATER_TEMP 45 -#define NO_FORCED_HEATING_ON_SUNNY_DAYS 750 -#define FORCED_MODE_VALUE 190 /* A fraction of 255 */ -uint32_t forced_heater_mode = 0; -uint32_t forced_heater_delay = 0; -uint32_t forced_heater_time = 0; - -#define FORCED_HEATER_DELAY (7 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */ -#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */ - -#define MANUAL_ACTIVATION_DURATION (3600 * 1000 / DEC_PERIOD) /* One hour */ - -uint32_t never_force = 0; - -/***************************************************************************** */ -/* 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 }, - { LPC_UART0_RTS_PIO_0_0, LPC_IO_DIGITAL }, - /* UART 1 */ - { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL }, - { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL }, - /* 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 }, - /* GPIO */ - { LPC_GPIO_0_12, LPC_IO_DIGITAL }, - { LPC_GPIO_0_25, LPC_IO_DIGITAL }, - { LPC_GPIO_0_26, LPC_IO_DIGITAL }, - { LPC_GPIO_0_28, LPC_IO_DIGITAL }, - { LPC_GPIO_0_29, LPC_IO_DIGITAL }, - ARRAY_LAST_PIO, -}; - -const struct pio_config adc_pins[] = { - { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */ - { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */ - ARRAY_LAST_PIO, -}; - -const struct pio status_led_green = LPC_GPIO_0_28; -const struct pio status_led_red = LPC_GPIO_0_29; - -const struct pio button = LPC_GPIO_0_12; /* ISP button */ -const struct pio ejp_in_pin = LPC_GPIO_0_25; -#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */ -int ejp_in = 0; - -/* Thermocouple reading */ -const struct max31855_sensor_config thermo = { - .ssp_bus_num = 0, - .chip_select = LPC_GPIO_0_26, -}; - -/* DMX signals */ -/* DMX signals are on LPC_GPIO_0_0 for Frame enable pin (RS485 enable) and LPC_GPIO_0_2 for - * Frame Tx pin (RS485 Tx). - */ -const struct pio frame_tx_pin = LPC_GPIO_0_2; -const struct pio frame_en_pin = LPC_GPIO_0_0; - - - -/***************************************************************************** */ -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 = 0x0FFFFFF, /* 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); - set_pins(adc_pins); - gpio_on(); - status_led_config(&status_led_green, &status_led_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); -} - - -/* Put any data received from RS485 UART in rs485_rx_buff - */ -void rs485_rx(uint8_t c) -{ -} - -void cmd_rx(uint8_t c) -{ -} - -void dmx_send_frame(uint8_t start_code, uint8_t* slots, uint16_t nb_slots) -{ - uint16_t sent = 0; - - /* Configure pins for Start of frame transmission */ - config_gpio(&frame_tx_pin, LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 0); - config_gpio(&frame_en_pin, LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 1); - /* Send break : 92us minimum - * Pins default state chosen to generate the break condition */ - usleep(100); - /* Send Mark-After-Break (MAB) of 12us minimum */ - gpio_set(frame_tx_pin); - usleep(12); - - /* Configure back for RS485. - * This is done using direct access to the registers as this step is time critical - */ - /* Make sure IO_Config is clocked */ - io_config_clk_on(); - LPC_IO_CONTROL->pio0_0 = (LPC_IO_FUNC_ALT(2) | LPC_IO_DIGITAL); - LPC_IO_CONTROL->pio0_2 = (LPC_IO_FUNC_ALT(2) | LPC_IO_DIGITAL); - - - /* Send start code */ - serial_send_quickbyte(0, start_code); - - /* And send slots data */ - while (sent < nb_slots) { - int tmp = serial_write(0, (char*)(slots + sent), (nb_slots - sent)); - if (tmp == -1) { - break; - } - sent += tmp; - } - - /* Config done, power off IO_CONFIG block */ - io_config_clk_off(); -} - - -uint32_t manual_activation_request = 0; -void manual_activation(uint32_t gpio) { - manual_activation_request = MANUAL_ACTIVATION_DURATION; -} -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--; - } - } -} - - -/* DMX */ -#define DMX_NB_SLOTS 4 -#define NB_VAL 20 - - -enum modes { - heat = 'C', - ejp = 'E', - no_heat_prod = 'P', - forced = 'F', - temp_OK = 'T', - manual = 'M', - idle_heat = 'L', - full_heat = 'F', -}; - -/***************************************************************************** */ -int main(void) -{ - uint16_t isnail_solar_values[NB_VAL]; - uint16_t isnail_home_values[NB_VAL]; - uint8_t slots[DMX_NB_SLOTS + 1]; - uint8_t idx = 0; - uint32_t loop = 0; - char mode = heat; /* Debug info */ - - system_init(); - status_led(red_only); - uart_set_config(0, (LPC_UART_8BIT | LPC_UART_NO_PAR | LPC_UART_2STOP)); - uart_on(UART0, 250000, rs485_rx); /* FIXME : configure for 250 kbits/s and 8N2 */ - uart_on(UART1, 115200, cmd_rx); - ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000); - adc_on(NULL); - - /* RS485 config */ - if (1) { - uint32_t rs485_ctrl = LPC_RS485_ENABLE; - //rs485_ctrl |= LPC_RS485_DIR_PIN_RTS | LPC_RS485_AUTO_DIR_EN | LPC_RS485_DIR_CTRL_INV; - rs485_ctrl |= LPC_RS485_DIR_PIN_RTS | LPC_RS485_AUTO_DIR_EN | LPC_RS485_DIR_CTRL_INV; - uart_set_mode_rs485(0, rs485_ctrl, 0, 1); - } - - /* Thermocouple configuration */ - max31855_sensor_config(&thermo); - uprintf(UART1, "Thermocouple config OK\n"); - - /* Activate on Rising edge (button release) */ - set_gpio_callback(manual_activation, &button, EDGE_RISING); - - /* Start ADC sampling */ - adc_start_burst_conversion(ADC_MCH(1) | ADC_MCH(2), LPC_ADC_SEQ(0)); - - /* Configure Input GPIO for EJP mode detection */ - config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0); - - status_led(green_only); - memset(slots, 0, (DMX_NB_SLOTS + 1)); - - msleep(50); - /* Read parameters from memory */ - if (1) { - never_force = 0; - forced_heater_delay = FORCED_HEATER_DELAY; - forced_heater_time = FORCED_HEATER_DURATION; - } - - while (1) { - static uint8_t dmx_val = 0; - uint32_t moyenne_solar = 0; - uint32_t moyenne_home = 0; - uint16_t isnail_val_solar = 0; - uint16_t isnail_val_home = 0; - int centi_degrees = 0; - - mode = heat; - /* Always track power consumption and production */ - adc_get_value(&isnail_val_solar, LPC_ADC(1)); - adc_get_value(&isnail_val_home, LPC_ADC(2)); - /* Convert to mA value */ - isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ - isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ - /* Store value */ - isnail_solar_values[idx] = isnail_val_solar; - isnail_home_values[idx++] = isnail_val_home; - if (idx == NB_VAL) { - idx = 0; - } - /* Compute average value when we sampled enough values */ - if ((idx == 0) || (idx == (NB_VAL / 2))) { - int i = 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; - } else { - /* Sleep for a litle more than a period (20ms at 50Hz) */ - msleep(23); - continue; - } - - /* Feed the dog */ - if ((moyenne_solar != 0) && (moyenne_home != 0)) { - watchdog_feed(); - } - - /* Get thermocouple value */ - if (1) { - int ret = 0; - ret = max31855_sensor_read(&thermo, NULL, ¢i_degrees); - if (ret != 0) { - uprintf(UART1, "Temp read error : %d\n", ret); - } - } - if (centi_degrees < (FORCE_HEATER_TEMP * 100)) { - if (forced_heater_mode == 0) { - uprintf(UART1, "Entering forced mode\n"); - forced_heater_mode = 1; - } - status_led(red_on); - mode = forced; - } else if ((centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) { - status_led(red_off); - forced_heater_mode = 0; - dmx_val = 0; - uprintf(UART1, "Forced mode exit\n"); - mode = temp_OK; - } - - /* Do not force if there is some sun, it may be enough to heat again */ - if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) { - forced_heater_mode = 0; - mode = no_heat_prod; - forced_heater_delay = FORCED_HEATER_DELAY; - } - - /* Do not force heating if this is an EJP day */ - ejp_in = gpio_read(ejp_in_pin); - if (ejp_in == DAY_IS_EJP) { - forced_heater_mode = 0; - mode = ejp; - } - - if (never_force == 1) { - forced_heater_mode = 0; - } - - /* Did the user request a forced heating ? */ - if (manual_activation_request > 1) { - forced_heater_mode = 1; - mode = manual; - if (manual_activation_request == MANUAL_ACTIVATION_DURATION) { - uprintf(UART1, "Entering manual forced mode for %d ticks\n", manual_activation_request); - /* Add a systick callback to handle time counting */ - add_systick_callback(handle_dec_request, DEC_PERIOD); - } - if (manual_activation_request < 10) { - uprintf(UART1, "Leaving manual forced mode\n"); - manual_activation_request = 0; - remove_systick_callback(handle_dec_request); - } - } - - - /* Which is the current mode ? */ - if (forced_heater_mode == 1) { - /* Forced heating mode */ - if ((forced_heater_delay == 0) && (forced_heater_time > 0)) { - dmx_val = FORCED_MODE_VALUE; - } - if (forced_heater_time == 0) { - forced_heater_delay = FORCED_HEATER_DELAY; - forced_heater_time = FORCED_HEATER_DURATION; - } - } else if (moyenne_solar < moyenne_home) { - /* Low production mode */ - if (dmx_val > 25) { - dmx_val -= 25; - } else { - dmx_val = 0; - mode = idle_heat; - } - status_led(green_off); - } else { - /* High production mode */ - if (dmx_val < 245) { - dmx_val += 10; - } else { - dmx_val = 255; - mode = full_heat; - } - status_led(green_on); - } - - /* Send DMX frame */ - slots[0] = dmx_val; - if (slots[0] > 255) { - slots[0] = 255; - } - dmx_send_frame(0x00, slots, 1); - /* Display */ - if (1) { - int abs_centi = centi_degrees; - uprintf(UART1, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++, - (moyenne_solar / 1000), (moyenne_solar % 1000), - (moyenne_home / 1000), (moyenne_home % 1000)); - if (centi_degrees < 0) { - abs_centi = -centi_degrees; - } - uprintf(UART1, "Temp : % 4d.%02d\n", (centi_degrees / 100), (abs_centi % 100)); - uprintf(UART1, "DMX: %d\n\n", slots[0]); - } - } - return 0; -} - - - diff --git a/beta/Makefile b/beta/Makefile deleted file mode 100644 index 41ae555..0000000 --- a/beta/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# 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/beta/README b/beta/README deleted file mode 100644 index 42a1d84..0000000 --- a/beta/README +++ /dev/null @@ -1,22 +0,0 @@ -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 . - * - *************************************************************************** */ - -Beta version of Scialys system. diff --git a/beta/main.c b/beta/main.c deleted file mode 100644 index b61a2a7..0000000 --- a/beta/main.c +++ /dev/null @@ -1,819 +0,0 @@ -/**************************************************************************** - * apps/scialys/beta/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 "core/system.h" -#include "core/systick.h" -#include "core/pio.h" -#include "lib/stdio.h" -#include "lib/errno.h" -#include "drivers/serial.h" -#include "drivers/gpio.h" -#include "drivers/adc.h" -#include "drivers/ssp.h" -#include "drivers/i2c.h" -#include "drivers/timers.h" - -#include "extdrv/status_led.h" -#include "extdrv/ws2812.h" -#include "extdrv/max31855_thermocouple.h" -#include "extdrv/tmp101_temp_sensor.h" -#include "extdrv/rtc_pcf85363a.h" -#include "extdrv/ssd130x_oled_driver.h" -#include "extdrv/ssd130x_oled_buffer.h" -#include "lib/font.h" -#include "lib/time.h" - -#include "extdrv/sdmmc.h" - -#define MODULE_VERSION 0x01 -#define MODULE_NAME "Scialys uC" - - -#define SELECTED_FREQ FREQ_SEL_48MHz - - -/***************************************************************************** */ -/* System configuration - * Most of the defines in here should go to configuration setting in user flash - */ - -/* Period of the decrementer handler from the systick interrupt */ -#define DEC_PERIOD 100 - -/* 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 35 -#define MAX_HEATER_TEMP 90 -#define TARGET_FORCED_HEATER_TEMP 50 -#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 NO_FORCED_HEATING_ON_SUNNY_DAYS 750 - -uint32_t forced_heater_mode = 0; -uint32_t forced_heater_delay = 0; -uint32_t forced_heater_time = 0; - -#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */ -#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */ - -#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */ - -uint32_t never_force = 0; - - -#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */ -int ejp_in = 0; - - - -/***************************************************************************** */ -/* 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 : Config / Debug / USB */ - { LPC_UART0_RX_PIO_0_1, LPC_IO_DIGITAL }, - { LPC_UART0_TX_PIO_0_2, LPC_IO_DIGITAL }, - /* UART 1 : UEXT */ - { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL }, - { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL }, - /* I2C : RTC, Display, UEXT */ - { 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 (Thermocouple + uSD card + UEXT) */ - { 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 }, - /* TIMER_32B0 */ - { LPC_TIMER_32B0_M0_PIO_0_18, (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL) }, /* Fan control */ - /* GPIO */ - { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */ - { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* EJP / External switch input */ - { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */ - { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Temperature driver warning (Mosfet board only) */ - { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet driver Shutdown (Mosfet board only) */ - { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Mosfet / Triac control */ - { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */ - { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */ - { LPC_GPIO_0_23, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */ - { LPC_GPIO_0_26, LPC_IO_DIGITAL }, /* User button B2 */ - { LPC_GPIO_0_27, LPC_IO_DIGITAL }, /* User button B1 */ - { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */ - { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */ - { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */ - ARRAY_LAST_PIO, -}; - -const struct pio_config adc_pins[] = { - { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* ADC0 */ - { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */ - { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */ - { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* ADC3 */ - ARRAY_LAST_PIO, -}; - -const struct pio status_led_green = LPC_GPIO_1_2; -const struct pio status_led_red = LPC_GPIO_1_3; - -/* Inputs */ -/* Buttons */ -const struct pio button_ok = LPC_GPIO_0_12; -const struct pio button_b1 = LPC_GPIO_0_27; -const struct pio button_b2 = LPC_GPIO_0_26; -/* External signals */ -const struct pio rtc_in_pin = LPC_GPIO_0_0; -const struct pio ejp_in_pin = LPC_GPIO_0_3; -const struct pio zero_cross_in_pin = LPC_GPIO_0_4; -const struct pio th_warn_in_pin = LPC_GPIO_0_5; -const struct pio charge_status_in_pin = LPC_GPIO_0_28; - -/* Outputs */ -/* Led control data pin */ -const struct pio ws2812_data_out_pin = LPC_GPIO_0_23; -/* AC output control (Mosfet / Triac) */ -const struct pio ac_ctrl = LPC_GPIO_0_7; -/* Fixme : Fan */ - - -/* Thermocouple reading */ -const struct max31855_sensor_config thermo = { - .ssp_bus_num = 0, - .chip_select = LPC_GPIO_0_15, -}; - -/* TMP101 onboard I2C temperature sensor */ -#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */ -struct tmp101_sensor_config tmp101_sensor = { - .bus_num = I2C0, - .addr = TMP101_ADDR, - .resolution = TMP_RES_ELEVEN_BITS, -}; - - -#define FAN_ON (10 * 1000) -const struct lpc_timer_pwm_config fan_pwm_conf = { - .nb_channels = 1, - .period_chan = CHAN3, - .period = FAN_ON, - .outputs = { CHAN0, }, - .match_values = { 0, }, -}; - -const struct lpc_tc_config ac_timer_conf = { - .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH, - .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, }, - .match = { 0, 10, 0, 0, }, - .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, }, -}; - - -/***************************************************************************** */ -/* SD/MMC Card */ -struct sdmmc_card micro_sd = { - .ssp_bus_num = SSP_BUS_0, - .card_type = MMC_CARDTYPE_UNKNOWN, - .block_size = 64, - .chip_select = LPC_GPIO_1_6, -}; - -uint8_t mmc_data[MMC_MAX_SECTOR_SIZE]; - - -/***************************************************************************** */ -/* RTC and time */ -#define RTC_ADDR 0xA2 -struct rtc_pcf85363a_config rtc_conf = { - .bus_num = I2C0, - .addr = RTC_ADDR, - .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 = 0x09, - .day = 0x08, - .hour = 0x18, - .min = 0x00, -}; -static struct rtc_time now; - - -/***************************************************************************** */ -/* 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); - set_pins(adc_pins); - gpio_on(); - status_led_config(&status_led_green, &status_led_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); -} - - -/***************************************************************************** */ -/* System configuration over USB */ -static uint32_t fan_speed = 0; -static int act_cmd = 0; -void config_rx(uint8_t c) -{ - /* FAN control */ - if (c == 'f') { - fan_speed = 100; - timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON); - } else if (c == 'z') { - fan_speed = 0; - timer_set_match(LPC_TIMER_32B0, CHAN0, 0); - } else if ((c >= '0') && (c <= '9')) { - fan_speed = ((c - '0') * 10); - if (fan_speed < 60) { - fan_speed = 0; - } - timer_set_match(LPC_TIMER_32B0, CHAN0, (FAN_ON - ((FAN_ON / 100) * (100 - fan_speed)))); - } -} - - - -/***************************************************************************** */ -/* System communication over UART1 */ -void cmd_rx(uint8_t c) -{ -} - - -/***************************************************************************** */ - -void set_ctrl_duty_cycle(uint8_t value) -{ - act_cmd = value; - if (act_cmd > 100) { - /* 100 is the maximum allowed value */ - act_cmd = 100; - } else if (act_cmd <= 2) { - /* Below 3% there are triggering problems which lead to 50% instead of 1% or 2% */ - act_cmd = 0; - } -} - -static volatile uint8_t ac_ctrl_state = 0; -void ac_switch_on(uint32_t flags) -{ - /* Generate a 5us (approx) pulse */ - if (ac_ctrl_state == 1) { - /* Start of pulse */ - gpio_set(ac_ctrl); - /* Change state machine state */ - ac_ctrl_state = 0; - /* And request interrupt in approx 5us to clear ac_ctrl output */ - timer_set_match(LPC_TIMER_32B1, CHAN1, (5 * 480)); - timer_restart(LPC_TIMER_32B1); - } else { - /* End of pulse */ - gpio_clear(ac_ctrl); - } -} - -static uint32_t clk_cycles_ac_zc = 0; -static volatile uint32_t zc_count = 0; /* Wraps every 1.36 year ... */ -void zero_cross(uint32_t gpio) -{ - uint32_t delay = 0; - - zc_count ++; - - 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, CHAN1, delay); - timer_restart(LPC_TIMER_32B1); - /* Set "AC triggering" stame machine state */ - ac_ctrl_state = 1; -} - - - -/***************************************************************************** */ -/* System interface */ -enum buttons { - BUTTON_NONE = 0, - BUTTON_OK, - BUTTON_UP, - BUTTON_DOWN, -}; - - -uint32_t manual_activation_request = 0; -uint8_t button_pressed = 0; -void manual_activation(uint32_t gpio) { - manual_activation_request = MANUAL_ACTIVATION_DURATION; - button_pressed = BUTTON_OK; -} -void manual_up(uint32_t gpio) { - manual_activation_request = MANUAL_ACTIVATION_DURATION; - button_pressed = BUTTON_UP; -} -void manual_down(uint32_t gpio) { - manual_activation_request = MANUAL_ACTIVATION_DURATION; - button_pressed = BUTTON_DOWN; -} -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--; - } - } -} - - -/***************************************************************************** */ -void temp_config(int uart_num) -{ - int ret = 0; - ret = tmp101_sensor_config(&tmp101_sensor); - if (ret != 0) { - uprintf(uart_num, "Temp config error: %d\n", ret); - } -} - - -/***************************************************************************** */ -/* Oled Display */ -#define DISPLAY_ADDR 0x78 -static uint8_t gddram[ 4 + GDDRAM_SIZE ]; -struct oled_display display = { - .bus_type = SSD130x_BUS_I2C, - .address = DISPLAY_ADDR, - .bus_num = I2C0, - .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); -} -#define OLED_LINE_CHAR_LENGTH (SSD130x_NB_COL / 8) -#define DISPLAY_LINE_LENGTH (OLED_LINE_CHAR_LENGTH + 1) -int display_line(uint8_t line, uint8_t col, char* text) -{ - int len = strlen((char*)text); - int i = 0; - - 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) { - return i; - } - } - } - return len; -} - - - - -/***************************************************************************** */ -#define NB_VAL 20 - -enum modes { - heat = 'C', - ejp = 'E', - delayed_heat_prod = 'P', - forced = 'F', - temp_OK = 'T', - manual = 'M', - idle_heat = 'L', - full_heat = 'F', -}; - -/***************************************************************************** */ -int main(void) -{ - uint16_t isnail_solar_values[NB_VAL]; - uint16_t isnail_home_values[NB_VAL]; - uint8_t idx = 0; - uint32_t loop = 0; - char mode = heat; /* Debug info */ - int ret = 0; - - system_init(); - status_led(red_only); - uart_on(UART0, 115200, config_rx); - uart_on(UART1, 115200, cmd_rx); - i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER); - ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000); - adc_on(NULL); - timer_on(LPC_TIMER_32B0, 0, NULL); - timer_on(LPC_TIMER_32B1, 0, ac_switch_on); - - /* Immediatly turn off Mosfet / Triac */ - config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0); - - /* Thermocouple configuration */ - max31855_sensor_config(&thermo); - uprintf(UART0, "Thermocouple config done\n"); - - /* TMP101 sensor config */ - temp_config(UART0); - - /* Activate on Rising edge (button release) */ - set_gpio_callback(manual_activation, &button_ok, EDGE_RISING); -#if 0 - /* Debug */ - config_gpio(&button_b1, 0, GPIO_DIR_OUT, 0); - config_gpio(&button_b2, 0, GPIO_DIR_OUT, 0); -#endif - set_gpio_callback(manual_up, &button_b1, EDGE_RISING); - set_gpio_callback(manual_down, &button_b2, EDGE_RISING); - - /* Zero cross and alert pin */ - set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING); - - /* Start ADC sampling */ - adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0)); - - /* Configure Input GPIO */ - config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0); - config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0); - config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1); - - /* WS2812B Leds on display board */ - ws2812_config(&ws2812_data_out_pin); - - /* FAN Config */ - timer_pwm_config(LPC_TIMER_32B0, &fan_pwm_conf); - timer_start(LPC_TIMER_32B0); - /* AC Switch Config */ - timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf); - /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */ - clk_cycles_ac_zc = get_main_clock() / (100 * 100); - - status_led(green_only); - - /* Configure and start display */ - ret = ssd130x_display_on(&display); - /* Erase screen */ - ssd130x_buffer_set(gddram, 0x00); - ret = ssd130x_display_full_screen(&display); - - /* RTC init */ - ret = rtc_pcf85363a_config(&rtc_conf); - ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest); - if (ret == 1) { - char buff[30]; - rtc_pcf85363_time_read(&rtc_conf, &now); - rtc_pcf85363_time_to_str(&now, buff, 30); - /* Debug */ - uprintf(UART0, buff); - } else if (ret == -EFAULT) { - memcpy(&now, &oldest, sizeof(struct rtc_time)); - rtc_pcf85363_time_write(&rtc_conf, &now); - } - - /* microSD card init */ - ret = sdmmc_init(µ_sd); - if (ret == 0) { - msleep(1); - ret = sdmmc_init_wait_card_ready(µ_sd); - if (ret == 0) { - ret = sdmmc_init_end(µ_sd); - } - } - uprintf(UART0, "uSD init: %d, type: %d, bs: %d\n", ret, micro_sd.card_type, micro_sd.block_size); - ret = sdmmc_read_block(µ_sd, 0, mmc_data); - uprintf(UART0, "uSD read: %s\n", mmc_data); - - /* Add a systick callback to handle time counting */ - //add_systick_callback(handle_dec_request, DEC_PERIOD); - - msleep(50); - /* Read parameters from memory */ - if (1) { - never_force = 0; - forced_heater_delay = 0; - forced_heater_time = FORCED_HEATER_DURATION; - } - - while (1) { - static uint8_t command_val = 0; - static uint8_t n_dec = 0; /* Add some PID like (derivative part) */ - static uint8_t n_inc = 0; /* Add some PID like (derivative part) */ - int moyenne_solar = 0; - int moyenne_home = 0; - uint16_t isnail_val_solar = 0; - uint16_t isnail_val_home = 0; - uint16_t acs_val_load = 0; - uint16_t user_potar = 0; - int water_centi_degrees = 0; - int tmp101_deci_degrees = 0; - - mode = heat; - tmp101_sensor_start_conversion(&tmp101_sensor); - /* Always track power consumption and production */ - adc_get_value(&isnail_val_solar, LPC_ADC(1)); - adc_get_value(&isnail_val_home, LPC_ADC(0)); - adc_get_value(&acs_val_load, LPC_ADC(2)); - adc_get_value(&user_potar, LPC_ADC(7)); - /* Convert to mA value */ - isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ - isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ - /* Store value */ - isnail_solar_values[idx] = isnail_val_solar; - isnail_home_values[idx++] = isnail_val_home; - if (idx == NB_VAL) { - idx = 0; - } - /* Compute average value when we sampled enough values */ - /* FIXME : Improve by substracting oldest value before storing new one in table and adding new one */ - if ((idx == 0) || (idx == (NB_VAL / 2))) { - int i = 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; - } else { - /* Sleep for a litle more than a period (20ms at 50Hz) */ - msleep(23); - continue; - } - - /* Feed the dog */ - if ((moyenne_solar != 0) && (moyenne_home != 0)) { - watchdog_feed(); - } - - /* Get internal temperature */ - if (1) { - int ret = 0; - msleep(40); - ret = tmp101_sensor_read(&tmp101_sensor, NULL, &tmp101_deci_degrees); - if (ret != 0) { - uprintf(UART0, "TMP101 read error : %d\n", ret); - } - } - /* If internal temperature is above 30°C, then turn on fan. Turn of when back to under 28.5°C */ - if (tmp101_deci_degrees > 300) { - fan_speed = 100; - timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON); - } else if (tmp101_deci_degrees < 285) { - fan_speed = 0; - timer_set_match(LPC_TIMER_32B0, CHAN0, 0); - } - - /* Get thermocouple value */ - if (1) { - int ret = 0; - ret = max31855_sensor_read(&thermo, NULL, &water_centi_degrees); - if (ret != 0) { - uprintf(UART0, "Water Temp read error : %d\n", ret); - } - } - - /* Need to enter Forced heating mode ? */ - if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) { - if (forced_heater_mode == 0) { - uprintf(UART0, "Entering forced mode\n"); - forced_heater_mode = 1; - } - status_led(red_on); - mode = forced; - } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) { - status_led(red_off); - forced_heater_mode = 0; - command_val = 0; - uprintf(UART0, "Forced mode exit\n"); - mode = temp_OK; - } - - /* Do not force if there is a lot of sun, it may be enough to heat again soon */ - if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) { - mode = delayed_heat_prod; - forced_heater_mode = 0; - /* Note : Do not set forced_heater_mode to 0 in order to keep decrementing the delay for force - * heating in case the house power usage does not fall below the production value. */ - } - - /* Do not force heating if this is an EJP day */ - ejp_in = gpio_read(ejp_in_pin); - if (ejp_in == DAY_IS_EJP) { - forced_heater_mode = 0; - mode = ejp; - } - - if (never_force == 1) { - forced_heater_mode = 0; - } - - /* Did the user request a forced heating ? */ - if (manual_activation_request > 1) { - forced_heater_mode = 1; - mode = manual; - if (manual_activation_request == MANUAL_ACTIVATION_DURATION) { - uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request); - /* Add a systick callback to handle time counting */ - add_systick_callback(handle_dec_request, DEC_PERIOD); - } - if (manual_activation_request < 10) { - uprintf(UART0, "Leaving manual forced mode\n"); - manual_activation_request = 0; - remove_systick_callback(handle_dec_request); - } - } - - - /* Which is the current mode ? */ - if (forced_heater_mode == 1) { - /* Forced heating mode */ - if ((forced_heater_delay == 0) && (forced_heater_time > 0)) { - command_val = FORCED_MODE_VALUE; - } - if (forced_heater_time == 0) { - forced_heater_delay = FORCED_HEATER_DELAY; - forced_heater_time = FORCED_HEATER_DURATION; - } - } else if (moyenne_solar < (moyenne_home - 75)) { - /* Low production mode */ - if (command_val > 15) { - command_val -= ((3 + n_dec) % 15); - /* Asservissement */ - n_dec++; - if (n_dec >= 3) { - n_inc = 0; - } - } else { - command_val = 0; - mode = idle_heat; - } - status_led(green_off); - } else if (moyenne_solar > (moyenne_home + 75)) { - /* High production mode */ - if (command_val < 95) { - command_val += (2 + n_inc); - /* Asservissement */ - n_inc++; - if (n_inc >= 5) { - n_inc = 5; - n_dec = 0; - } - } else { - command_val = 100; - mode = full_heat; - } - status_led(green_on); - } - - /* Set Control Output duty cycle */ - if (water_centi_degrees > (MAX_HEATER_TEMP * 100)) { - command_val = 0; - } - - set_ctrl_duty_cycle(command_val); - /* Debug Nath TMP */ - //set_ctrl_duty_cycle( user_potar / 10 ); - /* Display */ - if (1) { - char line[DISPLAY_LINE_LENGTH]; - int abs_centi = water_centi_degrees; - int abs_deci = tmp101_deci_degrees; - 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)); - if (water_centi_degrees < 0) { - abs_centi = -water_centi_degrees; - } - uprintf(UART0, "Water Temp : % 4d.%02d\n", (water_centi_degrees / 100), (abs_centi % 100)); - if (tmp101_deci_degrees < 0) { - abs_deci = -tmp101_deci_degrees; - } - uprintf(UART0, "Internal Temp : % 4d.%02d\n", (tmp101_deci_degrees / 10), (abs_deci % 10)); - uprintf(UART0, "ZC_cnt: %d\n", zc_count); - uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d, User: %d\n", - isnail_val_solar, isnail_val_home, acs_val_load, user_potar); - if (button_pressed != 0) { - uprintf(UART0, "Button : %d\n", button_pressed); - button_pressed = 0; - } - uprintf(UART0, "CMD: %d/%d, Fan: %d, ndec:%d, ninc:%d\n\n", command_val, act_cmd, fan_speed, n_dec, n_inc); - ws2812_set_pixel(0, (isnail_val_home / 2000), (isnail_val_solar / 2000), fan_speed); - ws2812_set_pixel(1, 0, 0, (user_potar >> 2)); - ws2812_send_frame(0); - /* Erase screen (internal copy) */ - ssd130x_buffer_set(gddram, 0x00); - /* Update time and time display on internal memory */ - rtc_pcf85363_time_read(&rtc_conf, &now); - snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec); - display_line(0, 0, line); - /* Display info */ - snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F); - display_line(2, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (isnail_val_solar / 1000), ((isnail_val_solar % 1000) / 10)); - display_line(3, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (isnail_val_home / 1000), ((isnail_val_home % 1000) / 10)); - display_line(4, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", act_cmd); - display_line(5, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", mode); - display_line(6, 0, line); - /* Update Oled display */ - ret = ssd130x_display_full_screen(&display); - } - } - return 0; -} - - - diff --git a/mosfet/Makefile b/mosfet/Makefile deleted file mode 100644 index 41ae555..0000000 --- a/mosfet/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# 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/mosfet/README b/mosfet/README deleted file mode 100644 index 42a1d84..0000000 --- a/mosfet/README +++ /dev/null @@ -1,22 +0,0 @@ -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 . - * - *************************************************************************** */ - -Beta version of Scialys system. diff --git a/mosfet/main.c b/mosfet/main.c deleted file mode 100644 index d69574c..0000000 --- a/mosfet/main.c +++ /dev/null @@ -1,852 +0,0 @@ -/**************************************************************************** - * apps/scialys/beta/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 "core/system.h" -#include "core/systick.h" -#include "core/pio.h" -#include "lib/stdio.h" -#include "lib/errno.h" -#include "drivers/serial.h" -#include "drivers/gpio.h" -#include "drivers/adc.h" -#include "drivers/ssp.h" -#include "drivers/i2c.h" -#include "drivers/timers.h" - -#include "extdrv/status_led.h" -#include "extdrv/ws2812.h" -#include "extdrv/max31855_thermocouple.h" -#include "extdrv/tmp101_temp_sensor.h" -#include "extdrv/rtc_pcf85363a.h" -#include "extdrv/ssd130x_oled_driver.h" -#include "lib/font.h" -#include "lib/time.h" - -#include "extdrv/sdmmc.h" - -#define MODULE_VERSION 0x02 -#define MODULE_NAME "Scialys uC" - - -#define SELECTED_FREQ FREQ_SEL_48MHz - - -/***************************************************************************** */ -/* System configuration - * Most of the defines in here should go to configuration setting in user flash - */ - -/* Period of the decrementer handler from the systick interrupt */ -#define DEC_PERIOD 100 - -/* 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 28 -#define TARGET_FORCED_HEATER_TEMP 45 -#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 NO_FORCED_HEATING_ON_SUNNY_DAYS 1000 - -uint32_t forced_heater_mode = 0; -uint32_t forced_heater_delay = 0; -uint32_t forced_heater_time = 0; - -#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */ -#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */ - -#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */ - -uint32_t never_force = 0; - - -#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */ -int ejp_in = 0; - - - -/***************************************************************************** */ -/* 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 : Config / Debug / USB */ - { LPC_UART0_RX_PIO_0_1, LPC_IO_DIGITAL }, - { LPC_UART0_TX_PIO_0_2, LPC_IO_DIGITAL }, - /* UART 1 : UEXT */ - { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL }, - { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL }, - /* I2C : RTC, Display, UEXT */ - { 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 (Thermocouple + uSD card + UEXT) */ - { 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 }, - /* TIMER_32B0 */ - { LPC_TIMER_32B0_M0_PIO_0_18, (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL) }, /* Fan control */ - /* GPIO */ - { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */ - { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* EJP / External switch input */ - { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */ - { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Temperature driver warning (Mosfet board only) */ - { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet driver Shutdown (Mosfet board only) */ - { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Mosfet / Triac control */ - { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */ - { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */ - { LPC_GPIO_0_23, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */ - { LPC_GPIO_0_26, LPC_IO_DIGITAL }, /* User button B2 */ - { LPC_GPIO_0_27, LPC_IO_DIGITAL }, /* User button B1 */ - { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */ - { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */ - { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */ - ARRAY_LAST_PIO, -}; - -const struct pio_config adc_pins[] = { - { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* ADC0 */ - { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */ - { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */ - { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* ADC3 */ - ARRAY_LAST_PIO, -}; - -const struct pio status_led_green = LPC_GPIO_1_2; -const struct pio status_led_red = LPC_GPIO_1_3; - -/* Inputs */ -/* Buttons */ -const struct pio button_ok = LPC_GPIO_0_12; -const struct pio button_b1 = LPC_GPIO_0_27; -const struct pio button_b2 = LPC_GPIO_0_26; -/* External signals */ -const struct pio rtc_in_pin = LPC_GPIO_0_0; -const struct pio ejp_in_pin = LPC_GPIO_0_3; -const struct pio zero_cross_in_pin = LPC_GPIO_0_4; -const struct pio th_warn_in_pin = LPC_GPIO_0_5; -const struct pio charge_status_in_pin = LPC_GPIO_0_28; - -/* Outputs */ -/* Led control data pin */ -const struct pio ws2812_data_out_pin = LPC_GPIO_0_23; -/* AC output control (Mosfet / Triac) */ -const struct pio ac_ctrl = LPC_GPIO_0_7; -const struct pio mos_shutdown = LPC_GPIO_0_6; - - -/* Thermocouple reading */ -const struct max31855_sensor_config thermo = { - .ssp_bus_num = 0, - .chip_select = LPC_GPIO_0_15, -}; - -/* TMP101 onboard I2C temperature sensor */ -#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */ -struct tmp101_sensor_config tmp101_sensor = { - .bus_num = I2C0, - .addr = TMP101_ADDR, - .resolution = TMP_RES_ELEVEN_BITS, -}; - - -#define FAN_ON (10 * 1000) -const struct lpc_timer_pwm_config fan_pwm_conf = { - .nb_channels = 1, - .period_chan = CHAN3, - .period = FAN_ON, - .outputs = { CHAN0, }, - .match_values = { 0, }, -}; - -const struct lpc_tc_config ac_timer_conf = { - .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH, - .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, }, - .match = { 0, 10, 0, 0, }, - .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, }, -}; - - -/***************************************************************************** */ -/* SD/MMC Card */ -struct sdmmc_card micro_sd = { - .ssp_bus_num = SSP_BUS_0, - .card_type = MMC_CARDTYPE_UNKNOWN, - .block_size = 64, - .chip_select = LPC_GPIO_1_6, -}; - -uint8_t mmc_data[MMC_MAX_SECTOR_SIZE]; - - -/***************************************************************************** */ -/* RTC and time */ -#define RTC_ADDR 0xA2 -struct rtc_pcf85363a_config rtc_conf = { - .bus_num = I2C0, - .addr = RTC_ADDR, - .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 = 0x16, - .month = 0x10, - .day = 0x30, - .hour = 0x13, - .min = 0x00, -}; -static struct rtc_time now; - - -/***************************************************************************** */ -/* 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); - set_pins(adc_pins); - gpio_on(); - status_led_config(&status_led_green, &status_led_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); -} - - -/***************************************************************************** */ -/* Some global flags or variables */ -static uint8_t thermal_warn_flag = 0; -static uint32_t fan_speed = 0; -static int act_cmd = 0; - - -/***************************************************************************** */ -/* System configuration over USB */ -void config_rx(uint8_t c) -{ - /* FAN control */ - if (c == 'f') { - fan_speed = 100; - timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON); - } else if (c == 'z') { - fan_speed = 0; - timer_set_match(LPC_TIMER_32B0, CHAN0, 0); - } else if ((c >= '0') && (c <= '9')) { - fan_speed = ((c - '0') * 10); - if (fan_speed < 60) { - fan_speed = 0; - } - timer_set_match(LPC_TIMER_32B0, CHAN0, (FAN_ON - ((FAN_ON / 100) * (100 - fan_speed)))); - } -} - - - -/***************************************************************************** */ -/* System communication over UART1 */ -void cmd_rx(uint8_t c) -{ -} - - - -/***************************************************************************** */ -/* AC control */ - -void set_ctrl_duty_cycle(uint8_t value) -{ - if (thermal_warn_flag == 1) { - /* Thermal warning condition still present ? */ - if (gpio_read(th_warn_in_pin) != 0) { - thermal_warn_flag = 0; - } else { - act_cmd = 0; - return; - } - } - act_cmd = value; - if (act_cmd > 100) { - /* 100 is the maximum allowed value */ - act_cmd = 100; - } else if (act_cmd <= 2) { - /* Below 3% there are triggering problems which lead to 50% instead of 1% or 2% */ - act_cmd = 0; - /* Set mosfet driver shutdown */ - gpio_set(mos_shutdown); - return; - } - /* Clear the shutdown condition from the mosfet driver */ - gpio_clear(mos_shutdown); -} - -void ac_switch_on(uint32_t flags) -{ - gpio_set(ac_ctrl); - gpio_set(button_b2); -} - -static uint32_t clk_cycles_ac_zc = 0; -static volatile uint32_t zc_count = 0; /* Wraps every 1.36 year ... */ -void zero_cross(uint32_t gpio) -{ - uint32_t delay = 0; - - zc_count ++; - gpio_toggle(button_b1); - - gpio_clear(ac_ctrl); - gpio_clear(button_b2); - if (act_cmd == 0) { - return; - } - /* Set timer to trigger ac out ON at given delay */ - delay = clk_cycles_ac_zc * act_cmd; - timer_set_match(LPC_TIMER_32B1, CHAN1, delay); - timer_restart(LPC_TIMER_32B1); -} - -void th_warning(uint32_t gpio) -{ - /* Test for condition set or removed */ - if (gpio_read(th_warn_in_pin) == 0) { - /* Turn off AC output */ - gpio_clear(ac_ctrl); - /* Set mosfet driver shutdown */ - gpio_set(mos_shutdown); - act_cmd = 0; - thermal_warn_flag = 1; - /* Turn on Fan at max speed */ - timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON); - fan_speed = 100; - } else { - /* Only clear the warning flag, the rest will get back to the desired state soon enough. */ - thermal_warn_flag = 0; - } -} - - - -/***************************************************************************** */ -/* System interface */ -enum buttons { - BUTTON_NONE = 0, - BUTTON_OK, - BUTTON_UP, - BUTTON_DOWN, -}; - - -uint32_t manual_activation_request = 0; -uint8_t button_pressed = 0; -void manual_activation(uint32_t gpio) { - manual_activation_request = MANUAL_ACTIVATION_DURATION; - button_pressed = BUTTON_OK; -} -void manual_up(uint32_t gpio) { - manual_activation_request = MANUAL_ACTIVATION_DURATION; - button_pressed = BUTTON_UP; -} -void manual_down(uint32_t gpio) { - manual_activation_request = MANUAL_ACTIVATION_DURATION; - button_pressed = BUTTON_DOWN; -} -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--; - } - } -} - - -/***************************************************************************** */ -void temp_config(int uart_num) -{ - int ret = 0; - ret = tmp101_sensor_config(&tmp101_sensor); - if (ret != 0) { - uprintf(uart_num, "Temp config error: %d\n", ret); - } -} - - -/***************************************************************************** */ -/* Oled Display */ -#define DISPLAY_ADDR 0x78 -struct oled_display display = { - .address = DISPLAY_ADDR, - .bus_num = I2C0, - .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, -}; - -#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_set_tile(&display, col, line, tile_data); -} -#define OLED_LINE_CHAR_LENGTH (SSD130x_NB_COL / 8) -#define DISPLAY_LINE_LENGTH (OLED_LINE_CHAR_LENGTH + 1) -int display_line(uint8_t line, uint8_t col, char* text) -{ - int len = strlen((char*)text); - int i = 0; - - 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_set_tile(&display, col++, line, tile_data); - if (col >= (OLED_LINE_CHAR_LENGTH)) { - col = 0; - line++; - if (line >= SSD130x_NB_PAGES) { - return i; - } - } - } - return len; -} - - - - -/***************************************************************************** */ -#define NB_VAL 20 - -enum modes { - heat = 'C', - ejp = 'E', - delayed_heat_prod = 'P', - forced = 'F', - temp_OK = 'T', - manual = 'M', - idle_heat = 'L', - full_heat = 'F', -}; - -/***************************************************************************** */ -int main(void) -{ - uint16_t isnail_solar_values[NB_VAL]; - uint16_t isnail_home_values[NB_VAL]; - uint8_t idx = 0; - uint32_t loop = 0; - char mode = heat; /* Debug info */ - int ret = 0; - - system_init(); - status_led(red_only); - uart_on(UART0, 115200, config_rx); - uart_on(UART1, 115200, cmd_rx); - i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER); - ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000); - adc_on(NULL); - timer_on(LPC_TIMER_32B0, 0, NULL); - timer_on(LPC_TIMER_32B1, 0, ac_switch_on); - - /* Immediatly turn off Mosfet / Triac */ - config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0); - config_gpio(&mos_shutdown, 0, GPIO_DIR_OUT, 1); - - /* Thermocouple configuration */ - max31855_sensor_config(&thermo); - uprintf(UART0, "Thermocouple config done\n"); - - /* TMP101 sensor config */ - temp_config(UART0); - - /* Activate on Rising edge (button release) */ - set_gpio_callback(manual_activation, &button_ok, EDGE_RISING); -#if 0 - set_gpio_callback(manual_up, &button_b1, EDGE_RISING); - set_gpio_callback(manual_down, &button_b2, EDGE_RISING); -#else - config_gpio(&button_b1, 0, GPIO_DIR_OUT, 0); - config_gpio(&button_b2, 0, GPIO_DIR_OUT, 0); -#endif - - /* Zero cross and alert pin */ - set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING); - set_gpio_callback(th_warning, &th_warn_in_pin, EDGES_BOTH); - - /* Start ADC sampling */ - adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0)); - - /* Configure Input GPIO */ - config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0); - config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0); - config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1); - - /* WS2812B Leds on display board */ - ws2812_config(&ws2812_data_out_pin); - - /* FAN Config */ - timer_pwm_config(LPC_TIMER_32B0, &fan_pwm_conf); - timer_start(LPC_TIMER_32B0); - /* AC Switch Config */ - timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf); - /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */ - clk_cycles_ac_zc = get_main_clock() / (100 * 100); - - status_led(green_only); - - /* Configure and start display */ - ret = ssd130x_display_on(&display); - /* Erase screen */ - ssd130x_display_set(&display, 0x00); - ret = ssd130x_display_full_screen(&display); - - /* RTC init */ - ret = rtc_pcf85363a_config(&rtc_conf); - ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest); - if (ret == 1) { - char buff[30]; - rtc_pcf85363_time_read(&rtc_conf, &now); - rtc_pcf85363_time_to_str(&now, buff, 30); - /* Debug */ - uprintf(UART0, buff); - } else if (ret == -EFAULT) { - memcpy(&now, &oldest, sizeof(struct rtc_time)); - rtc_pcf85363_time_write(&rtc_conf, &now); - } - - /* microSD card init */ - ret = sdmmc_init(µ_sd); - if (ret == 0) { - msleep(1); - ret = sdmmc_init_wait_card_ready(µ_sd); - if (ret == 0) { - ret = sdmmc_init_end(µ_sd); - } - } - uprintf(UART0, "uSD init: %d, type: %d, bs: %d\n", ret, micro_sd.card_type, micro_sd.block_size); - ret = sdmmc_read_block(µ_sd, 0, mmc_data); - uprintf(UART0, "uSD read: %s\n", mmc_data); - - /* Add a systick callback to handle time counting */ - //add_systick_callback(handle_dec_request, DEC_PERIOD); - - msleep(50); - /* Read parameters from memory */ - if (1) { - never_force = 0; - forced_heater_delay = 0; - forced_heater_time = FORCED_HEATER_DURATION; - } - - /* Clear thermal warning flag if warning pin is high */ - if (gpio_read(th_warn_in_pin) != 0) { - thermal_warn_flag = 0; - } - - while (1) { - static uint8_t command_val = 0; - static uint8_t n_dec = 0; /* Add some PID like (derivative part) */ - static uint8_t n_inc = 0; /* Add some PID like (derivative part) */ - int moyenne_solar = 0; - int moyenne_home = 0; - uint16_t isnail_val_solar = 0; - uint16_t isnail_val_home = 0; - uint16_t acs_val_load = 0; - uint16_t user_potar = 0; - int water_centi_degrees = 0; - int tmp101_deci_degrees = 0; - - mode = heat; - tmp101_sensor_start_conversion(&tmp101_sensor); - /* Always track power consumption and production */ - adc_get_value(&isnail_val_solar, LPC_ADC(1)); - adc_get_value(&isnail_val_home, LPC_ADC(0)); - adc_get_value(&acs_val_load, LPC_ADC(2)); - adc_get_value(&user_potar, LPC_ADC(7)); - /* Convert to mA value */ - isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ - isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ - /* Store value */ - isnail_solar_values[idx] = isnail_val_solar; - isnail_home_values[idx++] = isnail_val_home; - if (idx == NB_VAL) { - idx = 0; - } - /* Compute average value when we sampled enough values */ - /* FIXME : Improve by substracting oldest value before storing new one in table and adding new one */ - if ((idx == 0) || (idx == (NB_VAL / 2))) { - int i = 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; - } else { - /* Sleep for a litle more than a period (20ms at 50Hz) */ - msleep(23); - continue; - } - - /* Feed the dog */ - if ((moyenne_solar != 0) && (moyenne_home != 0)) { - watchdog_feed(); - } - - /* Get internal temperature */ - if (1) { - int ret = 0; - msleep(40); - ret = tmp101_sensor_read(&tmp101_sensor, NULL, &tmp101_deci_degrees); - if (ret != 0) { - uprintf(UART0, "TMP101 read error : %d\n", ret); - } - } - /* If internal temperature is above 30°C, then turn on fan. Turn of when back to under 28.5°C */ - if (tmp101_deci_degrees > 300) { - fan_speed = 100; - timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON); - } else if (tmp101_deci_degrees < 285) { - fan_speed = 0; - timer_set_match(LPC_TIMER_32B0, CHAN0, 0); - } - - /* Get thermocouple value */ - if (1) { - int ret = 0; - ret = max31855_sensor_read(&thermo, NULL, &water_centi_degrees); - if (ret != 0) { - uprintf(UART0, "Water Temp read error : %d\n", ret); - } - } - - /* Need to enter Forced heating mode ? */ - if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) { - if (forced_heater_mode == 0) { - uprintf(UART0, "Entering forced mode\n"); - forced_heater_mode = 1; - } - status_led(red_on); - mode = forced; - } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) { - status_led(red_off); - forced_heater_mode = 0; - command_val = 0; - uprintf(UART0, "Forced mode exit\n"); - mode = temp_OK; - } - - /* Do not force if there is a lot of sun, it may be enough to heat again soon */ - if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) { - mode = delayed_heat_prod; - forced_heater_mode = 0; - /* Note : Do not set forced_heater_mode to 0 in order to keep decrementing the delay for force - * heating in case the house power usage does not fall below the production value. */ - } - - /* Do not force heating if this is an EJP day */ - ejp_in = gpio_read(ejp_in_pin); - if (ejp_in == DAY_IS_EJP) { - forced_heater_mode = 0; - mode = ejp; - } - - if (never_force == 1) { - forced_heater_mode = 0; - } - - /* Did the user request a forced heating ? */ - if (manual_activation_request > 1) { - forced_heater_mode = 1; - mode = manual; - if (manual_activation_request == MANUAL_ACTIVATION_DURATION) { - uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request); - /* Add a systick callback to handle time counting */ - add_systick_callback(handle_dec_request, DEC_PERIOD); - } - if (manual_activation_request < 10) { - uprintf(UART0, "Leaving manual forced mode\n"); - manual_activation_request = 0; - remove_systick_callback(handle_dec_request); - } - } - - - /* Which is the current mode ? */ - if (forced_heater_mode == 1) { - /* Forced heating mode */ - if ((forced_heater_delay == 0) && (forced_heater_time > 0)) { - command_val = FORCED_MODE_VALUE; - } - if (forced_heater_time == 0) { - forced_heater_delay = FORCED_HEATER_DELAY; - forced_heater_time = FORCED_HEATER_DURATION; - } - } else if (moyenne_solar < (moyenne_home - 75)) { - /* Low production mode */ - if (command_val > 15) { - command_val -= ((3 + n_dec) % 15); - /* Asservissement */ - n_dec++; - if (n_dec >= 3) { - n_inc = 0; - } - } else { - command_val = 0; - mode = idle_heat; - } - status_led(green_off); - } else if (moyenne_solar > (moyenne_home + 75)) { - /* High production mode */ - if (command_val < 95) { - command_val += (2 + n_inc); - /* Asservissement */ - n_inc++; - if (n_inc >= 5) { - n_inc = 5; - n_dec = 0; - } - } else { - command_val = 100; - mode = full_heat; - } - status_led(green_on); - } - - /* Set Control Output duty cycle */ - //set_ctrl_duty_cycle(command_val); - /* Debug Nath TMP */ - set_ctrl_duty_cycle( (user_potar - 5) / 10 ); - - /* Display */ - if (1) { - int abs_centi = water_centi_degrees; - int abs_deci = tmp101_deci_degrees; - - if (water_centi_degrees < 0) { - abs_centi = -water_centi_degrees; - } - if (tmp101_deci_degrees < 0) { - abs_deci = -tmp101_deci_degrees; - } - - 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 : % 4d.%02d\n", (water_centi_degrees / 100), (abs_centi % 100)); - uprintf(UART0, "Internal Temp : % 4d.%02d\n", (tmp101_deci_degrees / 10), (abs_deci % 10)); - uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d, User: %d\n", - isnail_val_solar, isnail_val_home, acs_val_load, user_potar); - if (button_pressed != 0) { - uprintf(UART0, "Button : %d\n", button_pressed); - button_pressed = 0; - } - uprintf(UART0, "ZC_cnt: %d, user: %d, ThW: %d\n", zc_count, user_potar / 10, thermal_warn_flag); - uprintf(UART0, "CMD: %d/%d, Fan: %d, ndec:%d, ninc:%d\n\n", command_val, act_cmd, fan_speed, n_dec, n_inc); - } - - if (1) { - ws2812_set_pixel(0, (isnail_val_home / 2000), (isnail_val_solar / 2000), fan_speed); - ws2812_set_pixel(1, 0, 0, (user_potar >> 2)); - ws2812_send_frame(0); - } - - if (1) { - char line[DISPLAY_LINE_LENGTH]; - /* Erase screen (internal copy) */ - ssd130x_display_set(&display, 0x00); - /* Update time and time display on internal memory */ - rtc_pcf85363_time_read(&rtc_conf, &now); - snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec); - display_line(0, 0, line); - /* Display info */ - snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F); - display_line(2, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (isnail_val_solar / 1000), ((isnail_val_solar % 1000) / 10)); - display_line(3, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (isnail_val_home / 1000), ((isnail_val_home % 1000) / 10)); - display_line(4, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", act_cmd); - display_line(5, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", mode); - display_line(6, 0, line); - /* Update Oled display */ - ret = ssd130x_display_full_screen(&display); - } - } - } - return 0; -} - - - diff --git a/old/alpha/Makefile b/old/alpha/Makefile new file mode 100644 index 0000000..41ae555 --- /dev/null +++ b/old/alpha/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/old/alpha/README b/old/alpha/README new file mode 100644 index 0000000..2eebdad --- /dev/null +++ b/old/alpha/README @@ -0,0 +1,24 @@ +DMX module for solar-panel power generation tracking and fair use. + +Copyright 2015 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 . + * + *************************************************************************** */ + +First prototype version of the Scialys module. +Used for validation of feasability + diff --git a/old/alpha/main.c b/old/alpha/main.c new file mode 100644 index 0000000..24a7030 --- /dev/null +++ b/old/alpha/main.c @@ -0,0 +1,462 @@ +/**************************************************************************** + * apps/scialys/alpha/main.c + * + * DMX module for solar-panel power generation tracking and fair use. + * + * Copyright 2015-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 . + * + *************************************************************************** */ + + +/* DMX informations : + * https://en.wikipedia.org/wiki/DMX512 + * + * DMX uses an RS485 line with a few glitches (the break and start pulse). + * The DMX module uses an ADM2482 isolated RS485 bridge from analog devices. + */ + +#include "core/system.h" +#include "core/pio.h" +#include "core/systick.h" +#include "lib/stdio.h" +#include "drivers/serial.h" +#include "drivers/gpio.h" +#include "extdrv/status_led.h" +#include "drivers/adc.h" +#include "drivers/ssp.h" + +#include "extdrv/max31855_thermocouple.h" + + +#define MODULE_VERSION 0x02 +#define MODULE_NAME "DMX Module" + + +#define SELECTED_FREQ FREQ_SEL_48MHz + +/* Period of the decrementer handler from the systick interrupt */ +#define DEC_PERIOD 100 + +/* 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 heated is controlled to heat at FORCED_MODE_VALUE which + * is between 0 and 255. + */ +#define FORCE_HEATER_TEMP 40 +#define TARGET_FORCED_HEATER_TEMP 45 +#define NO_FORCED_HEATING_ON_SUNNY_DAYS 750 +#define FORCED_MODE_VALUE 190 /* A fraction of 255 */ +uint32_t forced_heater_mode = 0; +uint32_t forced_heater_delay = 0; +uint32_t forced_heater_time = 0; + +#define FORCED_HEATER_DELAY (7 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */ +#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */ + +#define MANUAL_ACTIVATION_DURATION (3600 * 1000 / DEC_PERIOD) /* One hour */ + +uint32_t never_force = 0; + +/***************************************************************************** */ +/* 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 }, + { LPC_UART0_RTS_PIO_0_0, LPC_IO_DIGITAL }, + /* UART 1 */ + { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL }, + { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL }, + /* 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 }, + /* GPIO */ + { LPC_GPIO_0_12, LPC_IO_DIGITAL }, + { LPC_GPIO_0_25, LPC_IO_DIGITAL }, + { LPC_GPIO_0_26, LPC_IO_DIGITAL }, + { LPC_GPIO_0_28, LPC_IO_DIGITAL }, + { LPC_GPIO_0_29, LPC_IO_DIGITAL }, + ARRAY_LAST_PIO, +}; + +const struct pio_config adc_pins[] = { + { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */ + { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */ + ARRAY_LAST_PIO, +}; + +const struct pio status_led_green = LPC_GPIO_0_28; +const struct pio status_led_red = LPC_GPIO_0_29; + +const struct pio button = LPC_GPIO_0_12; /* ISP button */ +const struct pio ejp_in_pin = LPC_GPIO_0_25; +#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */ +int ejp_in = 0; + +/* Thermocouple reading */ +const struct max31855_sensor_config thermo = { + .ssp_bus_num = 0, + .chip_select = LPC_GPIO_0_26, +}; + +/* DMX signals */ +/* DMX signals are on LPC_GPIO_0_0 for Frame enable pin (RS485 enable) and LPC_GPIO_0_2 for + * Frame Tx pin (RS485 Tx). + */ +const struct pio frame_tx_pin = LPC_GPIO_0_2; +const struct pio frame_en_pin = LPC_GPIO_0_0; + + + +/***************************************************************************** */ +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 = 0x0FFFFFF, /* 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); + set_pins(adc_pins); + gpio_on(); + status_led_config(&status_led_green, &status_led_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); +} + + +/* Put any data received from RS485 UART in rs485_rx_buff + */ +void rs485_rx(uint8_t c) +{ +} + +void cmd_rx(uint8_t c) +{ +} + +void dmx_send_frame(uint8_t start_code, uint8_t* slots, uint16_t nb_slots) +{ + uint16_t sent = 0; + + /* Configure pins for Start of frame transmission */ + config_gpio(&frame_tx_pin, LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 0); + config_gpio(&frame_en_pin, LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 1); + /* Send break : 92us minimum + * Pins default state chosen to generate the break condition */ + usleep(100); + /* Send Mark-After-Break (MAB) of 12us minimum */ + gpio_set(frame_tx_pin); + usleep(12); + + /* Configure back for RS485. + * This is done using direct access to the registers as this step is time critical + */ + /* Make sure IO_Config is clocked */ + io_config_clk_on(); + LPC_IO_CONTROL->pio0_0 = (LPC_IO_FUNC_ALT(2) | LPC_IO_DIGITAL); + LPC_IO_CONTROL->pio0_2 = (LPC_IO_FUNC_ALT(2) | LPC_IO_DIGITAL); + + + /* Send start code */ + serial_send_quickbyte(0, start_code); + + /* And send slots data */ + while (sent < nb_slots) { + int tmp = serial_write(0, (char*)(slots + sent), (nb_slots - sent)); + if (tmp == -1) { + break; + } + sent += tmp; + } + + /* Config done, power off IO_CONFIG block */ + io_config_clk_off(); +} + + +uint32_t manual_activation_request = 0; +void manual_activation(uint32_t gpio) { + manual_activation_request = MANUAL_ACTIVATION_DURATION; +} +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--; + } + } +} + + +/* DMX */ +#define DMX_NB_SLOTS 4 +#define NB_VAL 20 + + +enum modes { + heat = 'C', + ejp = 'E', + no_heat_prod = 'P', + forced = 'F', + temp_OK = 'T', + manual = 'M', + idle_heat = 'L', + full_heat = 'F', +}; + +/***************************************************************************** */ +int main(void) +{ + uint16_t isnail_solar_values[NB_VAL]; + uint16_t isnail_home_values[NB_VAL]; + uint8_t slots[DMX_NB_SLOTS + 1]; + uint8_t idx = 0; + uint32_t loop = 0; + char mode = heat; /* Debug info */ + + system_init(); + status_led(red_only); + uart_set_config(0, (LPC_UART_8BIT | LPC_UART_NO_PAR | LPC_UART_2STOP)); + uart_on(UART0, 250000, rs485_rx); /* FIXME : configure for 250 kbits/s and 8N2 */ + uart_on(UART1, 115200, cmd_rx); + ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000); + adc_on(NULL); + + /* RS485 config */ + if (1) { + uint32_t rs485_ctrl = LPC_RS485_ENABLE; + //rs485_ctrl |= LPC_RS485_DIR_PIN_RTS | LPC_RS485_AUTO_DIR_EN | LPC_RS485_DIR_CTRL_INV; + rs485_ctrl |= LPC_RS485_DIR_PIN_RTS | LPC_RS485_AUTO_DIR_EN | LPC_RS485_DIR_CTRL_INV; + uart_set_mode_rs485(0, rs485_ctrl, 0, 1); + } + + /* Thermocouple configuration */ + max31855_sensor_config(&thermo); + uprintf(UART1, "Thermocouple config OK\n"); + + /* Activate on Rising edge (button release) */ + set_gpio_callback(manual_activation, &button, EDGE_RISING); + + /* Start ADC sampling */ + adc_start_burst_conversion(ADC_MCH(1) | ADC_MCH(2), LPC_ADC_SEQ(0)); + + /* Configure Input GPIO for EJP mode detection */ + config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0); + + status_led(green_only); + memset(slots, 0, (DMX_NB_SLOTS + 1)); + + msleep(50); + /* Read parameters from memory */ + if (1) { + never_force = 0; + forced_heater_delay = FORCED_HEATER_DELAY; + forced_heater_time = FORCED_HEATER_DURATION; + } + + while (1) { + static uint8_t dmx_val = 0; + uint32_t moyenne_solar = 0; + uint32_t moyenne_home = 0; + uint16_t isnail_val_solar = 0; + uint16_t isnail_val_home = 0; + int centi_degrees = 0; + + mode = heat; + /* Always track power consumption and production */ + adc_get_value(&isnail_val_solar, LPC_ADC(1)); + adc_get_value(&isnail_val_home, LPC_ADC(2)); + /* Convert to mA value */ + isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ + isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ + /* Store value */ + isnail_solar_values[idx] = isnail_val_solar; + isnail_home_values[idx++] = isnail_val_home; + if (idx == NB_VAL) { + idx = 0; + } + /* Compute average value when we sampled enough values */ + if ((idx == 0) || (idx == (NB_VAL / 2))) { + int i = 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; + } else { + /* Sleep for a litle more than a period (20ms at 50Hz) */ + msleep(23); + continue; + } + + /* Feed the dog */ + if ((moyenne_solar != 0) && (moyenne_home != 0)) { + watchdog_feed(); + } + + /* Get thermocouple value */ + if (1) { + int ret = 0; + ret = max31855_sensor_read(&thermo, NULL, ¢i_degrees); + if (ret != 0) { + uprintf(UART1, "Temp read error : %d\n", ret); + } + } + if (centi_degrees < (FORCE_HEATER_TEMP * 100)) { + if (forced_heater_mode == 0) { + uprintf(UART1, "Entering forced mode\n"); + forced_heater_mode = 1; + } + status_led(red_on); + mode = forced; + } else if ((centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) { + status_led(red_off); + forced_heater_mode = 0; + dmx_val = 0; + uprintf(UART1, "Forced mode exit\n"); + mode = temp_OK; + } + + /* Do not force if there is some sun, it may be enough to heat again */ + if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) { + forced_heater_mode = 0; + mode = no_heat_prod; + forced_heater_delay = FORCED_HEATER_DELAY; + } + + /* Do not force heating if this is an EJP day */ + ejp_in = gpio_read(ejp_in_pin); + if (ejp_in == DAY_IS_EJP) { + forced_heater_mode = 0; + mode = ejp; + } + + if (never_force == 1) { + forced_heater_mode = 0; + } + + /* Did the user request a forced heating ? */ + if (manual_activation_request > 1) { + forced_heater_mode = 1; + mode = manual; + if (manual_activation_request == MANUAL_ACTIVATION_DURATION) { + uprintf(UART1, "Entering manual forced mode for %d ticks\n", manual_activation_request); + /* Add a systick callback to handle time counting */ + add_systick_callback(handle_dec_request, DEC_PERIOD); + } + if (manual_activation_request < 10) { + uprintf(UART1, "Leaving manual forced mode\n"); + manual_activation_request = 0; + remove_systick_callback(handle_dec_request); + } + } + + + /* Which is the current mode ? */ + if (forced_heater_mode == 1) { + /* Forced heating mode */ + if ((forced_heater_delay == 0) && (forced_heater_time > 0)) { + dmx_val = FORCED_MODE_VALUE; + } + if (forced_heater_time == 0) { + forced_heater_delay = FORCED_HEATER_DELAY; + forced_heater_time = FORCED_HEATER_DURATION; + } + } else if (moyenne_solar < moyenne_home) { + /* Low production mode */ + if (dmx_val > 25) { + dmx_val -= 25; + } else { + dmx_val = 0; + mode = idle_heat; + } + status_led(green_off); + } else { + /* High production mode */ + if (dmx_val < 245) { + dmx_val += 10; + } else { + dmx_val = 255; + mode = full_heat; + } + status_led(green_on); + } + + /* Send DMX frame */ + slots[0] = dmx_val; + if (slots[0] > 255) { + slots[0] = 255; + } + dmx_send_frame(0x00, slots, 1); + /* Display */ + if (1) { + int abs_centi = centi_degrees; + uprintf(UART1, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++, + (moyenne_solar / 1000), (moyenne_solar % 1000), + (moyenne_home / 1000), (moyenne_home % 1000)); + if (centi_degrees < 0) { + abs_centi = -centi_degrees; + } + uprintf(UART1, "Temp : % 4d.%02d\n", (centi_degrees / 100), (abs_centi % 100)); + uprintf(UART1, "DMX: %d\n\n", slots[0]); + } + } + return 0; +} + + + diff --git a/old/beta/Makefile b/old/beta/Makefile new file mode 100644 index 0000000..41ae555 --- /dev/null +++ b/old/beta/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/old/beta/README b/old/beta/README new file mode 100644 index 0000000..42a1d84 --- /dev/null +++ b/old/beta/README @@ -0,0 +1,22 @@ +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 . + * + *************************************************************************** */ + +Beta version of Scialys system. diff --git a/old/beta/main.c b/old/beta/main.c new file mode 100644 index 0000000..b61a2a7 --- /dev/null +++ b/old/beta/main.c @@ -0,0 +1,819 @@ +/**************************************************************************** + * apps/scialys/beta/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 "core/system.h" +#include "core/systick.h" +#include "core/pio.h" +#include "lib/stdio.h" +#include "lib/errno.h" +#include "drivers/serial.h" +#include "drivers/gpio.h" +#include "drivers/adc.h" +#include "drivers/ssp.h" +#include "drivers/i2c.h" +#include "drivers/timers.h" + +#include "extdrv/status_led.h" +#include "extdrv/ws2812.h" +#include "extdrv/max31855_thermocouple.h" +#include "extdrv/tmp101_temp_sensor.h" +#include "extdrv/rtc_pcf85363a.h" +#include "extdrv/ssd130x_oled_driver.h" +#include "extdrv/ssd130x_oled_buffer.h" +#include "lib/font.h" +#include "lib/time.h" + +#include "extdrv/sdmmc.h" + +#define MODULE_VERSION 0x01 +#define MODULE_NAME "Scialys uC" + + +#define SELECTED_FREQ FREQ_SEL_48MHz + + +/***************************************************************************** */ +/* System configuration + * Most of the defines in here should go to configuration setting in user flash + */ + +/* Period of the decrementer handler from the systick interrupt */ +#define DEC_PERIOD 100 + +/* 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 35 +#define MAX_HEATER_TEMP 90 +#define TARGET_FORCED_HEATER_TEMP 50 +#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 NO_FORCED_HEATING_ON_SUNNY_DAYS 750 + +uint32_t forced_heater_mode = 0; +uint32_t forced_heater_delay = 0; +uint32_t forced_heater_time = 0; + +#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */ +#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */ + +#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */ + +uint32_t never_force = 0; + + +#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */ +int ejp_in = 0; + + + +/***************************************************************************** */ +/* 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 : Config / Debug / USB */ + { LPC_UART0_RX_PIO_0_1, LPC_IO_DIGITAL }, + { LPC_UART0_TX_PIO_0_2, LPC_IO_DIGITAL }, + /* UART 1 : UEXT */ + { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL }, + { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL }, + /* I2C : RTC, Display, UEXT */ + { 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 (Thermocouple + uSD card + UEXT) */ + { 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 }, + /* TIMER_32B0 */ + { LPC_TIMER_32B0_M0_PIO_0_18, (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL) }, /* Fan control */ + /* GPIO */ + { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */ + { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* EJP / External switch input */ + { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */ + { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Temperature driver warning (Mosfet board only) */ + { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet driver Shutdown (Mosfet board only) */ + { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Mosfet / Triac control */ + { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */ + { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */ + { LPC_GPIO_0_23, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */ + { LPC_GPIO_0_26, LPC_IO_DIGITAL }, /* User button B2 */ + { LPC_GPIO_0_27, LPC_IO_DIGITAL }, /* User button B1 */ + { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */ + { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */ + { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */ + ARRAY_LAST_PIO, +}; + +const struct pio_config adc_pins[] = { + { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* ADC0 */ + { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */ + { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */ + { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* ADC3 */ + ARRAY_LAST_PIO, +}; + +const struct pio status_led_green = LPC_GPIO_1_2; +const struct pio status_led_red = LPC_GPIO_1_3; + +/* Inputs */ +/* Buttons */ +const struct pio button_ok = LPC_GPIO_0_12; +const struct pio button_b1 = LPC_GPIO_0_27; +const struct pio button_b2 = LPC_GPIO_0_26; +/* External signals */ +const struct pio rtc_in_pin = LPC_GPIO_0_0; +const struct pio ejp_in_pin = LPC_GPIO_0_3; +const struct pio zero_cross_in_pin = LPC_GPIO_0_4; +const struct pio th_warn_in_pin = LPC_GPIO_0_5; +const struct pio charge_status_in_pin = LPC_GPIO_0_28; + +/* Outputs */ +/* Led control data pin */ +const struct pio ws2812_data_out_pin = LPC_GPIO_0_23; +/* AC output control (Mosfet / Triac) */ +const struct pio ac_ctrl = LPC_GPIO_0_7; +/* Fixme : Fan */ + + +/* Thermocouple reading */ +const struct max31855_sensor_config thermo = { + .ssp_bus_num = 0, + .chip_select = LPC_GPIO_0_15, +}; + +/* TMP101 onboard I2C temperature sensor */ +#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */ +struct tmp101_sensor_config tmp101_sensor = { + .bus_num = I2C0, + .addr = TMP101_ADDR, + .resolution = TMP_RES_ELEVEN_BITS, +}; + + +#define FAN_ON (10 * 1000) +const struct lpc_timer_pwm_config fan_pwm_conf = { + .nb_channels = 1, + .period_chan = CHAN3, + .period = FAN_ON, + .outputs = { CHAN0, }, + .match_values = { 0, }, +}; + +const struct lpc_tc_config ac_timer_conf = { + .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH, + .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, }, + .match = { 0, 10, 0, 0, }, + .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, }, +}; + + +/***************************************************************************** */ +/* SD/MMC Card */ +struct sdmmc_card micro_sd = { + .ssp_bus_num = SSP_BUS_0, + .card_type = MMC_CARDTYPE_UNKNOWN, + .block_size = 64, + .chip_select = LPC_GPIO_1_6, +}; + +uint8_t mmc_data[MMC_MAX_SECTOR_SIZE]; + + +/***************************************************************************** */ +/* RTC and time */ +#define RTC_ADDR 0xA2 +struct rtc_pcf85363a_config rtc_conf = { + .bus_num = I2C0, + .addr = RTC_ADDR, + .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 = 0x09, + .day = 0x08, + .hour = 0x18, + .min = 0x00, +}; +static struct rtc_time now; + + +/***************************************************************************** */ +/* 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); + set_pins(adc_pins); + gpio_on(); + status_led_config(&status_led_green, &status_led_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); +} + + +/***************************************************************************** */ +/* System configuration over USB */ +static uint32_t fan_speed = 0; +static int act_cmd = 0; +void config_rx(uint8_t c) +{ + /* FAN control */ + if (c == 'f') { + fan_speed = 100; + timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON); + } else if (c == 'z') { + fan_speed = 0; + timer_set_match(LPC_TIMER_32B0, CHAN0, 0); + } else if ((c >= '0') && (c <= '9')) { + fan_speed = ((c - '0') * 10); + if (fan_speed < 60) { + fan_speed = 0; + } + timer_set_match(LPC_TIMER_32B0, CHAN0, (FAN_ON - ((FAN_ON / 100) * (100 - fan_speed)))); + } +} + + + +/***************************************************************************** */ +/* System communication over UART1 */ +void cmd_rx(uint8_t c) +{ +} + + +/***************************************************************************** */ + +void set_ctrl_duty_cycle(uint8_t value) +{ + act_cmd = value; + if (act_cmd > 100) { + /* 100 is the maximum allowed value */ + act_cmd = 100; + } else if (act_cmd <= 2) { + /* Below 3% there are triggering problems which lead to 50% instead of 1% or 2% */ + act_cmd = 0; + } +} + +static volatile uint8_t ac_ctrl_state = 0; +void ac_switch_on(uint32_t flags) +{ + /* Generate a 5us (approx) pulse */ + if (ac_ctrl_state == 1) { + /* Start of pulse */ + gpio_set(ac_ctrl); + /* Change state machine state */ + ac_ctrl_state = 0; + /* And request interrupt in approx 5us to clear ac_ctrl output */ + timer_set_match(LPC_TIMER_32B1, CHAN1, (5 * 480)); + timer_restart(LPC_TIMER_32B1); + } else { + /* End of pulse */ + gpio_clear(ac_ctrl); + } +} + +static uint32_t clk_cycles_ac_zc = 0; +static volatile uint32_t zc_count = 0; /* Wraps every 1.36 year ... */ +void zero_cross(uint32_t gpio) +{ + uint32_t delay = 0; + + zc_count ++; + + 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, CHAN1, delay); + timer_restart(LPC_TIMER_32B1); + /* Set "AC triggering" stame machine state */ + ac_ctrl_state = 1; +} + + + +/***************************************************************************** */ +/* System interface */ +enum buttons { + BUTTON_NONE = 0, + BUTTON_OK, + BUTTON_UP, + BUTTON_DOWN, +}; + + +uint32_t manual_activation_request = 0; +uint8_t button_pressed = 0; +void manual_activation(uint32_t gpio) { + manual_activation_request = MANUAL_ACTIVATION_DURATION; + button_pressed = BUTTON_OK; +} +void manual_up(uint32_t gpio) { + manual_activation_request = MANUAL_ACTIVATION_DURATION; + button_pressed = BUTTON_UP; +} +void manual_down(uint32_t gpio) { + manual_activation_request = MANUAL_ACTIVATION_DURATION; + button_pressed = BUTTON_DOWN; +} +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--; + } + } +} + + +/***************************************************************************** */ +void temp_config(int uart_num) +{ + int ret = 0; + ret = tmp101_sensor_config(&tmp101_sensor); + if (ret != 0) { + uprintf(uart_num, "Temp config error: %d\n", ret); + } +} + + +/***************************************************************************** */ +/* Oled Display */ +#define DISPLAY_ADDR 0x78 +static uint8_t gddram[ 4 + GDDRAM_SIZE ]; +struct oled_display display = { + .bus_type = SSD130x_BUS_I2C, + .address = DISPLAY_ADDR, + .bus_num = I2C0, + .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); +} +#define OLED_LINE_CHAR_LENGTH (SSD130x_NB_COL / 8) +#define DISPLAY_LINE_LENGTH (OLED_LINE_CHAR_LENGTH + 1) +int display_line(uint8_t line, uint8_t col, char* text) +{ + int len = strlen((char*)text); + int i = 0; + + 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) { + return i; + } + } + } + return len; +} + + + + +/***************************************************************************** */ +#define NB_VAL 20 + +enum modes { + heat = 'C', + ejp = 'E', + delayed_heat_prod = 'P', + forced = 'F', + temp_OK = 'T', + manual = 'M', + idle_heat = 'L', + full_heat = 'F', +}; + +/***************************************************************************** */ +int main(void) +{ + uint16_t isnail_solar_values[NB_VAL]; + uint16_t isnail_home_values[NB_VAL]; + uint8_t idx = 0; + uint32_t loop = 0; + char mode = heat; /* Debug info */ + int ret = 0; + + system_init(); + status_led(red_only); + uart_on(UART0, 115200, config_rx); + uart_on(UART1, 115200, cmd_rx); + i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER); + ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000); + adc_on(NULL); + timer_on(LPC_TIMER_32B0, 0, NULL); + timer_on(LPC_TIMER_32B1, 0, ac_switch_on); + + /* Immediatly turn off Mosfet / Triac */ + config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0); + + /* Thermocouple configuration */ + max31855_sensor_config(&thermo); + uprintf(UART0, "Thermocouple config done\n"); + + /* TMP101 sensor config */ + temp_config(UART0); + + /* Activate on Rising edge (button release) */ + set_gpio_callback(manual_activation, &button_ok, EDGE_RISING); +#if 0 + /* Debug */ + config_gpio(&button_b1, 0, GPIO_DIR_OUT, 0); + config_gpio(&button_b2, 0, GPIO_DIR_OUT, 0); +#endif + set_gpio_callback(manual_up, &button_b1, EDGE_RISING); + set_gpio_callback(manual_down, &button_b2, EDGE_RISING); + + /* Zero cross and alert pin */ + set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING); + + /* Start ADC sampling */ + adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0)); + + /* Configure Input GPIO */ + config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0); + config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0); + config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1); + + /* WS2812B Leds on display board */ + ws2812_config(&ws2812_data_out_pin); + + /* FAN Config */ + timer_pwm_config(LPC_TIMER_32B0, &fan_pwm_conf); + timer_start(LPC_TIMER_32B0); + /* AC Switch Config */ + timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf); + /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */ + clk_cycles_ac_zc = get_main_clock() / (100 * 100); + + status_led(green_only); + + /* Configure and start display */ + ret = ssd130x_display_on(&display); + /* Erase screen */ + ssd130x_buffer_set(gddram, 0x00); + ret = ssd130x_display_full_screen(&display); + + /* RTC init */ + ret = rtc_pcf85363a_config(&rtc_conf); + ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest); + if (ret == 1) { + char buff[30]; + rtc_pcf85363_time_read(&rtc_conf, &now); + rtc_pcf85363_time_to_str(&now, buff, 30); + /* Debug */ + uprintf(UART0, buff); + } else if (ret == -EFAULT) { + memcpy(&now, &oldest, sizeof(struct rtc_time)); + rtc_pcf85363_time_write(&rtc_conf, &now); + } + + /* microSD card init */ + ret = sdmmc_init(µ_sd); + if (ret == 0) { + msleep(1); + ret = sdmmc_init_wait_card_ready(µ_sd); + if (ret == 0) { + ret = sdmmc_init_end(µ_sd); + } + } + uprintf(UART0, "uSD init: %d, type: %d, bs: %d\n", ret, micro_sd.card_type, micro_sd.block_size); + ret = sdmmc_read_block(µ_sd, 0, mmc_data); + uprintf(UART0, "uSD read: %s\n", mmc_data); + + /* Add a systick callback to handle time counting */ + //add_systick_callback(handle_dec_request, DEC_PERIOD); + + msleep(50); + /* Read parameters from memory */ + if (1) { + never_force = 0; + forced_heater_delay = 0; + forced_heater_time = FORCED_HEATER_DURATION; + } + + while (1) { + static uint8_t command_val = 0; + static uint8_t n_dec = 0; /* Add some PID like (derivative part) */ + static uint8_t n_inc = 0; /* Add some PID like (derivative part) */ + int moyenne_solar = 0; + int moyenne_home = 0; + uint16_t isnail_val_solar = 0; + uint16_t isnail_val_home = 0; + uint16_t acs_val_load = 0; + uint16_t user_potar = 0; + int water_centi_degrees = 0; + int tmp101_deci_degrees = 0; + + mode = heat; + tmp101_sensor_start_conversion(&tmp101_sensor); + /* Always track power consumption and production */ + adc_get_value(&isnail_val_solar, LPC_ADC(1)); + adc_get_value(&isnail_val_home, LPC_ADC(0)); + adc_get_value(&acs_val_load, LPC_ADC(2)); + adc_get_value(&user_potar, LPC_ADC(7)); + /* Convert to mA value */ + isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ + isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ + /* Store value */ + isnail_solar_values[idx] = isnail_val_solar; + isnail_home_values[idx++] = isnail_val_home; + if (idx == NB_VAL) { + idx = 0; + } + /* Compute average value when we sampled enough values */ + /* FIXME : Improve by substracting oldest value before storing new one in table and adding new one */ + if ((idx == 0) || (idx == (NB_VAL / 2))) { + int i = 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; + } else { + /* Sleep for a litle more than a period (20ms at 50Hz) */ + msleep(23); + continue; + } + + /* Feed the dog */ + if ((moyenne_solar != 0) && (moyenne_home != 0)) { + watchdog_feed(); + } + + /* Get internal temperature */ + if (1) { + int ret = 0; + msleep(40); + ret = tmp101_sensor_read(&tmp101_sensor, NULL, &tmp101_deci_degrees); + if (ret != 0) { + uprintf(UART0, "TMP101 read error : %d\n", ret); + } + } + /* If internal temperature is above 30°C, then turn on fan. Turn of when back to under 28.5°C */ + if (tmp101_deci_degrees > 300) { + fan_speed = 100; + timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON); + } else if (tmp101_deci_degrees < 285) { + fan_speed = 0; + timer_set_match(LPC_TIMER_32B0, CHAN0, 0); + } + + /* Get thermocouple value */ + if (1) { + int ret = 0; + ret = max31855_sensor_read(&thermo, NULL, &water_centi_degrees); + if (ret != 0) { + uprintf(UART0, "Water Temp read error : %d\n", ret); + } + } + + /* Need to enter Forced heating mode ? */ + if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) { + if (forced_heater_mode == 0) { + uprintf(UART0, "Entering forced mode\n"); + forced_heater_mode = 1; + } + status_led(red_on); + mode = forced; + } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) { + status_led(red_off); + forced_heater_mode = 0; + command_val = 0; + uprintf(UART0, "Forced mode exit\n"); + mode = temp_OK; + } + + /* Do not force if there is a lot of sun, it may be enough to heat again soon */ + if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) { + mode = delayed_heat_prod; + forced_heater_mode = 0; + /* Note : Do not set forced_heater_mode to 0 in order to keep decrementing the delay for force + * heating in case the house power usage does not fall below the production value. */ + } + + /* Do not force heating if this is an EJP day */ + ejp_in = gpio_read(ejp_in_pin); + if (ejp_in == DAY_IS_EJP) { + forced_heater_mode = 0; + mode = ejp; + } + + if (never_force == 1) { + forced_heater_mode = 0; + } + + /* Did the user request a forced heating ? */ + if (manual_activation_request > 1) { + forced_heater_mode = 1; + mode = manual; + if (manual_activation_request == MANUAL_ACTIVATION_DURATION) { + uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request); + /* Add a systick callback to handle time counting */ + add_systick_callback(handle_dec_request, DEC_PERIOD); + } + if (manual_activation_request < 10) { + uprintf(UART0, "Leaving manual forced mode\n"); + manual_activation_request = 0; + remove_systick_callback(handle_dec_request); + } + } + + + /* Which is the current mode ? */ + if (forced_heater_mode == 1) { + /* Forced heating mode */ + if ((forced_heater_delay == 0) && (forced_heater_time > 0)) { + command_val = FORCED_MODE_VALUE; + } + if (forced_heater_time == 0) { + forced_heater_delay = FORCED_HEATER_DELAY; + forced_heater_time = FORCED_HEATER_DURATION; + } + } else if (moyenne_solar < (moyenne_home - 75)) { + /* Low production mode */ + if (command_val > 15) { + command_val -= ((3 + n_dec) % 15); + /* Asservissement */ + n_dec++; + if (n_dec >= 3) { + n_inc = 0; + } + } else { + command_val = 0; + mode = idle_heat; + } + status_led(green_off); + } else if (moyenne_solar > (moyenne_home + 75)) { + /* High production mode */ + if (command_val < 95) { + command_val += (2 + n_inc); + /* Asservissement */ + n_inc++; + if (n_inc >= 5) { + n_inc = 5; + n_dec = 0; + } + } else { + command_val = 100; + mode = full_heat; + } + status_led(green_on); + } + + /* Set Control Output duty cycle */ + if (water_centi_degrees > (MAX_HEATER_TEMP * 100)) { + command_val = 0; + } + + set_ctrl_duty_cycle(command_val); + /* Debug Nath TMP */ + //set_ctrl_duty_cycle( user_potar / 10 ); + /* Display */ + if (1) { + char line[DISPLAY_LINE_LENGTH]; + int abs_centi = water_centi_degrees; + int abs_deci = tmp101_deci_degrees; + 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)); + if (water_centi_degrees < 0) { + abs_centi = -water_centi_degrees; + } + uprintf(UART0, "Water Temp : % 4d.%02d\n", (water_centi_degrees / 100), (abs_centi % 100)); + if (tmp101_deci_degrees < 0) { + abs_deci = -tmp101_deci_degrees; + } + uprintf(UART0, "Internal Temp : % 4d.%02d\n", (tmp101_deci_degrees / 10), (abs_deci % 10)); + uprintf(UART0, "ZC_cnt: %d\n", zc_count); + uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d, User: %d\n", + isnail_val_solar, isnail_val_home, acs_val_load, user_potar); + if (button_pressed != 0) { + uprintf(UART0, "Button : %d\n", button_pressed); + button_pressed = 0; + } + uprintf(UART0, "CMD: %d/%d, Fan: %d, ndec:%d, ninc:%d\n\n", command_val, act_cmd, fan_speed, n_dec, n_inc); + ws2812_set_pixel(0, (isnail_val_home / 2000), (isnail_val_solar / 2000), fan_speed); + ws2812_set_pixel(1, 0, 0, (user_potar >> 2)); + ws2812_send_frame(0); + /* Erase screen (internal copy) */ + ssd130x_buffer_set(gddram, 0x00); + /* Update time and time display on internal memory */ + rtc_pcf85363_time_read(&rtc_conf, &now); + snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec); + display_line(0, 0, line); + /* Display info */ + snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F); + display_line(2, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (isnail_val_solar / 1000), ((isnail_val_solar % 1000) / 10)); + display_line(3, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (isnail_val_home / 1000), ((isnail_val_home % 1000) / 10)); + display_line(4, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", act_cmd); + display_line(5, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", mode); + display_line(6, 0, line); + /* Update Oled display */ + ret = ssd130x_display_full_screen(&display); + } + } + return 0; +} + + + diff --git a/old/mosfet/Makefile b/old/mosfet/Makefile new file mode 100644 index 0000000..41ae555 --- /dev/null +++ b/old/mosfet/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/old/mosfet/README b/old/mosfet/README new file mode 100644 index 0000000..42a1d84 --- /dev/null +++ b/old/mosfet/README @@ -0,0 +1,22 @@ +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 . + * + *************************************************************************** */ + +Beta version of Scialys system. diff --git a/old/mosfet/main.c b/old/mosfet/main.c new file mode 100644 index 0000000..d69574c --- /dev/null +++ b/old/mosfet/main.c @@ -0,0 +1,852 @@ +/**************************************************************************** + * apps/scialys/beta/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 "core/system.h" +#include "core/systick.h" +#include "core/pio.h" +#include "lib/stdio.h" +#include "lib/errno.h" +#include "drivers/serial.h" +#include "drivers/gpio.h" +#include "drivers/adc.h" +#include "drivers/ssp.h" +#include "drivers/i2c.h" +#include "drivers/timers.h" + +#include "extdrv/status_led.h" +#include "extdrv/ws2812.h" +#include "extdrv/max31855_thermocouple.h" +#include "extdrv/tmp101_temp_sensor.h" +#include "extdrv/rtc_pcf85363a.h" +#include "extdrv/ssd130x_oled_driver.h" +#include "lib/font.h" +#include "lib/time.h" + +#include "extdrv/sdmmc.h" + +#define MODULE_VERSION 0x02 +#define MODULE_NAME "Scialys uC" + + +#define SELECTED_FREQ FREQ_SEL_48MHz + + +/***************************************************************************** */ +/* System configuration + * Most of the defines in here should go to configuration setting in user flash + */ + +/* Period of the decrementer handler from the systick interrupt */ +#define DEC_PERIOD 100 + +/* 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 28 +#define TARGET_FORCED_HEATER_TEMP 45 +#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 NO_FORCED_HEATING_ON_SUNNY_DAYS 1000 + +uint32_t forced_heater_mode = 0; +uint32_t forced_heater_delay = 0; +uint32_t forced_heater_time = 0; + +#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */ +#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */ + +#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */ + +uint32_t never_force = 0; + + +#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */ +int ejp_in = 0; + + + +/***************************************************************************** */ +/* 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 : Config / Debug / USB */ + { LPC_UART0_RX_PIO_0_1, LPC_IO_DIGITAL }, + { LPC_UART0_TX_PIO_0_2, LPC_IO_DIGITAL }, + /* UART 1 : UEXT */ + { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL }, + { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL }, + /* I2C : RTC, Display, UEXT */ + { 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 (Thermocouple + uSD card + UEXT) */ + { 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 }, + /* TIMER_32B0 */ + { LPC_TIMER_32B0_M0_PIO_0_18, (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL) }, /* Fan control */ + /* GPIO */ + { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */ + { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* EJP / External switch input */ + { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */ + { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Temperature driver warning (Mosfet board only) */ + { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet driver Shutdown (Mosfet board only) */ + { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Mosfet / Triac control */ + { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */ + { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */ + { LPC_GPIO_0_23, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */ + { LPC_GPIO_0_26, LPC_IO_DIGITAL }, /* User button B2 */ + { LPC_GPIO_0_27, LPC_IO_DIGITAL }, /* User button B1 */ + { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */ + { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */ + { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */ + ARRAY_LAST_PIO, +}; + +const struct pio_config adc_pins[] = { + { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* ADC0 */ + { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */ + { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */ + { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* ADC3 */ + ARRAY_LAST_PIO, +}; + +const struct pio status_led_green = LPC_GPIO_1_2; +const struct pio status_led_red = LPC_GPIO_1_3; + +/* Inputs */ +/* Buttons */ +const struct pio button_ok = LPC_GPIO_0_12; +const struct pio button_b1 = LPC_GPIO_0_27; +const struct pio button_b2 = LPC_GPIO_0_26; +/* External signals */ +const struct pio rtc_in_pin = LPC_GPIO_0_0; +const struct pio ejp_in_pin = LPC_GPIO_0_3; +const struct pio zero_cross_in_pin = LPC_GPIO_0_4; +const struct pio th_warn_in_pin = LPC_GPIO_0_5; +const struct pio charge_status_in_pin = LPC_GPIO_0_28; + +/* Outputs */ +/* Led control data pin */ +const struct pio ws2812_data_out_pin = LPC_GPIO_0_23; +/* AC output control (Mosfet / Triac) */ +const struct pio ac_ctrl = LPC_GPIO_0_7; +const struct pio mos_shutdown = LPC_GPIO_0_6; + + +/* Thermocouple reading */ +const struct max31855_sensor_config thermo = { + .ssp_bus_num = 0, + .chip_select = LPC_GPIO_0_15, +}; + +/* TMP101 onboard I2C temperature sensor */ +#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */ +struct tmp101_sensor_config tmp101_sensor = { + .bus_num = I2C0, + .addr = TMP101_ADDR, + .resolution = TMP_RES_ELEVEN_BITS, +}; + + +#define FAN_ON (10 * 1000) +const struct lpc_timer_pwm_config fan_pwm_conf = { + .nb_channels = 1, + .period_chan = CHAN3, + .period = FAN_ON, + .outputs = { CHAN0, }, + .match_values = { 0, }, +}; + +const struct lpc_tc_config ac_timer_conf = { + .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH, + .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, }, + .match = { 0, 10, 0, 0, }, + .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, }, +}; + + +/***************************************************************************** */ +/* SD/MMC Card */ +struct sdmmc_card micro_sd = { + .ssp_bus_num = SSP_BUS_0, + .card_type = MMC_CARDTYPE_UNKNOWN, + .block_size = 64, + .chip_select = LPC_GPIO_1_6, +}; + +uint8_t mmc_data[MMC_MAX_SECTOR_SIZE]; + + +/***************************************************************************** */ +/* RTC and time */ +#define RTC_ADDR 0xA2 +struct rtc_pcf85363a_config rtc_conf = { + .bus_num = I2C0, + .addr = RTC_ADDR, + .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 = 0x16, + .month = 0x10, + .day = 0x30, + .hour = 0x13, + .min = 0x00, +}; +static struct rtc_time now; + + +/***************************************************************************** */ +/* 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); + set_pins(adc_pins); + gpio_on(); + status_led_config(&status_led_green, &status_led_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); +} + + +/***************************************************************************** */ +/* Some global flags or variables */ +static uint8_t thermal_warn_flag = 0; +static uint32_t fan_speed = 0; +static int act_cmd = 0; + + +/***************************************************************************** */ +/* System configuration over USB */ +void config_rx(uint8_t c) +{ + /* FAN control */ + if (c == 'f') { + fan_speed = 100; + timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON); + } else if (c == 'z') { + fan_speed = 0; + timer_set_match(LPC_TIMER_32B0, CHAN0, 0); + } else if ((c >= '0') && (c <= '9')) { + fan_speed = ((c - '0') * 10); + if (fan_speed < 60) { + fan_speed = 0; + } + timer_set_match(LPC_TIMER_32B0, CHAN0, (FAN_ON - ((FAN_ON / 100) * (100 - fan_speed)))); + } +} + + + +/***************************************************************************** */ +/* System communication over UART1 */ +void cmd_rx(uint8_t c) +{ +} + + + +/***************************************************************************** */ +/* AC control */ + +void set_ctrl_duty_cycle(uint8_t value) +{ + if (thermal_warn_flag == 1) { + /* Thermal warning condition still present ? */ + if (gpio_read(th_warn_in_pin) != 0) { + thermal_warn_flag = 0; + } else { + act_cmd = 0; + return; + } + } + act_cmd = value; + if (act_cmd > 100) { + /* 100 is the maximum allowed value */ + act_cmd = 100; + } else if (act_cmd <= 2) { + /* Below 3% there are triggering problems which lead to 50% instead of 1% or 2% */ + act_cmd = 0; + /* Set mosfet driver shutdown */ + gpio_set(mos_shutdown); + return; + } + /* Clear the shutdown condition from the mosfet driver */ + gpio_clear(mos_shutdown); +} + +void ac_switch_on(uint32_t flags) +{ + gpio_set(ac_ctrl); + gpio_set(button_b2); +} + +static uint32_t clk_cycles_ac_zc = 0; +static volatile uint32_t zc_count = 0; /* Wraps every 1.36 year ... */ +void zero_cross(uint32_t gpio) +{ + uint32_t delay = 0; + + zc_count ++; + gpio_toggle(button_b1); + + gpio_clear(ac_ctrl); + gpio_clear(button_b2); + if (act_cmd == 0) { + return; + } + /* Set timer to trigger ac out ON at given delay */ + delay = clk_cycles_ac_zc * act_cmd; + timer_set_match(LPC_TIMER_32B1, CHAN1, delay); + timer_restart(LPC_TIMER_32B1); +} + +void th_warning(uint32_t gpio) +{ + /* Test for condition set or removed */ + if (gpio_read(th_warn_in_pin) == 0) { + /* Turn off AC output */ + gpio_clear(ac_ctrl); + /* Set mosfet driver shutdown */ + gpio_set(mos_shutdown); + act_cmd = 0; + thermal_warn_flag = 1; + /* Turn on Fan at max speed */ + timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON); + fan_speed = 100; + } else { + /* Only clear the warning flag, the rest will get back to the desired state soon enough. */ + thermal_warn_flag = 0; + } +} + + + +/***************************************************************************** */ +/* System interface */ +enum buttons { + BUTTON_NONE = 0, + BUTTON_OK, + BUTTON_UP, + BUTTON_DOWN, +}; + + +uint32_t manual_activation_request = 0; +uint8_t button_pressed = 0; +void manual_activation(uint32_t gpio) { + manual_activation_request = MANUAL_ACTIVATION_DURATION; + button_pressed = BUTTON_OK; +} +void manual_up(uint32_t gpio) { + manual_activation_request = MANUAL_ACTIVATION_DURATION; + button_pressed = BUTTON_UP; +} +void manual_down(uint32_t gpio) { + manual_activation_request = MANUAL_ACTIVATION_DURATION; + button_pressed = BUTTON_DOWN; +} +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--; + } + } +} + + +/***************************************************************************** */ +void temp_config(int uart_num) +{ + int ret = 0; + ret = tmp101_sensor_config(&tmp101_sensor); + if (ret != 0) { + uprintf(uart_num, "Temp config error: %d\n", ret); + } +} + + +/***************************************************************************** */ +/* Oled Display */ +#define DISPLAY_ADDR 0x78 +struct oled_display display = { + .address = DISPLAY_ADDR, + .bus_num = I2C0, + .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, +}; + +#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_set_tile(&display, col, line, tile_data); +} +#define OLED_LINE_CHAR_LENGTH (SSD130x_NB_COL / 8) +#define DISPLAY_LINE_LENGTH (OLED_LINE_CHAR_LENGTH + 1) +int display_line(uint8_t line, uint8_t col, char* text) +{ + int len = strlen((char*)text); + int i = 0; + + 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_set_tile(&display, col++, line, tile_data); + if (col >= (OLED_LINE_CHAR_LENGTH)) { + col = 0; + line++; + if (line >= SSD130x_NB_PAGES) { + return i; + } + } + } + return len; +} + + + + +/***************************************************************************** */ +#define NB_VAL 20 + +enum modes { + heat = 'C', + ejp = 'E', + delayed_heat_prod = 'P', + forced = 'F', + temp_OK = 'T', + manual = 'M', + idle_heat = 'L', + full_heat = 'F', +}; + +/***************************************************************************** */ +int main(void) +{ + uint16_t isnail_solar_values[NB_VAL]; + uint16_t isnail_home_values[NB_VAL]; + uint8_t idx = 0; + uint32_t loop = 0; + char mode = heat; /* Debug info */ + int ret = 0; + + system_init(); + status_led(red_only); + uart_on(UART0, 115200, config_rx); + uart_on(UART1, 115200, cmd_rx); + i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER); + ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000); + adc_on(NULL); + timer_on(LPC_TIMER_32B0, 0, NULL); + timer_on(LPC_TIMER_32B1, 0, ac_switch_on); + + /* Immediatly turn off Mosfet / Triac */ + config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0); + config_gpio(&mos_shutdown, 0, GPIO_DIR_OUT, 1); + + /* Thermocouple configuration */ + max31855_sensor_config(&thermo); + uprintf(UART0, "Thermocouple config done\n"); + + /* TMP101 sensor config */ + temp_config(UART0); + + /* Activate on Rising edge (button release) */ + set_gpio_callback(manual_activation, &button_ok, EDGE_RISING); +#if 0 + set_gpio_callback(manual_up, &button_b1, EDGE_RISING); + set_gpio_callback(manual_down, &button_b2, EDGE_RISING); +#else + config_gpio(&button_b1, 0, GPIO_DIR_OUT, 0); + config_gpio(&button_b2, 0, GPIO_DIR_OUT, 0); +#endif + + /* Zero cross and alert pin */ + set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING); + set_gpio_callback(th_warning, &th_warn_in_pin, EDGES_BOTH); + + /* Start ADC sampling */ + adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0)); + + /* Configure Input GPIO */ + config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0); + config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0); + config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1); + + /* WS2812B Leds on display board */ + ws2812_config(&ws2812_data_out_pin); + + /* FAN Config */ + timer_pwm_config(LPC_TIMER_32B0, &fan_pwm_conf); + timer_start(LPC_TIMER_32B0); + /* AC Switch Config */ + timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf); + /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */ + clk_cycles_ac_zc = get_main_clock() / (100 * 100); + + status_led(green_only); + + /* Configure and start display */ + ret = ssd130x_display_on(&display); + /* Erase screen */ + ssd130x_display_set(&display, 0x00); + ret = ssd130x_display_full_screen(&display); + + /* RTC init */ + ret = rtc_pcf85363a_config(&rtc_conf); + ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest); + if (ret == 1) { + char buff[30]; + rtc_pcf85363_time_read(&rtc_conf, &now); + rtc_pcf85363_time_to_str(&now, buff, 30); + /* Debug */ + uprintf(UART0, buff); + } else if (ret == -EFAULT) { + memcpy(&now, &oldest, sizeof(struct rtc_time)); + rtc_pcf85363_time_write(&rtc_conf, &now); + } + + /* microSD card init */ + ret = sdmmc_init(µ_sd); + if (ret == 0) { + msleep(1); + ret = sdmmc_init_wait_card_ready(µ_sd); + if (ret == 0) { + ret = sdmmc_init_end(µ_sd); + } + } + uprintf(UART0, "uSD init: %d, type: %d, bs: %d\n", ret, micro_sd.card_type, micro_sd.block_size); + ret = sdmmc_read_block(µ_sd, 0, mmc_data); + uprintf(UART0, "uSD read: %s\n", mmc_data); + + /* Add a systick callback to handle time counting */ + //add_systick_callback(handle_dec_request, DEC_PERIOD); + + msleep(50); + /* Read parameters from memory */ + if (1) { + never_force = 0; + forced_heater_delay = 0; + forced_heater_time = FORCED_HEATER_DURATION; + } + + /* Clear thermal warning flag if warning pin is high */ + if (gpio_read(th_warn_in_pin) != 0) { + thermal_warn_flag = 0; + } + + while (1) { + static uint8_t command_val = 0; + static uint8_t n_dec = 0; /* Add some PID like (derivative part) */ + static uint8_t n_inc = 0; /* Add some PID like (derivative part) */ + int moyenne_solar = 0; + int moyenne_home = 0; + uint16_t isnail_val_solar = 0; + uint16_t isnail_val_home = 0; + uint16_t acs_val_load = 0; + uint16_t user_potar = 0; + int water_centi_degrees = 0; + int tmp101_deci_degrees = 0; + + mode = heat; + tmp101_sensor_start_conversion(&tmp101_sensor); + /* Always track power consumption and production */ + adc_get_value(&isnail_val_solar, LPC_ADC(1)); + adc_get_value(&isnail_val_home, LPC_ADC(0)); + adc_get_value(&acs_val_load, LPC_ADC(2)); + adc_get_value(&user_potar, LPC_ADC(7)); + /* Convert to mA value */ + isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ + isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ + /* Store value */ + isnail_solar_values[idx] = isnail_val_solar; + isnail_home_values[idx++] = isnail_val_home; + if (idx == NB_VAL) { + idx = 0; + } + /* Compute average value when we sampled enough values */ + /* FIXME : Improve by substracting oldest value before storing new one in table and adding new one */ + if ((idx == 0) || (idx == (NB_VAL / 2))) { + int i = 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; + } else { + /* Sleep for a litle more than a period (20ms at 50Hz) */ + msleep(23); + continue; + } + + /* Feed the dog */ + if ((moyenne_solar != 0) && (moyenne_home != 0)) { + watchdog_feed(); + } + + /* Get internal temperature */ + if (1) { + int ret = 0; + msleep(40); + ret = tmp101_sensor_read(&tmp101_sensor, NULL, &tmp101_deci_degrees); + if (ret != 0) { + uprintf(UART0, "TMP101 read error : %d\n", ret); + } + } + /* If internal temperature is above 30°C, then turn on fan. Turn of when back to under 28.5°C */ + if (tmp101_deci_degrees > 300) { + fan_speed = 100; + timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON); + } else if (tmp101_deci_degrees < 285) { + fan_speed = 0; + timer_set_match(LPC_TIMER_32B0, CHAN0, 0); + } + + /* Get thermocouple value */ + if (1) { + int ret = 0; + ret = max31855_sensor_read(&thermo, NULL, &water_centi_degrees); + if (ret != 0) { + uprintf(UART0, "Water Temp read error : %d\n", ret); + } + } + + /* Need to enter Forced heating mode ? */ + if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) { + if (forced_heater_mode == 0) { + uprintf(UART0, "Entering forced mode\n"); + forced_heater_mode = 1; + } + status_led(red_on); + mode = forced; + } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) { + status_led(red_off); + forced_heater_mode = 0; + command_val = 0; + uprintf(UART0, "Forced mode exit\n"); + mode = temp_OK; + } + + /* Do not force if there is a lot of sun, it may be enough to heat again soon */ + if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) { + mode = delayed_heat_prod; + forced_heater_mode = 0; + /* Note : Do not set forced_heater_mode to 0 in order to keep decrementing the delay for force + * heating in case the house power usage does not fall below the production value. */ + } + + /* Do not force heating if this is an EJP day */ + ejp_in = gpio_read(ejp_in_pin); + if (ejp_in == DAY_IS_EJP) { + forced_heater_mode = 0; + mode = ejp; + } + + if (never_force == 1) { + forced_heater_mode = 0; + } + + /* Did the user request a forced heating ? */ + if (manual_activation_request > 1) { + forced_heater_mode = 1; + mode = manual; + if (manual_activation_request == MANUAL_ACTIVATION_DURATION) { + uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request); + /* Add a systick callback to handle time counting */ + add_systick_callback(handle_dec_request, DEC_PERIOD); + } + if (manual_activation_request < 10) { + uprintf(UART0, "Leaving manual forced mode\n"); + manual_activation_request = 0; + remove_systick_callback(handle_dec_request); + } + } + + + /* Which is the current mode ? */ + if (forced_heater_mode == 1) { + /* Forced heating mode */ + if ((forced_heater_delay == 0) && (forced_heater_time > 0)) { + command_val = FORCED_MODE_VALUE; + } + if (forced_heater_time == 0) { + forced_heater_delay = FORCED_HEATER_DELAY; + forced_heater_time = FORCED_HEATER_DURATION; + } + } else if (moyenne_solar < (moyenne_home - 75)) { + /* Low production mode */ + if (command_val > 15) { + command_val -= ((3 + n_dec) % 15); + /* Asservissement */ + n_dec++; + if (n_dec >= 3) { + n_inc = 0; + } + } else { + command_val = 0; + mode = idle_heat; + } + status_led(green_off); + } else if (moyenne_solar > (moyenne_home + 75)) { + /* High production mode */ + if (command_val < 95) { + command_val += (2 + n_inc); + /* Asservissement */ + n_inc++; + if (n_inc >= 5) { + n_inc = 5; + n_dec = 0; + } + } else { + command_val = 100; + mode = full_heat; + } + status_led(green_on); + } + + /* Set Control Output duty cycle */ + //set_ctrl_duty_cycle(command_val); + /* Debug Nath TMP */ + set_ctrl_duty_cycle( (user_potar - 5) / 10 ); + + /* Display */ + if (1) { + int abs_centi = water_centi_degrees; + int abs_deci = tmp101_deci_degrees; + + if (water_centi_degrees < 0) { + abs_centi = -water_centi_degrees; + } + if (tmp101_deci_degrees < 0) { + abs_deci = -tmp101_deci_degrees; + } + + 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 : % 4d.%02d\n", (water_centi_degrees / 100), (abs_centi % 100)); + uprintf(UART0, "Internal Temp : % 4d.%02d\n", (tmp101_deci_degrees / 10), (abs_deci % 10)); + uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d, User: %d\n", + isnail_val_solar, isnail_val_home, acs_val_load, user_potar); + if (button_pressed != 0) { + uprintf(UART0, "Button : %d\n", button_pressed); + button_pressed = 0; + } + uprintf(UART0, "ZC_cnt: %d, user: %d, ThW: %d\n", zc_count, user_potar / 10, thermal_warn_flag); + uprintf(UART0, "CMD: %d/%d, Fan: %d, ndec:%d, ninc:%d\n\n", command_val, act_cmd, fan_speed, n_dec, n_inc); + } + + if (1) { + ws2812_set_pixel(0, (isnail_val_home / 2000), (isnail_val_solar / 2000), fan_speed); + ws2812_set_pixel(1, 0, 0, (user_potar >> 2)); + ws2812_send_frame(0); + } + + if (1) { + char line[DISPLAY_LINE_LENGTH]; + /* Erase screen (internal copy) */ + ssd130x_display_set(&display, 0x00); + /* Update time and time display on internal memory */ + rtc_pcf85363_time_read(&rtc_conf, &now); + snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec); + display_line(0, 0, line); + /* Display info */ + snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F); + display_line(2, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (isnail_val_solar / 1000), ((isnail_val_solar % 1000) / 10)); + display_line(3, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (isnail_val_home / 1000), ((isnail_val_home % 1000) / 10)); + display_line(4, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", act_cmd); + display_line(5, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", mode); + display_line(6, 0, line); + /* Update Oled display */ + ret = ssd130x_display_full_screen(&display); + } + } + } + return 0; +} + + + diff --git a/old/v03/Makefile b/old/v03/Makefile new file mode 100644 index 0000000..41ae555 --- /dev/null +++ b/old/v03/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/old/v03/main.c b/old/v03/main.c new file mode 100644 index 0000000..d763173 --- /dev/null +++ b/old/v03/main.c @@ -0,0 +1,798 @@ +/**************************************************************************** + * apps/scialys/beta/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 "core/system.h" +#include "core/systick.h" +#include "core/pio.h" +#include "lib/stdio.h" +#include "lib/errno.h" +#include "drivers/serial.h" +#include "drivers/gpio.h" +#include "drivers/adc.h" +#include "drivers/ssp.h" +#include "drivers/i2c.h" +#include "drivers/timers.h" + +#include "extdrv/status_led.h" +#include "extdrv/ws2812.h" +#include "extdrv/max31855_thermocouple.h" +#include "extdrv/tmp101_temp_sensor.h" +#include "extdrv/rtc_pcf85363a.h" +#include "extdrv/ssd130x_oled_driver.h" +#include "lib/font.h" +#include "lib/time.h" + +#include "extdrv/sdmmc.h" + +#define MODULE_VERSION 0x03 +#define MODULE_NAME "Scialys uC" + + +#define SELECTED_FREQ FREQ_SEL_48MHz + + +/***************************************************************************** */ +/* System configuration + * Most of the defines in here should go to configuration setting in user flash + */ + +/* Period of the decrementer handler from the systick interrupt */ +#define DEC_PERIOD 100 + +/* 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 28 +#define TARGET_FORCED_HEATER_TEMP 45 +#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 NO_FORCED_HEATING_ON_SUNNY_DAYS 1000 + +uint32_t forced_heater_mode = 0; +uint32_t forced_heater_delay = 0; +uint32_t forced_heater_time = 0; + +#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */ +#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */ + +#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */ + +uint32_t never_force = 0; + + +#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */ +int ejp_in = 0; + + + +/***************************************************************************** */ +/* 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 : Config / Debug / USB */ + { LPC_UART0_RX_PIO_0_1, LPC_IO_DIGITAL }, + { LPC_UART0_TX_PIO_0_2, LPC_IO_DIGITAL }, + /* UART 1 : UEXT */ + { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL }, + { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL }, + /* I2C : RTC, Display, UEXT */ + { 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 (Thermocouple + uSD card + UEXT) */ + { 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 }, + /* GPIO */ + { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */ + { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* EJP / External switch input */ + { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */ + { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */ + { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Fan control */ + { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */ + { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */ + { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */ + { LPC_GPIO_0_22, LPC_IO_DIGITAL }, /* User button B4 */ + { LPC_GPIO_0_23, LPC_IO_DIGITAL }, /* User button B3 */ + { LPC_GPIO_0_24, LPC_IO_DIGITAL }, /* User button B2 */ + { LPC_GPIO_0_25, LPC_IO_DIGITAL }, /* User button B1 */ + { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */ + { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */ + { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */ + ARRAY_LAST_PIO, +}; + +const struct pio_config adc_pins[] = { + { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* ADC0 */ + { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */ + { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */ + { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* ADC3 */ + ARRAY_LAST_PIO, +}; + +const struct pio status_led_green = LPC_GPIO_0_26; +const struct pio status_led_red = LPC_GPIO_0_27; + +/* Inputs */ +/* Buttons */ +const struct pio button_ok = LPC_GPIO_0_12; +const struct pio button_b1 = LPC_GPIO_0_25; +const struct pio button_b2 = LPC_GPIO_0_24; +const struct pio button_b1 = LPC_GPIO_0_23; +const struct pio button_b2 = LPC_GPIO_0_22; +/* External signals */ +const struct pio rtc_in_pin = LPC_GPIO_0_0; +const struct pio ejp_in_pin = LPC_GPIO_0_3; +const struct pio zero_cross_in_pin = LPC_GPIO_0_4; +const struct pio charge_status_in_pin = LPC_GPIO_0_28; + +/* Outputs */ +/* Led control data pin */ +const struct pio ws2812_data_out_pin = LPC_GPIO_0_21; +/* AC output control (Mosfet) */ +const struct pio ac_ctrl = LPC_GPIO_0_6; +const struct pio fan_ctrl = LPC_GPIO_0_7; + + +/* Thermocouple reading */ +const struct max31855_sensor_config thermo = { + .ssp_bus_num = 0, + .chip_select = LPC_GPIO_0_15, +}; + +/* TMP101 onboard I2C temperature sensor */ +#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */ +struct tmp101_sensor_config tmp101_sensor = { + .bus_num = I2C0, + .addr = TMP101_ADDR, + .resolution = TMP_RES_ELEVEN_BITS, +}; + + +const struct lpc_tc_config ac_timer_conf = { + .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH, + .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, }, + .match = { 0, 10, 0, 0, }, + .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, }, +}; + + +/***************************************************************************** */ +/* SD/MMC Card */ +struct sdmmc_card micro_sd = { + .ssp_bus_num = SSP_BUS_0, + .card_type = MMC_CARDTYPE_UNKNOWN, + .block_size = 64, + .chip_select = LPC_GPIO_1_6, +}; + +uint8_t mmc_data[MMC_MAX_SECTOR_SIZE]; + + +/***************************************************************************** */ +/* RTC and time */ +#define RTC_ADDR 0xA2 +struct rtc_pcf85363a_config rtc_conf = { + .bus_num = I2C0, + .addr = RTC_ADDR, + .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 = 0x16, + .month = 0x10, + .day = 0x30, + .hour = 0x13, + .min = 0x00, +}; +static struct rtc_time now; + + +/***************************************************************************** */ +/* 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); + set_pins(adc_pins); + gpio_on(); + status_led_config(&status_led_green, &status_led_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); +} + + +/***************************************************************************** */ +/* Some global flags or variables */ +static int act_cmd = 0; +static int fan_on = 0; + + +/***************************************************************************** */ +/* System configuration over USB */ +void config_rx(uint8_t c) +{ + /* FAN control */ + if (c == 'f') { + gpio_set(fan_ctrl); + } else { + gpio_clear(fan_ctrl); + } +} + + + +/***************************************************************************** */ +/* System communication over UART1 */ +void cmd_rx(uint8_t c) +{ +} + + + +/***************************************************************************** */ +/* AC control */ + +void set_ctrl_duty_cycle(uint8_t value) +{ + act_cmd = value; + if (act_cmd > 100) { + /* 100 is the maximum allowed value */ + act_cmd = 100; + } else if (act_cmd <= 2) { + /* Below 3% there are triggering problems which lead to 50% instead of 1% or 2% */ + act_cmd = 0; + } +} + +void ac_switch_on(uint32_t flags) +{ + gpio_set(ac_ctrl); + gpio_set(button_b2); +} + +static uint32_t clk_cycles_ac_zc = 0; +static volatile uint32_t zc_count = 0; /* Wraps every 1.36 year ... */ +void zero_cross(uint32_t gpio) +{ + uint32_t delay = 0; + + zc_count ++; + gpio_toggle(button_b1); + + gpio_clear(ac_ctrl); + gpio_clear(button_b2); + if (act_cmd == 0) { + return; + } + /* Set timer to trigger ac out ON at given delay */ + delay = clk_cycles_ac_zc * act_cmd; + timer_set_match(LPC_TIMER_32B1, CHAN1, delay); + timer_restart(LPC_TIMER_32B1); +} + + + +/***************************************************************************** */ +/* System interface */ +enum buttons { + BUTTON_NONE = 0, + BUTTON_OK, + BUTTON_UP, + BUTTON_DOWN, +}; + + +uint32_t manual_activation_request = 0; +uint8_t button_pressed = 0; +void manual_activation(uint32_t gpio) { + manual_activation_request = MANUAL_ACTIVATION_DURATION; + button_pressed = BUTTON_OK; +} +void manual_up(uint32_t gpio) { + manual_activation_request = MANUAL_ACTIVATION_DURATION; + button_pressed = BUTTON_UP; +} +void manual_down(uint32_t gpio) { + manual_activation_request = MANUAL_ACTIVATION_DURATION; + button_pressed = BUTTON_DOWN; +} +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--; + } + } +} + + +/***************************************************************************** */ +void temp_config(int uart_num) +{ + int ret = 0; + ret = tmp101_sensor_config(&tmp101_sensor); + if (ret != 0) { + uprintf(uart_num, "Temp config error: %d\n", ret); + } +} + + +/***************************************************************************** */ +/* Oled Display */ +#define DISPLAY_ADDR 0x78 +struct oled_display display = { + .address = DISPLAY_ADDR, + .bus_num = I2C0, + .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, +}; + +#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_set_tile(&display, col, line, tile_data); +} +#define OLED_LINE_CHAR_LENGTH (SSD130x_NB_COL / 8) +#define DISPLAY_LINE_LENGTH (OLED_LINE_CHAR_LENGTH + 1) +int display_line(uint8_t line, uint8_t col, char* text) +{ + int len = strlen((char*)text); + int i = 0; + + 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_set_tile(&display, col++, line, tile_data); + if (col >= (OLED_LINE_CHAR_LENGTH)) { + col = 0; + line++; + if (line >= SSD130x_NB_PAGES) { + return i; + } + } + } + return len; +} + + + + +/***************************************************************************** */ +void scialys_config(void) +{ + /* Immediatly turn off Mosfet / Triac */ + config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0); + /* Start with FAN ON */ + config_gpio(&fan_ctrl, 0, GPIO_DIR_OUT, 1); + + /* Thermocouple configuration */ + max31855_sensor_config(&thermo); + uprintf(UART0, "Thermocouple config done\n"); + + /* TMP101 sensor config */ + temp_config(UART0); + + /* Activate on Rising edge (button release) */ + set_gpio_callback(manual_activation, &button_ok, EDGE_RISING); +#if 0 + set_gpio_callback(manual_up, &button_b1, EDGE_RISING); + set_gpio_callback(manual_down, &button_b2, EDGE_RISING); +#else + config_gpio(&button_b1, 0, GPIO_DIR_OUT, 0); + config_gpio(&button_b2, 0, GPIO_DIR_OUT, 0); +#endif + + /* Zero cross */ + set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING); + + /* Start ADC sampling */ + adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0)); + + /* Configure Input GPIO */ + config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0); + config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0); + config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1); + + /* WS2812B Leds on display board */ + ws2812_config(&ws2812_data_out_pin); + + /* AC Switch Config */ + timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf); + /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */ + clk_cycles_ac_zc = get_main_clock() / (100 * 100); + + status_led(green_only); + + /* Configure and start display */ + ret = ssd130x_display_on(&display); + /* Erase screen */ + ssd130x_display_set(&display, 0x00); + ret = ssd130x_display_full_screen(&display); + + /* RTC init */ + ret = rtc_pcf85363a_config(&rtc_conf); + ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest); + if (ret == 1) { + char buff[30]; + rtc_pcf85363_time_read(&rtc_conf, &now); + rtc_pcf85363_time_to_str(&now, buff, 30); + /* Debug */ + uprintf(UART0, buff); + } else if (ret == -EFAULT) { + memcpy(&now, &oldest, sizeof(struct rtc_time)); + rtc_pcf85363_time_write(&rtc_conf, &now); + } + + /* microSD card init */ + ret = sdmmc_init(µ_sd); + if (ret == 0) { + msleep(1); + ret = sdmmc_init_wait_card_ready(µ_sd); + if (ret == 0) { + ret = sdmmc_init_end(µ_sd); + } + } + uprintf(UART0, "uSD init: %d, type: %d, bs: %d\n", ret, micro_sd.card_type, micro_sd.block_size); + ret = sdmmc_read_block(µ_sd, 0, mmc_data); + uprintf(UART0, "uSD read: %s\n", mmc_data); + + /* Add a systick callback to handle time counting */ + //add_systick_callback(handle_dec_request, DEC_PERIOD); + + msleep(50); + /* Read parameters from memory */ + if (1) { + never_force = 0; + forced_heater_delay = 0; + forced_heater_time = FORCED_HEATER_DURATION; + } +} + +/***************************************************************************** */ +#define NB_VAL 20 + +enum modes { + heat = 'C', + ejp = 'E', + delayed_heat_prod = 'P', + forced = 'F', + temp_OK = 'T', + manual = 'M', + idle_heat = 'L', + full_heat = 'F', +}; + +/***************************************************************************** */ +int main(void) +{ + uint16_t isnail_solar_values[NB_VAL]; + uint16_t isnail_home_values[NB_VAL]; + uint8_t idx = 0; + uint32_t loop = 0; + char mode = heat; /* Debug info */ + int ret = 0; + + system_init(); + status_led(red_only); + uart_on(UART0, 115200, config_rx); + uart_on(UART1, 115200, cmd_rx); + i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER); + ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000); + adc_on(NULL); + timer_on(LPC_TIMER_32B1, 0, ac_switch_on); + + scialys_config(void) + + while (1) { + static uint8_t command_val = 0; + static uint8_t n_dec = 0; /* Add some PID like (derivative part) */ + static uint8_t n_inc = 0; /* Add some PID like (derivative part) */ + int moyenne_solar = 0; + int moyenne_home = 0; + uint16_t isnail_val_solar = 0; + uint16_t isnail_val_home = 0; + uint16_t acs_val_load = 0; + uint16_t user_potar = 0; + int water_centi_degrees = 0; + int tmp101_deci_degrees = 0; + + mode = heat; + tmp101_sensor_start_conversion(&tmp101_sensor); + /* Always track power consumption and production */ + adc_get_value(&isnail_val_solar, LPC_ADC(1)); + adc_get_value(&isnail_val_home, LPC_ADC(0)); + adc_get_value(&acs_val_load, LPC_ADC(2)); + adc_get_value(&user_potar, LPC_ADC(7)); + /* Convert to mA value */ + isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ + isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ + /* Store value */ + isnail_solar_values[idx] = isnail_val_solar; + isnail_home_values[idx++] = isnail_val_home; + if (idx == NB_VAL) { + idx = 0; + } + /* Compute average value when we sampled enough values */ + /* FIXME : Improve by substracting oldest value before storing new one in table and adding new one */ + if ((idx == 0) || (idx == (NB_VAL / 2))) { + int i = 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; + } else { + /* Sleep for a litle more than a period (20ms at 50Hz) */ + msleep(23); + continue; + } + + /* Feed the dog */ + if ((moyenne_solar != 0) && (moyenne_home != 0)) { + watchdog_feed(); + } + + /* Get internal temperature */ + if (1) { + int ret = 0; + msleep(40); + ret = tmp101_sensor_read(&tmp101_sensor, NULL, &tmp101_deci_degrees); + if (ret != 0) { + uprintf(UART0, "TMP101 read error : %d\n", ret); + } + } + /* If internal temperature is above 30°C, then turn on fan. Turn of when back to under 28.5°C */ + if (tmp101_deci_degrees > 300) { + fan_on = 1; + gpio_set(fan_ctrl); + } else if (tmp101_deci_degrees < 285) { + fan_on = 0; + gpio_clear(fan_ctrl); + } + + /* Get thermocouple value */ + if (1) { + int ret = 0; + ret = max31855_sensor_read(&thermo, NULL, &water_centi_degrees); + if (ret != 0) { + uprintf(UART0, "Water Temp read error : %d\n", ret); + } + } + + /* Need to enter Forced heating mode ? */ + if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) { + if (forced_heater_mode == 0) { + uprintf(UART0, "Entering forced mode\n"); + forced_heater_mode = 1; + } + status_led(red_on); + mode = forced; + } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) { + status_led(red_off); + forced_heater_mode = 0; + command_val = 0; + uprintf(UART0, "Forced mode exit\n"); + mode = temp_OK; + } + + /* Do not force if there is a lot of sun, it may be enough to heat again soon */ + if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) { + mode = delayed_heat_prod; + forced_heater_mode = 0; + /* Note : Do not set forced_heater_mode to 0 in order to keep decrementing the delay for force + * heating in case the house power usage does not fall below the production value. */ + } + + /* Do not force heating if this is an EJP day */ + ejp_in = gpio_read(ejp_in_pin); + if (ejp_in == DAY_IS_EJP) { + forced_heater_mode = 0; + mode = ejp; + } + + if (never_force == 1) { + forced_heater_mode = 0; + } + + /* Did the user request a forced heating ? */ + if (manual_activation_request > 1) { + forced_heater_mode = 1; + mode = manual; + if (manual_activation_request == MANUAL_ACTIVATION_DURATION) { + uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request); + /* Add a systick callback to handle time counting */ + add_systick_callback(handle_dec_request, DEC_PERIOD); + } + if (manual_activation_request < 10) { + uprintf(UART0, "Leaving manual forced mode\n"); + manual_activation_request = 0; + remove_systick_callback(handle_dec_request); + } + } + + + /* Which is the current mode ? */ + if (forced_heater_mode == 1) { + /* Forced heating mode */ + if ((forced_heater_delay == 0) && (forced_heater_time > 0)) { + command_val = FORCED_MODE_VALUE; + } + if (forced_heater_time == 0) { + forced_heater_delay = FORCED_HEATER_DELAY; + forced_heater_time = FORCED_HEATER_DURATION; + } + } else if (moyenne_solar < (moyenne_home - 75)) { + /* Low production mode */ + if (command_val > 15) { + command_val -= ((3 + n_dec) % 15); + /* Asservissement */ + n_dec++; + if (n_dec >= 3) { + n_inc = 0; + } + } else { + command_val = 0; + mode = idle_heat; + } + status_led(green_off); + } else if (moyenne_solar > (moyenne_home + 75)) { + /* High production mode */ + if (command_val < 95) { + command_val += (2 + n_inc); + /* Asservissement */ + n_inc++; + if (n_inc >= 5) { + n_inc = 5; + n_dec = 0; + } + } else { + command_val = 100; + mode = full_heat; + } + status_led(green_on); + } + + /* Set Control Output duty cycle */ + //set_ctrl_duty_cycle(command_val); + /* Debug Nath TMP */ + set_ctrl_duty_cycle( (user_potar - 5) / 10 ); + + /* Display */ + if (1) { + int abs_centi = water_centi_degrees; + int abs_deci = tmp101_deci_degrees; + + if (water_centi_degrees < 0) { + abs_centi = -water_centi_degrees; + } + if (tmp101_deci_degrees < 0) { + abs_deci = -tmp101_deci_degrees; + } + + 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 : % 4d.%02d\n", (water_centi_degrees / 100), (abs_centi % 100)); + uprintf(UART0, "Internal Temp : % 4d.%02d\n", (tmp101_deci_degrees / 10), (abs_deci % 10)); + uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d, User: %d\n", + isnail_val_solar, isnail_val_home, acs_val_load, user_potar); + if (button_pressed != 0) { + uprintf(UART0, "Button : %d\n", button_pressed); + button_pressed = 0; + } + uprintf(UART0, "ZC_cnt: %d, user: %d\n", zc_count, user_potar / 10); + uprintf(UART0, "CMD: %d/%d, Fan: %d, ndec:%d, ninc:%d\n\n", command_val, act_cmd, fan_on, n_dec, n_inc); + } + + if (1) { + ws2812_set_pixel(0, (isnail_val_home / 2000), (isnail_val_solar / 2000), (fan_on * 20)); + ws2812_set_pixel(1, 0, 0, (user_potar >> 2)); + ws2812_send_frame(0); + } + + if (1) { + char line[DISPLAY_LINE_LENGTH]; + /* Erase screen (internal copy) */ + ssd130x_display_set(&display, 0x00); + /* Update time and time display on internal memory */ + rtc_pcf85363_time_read(&rtc_conf, &now); + snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec); + display_line(0, 0, line); + /* Display info */ + snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F); + display_line(2, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (isnail_val_solar / 1000), ((isnail_val_solar % 1000) / 10)); + display_line(3, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (isnail_val_home / 1000), ((isnail_val_home % 1000) / 10)); + display_line(4, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", act_cmd); + display_line(5, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", mode); + display_line(6, 0, line); + /* Update Oled display */ + ret = ssd130x_display_full_screen(&display); + } + } + } + return 0; +} + + + diff --git a/old/v05/Makefile b/old/v05/Makefile new file mode 100644 index 0000000..41ae555 --- /dev/null +++ b/old/v05/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/old/v05/config.c b/old/v05/config.c new file mode 100644 index 0000000..6121a70 --- /dev/null +++ b/old/v05/config.c @@ -0,0 +1,239 @@ +/**************************************************************************** + * apps/scialys/v05/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 */ + { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* Display potentiometer */ + /* GPIO */ + { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */ + { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* External switch input */ + { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */ + { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */ + { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Fan control */ + { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */ + { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */ + { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */ + { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */ + { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */ + { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */ + 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 rtc_in_pin = LPC_GPIO_0_0; +const struct pio ext_disable_in_pin = LPC_GPIO_0_3; +const struct pio charge_status_in_pin = LPC_GPIO_0_28; + +/* Outputs */ +/* AC output control (Mosfet) */ +const struct pio ac_ctrl = LPC_GPIO_0_6; +const struct pio fan_ctrl = LPC_GPIO_0_7; +/* Chip selects */ +const struct pio uSD_cs = LPC_GPIO_1_6; /* uSD card */ +const struct pio th_cs = LPC_GPIO_0_15; /* Thermocouple */ +const struct pio uext_cs = LPC_GPIO_1_1; /* 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 = { + .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH, + .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, }, + .match = { 0, 10, 0, 0, }, + .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, }, +}; + +extern void config_rx(uint8_t c); +extern void comm_rx(uint8_t c); +extern void ac_switch_on(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) | ADC_MCH(7), LPC_ADC_SEQ(0)); + timer_on(LPC_TIMER_32B1, 0, ac_switch_on); + timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf); +} + + +/***************************************************************************** */ +extern void zero_cross(uint32_t gpio); +void board_io_config(void) +{ + /* Immediatly turn off Mosfet / Triac */ + config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0); + /* 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); + config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0); + config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1); + + /* Zero-crossing detection */ + set_gpio_callback(zero_cross, &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_15, +}; + +/* 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/old/v05/config.h b/old/v05/config.h new file mode 100644 index 0000000..fec19b6 --- /dev/null +++ b/old/v05/config.h @@ -0,0 +1,97 @@ +/**************************************************************************** + * apps/blyes/inter_lights/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 rtc_in_pin; +extern const struct pio ext_disable_in_pin; +extern const struct pio charge_status_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/old/v05/interface.c b/old/v05/interface.c new file mode 100644 index 0000000..338c427 --- /dev/null +++ b/old/v05/interface.c @@ -0,0 +1,299 @@ +/**************************************************************************** + * apps/scialys/v05/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_0_25; +#define GPIO_BUTTON_UP 25 +const struct pio button_right = LPC_GPIO_0_24; +#define GPIO_BUTTON_RIGHT 24 +const struct pio button_left = LPC_GPIO_0_23; +#define GPIO_BUTTON_LEFT 23 +const struct pio button_down = LPC_GPIO_0_22; +#define GPIO_BUTTON_DOWN 22 +/* 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_21; + + +static uint8_t interface_board_present = 0; + +/***************************************************************************** */ +/* TMP101 onboard I2C temperature sensor */ +#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */ +struct tmp101_sensor_config tmp101_sensor = { + .bus_num = I2C0, + .addr = TMP101_ADDR, + .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_EXT_VCC, + .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, 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) +{ + int ret = 0; + + if (interface_board_present == 0) { + return -1; + } + + ret = tmp101_sensor_read(&tmp101_sensor, NULL, deci_degrees); + if (ret != 0) { + uprintf(uart, "TMP101 read error : %d\n", ret); + return -1; + } + return 0; +} + + +/***************************************************************************** */ +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 */ + ret = tmp101_sensor_config(&tmp101_sensor); + if (ret != 0) { + uprintf(uart, "Temp config error: %d\n", ret); + interface_board_present = 0; + return -1; + } + ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor); + if (ret != 0) { + uprintf(uart, "Temp config error: %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 */ + 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; +} + + +enum interface_modes { + MODE_RUN = 0, + MODE_CONFIG, + MODE_DISPLAY, +}; + +extern int water_centi_degrees; +extern uint32_t moyenne_solar; +extern uint32_t moyenne_home; +extern volatile uint8_t command_val; + +static int current_mode = MODE_RUN; +volatile int manual_activation_request = 0; +void interface_update(char heat_mode) +{ + int abs_centi = water_centi_degrees; + uint16_t user_potar = 0; + + if (water_centi_degrees < 0) { + abs_centi = -water_centi_degrees; + } + + /* Get potentiometer value */ + adc_get_value(&user_potar, LPC_ADC(7)); + + if (current_mode == MODE_RUN) { + if (button_pressed & BUTTON_OK) { + manual_activation_request = -1; + } + if (interface_board_present != 0) { + char line[DISPLAY_LINE_LENGTH]; + + /* 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, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec); + display_line(0, 0, line); + + /* Display info */ + snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F); + display_line(2, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (moyenne_solar / 1000), ((moyenne_solar % 1000) / 10)); + display_line(3, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (moyenne_home / 1000), ((moyenne_home % 1000) / 10)); + display_line(4, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", command_val); + display_line(5, 0, line); + snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", heat_mode); + display_line(6, 0, line); + /* Update Oled display */ + ssd130x_display_full_screen(&display); + } + + /* Update RGB leds */ + ws2812_set_pixel(0, (moyenne_home / 2000), (moyenne_solar / 2000), 0); + ws2812_set_pixel(1, 0, 0, (user_potar >> 2)); + ws2812_send_frame(0); + } + + if (button_pressed != 0) { + uprintf(UART0, "Button : 0x%02x\n", button_pressed); + button_pressed = 0; + } + +} diff --git a/old/v05/interface.h b/old/v05/interface.h new file mode 100644 index 0000000..84e2839 --- /dev/null +++ b/old/v05/interface.h @@ -0,0 +1,66 @@ +/**************************************************************************** + * 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 DISPLAY_LINE_LENGTH (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 sensor */ +int temp_read(uint32_t uart, int* deci_degrees); + +/* Interface content update */ +void interface_update(char mode); + +#endif /* INTERFACE_H */ + diff --git a/old/v05/main.c b/old/v05/main.c new file mode 100644 index 0000000..b26421a --- /dev/null +++ b/old/v05/main.c @@ -0,0 +1,453 @@ +/**************************************************************************** + * apps/scialys/v05/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 500 + +/* Max water temperature. Internal protection which cannot be overriden by configuration */ +#define MAX_WATER_TEMP 90 +/* Internal system max temperature : turn off heating when reached */ +#define MAX_INTERNAL_TEMP 80 /* FIXME */ + +/* 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 30 +#define TARGET_FORCED_HEATER_TEMP 50 +#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 tmp101_deci_degrees = 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 = 0; +static int fan_on = 0; + +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 = 97; + } 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; + + gpio_clear(ac_ctrl); + zc_cnt++; + 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, CHAN1, delay); + timer_restart(LPC_TIMER_32B1); +} + + +/* 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 * 100)) { + cmd = 0; + 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); +} + + +/***************************************************************************** */ +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); + + /* 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); +} + + +/***************************************************************************** */ +int main(void) +{ + uint32_t loop = 0; + char mode = heat; /* Debug info */ + + system_init(); + board_io_config(); + modules_config(); + + external_config(UART0); + scialys_config(); + + msleep(1500); + status_led(green_only); + + while (1) { + uint16_t snapval_load = 0; + + /* Default mode : try to heat the water tank */ + mode = heat; + + /* Always track power consumption and production */ + track_isnail_values(); + + /* Get load power usage */ + adc_get_value(&snapval_load, LPC_ADC(2)); + + /* Feed the dog */ + if ((moyenne_solar != 0) && (moyenne_home != 0)) { + watchdog_feed(); + } + + /* Get internal temperature */ + if (temp_read(UART0, &tmp101_deci_degrees) != 0) { + tmp101_deci_degrees = 1500; + } + + /* Internal temperature protection */ + if (tmp101_deci_degrees > (MAX_INTERNAL_TEMP * 10)) { + error_shutdown = 1; + } else if (tmp101_deci_degrees < ((MAX_INTERNAL_TEMP * 10) / 2)) { + error_shutdown = 0; + } + /* If internal temperature is above 32°C, then turn on fan. Turn off when back to under 28°C */ + if (tmp101_deci_degrees > 320) { + fan_on = 1; + gpio_set(fan_ctrl); + } else if (tmp101_deci_degrees < 280) { + 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 * 100)) { + 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 * 100)) && (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; + } + + 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; + } + } + + /* Wait approx 10ms between each loop */ + msleep(10); + + /* 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 : %d\n", tmp101_deci_degrees); + uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d\n", + snapval_solar, snapval_home, snapval_load); + uprintf(UART0, "Zc: %d\n", zc_cnt); + uprintf(UART0, "CMD: %d/%d, Fan: %d\n\n", command_val, act_cmd, fan_on); + } + + } + return 0; +} + + + diff --git a/old/v05/time.c b/old/v05/time.c new file mode 100644 index 0000000..e1fd5ac --- /dev/null +++ b/old/v05/time.c @@ -0,0 +1,92 @@ +/**************************************************************************** + * 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; +} + + +/***************************************************************************** */ +/* 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/old/v05/time.h b/old/v05/time.h new file mode 100644 index 0000000..a1533f8 --- /dev/null +++ b/old/v05/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/old/v05/uSD.c b/old/v05/uSD.c new file mode 100644 index 0000000..e56379d --- /dev/null +++ b/old/v05/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/old/v05/uSD.h b/old/v05/uSD.h new file mode 100644 index 0000000..f0c10c0 --- /dev/null +++ b/old/v05/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 */ + diff --git a/old/v07/Makefile b/old/v07/Makefile new file mode 100644 index 0000000..41ae555 --- /dev/null +++ b/old/v07/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/old/v07/config.c b/old/v07/config.c new file mode 100644 index 0000000..e433794 --- /dev/null +++ b/old/v07/config.c @@ -0,0 +1,242 @@ +/**************************************************************************** + * 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 }, /* Oled_Reset */ + { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* Oled Control / I2C SA0 */ + { 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, }, +}; + +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); + +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); +} + + +/***************************************************************************** */ +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/old/v07/config.h b/old/v07/config.h new file mode 100644 index 0000000..7a2a307 --- /dev/null +++ b/old/v07/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/old/v07/interface.c b/old/v07/interface.c new file mode 100644 index 0000000..aad997b --- /dev/null +++ b/old/v07/interface.c @@ -0,0 +1,615 @@ +/**************************************************************************** + * 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 + + +const struct pio oled_i2c_sa0 = LPC_GPIO_0_21; /* Oled Control / I2C SA0 */ + + +static uint8_t interface_board_present = 0; + +/***************************************************************************** */ +/* TMP101 onboard I2C temperature sensor */ +#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */ +struct tmp101_sensor_config tmp101_sensor = { + .bus_num = I2C0, + .addr = TMP101_ADDR, + .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) +{ + int ret = 0; + + if (interface_board_present == 0) { + return -1; + } + + ret = tmp101_sensor_read(&tmp101_sensor, NULL, deci_degrees); + if (ret != 0) { + uprintf(uart, "TMP101 read error : %d\n", ret); + return -1; + } + return 0; +} + + +/***************************************************************************** */ +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 */ + ret = tmp101_sensor_config(&tmp101_sensor); + if (ret != 0) { + uprintf(uart, "Temp config error: %d\n", ret); + interface_board_present = 0; + return -1; + } + ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor); + if (ret != 0) { + uprintf(uart, "Temp config error: %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); + + + /* Configure and start display */ + config_gpio(&oled_i2c_sa0, 0, GPIO_DIR_OUT, 0); /* Low I2C address bit set to 0 */ + 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); + } + } else { + /* Config mode. Mode entered by button action, which implies that interface board is present. */ + config_interface_handle(); + } +} + + diff --git a/old/v07/interface.h b/old/v07/interface.h new file mode 100644 index 0000000..0fd191d --- /dev/null +++ b/old/v07/interface.h @@ -0,0 +1,66 @@ +/**************************************************************************** + * 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 sensor */ +int temp_read(uint32_t uart, int* deci_degrees); + +/* Interface content update */ +void interface_update(char mode); + +#endif /* INTERFACE_H */ + diff --git a/old/v07/main.c b/old/v07/main.c new file mode 100644 index 0000000..689160c --- /dev/null +++ b/old/v07/main.c @@ -0,0 +1,492 @@ +/**************************************************************************** + * 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 350 +#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 tmp101_deci_degrees = 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); +} + + +/***************************************************************************** */ +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); + /* And configure the fake zero-cross generation timer to 100Hz */ + timer_set_match(LPC_TIMER_32B0, CHAN0, (clk_cycles_ac_zc * 100)); + + /* 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 */ + + system_init(); + board_io_config(); + modules_config(); + + external_config(UART0); + scialys_config(); + + msleep(1500); + status_led(green_only); + + while (1) { + uint16_t snapval_load = 0; + + /* Default mode : try to heat the water tank */ + mode = heat; + + /* Always track power consumption and production */ + track_isnail_values(); + + /* Get load power usage */ + adc_get_value(&snapval_load, LPC_ADC(2)); + + /* Feed the dog */ + if ((moyenne_solar != 0) && (moyenne_home != 0)) { + watchdog_feed(); + } + + /* Get internal temperature */ + if (temp_read(UART0, &tmp101_deci_degrees) != 0) { + /* Set internal temperature to 60°C if there's a temp error, as this will trigger the + * internal fan */ + tmp101_deci_degrees = DEFAULT_INTERNAL_TEMP; + } + + /* Internal temperature protection */ + if (tmp101_deci_degrees > (MAX_INTERNAL_TEMP)) { + error_shutdown = 1; + } else if (tmp101_deci_degrees < ((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 ((tmp101_deci_degrees > FAN_ON_INTERNAL_TEMP) || (force_fan != 0)) { + fan_on = 1; + gpio_set(fan_ctrl); + } else if (tmp101_deci_degrees < 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; + } + + 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 : %d\n", tmp101_deci_degrees); + uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d\n", + snapval_solar, snapval_home, snapval_load); + 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/old/v07/time.c b/old/v07/time.c new file mode 100644 index 0000000..257c1fe --- /dev/null +++ b/old/v07/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/old/v07/time.h b/old/v07/time.h new file mode 100644 index 0000000..a1533f8 --- /dev/null +++ b/old/v07/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/old/v07/uSD.c b/old/v07/uSD.c new file mode 100644 index 0000000..30022cf --- /dev/null +++ b/old/v07/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/old/v07/uSD.h b/old/v07/uSD.h new file mode 100644 index 0000000..f0c10c0 --- /dev/null +++ b/old/v07/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 */ + diff --git a/old/v08/Makefile b/old/v08/Makefile new file mode 100644 index 0000000..41ae555 --- /dev/null +++ b/old/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/old/v08/config.c b/old/v08/config.c new file mode 100644 index 0000000..7e321e2 --- /dev/null +++ b/old/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/old/v08/config.h b/old/v08/config.h new file mode 100644 index 0000000..7a2a307 --- /dev/null +++ b/old/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/old/v08/interface.c b/old/v08/interface.c new file mode 100644 index 0000000..c1960a6 --- /dev/null +++ b/old/v08/interface.c @@ -0,0 +1,659 @@ +/**************************************************************************** + * 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 solar_prod_value; +extern uint32_t home_conso_value; +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", (solar_prod_value / 1000), ((solar_prod_value % 1000) / 10)); + display_line(3, 0, line); + snprintf(line, DISP_LLEN, "Conso:% 2d,%03dA", (home_conso_value / 1000), ((home_conso_value % 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); + snprintf(line, DISP_LLEN, "v0.8 - 0.3.3"); + display_line(7, 0, line); + /* Update Oled display */ + ssd130x_display_full_screen(&display); + + /* Update RGB leds */ + /* FIXME : use for error signal */ + ws2812_set_pixel(0, (home_conso_value / 2000), (solar_prod_value / 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/old/v08/interface.h b/old/v08/interface.h new file mode 100644 index 0000000..59518a8 --- /dev/null +++ b/old/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/old/v08/main.c b/old/v08/main.c new file mode 100644 index 0000000..c05ccee --- /dev/null +++ b/old/v08/main.c @@ -0,0 +1,532 @@ +/**************************************************************************** + * 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 0x08 +#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 100 + +/* 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 manual_forced_heater = 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', /* Normal heating */ + ext_disable = 'E', /* Forced heating disabled by external input */ + forced = 'F', /* Forced heating */ + manual = 'M', + delayed_heat_prod = 'P', /* Pause */ + overprod = 'S', /* Over production, try to start other loads */ + temp_OK = 'T', /* Max temperature reached */ +}; + +/* 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 solar_prod_value = 0; +uint32_t home_conso_value = 0; + +static void track_isnail_values(void) +{ + uint16_t snapval_solar = 0; + uint16_t snapval_home = 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 */ + solar_prod_value = snapval_solar << 6; + home_conso_value = snapval_home << 6; +} + +/***************************************************************************** */ +/* 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 3 +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; + goto cmd_update_end; + } + if (manual_forced_heater > 0) { + cmd = FORCED_MODE_VALUE; + goto cmd_update_end; + } + /* 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; + } + } + if (solar_prod_value < (home_conso_value - 64)) { + /* Low production mode */ + if (cmd > 0) { + cmd--; + } + } else if (solar_prod_value > (home_conso_value + 1280)) { + if (cmd < 100) { + cmd++; + } + } else if (solar_prod_value > (home_conso_value + 256)) { + /* 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: + if ((cmd > 0) || (force_fan == 1)) { + fan_on = 1; + gpio_set(fan_ctrl); + } else { + fan_on = 0; + gpio_clear(fan_ctrl); + } + 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 ((solar_prod_value != 0) && (home_conso_value != 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; + } + + /* 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 ((solar_prod_value > sunny_days_prod_value_config) && (forced_heater_mode == 1)) { + 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; + manual_forced_heater = 1; + } + if (manual_activation_request < 10) { + uprintf(UART0, "Leaving manual forced mode\n"); + manual_activation_request = 0; + } + } + + /* Command at 100% and still more production than energy used ? */ + if ((command_val == 100) && (solar_prod_value > home_conso_value)) { + mode = overprod; + } + + /* Display */ + interface_update(mode); + + /* Debug */ + if (1) { + uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++, + (solar_prod_value / 1000), (solar_prod_value % 1000), + (home_conso_value / 1000), (home_conso_value % 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", solar_prod_value, home_conso_value); + 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/old/v08/time.c b/old/v08/time.c new file mode 100644 index 0000000..257c1fe --- /dev/null +++ b/old/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/old/v08/time.h b/old/v08/time.h new file mode 100644 index 0000000..a1533f8 --- /dev/null +++ b/old/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/old/v08/uSD.c b/old/v08/uSD.c new file mode 100644 index 0000000..30022cf --- /dev/null +++ b/old/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/old/v08/uSD.h b/old/v08/uSD.h new file mode 100644 index 0000000..f0c10c0 --- /dev/null +++ b/old/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 */ + diff --git a/old/v09/Makefile b/old/v09/Makefile new file mode 100644 index 0000000..41ae555 --- /dev/null +++ b/old/v09/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/old/v09/config.c b/old/v09/config.c new file mode 100644 index 0000000..b20dcc2 --- /dev/null +++ b/old/v09/config.c @@ -0,0 +1,258 @@ +/**************************************************************************** + * 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_0, LPC_IO_DIGITAL }, /* Over Voltage protection input */ + { 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_0_28, LPC_IO_DIGITAL }, /* Over Temperature protection input */ + { 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; +const struct pio overvoltage_pin = LPC_GPIO_0_0; +const struct pio overtemperature_pin = LPC_GPIO_0_28; + +/* 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); /* This one is used to generate the second zero-crossing */ + timer_counter_config(LPC_TIMER_32B0, &ac_timer_conf_zc); + timer_on(LPC_TIMER_32B1, 0, ac_switch_on); /* This one is used to generate the power PMW */ + timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf_delay); + timer_on(LPC_TIMER_16B0, 0, power_track); /* This one is used to call the load power tracking ADC sampler */ + timer_counter_config(LPC_TIMER_16B0, &ac_timer_conf_power_track); +} + + +/***************************************************************************** */ +extern void zero_cross_detect(uint32_t gpio); +extern void overvoltage_protect(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); + config_gpio(&overtemperature_pin, 0, GPIO_DIR_IN, 0); + + /* Over-voltage protection */ + set_gpio_callback(overvoltage_protect, &overvoltage_pin, EDGE_RISING); + + /* 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/old/v09/config.h b/old/v09/config.h new file mode 100644 index 0000000..757c65f --- /dev/null +++ b/old/v09/config.h @@ -0,0 +1,98 @@ +/**************************************************************************** + * 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; +extern const struct pio overvoltage_pin; +extern const struct pio overtemperature_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/old/v09/interface.c b/old/v09/interface.c new file mode 100644 index 0000000..80f50f9 --- /dev/null +++ b/old/v09/interface.c @@ -0,0 +1,585 @@ +/**************************************************************************** + * 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, + FUNCTIONS, + LIMITS, + 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", + [FUNCTIONS] = "Reglages", + [LIMITS] = "Limites", + [SAVE_CONFIG] = "Save config", +}; +static uint8_t current_menu = MAIN_MENU; +static uint8_t current_entry = MANUAL_MODE; + +enum func_menu_list { + FUNC_CMD_TYPE, + FUNC_TENSION, + INST_POWER, + LOAD_POWER, + FORCED_MODE_VAL, + FUNC_NB_MENU, /* This one must be the last */ +}; +static const char* func_modes_titles[] = { + [FUNC_CMD_TYPE] = "Type Cmd", /* ToR / PWM */ + [FUNC_TENSION] = "Tension", /* AC / DC */ + [INST_POWER] = "P Prod", /* Production power (kW) */ + [LOAD_POWER] = "P Cahrge", /* Load power (kW) */ + [FORCED_MODE_VAL] = "Force Val", /* Default is 75% */ +}; +static uint8_t func_cur_menu = FUNC_CMD_TYPE; +static uint8_t func_cur_entry = FUNC_CMD_TYPE; + +enum limits_menu_list { + TEMP_FORCED_START, + TEMP_FORCED_TARGET, + SUNNY_DAY_VALUE, + LIM_NB_MENU, /* This one must be the last */ +}; +static const char* limits_modes_titles[] = { + [TEMP_FORCED_START] = "Water Min", + [TEMP_FORCED_TARGET] = "Water Target", + [SUNNY_DAY_VALUE] = "Sun Prod", +}; +static uint8_t limits_cur_menu = TEMP_FORCED_START; +static uint8_t limits_cur_entry = TEMP_FORCED_START; + + + +/* 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_RIGHT) { + current_menu = current_entry; + sub_menu_level = 1; + } + if (button & (BUTTON_LEFT | BUTTON_OK)) { + 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 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 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 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 solar_prod_value; +extern uint32_t home_conso_value; +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; + } + 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", (solar_prod_value / 1000), ((solar_prod_value % 1000) / 10)); + display_line(3, 0, line); + snprintf(line, DISP_LLEN, "Conso:% 2d,%03dA", (home_conso_value / 1000), ((home_conso_value % 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); + snprintf(line, DISP_LLEN, "v0.9 - 0.3.5"); + display_line(7, 0, line); + /* Update Oled display */ + ssd130x_display_full_screen(&display); + + /* Update RGB leds */ + /* FIXME : use for error signal */ + ws2812_set_pixel(0, (home_conso_value / 2000), (solar_prod_value / 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/old/v09/interface.h b/old/v09/interface.h new file mode 100644 index 0000000..59518a8 --- /dev/null +++ b/old/v09/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/old/v09/main.c b/old/v09/main.c new file mode 100644 index 0000000..a50bd52 --- /dev/null +++ b/old/v09/main.c @@ -0,0 +1,605 @@ +/**************************************************************************** + * apps/scialys/testv09/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 0x09 +#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 50 +#define ADC_UPD_PERIOD 17 + +/* Max water temperature. Internal protection which cannot be overriden by configuration */ +#define MAX_WATER_TEMP (90 * 100) /* Hundredth of degrees C : 90°C */ +/* Internal system max temperature : turn off heating when reached */ +#define MAX_INTERNAL_TEMP (80 * 10) /* Tenth of degrees C : 80°C */ + +/* Number of main loops during which we keep the mosfets ON in order to keep them safe */ +#define OVERVOLTAGE_PROTECTION_CYCLES 100 + +/* 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 (30 * 100) +#define TARGET_FORCED_HEATER_TEMP (50 * 100) +#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 manual_forced_heater = 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; +int temp_shutdown = 0; +int overvoltage = 0; + +enum modes { + heat = 'C', /* Normal heating */ + ext_disable = 'E', /* Forced heating disabled by external input */ + forced = 'F', /* Forced heating */ + manual = 'M', + delayed_heat_prod = 'P', /* Pause */ + overprod = 'S', /* Over production, try to start other loads */ + temp_OK = 'T', /* Max temperature reached */ +}; + +/* 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 solar_prod_value = 0; +uint32_t home_conso_value = 0; +#define NB_VAL 4 /* MUST be a power of 2 */ +#define NB_SHIFT 2 + +static void track_isnail_values(uint32_t flags) +{ + static uint8_t idx = 0; + static uint16_t solar[NB_VAL]; + static uint16_t home[NB_VAL]; + uint16_t snapval_solar = 0; + uint16_t snapval_home = 0; + int cnt, sum_solar = 0, sum_home = 0; + + /* Get new values */ + adc_get_value(&snapval_solar, LPC_ADC(1)); + adc_get_value(&snapval_home, LPC_ADC(0)); + + /* Store values */ + solar[idx] = snapval_solar; + home[idx] = snapval_home; + idx++; + if (idx >= NB_VAL) { + idx = 0; + } + + /* Compute average value */ + for (cnt = 0; cnt < NB_VAL; cnt++) { + sum_solar += solar[cnt]; + sum_home += home[cnt]; + } + + /* 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 */ + solar_prod_value = sum_solar << (6 - NB_SHIFT); + home_conso_value = sum_home << (6 - NB_SHIFT); +} + +/***************************************************************************** */ +/* AC control */ + +volatile uint8_t command_val = 0; +volatile int act_cmd = 0; /* 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 <= 3) { + act_cmd = 0; + } +} + +void ac_switch_on(uint32_t flags) +{ + gpio_clear(ac_ctrl); +} + +static uint32_t clk_cycles_ac_zc = 0; +extern uint32_t power_delay[101]; +volatile uint32_t zc_cnt = 0; + +void zero_cross(uint32_t unused) +{ + uint32_t delay = 0; + + zc_cnt++; + gpio_set(ac_ctrl); + if (act_cmd == 0) { + return; + } + if (act_cmd == 100) { + gpio_clear(ac_ctrl); + return; + } + /* Set timer to trigger ac out ON at given delay */ + if (0) { + /* Time based delay */ + delay = clk_cycles_ac_zc * (100 - act_cmd); + } else { + /* Power based delay */ + delay = power_delay[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 unused) +{ + timer_restart(LPC_TIMER_32B0); + zero_cross(0); +} + +/* Handle the power command */ +#define CMD_UP_DELAY_RESET_VALUE 3 +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; + force_fan = 1; + goto cmd_update_end; + } + + /* Water max temperature protection */ + if (water_centi_degrees > MAX_WATER_TEMP) { + 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; + goto cmd_update_end; + } + if (manual_forced_heater > 0) { + cmd = FORCED_MODE_VALUE; + goto cmd_update_end; + } + /* 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; + } + } + if ((home_conso_value > 1280) && (solar_prod_value < (home_conso_value - 1280))) { + /* Very low production mode */ + if (cmd >= 10) { + cmd -= 10; + } else { + cmd = 0; + } + } else if ((home_conso_value > 64) && (solar_prod_value < (home_conso_value - 64))) { + /* Low production mode */ + if (cmd > 0) { + cmd--; + } + } else if (solar_prod_value > (home_conso_value + 1280)) { + if (cmd <= 98) { + cmd += 2; + } else { + cmd = 100; + } + } else if (solar_prod_value > (home_conso_value + 256)) { + /* 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: + if ((cmd > 0) || (force_fan == 1)) { + fan_on = 1; + gpio_set(fan_ctrl); + } else { + fan_on = 0; + gpio_clear(fan_ctrl); + } + command_val = cmd; + /* Set Control Output duty cycle */ + set_ctrl_duty_cycle(cmd); +} + +/* Over-voltage protection */ +void overvoltage_protect(uint32_t gpio) +{ + gpio_clear(ac_ctrl); + timer_stop(LPC_TIMER_32B1); + set_ctrl_duty_cycle(100); + force_cmd = 100; + overvoltage = OVERVOLTAGE_PROTECTION_CYCLES; + gpio_set(fan_ctrl); +} + + +/* Update current working mode depending on environnement */ +volatile char mode = heat; /* Debug info */ +volatile char* msg = NULL; +void mode_update(uint32_t unused) +{ + /* Default mode : try to heat the water tank */ + mode = heat; + + /* Need to enter Forced heating mode ? */ + if (water_centi_degrees < FORCE_HEATER_TEMP) { + if (forced_heater_mode == 0) { + msg = "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) && (forced_heater_mode == 1)) { + status_led(red_off); + msg = "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 ((solar_prod_value > sunny_days_prod_value_config) && (forced_heater_mode == 1)) { + 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; + msg = "Forced mode disabled by external input\n"; + } + /* Get Over-Voltage or Over-Temperature information */ + temp_shutdown = gpio_read(overtemperature_pin); + if (overvoltage > 0) { + uint32_t ov_tmp = gpio_read(overvoltage_pin); + if (ov_tmp == 0) { + overvoltage--; + if (overvoltage == 0) { + /* Get back to normal */ + force_cmd = 0; + } + } + } + + 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) { + msg = "Entering manual forced mode\n"; + manual_activation_request = MANUAL_ACTIVATION_DURATION; + manual_forced_heater = 1; + } + if (manual_activation_request < 10) { + msg = "Leaving manual forced mode\n"; + manual_activation_request = 0; + } + } + + /* Command at 100% and still more production than energy used ? */ + if ((command_val == 100) && (solar_prod_value > home_conso_value)) { + mode = overprod; + } +} + +/***************************************************************************** */ +/* 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); + uprintf(UART0, "clk_cycles_ac_zc: %d\n", clk_cycles_ac_zc); + + /* 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 track ismail values (power consumption and production) */ + add_systick_callback(track_isnail_values, ADC_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; + int ret = 0; + + system_init(); + board_io_config(); + modules_config(); + + external_config(UART0); + scialys_config(); + + msleep(500); + status_led(green_only); + + while (1) { + /* Feed the dog */ + if ((solar_prod_value != 0) && (home_conso_value != 0)) { + watchdog_feed(); + } + + /* Update internal temperatures */ + if (1) { + /* Get internal temperatures */ + ret = temp_read(UART0, &deci_degrees_disp, &deci_degrees_power); + if (ret != CONV_OK) { + if ((ret & CONV_POWER_OK) == 0) { + force_fan = 1; + } + if ((ret & CONV_DISPLAY_OK) == 0) { + force_fan = 1; + } + } + /* Internal temperature protection */ + if ((deci_degrees_power > MAX_INTERNAL_TEMP) || (deci_degrees_disp > MAX_INTERNAL_TEMP)) { + error_shutdown = 1; + uprintf(UART0, "Error shutdown !\n"); + } else if ((deci_degrees_power < ((MAX_INTERNAL_TEMP) / 2)) && + (deci_degrees_disp < ((MAX_INTERNAL_TEMP) / 2))) { + error_shutdown = 0; + } + } + + /* Update water tank temperature */ + if (1) { + int ret = 0, old_water_temp = 0; + old_water_temp = water_centi_degrees; + /* Get thermocouple value */ + 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; + } + } + + /* Update current mode */ + mode_update(0); + + /* Display */ + if (1) { + interface_update(mode); + } + + /* Debug */ + if (1) { + uprintf(UART0, "#%c:%d:%d:%d:%d:%d:%d:", + mode, loop++, solar_prod_value, home_conso_value, + water_centi_degrees, deci_degrees_power, deci_degrees_disp); + uprintf(UART0, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d$\n", + load_power_lowest, load_power_highest, + zc_cnt, command_val, act_cmd, fan_on, force_fan, + error_shutdown, temp_shutdown, overvoltage, + external_disable, forced_heater_mode, manual_activation_request); + if (msg != NULL) { + uprintf(UART0, (char*)msg); + msg = NULL; + } + } + } + return 0; +} + + + diff --git a/old/v09/power_delay.c b/old/v09/power_delay.c new file mode 100644 index 0000000..fdbe3b0 --- /dev/null +++ b/old/v09/power_delay.c @@ -0,0 +1,105 @@ +#include "lib/stdint.h" + +uint32_t power_delay[101] = { + [0] = 0, + [1] = 30608, + [2] = 43360, + [3] = 53195, + [4] = 61530, + [5] = 68911, + [6] = 75620, + [7] = 81822, + [8] = 87626, + [9] = 93107, + [10] = 98319, + [11] = 103305, + [12] = 108095, + [13] = 112716, + [14] = 117188, + [15] = 121527, + [16] = 125750, + [17] = 129867, + [18] = 133888, + [19] = 137823, + [20] = 141680, + [21] = 145465, + [22] = 149184, + [23] = 152843, + [24] = 156447, + [25] = 159999, + [26] = 163505, + [27] = 166967, + [28] = 170389, + [29] = 173774, + [30] = 177124, + [31] = 180443, + [32] = 183732, + [33] = 186995, + [34] = 190232, + [35] = 193446, + [36] = 196639, + [37] = 199813, + [38] = 202969, + [39] = 206109, + [40] = 209234, + [41] = 212347, + [42] = 215448, + [43] = 218539, + [44] = 221621, + [45] = 224695, + [46] = 227763, + [47] = 230827, + [48] = 233886, + [49] = 236944, + [50] = 240000, + [51] = 243055, + [52] = 246113, + [53] = 249172, + [54] = 252236, + [55] = 255304, + [56] = 258378, + [57] = 261460, + [58] = 264551, + [59] = 267652, + [60] = 270765, + [61] = 273890, + [62] = 277030, + [63] = 280186, + [64] = 283360, + [65] = 286553, + [66] = 289767, + [67] = 293004, + [68] = 296267, + [69] = 299556, + [70] = 302875, + [71] = 306225, + [72] = 309610, + [73] = 313032, + [74] = 316494, + [75] = 320000, + [76] = 323552, + [77] = 327156, + [78] = 330815, + [79] = 334534, + [80] = 338319, + [81] = 342176, + [82] = 346111, + [83] = 350132, + [84] = 354249, + [85] = 358472, + [86] = 362811, + [87] = 367283, + [88] = 371904, + [89] = 376694, + [90] = 381680, + [91] = 386892, + [92] = 392373, + [93] = 398177, + [94] = 404379, + [95] = 411088, + [96] = 418469, + [97] = 426804, + [98] = 436639, + [99] = 449391, + [100] = 480000, +}; diff --git a/old/v09/sin.pl b/old/v09/sin.pl new file mode 100755 index 0000000..063ef81 --- /dev/null +++ b/old/v09/sin.pl @@ -0,0 +1,34 @@ +#! /usr/bin/perl -w + +use strict; +use warnings; + +use Math::Trig; + +my ($clocks, $outfile) = @ARGV; +my ($fh, $angle, $v, $w, $idx); + +if (defined $outfile) { + open($fh, '>', $outfile); + print $fh "#include \"lib/stdint.h\"\n\n"; + print $fh "uint32_t power_delay[101] = {\n"; +} + +for (my $i = 0; $i <= 100; $i++) { + $angle = $i * 0.02; + $v = (acos(1 - $angle) / pi * 100); + $w = int($v * $clocks); + if (not defined $outfile) { + print "$v - [$i] = $w,\n"; + } else { + print $fh "\t[$i] = $w,\n"; + } +} + +if (defined $outfile) { + print $fh "};\n"; + close($fh); +} +print "Done\n"; + + diff --git a/old/v09/time.c b/old/v09/time.c new file mode 100644 index 0000000..257c1fe --- /dev/null +++ b/old/v09/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/old/v09/time.h b/old/v09/time.h new file mode 100644 index 0000000..a1533f8 --- /dev/null +++ b/old/v09/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/old/v09/uSD.c b/old/v09/uSD.c new file mode 100644 index 0000000..30022cf --- /dev/null +++ b/old/v09/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/old/v09/uSD.h b/old/v09/uSD.h new file mode 100644 index 0000000..f0c10c0 --- /dev/null +++ b/old/v09/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 */ + diff --git a/v03/Makefile b/v03/Makefile deleted file mode 100644 index 41ae555..0000000 --- a/v03/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# 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/v03/main.c b/v03/main.c deleted file mode 100644 index d763173..0000000 --- a/v03/main.c +++ /dev/null @@ -1,798 +0,0 @@ -/**************************************************************************** - * apps/scialys/beta/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 "core/system.h" -#include "core/systick.h" -#include "core/pio.h" -#include "lib/stdio.h" -#include "lib/errno.h" -#include "drivers/serial.h" -#include "drivers/gpio.h" -#include "drivers/adc.h" -#include "drivers/ssp.h" -#include "drivers/i2c.h" -#include "drivers/timers.h" - -#include "extdrv/status_led.h" -#include "extdrv/ws2812.h" -#include "extdrv/max31855_thermocouple.h" -#include "extdrv/tmp101_temp_sensor.h" -#include "extdrv/rtc_pcf85363a.h" -#include "extdrv/ssd130x_oled_driver.h" -#include "lib/font.h" -#include "lib/time.h" - -#include "extdrv/sdmmc.h" - -#define MODULE_VERSION 0x03 -#define MODULE_NAME "Scialys uC" - - -#define SELECTED_FREQ FREQ_SEL_48MHz - - -/***************************************************************************** */ -/* System configuration - * Most of the defines in here should go to configuration setting in user flash - */ - -/* Period of the decrementer handler from the systick interrupt */ -#define DEC_PERIOD 100 - -/* 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 28 -#define TARGET_FORCED_HEATER_TEMP 45 -#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 NO_FORCED_HEATING_ON_SUNNY_DAYS 1000 - -uint32_t forced_heater_mode = 0; -uint32_t forced_heater_delay = 0; -uint32_t forced_heater_time = 0; - -#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */ -#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */ - -#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */ - -uint32_t never_force = 0; - - -#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */ -int ejp_in = 0; - - - -/***************************************************************************** */ -/* 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 : Config / Debug / USB */ - { LPC_UART0_RX_PIO_0_1, LPC_IO_DIGITAL }, - { LPC_UART0_TX_PIO_0_2, LPC_IO_DIGITAL }, - /* UART 1 : UEXT */ - { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL }, - { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL }, - /* I2C : RTC, Display, UEXT */ - { 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 (Thermocouple + uSD card + UEXT) */ - { 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 }, - /* GPIO */ - { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */ - { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* EJP / External switch input */ - { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */ - { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */ - { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Fan control */ - { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */ - { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */ - { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */ - { LPC_GPIO_0_22, LPC_IO_DIGITAL }, /* User button B4 */ - { LPC_GPIO_0_23, LPC_IO_DIGITAL }, /* User button B3 */ - { LPC_GPIO_0_24, LPC_IO_DIGITAL }, /* User button B2 */ - { LPC_GPIO_0_25, LPC_IO_DIGITAL }, /* User button B1 */ - { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */ - { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */ - { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */ - ARRAY_LAST_PIO, -}; - -const struct pio_config adc_pins[] = { - { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* ADC0 */ - { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */ - { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */ - { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* ADC3 */ - ARRAY_LAST_PIO, -}; - -const struct pio status_led_green = LPC_GPIO_0_26; -const struct pio status_led_red = LPC_GPIO_0_27; - -/* Inputs */ -/* Buttons */ -const struct pio button_ok = LPC_GPIO_0_12; -const struct pio button_b1 = LPC_GPIO_0_25; -const struct pio button_b2 = LPC_GPIO_0_24; -const struct pio button_b1 = LPC_GPIO_0_23; -const struct pio button_b2 = LPC_GPIO_0_22; -/* External signals */ -const struct pio rtc_in_pin = LPC_GPIO_0_0; -const struct pio ejp_in_pin = LPC_GPIO_0_3; -const struct pio zero_cross_in_pin = LPC_GPIO_0_4; -const struct pio charge_status_in_pin = LPC_GPIO_0_28; - -/* Outputs */ -/* Led control data pin */ -const struct pio ws2812_data_out_pin = LPC_GPIO_0_21; -/* AC output control (Mosfet) */ -const struct pio ac_ctrl = LPC_GPIO_0_6; -const struct pio fan_ctrl = LPC_GPIO_0_7; - - -/* Thermocouple reading */ -const struct max31855_sensor_config thermo = { - .ssp_bus_num = 0, - .chip_select = LPC_GPIO_0_15, -}; - -/* TMP101 onboard I2C temperature sensor */ -#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */ -struct tmp101_sensor_config tmp101_sensor = { - .bus_num = I2C0, - .addr = TMP101_ADDR, - .resolution = TMP_RES_ELEVEN_BITS, -}; - - -const struct lpc_tc_config ac_timer_conf = { - .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH, - .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, }, - .match = { 0, 10, 0, 0, }, - .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, }, -}; - - -/***************************************************************************** */ -/* SD/MMC Card */ -struct sdmmc_card micro_sd = { - .ssp_bus_num = SSP_BUS_0, - .card_type = MMC_CARDTYPE_UNKNOWN, - .block_size = 64, - .chip_select = LPC_GPIO_1_6, -}; - -uint8_t mmc_data[MMC_MAX_SECTOR_SIZE]; - - -/***************************************************************************** */ -/* RTC and time */ -#define RTC_ADDR 0xA2 -struct rtc_pcf85363a_config rtc_conf = { - .bus_num = I2C0, - .addr = RTC_ADDR, - .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 = 0x16, - .month = 0x10, - .day = 0x30, - .hour = 0x13, - .min = 0x00, -}; -static struct rtc_time now; - - -/***************************************************************************** */ -/* 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); - set_pins(adc_pins); - gpio_on(); - status_led_config(&status_led_green, &status_led_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); -} - - -/***************************************************************************** */ -/* Some global flags or variables */ -static int act_cmd = 0; -static int fan_on = 0; - - -/***************************************************************************** */ -/* System configuration over USB */ -void config_rx(uint8_t c) -{ - /* FAN control */ - if (c == 'f') { - gpio_set(fan_ctrl); - } else { - gpio_clear(fan_ctrl); - } -} - - - -/***************************************************************************** */ -/* System communication over UART1 */ -void cmd_rx(uint8_t c) -{ -} - - - -/***************************************************************************** */ -/* AC control */ - -void set_ctrl_duty_cycle(uint8_t value) -{ - act_cmd = value; - if (act_cmd > 100) { - /* 100 is the maximum allowed value */ - act_cmd = 100; - } else if (act_cmd <= 2) { - /* Below 3% there are triggering problems which lead to 50% instead of 1% or 2% */ - act_cmd = 0; - } -} - -void ac_switch_on(uint32_t flags) -{ - gpio_set(ac_ctrl); - gpio_set(button_b2); -} - -static uint32_t clk_cycles_ac_zc = 0; -static volatile uint32_t zc_count = 0; /* Wraps every 1.36 year ... */ -void zero_cross(uint32_t gpio) -{ - uint32_t delay = 0; - - zc_count ++; - gpio_toggle(button_b1); - - gpio_clear(ac_ctrl); - gpio_clear(button_b2); - if (act_cmd == 0) { - return; - } - /* Set timer to trigger ac out ON at given delay */ - delay = clk_cycles_ac_zc * act_cmd; - timer_set_match(LPC_TIMER_32B1, CHAN1, delay); - timer_restart(LPC_TIMER_32B1); -} - - - -/***************************************************************************** */ -/* System interface */ -enum buttons { - BUTTON_NONE = 0, - BUTTON_OK, - BUTTON_UP, - BUTTON_DOWN, -}; - - -uint32_t manual_activation_request = 0; -uint8_t button_pressed = 0; -void manual_activation(uint32_t gpio) { - manual_activation_request = MANUAL_ACTIVATION_DURATION; - button_pressed = BUTTON_OK; -} -void manual_up(uint32_t gpio) { - manual_activation_request = MANUAL_ACTIVATION_DURATION; - button_pressed = BUTTON_UP; -} -void manual_down(uint32_t gpio) { - manual_activation_request = MANUAL_ACTIVATION_DURATION; - button_pressed = BUTTON_DOWN; -} -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--; - } - } -} - - -/***************************************************************************** */ -void temp_config(int uart_num) -{ - int ret = 0; - ret = tmp101_sensor_config(&tmp101_sensor); - if (ret != 0) { - uprintf(uart_num, "Temp config error: %d\n", ret); - } -} - - -/***************************************************************************** */ -/* Oled Display */ -#define DISPLAY_ADDR 0x78 -struct oled_display display = { - .address = DISPLAY_ADDR, - .bus_num = I2C0, - .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, -}; - -#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_set_tile(&display, col, line, tile_data); -} -#define OLED_LINE_CHAR_LENGTH (SSD130x_NB_COL / 8) -#define DISPLAY_LINE_LENGTH (OLED_LINE_CHAR_LENGTH + 1) -int display_line(uint8_t line, uint8_t col, char* text) -{ - int len = strlen((char*)text); - int i = 0; - - 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_set_tile(&display, col++, line, tile_data); - if (col >= (OLED_LINE_CHAR_LENGTH)) { - col = 0; - line++; - if (line >= SSD130x_NB_PAGES) { - return i; - } - } - } - return len; -} - - - - -/***************************************************************************** */ -void scialys_config(void) -{ - /* Immediatly turn off Mosfet / Triac */ - config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0); - /* Start with FAN ON */ - config_gpio(&fan_ctrl, 0, GPIO_DIR_OUT, 1); - - /* Thermocouple configuration */ - max31855_sensor_config(&thermo); - uprintf(UART0, "Thermocouple config done\n"); - - /* TMP101 sensor config */ - temp_config(UART0); - - /* Activate on Rising edge (button release) */ - set_gpio_callback(manual_activation, &button_ok, EDGE_RISING); -#if 0 - set_gpio_callback(manual_up, &button_b1, EDGE_RISING); - set_gpio_callback(manual_down, &button_b2, EDGE_RISING); -#else - config_gpio(&button_b1, 0, GPIO_DIR_OUT, 0); - config_gpio(&button_b2, 0, GPIO_DIR_OUT, 0); -#endif - - /* Zero cross */ - set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING); - - /* Start ADC sampling */ - adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0)); - - /* Configure Input GPIO */ - config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0); - config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0); - config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1); - - /* WS2812B Leds on display board */ - ws2812_config(&ws2812_data_out_pin); - - /* AC Switch Config */ - timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf); - /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */ - clk_cycles_ac_zc = get_main_clock() / (100 * 100); - - status_led(green_only); - - /* Configure and start display */ - ret = ssd130x_display_on(&display); - /* Erase screen */ - ssd130x_display_set(&display, 0x00); - ret = ssd130x_display_full_screen(&display); - - /* RTC init */ - ret = rtc_pcf85363a_config(&rtc_conf); - ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest); - if (ret == 1) { - char buff[30]; - rtc_pcf85363_time_read(&rtc_conf, &now); - rtc_pcf85363_time_to_str(&now, buff, 30); - /* Debug */ - uprintf(UART0, buff); - } else if (ret == -EFAULT) { - memcpy(&now, &oldest, sizeof(struct rtc_time)); - rtc_pcf85363_time_write(&rtc_conf, &now); - } - - /* microSD card init */ - ret = sdmmc_init(µ_sd); - if (ret == 0) { - msleep(1); - ret = sdmmc_init_wait_card_ready(µ_sd); - if (ret == 0) { - ret = sdmmc_init_end(µ_sd); - } - } - uprintf(UART0, "uSD init: %d, type: %d, bs: %d\n", ret, micro_sd.card_type, micro_sd.block_size); - ret = sdmmc_read_block(µ_sd, 0, mmc_data); - uprintf(UART0, "uSD read: %s\n", mmc_data); - - /* Add a systick callback to handle time counting */ - //add_systick_callback(handle_dec_request, DEC_PERIOD); - - msleep(50); - /* Read parameters from memory */ - if (1) { - never_force = 0; - forced_heater_delay = 0; - forced_heater_time = FORCED_HEATER_DURATION; - } -} - -/***************************************************************************** */ -#define NB_VAL 20 - -enum modes { - heat = 'C', - ejp = 'E', - delayed_heat_prod = 'P', - forced = 'F', - temp_OK = 'T', - manual = 'M', - idle_heat = 'L', - full_heat = 'F', -}; - -/***************************************************************************** */ -int main(void) -{ - uint16_t isnail_solar_values[NB_VAL]; - uint16_t isnail_home_values[NB_VAL]; - uint8_t idx = 0; - uint32_t loop = 0; - char mode = heat; /* Debug info */ - int ret = 0; - - system_init(); - status_led(red_only); - uart_on(UART0, 115200, config_rx); - uart_on(UART1, 115200, cmd_rx); - i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER); - ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000); - adc_on(NULL); - timer_on(LPC_TIMER_32B1, 0, ac_switch_on); - - scialys_config(void) - - while (1) { - static uint8_t command_val = 0; - static uint8_t n_dec = 0; /* Add some PID like (derivative part) */ - static uint8_t n_inc = 0; /* Add some PID like (derivative part) */ - int moyenne_solar = 0; - int moyenne_home = 0; - uint16_t isnail_val_solar = 0; - uint16_t isnail_val_home = 0; - uint16_t acs_val_load = 0; - uint16_t user_potar = 0; - int water_centi_degrees = 0; - int tmp101_deci_degrees = 0; - - mode = heat; - tmp101_sensor_start_conversion(&tmp101_sensor); - /* Always track power consumption and production */ - adc_get_value(&isnail_val_solar, LPC_ADC(1)); - adc_get_value(&isnail_val_home, LPC_ADC(0)); - adc_get_value(&acs_val_load, LPC_ADC(2)); - adc_get_value(&user_potar, LPC_ADC(7)); - /* Convert to mA value */ - isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ - isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */ - /* Store value */ - isnail_solar_values[idx] = isnail_val_solar; - isnail_home_values[idx++] = isnail_val_home; - if (idx == NB_VAL) { - idx = 0; - } - /* Compute average value when we sampled enough values */ - /* FIXME : Improve by substracting oldest value before storing new one in table and adding new one */ - if ((idx == 0) || (idx == (NB_VAL / 2))) { - int i = 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; - } else { - /* Sleep for a litle more than a period (20ms at 50Hz) */ - msleep(23); - continue; - } - - /* Feed the dog */ - if ((moyenne_solar != 0) && (moyenne_home != 0)) { - watchdog_feed(); - } - - /* Get internal temperature */ - if (1) { - int ret = 0; - msleep(40); - ret = tmp101_sensor_read(&tmp101_sensor, NULL, &tmp101_deci_degrees); - if (ret != 0) { - uprintf(UART0, "TMP101 read error : %d\n", ret); - } - } - /* If internal temperature is above 30°C, then turn on fan. Turn of when back to under 28.5°C */ - if (tmp101_deci_degrees > 300) { - fan_on = 1; - gpio_set(fan_ctrl); - } else if (tmp101_deci_degrees < 285) { - fan_on = 0; - gpio_clear(fan_ctrl); - } - - /* Get thermocouple value */ - if (1) { - int ret = 0; - ret = max31855_sensor_read(&thermo, NULL, &water_centi_degrees); - if (ret != 0) { - uprintf(UART0, "Water Temp read error : %d\n", ret); - } - } - - /* Need to enter Forced heating mode ? */ - if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) { - if (forced_heater_mode == 0) { - uprintf(UART0, "Entering forced mode\n"); - forced_heater_mode = 1; - } - status_led(red_on); - mode = forced; - } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) { - status_led(red_off); - forced_heater_mode = 0; - command_val = 0; - uprintf(UART0, "Forced mode exit\n"); - mode = temp_OK; - } - - /* Do not force if there is a lot of sun, it may be enough to heat again soon */ - if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) { - mode = delayed_heat_prod; - forced_heater_mode = 0; - /* Note : Do not set forced_heater_mode to 0 in order to keep decrementing the delay for force - * heating in case the house power usage does not fall below the production value. */ - } - - /* Do not force heating if this is an EJP day */ - ejp_in = gpio_read(ejp_in_pin); - if (ejp_in == DAY_IS_EJP) { - forced_heater_mode = 0; - mode = ejp; - } - - if (never_force == 1) { - forced_heater_mode = 0; - } - - /* Did the user request a forced heating ? */ - if (manual_activation_request > 1) { - forced_heater_mode = 1; - mode = manual; - if (manual_activation_request == MANUAL_ACTIVATION_DURATION) { - uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request); - /* Add a systick callback to handle time counting */ - add_systick_callback(handle_dec_request, DEC_PERIOD); - } - if (manual_activation_request < 10) { - uprintf(UART0, "Leaving manual forced mode\n"); - manual_activation_request = 0; - remove_systick_callback(handle_dec_request); - } - } - - - /* Which is the current mode ? */ - if (forced_heater_mode == 1) { - /* Forced heating mode */ - if ((forced_heater_delay == 0) && (forced_heater_time > 0)) { - command_val = FORCED_MODE_VALUE; - } - if (forced_heater_time == 0) { - forced_heater_delay = FORCED_HEATER_DELAY; - forced_heater_time = FORCED_HEATER_DURATION; - } - } else if (moyenne_solar < (moyenne_home - 75)) { - /* Low production mode */ - if (command_val > 15) { - command_val -= ((3 + n_dec) % 15); - /* Asservissement */ - n_dec++; - if (n_dec >= 3) { - n_inc = 0; - } - } else { - command_val = 0; - mode = idle_heat; - } - status_led(green_off); - } else if (moyenne_solar > (moyenne_home + 75)) { - /* High production mode */ - if (command_val < 95) { - command_val += (2 + n_inc); - /* Asservissement */ - n_inc++; - if (n_inc >= 5) { - n_inc = 5; - n_dec = 0; - } - } else { - command_val = 100; - mode = full_heat; - } - status_led(green_on); - } - - /* Set Control Output duty cycle */ - //set_ctrl_duty_cycle(command_val); - /* Debug Nath TMP */ - set_ctrl_duty_cycle( (user_potar - 5) / 10 ); - - /* Display */ - if (1) { - int abs_centi = water_centi_degrees; - int abs_deci = tmp101_deci_degrees; - - if (water_centi_degrees < 0) { - abs_centi = -water_centi_degrees; - } - if (tmp101_deci_degrees < 0) { - abs_deci = -tmp101_deci_degrees; - } - - 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 : % 4d.%02d\n", (water_centi_degrees / 100), (abs_centi % 100)); - uprintf(UART0, "Internal Temp : % 4d.%02d\n", (tmp101_deci_degrees / 10), (abs_deci % 10)); - uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d, User: %d\n", - isnail_val_solar, isnail_val_home, acs_val_load, user_potar); - if (button_pressed != 0) { - uprintf(UART0, "Button : %d\n", button_pressed); - button_pressed = 0; - } - uprintf(UART0, "ZC_cnt: %d, user: %d\n", zc_count, user_potar / 10); - uprintf(UART0, "CMD: %d/%d, Fan: %d, ndec:%d, ninc:%d\n\n", command_val, act_cmd, fan_on, n_dec, n_inc); - } - - if (1) { - ws2812_set_pixel(0, (isnail_val_home / 2000), (isnail_val_solar / 2000), (fan_on * 20)); - ws2812_set_pixel(1, 0, 0, (user_potar >> 2)); - ws2812_send_frame(0); - } - - if (1) { - char line[DISPLAY_LINE_LENGTH]; - /* Erase screen (internal copy) */ - ssd130x_display_set(&display, 0x00); - /* Update time and time display on internal memory */ - rtc_pcf85363_time_read(&rtc_conf, &now); - snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec); - display_line(0, 0, line); - /* Display info */ - snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F); - display_line(2, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (isnail_val_solar / 1000), ((isnail_val_solar % 1000) / 10)); - display_line(3, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (isnail_val_home / 1000), ((isnail_val_home % 1000) / 10)); - display_line(4, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", act_cmd); - display_line(5, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", mode); - display_line(6, 0, line); - /* Update Oled display */ - ret = ssd130x_display_full_screen(&display); - } - } - } - return 0; -} - - - diff --git a/v05/Makefile b/v05/Makefile deleted file mode 100644 index 41ae555..0000000 --- a/v05/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# 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/v05/config.c b/v05/config.c deleted file mode 100644 index 6121a70..0000000 --- a/v05/config.c +++ /dev/null @@ -1,239 +0,0 @@ -/**************************************************************************** - * apps/scialys/v05/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 */ - { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* Display potentiometer */ - /* GPIO */ - { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */ - { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* External switch input */ - { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */ - { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */ - { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Fan control */ - { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */ - { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */ - { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */ - { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */ - { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */ - { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */ - 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 rtc_in_pin = LPC_GPIO_0_0; -const struct pio ext_disable_in_pin = LPC_GPIO_0_3; -const struct pio charge_status_in_pin = LPC_GPIO_0_28; - -/* Outputs */ -/* AC output control (Mosfet) */ -const struct pio ac_ctrl = LPC_GPIO_0_6; -const struct pio fan_ctrl = LPC_GPIO_0_7; -/* Chip selects */ -const struct pio uSD_cs = LPC_GPIO_1_6; /* uSD card */ -const struct pio th_cs = LPC_GPIO_0_15; /* Thermocouple */ -const struct pio uext_cs = LPC_GPIO_1_1; /* 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 = { - .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH, - .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, }, - .match = { 0, 10, 0, 0, }, - .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, }, -}; - -extern void config_rx(uint8_t c); -extern void comm_rx(uint8_t c); -extern void ac_switch_on(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) | ADC_MCH(7), LPC_ADC_SEQ(0)); - timer_on(LPC_TIMER_32B1, 0, ac_switch_on); - timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf); -} - - -/***************************************************************************** */ -extern void zero_cross(uint32_t gpio); -void board_io_config(void) -{ - /* Immediatly turn off Mosfet / Triac */ - config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0); - /* 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); - config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0); - config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1); - - /* Zero-crossing detection */ - set_gpio_callback(zero_cross, &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_15, -}; - -/* 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/v05/config.h b/v05/config.h deleted file mode 100644 index fec19b6..0000000 --- a/v05/config.h +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************** - * apps/blyes/inter_lights/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 rtc_in_pin; -extern const struct pio ext_disable_in_pin; -extern const struct pio charge_status_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/v05/interface.c b/v05/interface.c deleted file mode 100644 index 338c427..0000000 --- a/v05/interface.c +++ /dev/null @@ -1,299 +0,0 @@ -/**************************************************************************** - * apps/scialys/v05/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_0_25; -#define GPIO_BUTTON_UP 25 -const struct pio button_right = LPC_GPIO_0_24; -#define GPIO_BUTTON_RIGHT 24 -const struct pio button_left = LPC_GPIO_0_23; -#define GPIO_BUTTON_LEFT 23 -const struct pio button_down = LPC_GPIO_0_22; -#define GPIO_BUTTON_DOWN 22 -/* 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_21; - - -static uint8_t interface_board_present = 0; - -/***************************************************************************** */ -/* TMP101 onboard I2C temperature sensor */ -#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */ -struct tmp101_sensor_config tmp101_sensor = { - .bus_num = I2C0, - .addr = TMP101_ADDR, - .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_EXT_VCC, - .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, 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) -{ - int ret = 0; - - if (interface_board_present == 0) { - return -1; - } - - ret = tmp101_sensor_read(&tmp101_sensor, NULL, deci_degrees); - if (ret != 0) { - uprintf(uart, "TMP101 read error : %d\n", ret); - return -1; - } - return 0; -} - - -/***************************************************************************** */ -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 */ - ret = tmp101_sensor_config(&tmp101_sensor); - if (ret != 0) { - uprintf(uart, "Temp config error: %d\n", ret); - interface_board_present = 0; - return -1; - } - ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor); - if (ret != 0) { - uprintf(uart, "Temp config error: %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 */ - 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; -} - - -enum interface_modes { - MODE_RUN = 0, - MODE_CONFIG, - MODE_DISPLAY, -}; - -extern int water_centi_degrees; -extern uint32_t moyenne_solar; -extern uint32_t moyenne_home; -extern volatile uint8_t command_val; - -static int current_mode = MODE_RUN; -volatile int manual_activation_request = 0; -void interface_update(char heat_mode) -{ - int abs_centi = water_centi_degrees; - uint16_t user_potar = 0; - - if (water_centi_degrees < 0) { - abs_centi = -water_centi_degrees; - } - - /* Get potentiometer value */ - adc_get_value(&user_potar, LPC_ADC(7)); - - if (current_mode == MODE_RUN) { - if (button_pressed & BUTTON_OK) { - manual_activation_request = -1; - } - if (interface_board_present != 0) { - char line[DISPLAY_LINE_LENGTH]; - - /* 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, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec); - display_line(0, 0, line); - - /* Display info */ - snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F); - display_line(2, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (moyenne_solar / 1000), ((moyenne_solar % 1000) / 10)); - display_line(3, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (moyenne_home / 1000), ((moyenne_home % 1000) / 10)); - display_line(4, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", command_val); - display_line(5, 0, line); - snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", heat_mode); - display_line(6, 0, line); - /* Update Oled display */ - ssd130x_display_full_screen(&display); - } - - /* Update RGB leds */ - ws2812_set_pixel(0, (moyenne_home / 2000), (moyenne_solar / 2000), 0); - ws2812_set_pixel(1, 0, 0, (user_potar >> 2)); - ws2812_send_frame(0); - } - - if (button_pressed != 0) { - uprintf(UART0, "Button : 0x%02x\n", button_pressed); - button_pressed = 0; - } - -} diff --git a/v05/interface.h b/v05/interface.h deleted file mode 100644 index 84e2839..0000000 --- a/v05/interface.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** - * 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 DISPLAY_LINE_LENGTH (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 sensor */ -int temp_read(uint32_t uart, int* deci_degrees); - -/* Interface content update */ -void interface_update(char mode); - -#endif /* INTERFACE_H */ - diff --git a/v05/main.c b/v05/main.c deleted file mode 100644 index b26421a..0000000 --- a/v05/main.c +++ /dev/null @@ -1,453 +0,0 @@ -/**************************************************************************** - * apps/scialys/v05/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 500 - -/* Max water temperature. Internal protection which cannot be overriden by configuration */ -#define MAX_WATER_TEMP 90 -/* Internal system max temperature : turn off heating when reached */ -#define MAX_INTERNAL_TEMP 80 /* FIXME */ - -/* 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 30 -#define TARGET_FORCED_HEATER_TEMP 50 -#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 tmp101_deci_degrees = 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 = 0; -static int fan_on = 0; - -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 = 97; - } 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; - - gpio_clear(ac_ctrl); - zc_cnt++; - 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, CHAN1, delay); - timer_restart(LPC_TIMER_32B1); -} - - -/* 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 * 100)) { - cmd = 0; - 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); -} - - -/***************************************************************************** */ -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); - - /* 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); -} - - -/***************************************************************************** */ -int main(void) -{ - uint32_t loop = 0; - char mode = heat; /* Debug info */ - - system_init(); - board_io_config(); - modules_config(); - - external_config(UART0); - scialys_config(); - - msleep(1500); - status_led(green_only); - - while (1) { - uint16_t snapval_load = 0; - - /* Default mode : try to heat the water tank */ - mode = heat; - - /* Always track power consumption and production */ - track_isnail_values(); - - /* Get load power usage */ - adc_get_value(&snapval_load, LPC_ADC(2)); - - /* Feed the dog */ - if ((moyenne_solar != 0) && (moyenne_home != 0)) { - watchdog_feed(); - } - - /* Get internal temperature */ - if (temp_read(UART0, &tmp101_deci_degrees) != 0) { - tmp101_deci_degrees = 1500; - } - - /* Internal temperature protection */ - if (tmp101_deci_degrees > (MAX_INTERNAL_TEMP * 10)) { - error_shutdown = 1; - } else if (tmp101_deci_degrees < ((MAX_INTERNAL_TEMP * 10) / 2)) { - error_shutdown = 0; - } - /* If internal temperature is above 32°C, then turn on fan. Turn off when back to under 28°C */ - if (tmp101_deci_degrees > 320) { - fan_on = 1; - gpio_set(fan_ctrl); - } else if (tmp101_deci_degrees < 280) { - 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 * 100)) { - 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 * 100)) && (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; - } - - 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; - } - } - - /* Wait approx 10ms between each loop */ - msleep(10); - - /* 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 : %d\n", tmp101_deci_degrees); - uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d\n", - snapval_solar, snapval_home, snapval_load); - uprintf(UART0, "Zc: %d\n", zc_cnt); - uprintf(UART0, "CMD: %d/%d, Fan: %d\n\n", command_val, act_cmd, fan_on); - } - - } - return 0; -} - - - diff --git a/v05/time.c b/v05/time.c deleted file mode 100644 index e1fd5ac..0000000 --- a/v05/time.c +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** - * 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; -} - - -/***************************************************************************** */ -/* 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/v05/time.h b/v05/time.h deleted file mode 100644 index a1533f8..0000000 --- a/v05/time.h +++ /dev/null @@ -1,34 +0,0 @@ -/**************************************************************************** - * 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/v05/uSD.c b/v05/uSD.c deleted file mode 100644 index e56379d..0000000 --- a/v05/uSD.c +++ /dev/null @@ -1,132 +0,0 @@ -/**************************************************************************** - * 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/v05/uSD.h b/v05/uSD.h deleted file mode 100644 index f0c10c0..0000000 --- a/v05/uSD.h +++ /dev/null @@ -1,42 +0,0 @@ -/**************************************************************************** - * 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 */ - diff --git a/v07/Makefile b/v07/Makefile deleted file mode 100644 index 41ae555..0000000 --- a/v07/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# 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/v07/config.c b/v07/config.c deleted file mode 100644 index e433794..0000000 --- a/v07/config.c +++ /dev/null @@ -1,242 +0,0 @@ -/**************************************************************************** - * 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 }, /* Oled_Reset */ - { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* Oled Control / I2C SA0 */ - { 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, }, -}; - -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); - -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); -} - - -/***************************************************************************** */ -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/v07/config.h b/v07/config.h deleted file mode 100644 index 7a2a307..0000000 --- a/v07/config.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** - * 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/v07/interface.c b/v07/interface.c deleted file mode 100644 index aad997b..0000000 --- a/v07/interface.c +++ /dev/null @@ -1,615 +0,0 @@ -/**************************************************************************** - * 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 - - -const struct pio oled_i2c_sa0 = LPC_GPIO_0_21; /* Oled Control / I2C SA0 */ - - -static uint8_t interface_board_present = 0; - -/***************************************************************************** */ -/* TMP101 onboard I2C temperature sensor */ -#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */ -struct tmp101_sensor_config tmp101_sensor = { - .bus_num = I2C0, - .addr = TMP101_ADDR, - .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) -{ - int ret = 0; - - if (interface_board_present == 0) { - return -1; - } - - ret = tmp101_sensor_read(&tmp101_sensor, NULL, deci_degrees); - if (ret != 0) { - uprintf(uart, "TMP101 read error : %d\n", ret); - return -1; - } - return 0; -} - - -/***************************************************************************** */ -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 */ - ret = tmp101_sensor_config(&tmp101_sensor); - if (ret != 0) { - uprintf(uart, "Temp config error: %d\n", ret); - interface_board_present = 0; - return -1; - } - ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor); - if (ret != 0) { - uprintf(uart, "Temp config error: %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); - - - /* Configure and start display */ - config_gpio(&oled_i2c_sa0, 0, GPIO_DIR_OUT, 0); /* Low I2C address bit set to 0 */ - 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); - } - } else { - /* Config mode. Mode entered by button action, which implies that interface board is present. */ - config_interface_handle(); - } -} - - diff --git a/v07/interface.h b/v07/interface.h deleted file mode 100644 index 0fd191d..0000000 --- a/v07/interface.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** - * 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 sensor */ -int temp_read(uint32_t uart, int* deci_degrees); - -/* Interface content update */ -void interface_update(char mode); - -#endif /* INTERFACE_H */ - diff --git a/v07/main.c b/v07/main.c deleted file mode 100644 index 689160c..0000000 --- a/v07/main.c +++ /dev/null @@ -1,492 +0,0 @@ -/**************************************************************************** - * 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 350 -#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 tmp101_deci_degrees = 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); -} - - -/***************************************************************************** */ -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); - /* And configure the fake zero-cross generation timer to 100Hz */ - timer_set_match(LPC_TIMER_32B0, CHAN0, (clk_cycles_ac_zc * 100)); - - /* 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 */ - - system_init(); - board_io_config(); - modules_config(); - - external_config(UART0); - scialys_config(); - - msleep(1500); - status_led(green_only); - - while (1) { - uint16_t snapval_load = 0; - - /* Default mode : try to heat the water tank */ - mode = heat; - - /* Always track power consumption and production */ - track_isnail_values(); - - /* Get load power usage */ - adc_get_value(&snapval_load, LPC_ADC(2)); - - /* Feed the dog */ - if ((moyenne_solar != 0) && (moyenne_home != 0)) { - watchdog_feed(); - } - - /* Get internal temperature */ - if (temp_read(UART0, &tmp101_deci_degrees) != 0) { - /* Set internal temperature to 60°C if there's a temp error, as this will trigger the - * internal fan */ - tmp101_deci_degrees = DEFAULT_INTERNAL_TEMP; - } - - /* Internal temperature protection */ - if (tmp101_deci_degrees > (MAX_INTERNAL_TEMP)) { - error_shutdown = 1; - } else if (tmp101_deci_degrees < ((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 ((tmp101_deci_degrees > FAN_ON_INTERNAL_TEMP) || (force_fan != 0)) { - fan_on = 1; - gpio_set(fan_ctrl); - } else if (tmp101_deci_degrees < 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; - } - - 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 : %d\n", tmp101_deci_degrees); - uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d\n", - snapval_solar, snapval_home, snapval_load); - 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/v07/time.c b/v07/time.c deleted file mode 100644 index 257c1fe..0000000 --- a/v07/time.c +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** - * 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/v07/time.h b/v07/time.h deleted file mode 100644 index a1533f8..0000000 --- a/v07/time.h +++ /dev/null @@ -1,34 +0,0 @@ -/**************************************************************************** - * 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/v07/uSD.c b/v07/uSD.c deleted file mode 100644 index 30022cf..0000000 --- a/v07/uSD.c +++ /dev/null @@ -1,132 +0,0 @@ -/**************************************************************************** - * 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/v07/uSD.h b/v07/uSD.h deleted file mode 100644 index f0c10c0..0000000 --- a/v07/uSD.h +++ /dev/null @@ -1,42 +0,0 @@ -/**************************************************************************** - * 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 */ - diff --git a/v08/Makefile b/v08/Makefile deleted file mode 100644 index 41ae555..0000000 --- a/v08/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# 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 deleted file mode 100644 index 7e321e2..0000000 --- a/v08/config.c +++ /dev/null @@ -1,249 +0,0 @@ -/**************************************************************************** - * 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 deleted file mode 100644 index 7a2a307..0000000 --- a/v08/config.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** - * 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 deleted file mode 100644 index c1960a6..0000000 --- a/v08/interface.c +++ /dev/null @@ -1,659 +0,0 @@ -/**************************************************************************** - * 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 solar_prod_value; -extern uint32_t home_conso_value; -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", (solar_prod_value / 1000), ((solar_prod_value % 1000) / 10)); - display_line(3, 0, line); - snprintf(line, DISP_LLEN, "Conso:% 2d,%03dA", (home_conso_value / 1000), ((home_conso_value % 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); - snprintf(line, DISP_LLEN, "v0.8 - 0.3.3"); - display_line(7, 0, line); - /* Update Oled display */ - ssd130x_display_full_screen(&display); - - /* Update RGB leds */ - /* FIXME : use for error signal */ - ws2812_set_pixel(0, (home_conso_value / 2000), (solar_prod_value / 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 deleted file mode 100644 index 59518a8..0000000 --- a/v08/interface.h +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************** - * 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 deleted file mode 100644 index c05ccee..0000000 --- a/v08/main.c +++ /dev/null @@ -1,532 +0,0 @@ -/**************************************************************************** - * 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 0x08 -#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 100 - -/* 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 manual_forced_heater = 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', /* Normal heating */ - ext_disable = 'E', /* Forced heating disabled by external input */ - forced = 'F', /* Forced heating */ - manual = 'M', - delayed_heat_prod = 'P', /* Pause */ - overprod = 'S', /* Over production, try to start other loads */ - temp_OK = 'T', /* Max temperature reached */ -}; - -/* 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 solar_prod_value = 0; -uint32_t home_conso_value = 0; - -static void track_isnail_values(void) -{ - uint16_t snapval_solar = 0; - uint16_t snapval_home = 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 */ - solar_prod_value = snapval_solar << 6; - home_conso_value = snapval_home << 6; -} - -/***************************************************************************** */ -/* 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 3 -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; - goto cmd_update_end; - } - if (manual_forced_heater > 0) { - cmd = FORCED_MODE_VALUE; - goto cmd_update_end; - } - /* 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; - } - } - if (solar_prod_value < (home_conso_value - 64)) { - /* Low production mode */ - if (cmd > 0) { - cmd--; - } - } else if (solar_prod_value > (home_conso_value + 1280)) { - if (cmd < 100) { - cmd++; - } - } else if (solar_prod_value > (home_conso_value + 256)) { - /* 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: - if ((cmd > 0) || (force_fan == 1)) { - fan_on = 1; - gpio_set(fan_ctrl); - } else { - fan_on = 0; - gpio_clear(fan_ctrl); - } - 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 ((solar_prod_value != 0) && (home_conso_value != 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; - } - - /* 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 ((solar_prod_value > sunny_days_prod_value_config) && (forced_heater_mode == 1)) { - 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; - manual_forced_heater = 1; - } - if (manual_activation_request < 10) { - uprintf(UART0, "Leaving manual forced mode\n"); - manual_activation_request = 0; - } - } - - /* Command at 100% and still more production than energy used ? */ - if ((command_val == 100) && (solar_prod_value > home_conso_value)) { - mode = overprod; - } - - /* Display */ - interface_update(mode); - - /* Debug */ - if (1) { - uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++, - (solar_prod_value / 1000), (solar_prod_value % 1000), - (home_conso_value / 1000), (home_conso_value % 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", solar_prod_value, home_conso_value); - 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 deleted file mode 100644 index 257c1fe..0000000 --- a/v08/time.c +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** - * 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 deleted file mode 100644 index a1533f8..0000000 --- a/v08/time.h +++ /dev/null @@ -1,34 +0,0 @@ -/**************************************************************************** - * 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 deleted file mode 100644 index 30022cf..0000000 --- a/v08/uSD.c +++ /dev/null @@ -1,132 +0,0 @@ -/**************************************************************************** - * 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 deleted file mode 100644 index f0c10c0..0000000 --- a/v08/uSD.h +++ /dev/null @@ -1,42 +0,0 @@ -/**************************************************************************** - * 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 */ - diff --git a/v09/Makefile b/v09/Makefile deleted file mode 100644 index 41ae555..0000000 --- a/v09/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# 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/v09/config.c b/v09/config.c deleted file mode 100644 index b20dcc2..0000000 --- a/v09/config.c +++ /dev/null @@ -1,258 +0,0 @@ -/**************************************************************************** - * 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_0, LPC_IO_DIGITAL }, /* Over Voltage protection input */ - { 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_0_28, LPC_IO_DIGITAL }, /* Over Temperature protection input */ - { 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; -const struct pio overvoltage_pin = LPC_GPIO_0_0; -const struct pio overtemperature_pin = LPC_GPIO_0_28; - -/* 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); /* This one is used to generate the second zero-crossing */ - timer_counter_config(LPC_TIMER_32B0, &ac_timer_conf_zc); - timer_on(LPC_TIMER_32B1, 0, ac_switch_on); /* This one is used to generate the power PMW */ - timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf_delay); - timer_on(LPC_TIMER_16B0, 0, power_track); /* This one is used to call the load power tracking ADC sampler */ - timer_counter_config(LPC_TIMER_16B0, &ac_timer_conf_power_track); -} - - -/***************************************************************************** */ -extern void zero_cross_detect(uint32_t gpio); -extern void overvoltage_protect(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); - config_gpio(&overtemperature_pin, 0, GPIO_DIR_IN, 0); - - /* Over-voltage protection */ - set_gpio_callback(overvoltage_protect, &overvoltage_pin, EDGE_RISING); - - /* 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/v09/config.h b/v09/config.h deleted file mode 100644 index 757c65f..0000000 --- a/v09/config.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** - * 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; -extern const struct pio overvoltage_pin; -extern const struct pio overtemperature_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/v09/interface.c b/v09/interface.c deleted file mode 100644 index 80f50f9..0000000 --- a/v09/interface.c +++ /dev/null @@ -1,585 +0,0 @@ -/**************************************************************************** - * 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, - FUNCTIONS, - LIMITS, - 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", - [FUNCTIONS] = "Reglages", - [LIMITS] = "Limites", - [SAVE_CONFIG] = "Save config", -}; -static uint8_t current_menu = MAIN_MENU; -static uint8_t current_entry = MANUAL_MODE; - -enum func_menu_list { - FUNC_CMD_TYPE, - FUNC_TENSION, - INST_POWER, - LOAD_POWER, - FORCED_MODE_VAL, - FUNC_NB_MENU, /* This one must be the last */ -}; -static const char* func_modes_titles[] = { - [FUNC_CMD_TYPE] = "Type Cmd", /* ToR / PWM */ - [FUNC_TENSION] = "Tension", /* AC / DC */ - [INST_POWER] = "P Prod", /* Production power (kW) */ - [LOAD_POWER] = "P Cahrge", /* Load power (kW) */ - [FORCED_MODE_VAL] = "Force Val", /* Default is 75% */ -}; -static uint8_t func_cur_menu = FUNC_CMD_TYPE; -static uint8_t func_cur_entry = FUNC_CMD_TYPE; - -enum limits_menu_list { - TEMP_FORCED_START, - TEMP_FORCED_TARGET, - SUNNY_DAY_VALUE, - LIM_NB_MENU, /* This one must be the last */ -}; -static const char* limits_modes_titles[] = { - [TEMP_FORCED_START] = "Water Min", - [TEMP_FORCED_TARGET] = "Water Target", - [SUNNY_DAY_VALUE] = "Sun Prod", -}; -static uint8_t limits_cur_menu = TEMP_FORCED_START; -static uint8_t limits_cur_entry = TEMP_FORCED_START; - - - -/* 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_RIGHT) { - current_menu = current_entry; - sub_menu_level = 1; - } - if (button & (BUTTON_LEFT | BUTTON_OK)) { - 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 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 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 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 solar_prod_value; -extern uint32_t home_conso_value; -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; - } - 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", (solar_prod_value / 1000), ((solar_prod_value % 1000) / 10)); - display_line(3, 0, line); - snprintf(line, DISP_LLEN, "Conso:% 2d,%03dA", (home_conso_value / 1000), ((home_conso_value % 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); - snprintf(line, DISP_LLEN, "v0.9 - 0.3.5"); - display_line(7, 0, line); - /* Update Oled display */ - ssd130x_display_full_screen(&display); - - /* Update RGB leds */ - /* FIXME : use for error signal */ - ws2812_set_pixel(0, (home_conso_value / 2000), (solar_prod_value / 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/v09/interface.h b/v09/interface.h deleted file mode 100644 index 59518a8..0000000 --- a/v09/interface.h +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************** - * 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/v09/main.c b/v09/main.c deleted file mode 100644 index a50bd52..0000000 --- a/v09/main.c +++ /dev/null @@ -1,605 +0,0 @@ -/**************************************************************************** - * apps/scialys/testv09/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 0x09 -#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 50 -#define ADC_UPD_PERIOD 17 - -/* Max water temperature. Internal protection which cannot be overriden by configuration */ -#define MAX_WATER_TEMP (90 * 100) /* Hundredth of degrees C : 90°C */ -/* Internal system max temperature : turn off heating when reached */ -#define MAX_INTERNAL_TEMP (80 * 10) /* Tenth of degrees C : 80°C */ - -/* Number of main loops during which we keep the mosfets ON in order to keep them safe */ -#define OVERVOLTAGE_PROTECTION_CYCLES 100 - -/* 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 (30 * 100) -#define TARGET_FORCED_HEATER_TEMP (50 * 100) -#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 manual_forced_heater = 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; -int temp_shutdown = 0; -int overvoltage = 0; - -enum modes { - heat = 'C', /* Normal heating */ - ext_disable = 'E', /* Forced heating disabled by external input */ - forced = 'F', /* Forced heating */ - manual = 'M', - delayed_heat_prod = 'P', /* Pause */ - overprod = 'S', /* Over production, try to start other loads */ - temp_OK = 'T', /* Max temperature reached */ -}; - -/* 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 solar_prod_value = 0; -uint32_t home_conso_value = 0; -#define NB_VAL 4 /* MUST be a power of 2 */ -#define NB_SHIFT 2 - -static void track_isnail_values(uint32_t flags) -{ - static uint8_t idx = 0; - static uint16_t solar[NB_VAL]; - static uint16_t home[NB_VAL]; - uint16_t snapval_solar = 0; - uint16_t snapval_home = 0; - int cnt, sum_solar = 0, sum_home = 0; - - /* Get new values */ - adc_get_value(&snapval_solar, LPC_ADC(1)); - adc_get_value(&snapval_home, LPC_ADC(0)); - - /* Store values */ - solar[idx] = snapval_solar; - home[idx] = snapval_home; - idx++; - if (idx >= NB_VAL) { - idx = 0; - } - - /* Compute average value */ - for (cnt = 0; cnt < NB_VAL; cnt++) { - sum_solar += solar[cnt]; - sum_home += home[cnt]; - } - - /* 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 */ - solar_prod_value = sum_solar << (6 - NB_SHIFT); - home_conso_value = sum_home << (6 - NB_SHIFT); -} - -/***************************************************************************** */ -/* AC control */ - -volatile uint8_t command_val = 0; -volatile int act_cmd = 0; /* 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 <= 3) { - act_cmd = 0; - } -} - -void ac_switch_on(uint32_t flags) -{ - gpio_clear(ac_ctrl); -} - -static uint32_t clk_cycles_ac_zc = 0; -extern uint32_t power_delay[101]; -volatile uint32_t zc_cnt = 0; - -void zero_cross(uint32_t unused) -{ - uint32_t delay = 0; - - zc_cnt++; - gpio_set(ac_ctrl); - if (act_cmd == 0) { - return; - } - if (act_cmd == 100) { - gpio_clear(ac_ctrl); - return; - } - /* Set timer to trigger ac out ON at given delay */ - if (0) { - /* Time based delay */ - delay = clk_cycles_ac_zc * (100 - act_cmd); - } else { - /* Power based delay */ - delay = power_delay[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 unused) -{ - timer_restart(LPC_TIMER_32B0); - zero_cross(0); -} - -/* Handle the power command */ -#define CMD_UP_DELAY_RESET_VALUE 3 -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; - force_fan = 1; - goto cmd_update_end; - } - - /* Water max temperature protection */ - if (water_centi_degrees > MAX_WATER_TEMP) { - 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; - goto cmd_update_end; - } - if (manual_forced_heater > 0) { - cmd = FORCED_MODE_VALUE; - goto cmd_update_end; - } - /* 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; - } - } - if ((home_conso_value > 1280) && (solar_prod_value < (home_conso_value - 1280))) { - /* Very low production mode */ - if (cmd >= 10) { - cmd -= 10; - } else { - cmd = 0; - } - } else if ((home_conso_value > 64) && (solar_prod_value < (home_conso_value - 64))) { - /* Low production mode */ - if (cmd > 0) { - cmd--; - } - } else if (solar_prod_value > (home_conso_value + 1280)) { - if (cmd <= 98) { - cmd += 2; - } else { - cmd = 100; - } - } else if (solar_prod_value > (home_conso_value + 256)) { - /* 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: - if ((cmd > 0) || (force_fan == 1)) { - fan_on = 1; - gpio_set(fan_ctrl); - } else { - fan_on = 0; - gpio_clear(fan_ctrl); - } - command_val = cmd; - /* Set Control Output duty cycle */ - set_ctrl_duty_cycle(cmd); -} - -/* Over-voltage protection */ -void overvoltage_protect(uint32_t gpio) -{ - gpio_clear(ac_ctrl); - timer_stop(LPC_TIMER_32B1); - set_ctrl_duty_cycle(100); - force_cmd = 100; - overvoltage = OVERVOLTAGE_PROTECTION_CYCLES; - gpio_set(fan_ctrl); -} - - -/* Update current working mode depending on environnement */ -volatile char mode = heat; /* Debug info */ -volatile char* msg = NULL; -void mode_update(uint32_t unused) -{ - /* Default mode : try to heat the water tank */ - mode = heat; - - /* Need to enter Forced heating mode ? */ - if (water_centi_degrees < FORCE_HEATER_TEMP) { - if (forced_heater_mode == 0) { - msg = "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) && (forced_heater_mode == 1)) { - status_led(red_off); - msg = "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 ((solar_prod_value > sunny_days_prod_value_config) && (forced_heater_mode == 1)) { - 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; - msg = "Forced mode disabled by external input\n"; - } - /* Get Over-Voltage or Over-Temperature information */ - temp_shutdown = gpio_read(overtemperature_pin); - if (overvoltage > 0) { - uint32_t ov_tmp = gpio_read(overvoltage_pin); - if (ov_tmp == 0) { - overvoltage--; - if (overvoltage == 0) { - /* Get back to normal */ - force_cmd = 0; - } - } - } - - 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) { - msg = "Entering manual forced mode\n"; - manual_activation_request = MANUAL_ACTIVATION_DURATION; - manual_forced_heater = 1; - } - if (manual_activation_request < 10) { - msg = "Leaving manual forced mode\n"; - manual_activation_request = 0; - } - } - - /* Command at 100% and still more production than energy used ? */ - if ((command_val == 100) && (solar_prod_value > home_conso_value)) { - mode = overprod; - } -} - -/***************************************************************************** */ -/* 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); - uprintf(UART0, "clk_cycles_ac_zc: %d\n", clk_cycles_ac_zc); - - /* 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 track ismail values (power consumption and production) */ - add_systick_callback(track_isnail_values, ADC_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; - int ret = 0; - - system_init(); - board_io_config(); - modules_config(); - - external_config(UART0); - scialys_config(); - - msleep(500); - status_led(green_only); - - while (1) { - /* Feed the dog */ - if ((solar_prod_value != 0) && (home_conso_value != 0)) { - watchdog_feed(); - } - - /* Update internal temperatures */ - if (1) { - /* Get internal temperatures */ - ret = temp_read(UART0, &deci_degrees_disp, &deci_degrees_power); - if (ret != CONV_OK) { - if ((ret & CONV_POWER_OK) == 0) { - force_fan = 1; - } - if ((ret & CONV_DISPLAY_OK) == 0) { - force_fan = 1; - } - } - /* Internal temperature protection */ - if ((deci_degrees_power > MAX_INTERNAL_TEMP) || (deci_degrees_disp > MAX_INTERNAL_TEMP)) { - error_shutdown = 1; - uprintf(UART0, "Error shutdown !\n"); - } else if ((deci_degrees_power < ((MAX_INTERNAL_TEMP) / 2)) && - (deci_degrees_disp < ((MAX_INTERNAL_TEMP) / 2))) { - error_shutdown = 0; - } - } - - /* Update water tank temperature */ - if (1) { - int ret = 0, old_water_temp = 0; - old_water_temp = water_centi_degrees; - /* Get thermocouple value */ - 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; - } - } - - /* Update current mode */ - mode_update(0); - - /* Display */ - if (1) { - interface_update(mode); - } - - /* Debug */ - if (1) { - uprintf(UART0, "#%c:%d:%d:%d:%d:%d:%d:", - mode, loop++, solar_prod_value, home_conso_value, - water_centi_degrees, deci_degrees_power, deci_degrees_disp); - uprintf(UART0, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d$\n", - load_power_lowest, load_power_highest, - zc_cnt, command_val, act_cmd, fan_on, force_fan, - error_shutdown, temp_shutdown, overvoltage, - external_disable, forced_heater_mode, manual_activation_request); - if (msg != NULL) { - uprintf(UART0, (char*)msg); - msg = NULL; - } - } - } - return 0; -} - - - diff --git a/v09/power_delay.c b/v09/power_delay.c deleted file mode 100644 index fdbe3b0..0000000 --- a/v09/power_delay.c +++ /dev/null @@ -1,105 +0,0 @@ -#include "lib/stdint.h" - -uint32_t power_delay[101] = { - [0] = 0, - [1] = 30608, - [2] = 43360, - [3] = 53195, - [4] = 61530, - [5] = 68911, - [6] = 75620, - [7] = 81822, - [8] = 87626, - [9] = 93107, - [10] = 98319, - [11] = 103305, - [12] = 108095, - [13] = 112716, - [14] = 117188, - [15] = 121527, - [16] = 125750, - [17] = 129867, - [18] = 133888, - [19] = 137823, - [20] = 141680, - [21] = 145465, - [22] = 149184, - [23] = 152843, - [24] = 156447, - [25] = 159999, - [26] = 163505, - [27] = 166967, - [28] = 170389, - [29] = 173774, - [30] = 177124, - [31] = 180443, - [32] = 183732, - [33] = 186995, - [34] = 190232, - [35] = 193446, - [36] = 196639, - [37] = 199813, - [38] = 202969, - [39] = 206109, - [40] = 209234, - [41] = 212347, - [42] = 215448, - [43] = 218539, - [44] = 221621, - [45] = 224695, - [46] = 227763, - [47] = 230827, - [48] = 233886, - [49] = 236944, - [50] = 240000, - [51] = 243055, - [52] = 246113, - [53] = 249172, - [54] = 252236, - [55] = 255304, - [56] = 258378, - [57] = 261460, - [58] = 264551, - [59] = 267652, - [60] = 270765, - [61] = 273890, - [62] = 277030, - [63] = 280186, - [64] = 283360, - [65] = 286553, - [66] = 289767, - [67] = 293004, - [68] = 296267, - [69] = 299556, - [70] = 302875, - [71] = 306225, - [72] = 309610, - [73] = 313032, - [74] = 316494, - [75] = 320000, - [76] = 323552, - [77] = 327156, - [78] = 330815, - [79] = 334534, - [80] = 338319, - [81] = 342176, - [82] = 346111, - [83] = 350132, - [84] = 354249, - [85] = 358472, - [86] = 362811, - [87] = 367283, - [88] = 371904, - [89] = 376694, - [90] = 381680, - [91] = 386892, - [92] = 392373, - [93] = 398177, - [94] = 404379, - [95] = 411088, - [96] = 418469, - [97] = 426804, - [98] = 436639, - [99] = 449391, - [100] = 480000, -}; diff --git a/v09/sin.pl b/v09/sin.pl deleted file mode 100755 index 063ef81..0000000 --- a/v09/sin.pl +++ /dev/null @@ -1,34 +0,0 @@ -#! /usr/bin/perl -w - -use strict; -use warnings; - -use Math::Trig; - -my ($clocks, $outfile) = @ARGV; -my ($fh, $angle, $v, $w, $idx); - -if (defined $outfile) { - open($fh, '>', $outfile); - print $fh "#include \"lib/stdint.h\"\n\n"; - print $fh "uint32_t power_delay[101] = {\n"; -} - -for (my $i = 0; $i <= 100; $i++) { - $angle = $i * 0.02; - $v = (acos(1 - $angle) / pi * 100); - $w = int($v * $clocks); - if (not defined $outfile) { - print "$v - [$i] = $w,\n"; - } else { - print $fh "\t[$i] = $w,\n"; - } -} - -if (defined $outfile) { - print $fh "};\n"; - close($fh); -} -print "Done\n"; - - diff --git a/v09/time.c b/v09/time.c deleted file mode 100644 index 257c1fe..0000000 --- a/v09/time.c +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** - * 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/v09/time.h b/v09/time.h deleted file mode 100644 index a1533f8..0000000 --- a/v09/time.h +++ /dev/null @@ -1,34 +0,0 @@ -/**************************************************************************** - * 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/v09/uSD.c b/v09/uSD.c deleted file mode 100644 index 30022cf..0000000 --- a/v09/uSD.c +++ /dev/null @@ -1,132 +0,0 @@ -/**************************************************************************** - * 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/v09/uSD.h b/v09/uSD.h deleted file mode 100644 index f0c10c0..0000000 --- a/v09/uSD.h +++ /dev/null @@ -1,42 +0,0 @@ -/**************************************************************************** - * 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 */ -