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