|
@@ -5,49 +5,62 @@ from multiprocessing import Process, Pipe
|
5
|
5
|
# Message header id's for messages passed between the application
|
6
|
6
|
# process and the microcontroller and video processes
|
7
|
7
|
|
8
|
|
-TinyOpD = {
|
9
|
|
- 'setPwmOp': 0,
|
10
|
|
- 'noteOnVelOp': 1,
|
11
|
|
- 'noteOnUsecOp': 2,
|
12
|
|
- 'noteOffOp': 3,
|
13
|
|
- 'readOp': 4,
|
14
|
|
- 'writeOp': 5
|
15
|
|
-}
|
|
8
|
+class TinyOp(Enum):
|
|
9
|
+ setPwmOp = 0
|
|
10
|
+ noteOnVelOp = 1
|
|
11
|
+ noteOnUsecOp = 2
|
|
12
|
+ noteOffOp = 3
|
|
13
|
+ setReadAddr = 4
|
|
14
|
+ writeOp = 5
|
|
15
|
+
|
16
|
16
|
|
17
|
17
|
class TinyRegAddr(Enum):
|
18
|
|
- kRdRegAddrAddr = 0,
|
19
|
|
- kRdTableAddrAddr = 1,
|
20
|
|
- kRdEEAddrAddr = 2,
|
21
|
|
- kRdSrcAddr = 3,
|
22
|
|
- kWrRegAddrAddr = 4,
|
23
|
|
- kWrTableAddrAddr = 5,
|
24
|
|
- kWrEEAddrAddr = 6,
|
25
|
|
- kWrDstAddr = 7,
|
26
|
|
- kTmrCoarseAddr = 8,
|
27
|
|
- kTmrFineAddr = 9,
|
28
|
|
- kTmrPrescaleAddr = 10,
|
29
|
|
- kPwmEnableAddr = 11,
|
30
|
|
- kPwmDutyAddr = 12,
|
31
|
|
- kPwmFreqAddr = 13,
|
32
|
|
- kModeAddr = 14,
|
33
|
|
- kStateAddr = 15,
|
34
|
|
- kErrorCodeAddr = 16
|
|
18
|
+ kRdRegAddrAddr = 0
|
|
19
|
+ kRdTableAddrAddr = 1
|
|
20
|
+ kRdEEAddrAddr = 2
|
|
21
|
+ kRdSrcAddr = 3
|
|
22
|
+ kWrRegAddrAddr = 4
|
|
23
|
+ kWrTableAddrAddr = 5
|
|
24
|
+ kWrEEAddrAddr = 6
|
|
25
|
+ kWrDstAddr = 7
|
|
26
|
+ kTmrCoarseAddr = 8
|
|
27
|
+ kTmrFineAddr = 9
|
|
28
|
+ kTmrPrescaleAddr = 10
|
|
29
|
+ kPwmDutyAddr = 11
|
|
30
|
+ kPwmFreqAddr = 12
|
|
31
|
+ kModeAddr = 13
|
|
32
|
+ kStateAddr = 14
|
|
33
|
+ kErrorCodeAddr = 15
|
35
|
34
|
|
36
|
35
|
class TinyConst(Enum):
|
37
|
|
- kRdRegSrcId = TinyRegAddr.kRdRegAddrAddr, # 0
|
38
|
|
- kRdTableSrcId = TinyRegAddr.kRdTableAddrAddr, # 1
|
39
|
|
- kRdEESrcId = TinyRegAddr.kRdEEAddrAddr # 2
|
|
36
|
+ kRdRegSrcId = TinyRegAddr.kRdRegAddrAddr # 0
|
|
37
|
+ kRdTableSrcId = TinyRegAddr.kRdTableAddrAddr # 1
|
|
38
|
+ kRdEESrcId = TinyRegAddr.kRdEEAddrAddr # 2
|
40
|
39
|
|
41
|
|
- kWrRegDstId = TinyRegAddr.kWrRegAddrAddr, # 4
|
42
|
|
- kWrTableDstId = TinyRegAddr.kWrTableAddrAddr, # 5
|
43
|
|
- kWrEEDstId = TinyRegAddr.kWrEEAddrAddr, # 6
|
44
|
|
- kWrAddrFl = 0x08, # first avail bit above kWrEEAddr
|
|
40
|
+ kWrRegDstId = TinyRegAddr.kWrRegAddrAddr # 4
|
|
41
|
+ kWrTableDstId = TinyRegAddr.kWrTableAddrAddr # 5
|
|
42
|
+ kWrEEDstId = TinyRegAddr.kWrEEAddrAddr # 6
|
|
43
|
+ kWrAddrFl = 0x08 # first avail bit above kWrEEAddr
|
45
|
44
|
|
46
|
45
|
class SerialMsgId(Enum):
|
47
|
46
|
QUIT_MSG = 0xffff
|
48
|
47
|
DATA_MSG = 0xfffe
|
49
|
48
|
|
|
49
|
+class Result(object):
|
|
50
|
+ def __init__( self, value=None, msg=None ):
|
|
51
|
+ self.value = value
|
|
52
|
+ self.msg = msg
|
50
|
53
|
|
|
54
|
+ def set_error( self, msg ):
|
|
55
|
+ if self.msg is None:
|
|
56
|
+ self.msg = ""
|
|
57
|
+
|
|
58
|
+ self.msg += " " + msg
|
|
59
|
+
|
|
60
|
+ def __bool__( self ):
|
|
61
|
+ return self.msg is None
|
|
62
|
+
|
|
63
|
+
|
51
|
64
|
def _serial_process_func( serial_dev, baud, pipe ):
|
52
|
65
|
|
53
|
66
|
reset_N = 0
|
|
@@ -100,7 +113,8 @@ class SerialProcess(Process):
|
100
|
113
|
self.parent_end, child_end = Pipe()
|
101
|
114
|
super(SerialProcess, self).__init__(target=_serial_process_func, name="Serial", args=(serial_dev,serial_baud,child_end,))
|
102
|
115
|
self.doneFl = False
|
103
|
|
-
|
|
116
|
+
|
|
117
|
+
|
104
|
118
|
def quit(self):
|
105
|
119
|
# send quit msg to the child process
|
106
|
120
|
self.parent_end.send((SerialMsgId.QUIT_MSG,0))
|
|
@@ -108,6 +122,7 @@ class SerialProcess(Process):
|
108
|
122
|
def send(self,msg_id,value):
|
109
|
123
|
# send a msg to the child process
|
110
|
124
|
self.parent_end.send((msg_id,value))
|
|
125
|
+ return Result()
|
111
|
126
|
|
112
|
127
|
def recv(self):
|
113
|
128
|
#
|
|
@@ -127,7 +142,7 @@ class SerialProcess(Process):
|
127
|
142
|
|
128
|
143
|
|
129
|
144
|
class Picadae:
|
130
|
|
- def __init__( self, key_mapL, i2c_base_addr=1, serial_dev='/dev/ttyACM0', serial_baud=38400 ):
|
|
145
|
+ def __init__( self, key_mapL, i2c_base_addr=21, serial_dev='/dev/ttyACM0', serial_baud=38400, prescaler_usec=16 ):
|
131
|
146
|
"""
|
132
|
147
|
key_mapL = [{ index, board, ch, type, midi, class }]
|
133
|
148
|
serial_dev = /dev/ttyACM0
|
|
@@ -137,65 +152,86 @@ class Picadae:
|
137
|
152
|
self.serialProc = SerialProcess( serial_dev, serial_baud )
|
138
|
153
|
self.keyMapD = { d['midi']:d for d in key_mapL }
|
139
|
154
|
self.i2c_base_addr = i2c_base_addr
|
140
|
|
- self.prescaler_usec = 32
|
|
155
|
+ self.prescaler_usec = prescaler_usec
|
141
|
156
|
|
142
|
157
|
self.serialProc.start()
|
143
|
158
|
|
144
|
159
|
def close( self ):
|
145
|
160
|
self.serialProc.quit()
|
146
|
161
|
|
147
|
|
- def write( self, i2c_addr, reg_addr, byteL ):
|
148
|
|
- return self._send( 'w', i2c_addr, reg_addr, [ len(byteL) ] + byteL )
|
149
|
|
-
|
150
|
|
- def set_read_addr( self, i2c_addr, src, addr ):
|
151
|
|
- return self. write(i2c_addr, TinyOpD['readOp'], src, addr )
|
152
|
|
-
|
153
|
|
- def set_reg_read_addr( self, i2c_addr, addr ):
|
154
|
|
- return self.set_read_addr(i2c_addr, TinyRegAddr.kRdRegAddrAddr, addr )
|
|
162
|
+ def wait_for_serial_sync(self, timeoutMs=10000):
|
155
|
163
|
|
156
|
|
- def set_table_read_addr( self, i2c_addr, addr ):
|
157
|
|
- return self.set_read_addr(i2c_addr, TinyRegAddr.kRdTableAddrAddr, addr )
|
158
|
|
-
|
159
|
|
- def set_eeprom_read_addr( self, i2c_addr, addr ):
|
160
|
|
- return self.set_read_addr(i2c_addr, TinyRegAddr.kRdEEAddrAddr, addr )
|
161
|
|
-
|
162
|
|
- def read_request( self, i2c_addr, reg_addr, argL ):
|
163
|
|
- return self._send( 'r', i2c_addr, reg_addr, [ byteOutN, len(argL) ] + argL )
|
|
164
|
+ # wait for the letter 'a' to come back from the serial port
|
|
165
|
+ result = self.block_on_serial_read(1,timeoutMs)
|
164
|
166
|
|
165
|
|
- def read_poll( self ):
|
166
|
|
- return self.serialProc.recv()
|
|
167
|
+ if result and len(result.value)>0 and result.value[0] == ord('a'):
|
|
168
|
+ pass
|
|
169
|
+ else:
|
|
170
|
+ result.set_error("Serial sync failed.")
|
167
|
171
|
|
168
|
|
- def read_block( self, i2c_addr, reg_addr, argL, byteOutN, time_out_ms ):
|
|
172
|
+ return result
|
169
|
173
|
|
170
|
|
- self.read_request( self, i2c_addr, reg_addr, argL, byteOutN )
|
|
174
|
+ def write( self, i2c_addr, reg_addr, byteL ):
|
|
175
|
+ return self._send( 'w', i2c_addr, reg_addr, [ len(byteL) ] + byteL )
|
171
|
176
|
|
172
|
|
- ts = datetime.datetime.now() + datetime.timeDelta(milliseconds=time_out_ms)
|
|
177
|
+ def set_read_addr( self, i2c_addr, mem_id, addr ):
|
|
178
|
+ return self. write(i2c_addr, TinyOp.setReadAddr.value,[ mem_id, addr ])
|
|
179
|
+
|
|
180
|
+ def read_request( self, i2c_addr, reg_addr, byteOutN ):
|
|
181
|
+ return self._send( 'r', i2c_addr, reg_addr,[ byteOutN ] )
|
173
|
182
|
|
|
183
|
+ def block_on_serial_read( self, byteOutN, time_out_ms=250 ):
|
|
184
|
+
|
|
185
|
+ ts = datetime.datetime.now() + datetime.timedelta(milliseconds=time_out_ms)
|
174
|
186
|
retL = []
|
|
187
|
+
|
175
|
188
|
while datetime.datetime.now() < ts and len(retL) < byteOutN:
|
176
|
|
-
|
|
189
|
+
|
|
190
|
+ # If a value is available at the serial port return is otherwise return None.
|
177
|
191
|
x = self.serialProc.recv()
|
178
|
|
- if x is not None:
|
179
|
|
- retL.append(x)
|
|
192
|
+
|
|
193
|
+ if x is not None and x[0] == SerialMsgId.DATA_MSG:
|
|
194
|
+ for b in x[1]:
|
|
195
|
+ retL.append(int(b))
|
180
|
196
|
|
181
|
197
|
time.sleep(0.01)
|
182
|
|
-
|
183
|
|
- return retL
|
|
198
|
+
|
|
199
|
+ result = Result(value=retL)
|
|
200
|
+
|
|
201
|
+ if len(retL) < byteOutN:
|
|
202
|
+ result.set_error("Serial port time out on read.")
|
|
203
|
+
|
|
204
|
+ return result
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+ def block_on_picadae_read( self, i2c_addr, mem_id, reg_addr, argL, byteOutN, time_out_ms ):
|
|
209
|
+
|
|
210
|
+ result = self.set_read_addr( i2c_addr, mem_id, reg_addr )
|
|
211
|
+
|
|
212
|
+ if result:
|
|
213
|
+ result = self.read_request( i2c_addr, TinyOp.setReadAddr.value, byteOutN )
|
|
214
|
+
|
|
215
|
+ if result:
|
|
216
|
+ result = self.block_on_serial_read( byteOutN, time_out_ms )
|
|
217
|
+
|
|
218
|
+ return result
|
184
|
219
|
|
185
|
220
|
|
186
|
221
|
def note_on_vel( self, midi_pitch, midi_vel ):
|
187
|
222
|
return self.write( self._pitch_to_i2c_addr( midi_pitch ),
|
188
|
|
- TinyOpD['noteOnVelOp'],
|
|
223
|
+ TinyOp.noteOnVelOp.value,
|
189
|
224
|
[self._validate_vel(midi_vel)] )
|
190
|
225
|
|
191
|
226
|
def note_on_us( self, midi_pitch, pulse_usec ):
|
192
|
227
|
return self.write( self._pitch_to_i2c_addr( midi_pitch ),
|
193
|
|
- TinyOpD['noteOnUsecOp'],
|
|
228
|
+ TinyOp.noteOnUsecOp.value,
|
194
|
229
|
list(self._usec_to_coarse_and_fine(pulse_usec)) )
|
195
|
230
|
|
196
|
231
|
def note_off( self, midi_pitch ):
|
197
|
232
|
return self.write( self._pitch_to_i2c_addr( midi_pitch ),
|
198
|
|
- TinyOpD['noteOffOp'],[0] ) # TODO: sending a dummy byte because we can handle sending a command with no data bytes.
|
|
233
|
+ TinyOp.noteOffOp.value,
|
|
234
|
+ [0] ) # TODO: sending a dummy byte because we can't handle sending a command with no data bytes.
|
199
|
235
|
|
200
|
236
|
def set_velocity_map( self, midi_pitch, midi_vel, pulse_usec ):
|
201
|
237
|
pass
|
|
@@ -203,14 +239,15 @@ class Picadae:
|
203
|
239
|
def get_velocity_map( self, midi_pitch, midi_vel, time_out_ms=250 ):
|
204
|
240
|
pass
|
205
|
241
|
|
206
|
|
- def set_pwm_duty( self, midi_pitch, duty_cycle_pct, enableFl=True ):
|
|
242
|
+ def set_pwm_duty( self, midi_pitch, duty_cycle_pct ):
|
207
|
243
|
return self.write( self._pitch_to_i2c_addr( midi_pitch ),
|
208
|
|
- TinyOpD['setPwmOp'],
|
209
|
|
- [enableFl, int( duty_cycle_pct * 255.0 /100.0 )])
|
|
244
|
+ TinyOp.setPwmOp.value,
|
|
245
|
+ [ int( duty_cycle_pct * 255.0 /100.0 )])
|
210
|
246
|
|
211
|
247
|
def get_pwm_duty( self, midi_pitch, time_out_ms=250 ):
|
212
|
|
- return self.read_block( self._pitch_to_i2c_addr( midi_pitch ),
|
213
|
|
- TinyRegAddr.kPwmDutyAddr,
|
|
248
|
+ return self.block_on_picadae_read( self._pitch_to_i2c_addr( midi_pitch ),
|
|
249
|
+ TinyRegAddr.kRdRegAddrAddr.value,
|
|
250
|
+ TinyRegAddr.kPwmDutyAddr.value,
|
214
|
251
|
[], 1, time_out_ms )
|
215
|
252
|
|
216
|
253
|
def set_pwm_freq( self, midi_pitch, freq_div_id ):
|
|
@@ -219,13 +256,14 @@ class Picadae:
|
219
|
256
|
pass
|
220
|
257
|
|
221
|
258
|
def get_pwm_freq( self, midi_pitch, time_out_ms=250 ):
|
222
|
|
- return self.read_block( self._pitch_to_i2c_addr( midi_pitch ),
|
223
|
|
- TinyRegAddr.kPwmFreqAddr,
|
|
259
|
+ return self.block_on_picadae_read( self._pitch_to_i2c_addr( midi_pitch ),
|
|
260
|
+ TinyRegAddr.kRdRegAddrAddr.value,
|
|
261
|
+ TinyRegAddr.kPwmFreqAddr.value,
|
224
|
262
|
[], 1, time_out_ms )
|
225
|
263
|
|
226
|
264
|
def set_flags( self, midi_pitch, flags ):
|
227
|
265
|
return self.write( self._pitch_to_i2c_addr( midi_pitch ),
|
228
|
|
- TinyOpD['writeOp'],
|
|
266
|
+ TinyOp.writeOp.value,
|
229
|
267
|
[ 12, 14, flags ])
|
230
|
268
|
|
231
|
269
|
def make_note( self, midi_pitch, atk_us, dur_ms ):
|
|
@@ -245,7 +283,6 @@ class Picadae:
|
245
|
283
|
coarse = int( usec / (self.prescaler_usec*255))
|
246
|
284
|
fine = int((usec - coarse*self.prescaler_usec*255) / self.prescaler_usec)
|
247
|
285
|
|
248
|
|
- print(coarse,fine)
|
249
|
286
|
assert( coarse <= 255 )
|
250
|
287
|
assert( fine <= 255)
|
251
|
288
|
|