From 148ef3d2ec9bae10e710ff64aa41f89650e05113 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Thu, 18 Jul 2019 01:27:28 +0200 Subject: [PATCH] Aircon: Remote control --- aircon/firmware/main.c | 143 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 135 insertions(+), 8 deletions(-) diff --git a/aircon/firmware/main.c b/aircon/firmware/main.c index 2e8881b..b4650bf 100644 --- a/aircon/firmware/main.c +++ b/aircon/firmware/main.c @@ -18,7 +18,9 @@ #include -static void clock_setup(void) +static void rc_init(void); + +static void clock_init(void) { rcc_clock_setup_in_hse_8mhz_out_72mhz(); @@ -44,7 +46,7 @@ static void clock_setup(void) 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); @@ -97,7 +99,7 @@ void sys_tick_handler(void) ms_ticks++; } -static void tick_setup(void) +static void tick_init(void) { systick_set_frequency(1000, 72000000); systick_counter_enable(); @@ -111,7 +113,7 @@ static void delay_ms(uint ms) ; } -static void usart_setup(void) +static void usart_init(void) { usart_set_baudrate(USART1, 115200); usart_set_databits(USART1, 8); @@ -129,6 +131,7 @@ static void usart_setup(void) static byte bypass_active; static byte fan_pwm; +// FIXME static void show_temperature(void) { debug_putc('#'); @@ -170,11 +173,12 @@ static void pwm_init(void) int main(void) { - clock_setup(); - gpio_setup(); - tick_setup(); - usart_setup(); + clock_init(); + gpio_init(); + tick_init(); + usart_init(); pwm_init(); + rc_init(); debug_puts("Hello, world!\n"); @@ -226,6 +230,126 @@ int main(void) return 0; } +/*** 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 +}; + +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*$", +}; + +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; + + const char *s = strchr(rc_keys, key); + if (!s) + return false; + rc_pending = key; + rc_pattern_pos = rc_patterns[s - rc_keys]; + + 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) @@ -317,6 +441,7 @@ u16 modbus_get_holding_register(u16 addr) case AIRCON_HREG_EXCHANGER_FAN: return fan_pwm; case AIRCON_HREG_REMOTE_CONTROL: + return rc_pending; default: return 0; } @@ -330,6 +455,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: + if (!rc_send(value)) + modbus_slave_error(); break; default: ; -- 2.39.2