Dienstag, 28. Mai 2013

Suchbild

Wie viele Unterschiede sind zu sehen?

Ein Unterschied: der Preis. Wer genauer hinschaut, findet auch den Grund.



... und auf den Rückseiten?






Sonntag, 26. Mai 2013

Musik - Schritt für Schritt (Teil 4): Erste Gehversuche mit Koffer



Das Encoder Board ist fertig, das SPI Board schon länger. Eine MIDI Anbindung ist vorhanden, ein Teilregister verkabelt. Zeit, die Teile zu verbinden und ein wenig zu programmieren.


Um das Ergebnis vorweg zu nehmen:



Die Funktion beschränkt sich auf den Sequenzablauf. Über die Encoder kann die Tonhöhe (Pitch) gesteuert werden. Velocity bleibt dabei konstant. 

GO-Taste (START/STOP):
Die Sequenz wird durch Druck auf den GO-Taster gestartet. Eine rote LED zeigt dann den jeweiligen Step an. Im Video scheint es, als leuchten 2-3 LEDs gleichzeitig. Dem ist real nicht so und liegt nur an der langen Öffnungszeit der Webcam.
Durch erneuten Druck auf GO wird die Sequenz nach Beendigung des aktuellen Steps gestoppt. Der nächste noch nicht ausgeführte Step wird dann durch eine grüne LED angezeigt. Die Status-LED neben dem GO-Taster zeigt durch Leuchten den STOP-Zustand an.

RESET-Taste:
Bei laufender Sequenz bewirkt ein Druck auf die RESET-Taste, dass nach dem Ende des aktuelle Steps die Sequenz unterbrochen wird und bei Step 1 wieder startet.



Zum Aufbau des Ganzen:

Ich habe alle Komponenten vorerst in einem kleinen Koffer (25 x 35 cm) untergebracht. Da die Komponenten noch locker zusammengesteckt sind, erspare ich mir so das Auf- und Abbauen, wenn ich den Code testen möchte.


Einfach den Koffer öffnen, MIDI Anschlüsse und USB-Anschluss mit dem Computer verbinden - fertig. Zum Auswerten und "Vertonen" der MIDI Kommandos verwende ich MIDI-OX.





Das SPI Board und Encoder Board sind noch nicht voll ausgelastet. Derzeit ist nur ein Teilregister (Encoder + LEDs) angeschlossen. Als Sequence Engine dient derzeit der Arduino Uno, hier mit dem MIDI Shield bestückt. Die Anschlüsse an die Sequence Engine laufen derzeit über ein Steckbrett, damit ich die Verkabelung bei Bedarf ändern kann. Auch die Sequence Control Taster  (START/STOP, STEP und RESET) und eine Status-LED sind auf dem Steckbrett untergebracht.



Rechts unten befindet sich noch ein Potentiometer zum Einstellen der Geschwindigkeit. Unten mittig - noch nicht integriert, aber schon mal getestet - ein Micro-SD Card Slot. Er wird später auf dem Configuration Board zu finden sein, dass auch das Display enthält. Mit diesem Board sollen u.a. Einstellungen gespeichert und geladen werden können.



Der geplante Systemaufbau:






Wie geht es weiter?

Die Pin-Belegung der Sequence Engine sieht im Moment so aus:



*328 ist die Pin-Belegung des ATmega328P-PU, die man berücksichtigen muss, wenn man das Arduino-Board durch "selbstgegossene" Hardware ersetzen möchte.

Zusätzlich ist das BPM-Poti am Pin A0 (23). Es wird später nicht mehr benötigt, da die Funktion vom Configuration Board geliefert wird.

Bei Verwendung des ATmega328P-PU ist nur eine serielle Schnittstelle vorhanden, die für MIDI genutzt werden kann. Evtl. ersetze ich ihn durch einen ATmega2560, der 4 Schnittstellen bietet. Eine davon könnte ich dann exklusiv für Software-Updates nutzen. 

