+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 ***/