1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
#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/log.h"
#include "kern/mem.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);
}
}
|