+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_XXX_A,
+ RC_XXX_B,
+ 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*",
+ [RC_XXX_A] = "^#*A*B*A*A*B*A*B*B*A*A*B*A*A*A*A*A*A*B*B*B*B*A*A*A*B*A*A*A*A*B*B*B*",
+ [RC_XXX_B] = "^#*A*B*A*A*B*A*B*B*A*A*B*A*A*A*A*A*B*B*B*B*B*A*A*A*A*A*A*A*A*B*B*B*",
+};
+
+// FIXME
+static const char rc_keys[] = "aufhtmdslp12";
+
+// 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 void rc_setup(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_PWM1);
+ 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, T4_CYCLE / 4);
+ timer_set_oc_polarity_high(TIM4, TIM_OC1);
+ timer_enable_oc_output(TIM4, TIM_OC1);
+
+ timer_enable_counter(TIM4);
+
+ // 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 const char *rc_pattern_pos;
+
+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;
+ default:
+ // End of transmission
+ gpio_set(GPIOC, GPIO13); // FIXME
+ rc_pattern_pos = NULL;
+ val = 0;
+ duration = 10000;
+ }
+
+ 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 void rc_send(uint key)
+{
+ rc_pattern_pos = rc_patterns[key];
+
+ gpio_clear(GPIOC, GPIO13);
+
+ timer_set_period(TIM1, 1);
+ timer_generate_event(TIM1, TIM_EGR_UG);
+ timer_enable_counter(TIM1);
+}
+