#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"
+
#define MODULE_VERSION 0x01
#define MODULE_NAME "Scialys uC"
/* 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.
+ * 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 25
#define TARGET_FORCED_HEATER_TEMP 45
#define NO_FORCED_HEATING_ON_SUNNY_DAYS 750
-#define FORCED_MODE_VALUE 190 /* A fraction of 255 */
+#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
uint32_t forced_heater_mode = 0;
uint32_t forced_heater_delay = 0;
uint32_t forced_heater_time = 0;
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,
.resolution = TMP_RES_ELEVEN_BITS,
};
-/* Led control data pin */
-const struct pio ws2812_data_out_pin = LPC_GPIO_0_23;
+#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, },
+};
+
+#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 = 0x26,
+};
+static struct rtc_time now;
/***************************************************************************** */
/* Basic system init and configuration */
.intr_mode_only = 0,
.callback = wdt_callback,
.locks = 0,
- .nb_clk = 0x0FFFFFF, /* 0x3FF to 0x03FFFFFF */
+ .nb_clk = 0x03FFFFFF, /* 0x3FF to 0x03FFFFFF */
.wdt_window = 0,
.wdt_warn = 0x3FF,
};
/***************************************************************************** */
+/* 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))));
+ }
+ /* Load control */
+ if (c == 'l') {
+ act_cmd = 100;
+ } else if (c == 'x') {
+ act_cmd = 0;
+ }
}
+
+
+
+/***************************************************************************** */
+/* System communication over UART1 */
void cmd_rx(uint8_t c)
{
}
-void dmx_send_frame(uint8_t start_code, uint8_t* slots, uint16_t nb_slots)
+
+/***************************************************************************** */
+
+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;
+}
+
+static uint8_t thermal_warn_flag = 0;
+void th_warning(uint32_t gpio)
{
+ /* FIXME : test for condition set or removed */
+ /* Turn off AC output */
+ gpio_clear(ac_ctrl);
+ act_cmd = 0;
+ thermal_warn_flag = 1;
+ /* Turn on Fan at max speed */
+ timer_set_match(LPC_TIMER_32B0, CHAN0, 0);
}
+
+
+/***************************************************************************** */
+/* System interface */
enum buttons {
BUTTON_NONE = 0,
BUTTON_OK,
BUTTON_DOWN,
};
+
uint32_t manual_activation_request = 0;
uint8_t button_pressed = 0;
void manual_activation(uint32_t gpio) {
}
-void zero_cross(uint32_t gpio) {
-}
-void th_warning(uint32_t gpio) {
-}
-
-
+/***************************************************************************** */
void temp_config(int uart_num)
{
int ret = 0;
}
-#define NB_VAL 20
+/***************************************************************************** */
+/* 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);
+}
+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 >= (SSD130x_NB_COL / 8)) {
+ col = 0;
+ line++;
+ if (line >= SSD130x_NB_PAGES) {
+ return i;
+ }
+ }
+ }
+ return len;
+}
+
+
+/***************************************************************************** */
+#define NB_VAL 20
+
enum modes {
heat = 'C',
ejp = 'E',
- no_heat_prod = 'P',
+ delayed_heat_prod = 'P',
forced = 'F',
temp_OK = 'T',
manual = 'M',
uint8_t idx = 0;
uint32_t loop = 0;
char mode = heat; /* Debug info */
+ int ret = 0;
system_init();
status_led(red_only);
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);
/* 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_RISING);
- set_gpio_callback(th_warning, &th_warn_in_pin, EDGE_RISING);
+ 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));
/* 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);
+ uprintf(UART0, buff);
+ } else if (ret == -EFAULT) {
+ memcpy(&now, &oldest, sizeof(struct rtc_time));
+ rtc_pcf85363_time_write(&rtc_conf, &now);
+ }
+
msleep(50);
/* Read parameters from memory */
if (1) {
never_force = 0;
- forced_heater_delay = FORCED_HEATER_DELAY;
+ forced_heater_delay = 0;
forced_heater_time = FORCED_HEATER_DURATION;
}
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");
mode = temp_OK;
}
- /* Do not force if there is some sun, it may be enough to heat again */
+ /* 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) {
forced_heater_mode = 0;
- mode = no_heat_prod;
- forced_heater_delay = FORCED_HEATER_DELAY;
+ mode = delayed_heat_prod;
}
/* Do not force heating if this is an EJP day */
}
} else if (moyenne_solar < moyenne_home) {
/* Low production mode */
- if (command_val > 25) {
- command_val -= 25;
+ if (command_val > 10) {
+ command_val -= 10;
} else {
command_val = 0;
mode = idle_heat;
status_led(green_off);
} else {
/* High production mode */
- if (command_val < 245) {
+ if (command_val < 90) {
command_val += 10;
} else {
- command_val = 255;
+ command_val = 100;
mode = full_heat;
}
status_led(green_on);
}
- /* Send DMX frame */
- dmx_send_frame(0x00, 0, 1);
+ /* Set Control Output duty cycle */
+ /* Debug Nath TMP */
+ //set_ctrl_duty_cycle(command_val);
+ set_ctrl_duty_cycle( user_potar / 10 );
/* Display */
if (1) {
int abs_centi = water_centi_degrees;
uprintf(UART0, "Button : %d\n", button_pressed);
button_pressed = 0;
}
- uprintf(UART0, "CMD: %d\n\n", 0);
- ws2812_set_pixel(0, (isnail_val_home / 100), (isnail_val_solar / 100), 0);
- ws2812_set_pixel(1, (acs_val_load >> 2), 0, (user_potar >> 2));
+ uprintf(UART0, "CMD: %d/%d, Fan: %d, ZC: %d\n\n", command_val, act_cmd, fan_speed, zc_count);
+ ws2812_set_pixel(0, (isnail_val_home / 100), (isnail_val_solar / 100), fan_speed);
+ ws2812_set_pixel(1, (acs_val_load >> 2), (zc_count & 0xFF), (user_potar >> 2));
ws2812_send_frame(0);
}
}