]> mj.ucw.cz Git - home-hw.git/blobdiff - aircon/firmware/main.c
Merge branch 'master' of ssh://git.ucw.cz/home/mj/GIT/home-hw
[home-hw.git] / aircon / firmware / main.c
index 89307b48710cd0c3c7680df1386c6b0ccbb4a9d2..da9df476d2e6403e1fc47cf0d726e2e5f47285d1 100644 (file)
@@ -7,6 +7,7 @@
 #include "util.h"
 #include "ds18b20.h"
 #include "modbus.h"
 #include "util.h"
 #include "ds18b20.h"
 #include "modbus.h"
+#include "registers.h"
 
 #include <libopencm3/cm3/nvic.h>
 #include <libopencm3/cm3/systick.h>
 
 #include <libopencm3/cm3/nvic.h>
 #include <libopencm3/cm3/systick.h>
 
 #include <string.h>
 
 
 #include <string.h>
 
-static void clock_setup(void)
+static void rc_init(void);
+static bool rc_send(char key);
+
+static void clock_init(void)
 {
        rcc_clock_setup_in_hse_8mhz_out_72mhz();
 
 {
        rcc_clock_setup_in_hse_8mhz_out_72mhz();
 
@@ -43,7 +47,7 @@ static void clock_setup(void)
        rcc_periph_reset_pulse(RST_TIM4);
 }
 
        rcc_periph_reset_pulse(RST_TIM4);
 }
 
-static void gpio_setup(void)
+static void gpio_init(void)
 {
        // Switch JTAG off to free up pins
        gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0);
 {
        // Switch JTAG off to free up pins
        gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0);
@@ -96,21 +100,23 @@ void sys_tick_handler(void)
        ms_ticks++;
 }
 
        ms_ticks++;
 }
 
-static void tick_setup(void)
+static void tick_init(void)
 {
        systick_set_frequency(1000, 72000000);
        systick_counter_enable();
        systick_interrupt_enable();
 }
 
 {
        systick_set_frequency(1000, 72000000);
        systick_counter_enable();
        systick_interrupt_enable();
 }
 
+#if 0
 static void delay_ms(uint ms)
 {
        u32 start_ticks = ms_ticks;
        while (ms_ticks - start_ticks < ms)
                ;
 }
 static void delay_ms(uint ms)
 {
        u32 start_ticks = ms_ticks;
        while (ms_ticks - start_ticks < ms)
                ;
 }