Weitere Tests müssen noch zeigen, ob die Sequence Control Taster wie aktuell abgefragt werden, oder durch eine Interrupt-Schaltung angenehmer funktionieren. Der Nachteil der Tasterabfrage per Polling ist, dass der Taster so lange gedrückt bleiben muss, bis der aktuelle Step beendet ist. Bei schnellem Tempo kein Problem, bei langsamen Schrittfolgen etwas nervig, weil es öfter vorkommt, dass ich den Taster zu früh los lasse und der Tastendruck somit keine Wirkung hat.

[... wird fortgesetzt]

Donnerstag, 16. Mai 2013

Musik - Schritt für Schritt (Teil 3): Encoder Board

Hier geht es zu Teil 1
Hier geht es zu Teil 2

Die ersten Tests meines CSQ-1 Step Sequencer Projektes zeigten, das es manchmal nicht so einfach ist, wie man es sich vorgestellt hat. Die Folge war die Entwicklung des Encoder Boards.


Wozu ein Encoder Board?

Je nach Drehgeschwindigkeit erzeugen Encoder (Inkrementalgeber) Impulse unterschiedlicher Frequenz, die man durch Verändern eines Zählerstandes in numerische Werte wandeln kann. Im Fall meines CSQ-1 Step Sequencers z.B. um die Tonhöhe eines Steps zu verändern.
Dummerweise kann es passieren, dass gerade in dem Moment, in dem man am Encoder dreht, der Mikrocontroller mit etwas anderem beschäftigt ist, als die Encoder-getriggerten Zählerstände zu verändern. Die Folge ist, dass Impulse verloren gehen und die beabsichtigte Änderung eines Parameters nicht in dem gewünschten Umfang eintritt. Anders ausgedrückt: die Zählerstandsänderung ist nicht mehr linear zur Änderung des Encoderdrehwinkels. Die Auswirkungen dieses Makels sind nicht vorhersagbar und es entsteht der Eindruck, dass das Gerät nicht richtig funktioniert.

Man könnte den Controller bei seiner bisherigen Tätigkeit über die Ansteuerung per externem Interrupt unterbrechen, so dass er sich vorrangig um das Erfassen der Encoder Impulse kümmert. Im Fall eines MIDI Step Sequencers könnte dies jedoch zu unerwünschten Verzögerungen in der Erzeugung der MIDI Daten führen.

Als brauchbare Lösung betrachte ich daher das parallele Bearbeiten dieser Aufgaben. D.h. um die Encoder muss sich ein separater Controller kümmern. Er soll stetig die Impulse der Encoder erfassen und die Zähler entsprechend verändern. Auf Anfrage des Hauptcontrollers (Master), überträgt der Subcontroller (Slave) die aktuellen Zählerstände.
Bei meinem CSQ1 soll der ATmega328P-PU diese Aufgabe erledigen. Er ist auch im Arduino UNO verbaut, wodurch sich die Programmentwicklung einfach gestalltete. Dazu muss der ATmega328 mit dem Arduino Bootloader ausgestattet werden. Das ist mit einem Programmer, z.B. mySmartUSB MK2 oder ähnlichem. Ich hatte jedoch noch Controller mit vorinstalliertem Bootloader und konnte mir diesen Schritt sparen.
Die Datenübertragung sollte per I²C-Bus (Inter-Integrated Circuit) erfolgen, der auch im Standard-Mode mit 100 kHz Taktrate schnell genug für diese Anwendung sein müsste.

 
Die Hardware:

Der ATmega328 stellt 23 Ein-/Ausgänge zur Verfügung:
Bei Nutzung des Arduino Boards sind die blauen Bezeichnungen in der Programmierung zu verwenden. Für die Hardware Entwicklung ist natürlich die Pin-Nummerierung des ICs relevant.

Die Pins werden wie folgt verwendet:

