#include "drv/ir/ir.h" #include "arch/stm32l4xxx/peripherals/exti.h" #include "arch/stm32l4xxx/peripherals/irq.h" #include "arch/stm32l4xxx/peripherals/rcc.h" #include "arch/stm32l4xxx/peripherals/syscfg.h" #include "arch/stm32l4xxx/peripherals/tim.h" #include "kern/gpio/gpio_manager.h" #include "kern/mem.h" #include "kern/log.h" #include "kern/panic.h" #include "shared/linked_list.h" typedef void (*ir_callback_t)(const ir_code_t*); LINKED_LIST_DECL(ir_callback_t); LINKED_LIST_IMPL(ir_callback_t); static linked_list_t(ir_callback_t) callbacks; static ir_code_t read_into; void on_exti9_5() { EXTI.p_r1 |= 1 << 6; /* The IR pin has been activated. */ if (read_into.n == 0) { /* Starting fresh, start the timer. */ regset(TIM2.c_r1, tim_cen, 1); read_into.ts[read_into.n ++] = 0; } else { uint32_t ts = TIM2.cnt; if (read_into.n < 64) { read_into.ts[read_into.n++] = ts; } } } typedef enum { PARITY_LOW = 0, PARITY_HIGH = 1 } ir_parity_t; static ir_parity_t ir_discover_parity(const ir_code_t* code) { uint32_t max_even = 0; uint32_t min_even = (uint32_t) -1; uint32_t max_odd = 0; uint32_t min_odd = (uint32_t) -1; for (int i = 3; i < code->n; ++ i) { uint32_t n = code->ts[i] - code->ts[i-1]; if (i % 2 == 0) { if (n < min_even) { min_even = n; } else if (n > max_even) { max_even = n; } } else { if (n < min_odd) { min_odd = n; } else if (n > max_odd) { max_odd = n; } } } if (max_even - min_even > max_odd - min_odd) { return PARITY_LOW; } else { return PARITY_HIGH; } } int ir_generic_decode( const ir_code_t* code, uint32_t* out_) { if (code->n < 5) { return 0; } uint32_t min = (uint32_t) -1; uint32_t max = 0; uint32_t start = 4 + ir_discover_parity(code); for (int i = start; i < code->n; i += 2) { uint32_t n = code->ts[i] - code->ts[i-1]; if (n > max) { max = n; } else if (n < min) { min = n; } } uint32_t mid = (max + min) / 2; uint32_t out = 0; uint32_t mask = 0x80000000; for (int i = start; i < code->n; i += 2) { if ((code->ts[i] - code->ts[i-1]) >= mid) { out |= mask; } mask >>= 1; } *out_ = out; return 1; } void on_tim2() { if (regget(TIM2.s_r, tim_uif)) { regset(TIM2.s_r, tim_uif, 0); } /* The timer has run out. */ linked_list_foreach(callbacks, cb) { cb(&read_into); } read_into.n = 0; } void add_ir_callback(ir_callback_t callback) { linked_list_push_front(ir_callback_t)(&callbacks, callback); } void ir_begin_listen() { int ec; gpio_pin_opts_t opts = DEFAULT_GPIO_OPTS_INPUT; opts.pull_dir = GPIO_PULL_DIR_NONE; gpio_reserved_pin_t pb6 = reserve_gpio_pin(GPIO_PIN_PB6, &opts, &ec); regset(RCC.apb1en1_r, rcc_tim2en, 1); regset(RCC.apb2en_r, rcc_syscfgen, 1); enable_interrupt(IRQ_TIM2); enable_interrupt(IRQ_EXTI9_5); /* Configure the timer. */ TIM2.psc = 80; /* Counts every microsecond. */ TIM2.ar_r = 50000; /* Stop after 50ms. */ regset(TIM2.die_r, tim_uie, 1); /* Enable interrupts. */ regset(TIM2.c_r1, tim_opm, 1); /* Set one-pulse-mode. */ /* Configure the external interrupts. */ regset(SYSCFG.extic_r2, syscfg_exti6, 1 /* Port B. */); regset(EXTI.im_r1, exti_im_n(6), 1); /* Enable the EXTI interrupt. */ regset(EXTI.fts_r1, exti_ft_n(6), 1); /* Enable for falling edge. */ regset(EXTI.rts_r1, exti_rt_n(6), 1); /* Enable for rising edge. */ if (ec) { panic("Unable to reserve GPIO pin ec=%d\n", ec); } }