Für die Gammaspektroskopie (https://stoppi-homemade-physics.de/gammaspektroskopie/) wird ein sogenannter Multi-channel-analyzer (kurz MCA oder zu deutsch Vielkanalzähler) benötigt. Was macht ein MCA?
Nun, wie wir bei meiner Gammaspektroskopie gesehen haben, ist die Höhe des Photomultiplier-Ausgabepulses proportional zur Lichtintensität, welche von dem Elektron im Szintillator erzeugt wurde. Und die Energie des Elektrons ist wiederum proportional zur Energie des Gammaquants. Also ist die Höhe der Photomultiplier-Ausgabepulse proportional zur Energie der Gammastrahlung h·f. Um ein Gammaenergiespektrum aufnehmen zu können, muss ich daher die Höhe der Ausgabepulse bestimmen und dann der Höhe nach zählen bzw. ordnen. Genau dies macht ein Multi-channel-analyzer. Er bestimmt die Höhe der Spannungspulse (z.B. 2.3 V) und erhöht dann den Zähler für eben diese Spannung um +1. Am Ende stellt er die Anzahl der gezählten Pulse in Abhängigkeit von ihrer Größe graphisch dar. Dies ergibt das sog. Gammaspektrum.
Es gibt mehrere kostenlose MCA-Softwarelösungen wie etwa Theremino (http://www.theremino.com/wp-content/uploads/files/Theremino_MCA_V7.2.zip), BecMoni oder PRA (https://www.gammaspectacular.com/blue/pra-spectrometry-software).
Ich habe nun aber auch versucht, mittels Arduino einen MCA zu realisieren. Die Aufgabe der Elektronik ist es, bei einem ankommenden Spannungspuls mittels Monoflop den Arduino darüber zu informieren. Die Höhe des ankommenden Pulses wird sodann kurz gespeichert. Dies macht ein sog. Spitzenwertdetektor bzw. peak-detector. Nun liest der Arduino über einen seiner analogen Eingänge den gespeicherten Spitzenwert ein und erhöht den entsprechenden Zähler. Danach leert er den Spitzenwertspeicher und wartet auf den nächsten Puls.
Der unten abgebildete Schaltungsteil ist genau dieser peak-detector. Der 4.7 nF Kondensator dient dazu, den Spitzenwert so lange zu speichern, bis dieser vom Arduino ausgelesen wird. Die Diode 1N4148 sorgt dafür, dass immer nur höhere Spannungen als bisher gespeichert zum Kondensator gelangen und der Transistor BC327 soll die an der Diode abfallende Spannung (ca. 0.5 V) kompensieren:
Der komplette Schaltplan sieht wiefolgt aus:
Angezeigt wird das Gammaspektrum schlussendlich mit einem 128×64 pixel display:
Im letzten Spektrum ist der Photopeak von Cäsium-137 bei 662 keV sehr schön zu sehen. Auch tritt die sog. Comptonkante sehr gut hervor.
h
Arduino-Code:
1 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 |
#include <U8glib.h> U8GLIB_ST7920_128X64 u8g(13, 11, 12, U8G_PIN_NONE); // Einstellungs-Tabelle für die Erhöhung der Taktfrequenz // ====================================================== // ADPS2 ADPS1 ADPS0 Division factor // 0 0 0 2 // 0 0 1 2 // 0 1 0 4 // 0 1 1 8 // 1 0 0 16 // 1 0 1 32 // 1 1 0 64 // 1 1 1 128 (Standard) #define FASTADC 1 // defines for setting and clearing register bits #ifndef cbi #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #endif #ifndef sbi #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) #endif // Variablendeklaration // ==================== const int analogInPin = A0; // Analog input pin für die Pulshöhe const int pin_verzoegerung = A1; // Analog input pin für die über ein Poti einstellbare variable Verzögerung const int pin_entleeren = 3; // Digital output pin für das Entleeren des Speicherkondensators const int pin_treffer = 4; // Digital input pin für vom Monoflop kommenden treffer const int pin_switch = 5; // Digital output pin für switch const int pin_test = 7; // Digital output pin um zu überprüfen, in welcher Phase der Entladekurve von C die Spannungsmessung erfolgt const int pin_reset = 8; // Digital input pin für das Leeren des Pulshöhenarrays um eine neue Messung zu starten int U_puls = 0; // aktuelle gemessene Pulshöhe float Spannung; // aktuelle gemessene Pulsspannung int Index = 0; // Index der gemessenen Pulshöhe im Intervall [0,127] float Faktor = 1; // Vergrößerungs- bzw. Verkleinerungsfaktor int Pulshoehen[128]; // array mit der Anzahl der jeweils 128 verschiedenen Pulshöhen int reset_time; // Zeitdauer in ms der U-Max-Speicherentleerung int j; // Zählvariable als Maß für die Schnelligkeit der Pulsabfrage int time_alt, time_neu; // Variablen zur Ermittlung der Schleifendauer int Verzoegerung; // Verzögerungszeit in µs // =========================== // ========== SETUP ========== // =========================== void setup(void) { Serial.begin(9600); pinMode(pin_entleeren, OUTPUT); // U-Speicher-Entleer-pin als output deklariert pinMode(pin_switch, OUTPUT); // switch-pin als output deklariert pinMode(pin_test, OUTPUT); // test-pin als output deklariert pinMode(pin_treffer, INPUT); // treffer-pin als input deklariert pinMode(pin_reset, INPUT); // reset-pin für die Anzeige als input deklariert digitalWrite(pin_entleeren, HIGH); // U_max-Speicher entleeren digitalWrite(pin_switch, LOW); // Schalter für U-Zuleitung offen digitalWrite(pin_test, LOW); // Test-pin auf 0 setzen for (int i = 0; i < 128; i++) { Pulshoehen[i] = 0; // alle Pulshöhentreffer auf 0 setzen } Faktor = 1; // Startvergrößerung für die graphische Darstellung reset_time = 5; // Zeitdauer in ms der U-Max-Speicherentleerung #if FASTADC // set prescale to 16 sbi(ADCSRA,ADPS2) ; // ADPS2 auf 1 gesetzt cbi(ADCSRA,ADPS1) ; // ADPS1 auf 0 gesetzt cbi(ADCSRA,ADPS0) ; // ADPS0 auf 0 gesetzt // cbi = 0; sbi = 1 #endif // Zeichnen der Achsen // =================== u8g.firstPage(); do { u8g.drawLine(0, 63, 127, 63); u8g.drawLine(0, 0, 0, 63); } while( u8g.nextPage() ); } // =================================== // ========== HAUPTSCHLEIFE ========== // =================================== void loop(void) { /* Verzoegerung = analogRead(pin_verzoegerung); Verzoegerung = Verzoegerung * 10; // Verzögerungsintervall 0 bis 1023 * 10 µs Serial.print("Verzoegerung im µs = "); Serial.println(Verzoegerung); */ digitalWrite(pin_entleeren, HIGH); // U_max-Speicher gelöscht delay(reset_time); // reset_time ms Wartezeit zum ausreichenden Löschen des U_max-Speichers digitalWrite(pin_entleeren, LOW); // U_max-Speicher bereit digitalWrite(pin_switch, HIGH); // Schalter für U-Zuleitung geschlossen; Messung startet... eigentlich unnötig, da ja eh der Kondensator gelöscht wird und auf den ersten Puls gewartet wird // Warte bis ein vom Monoflop verlängerter Puls über DIGITAL-IN registriert wird! // ============================================================================== while(digitalRead(pin_treffer) == LOW) { } //delayMicroseconds(Verzoegerung); // übers Poti variable Verzögerung, damit der Zuleitungsschalter nicht schon schließt obwohl der Spannungspuls noch nicht sein Maximum erreicht hat! delayMicroseconds(5); // fixe Verzögerung, damit der Zuleitungsschalter nicht zu früh schließt und der Spannungspuls auch zu seinem Maximum anwachsen kann! digitalWrite(pin_switch, LOW); // Puls wurde registriert --> Schalter für U-Zuleitung offen; Messung gestoppt // Pulshöhe wird eingelesen // ======================== U_puls = analogRead(analogInPin); // Einlesen der Pulshöhe 0-5V // digitale Ausgabe über den Test-Pin unmittelbar nach Einlesen der Spannung um festzustellen, "wo" in der Entladekurve die Spannung gemessen wird. Soll ja genau im Maximum erfolgen! // ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- /* digitalWrite(pin_test, HIGH); // Test-pin auf 1 setzen delay(5); // Verzögerung digitalWrite(pin_test, LOW); // Test-pin auf 0 setzen */ // graphische und Speicher-Verarbeitung der registrierten Pulshöhe // --------------------------------------------------------------- if(U_puls > 130) // Vermeidung von der Anzeige dominierender peaks mit geringer Amplitude { Index = map(U_puls, 0, 1023, 0, 127); // Index der aktuell gemessenen Pulshöhe im Intervall [0,127] Pulshoehen[Index] = Pulshoehen[Index] + 1; // aktuelle Pulshöhe erhält einen Treffer //Spannung = U_puls * 5.0 / 1023; //Serial.print("Pulshoehe = "); // serielle Ausgabe der aktuellen Pulshöhe 0-5V //Serial.println(Spannung,2); } // ============================================== // ============ graphische Ausgabe ============== // ============================================== if (int(Faktor * Pulshoehen[Index]) > 62) // Zuviele Treffer für die ausreichende y-Darstellung ---> Stauchung um Faktor 2 { u8g.firstPage(); do { u8g.drawLine(0, 63, 127, 63); u8g.drawLine(0, 0, 0, 63); for (int i = 0; i < 128; i++) { u8g.drawLine(0+i, 63, 0+i, 63 - int(Faktor * Pulshoehen[i])); // zeichnen aller Treffer } } while( u8g.nextPage() ); Faktor = Faktor / 2; } // Abfrage, ob der Reset-Knopf betätigt wurde // ------------------------------------------ if (digitalRead(pin_reset) == HIGH) { // Spektrum an den Computer senden // =============================== for (int i = 0; i < 128; i++) { Serial.println(Pulshoehen[i]); } // Spektrum löschen // ================ for (int i = 0; i < 128; i++) { Pulshoehen[i] = 0; // alle Pulshöhentreffer auf 0 setzen } Faktor = 1; // Startvergrößerung für die graphische Darstellung u8g.firstPage(); do { u8g.drawLine(0, 63, 127, 63); u8g.drawLine(0, 0, 0, 63); } while( u8g.nextPage() ); } //delay(2); // optionale Zeitverzögerung } |