PIN UNO Verwendung
1 RESET RESET (zum Einspielen der Firmware erforderlich)
2, 3 0, 1 RXD, TXD (zum Einspielen der Firmware erforderlich)
4 - 6
11 - 19
23 - 26
2 - 13
A0 - A3
16 Input Pins für 8 Encoder
7, 8
Spannungsversorgung
9, 10
Externe Beschaltung mit 16 MHz Quarz
20 - 22
Unrelevant (extern mit +5V und GND beschaltet)
27, 28 A4, A5 SDA, SCL (I2C für Datenübertragung zum Hauptprozessor)


Die externe Beschaltung des ATmega328 beschränkt sich auf die Taktgenerierung per 16 MHz Quarz, eine paar passive Bauteile und einen Reset-Taster. Als guter Anhaltspunkt für die Schaltungsentwicklung diente mir das Referenz Design des Arduino Uno (man muss das Rad nicht 2x erfinden).
Zusätzlich wurden 2 Pull-Up Widerstände (R3, R4) für den I²C-Bus eingebaut.

Mit einem Slave-Controller können 8 Encoder angeschlossen werden, das entspricht beim CSQ-1 einem Register. Es werden also 2 Slave-Controller benötigt. 

Der Schaltplan:


Vor dem Platinendesign noch ein kleiner Test der Schaltung auf einem Steckbrett.


Der mySmartUSB MK2 Programmer diente als UART-Bridge zum Einspielen der Firmware in den ATmega und zur Stromversorgung. Der Arduino Mega wurde mit einem Polling Programm versehen, das die Zählerstände via I²C-Bus abfragt und diese dann über den seriellen Monitor sichtbar macht. Fazit: die Schaltung funktionierte und konnte "in Platinenmaterial gegossen werden".


 

Bestückungsplan:



 
Ätzvorlage:



Das fertige Encoder Board:



Einspielen der Firmware in die beiden ATmegas:

Die Board- und Programmer Einstellungen in der Arduino IDE werden für den Arduino Uno gewählt:

    -> Tools -> Board -> Arduino Uno
    -> Tools -> Programmer -> Arduino as ISP
   
Beim Upload der Firmware mit dem mySmartUSB ist nach Erscheinen der Nachricht "Binary sketch size..." im IDE Statusfenster kurz die Reset-Taste zu betätigen, da bei dieser Beschaltung kein automatisches Reset erfolgt (ähnlich der Programmierung eines Arduino Pro Mini). Nur so wird der Upload fehlerfrei beendet.

Wichtig: Für Register 2 muss in der Firmware (Zeile 53)  eine von Register 1 unterschiedliche Bus-Adresse eingetragen werden. Ich verwende für
Register 1:      int bus_adr = 2;
Register 2:      int bus_adr = 3;


Die Firmware (Register 1):


/* Firmware for CSQ1 encoder board register 1

  by christian marmann, 130514
 
  I2C address = 2

  IDE selektions using mySmartUSB mkII:
    -> Tools -> Board -> Arduino Uno
    -> Tools -> Programmer -> Arduino as ISP
  
  Upload with mySmartUSB mkII:
 
    - connection diagram:
    mySmartUSB mkII  ---- encoder board
    right interface        register 1   or   register 2
      PIN 10 = GND   ---- PIN 9  = GND ---- PIN 9  = GND
      PIN 9  = +5V   ---- PIN 10 = +5V ---- PIN 10 = +5V
      PIN 8  = RxD   ---- PIN 3  = TxD ---- PIN 7  = TxD
      PIN 7  = TxD   ---- PIN 4  = RxD ---- PIN 6  = RxD

    - starting upload:
      when message "Binäre Sketchgröße [...]" appears, then

      push  reset button
      for about half a second and wait till end of the upload
    
    - verify upload:
      start serial monitor with 9600 baud to verify encoder

      functions:
      - display:
        enc#  #[enc0]  #[enc1]  #[enc2]  #[enc3]  #[enc4]  #[enc5]  #[enc6]  #[enc7]
      
      - turning CW --> counter should increase
      - turning CCW --> counter should decrease till "0"
      - if counting is reverse, then shift encoder pin definition
        in "Encoder myEncx()" statement below or shift cabeling of the
        related encoder   
*/

