Scialys v09 app
authorNathael Pajani <nathael.pajani@ed3l.fr>
Thu, 11 Jun 2020 19:03:46 +0000 (21:03 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 11:07:29 +0000 (12:07 +0100)
12 files changed:
v09/Makefile [new file with mode: 0644]
v09/config.c [new file with mode: 0644]
v09/config.h [new file with mode: 0644]
v09/interface.c [new file with mode: 0644]
v09/interface.h [new file with mode: 0644]
v09/main.c [new file with mode: 0644]
v09/power_delay.c [new file with mode: 0644]
v09/sin.pl [new file with mode: 0755]
v09/time.c [new file with mode: 0644]
v09/time.h [new file with mode: 0644]
v09/uSD.c [new file with mode: 0644]
v09/uSD.h [new file with mode: 0644]

diff --git a/v09/Makefile b/v09/Makefile
new file mode 100644 (file)
index 0000000..41ae555
--- /dev/null
@@ -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/v09/config.c b/v09/config.c
new file mode 100644 (file)
index 0000000..b20dcc2
--- /dev/null
@@ -0,0 +1,258 @@
+/****************************************************************************
+ *   apps/scialys/v07/config.c
+ *
+ * Copyright 2018 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#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
new file mode 100644 (file)
index 0000000..757c65f
--- /dev/null
@@ -0,0 +1,98 @@
+/****************************************************************************
+ *   apps/scialys/v07/config.h
+ *
+ * Copyright 2018 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#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
new file mode 100644 (file)
index 0000000..80f50f9
--- /dev/null
@@ -0,0 +1,585 @@
+/****************************************************************************
+ *   apps/scialys/v07/interface.c
+ *
+ * Copyright 2018 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#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
new file mode 100644 (file)
index 0000000..59518a8
--- /dev/null
@@ -0,0 +1,69 @@
+/****************************************************************************
+ *   apps/blyes/relay_lights/interface.h
+ *
+ * Copyright 2018 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#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
new file mode 100644 (file)
index 0000000..a50bd52
--- /dev/null
@@ -0,0 +1,605 @@
+/****************************************************************************
+ *   apps/scialys/testv09/main.c
+ *
+ * Scialys system for solar-panel power generation tracking and fair use.
+ *
+ * Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+#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
new file mode 100644 (file)
index 0000000..fdbe3b0
--- /dev/null
@@ -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/v09/sin.pl b/v09/sin.pl
new file mode 100755 (executable)
index 0000000..063ef81
--- /dev/null
@@ -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/v09/time.c b/v09/time.c
new file mode 100644 (file)
index 0000000..257c1fe
--- /dev/null
@@ -0,0 +1,98 @@
+/****************************************************************************
+ *   apps/blyes/inter_lights/time.c
+ *
+ * Copyright 2018 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#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
new file mode 100644 (file)
index 0000000..a1533f8
--- /dev/null
@@ -0,0 +1,34 @@
+/****************************************************************************
+ *   apps/blyes/inter_lights/time.h
+ *
+ * Copyright 2018 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#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
new file mode 100644 (file)
index 0000000..30022cf
--- /dev/null
+++ b/v09/uSD.c
@@ -0,0 +1,132 @@
+/****************************************************************************
+ *   apps/blyes/inter_lights/uSD.c
+ *
+ * Copyright 2018 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#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(&micro_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(&micro_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(&micro_sd);
+               if (ret == 0) {
+                       step = 1;
+                       msleep(10);
+                       ret = sdmmc_init_wait_card_ready(&micro_sd);
+                       if (ret <= 1) {
+                               step = 2;
+                               ret = sdmmc_init_end(&micro_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
new file mode 100644 (file)
index 0000000..f0c10c0
--- /dev/null
+++ b/v09/uSD.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+ *   apps/blyes/inter_lights/usSD.h
+ *
+ * Copyright 2018 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#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 */
+