From: Martin Mares Date: Tue, 16 Jul 2019 18:47:48 +0000 (+0200) Subject: Aircon: Firmware moved to a sub-directory X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=d44f6eb429d864273ba050ee4c1c32c30dfb05a6;p=home-hw.git Aircon: Firmware moved to a sub-directory --- diff --git a/aircon/Makefile b/aircon/Makefile deleted file mode 100644 index 1b9f247..0000000 --- a/aircon/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -ROOT=.. -BINARY=main -OBJS=main.o -LIB_OBJS=util-debug.o ds18b20.o modbus.o - -include $(ROOT)/mk/bluepill.mk diff --git a/aircon/config.h b/aircon/config.h deleted file mode 100644 index 2c40c0b..0000000 --- a/aircon/config.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Air Conditioning Controller -- Configuration - * - * (c) 2019 Martin Mareš - */ - -// Processor clock - -#define CPU_CLOCK_MHZ 72 - -// Debugging port - -#define DEBUG_USART USART1 -#define DEBUG_LED_BLUEPILL - -// MODBUS library parameters - -#define MODBUS_USART USART3 -#define MODBUS_NVIC_USART_IRQ NVIC_USART3_IRQ -#define MODBUS_USART_ISR usart3_isr - -#define MODBUS_TXEN_GPIO_PORT GPIOA -#define MODBUS_TXEN_GPIO_PIN GPIO6 - -#define MODBUS_TIMER TIM2 -#define MODBUS_NVIC_TIMER_IRQ NVIC_TIM2_IRQ -#define MODBUS_TIMER_ISR tim2_isr - -#define MODBUS_OUR_ADDRESS 42 - -#define MODBUS_BAUD_RATE 19200 - -#define MODBUS_DEBUG - -// DS18B20 library parameters - -#define DS_TIMER TIM3 -#define DS_GPIO GPIOA -#define DS_PIN GPIO7 -#define DS_DMA DMA1 -#define DS_DMA_CH 6 -#define DS_NUM_SENSORS 8 - -#undef DS_DEBUG -#undef DS_DEBUG2 diff --git a/aircon/firmware/Makefile b/aircon/firmware/Makefile new file mode 100644 index 0000000..1b9f247 --- /dev/null +++ b/aircon/firmware/Makefile @@ -0,0 +1,6 @@ +ROOT=.. +BINARY=main +OBJS=main.o +LIB_OBJS=util-debug.o ds18b20.o modbus.o + +include $(ROOT)/mk/bluepill.mk diff --git a/aircon/firmware/config.h b/aircon/firmware/config.h new file mode 100644 index 0000000..2c40c0b --- /dev/null +++ b/aircon/firmware/config.h @@ -0,0 +1,45 @@ +/* + * Air Conditioning Controller -- Configuration + * + * (c) 2019 Martin Mareš + */ + +// Processor clock + +#define CPU_CLOCK_MHZ 72 + +// Debugging port + +#define DEBUG_USART USART1 +#define DEBUG_LED_BLUEPILL + +// MODBUS library parameters + +#define MODBUS_USART USART3 +#define MODBUS_NVIC_USART_IRQ NVIC_USART3_IRQ +#define MODBUS_USART_ISR usart3_isr + +#define MODBUS_TXEN_GPIO_PORT GPIOA +#define MODBUS_TXEN_GPIO_PIN GPIO6 + +#define MODBUS_TIMER TIM2 +#define MODBUS_NVIC_TIMER_IRQ NVIC_TIM2_IRQ +#define MODBUS_TIMER_ISR tim2_isr + +#define MODBUS_OUR_ADDRESS 42 + +#define MODBUS_BAUD_RATE 19200 + +#define MODBUS_DEBUG + +// DS18B20 library parameters + +#define DS_TIMER TIM3 +#define DS_GPIO GPIOA +#define DS_PIN GPIO7 +#define DS_DMA DMA1 +#define DS_DMA_CH 6 +#define DS_NUM_SENSORS 8 + +#undef DS_DEBUG +#undef DS_DEBUG2 diff --git a/aircon/firmware/kerm b/aircon/firmware/kerm new file mode 100644 index 0000000..988f567 --- /dev/null +++ b/aircon/firmware/kerm @@ -0,0 +1,5 @@ +set port /dev/ttyUSB0 +set speed 115200 +set flow-control none +set carrier-watch off +connect diff --git a/aircon/firmware/main.c b/aircon/firmware/main.c new file mode 100644 index 0000000..556ec89 --- /dev/null +++ b/aircon/firmware/main.c @@ -0,0 +1,377 @@ +/* + * Air Conditioning Controller + * + * (c) 2019 Martin Mareš + */ + +#include "util.h" +#include "ds18b20.h" +#include "modbus.h" + +#include +#include +#include +#include +#include +#include + +#include + +static void clock_setup(void) +{ + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOB); + rcc_periph_clock_enable(RCC_GPIOC); + rcc_periph_clock_enable(RCC_USART1); + rcc_periph_clock_enable(RCC_USART3); + rcc_periph_clock_enable(RCC_TIM1); + rcc_periph_clock_enable(RCC_TIM2); + rcc_periph_clock_enable(RCC_TIM3); + rcc_periph_clock_enable(RCC_TIM4); + rcc_periph_clock_enable(RCC_DMA1); + + rcc_periph_reset_pulse(RST_GPIOA); + rcc_periph_reset_pulse(RST_GPIOB); + rcc_periph_reset_pulse(RST_GPIOC); + rcc_periph_reset_pulse(RST_USART1); + rcc_periph_reset_pulse(RST_USART3); + rcc_periph_reset_pulse(RST_TIM1); + rcc_periph_reset_pulse(RST_TIM2); + rcc_periph_reset_pulse(RST_TIM3); + rcc_periph_reset_pulse(RST_TIM4); +} + +static void gpio_setup(void) +{ + // Switch JTAG off to free up pins + gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0); + + // PA6 = RS485 TX enable + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO6); + gpio_clear(GPIOA, GPIO6); + + // PA7 = TIM3_CH2 for DS18B20 bus -- configured by ds18b20 library + + // PA9 = TXD1 for debugging console + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9); + + // PA10 = RXD1 for debugging console + gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO10); + + // PB0 = bypass LED* + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO0); + gpio_set(GPIOB, GPIO0); + + // PB1 = MODBUS frame LED* + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO1); + gpio_set(GPIOB, GPIO1); + + // PB6 = TIM4_CH1 fan control opto-coupler + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO6); + + // PB7 = TIM4_CH2 IR LED + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO7); + + // PB10 = TXD3 for RS485 + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO10); + + // PB11 = RXD3 for RS485 + gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO11); + + // PC13 = BluePill LED + gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); + gpio_clear(GPIOC, GPIO13); + + // PC15 = bypass opto-coupler + gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO15); + gpio_clear(GPIOC, GPIO15); +} + +static volatile u32 ms_ticks; + +void sys_tick_handler(void) +{ + ms_ticks++; +} + +static void tick_setup(void) +{ + systick_set_frequency(1000, 72000000); + systick_counter_enable(); + systick_interrupt_enable(); +} + +static void delay_ms(uint ms) +{ + u32 start_ticks = ms_ticks; + while (ms_ticks - start_ticks < ms) + ; +} + +static void usart_setup(void) +{ + usart_set_baudrate(USART1, 115200); + usart_set_databits(USART1, 8); + usart_set_stopbits(USART1, USART_STOPBITS_1); + usart_set_mode(USART1, USART_MODE_TX_RX); + usart_set_parity(USART1, USART_PARITY_NONE); + usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); + + usart_enable(USART1); +} + +// TIM4 will run on CPU clock, it will overflow with frequency 38 kHz (IR modulation frequency) +#define T4_CYCLE ((CPU_CLOCK_MHZ * 1000000 + 37999) / 38000) + +static byte bypass_active; +static byte fan_pwm; + +static void show_temperature(void) +{ + debug_putc('#'); + for (uint i=0; ds_sensors[i].address[0]; i++) { + debug_putc(' '); + int t = ds_sensors[i].current_temp; + if (t == DS_TEMP_UNKNOWN) + debug_puts("---.---"); + else + debug_printf("%3d.%03d", t / 1000, t % 1000); + } + debug_printf(" %d", bypass_active); + debug_printf(" %d", fan_pwm); + debug_puts("\r\n"); +} + +static void pwm_init(void) +{ + timer_set_prescaler(TIM4, 0); + timer_set_mode(TIM4, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); + timer_disable_preload(TIM4); + timer_set_period(TIM4, T4_CYCLE - 1); + + // 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); + + // PWM for controlling fan + timer_set_oc_mode(TIM4, TIM_OC1, TIM_OCM_PWM1); + timer_set_oc_value(TIM4, TIM_OC1, 0); + timer_set_oc_polarity_high(TIM4, TIM_OC1); + timer_enable_oc_output(TIM4, TIM_OC1); + + timer_enable_counter(TIM4); +} + +int main(void) +{ + clock_setup(); + gpio_setup(); + tick_setup(); + usart_setup(); + pwm_init(); + + debug_puts("Hello, world!\n"); + + ds_init(); + modbus_init(); + + byte cycles = 0; + for (;;) { + debug_led_toggle(); + delay_ms(100); + ds_step(); + modbus_loop(); + if (usart_get_flag(USART1, USART_SR_RXNE)) { + uint ch = usart_recv(USART1); + if (ch == 'B') { + bypass_active = 1; + gpio_set(GPIOC, GPIO15); // opto-coupler + gpio_clear(GPIOB, GPIO0); // LED + } else if (ch == 'b') { + bypass_active = 0; + gpio_clear(GPIOC, GPIO15); // opto-coupler + gpio_set(GPIOB, GPIO0); // LED + } else if (ch >= '0' && ch <= '9') { + fan_pwm = 3*(ch - '0') + 1; + /* + * ch pwm % + * 0 1 0 + * 1 4 0 + * 2 7 18 + * 3 10 31 + * 4 13 44 + * 5 16 57 + * 6 19 71 + * 7 22 84 + * 8 25 97 + * 9 28 100 + * + * % = pwm*4.389 - 12.723 + */ + timer_set_oc_value(TIM4, TIM_OC1, T4_CYCLE * fan_pwm / 256); + } + } + if (cycles++ >= 50) { + cycles = 0; + show_temperature(); + } + } + + return 0; +} + +/*** Modbus callbacks ***/ + +enum aircon_coils { + AIRCON_COIL_EXCHANGER_BYPASS, + AIRCON_COIL_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 + 4*DS_NUM_SENSORS, +}; + +enum aircon_holding_registers { + AIRCON_HREG_EXCHANGER_FAN, + AIRCON_HREG_REMOTE_CONTROL, + AIRCON_HREG_MAX, +}; + +bool modbus_check_discrete_input(u16 addr UNUSED) +{ + return false; +} + +bool modbus_get_discrete_input(u16 addr UNUSED) +{ + return false; +} + +bool modbus_check_coil(u16 addr) +{ + return addr < AIRCON_COIL_MAX; +} + +bool modbus_get_coil(u16 addr) +{ + switch (addr) { + case AIRCON_COIL_EXCHANGER_BYPASS: + return bypass_active; + default: + return false; + } +} + +void modbus_set_coil(u16 addr, bool value) +{ + switch (addr) { + case AIRCON_COIL_EXCHANGER_BYPASS: + bypass_active = value; + if (bypass_active) { + gpio_set(GPIOC, GPIO15); // opto-coupler + gpio_clear(GPIOB, GPIO0); // LED + } else { + gpio_clear(GPIOC, GPIO15); // opto-coupler + gpio_set(GPIOB, GPIO0); // LED + } + break; + default: + ; + } +} + +bool modbus_check_input_register(u16 addr) +{ + return (addr < AIRCON_IREG_MAX || addr >= AIRCON_IREG_DS_ID_MAX && addr < AIRCON_IREG_DS_ID_MAX); +} + +static const byte temp_sensor_addrs[][8] = { + { 0xAA, 0x36, 0x69, 0x19, 0x13, 0x02 }, + { 0x4A, 0x1B, 0x79, 0x97, 0x01, 0x03 }, + { 0xAA, 0xAC, 0xD9, 0x18, 0x13, 0x02 }, + { 0x30, 0x66, 0x79, 0x97, 0x08, 0x03 }, + { 0xAA, 0x02, 0x64, 0x19, 0x13, 0x02 }, +}; + +u16 modbus_get_input_register(u16 addr) +{ + if (addr <= AIRCON_IREG_TEMP_MIXED) { + byte i = 0; + while (i < DS_NUM_SENSORS && memcmp(ds_sensors[i].address, temp_sensor_addrs[addr], 8)) + i++; + if (i >= DS_NUM_SENSORS) + return 0x8000; + if (ds_sensors[i].current_temp == DS_TEMP_UNKNOWN) + return 0x8000; + return ds_sensors[i].current_temp & 0xffff; + } else if (addr >= AIRCON_IREG_DS_ID_BASE && addr < AIRCON_IREG_DS_ID_MAX) { + byte i = (addr - AIRCON_IREG_DS_ID_BASE) / 4; + byte j = (addr - AIRCON_IREG_DS_ID_BASE) % 4; + return get_u16_be(ds_sensors[i].address + 2*j); + } else { + return 0; + } +} + +bool modbus_check_holding_register(u16 addr) +{ + return addr < AIRCON_HREG_MAX; +} + +u16 modbus_get_holding_register(u16 addr) +{ + switch (addr) { + case AIRCON_HREG_EXCHANGER_FAN: + return fan_pwm; + case AIRCON_HREG_REMOTE_CONTROL: + default: + return 0; + } +} + +void modbus_set_holding_register(u16 addr, u16 value) +{ + switch (addr) { + case AIRCON_HREG_EXCHANGER_FAN: + fan_pwm = MIN(value, 255); + timer_set_oc_value(TIM4, TIM_OC1, T4_CYCLE * fan_pwm / 256); + break; + case AIRCON_HREG_REMOTE_CONTROL: + break; + default: + ; + } +} + +void modbus_ready_hook(void) +{ + // Frame LED off + gpio_set(GPIOB, GPIO1); +} + +void modbus_frame_start_hook(void) +{ + // Frame LED on + gpio_clear(GPIOB, GPIO1); +} + +const char * const modbus_id_strings[MODBUS_ID_MAX] = { + [MODBUS_ID_VENDOR_NAME] = "United Computer Wizards", + [MODBUS_ID_PRODUCT_CODE] = "42", + [MODBUS_ID_MAJOR_MINOR_REVISION] = "1.0", + [MODBUS_ID_VENDOR_URL] = "http://www.ucw.cz/", + [MODBUS_ID_PRODUCT_NAME] = "Magic Gadget", + [MODBUS_ID_USER_APP_NAME] = NULL, +}; diff --git a/aircon/kerm b/aircon/kerm deleted file mode 100644 index 988f567..0000000 --- a/aircon/kerm +++ /dev/null @@ -1,5 +0,0 @@ -set port /dev/ttyUSB0 -set speed 115200 -set flow-control none -set carrier-watch off -connect diff --git a/aircon/main.c b/aircon/main.c deleted file mode 100644 index 556ec89..0000000 --- a/aircon/main.c +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Air Conditioning Controller - * - * (c) 2019 Martin Mareš - */ - -#include "util.h" -#include "ds18b20.h" -#include "modbus.h" - -#include -#include -#include -#include -#include -#include - -#include - -static void clock_setup(void) -{ - rcc_clock_setup_in_hse_8mhz_out_72mhz(); - - rcc_periph_clock_enable(RCC_GPIOA); - rcc_periph_clock_enable(RCC_GPIOB); - rcc_periph_clock_enable(RCC_GPIOC); - rcc_periph_clock_enable(RCC_USART1); - rcc_periph_clock_enable(RCC_USART3); - rcc_periph_clock_enable(RCC_TIM1); - rcc_periph_clock_enable(RCC_TIM2); - rcc_periph_clock_enable(RCC_TIM3); - rcc_periph_clock_enable(RCC_TIM4); - rcc_periph_clock_enable(RCC_DMA1); - - rcc_periph_reset_pulse(RST_GPIOA); - rcc_periph_reset_pulse(RST_GPIOB); - rcc_periph_reset_pulse(RST_GPIOC); - rcc_periph_reset_pulse(RST_USART1); - rcc_periph_reset_pulse(RST_USART3); - rcc_periph_reset_pulse(RST_TIM1); - rcc_periph_reset_pulse(RST_TIM2); - rcc_periph_reset_pulse(RST_TIM3); - rcc_periph_reset_pulse(RST_TIM4); -} - -static void gpio_setup(void) -{ - // Switch JTAG off to free up pins - gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0); - - // PA6 = RS485 TX enable - gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO6); - gpio_clear(GPIOA, GPIO6); - - // PA7 = TIM3_CH2 for DS18B20 bus -- configured by ds18b20 library - - // PA9 = TXD1 for debugging console - gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9); - - // PA10 = RXD1 for debugging console - gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO10); - - // PB0 = bypass LED* - gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO0); - gpio_set(GPIOB, GPIO0); - - // PB1 = MODBUS frame LED* - gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO1); - gpio_set(GPIOB, GPIO1); - - // PB6 = TIM4_CH1 fan control opto-coupler - gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO6); - - // PB7 = TIM4_CH2 IR LED - gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO7); - - // PB10 = TXD3 for RS485 - gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO10); - - // PB11 = RXD3 for RS485 - gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO11); - - // PC13 = BluePill LED - gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); - gpio_clear(GPIOC, GPIO13); - - // PC15 = bypass opto-coupler - gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO15); - gpio_clear(GPIOC, GPIO15); -} - -static volatile u32 ms_ticks; - -void sys_tick_handler(void) -{ - ms_ticks++; -} - -static void tick_setup(void) -{ - systick_set_frequency(1000, 72000000); - systick_counter_enable(); - systick_interrupt_enable(); -} - -static void delay_ms(uint ms) -{ - u32 start_ticks = ms_ticks; - while (ms_ticks - start_ticks < ms) - ; -} - -static void usart_setup(void) -{ - usart_set_baudrate(USART1, 115200); - usart_set_databits(USART1, 8); - usart_set_stopbits(USART1, USART_STOPBITS_1); - usart_set_mode(USART1, USART_MODE_TX_RX); - usart_set_parity(USART1, USART_PARITY_NONE); - usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); - - usart_enable(USART1); -} - -// TIM4 will run on CPU clock, it will overflow with frequency 38 kHz (IR modulation frequency) -#define T4_CYCLE ((CPU_CLOCK_MHZ * 1000000 + 37999) / 38000) - -static byte bypass_active; -static byte fan_pwm; - -static void show_temperature(void) -{ - debug_putc('#'); - for (uint i=0; ds_sensors[i].address[0]; i++) { - debug_putc(' '); - int t = ds_sensors[i].current_temp; - if (t == DS_TEMP_UNKNOWN) - debug_puts("---.---"); - else - debug_printf("%3d.%03d", t / 1000, t % 1000); - } - debug_printf(" %d", bypass_active); - debug_printf(" %d", fan_pwm); - debug_puts("\r\n"); -} - -static void pwm_init(void) -{ - timer_set_prescaler(TIM4, 0); - timer_set_mode(TIM4, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); - timer_disable_preload(TIM4); - timer_set_period(TIM4, T4_CYCLE - 1); - - // 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); - - // PWM for controlling fan - timer_set_oc_mode(TIM4, TIM_OC1, TIM_OCM_PWM1); - timer_set_oc_value(TIM4, TIM_OC1, 0); - timer_set_oc_polarity_high(TIM4, TIM_OC1); - timer_enable_oc_output(TIM4, TIM_OC1); - - timer_enable_counter(TIM4); -} - -int main(void) -{ - clock_setup(); - gpio_setup(); - tick_setup(); - usart_setup(); - pwm_init(); - - debug_puts("Hello, world!\n"); - - ds_init(); - modbus_init(); - - byte cycles = 0; - for (;;) { - debug_led_toggle(); - delay_ms(100); - ds_step(); - modbus_loop(); - if (usart_get_flag(USART1, USART_SR_RXNE)) { - uint ch = usart_recv(USART1); - if (ch == 'B') { - bypass_active = 1; - gpio_set(GPIOC, GPIO15); // opto-coupler - gpio_clear(GPIOB, GPIO0); // LED - } else if (ch == 'b') { - bypass_active = 0; - gpio_clear(GPIOC, GPIO15); // opto-coupler - gpio_set(GPIOB, GPIO0); // LED - } else if (ch >= '0' && ch <= '9') { - fan_pwm = 3*(ch - '0') + 1; - /* - * ch pwm % - * 0 1 0 - * 1 4 0 - * 2 7 18 - * 3 10 31 - * 4 13 44 - * 5 16 57 - * 6 19 71 - * 7 22 84 - * 8 25 97 - * 9 28 100 - * - * % = pwm*4.389 - 12.723 - */ - timer_set_oc_value(TIM4, TIM_OC1, T4_CYCLE * fan_pwm / 256); - } - } - if (cycles++ >= 50) { - cycles = 0; - show_temperature(); - } - } - - return 0; -} - -/*** Modbus callbacks ***/ - -enum aircon_coils { - AIRCON_COIL_EXCHANGER_BYPASS, - AIRCON_COIL_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 + 4*DS_NUM_SENSORS, -}; - -enum aircon_holding_registers { - AIRCON_HREG_EXCHANGER_FAN, - AIRCON_HREG_REMOTE_CONTROL, - AIRCON_HREG_MAX, -}; - -bool modbus_check_discrete_input(u16 addr UNUSED) -{ - return false; -} - -bool modbus_get_discrete_input(u16 addr UNUSED) -{ - return false; -} - -bool modbus_check_coil(u16 addr) -{ - return addr < AIRCON_COIL_MAX; -} - -bool modbus_get_coil(u16 addr) -{ - switch (addr) { - case AIRCON_COIL_EXCHANGER_BYPASS: - return bypass_active; - default: - return false; - } -} - -void modbus_set_coil(u16 addr, bool value) -{ - switch (addr) { - case AIRCON_COIL_EXCHANGER_BYPASS: - bypass_active = value; - if (bypass_active) { - gpio_set(GPIOC, GPIO15); // opto-coupler - gpio_clear(GPIOB, GPIO0); // LED - } else { - gpio_clear(GPIOC, GPIO15); // opto-coupler - gpio_set(GPIOB, GPIO0); // LED - } - break; - default: - ; - } -} - -bool modbus_check_input_register(u16 addr) -{ - return (addr < AIRCON_IREG_MAX || addr >= AIRCON_IREG_DS_ID_MAX && addr < AIRCON_IREG_DS_ID_MAX); -} - -static const byte temp_sensor_addrs[][8] = { - { 0xAA, 0x36, 0x69, 0x19, 0x13, 0x02 }, - { 0x4A, 0x1B, 0x79, 0x97, 0x01, 0x03 }, - { 0xAA, 0xAC, 0xD9, 0x18, 0x13, 0x02 }, - { 0x30, 0x66, 0x79, 0x97, 0x08, 0x03 }, - { 0xAA, 0x02, 0x64, 0x19, 0x13, 0x02 }, -}; - -u16 modbus_get_input_register(u16 addr) -{ - if (addr <= AIRCON_IREG_TEMP_MIXED) { - byte i = 0; - while (i < DS_NUM_SENSORS && memcmp(ds_sensors[i].address, temp_sensor_addrs[addr], 8)) - i++; - if (i >= DS_NUM_SENSORS) - return 0x8000; - if (ds_sensors[i].current_temp == DS_TEMP_UNKNOWN) - return 0x8000; - return ds_sensors[i].current_temp & 0xffff; - } else if (addr >= AIRCON_IREG_DS_ID_BASE && addr < AIRCON_IREG_DS_ID_MAX) { - byte i = (addr - AIRCON_IREG_DS_ID_BASE) / 4; - byte j = (addr - AIRCON_IREG_DS_ID_BASE) % 4; - return get_u16_be(ds_sensors[i].address + 2*j); - } else { - return 0; - } -} - -bool modbus_check_holding_register(u16 addr) -{ - return addr < AIRCON_HREG_MAX; -} - -u16 modbus_get_holding_register(u16 addr) -{ - switch (addr) { - case AIRCON_HREG_EXCHANGER_FAN: - return fan_pwm; - case AIRCON_HREG_REMOTE_CONTROL: - default: - return 0; - } -} - -void modbus_set_holding_register(u16 addr, u16 value) -{ - switch (addr) { - case AIRCON_HREG_EXCHANGER_FAN: - fan_pwm = MIN(value, 255); - timer_set_oc_value(TIM4, TIM_OC1, T4_CYCLE * fan_pwm / 256); - break; - case AIRCON_HREG_REMOTE_CONTROL: - break; - default: - ; - } -} - -void modbus_ready_hook(void) -{ - // Frame LED off - gpio_set(GPIOB, GPIO1); -} - -void modbus_frame_start_hook(void) -{ - // Frame LED on - gpio_clear(GPIOB, GPIO1); -} - -const char * const modbus_id_strings[MODBUS_ID_MAX] = { - [MODBUS_ID_VENDOR_NAME] = "United Computer Wizards", - [MODBUS_ID_PRODUCT_CODE] = "42", - [MODBUS_ID_MAJOR_MINOR_REVISION] = "1.0", - [MODBUS_ID_VENDOR_URL] = "http://www.ucw.cz/", - [MODBUS_ID_PRODUCT_NAME] = "Magic Gadget", - [MODBUS_ID_USER_APP_NAME] = NULL, -};