#define ENCODER_DO_NOT_USE_INTERRUPTS

#include <Encoder.h>
#include <Wire.h>

Encoder myEnc0( 9, 8 );
Encoder myEnc1( 10, 7 );
Encoder myEnc2( 6, 11 );
Encoder myEnc3( 5, 12 );
Encoder myEnc4( 13, 4 );
Encoder myEnc5( A0, 3 );
Encoder myEnc6( A1, 2 );
Encoder myEnc7( A2, A3 );
long pos[] = {0, 0, 0, 0, 0, 0, 0, 0};
long newPos[] = {0, 0, 0, 0, 0, 0, 0, 0};
int encAnz = 8;
int bus_adr = 2; // i2C bus address

void setup() {
  Serial.begin(9600);
  Serial.println("Basic NoInterrupts Test:");
  Wire.begin(bus_adr);                // join i2c bus
  Wire.onRequest(requestEvent); // register event
}

void loop() {
  newPos[0] = myEnc0.read();
  newPos[1] = myEnc1.read();
  newPos[2] = myEnc2.read();
  newPos[3] = myEnc3.read();
  newPos[4] = myEnc4.read();
  newPos[5] = myEnc5.read();
  newPos[6] = myEnc6.read();
  newPos[7] = myEnc7.read();
  for (int i=0; i < encAnz; i++){
    if (newPos[i] != pos[i]) {
      Serial.print( i );
      for (int j=0; j < encAnz; j++){
        if( newPos[i] <= 0 ){
          switch (i){
            case 0: myEnc0.write(0); break;
            case 1: myEnc1.write(0); break;
            case 2: myEnc2.write(0); break;
            case 3: myEnc3.write(0); break;
            case 4: myEnc4.write(0); break;
            case 5: myEnc5.write(0); break;
            case 6: myEnc6.write(0); break;
            case 7: myEnc7.write(0); break;
          }
           newPos[i] = 0;
           pos[i] = 0;
        }
        Serial.print ("\t");
        Serial.print ( newPos[j] );
      }
      Serial.println("");
      pos[i] = newPos[i];
    }
  }
}

void requestEvent(){
  union {
    int encoder [8];
    byte buf [16];
    } encValues;
  encValues.encoder [0] = pos[0];
  encValues.encoder [1] = pos[1];
  encValues.encoder [2] = pos[2];
  encValues.encoder [3] = pos[3];
  encValues.encoder [4] = pos[4];
  encValues.encoder [5] = pos[5];
  encValues.encoder [6] = pos[6];
  encValues.encoder [7] = pos[7];
  Wire.write((byte *) &encValues, sizeof encValues);
}



Das Polling Programm:
 
/* Encoder_I2C_poller

  by christian marmann, 130425
 
  dedicated to the CSQ-1 project

  this programm polls encoder position data via I2C from another
  atmega328 which handles the encoders


*/


#include <Wire.h>
int var[] = {0,0,0,0,0,0,0,0};
int bus_adr = 2;

void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(38400);  // start serial for output
}

void loop(){
  getEncValues();
  for( int i=0; i<8; i++ ){
    Serial.print(var[i]);
    Serial.print("\t");    // print the character
  }
  Serial.println();
  delay(10);
}

void getEncValues() {
  union {
    int encoder [8];
    byte buf [16];
    } encValues;
 
  if (Wire.requestFrom( bus_adr, sizeof encValues) != sizeof encValues)
    return;  // oops!
  
  for (byte i = 0; i < sizeof encValues; i++)
    encValues.buf [i] = Wire.read ();
  
  var[0] = encValues.encoder [0];
  var[1] = encValues.encoder [1];
  var[2] = encValues.encoder [2];
  var[3] = encValues.encoder [3];
  var[4] = encValues.encoder [4];
  var[5] = encValues.encoder [5];
  var[6] = encValues.encoder [6];
  var[7] = encValues.encoder [7];
}