+#endif
 
 
-static void usart_setup(void)
+static void usart_init(void)
 {
        usart_set_baudrate(USART1, 115200);
        usart_set_databits(USART1, 8);
 {
        usart_set_baudrate(USART1, 115200);
        usart_set_databits(USART1, 8);
@@ -153,7 +159,6 @@ static void pwm_init(void)
 
        // 50% PWM for the IR LED
        timer_set_oc_mode(TIM4, TIM_OC2, TIM_OCM_FORCE_HIGH);   // will be TIM_OCM_PWM1 when transmitting
 
        // 50% PWM for the IR LED
        timer_set_oc_mode(TIM4, TIM_OC2, TIM_OCM_FORCE_HIGH);   // will be TIM_OCM_PWM1 when transmitting
-       // timer_set_oc_mode(TIM4, TIM_OC2, TIM_OCM_PWM1);      // FIXME
        timer_set_oc_value(TIM4, TIM_OC2, T4_CYCLE / 2);
        timer_set_oc_polarity_high(TIM4, TIM_OC2);
        timer_enable_oc_output(TIM4, TIM_OC2);
        timer_set_oc_value(TIM4, TIM_OC2, T4_CYCLE / 2);
        timer_set_oc_polarity_high(TIM4, TIM_OC2);
        timer_enable_oc_output(TIM4, TIM_OC2);
@@ -169,23 +174,31 @@ static void pwm_init(void)
 
 int main(void)
 {
 
 int main(void)
 {
-       clock_setup();
-       gpio_setup();
-       tick_setup();
-       usart_setup();
+       clock_init();
+       gpio_init();
+       tick_init();
+       usart_init();
        pwm_init();
        pwm_init();
+       rc_init();
 
 
-       debug_puts("Hello, world!\n");
+       debug_puts("Hello, world! This is Aircon Controller speaking.\n");
 
        ds_init();
        modbus_init();
 
 
        ds_init();
        modbus_init();
 
-       byte cycles = 0;
+       u32 last_show_temp = 0;
+       u32 last_ds_step = 0;
+
        for (;;) {
        for (;;) {
-               debug_led_toggle();
-               delay_ms(100);
-               ds_step();
+               if (ms_ticks - last_ds_step >= 100) {
+                       debug_led_toggle();
+                       ds_step();
+                       last_ds_step = ms_ticks;
+               }
+
                modbus_loop();
                modbus_loop();
+
+#if 0
                if (usart_get_flag(USART1, USART_SR_RXNE)) {
                        uint ch = usart_recv(USART1);
                        if (ch == 'B') {
                if (usart_get_flag(USART1, USART_SR_RXNE)) {
                        uint ch = usart_recv(USART1);
                        if (ch == 'B') {
@@ -214,40 +227,147 @@ int main(void)
                                 *      % = pwm*4.389 - 12.723
                                 */
                                timer_set_oc_value(TIM4, TIM_OC1, T4_CYCLE * fan_pwm / 256);
                                 *      % = pwm*4.389 - 12.723
                                 */
                                timer_set_oc_value(TIM4, TIM_OC1, T4_CYCLE * fan_pwm / 256);
+                       } else {
+                               rc_send(ch);
                        }
                }
                        }
                }
-               if (cycles++ >= 50) {
-                       cycles = 0;
+#endif
+
+               if (ms_ticks - last_show_temp >= 5000) {
                        show_temperature();
                        show_temperature();
+                       last_show_temp = ms_ticks;
                }
                }
+
+               wait_for_interrupt();
        }
 
        return 0;
 }
 
        }
 
        return 0;
 }
 
-/*** Modbus callbacks ***/
-
-enum aircon_coils {
-       AIRCON_COIL_EXCHANGER_BYPASS,
-       AIRCON_COIL_MAX,
+/*** Infra-red remote control transmitter ***/
+
+enum rc_keys {
+       RC_AUTO,
+       RC_TEMP_UP,
+       RC_FUNC,
+       RC_HI,
+       RC_TIMER,
+       RC_MID,
+       RC_TEMP_DOWN,
+       RC_SLEEP,
+       RC_LOW,
+       RC_POWER,
+       RC_MAX
 };
 
 };
 
-enum aircon_input_registers {
-       AIRCON_IREG_TEMP_FROM_INSIDE,
-       AIRCON_IREG_TEMP_TO_INSIDE,
-       AIRCON_IREG_TEMP_FROM_OUTSIDE,
-       AIRCON_IREG_TEMP_TO_OUTSIDE,
-       AIRCON_IREG_TEMP_MIXED,
-       AIRCON_IREG_MAX,
-       AIRCON_IREG_DS_ID_BASE = 0x1000,
-       AIRCON_IREG_DS_ID_MAX = AIRCON_IREG_DS_ID_BASE + 3*DS_NUM_SENSORS,
+static const char * const rc_patterns[RC_MAX] = {
+       [RC_AUTO]       = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*A*B*B*A*B*A*A*B*B*A*A*B*A*B*B*A*$",
+       [RC_TEMP_UP]    = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*A*A*B*A*B*A*A*B*B*B*A*B*A*B*B*A*$",
+       [RC_FUNC]       = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*B*A*B*B*B*A*A*B*A*B*A*A*A*B*B*A*$",
+       [RC_HI]         = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*B*A*A*B*B*A*A*B*A*B*B*A*A*B*B*A*$",
+       [RC_TIMER]      = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*A*B*B*B*B*A*A*B*B*A*A*A*A*B*B*A*$",
+       [RC_MID]        = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*B*B*A*B*B*A*A*B*A*A*B*A*A*B*B*A*$",
+       [RC_TEMP_DOWN]  = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*B*A*B*A*B*A*A*B*A*B*A*B*A*B*B*A*$",
+       [RC_SLEEP]      = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*B*B*B*B*B*A*A*B*A*A*A*A*A*B*B*A*$",
+       [RC_LOW]        = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*A*B*A*B*B*A*A*B*B*A*B*A*A*B*B*A*$",
+       [RC_POWER]      = "^#*A*A*A*A*A*A*A*B*B*B*B*B*B*B*B*A*A*A*B*B*B*A*A*B*B*B*A*A*A*B*B*A*$",
 };
 
 };
 
-enum aircon_holding_registers {
-       AIRCON_HREG_EXCHANGER_FAN,
-       AIRCON_HREG_REMOTE_CONTROL,
-       AIRCON_HREG_MAX,
-};
+static const char rc_keys[] = "aufhtmdslp";
+
+static void rc_init(void)
+{
+       // TIM1 runs at 1 MHz and it is used for timing of RC pulses
+       timer_set_prescaler(TIM1, CPU_CLOCK_MHZ - 1);
+       timer_set_mode(TIM1, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
+       timer_update_on_overflow(TIM1);
+       timer_disable_preload(TIM1);
+       timer_one_shot_mode(TIM1);
+       timer_enable_irq(TIM1, TIM_DIER_UIE);
+       nvic_enable_irq(NVIC_TIM1_UP_IRQ);
+}
+
+static volatile const char *rc_pattern_pos;
+static volatile char rc_pending;
+
+void tim1_up_isr(void)
+{
+       if (TIM_SR(TIM1) & TIM_SR_UIF) {
+               TIM_SR(TIM1) &= ~TIM_SR_UIF;
+
+               if (!rc_pattern_pos)    // Just to be sure
+                       return;
+
+               bool val;       // 1=pulse, 0=break
+               uint duration;  // in μs
+
+               switch (*rc_pattern_pos++) {
+                       case '^':
+                               val = 1;
+                               duration = 9032;
+                               break;
+                       case '#':
+                               val = 0;
+                               duration = 4457;
+                               break;
+                       case '*':
+                               val = 1;
+                               duration = 619;
+                               break;
+                       case 'A':
+                               val = 0;
+                               duration = 514;
+                               break;
+                       case 'B':
+                               val = 0;
+                               duration = 1617;
+                               break;
+                       case '$':
+                               val = 0;
+                               duration = 10000;
+                               break;
+                       default:
+                               // End of transmission
+                               gpio_set(GPIOC, GPIO13);
+                               rc_pattern_pos = NULL;
+                               rc_pending = 0;
+                               return;
+               }
+
+               if (val)
+                       timer_set_oc_mode(TIM4, TIM_OC2, TIM_OCM_PWM1);
+               else
+                       timer_set_oc_mode(TIM4, TIM_OC2, TIM_OCM_FORCE_HIGH);
+
+               timer_set_period(TIM1, duration - 1);
+               timer_generate_event(TIM1, TIM_EGR_UG);
+               timer_enable_counter(TIM1);
+       }
+}
+
+static bool rc_send(char key)
+{
+       if (rc_pending)
+               return false;
+       if (!key)
+               return false;
+
+       const char *s = strchr(rc_keys, key);
+       if (!s)
+               return false;
+       rc_pending = key;
+       rc_pattern_pos = rc_patterns[s - rc_keys];
+       debug_printf("RC sending: %c\n", key);
+
+       gpio_clear(GPIOC, GPIO13);
+
+       timer_set_period(TIM1, 1);
+       timer_generate_event(TIM1, TIM_EGR_UG);
+       timer_enable_counter(TIM1);
+       return true;
+}
+
+/*** Modbus callbacks ***/
 
 bool modbus_check_discrete_input(u16 addr UNUSED)
 {
 
 bool modbus_check_discrete_input(u16 addr UNUSED)
 {
@@ -292,9 +412,11 @@ void modbus_set_coil(u16 addr, bool value)
        }
 }
 
        }
 }
 
+#define AIRCON_IREG_DS_ID_MAX (AIRCON_IREG_DS_ID_BASE + 3*DS_NUM_SENSORS)
+
 bool modbus_check_input_register(u16 addr)
 {
 bool modbus_check_input_register(u16 addr)
 {
-       return (addr < AIRCON_IREG_MAX || addr >= AIRCON_IREG_DS_ID_BASE && addr < AIRCON_IREG_DS_ID_MAX);
+       return (addr < AIRCON_IREG_MAX || addr >= AIRCON_IREG_DS_ID_BASE && addr < AIRCON_IREG_DS_ID_BASE);
 }
 
 static const byte temp_sensor_addrs[][8] = {
 }
 
 static const byte temp_sensor_addrs[][8] = {
@@ -315,7 +437,7 @@ u16 modbus_get_input_register(u16 addr)
                        return 0x8000;
                if (ds_sensors[i].current_temp == DS_TEMP_UNKNOWN)
                        return 0x8000;
                        return 0x8000;
                if (ds_sensors[i].current_temp == DS_TEMP_UNKNOWN)
                        return 0x8000;
-               return ds_sensors[i].current_temp & 0xffff;
+               return ((ds_sensors[i].current_temp + 5) / 10) & 0xffff;
        } else if (addr >= AIRCON_IREG_DS_ID_BASE && addr < AIRCON_IREG_DS_ID_MAX) {
                byte i = (addr - AIRCON_IREG_DS_ID_BASE) / 3;
                byte j = (addr - AIRCON_IREG_DS_ID_BASE) % 3;
        } else if (addr >= AIRCON_IREG_DS_ID_BASE && addr < AIRCON_IREG_DS_ID_MAX) {
                byte i = (addr - AIRCON_IREG_DS_ID_BASE) / 3;
                byte j = (addr - AIRCON_IREG_DS_ID_BASE) % 3;
@@ -336,6 +458,7 @@ u16 modbus_get_holding_register(u16 addr)
        case AIRCON_HREG_EXCHANGER_FAN:
                return fan_pwm;
        case AIRCON_HREG_REMOTE_CONTROL:
        case AIRCON_HREG_EXCHANGER_FAN:
                return fan_pwm;
        case AIRCON_HREG_REMOTE_CONTROL:
+               return rc_pending;
        default:
                return 0;
        }
        default:
                return 0;
        }
@@ -349,6 +472,8 @@ void modbus_set_holding_register(u16 addr, u16 value)
                timer_set_oc_value(TIM4, TIM_OC1, T4_CYCLE * fan_pwm / 256);
                break;
        case AIRCON_HREG_REMOTE_CONTROL:
                timer_set_oc_value(TIM4, TIM_OC1, T4_CYCLE * fan_pwm / 256);
                break;
        case AIRCON_HREG_REMOTE_CONTROL:
+               if (!rc_send(value))
+                       modbus_slave_error();
                break;
        default:
                ;
                break;
        default:
                ;