Bitte geben Sie einen Grund für die Verwarnung an
Der Grund erscheint unter dem Beitrag.Bei einer weiteren Verwarnung wird das Mitglied automatisch gesperrt.
Codeschnipsel Midi Pedal mit Pi Pico und Schieberegister
#1
Ich habe nun um mein Arduino Due durch einen Pi Pico zu ersetzen mich einmal mit dem Thema Schieberegister beschäftigt. Dazu habe ich mir zwei Platinen angefertigt (PCBWay) welche jeweils drei 74hc165 Schieberegister beinhalten. Diese werden als SPI Module angeschlossen und können durch weitere in Reihe erweitert werden. Theoretisch kann man so unbegrenzt viele in Reihe schalten, praktisch sollte man das natürlich nicht versuchen... Für ein Pedal, Manual oder auch Schalter reicht es aber vollkommen aus.
Hier ein kleines Python Programm zum abfragen. Angepasst auf mein Pedal. Wichtig wie dem Code zu entnehmen sind meine Reeds im Ruhezustand (Taste nicht gedrückt) geschlossen und offen wenn die Taste gedrückt wird. Dies kann im Code einfach geändert werden und allgemein ist der Code sehr flexibel und einfach aufgebaut. Wenn es jemanden hilft, dann freut es mich.
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
import time
import digitalio
import board
import usb_midi
import adafruit_midi
from adafruit_midi.note_on import NoteOn
from adafruit_midi.note_off import NoteOff
# ============================================================
# KONFIGURATION
# ============================================================
# Anzahl der 74HC165 in der Kette
NUM_CHIPS = 6
# Anzahl der tatsächlich verwendeten Pedale
NUM_PEDALS = 30
# Pin-Belegung Raspberry Pi Pico
PIN_CLK = board.GP14
PIN_PL = board.GP15
PIN_DATA = board.GP16
# Entprellzeit
DEBOUNCE_TIME = 0.030
# MIDI-Kanal
# 0 = MIDI Kanal 1
# 1 = MIDI Kanal 2
# ...
MIDI_CHANNEL = 0
# Anschlagstärke
MIDI_VELOCITY = 100
# ============================================================
# MIDI-NOTEN
# ============================================================
#
# Hier wird jedem Pedal eine MIDI-Note zugeordnet.
#
# Beispiel:
# Pedal 0 -> MIDI 36 = C2
# Pedal 1 -> MIDI 37 = Cis2
# usw.
#
# Diese Liste kannst du frei verändern.
#
MIDI_NOTES = [
36, # Pedal 0
37, # Pedal 1
38, # Pedal 2
39, # Pedal 3
40, # Pedal 4
41, # Pedal 5
42, # Pedal 6
43, # Pedal 7
44, # Pedal 8
45, # Pedal 9
46, # Pedal 10
47, # Pedal 11
48, # Pedal 12
49, # Pedal 13
50, # Pedal 14
51, # Pedal 15
52, # Pedal 16
53, # Pedal 17
54, # Pedal 18
55, # Pedal 19
56, # Pedal 20
57, # Pedal 21
58, # Pedal 22
59, # Pedal 23
60, # Pedal 24
61, # Pedal 25
62, # Pedal 26
63, # Pedal 27
64, # Pedal 28
65, # Pedal 29
]
# ============================================================
# MIDI INITIALISIEREN
# ============================================================
midi = adafruit_midi.MIDI(
midi_out=usb_midi.ports[1],
out_channel=MIDI_CHANNEL
)
# ============================================================
# SCHIEBEREGISTER INITIALISIEREN
# ============================================================
clk = digitalio.DigitalInOut(PIN_CLK)
clk.direction = digitalio.Direction.OUTPUT
clk.value = True
pl = digitalio.DigitalInOut(PIN_PL)
pl.direction = digitalio.Direction.OUTPUT
pl.value = True
data_pin = digitalio.DigitalInOut(PIN_DATA)
data_pin.direction = digitalio.Direction.INPUT
# ============================================================
# ZUSTANDSSPEICHER
# ============================================================
# Zustand der Reedkontakte
#
# WICHTIG:
# 0 = geschlossen
# 1 = offen
#
# Beim Start wird alles als GESCHLOSSEN angenommen.
#
last_state = [0] * (NUM_CHIPS * 8)
last_change_time = [0] * (NUM_CHIPS * 8)
# ============================================================
# SCHIEBEREGISTER LESEN
# ============================================================
def read_shift_registers():
input_values = []
# Parallel Load
pl.value = False
time.sleep(0.000001)
pl.value = True
# Daten auslesen
for _ in range(NUM_CHIPS * 8):
bit = data_pin.value
# Reedkontakt:
#
# geschlossen -> 0
# offen -> 1
#
input_values.append(1 if bit else 0)
# Clock
clk.value = False
time.sleep(0.000001)
clk.value = True
return input_values
# ============================================================
# MIDI NOTE ON
# ============================================================
def midi_note_on(pedal_number):
if pedal_number >= len(MIDI_NOTES):
return
note = MIDI_NOTES[pedal_number]
midi.send(
NoteOn(note, MIDI_VELOCITY)
)
print(
"Pedal",
pedal_number,
"NOTE ON",
note
)
# ============================================================
# MIDI NOTE OFF
# ============================================================
def midi_note_off(pedal_number):
if pedal_number >= len(MIDI_NOTES):
return
note = MIDI_NOTES[pedal_number]
midi.send(
NoteOff(note, 0)
)
print(
"Pedal",
pedal_number,
"NOTE OFF",
note
)
# ============================================================
# ENTprellung
# ============================================================
def get_debounced_state(new_readings):
stable_changes = []
current_time = time.monotonic()
for i in range(len(new_readings)):
old_val = last_state[i]
new_val = new_readings[i]
# Zustand hat sich verändert
if old_val != new_val:
if current_time - last_change_time[i] > DEBOUNCE_TIME:
# Neuer Zustand ist stabil
last_state[i] = new_val
last_change_time[i] = current_time
stable_changes.append(
(i, new_val)
)
else:
# Zustand unverändert
last_change_time[i] = current_time
return stable_changes
# ============================================================
# START
# ============================================================
print()
print("====================================")
print(" Orgelpedal MIDI Controller")
print("====================================")
print("Pedale:", NUM_PEDALS)
print("MIDI Kanal:", MIDI_CHANNEL + 1)
print("Startzustand: alle Reedkontakte geschlossen")
print()
# ============================================================
# HAUPTSCHLEIFE
# ============================================================
while True:
# --------------------------------------------------------
# 1. Schieberegister lesen
# --------------------------------------------------------
raw_data = read_shift_registers()
# --------------------------------------------------------
# 2. Entprellen
# --------------------------------------------------------
changes = get_debounced_state(raw_data)
# --------------------------------------------------------
# 3. Änderungen verarbeiten
# --------------------------------------------------------
for index, state in changes:
# Nur die tatsächlich vorhandenen Pedale verwenden
if index >= NUM_PEDALS:
continue
pedal_number = index
# ----------------------------------------------------
# Reedkontakt OFFEN
# -> Pedal gedrückt
# -> MIDI NOTE ON
# ----------------------------------------------------
if state == 1:
print(
"Pedal",
pedal_number,
"GEDRÜCKT / Reed OFFEN"
)
midi_note_on(pedal_number)
# ----------------------------------------------------
# Reedkontakt GESCHLOSSEN
# -> Pedal losgelassen
# -> MIDI NOTE OFF
# ----------------------------------------------------
else:
print(
"Pedal",
pedal_number,
"LOS GEGEBEN / Reed GESCHLOSSEN"
)
midi_note_off(pedal_number)
# Kleine Pause
time.sleep(0.005)
#3
Bei der verwendeten Abfrage der 6× 74HC165 ist die eigentliche Auslesezeit der Schieberegister gegenüber den übrigen Zeitparametern praktisch zu vernachlässigen. Eine Hardware-Latch-Funktion ist bei den 74HC165 für diese Anwendung nicht erforderlich.
Die wesentlichen Parameter für die Latenz sind:
time.sleep(0.005) # 5 ms
DEBOUNCE_TIME = 0.030 # 30 ms
Die 5-ms-Pause bestimmt im Wesentlichen, wie häufig die Eingänge abgefragt werden. Ein Kontaktwechsel kann daher im ungünstigsten Fall fast eine komplette Abfrageperiode warten, bevor er erkannt wird.
Die 30-ms-Entprellzeit bestimmt anschließend, wie lange ein erkannter Zustandswechsel stabil sein muss, bevor daraus ein MIDI-Ereignis erzeugt wird.
Bei ungefähr 5 ms Abfrageintervall ergeben sich damit näherungsweise:
Situation -Latenz bis MIDI
Best Case ~30 ms
Durchschnitt ~32,5 ms
Worst Case ~35 ms
Die Werte lassen sich durch Anpassung der beiden Parameter gezielt reduzieren.
Beispielsweise mit:
DEBOUNCE_TIME = 0.010
ergibt sich ungefähr:
Situation - Latenz bis MIDI
Best Case ~10 ms
Durchschnitt ~12,5 ms
Worst Case ~15 ms
Mit:
DEBOUNCE_TIME = 0.005
entsprechend ungefähr:
Situation - Latenz bis MIDI
Best Case ~5 ms
Durchschnitt ~7,5 ms
Worst Case ~10 ms
Die Werte sind Näherungen, da die tatsächliche Laufzeit der CircuitPython-Operationen und insbesondere midi.send() noch hinzukommt.
Für physikalische Schalter wie Reedkontakte sind 30 ms ein konservativer, sicherer Wert. Je nach Kontakt, Mechanik und elektrischer Beschaltung kann die Entprellzeit jedoch durchaus reduziert werden. Ob beispielsweise 5 ms oder 10 ms zuverlässig funktionieren, sollte praktisch am konkreten Instrument getestet werden.
Damit ist die Optimierung der SPI-/Schieberegister-Auslese für diesen Anwendungsfall zunächst nicht notwendig. Sinnvoller ist es, die Abfrage- und Entprellzeiten an das tatsächliche Spielgefühl und das Prellverhalten der Reedkontakte anzupassen.
Bei einem langsamen Sampler kann eine bestimmte Eingangsverzögerung möglicherweise stärker auffallen als bei einer sehr schnell reagierenden Software wie OrganTEQ. Letztlich ist deshalb das tatsächliche Zusammenspiel aus Reedkontakt, Pico, CircuitPython, USB-MIDI und verwendeter Software entscheidend.
- Hauptwerk
- Hauptwerk-Konfiguration, Diskussion
- Hauptwerk-Samplesets
- GrandOrgue
- GrandOrgue-Konfiguration, Diskussion
- GrandOrgue-Samplesets
- Sweelinq
- Sweelinq-Konfiguration, Diskussion
- Sweelinq-Samplesets
- Sonstige Orgelsoftware
- Organteq
- Sonstige Sampler
- Hardware
- Spieltische und Selbstbau
- Zubehör (PCs, Monitore, Interfaces etc.)
- Klangabstrahlung
- Musikalisches
- Noten, Einspielungen, Konzerte
- Sonstige Musikthemen
Jetzt anmelden!
Jetzt registrieren!