Der Test mit den Encodern:



Aus Fehlern lernt man:

Während Register 1 einwandfrei funktionierte, traten beim Register 2 Unregelmäßigkeiten auf. Die Zähler reagierten teils sprunghaft und entwickelten ein "Eigenleben". Auch nach vertauschen der Prozessoren und beim Test mit einem einzelnen Encoder zeigten sich diese Effekte. Der Verdacht war, das es entweder an der Platine selbst läge oder Störsignale durch einen Designfehler entstünden. Bei der Durchsicht der Platine mit starker vergrößernder Lupe konnte ich keine Brücken oder Unterbrechungen ausmachen.
Beim Debuggen mit Hilfe der Firmware und des seriellen Monitors kam zum Vorschein, dass immer wieder der setup() Block aufgerufen wurde, was in der I2C Polling Routine zu den unregelmäßigen Anzeigen führte.
Da dies beim Betätigen jedes Encoders der Fall war, vermutete ich, das es an der Masseverbindung liegen könnte. Beim Durchmessen der Masseleiterbahnen mit einem Widerstandsmessgerät konnte ich dann eine Leiterbahnunterbrechung lokalisieren, die ich zuvor mit der Lupe nicht entdeckt hatte. Evtl.hatte sich hier beim Tonertransfer ein Haar auf der Oberfläche befunden. Es kam eine Silberdraht-Brücke über die Lücke. Nicht schön, aber nun funktioniert auch Register 2. Also:
Mut zur Lücke!
[... wird fortgesetzt]

Samstag, 4. Mai 2013

Innenansichten: "Ü-Ei"

Ostern ist lange vorbei, und aus Schokolade ist es auch nicht. Aber in diesem Ei steckte trotzdem ein Überraschung.


Das Ü-Ei 
Die Eieruhr, oder richtig falsch: der Kurzzeit-Timer.

 

















Es war defekt, wollte keinen Pieps mehr von sich geben und war auch optisch recht unauffällig geworden - sprich: sein Display war ebenfalls ohne Anzeige (nein, es lag nicht an der Batterie).


Also aufschrauben!

Und da war sie, die Überraschung ...


Als Gewicht für den stabileren Stand dient hier eine Stahlmutter, M18x2,5. Einfach, preiswert, wenig effektiv. Denn das Ei neigt trotz "Fussbeschwerung" zum Rollen und ist letzlich auf dem Küchenboden gelandet und die M18-Mutter außerhalb ihres Halters.
Weniger überraschend war der abgerissene Masseleiter. Darüber, auf dem Batteriefach aufgeklebt, ein Piezoelement, damit sich das Ei auch lautstark bemerkbar machen kann.
In der linken Hälfte zu sehen ist ein Uhrenquarz - typischerweise mit einer Frequenz von 32,768 kHz - und  eine Spule (blau).

Nun ist das Teil schon offen, da kann man ja auch gleich die anderen Schrauben mal entfernen.
Auf der Rückseite befindet sich das Display, angeschlossen über einen sehr dünnen Flachbandleiter, und die beiden Folientaster für Minuten- und Sekundeneinstellung.


Unter dem Display befindet sich der vergossene Uhrenchip, 2 Kondensatoren, 1 Widerstand und ein Transistor, letzterer um das Piezoelement mit ausreichend Energie anzusteuern.
Auffällig: die beiden rechten Leiterbahnen des Flachbandleiters enden im Nichts bzw. sind ohne weitere Zuleitung auf die Platine aufgeklebt. 

Noch ein kurzer Einsatz des Lötkolben, 4 Schrauben reingedreht, eine Batterie eingelegt und ...

 ... geht wieder.
Bis zum nächsten Fall.