]> vault307.fbx.one Git - micorpython_ir.git/blob - ir_tx.py
a1cc9e9859d03a4d2d64b1b7da711f8eb7f79ceb
[micorpython_ir.git] / ir_tx.py
1 # ir_tx.py Nonblocking IR blaster
2 # Runs on Pyboard D or Pyboard 1.x only (not Pyboard Lite)
3
4 # Released under the MIT License (MIT). See LICENSE.
5
6 # Copyright (c) 2020 Peter Hinch
7
8 from pyb import Pin, Timer
9 from micropython import const
10 from array import array
11 import micropython
12
13 # micropython.alloc_emergency_exception_buf(100)
14
15 # Common
16 _SPACE = const(0) # Or 100. Depends on wiring: 0 assumes logic 0 turns IR off.
17 _STOP = const(0) # End of data
18 # NEC
19 _TBURST = const(563)
20 _T_ONE = const(1687)
21 # RC5
22 _T_RC5 = const(889) # Time for pulse of carrier
23 # RC6_M0
24 _T_RC6 = const(444)
25 _T2_RC6 = const(889)
26
27 # IR abstract base class. Array holds periods in μs between toggling 36/38KHz
28 # carrier on or off. Physical transmission occurs in an ISR context controlled
29 # by timer 2 and timer 5. See README.md for details of operation.
30 class IR:
31
32 def __init__(self, pin, freq, asize, duty, verbose):
33 tim = Timer(2, freq=freq) # Timer 2/pin produces 36/38KHz carrier
34 self._ch = tim.channel(1, Timer.PWM, pin=pin)
35 self._ch.pulse_width_percent(_SPACE) # Turn off IR LED
36 self._duty = duty if not _SPACE else (100 - duty)
37 self._tim = Timer(5) # Timer 5 controls carrier on/off times
38 self._tcb = self.cb # Pre-allocate
39 self.verbose = verbose
40 self.arr = array('H', 0 for _ in range(asize)) # on/off times (μs)
41 self.carrier = False # Notional carrier state while encoding biphase
42 self.aptr = 0 # Index into array
43
44 # Before populating array, zero pointer, set notional carrier state (off).
45 def transmit(self, addr, data, toggle=0): # NEC: toggle is unused
46 self.aptr = 0 # Inital conditions for tx: index into array
47 self.carrier = False
48 self.tx(addr, data, toggle)
49 self.append(_STOP)
50 self.aptr = 0 # Reset pointer
51 self.cb(self._tim) # Initiate physical transmission.
52
53 def cb(self, t): # T5 callback, generate a carrier mark or space
54 t.deinit()
55 p = self.aptr
56 v = self.arr[p]
57 if v == _STOP:
58 self._ch.pulse_width_percent(_SPACE) # Turn off IR LED.
59 return
60 self._ch.pulse_width_percent(_SPACE if p & 1 else self._duty)
61 self._tim.init(prescaler=84, period=v, callback=self._tcb)
62 self.aptr += 1
63
64 def append(self, *times): # Append one or more time peiods to .arr
65 for t in times:
66 self.arr[self.aptr] = t
67 self.aptr += 1
68 self.carrier = not self.carrier # Keep track of carrier state
69 self.verbose and print('append', t, 'carrier', self.carrier)
70
71 def add(self, t): # Increase last time value
72 self.verbose and print('add', t)
73 self.arr[self.aptr - 1] += t # Carrier unaffected
74
75 # NEC protocol
76 class NEC(IR):
77
78 def __init__(self, pin, freq=38000, verbose=False): # NEC specifies 38KHz
79 super().__init__(pin, freq, 68, 50, verbose)
80
81 def _bit(self, b):
82 self.append(_TBURST, _T_ONE if b else _TBURST)
83
84 def tx(self, addr, data, _): # Ignore toggle
85 self.append(9000, 4500)
86 if addr < 256: # Short address: append complement
87 addr |= ((addr ^ 0xff) << 8)
88 for _ in range(16):
89 self._bit(addr & 1)
90 addr >>= 1
91 data |= ((data ^ 0xff) << 8)
92 for _ in range(16):
93 self._bit(data & 1)
94 data >>= 1
95 self.append(_TBURST)
96
97 def repeat(self):
98 self.aptr = 0
99 self.append(9000, 2250, _TBURST, _STOP)
100 self.aptr = 0 # Reset pointer
101 self.cb(self._tim) # Initiate physical transmission.
102
103
104 # Philips RC5 protocol
105 class RC5(IR):
106
107 def __init__(self, pin, freq=36000, verbose=False):
108 super().__init__(pin, freq, 28, 30, verbose)
109
110 def tx(self, addr, data, toggle):
111 d = (data & 0x3f) | ((addr & 0x1f) << 6) | ((data & 0x40) << 6) | ((toggle & 1) << 11)
112 self.verbose and print(bin(d))
113 mask = 0x2000
114 while mask:
115 if mask == 0x2000:
116 self.append(_T_RC5)
117 else:
118 bit = bool(d & mask)
119 if bit ^ self.carrier:
120 self.add(_T_RC5)
121 self.append(_T_RC5)
122 else:
123 self.append(_T_RC5, _T_RC5)
124 mask >>= 1
125
126 # Philips RC6 mode 0 protocol
127 class RC6_M0(IR):
128
129 def __init__(self, pin, freq=36000, verbose=False):
130 super().__init__(pin, freq, 44, 30, verbose)
131
132 def tx(self, addr, data, toggle):
133 # leader, 1, 0, 0, 0
134 self.append(2666, _T2_RC6, _T_RC6, _T2_RC6, _T_RC6, _T_RC6, _T_RC6, _T_RC6, _T_RC6)
135 # Append a single bit of twice duration
136 if toggle:
137 self.add(_T2_RC6)
138 self.append(_T2_RC6)
139 else:
140 self.append(_T2_RC6, _T2_RC6)
141 d = (data & 0xff) | ((addr & 0xff) << 8)
142 mask = 0x8000
143 self.verbose and print('toggle', toggle, self.carrier, bool(d & mask))
144 while mask:
145 bit = bool(d & mask)
146 if bit ^ self.carrier:
147 self.append(_T_RC6, _T_RC6)
148 else:
149 self.add(_T_RC6)
150 self.append(_T_RC6)
151 mask >>= 1