diff options
Diffstat (limited to 'src/drv/ir')
-rw-r--r-- | src/drv/ir/control.c | 43 | ||||
-rw-r--r-- | src/drv/ir/ir.c | 160 |
2 files changed, 203 insertions, 0 deletions
diff --git a/src/drv/ir/control.c b/src/drv/ir/control.c new file mode 100644 index 0000000..6be2cf1 --- /dev/null +++ b/src/drv/ir/control.c @@ -0,0 +1,43 @@ +#include "drv/ir/control.h" + +#include "drv/ir/ir.h" +#include "shared/map.h" + +typedef struct { + void* closure; + void (*fn)(uint32_t code, void* closure); +} ir_code_listener_t; + +#define integer_cmp_(a, b) (a - b) +MAP_DECL(uint32_t, ir_code_listener_t); +MAP_IMPL(uint32_t, ir_code_listener_t, integer_cmp_, null_dtor); + +static map_t(uint32_t, ir_code_listener_t) * listeners; + +static void ir_callback(const ir_code_t* ir) +{ + uint32_t code; + if (ir_generic_decode(ir, &code)) { + ir_code_listener_t* l = + map_get(uint32_t, ir_code_listener_t)(listeners, code); + + if (l) { + l->fn(code, l->closure); + } + } +} + +void add_ir_code_callback_( + uint32_t code, void (*fn)(uint32_t, void*), void* closure) +{ + ir_code_listener_t l; + l.fn = fn; + l.closure = closure; + map_put(uint32_t, ir_code_listener_t)(listeners, code, l); +} + +void enable_ir_control() +{ + listeners = map_new(uint32_t, ir_code_listener_t)(); + add_ir_callback(ir_callback); +} diff --git a/src/drv/ir/ir.c b/src/drv/ir/ir.c new file mode 100644 index 0000000..c84417d --- /dev/null +++ b/src/drv/ir/ir.c @@ -0,0 +1,160 @@ +#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); + } +} |