Hallo im Forum !
Heute möchte ich euch mein kleines Bastelprojekt mit dem Mikrokontroller vorstellen. Nachdem
nun schon viele interessante Projekte hier im Forum vorgestellt wurden, folgt nun meine Bastelei: Ein Messwagen für Märklin HO.
Zum Hintergrund: Märklin hatte in der Vergangenheit einen Messwagen 49960 herausgegeben, dieser diente mir bei meinem Projekt als Vorbild. Natürlich hätte man auch einen einfachen Fahrradtacho nehmen können, doch ich wollte eine Art Komplettlösung, so wie es sie von Märklin gab.
In meinem Messwagen kommt ein Atmel Mega16A zum Einsatz. Programmiert wurde in C, AVR Studio5. Als Basis dient ein n-Wagen Märklin 4259. Der Sourcecode für den Mikrokontroller befindet sich am Ende des Posts.
Hier die ausführliche Dokumentation des Messwagens:
[attachment=0]Messwagen_Dokumentation.pdf[/attachment]
Fangen wir an: Welche Funktionen soll der Messwagen haben, und wie steuere ich diesen an?
Folgende Funktionen sind implementiert:
- aktuelle Geschwindigkeit
- maximale Geschwindigkeit
- gefahrene Strecke
- Fahrzeit
- Umgebungstemperatur
- Reset
- aus
Umgebungstemperatur? Ja, richtig gelesen, ich habe ebenfalls eine Temperaturmessung vorgesehen.
Dazu später mehr. Um zwischen diese einzelnen Funktionen schalten zu können, wird ein handelüblicher Funktionsdekoder verwendet, deren Ausgänge über einen Optokoppler mit dem Mikrokontroller verbunden sind. Um die anderen Messdaten zu erfassen, wird ein Radsensor in Form von einem Hallsensor eingesetzt.
Hier der gesamte Schaltplan:
Damit die gesamte Schaltung in den Wagenkasten passt, habe ich die Schaltung in verschiedene Teilschaltungen zerteilt.
Fangen wir mit der Spannungsversorgung an.
Ein Brückengleichrichter richtet die Digitalspannnung gleich, ein Pufferkondensator glättet die
Spannung. Der Linearspannungsregler stellt die nötigen 5V Versorgungsspannung der gesammten Schaltung zur Verfügung. Die LED dient als Kontrolle, das Spannnung anliegt.
Im Langzeittest wird der Linearregler relativ warm, einen Kühlkörper kann aufgrund der engen
Platzverhätnissen nicht eingebaut werden. Eine mögliche Alternative wäre ein DC/DC Wandler;
dieser hat dann einen deutlich besseren Wirkungsgrad (rund 70 bis 80%). Ein Schleifer und ein Messingbech aus der früheren Innenbeleuchtung (Märklin #7330) stellt den Kontakt zur Schiene her.
Dann folgt die Anzeige:
Bitte nicht erschrecken
Die 7 Segmentanzeigen werden vom Mikrokontroller durch Multiplexen angesteuert.
Zum Schluss noch der eigentliche Mikrokontroller mit dem Optokoppler, einem Zählbaustein 4024 und dem Thermistor für die Temperaturmessung (Mikrokontroller und 4024 jetzt nicht eingesteckt).
Der Optokoppler sorgt dafür, dass der Funktionsdekoder galvanisch von dem Rest der Schaltung entkopplet ist.
WICHTIG: Die Ausgänge des Funktionsdekoders müssen erst gedimmt werden, da sonst durch die hohe Spannung des Dekoderts der Optokoppler zerstört wird. Alternativ kann auch ein passender Widerstand zwischen Decoder fx und Optokopplereingang eingebaut werden.
Wie werden denn nun Geschwindigkeit und Strecke gemessen?
Der Hallsensor erfasst die Achsumdrehungen mittels eines kleinen Neodym-Bohr-Magneten. Der Zähler 4024 erfasst diese Impulse und der Mikrokontroller ließt den Zählerstand aus. Dieses passiert in einem Zeitfenster von 1 Sekunde. Anschließend kann mit der Anzahl der Impulsen und des Raddurchmessers die Strecke und die Geschwindigkeit ermittelt werden.
ich habe einen bipolaren Hallsensor verwendet. Daher müssen tatsächlich zwei Magnete an der Achse befestigt werden.
Die Temperatur wird über einem Widerstandsteiler ermittelt und mittels ADC-Port des Mikrokontrollers eingelesen.
Nachdem nun alles Aufgebaut ist, folgt die Preparierung des Wagen. Dafür muss der Wagenboden isoliert werden und ein Schleifer sowie eine Masseplatte in die Drehgestelle installiert werden (Teile stammen wie oben schon erwähnt von der ehemals eingebauten Innenbeleuchtung).
Platzieren der Platinen:
Hier der Sourcecode:
-Hauptfunktion
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
// Messwagen für Maerklin
/*
PIN Belegung
PortC0 - 3: Kathode 7LED
PortC5: Reset 4024
PortD: Anode 7LED
PortA1 - 3: fx Decoder
PortA0: ADC
PortB0 -5: Einlesen 4024
*/
#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "routines.h"
ISR(TIMER0_OVF_vect)
{
task_segments();
}
ISR(TIMER1_COMPA_vect)
{
gettime();
uint32_t impulse=getimpulse();
PORTC |= (1<<PC5); //RESET4024
getpath(impulse);
getspeed(impulse);
PORTC &= ~(1<<PC5);
}
int main(void)
{
init();
_delay_ms(1000); //start-up tine
sei();
while(1)
{
getswitch();
}
}
-benutzte Funktionen:
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
#define raddurchmesser 10.3
//Initialisierung ADC/Timer/Reset4024
void init( void );
//Initialisierung Timer0
void timer_enable0(void);
//Initialisierung Timer1
void timer_enable1(void);
//Initialisierung ADC
void adc_init(void);
//Einlesen der Switches von Multidecoder
void getswitch(void);
//Berechnung Temperatur
int64_t gettemperature(void);
//Ausgabe Temperatur
void set_segments(int64_t val);
//Ausgabe Impulse
uint32_t getimpulse(void);
//Multiplexen der Anzeige
void task_segments(void);
//Berechnung Fahrstrecke
void getpath(uint32_t val);
//Berechnung Gesschwindigkeit
void getspeed(uint32_t val);
//Berechnung Zeit
void gettime(void);
-Implementierung der Funktionen
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
#include <avr/io.h>
#include <util/delay.h>
#include "routines.h"
uint8_t pos = 0;
uint16_t zeit=0;
uint64_t strecke=0;
uint16_t geschwindigkeit=0;
uint16_t maxgeschwindigkeit=0;
int64_t temperatur =0;
uint8_t i =0;
uint16_t summe =0;
//Speicherarry für "Bilder" der 7-Segment Anzeigen
uint8_t display[]= { 0, 0, 0, 0, 0b00000000};
//Speicherarray für grafische Umsetzung der Zahlen
const uint8_t segments[] = { 0b01110111, 0b00010100, 0b10110011, 0b10110110,0b11010100, 0b11100110, 0b11100111, 0b00110100, 0b11110111, 0b11110110, 0b01111111, 0b00011100, 0b10111011, 0b10111110,0b11011100, 0b11101110, 0b11101111, 0b00111100, 0b11111111, 0b11111110 };
void init(void)
{
DDRA=0x00;
DDRB=0b00000000;
DDRC=0xFF;
DDRD=0xFF;
PORTC |= (1<<PC5);
_delay_ms(100);
PORTC &= ~(1<<PC5);
timer_enable0();
timer_enable1();
adc_init();
}
void timer_enable0(void)
{
TCCR0=2; //activate Timer0 with CPU-clock / 8
TCNT0=0; //reset Timer0
TIMSK|=(1<<TOIE0); // Timer Overflow Interrupt enable
}
void timer_enable1(void)
{
TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
TIMSK |= (1 << OCIE1A); // Enable CTC interrupt
OCR1A = 15624; // Set CTC compare value to 1Hz at 4MHz AVR clock
TCCR1B |= (1 << CS12); // Start timer at Fcpu/256
}
void adc_init(void)
{
ADCSRA |= (1<<ADEN) | (1<<ADATE) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0) | (1<<ADSC);
ADMUX|= (1<<REFS0) | (0<<REFS1)| (0<<ADLAR) ; // AVcc Volt als Referenz
SFIOR|= (0<< ADTS2)| (0<<ADTS1)| (0<<ADTS0); // free running
ADMUX|= (0<<MUX0) | (0<<MUX1) | (0<<MUX2) | (0<<MUX3)| (0<<MUX4) ; // choose Port PA0 for ADC
}
void getswitch(void) // get switches from Multidecoder
{
uint8_t fxdecoder=PINA;
fxdecoder = fxdecoder & 0b00001110;
if (fxdecoder==14) // off
{
display[0]=0x00;
display[1]=0x00;
display[2]=0x00;
display[3]=0x00;
}
else if (fxdecoder==12) // Geschwindigkeit f1
{
uint16_t temp = geschwindigkeit*3.132;
uint8_t tausend = temp/1000;
uint8_t hundert = (temp%1000)/100;
uint8_t zehner = (temp%100)/10;
uint8_t einer = (temp%10);
display[0]=segments[einer];
display[1]=segments[zehner+10];
display[2]=segments[hundert];
display[3]=segments[tausend];
}
else if (fxdecoder==10) // Strecke f2
{
uint16_t streckeraw = strecke*0.01;
uint8_t hundert = streckeraw/1000;
uint8_t zehner = (streckeraw%1000)/100;
uint8_t einer = (streckeraw%100)/10;
uint8_t komma = (streckeraw%10);
display[0]=segments[komma];
display[1]=segments[einer+10];
display[2]=segments[zehner];
display[3]=segments[hundert];
}
else if (fxdecoder==6) // Fahrzeit f3
{
uint16_t minutenraw=zeit/60;
uint8_t stunden1 = (minutenraw/60);
uint8_t minuten2 = (minutenraw%60)/10;
uint8_t minuten1 = (minutenraw%10);
display[0]=segments[minuten1];
display[1]=segments[minuten2];
display[2]=segments[stunden1+10];
display[3]=0b00000000;
}
else if (fxdecoder==8) //Vmax f1 + f2
{
uint16_t temp = maxgeschwindigkeit*3.132;
uint8_t tausend = temp/1000;
uint8_t hundert = (temp%1000)/100;
uint8_t zehner = (temp%100)/10;
uint8_t einer = (temp%10);
display[0]=segments[einer];
display[1]=segments[zehner+10];
display[2]=segments[hundert];
display[3]=segments[tausend];
}
else if (fxdecoder==4) //Temperatur f1 + f3
{
set_segments(gettemperature());
}
else if (fxdecoder==0) // RESET f1 + f2 + f3
{
zeit=0;
strecke=0;
geschwindigkeit=0;
maxgeschwindigkeit=0;
display[0]=0b10000000;
display[1]=0b10000000;
display[2]=0b10000000;
display[3]=0b10000000;
}
else
{
// display error
display[0]=0b00000000;
display[1]=0b10000001;
display[2]=0b10000001;
display[3]=0b11100011;
}
}
int64_t gettemperature(void)
{
int64_t temp_raw =0;
float hilf =0;
int16_t i;
int64_t temp1=0;
int64_t temp2=0;
int64_t temp3=0;
for(i = 0; i<1000 ; i++)
{
temp_raw +=ADC;
_delay_ms(5);
}
temp_raw=temp_raw*0.01;
hilf = (0.0002*5000*temp_raw*4.8828125)/(50000-(temp_raw*4.8828125));//0.2=1000/5000
temp1=hilf*hilf*hilf*2854;
temp2=hilf*hilf*15850;
temp3=hilf*47480;
temperatur=temp1-temp2+temp3-31985; // Kennlinie des PTC
temperatur-=130;//Kalibrierung um 1.3 Grad
return temperatur;
}
void task_segments(void)
{
switch(pos)
{
case 0: PORTD=display[4]; PORTC &= ~(1<<PC3);_delay_ms(1); PORTC |= (1<<PC0); PORTD=display[pos]; pos++; break;
case 1: PORTD=display[4]; PORTC &= ~(1<<PC0); _delay_ms(1);PORTC |= (1<<PC1); PORTD=display[pos]; pos++; break;
case 2: PORTD=display[4]; PORTC &= ~(1<<PC1); _delay_ms(1);PORTC |= (1<<PC2); PORTD=display[pos]; pos++; break;
case 3: PORTD=display[4]; PORTC &= ~(1<<PC2); _delay_ms(1);PORTC |= (1<<PC3); PORTD=display[pos]; pos=0; break;
}
}
uint32_t getimpulse(void)
{
uint32_t zaehler=PINB;
zaehler= zaehler & 0b00011111;
return zaehler;
}
void set_segments(int64_t val)
{
if(val<(-9999)) // return Error E1
{
display[0]=0b00000000;
display[1]=0b00000000;
display[3]=0b11100011;
display[2]=0b00010100;
return;
}
if(val>9999) // return Error E2
{
display[0]=0b00000000;
display[1]=0b00000000;
display[3]=0b11100011;
display[2]=0b10110011;
return;
}
// Ausgabe Temperatur
if(val>=0)
{
uint8_t tausend = (val%10000)*0.001;
uint8_t hundert = (val%1000)*0.01;
uint8_t zehner = (val%100)*0.1;
if(tausend==0)
{
display[2]=0b00000000;
}
else
{
display[2]=segments[tausend];
}
display[1]=segments[hundert+10];
display[0]=segments[zehner];
display[3]=0b00000000;
}
if(val<0)
{
val=-val;
uint8_t hundert = (val%1000)*0.01;
uint8_t zehner = (val%100)*0.1;
display[2]=0b10000000; // minus
display[1]=segments[hundert+10];
display[0]=segments[zehner];
display[3]=0b00000000;
}
}
void getpath(uint32_t val)
{
strecke= strecke+(val*raddurchmesser*3.14);
}
void getspeed(uint32_t val)
{
uint16_t wert = val*raddurchmesser*3.14; // mm pro sec
summe+=wert;
i++;
if (i==5)
{
geschwindigkeit=summe*0.2;
i=0;
summe=0;
}
if (geschwindigkeit>maxgeschwindigkeit)
{
maxgeschwindigkeit=geschwindigkeit;
}
}
void gettime(void)
{
zeit++;
}
Gruß
Oliver