Forum: Mikrocontroller und Digitale Elektronik Musikalisches Diskettenlaufwerk


von J. T. (chaoskind)


Lesenswert?

MoinMoin, ich hab ein kleines Musikinstrument (nach)gebastelt.
Ich hab ein paar alte 3,5" Diskettenlaufwerke in die Finger bekommen und 
bin nun dabei, ihnen ein neues Leben als Musikinstrument zu verschaffen.
Einfach mal Floppy Music bei Youtube eingeben, da findet ihr schon was.

Den Code samt Kommentaren wollt ich euch mal präsentieren, evtl habt ihr 
ja mal Lust und die Möglichkeit den Code aufzuspielen und so ein 
Laufwerk anzuschließen um euch die etwas wirre "Melodie" anzuhören =)

Weiter Funktionen sind geplant, zb Steuerung per Konsole, evtl sogar 
MIDI, mal sehen ob ichs hinbekomme, das Protokoll umzusetzen.

Für Kommentare oder Verbesserungsvorschläge bin ich immer gern offen.
MfG Chaos
1
/*
2
 *    FloppyMusik.c
3
 *
4
 *    Created: 11.07.2014 08:09:28
5
 *    Author: chaoskind
6
 *    Programm zur Steuerung von 3,5" Floppy Laufwerken, um Töne zu erzeugen
7
 *    
8
 *
9
 *    Pinbelegung(Active Low)    0/1
10
 *    
11
 *    PD1 = Floppy         An/Aus
12
 *    PD2 = Diskmotor            An/Aus
13
 *    PD3 = Richtung Lesekopf    Vor/Zurück
14
 *    PD4 = Schritt
15
 */
16
17
18
19
#define F_CPU 8000000UL
20
21
22
23
#define NeuerTon 1
24
#define TonAus 2
25
#define Tonaenderung 3
26
27
#define Floppy 1
28
#define Motor 2
29
#define Richtung 3
30
#define Schritt 4
31
32
#define vor 8
33
34
#include <avr/io.h>
35
#include <avr/interrupt.h>
36
37
void Init (void);
38
39
volatile uint8_t SysTickCnt = 0;
40
volatile uint8_t Schrittzaehler = 0;
41
42
volatile uint16_t Tonhoehe = 0;          //Tonhöhen müssen noch definiert und errechnet werden
43
volatile uint16_t SysTick_ms = 0;
44
45
46
ISR(TIMER1_COMPA_vect)
47
{
48
    uint8_t ISRArbeitsreg = 0;
49
    
50
  ISRArbeitsreg = PORTD & vor;    //Aktuellen Zustand einlesen und prüfen in welche Richtung der letzte Schritt ging
51
  
52
  if (ISRArbeitsreg == vor)      
53
  {      
54
    Schrittzaehler ++;
55
    if (Schrittzaehler >=70)    //um nicht in den Anschlag zu fahren, nach 70 Schritten Richtung wechseln
56
    {
57
      PORTD &= ~(1 << Richtung);
58
      Schrittzaehler = 0;
59
    }    
60
    else{}
61
  }
62
    
63
  else
64
  {    
65
    Schrittzaehler ++;
66
    if (Schrittzaehler >=70)
67
    {
68
      PORTD |= (1 << Richtung);
69
      Schrittzaehler = 0;
70
    }  
71
    else{}
72
  }
73
  
74
  PORTD &= ~(1 << Schritt);      //Schritt ausführen
75
  //PORTD &= ~(1 << Floppy);
76
  //PORTD |= (1 << Floppy);
77
  PORTD |= (1 << Schritt);      
78
}
79
80
ISR (TIMER0_COMP_vect)
81
{
82
  uint8_t ISRArbeitsreg = 0;
83
  ISRArbeitsreg = SysTickCnt;      //Drei Durchläufe mit Vorteiler 8 + 232 Ticks ergibt eine ms
84
  
85
  ISRArbeitsreg ++;
86
  
87
  if (ISRArbeitsreg == 3)
88
  {
89
    OCR0 = 232;
90
  }
91
  
92
  if (ISRArbeitsreg == 4)
93
  {
94
    SysTick_ms ++;
95
    ISRArbeitsreg = 0;
96
  }
97
  
98
  else 
99
  {
100
    OCR0 = 255;
101
  }
102
  
103
  SysTickCnt = ISRArbeitsreg;
104
}
105
106
107
108
int main(void)
109
{
110
  Init();
111
    
112
    while(1)
113
    {  
114
    switch (SysTick_ms)
115
    {
116
      case   200: OCR1A = 10000; break;
117
      case   488: OCR1A = 8000; break;
118
      case   732: OCR1A = 6000; break;
119
      case  1038: OCR1A = 4000; break;
120
      case  1453: OCR1A = 2000; break;
121
      case  2145: OCR1A = 1000; break;
122
      case  2256: OCR1A = 15555; break;
123
      case  2565: OCR1A = 13555; break;
124
      case  2900: OCR1A = 6443; break;
125
      case  3131: OCR1A = 3452; break;
126
      case  3333: OCR1A = 13431; break;
127
      case  3676: OCR1A = 33333; break;
128
      case  4000: OCR1A = 13333; break;
129
      case  4943: OCR1A = 1532; break;
130
      case  5924: OCR1A = 4245; break;
131
      case  8424: OCR1A = 1200; break;
132
      case  8600: TCCR1B &= ~(1<<CS01); break;
133
      case  8800: TCCR1B |= (1<<CS01); break;
134
      case  9000: OCR1A = 980; break;
135
      case  9200: OCR1A = 960; break;
136
      case  9400: TCCR1B &= ~(1<<CS01); break;
137
      case  9600: TCCR1B |= (1<<CS01); break;
138
      case  9800: OCR1A = 900; break;
139
      case 10000: OCR1A = 890; break;
140
      case 10500: TCCR1B &= ~(1<<CS01); break;
141
      case 10800: TCCR1B |= (1<<CS01); break;
142
      case 12000: OCR1A = 860; break;
143
      case 12500: OCR1A = 850; break;
144
      case 13000: OCR1A = 840; break;
145
      case 13500: OCR1A = 830; break;
146
    }
147
    
148
    if (SysTick_ms >= 14000)
149
    {
150
      SysTick_ms = 0;
151
    }
152
    
153
    
154
    /*if (SysTick_ms - SystickAlt >= 250)
155
    {
156
      OCR1A -= 5;
157
      SystickAlt = SysTick_ms;
158
    }*/
159
160
    }
161
}
162
 
163
 
164
165
void Init(void)
166
{
167
  DDRD  = 0b00011110;
168
  PORTD = 0b00011100;
169
  
170
  
171
172
  OCR1A = 900;
173
    TCCR1B = (1 << CS11) | (1 << WGM12);    //Timer1 im CTC-Modus(Clear Timer on Compare match) Vorteiler 8
174
  
175
  TCCR0 = (1 << WGM01) | (1 << CS01);            //Timer0 CTC, für Systick
176
  OCR0 = 255;
177
  
178
  TIMSK  = (1 << OCIE1A) | (1 << OCIE0);  //nach Timerablauf IRQ auslösen, in dem ein Floppyschritt gemacht wird (bestimmt die Tonhöhe)
179
  
180
  
181
182
  sei(); 
183
}

von hinz (Gast)


Lesenswert?

j. t. schrieb:
> Ich hab ein paar alte 3,5" Diskettenlaufwerke in die Finger bekommen und
> bin nun dabei, ihnen ein neues Leben als Musikinstrument zu verschaffen.

Gähn, das gabs schon in den '70ern.


> Einfach mal Floppy Music bei Youtube eingeben, da findet ihr schon was.

Das gabs damals noch nicht.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

j. t. schrieb:
> Für Kommentare oder Verbesserungsvorschläge bin ich immer gern offen.

Was hälst du von einem Midi In? Dann wäre das etwas universeller und du 
könntest leichter mehrstimmige Orchester aufbauen. Das ist auf der 
Hardware Seite nur der Optokoppler mit Pullup und auf der Software Seite 
ein Midi Receiver per UART.

von Falk B. (falk)


Lesenswert?

@ j. t. (chaoskind)

>Den Code samt Kommentaren wollt ich euch mal präsentieren, evtl habt ihr
>ja mal Lust und die Möglichkeit den Code aufzuspielen und so ein
>Laufwerk anzuschließen um euch die etwas wirre "Melodie" anzuhören =)

Hab kein Floppy.

>Für Kommentare oder Verbesserungsvorschläge bin ich immer gern offen.

Dein Code ist arg BASCOM-artig.

Den SWITCH packt man besser in eine gescheite Tabelle und arbeitet die 
dann über eine kleine Funktion ab. Das ist deutlich kompakter und besser 
erweiterbar bzw. man kann mit neuen Tabellen neue Musik machen.

von J. T. (chaoskind)


Lesenswert?

hinz schrieb:
> j. t. schrieb:
>> Ich hab ein paar alte 3,5" Diskettenlaufwerke in die Finger bekommen und
>> bin nun dabei, ihnen ein neues Leben als Musikinstrument zu verschaffen.
>
> Gähn, das gabs schon in den '70ern.
>
>
>> Einfach mal Floppy Music bei Youtube eingeben, da findet ihr schon was.
>
> Das gabs damals noch nicht.




>> ich hab ein kleines Musikinstrument (nach)gebastelt.

Ich hab ja nie behauptet, etwas völlig neues erfunden zu haben *gg

@Matthias:
Ja das mit dem Midi ist auf weite Sicht geplant, wie schon erwähnt, 
jedoch muss ich mich erstmal mit dem Midiprotokoll auseinander setzen, 
kennst du evtl eine gute Bibliothek? oder muss man das auch komplett 
selbst umsetzen?

@Falk:
Das ist lustig, mit BASCOM hab ich noch nie zu tun gehabt. Wie würde man 
das in C denn mit so einer Tabelle machen? mit nem Array?

btw n paar von den Floppys hab ich hier noch rumfliegen, falls du eins 
haben willst, schreib mir ne PN =)

: Bearbeitet durch User
von Jörg E. (jackfritt)


Lesenswert?

Ein Schaltplan wäre nett. Zzt. Sieht es für mich so aus als müsste man 
direkt an die Motorsteuerung und erstmal Pinbelegungen der Floppy 
rausfinden?

von J. T. (chaoskind)


Lesenswert?

An Schaltplan ist da nicht viel. Nur der Atmega32 in 
Standardbeschaltung, und die 4 Pins vom Floppy. Welche da sind Floppy 
an/aus an Pin 12 der geht an PD1(man kann ihn auch dauerhaft auf Masse 
legen), dann der "Scheibendrehermotor" an/aus auf Pin 16, welcher an PD2 
kommt(diesen kann man auch komplett weglassen), an Pin18 ist der 
Directionpin, um zu bestimmen in welche Richtung der Lesekopf sich 
bewegt, der geht an PD3. Und zu guter Letzt der Steppin auf Pin 24 kommt 
auf PD4. Wobei der Steppin laut nem Pinbelgungsbildchen eigl auf Pin22 
sein sollte, was erst für ein wenig Verwirrung sorgte, aber nach ein 
wenig herumprobieren stellte sich heraus, das er zumindest bei meinem 
Floppy auf Pin24 sitzt.

MfG chaos

von Falk B. (falk)


Lesenswert?

@ j. t. (chaoskind)

>das in C denn mit so einer Tabelle machen? mit nem Array?

Ja.

Es fehlt bei deiner INIT noch ein INIt des Floppy. Denn wie willst du 
sonst sicher sein, dass du den Stepper nicht über den Anschlag fährst? 
Die Ausgangslage ist im allgemeinen unbekannt!

Der Puls für deinen Stepper erscheint mir etwas gering, das sind geade 
mal 2 AVR Takte! Ich weiß nicht welche minimale Pulsbreiten die Floppys 
brauchen, meine aber etwas im µs Bereich mal gelesen zu haben.

Für eine 1ms Zeitbasis macht du ganz schöne Handstände. Das geht 
deutlich einfacher, entweder mit CTC DIREKT und dem passenden 
Reload-Wert oder bei komischen Quarzfrequenzen mit CTC und Timer 1.

#define Floppy 1
#define Motor 2
#define Richtung 3
#define Schritt 4

#define vor 8

DAS ist nicht gut! Eine Mischung aus Bitmasken und Bitnummer! Entscheide 
dich für EINE Variante! Siehe

http://www.mikrocontroller.net/articles/Bitmanipulation#Bitmanipulation_beim_MSP430


Naja, kann man so machen, dann muss man aber immer sowas schreiben..

von scanner (Gast)


Lesenswert?

j. t. schrieb:
> MoinMoin, ich hab ein kleines Musikinstrument (nach)gebastelt.

Das hab ich mir auch schon überlegt, aber eher nach diesem Vorbild:

http://www.youtube.com/watch?v=DkaUsBwe0fo

von J. T. (chaoskind)


Lesenswert?

@ Falk:
Dank dir erstmal für deine Antwort. Aber das mit dem 1ms_Tick hab ich 
zuerst versucht mit dem Timer1 mitzulösen, übers OCR1B, jedoch wurd da 
nie mein Wert erreicht, da ich im OCR1A(frequenzbestimmendes) noch 
kleinere Werte hatte, und somit der Timer immer schon lange vor der ms 
zurückgesetzt wurde.
Und mir ist nichts anderes eingefallen, um auf 1000 Ticks zu 
kommen.(ergibt ja die Millisekunde, bei prescaler 8). Mit höherem 
Prescaler würde ich immer auf krumme Werte kommen, was ja auch hieße, 
das ich jeden so und sovielten Durchlauf mein OCR-Wert ändern müsste?

Der Stepperpuls scheint aber lang genug zu sein, zumindest macht er 
anstandslos seine 70 Schritte in jede Richtung ohne in die Anschlage zu 
fahren, nur bei höheren Frequenzen scheint er Schritte zu verlieren, da 
fährt er dann teilweise nur in eine Richtung, oder auch mal garnicht. 
Evtl liegt das dann aber doch an der Pulslange, ich werd mal versuchen 
damit ein wenig herumzuspielen.

@Scanner:
Das ist natürlich die deluXe Variante *gg

von J. T. (chaoskind)


Lesenswert?

P.S.
Könntest du mir evtl auch noch mal erläutern, warum das so schlimm ist, 
Bitmasken und Nummern gemeinsam zu verwenden? Die Bitmasken brauche ich 
doch für die if-abfrage, ich prüfe doch einfach darauf ob das Bit 
gesetzt oder nicht, und wenn es gesetzt ist gibt das einen Wert/Bitmaske 
auf den ich prüfen kann. Die Bitnummern brauche ich aber um die Pins 
anzusteuern?

Heißt das, ich "soll" statt "#define x 8" "#define x 3"

und statt "if (wasimmer == x(8)" "if (wasimmer > x(3)" schreiben? Oder 
wie kann ich in der Abfrage schieben? Das ist mir in dem Artikel 
irgendwie nicht klar geworden.

vielen Dank schonmal und mit freundlichen Grüßen,
Chaos

von Falk B. (falk)


Lesenswert?

@ j. t. (chaoskind)

>Könntest du mir evtl auch noch mal erläutern, warum das so schlimm ist,
>Bitmasken und Nummern gemeinsam zu verwenden?

Weil man damit durcheinander kommt.

> Die Bitmasken brauche ich
>doch für die if-abfrage, ich prüfe doch einfach darauf ob das Bit
>gesetzt oder nicht,

Gut.

> und wenn es gesetzt ist gibt das einen Wert/Bitmaske
>auf den ich prüfen kann. Die Bitnummern brauche ich aber um die Pins
>anzusteuern?

das kann man genausogut mit Bitmasken machen, es sind praktisch IMMER 
Bitmasken. Siehe mein Artikel oben.
die Bitnummern sind eher ein historisches Erbstück aus den Atmel 
Headerfiles, die sowohl für Assembler als auch C gemacht sind. Und im 
Assembler gibt es spezielle Befehle, die eben die Bitnummern brauchen. 
aus der Bitnummer kann man einfach eine Bitmaske machen

Bitmaske = (1<< bitnummer).

der umgekehrte Weg ist deutlich aufwändiger, wenn gleich mit einem Makro 
lösbar.

Heißt das, ich "soll" statt "#define x 8" "#define x 3"

???

Schreibe so
1
#define MeinPinMotor (1<<PD3)

Das ist SEHR gut lesbar und direkt verständlich!

>und statt "if (wasimmer == x(8)" "if (wasimmer > x(3)" schreiben?

Hä?

Einfach immer so.
1
PORTD |= MeinPinMotor;    // Pin setzen
2
PORTD &= ~MeinPinMotor;   // Pin loeschen

Ich mach es oft sogar noch einfacher und lesbarer.
1
#define MOTOR_EIN   PORTD |= (1<<PD3);
2
#define MOTOR_AUS   PORTD &= ~(1<<PD3);
3
4
5
// im Programm
6
7
MOTOR_EIN
8
9
MOTOR_AUS

Das ist ohne Kommentare voll selbsterklärend.

>wie kann ich in der Abfrage schieben? Das ist mir in dem Artikel
>irgendwie nicht klar geworden.

Schade. Dann lies den Artikel noch mal.
Mehr als dort steht kann ich auch nicht sagen.

von J. T. (chaoskind)


Lesenswert?

Diese Variante gefällt mir sehr gut =)

#define MOTOR_EIN   PORTD |= (1<<PD3);
#define MOTOR_AUS   PORTD &= ~(1<<PD3);

wobei es doch von der Funktionaltiät (sehr wohl aber von der Lesbarkeit) 
her keinen Unterschied macht, ob ich deine Variante wähle oder:

#define Motor 3    und dann zum Bit setzen "PORTD |= (1<<Motor);" und 
zum löschen "PORTD &= ~(1<<Motor);" schreibe?

PD3 ist doch irgendwie als 3 definiert, wenn ich mir recht entsinne?

aber mir ging es hauptsächlich darum dass ich doch:

#define vor 8 definiere um abzufragen, ob der "Lesekopfrichtungspin" 
gesetzt ist, oder nicht.
1
ISR(TIMER1_COMPA_vect)
2
{
3
    uint8_t ISRArbeitsreg = 0;
4
    
5
  ISRArbeitsreg = PORTD & vor;    //Aktuellen Zustand einlesen und prüfen in welche Richtung der letzte Schritt ging
6
  
7
  if (ISRArbeitsreg == vor)      
8
  {      
9
    Schrittzaehler ++;
10
    if (Schrittzaehler >=70)    //um nicht in den Anschlag zu fahren, nach 70 Schritten Richtung wechseln
11
    {
12
      PORTD &= ~(1 << Richtung);
13
      Schrittzaehler = 0;
14
    }

ich lade in das Arbeitsreg den Zustand von PORTD mit "vor" also 8 (oder 
ganz auf den Punkt gebracht mit der Wertigkeit meines Pins fürs Richtung 
wählen) ver-und-et. Wie kann ich das ganze denn mit der Abfrage auf 3, 
also die Bitnummer machen?

Ich hoffe mein Problem ist nun ein kleines bischen klarer geworden =)

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ j. t. (chaoskind)

>Diese Variante gefällt mir sehr gut =)

schön.

>wobei es doch von der Funktionaltiät (sehr wohl aber von der Lesbarkeit)
>her keinen Unterschied macht, ob ich deine Variante wähle oder:

Sicher, aber darum geht es doch! Ich kann auch in Assembler oder gar 
Maschinencode ein und die selben, funktional IDENTISCHEN Programme 
schreiben, aber die Frage ist, wie ist es einfacher und weniger 
fehleranfällig?

>#define Motor 3    und dann zum Bit setzen "PORTD |= (1<<Motor);" und
>zum löschen "PORTD &= ~(1<<Motor);" schreibe?

Klar, aber das ist mehr Schreibarbeit.

>#define vor 8 definiere um abzufragen, ob der "Lesekopfrichtungspin"
>gesetzt ist, oder nicht.

sicher, aber das kann man auch so schreiben.
1
#define Richtung (1<<3)
2
3
ISR(TIMER1_COMPA_vect)
4
{
5
  
6
  if (PORTD & Richtung ) // Richtung plus?
7
  {      
8
    Schrittzaehler++;
9
    if (Schrittzaehler >=70)    //um nicht in den Anschlag zu fahren, nach 70 Schritten Richtung wechseln
10
    {
11
      PORTD &= Richtung;
12
      Schrittzaehler = 0;
13
    }

>ich lade in das Arbeitsreg den Zustand von PORTD mit "vor" also 8 (oder

Laden tut man in C gar nix, man liest einfach Variablen. Auch wenn diese 
Variablen in Wahrheit Hardwareregister sind.
"Laden" ist eine Sprechweise aus Assembler (lade Register x mit y)

>ganz auf den Punkt gebracht mit der Wertigkeit meines Pins fürs Richtung
>wählen) ver-und-et. Wie kann ich das ganze denn mit der Abfrage auf 3,
>also die Bitnummer machen?

Siehe oben.

>Ich hoffe mein Problem ist nun ein kleines bischen klarer geworden =)

Ich wiederhole mich. Lies den Artikel Bitmanipulation und denk 
drüber nach. Es steht ALLES drin!

: Bearbeitet durch User
von J. T. (chaoskind)


Lesenswert?

Aaaahhhhhh nun hat es klick gemacht!
Danke dir für deine Ausführungen, das werd ich nun erstmal umsetzen

von J. T. (chaoskind)


Lesenswert?

Eine Frage noch zur Zeitbasis: Wie würdest du das lösen, wenn der Timer1 
quasi blockiert ist. Die Timer0/2 sind ja nur 8bit breit, da sind beim 
so praktischen 8er prescaler (wenn man auf 8MHz läuft) ja mehrere 
Überläufe nötig, um auf eine Millisekunde zu kommen.

Wenn man andersrum OCR1B nutzt, man könnte da ja in dem 
Frequenzbestimmenden OCR1A ja jede ms 1000 abziehen, hätte aber das 
Gefrickel ja dann dort. Wobei bei rechter Betrachtung ist das deutlich 
weniger davon.

von J. T. (chaoskind)


Lesenswert?

Dabei stelle ich aber gerade fest, dass ich dann in Probleme laufe, wenn 
ich wenn OCR1A kleiner als 1000 wird....

von Falk B. (falk)


Lesenswert?

@ j. t. (chaoskind)

>Eine Frage noch zur Zeitbasis: Wie würdest du das lösen, wenn der Timer1
>quasi blockiert ist.

Ist er das wirklich?

> Die Timer0/2 sind ja nur 8bit breit, da sind beim
>so praktischen 8er prescaler (wenn man auf 8MHz läuft) ja mehrere
>Überläufe nötig, um auf eine Millisekunde zu kommen.

Kann man machen. Man kann aber auch mit einem Prescaler > 8 arbeiten. 
Und bei diesem ehrer einfachen Beispiel muss es wahrscheinlich nicht 
EXAKT 1ms sein, irgendwas mit ein par % Abweichung geht auch.

Oder nimm einen moderneren AVR ala ARmega88, der kann CTC auch auf 
Timer0.

Warum sollte Timer1 blockert sein?

von J. T. (chaoskind)


Lesenswert?

Er ist natürlich nicht wirklich blockiert, ich kann ihn ja benutzen.

Was ich meine ist:
1
ISR(TIMER1_COMPA_vect)
2
{
3
    
4
  
5
  if (PORTD & Rueckwaerts)          //Aktuellen Zustand einlesen und prüfen in welche Richtung der letzte Schritt ging  
6
  {      
7
    Schrittzaehler ++;
8
    if (Schrittzaehler >=70)    //um nicht in den Anschlag zu fahren, nach 70 Schritten Richtung wechseln
9
    {
10
      KopfVor
11
      Schrittzaehler = 0;
12
    }    
13
    else{}
14
  }
15
    
16
  else
17
  {    
18
    Schrittzaehler ++;
19
    if (Schrittzaehler >=70)
20
    {
21
      KopfRueck
22
      Schrittzaehler = 0;
23
    }  
24
    else{}
25
  }
26
  
27
  SchrittLow
28
  SchrittHigh        
29
}
30
31
ISR (TIMER1_COMPB_vect)
32
{
33
  SysTick_ms ++;
34
  OCR1A -= 1000;
35
}

Solange OCR1A größer als 1000 ist, ich den Kopf also langsamer als 1KHz 
ansteuere, ist alles gut. Bspw ist OCR1A 1500 und OCR1B 1000, dann wird 
nach 1000 Ticks ISR (TIMER1_COMPB_vect) angesprungen,OCRA dann auf 500 
sein, nach 500 weiteren Ticks wird dann ISR (TIMER1_COMPA_vect) 
angesprungen, der Kopf also nach 1,5ms bewegt... und dann kommt die Zeit 
auch schon wieder aus dem Tritt...

Ich denke du hast Recht, ich verzichte einfach auf die Genauigkeit und 
nimm den nächst größeren Prescaler...

Und stelle fest, ich brauch garnicht verzichten, Prescaler 64 bis 125 
tuts auch auf Timer0, der kann auch CTC.

Also nochmal vielen Dank für deine Mühe

Chaos

P.S. Gibt es in C eigentlich sowas wie nop? Ich weiß eigentlich soll man 
in einer ISR nicht warten, aber ich würd gern ca 10 nop´s machen, um 
eine Pulslänge > 1µs zu bekommen. Oder "darf" man für sowas eine kleien 
Warteschleife einfügen?

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ j. t. (chaoskind)

>ISR (TIMER1_COMPB_vect)
>{
>  SysTick_ms ++;
>  OCR1A -= 1000;
>}

Das muss

OCR1A += 1000;

heißen. Eben mit diesem "Trick" kann man mit EINEM, kontinuierlich 
durchlaufenden Timer1 und den zwei COMPA/B Interrupts ZWEI vollkommen 
unabhängige Frequenzen erzeugen!
Also KEIN CTC Mode sondern Normalmodus und das Nachstellen von OCR1A/B 
wie oben!

>Solange OCR1A größer als 1000 ist, ich den Kopf also langsamer als 1KHz
>ansteuere, ist alles gut. Bspw ist OCR1A 1500 und OCR1B 1000, dann wird
>nach 1000 Ticks ISR (TIMER1_COMPB_vect) angesprungen,OCRA dann auf 500
>sein, nach 500 weiteren Ticks wird dann ISR (TIMER1_COMPA_vect)
>angesprungen, der Kopf also nach 1,5ms bewegt... und dann kommt die Zeit
>auch schon wieder aus dem Tritt...

siehe oben.

>Und stelle fest, ich brauch garnicht verzichten, Prescaler 64 bis 125
>tuts auch auf Timer0, der kann auch CTC.

Siehst du ;-)

>P.S. Gibt es in C eigentlich sowas wie nop?

Nicht direkt, das ist immer eine compiler- und prozessorabhängige 
Erweiterung (Funktion oder Makro). Bei AVR GCC ist es schon drin.

nop();

> Ich weiß eigentlich soll man
>in einer ISR nicht warten, aber ich würd gern ca 10 nop´s machen, um
>eine Pulslänge > 1µs zu bekommen. Oder "darf" man für sowas eine kleien
>Warteschleife einfügen?

Ja. Man darf auch 100ms warten, wenn es sowieso nix besseres zu tun gibt 
und der Prozessor sich nicht sinnlos ausbremst. Warteschleifen unter 
100us sind mit so einem Delay deutlich einfacher und besser zu machen. 
Dafür gibt es im avr gcc auch schon fertige Funktionen.

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Warteschleifen_.28delay.h.29

von J. T. (chaoskind)


Lesenswert?

So nach deinen Verbesserungsvorschlägen sieht mein Code nun so aus.
Der Tipp mit der Verlängerung der Pulslänge war auch sehr gut, er 
verschluckt nun fast keine Schritte mehr.

Warum man 2 unabhängige Frequenzen erhält, wenn man in der OCR1B ISR 
1000 zum OCR1A addieren soll, ist mir aber irgendwie noch nicht ganz 
klar....

und zu den Arrays muss ich mich auch erstmal einlesen....

für das nop(); muss man wohl noch ne Bibliothek einbinden? Dann lass 
ichs lieber bei meiner Schleife.

Also heir erstmal der Code bis jetzt:
1
/*
2
 *    FloppyMusik.c
3
 *
4
 *    Created: 11.07.2014 08:09:28
5
 *    Author: chaoskind
6
 *    Programm zur Steuerung von 3,5" Floppy Laufwerken, um Töne zu erzeugen
7
 *    
8
 *
9
 *    Pinbelegung(Active Low)    0/1
10
 *    
11
 *    PD1 = Floppy         An/Aus
12
 *    PD2 = Diskmotor            An/Aus
13
 *    PD3 = Richtung Lesekopf    Vor/Zurück
14
 *    PD4 = Schritt
15
 */
16
17
18
19
#define F_CPU 8000000UL
20
21
#define Floppy 1
22
#define Motor 2
23
#define Richtung 3
24
#define Schritt 4
25
26
#define Rueckwaerts (1<<3)
27
28
#define FloppyAn    PORTD &= ~(1 << Floppy);
29
#define FloppyAus    PORTD |= (1 << Floppy);
30
31
#define MotorAn      PORTD &= ~(1 << Motor);
32
#define MotorAus    PORTD |= (1 << Motor);
33
34
#define KopfVor      PORTD &= ~(1 << Richtung);
35
#define KopfRueck    PORTD |= (1 << Richtung);
36
37
#define SchrittLow    PORTD &= ~(1 << Schritt);    //einmal Low High um einen Schritt auszuführen
38
#define SchrittHigh    PORTD |= (1 << Schritt);
39
40
#define TonAus      TCCR1B &= ~(1<<CS01);
41
#define TonAn      TCCR1B |= (1<<CS01);
42
43
#define Grundstellung  DDRD  = 0b00011110;
44
45
46
47
#include <avr/io.h>
48
#include <avr/interrupt.h>
49
50
void Init (void);
51
void Startposition (void);
52
void WildeTonfolge (void);
53
54
55
volatile uint8_t Schrittzaehler = 0;
56
volatile uint8_t Minutenzaehler = 0;
57
58
volatile uint16_t SysTick_ms = 0;
59
60
61
ISR(TIMER1_COMPA_vect)
62
{
63
  uint8_t i = 0;  
64
  
65
  if (PORTD & Rueckwaerts)          //Aktuellen Zustand einlesen und prüfen in welche Richtung der letzte Schritt ging  
66
  {      
67
    Schrittzaehler ++;
68
    if (Schrittzaehler >=60)    //um nicht in den Anschlag zu fahren, nach 60 Schritten Richtung wechseln (von Anschlag zu Anschlag etwa 85)
69
    {
70
      KopfVor
71
      Schrittzaehler = 0;
72
    }    
73
    else{}
74
  }
75
    
76
  else
77
  {    
78
    Schrittzaehler ++;
79
    if (Schrittzaehler >=60)
80
    {
81
      KopfRueck
82
      Schrittzaehler = 0;
83
    }  
84
    else{}
85
  }
86
  
87
  SchrittLow
88
  while (i < 10)
89
  {
90
    i ++;
91
  }
92
  SchrittHigh        
93
}
94
95
ISR (TIMER0_COMP_vect)
96
{
97
  SysTick_ms ++;
98
  if (SysTick_ms == 60000)
99
  {
100
    Minutenzaehler++;
101
    SysTick_ms = 0;
102
  }
103
}
104
105
106
107
int main(void)
108
{
109
  Init();
110
    
111
    while(1)
112
    {  
113
    WildeTonfolge();
114
  }
115
}
116
 
117
 
118
119
void Init(void)
120
{  
121
  Grundstellung
122
  FloppyAn
123
  
124
  OCR1A = 2000;
125
    OCR0 = 125;    
126
  TCCR0 = (1 << WGM01) | (1 << CS01) | (1 << CS00);  //Timer0 CTC Vorteiler 64, für Systick 125Ticks = 1ms    
127
  TIMSK  = (1 << OCIE1A) | (1 << OCIE0);    
128
  
129
  sei(); 
130
  
131
  Startposition();
132
  
133
  cli();
134
  TCCR1B = (1 << CS11) | (1 << WGM12);    //Timer1 im CTC-Modus(Clear Timer on Compare match) Vorteiler 8
135
  sei();
136
}
137
138
139
140
void Startposition (void)
141
{
142
  uint8_t i = 0;
143
  uint8_t SysTick_alt = 0;
144
  
145
  while (SysTick_ms < 100)        //100ms lang 1Schritt/ms um sicher an den Endanschlag zu fahren
146
  {
147
    KopfRueck
148
    
149
    if (SysTick_ms - SysTick_alt)    //wenn eine ms vergangen ist, nächsten Schritt machen
150
    {
151
      SchrittLow
152
      while (i < 10)          //etwas warten, damit Pulslänge > 1µs
153
      {
154
        i ++;
155
      }
156
      SchrittHigh
157
    }
158
159
    SysTick_alt = SysTick_ms;
160
  }
161
  
162
  if (SysTick_ms > 100)          //weitere 15ms 1S/ms um auf definierte Startposition zu kommen.
163
  {
164
    while (SysTick_ms < 115)
165
    {
166
      KopfVor
167
      
168
      if (SysTick_ms - SysTick_alt)
169
      {
170
        SchrittLow
171
        while (i < 10)
172
        {
173
          i ++;
174
        }
175
        SchrittHigh
176
      }
177
178
      SysTick_alt = SysTick_ms;
179
    }
180
  }
181
}
182
183
void WildeTonfolge (void)
184
{
185
  switch (SysTick_ms)
186
  {
187
    case   200: OCR1A = 10000; break;
188
    case   488: OCR1A = 8000; break;
189
    case   732: OCR1A = 6000; break;
190
    case  1038: OCR1A = 4000; break;
191
    case  1453: OCR1A = 2000; break;
192
    case  2145: OCR1A = 1000; break;
193
    case  2256: OCR1A = 15555; break;
194
    case  2565: OCR1A = 13555; break;
195
    case  2900: OCR1A = 6443; break;
196
    case  3131: OCR1A = 3452; break;
197
    case  3333: OCR1A = 13431; break;
198
    case  3676: OCR1A = 33333; break;
199
    case  4000: OCR1A = 45000; break;
200
    case  4943: OCR1A = 28000; break;
201
    case  5924: OCR1A = 4245; break;
202
    case  8424: OCR1A = 1200; break;
203
    case  8600: TonAus break;
204
    case  8800: TonAn break;
205
    case  9000: OCR1A = 980; break;
206
    case  9200: OCR1A = 960; break;
207
    case  9400: TonAus break;
208
    case  9600: TonAn break;
209
    case  9800: OCR1A = 900; break;
210
    case 10000: OCR1A = 890; break;
211
    case 10500: TonAus break;
212
    case 10800: TonAn break;
213
    case 12000: OCR1A = 860; break;
214
    case 12500: OCR1A = 850; break;
215
    case 13000: OCR1A = 840; break;
216
    case 13500: OCR1A = 830; break;
217
  }
218
  
219
  if (SysTick_ms >= 14000)
220
  {
221
    SysTick_ms = 0;
222
  }
223
}

: Bearbeitet durch User
von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

@ j. t. (chaoskind)

>Der Tipp mit der Verlängerung der Pulslänge war auch sehr gut, er
>verschluckt nun fast keine Schritte mehr.

Den Tip hast du aber maximal falsch umgesetzt. Deine Schleife schmeißt 
dir der Optimierer raus. Nimm _delay_us().

>Warum man 2 unabhängige Frequenzen erhält, wenn man in der OCR1B ISR
>1000 zum OCR1A addieren soll, ist mir aber irgendwie noch nicht ganz
>klar....

Ist doch einfach. Der TIMER1_COMPA/B Interrupt wird ausgelöst, wenn 
TIMER1 == OCR1A ist. Wenn man dann in der ISR OCR1A um einen Betrag X 
erhöht (nämlich durch OCR1A += X;), wird exakt X Takte nach dem ersten 
Aufruf die ISR wieder aufgerufen. Und das geht lückenlos, auch beim 
Überlauf des Timers.

>für das nop(); muss man wohl noch ne Bibliothek einbinden? Dann lass
>ichs lieber bei meiner Schleife.

Ist Murks und funktioniert nur, wenn die Optimierungsstufe auf AUS (-0s) 
seht.
1
   if (SysTick_ms - SysTick_alt)    //wenn eine ms vergangen ist, nächsten Schritt machen
2
    {
3
      SchrittLow
4
      while (i < 10)          //etwas warten, damit Pulslänge > 1µs
5
      {
6
        i ++;
7
      }
8
      SchrittHigh
9
    }

Ist eine sehr ungünstige, um nicht zu sagen irreführende Auswertung! 
Schreib DIREKT was du meinst, das ist genauso schnell!

Ich hab mal bissel aufgeräumt, siehe Anhang. Dort sind lange Quelltexte 
besser aufgehoben.

Makros sollte man immer GROSS schreiben, damit man sie als solche 
erkennt. Siehe

Strukturierte Programmierung auf Mikrocontrollern

von J. T. (chaoskind)


Lesenswert?

> Ist doch einfach. Der TIMER1_COMPA/B Interrupt wird ausgelöst, wenn
> TIMER1 == OCR1A ist. Wenn man dann in der ISR OCR1A um einen Betrag X
> erhöht (nämlich durch OCR1A += X;), wird exakt X Takte nach dem ersten
> Aufruf die ISR wieder aufgerufen. Und das geht lückenlos, auch beim
> Überlauf des Timers.


Aber was ist denn wenn wir angenommen wenn wir im OCRA 1000 stehen 
haben, und im OCRb 2100. Dann wird nach 1000 Schritten OCRA auf 2000 
gesetzt. Nach weiteren 1000 wirds auf 3000 gesetzt. Dann geht das ganze 
doch aber ganicht im CTC? sonst wird doch das erste mal bis 1000, das 
nächste mal bis 2000 gezählt. Insgesamt haben wir nun bis 3000 gezählt, 
der Zähler steht auf 2000, wird nun auf 3000 gesetzt. Nun erreicht der 
Zähler nach 2100 weiteren Ticks 2100 (wir hatten nun 5100 Ticks) und 
erst jetzt kann doch OCRB das erste mal zuschlagen? Obwohl wir also OCRB 
auf 2100 haben, löst er das erst mal erst nach 5100 Ticks aus. Und dann 
würd ja der Timer doch durch OCRB auf null gesetzt, Sprich das nächste 
auslösen von OCRA dauert nicht nur 1000 länger als das vorherige mal, 
sondern 1100?

von Falk B. (falk)


Lesenswert?

@j. t. (chaoskind)

>Aber was ist denn wenn wir angenommen wenn wir im OCRA 1000 stehen
>haben, und im OCRb 2100. Dann wird nach 1000 Schritten OCRA auf 2000
>gesetzt. Nach weiteren 1000 wirds auf 3000 gesetzt. Dann geht das ganze
>doch aber ganicht im CTC?

Nein, aber das muss es auch gar nicht. Schrieb ich bereits.

>erst jetzt kann doch OCRB das erste mal zuschlagen? Obwohl wir also OCRB
>auf 2100 haben, löst er das erst mal erst nach 5100 Ticks aus.

NEIN! Du vergisst den mormalen Timer. Vergiss hier CTC!

Der Timer läuft dauerhaft durch von 0-0xFFFF und so weiter. Die beiden 
OCR1A/B setzen sich quasi ihr eigenes Ziel immer um einen Betrag X zum 
akuellen Ziel. Je größer die Schrittweite, umso größer die Frequenz.

von J. T. (chaoskind)


Lesenswert?

AAACHSO, das hatte ich dann durcheinander bekommen, weil du sagtest, man 
könne ja auch den Timer2 (beim Mega88) nehmen, da der auch CTC kann.

Dann ist nun langsam alles klar. Ich hatte gestern noch erste Versuche 
zum Array gemacht.

1
int main(void)
2
{
3
    uint16_t Noten [1][11] = {{1000}};
4
        
5
    uint8_t i = 0;
6
    uint16_t WarteReg = 250;
7
    
8
    Init();
9
    
10
    for (i = 0; i < 11; i++)
11
    {
12
        Noten [0][i+1] = Noten [0][i] * 1.083;
13
    }
14
    
15
    for (i = 0; i < 11; i++)
16
    {
17
        Noten [1][i] = Noten [0][i] * 2;
18
    }
19
    
20
    Minutenzaehler = 0;
21
    SysTick_ms = 0;
22
    
23
    
24
    while(1)
25
    {    
26
        OCR1A = Noten[0][0];
27
        
28
        if (SysTick_ms = 500)
29
        {
30
            OCR1A = Noten[1][1];
31
        }        
32
        
33
        if (SysTick_ms = 1000)
34
        {
35
            OCR1A = Noten[1][1];
36
        }
37
        
38
        if (SysTick_ms = 1500)
39
        {
40
            OCR1A = Noten[1][2];
41
        }
42
    }
43
}

Als ich mir dann die Werte im Array angeschaut hab, hat er von [0][0] - 
[1][2] alle Werte so gesetzt, wie ich mir das vorgestellt habe, aber bei 
den höheren hat er gesagt, die Werte würden nicht existieren (unable to 
evaluate the expression, Ungültiger Zeiger).

Und irgendwie wird die Tonhöhe auch nicht mher geändert, wenn ich den 
OCR1A-Wert ändere....

: Bearbeitet durch User
von J. T. (chaoskind)


Lesenswert?

Nach genauerem hinsehen hab ich festgestellt, das auch [0][11] nicht 
richtig gesetzt wird. Der steht nämlich auf 2000, und der Wert von 
[0][10] ist 2213, und das mal 1,083 sollte ja noch mehr sein und nicht 
2000.

Dazu hab ich dann mal versucht, das i aus der Initialsierungsschleife 
des Arrays kleiner zu machen. Bei 10 hat sich nichts geändert, [0][11] 
war immernoch auf 2000, und bei 9 war dann der 10te Wert wie zu erwarten 
0, aber[0][11] war immernoch auf 2000.

Btw kennst du, oder wer anders der mitliest, zufällig die Richtige 
Formel um aus einem Ton den nächsten zu errechnen?. Ich sollte ja nach 
einer Oktave bei 2000 landen....

X(n+1) = X(n) * 2 ^ n/12? ^= x(n+1) = X(n) * 1,0594?

Macht auf jeden Fall nen besseren Eindruck

: Bearbeitet durch User
von J. T. (chaoskind)


Lesenswert?

x(n+1) = X(n) * 1,0594 war auch zu wenig...

Ich hab nun die math.h eingebunden, und:

for (i = 0; i < 11; i++)
  {
    Noten [0][i+1] = 1000 * pow(2,i/12);
  }

geschrieben. Jetzt werden aber alle Werte mit 1000 geladen. Bis auf das 
ominöse [0][11] das immer noch auf 2000 steht

von Falk B. (falk)


Lesenswert?

@ j. t. (chaoskind)

>Dann ist nun langsam alles klar. Ich hatte gestern noch erste Versuche
>zum Array gemacht.

Aua. Du hast ein schlechtes Konstrukt durch ein anderes ersetz. Hast du 
überhaupt schon mal ein Lehrbuch zui diesem Thema befragt? Und ich mein 
nicht 5 minuten die Seiten überflogen sondern mal ganze Kapitel gelesen, 
drüber nachgedacht und die Beispeile nachvollzogen? Eher nicht. 8-(
1
int main(void)
2
{
3
    uint16_t Noten [][2] = {
4
5
    {  200, 10000 },
6
    {  488, 8000 },
7
    {  732, 6000 },
8
    { 1038, 4000 },
9
    { 1453, 2000 },
10
    { 2145, 1000 },
11
    { 2256, 15555 },
12
    { 2565, 13555 },
13
    { 2900, 6443 },
14
    { 3131, 3452 },
15
    { 3333, 13431 },
16
    { 3676, 33333 },
17
    { 4000, 13333 },
18
    { 4943, 1532 },
19
    { 5924, 4245 },
20
    { 8424, 1200 },
21
    { 8600, 0 },
22
    { 8800, 1 },
23
    { 9000, 980 },
24
    { 9200, 960 },
25
    { 9400, 0 },
26
    { 9600, 1 },
27
    { 9800, 900 },
28
    {10000, 890 },
29
    {10500, 0 },
30
    {10800, 1 },
31
    {12000, 860 },
32
    {12500, 850 },
33
    {13000, 840 },
34
    {13500, 830 }
35
    };
36
    
37
    uint8_t i;
38
    uint16_t anzahl;
39
    uint16_t zeit;
40
    uint16_t ton;
41
    uint16_t WarteReg = 250;
42
    
43
    Init();
44
        
45
    Minutenzaehler = 0;
46
    SysTick_ms = 0;
47
    
48
49
    i=0;
50
    anzahl = sizeof(Noten)/4;
51
52
    while(1)
53
    {   
54
         
55
        zeit = Noten[i][0];
56
        ton = Noten[i][1];
57
        while (SysTick_ms != zeit);       
58
        
59
        if (ton > 1)
60
        {
61
            OCR1A = ton;
62
        } else if (ton == 0)
63
        {
64
            TonAus 
65
        } else
66
        {
67
            TonAn 
68
        }
69
70
        i++;
71
        if (i == anzahl)
72
        {
73
            i=0;
74
            SysTick_ms = 0;
75
        }         
76
    }
77
}

von Falk B. (falk)


Lesenswert?

@ j. t. (chaoskind)


>x(n+1) = X(n) * 1,0594 war auch zu wenig...

>Ich hab nun die math.h eingebunden, und:

Was soll der Unsinn? Hör auf zu frickeln!

von Karl H. (kbuchegg)


Lesenswert?

j. t. schrieb:
> x(n+1) = X(n) * 1,0594 war auch zu wenig...
>
> Ich hab nun die math.h eingebunden, und:
>
> for (i = 0; i < 11; i++)
>   {
>     Noten [0][i+1] = 1000 * pow(2,i/12);
>   }


i/12 ist eine komplizerite Schreibweise für 0

Wenn schon dann i/12.0 (und ein C Buch lesen)

Allerdings: ich würde mir eine derartige Notentabelle ehrlich gesagt auf 
dem PC in Excel generieren und dann nur noch die fertigen Werte im 
AVR-Programm in einer Notentabelle eintragen. Das ist einmalig zwar 10 
Minuten arbeit, erspart aber dem AVR die Floating Point Rechnerei und 
erleichtert auch das Erstellen von Musikstücken, wenn man in die 
'Partitur' nicht irgendwelche Frequenzkennwerte eintragen muss, sondern 
dort "echte" Noten einträgt.

Ob man die Umsetzung Note zu Frequenzwert mittels #define erledigt, oder 
ab man dazu ein Array zu Hilfe nimmt, ist Ansichtssache. Ein 
'Übersetzungsarray' hätte den Charm, das man dann in der Partitur für 
die Tonhöhe nicht mehr einen uint16_t benötigt, sondern nur noch einen 
uint8_t


Aber am wichtigsten scheint mir zu sein, dass du erst mal deine 
C-Kenntnisse aufpolierst. Wenn du mit 90% der Möglichkeiten deiner 
Programmiersprache gar nicht umgehen kannst, ist es zu früh ein Projekt 
anzufangen.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ j. t. (chaoskind)

>Nach genauerem hinsehen hab ich festgestellt, das auch [0][11] nicht
>richtig gesetzt wird. Der steht nämlich auf 2000, und der Wert von
>[0][10] ist 2213, und das mal 1,083 sollte ja noch mehr sein und nicht
>2000.

In deinem gepostet Code wird lediglich EIN EINZIGER Wert initialisiert, 
der Rest ist undefiniert!
Wi wären dir sehr verbudnen, wenn du auch den Code posten würdest, über 
den du auch redestr. Siehe Netiquette!!!

>Dazu hab ich dann mal versucht, das i aus der Initialsierungsschleife
>des Arrays kleiner zu machen. Bei 10 hat sich nichts geändert, [0][11]

MÜLL!!! C-Arrays werden ganz normal mit den Werten initialiseirt, die 
man hinschreibt.

von J. T. (chaoskind)


Lesenswert?

Wow, das klappt auf Anhieb! Danke dir Falk =).
Ich verstehe nur nicht wieso

anzahl = sizeof(Noten)/4;

du hier durch vier und nicht durch zwei teilst. Das Array hat doch 2 
Elemente pro Ton?

Und ja du hast Recht, das meiste C das ich "kann", hab ich aus 
irgendwelchen Tutorials zusammenüberflogen.... Die Beispiele die da drin 
waren, waren meist irgendwie so abstrakt, das ich die nicht meinen 
Problemen zuordnen konnte. Kannst du da evtl eines Empfehlen?


@Karl-Heinz

danke für den Hinweis i/12.0. Bei Int wird nach dem Komma einfach 
abgeschnitten oder? Das erklärt dann natürlich, das überall der selbe 
Wert reinkam.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

j. t. schrieb:
> Wow, das klappt auf Anhieb! Danke dir Falk =).
> Ich verstehe nur nicht wieso
>
> anzahl = sizeof(Noten)/4;
>
> du hier durch vier und nicht durch zwei teilst. Das Array hat doch 2
> Elemente pro Ton?

Ein Array-Eintrag (eine Zeile) besteht aus 2 uint16_t. Das sind in Summe 
4 Bytes.


> danke für den Hinweis i/12.0. Bei Int wird nach dem Komma einfach
> abgeschnitten oder?

Nein.
i ist ein int (mit einem Wert von 0 bis 11)
12 ist ein int

Also wird hier eine int DIvision gemacht. Die erzeugt keine 
Nachkommastellen. Da wird also nichts abgeschnitten, weil in erster 
Linie gar keine Nachkommastellen entstehen.

C-Buch!
(warum glaubt das bloss keiner? Solche Bücher haben nicht umsonst um die 
200 Seiten. Die sind ja nicht nur dazu da, dass Druckerschwärze 
verbaucht wird)

von J. T. (chaoskind)


Lesenswert?

>Also wird hier eine int DIvision gemacht. Die erzeugt keine
>Nachkommastellen. Da wird also nichts abgeschnitten, weil in erster
>Linie gar keine Nachkommastellen entstehen.

Das war, was ich versuchte auszudrücken... bei 1/12 kommt 0 raus, weil 
es ja mathematisch betrachtet 0.083.... sind, aber weil wir mit int 
rechnen, gibt es keine Nachkommastellen(also sind sie ja quasi irgendwie 
doch abgeschnitten, zumindest aus mathematischer Perspektive) also 0.

von J. T. (chaoskind)


Lesenswert?

>In deinem gepostet Code wird lediglich EIN EINZIGER Wert initialisiert,
>der Rest ist undefiniert!

uint16_t Noten [1][11] = {{1000}};

hiermit wird der eine Wert initialisiert,

for (i = 0; i < 11; i++)
    {
        Noten [0][i+1] = Noten [0][i] * 1.083;
    }

und hiermit dann die restlichen Werte, achsooo zugewiesen und nicht 
initialisiert, die sprachlichen Feinheiten.

Die Undefiniertheit sollte dann doch aber nach der Schleife vorbei sein? 
also den Teil hab ich so aus einem Tutorial, da füllt er seine Arrays 
auch per Schleife.

KarlHeinz schrieb:
>Ein Array-Eintrag (eine Zeile) besteht aus 2 uint16_t. Das sind in Summe
>4 Bytes.

Ahja klar, das macht Sinn.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Was in deinem Programm auch noch nicht stimmt (stimmte)
1
    uint16_t Noten [1][11] = {{1000}};
2
        
3
...
4
        Noten [0][i+1] = Noten [0][i] * 1.083;
5
...
6
        Noten [1][i] = Noten [0][i] * 2;

wird ein Array definiert, dann wird die ANzahl der Elemente angegeben!
Ein Array
1
  int a[5];
besteht aus 5 Elementen. Diese sind
1
  a[0], a[1], a[2], a[3], a[4]
Zähl nach, sind genau 5 Stück.

Aber: der höchste zulässige Index in das Array ist 4 und nicht 5!
Ein Element a[5] existiert schlicht und ergreifend nicht.

Wenn du also ein
1
  uint16_t Noten[1].....
hast und dann auf
1
    Noten[1][.....
beschreibst, dann überbügelst du dir irgendwas im Speicher, aber sicher 
nicht das Element mit dem ersten Index 1. Den das existiert schlicht und 
ergreifend nicht.



Genau deshalb halte ich nicht viel von Online-Tutorien. Denn 80% von den 
wichtigen Sachen stehen da nun mal nicht drinnen. Bis jetzt hab ich kaum 
ein C-Tutorial gesehen, das nicht mit viel Enthusiasmus gestartet worden 
wären, bei dem es dann aber nicht so gewesen wäre, dass der Autor 
irgendwann aufgegeben hat, nachdem er gemerkt hat, dass sich C eben 
nicht in 10 HTML Seiten erklären lässt.
Am Besten schneiden immer noch die Online-Versionen von diversen 
Vorlesungsunterlagen ab. Aber auch die enthalten des öfteren Fehler bzw. 
Unvollständigkeiten, die dann eben direkt in der Vorlesung angesprochen 
werden und die der Studierende handschriftlich in seinen Unterlagen 
ergänzt. War man aber nicht persönlich in der Vorlesung, dann bleibt man 
auf den Ungenauigkeiten oder Fehlern sitzen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Und ob du hier
1
    for (i = 0; i < 11; i++)
2
    {
3
        Noten [1][i] = Noten [0][i] * 2;

den Wert für Noten[1] aus Noten[0] errechnen willst, ist meiner Meinung 
nach auch mehr als fragwürdig.

Generell wäre hier für mich der perfekte Zeitpunkt, um das nächste 
C-Konzept, nämlich das einer Struktur, zum Einsatz zu bringen.
Aber wie so oft: man kann natürlich nur das benutzen, was man auch 
kennt. Genau deshalb ist es so wichtig, erst mal einen einigermassen 
fundierten Überblick über die Möglichkeiten meiner Programmiersprache zu 
haben.
Wer nur einen Hammer hat den er kennt, wird sich nun mal beim Hausbau 
schwer tun.

: Bearbeitet durch User
von J. T. (chaoskind)


Lesenswert?

In diesem Fall wäre das möglich, die Werte für noten[1] aus Noten[0] zu 
errechnen, da eine Oktave ja jeweils eine Halbierung/Verdopplung ist.

>Generell wäre hier für mich der perfekte Zeitpunkt, um das nächste
>C-Konzept, nämlich das einer Struktur, zum Einsatz zu bringen.
>Aber wie so oft: man kann natürlich nur das benutzen, was man auch
>kennt.

Ja da haste auch unangefochten Recht. Von Structs hab ich mal gehört, 
aber kennen tu ich die nun wirklich nicht. Ich bin mal auf nem STM32F429 
auf sie gestoßen, und konnte ihnen so überhaupt nichts abgewinnen. Aber 
das lag glaub ich am Aufbau selbiger.
Das was ich davon verstanden hab geht wohl in die Richtung, das man  ein 
Struct als einen Datentyp wie bspw Int betrachten kann, nur das es in 
diesem mehrere unterschiedliche Datentypen geben kann?

von Falk B. (falk)


Lesenswert?

@ j. t. (chaoskind)

>Ja da haste auch unangefochten Recht. Von Structs hab ich mal gehört,
>aber kennen tu ich die nun wirklich nicht. Ich bin mal auf nem STM32F429
>auf sie gestoßen, und konnte ihnen so überhaupt nichts abgewinnen. Aber
>das lag glaub ich am Aufbau selbiger.

Was zum Geier willst du mit einem 32 Bit ARM, wenn du nicht mal die 
Grundlagen von C beherrschst?

Kauf dir eines der Standardbücher von C und arbeite es KOMPLETT durch!
Das kann dir kein Tutorial, Forum, STM32 oder die Mutti abnehmen!

von J. T. (chaoskind)


Lesenswert?

Den gabs mal für umme mit nem Touchscreen, da kam "brauch ich" deutlich 
vor "kann ich" *gg

Ich hatte gehofft, die Dinger würden sich ähnlich komfortabel in ASM 
programmieren lassen wie die AVRs. Irgendwann stellte ich fest "ich muss 
C lernen". nuja und immer wenn sich mal n Moment findet, bastel ich ein 
wenig rum.

Eigentlich kann ich garnicht verstehen, warum ich mich so gegen ein Buch 
sträube. Ich dachte ja immer, das ist völlig übertrieben mit den Büchern 
bis ich dann eines Tages mal einen Tietze und Schenk in den Händen 
hielt, und feststellte dass die Elektronikkenntnisse mit so einem 
Schmöker rasant größer werden.

Ich vermute, ich sollte wirklcih mal über meinen Schatten springen und 
mir n K&R (so heißt doch der "Tietze und Schenk" für C?) besorgen.

von J. T. (chaoskind)


Angehängte Dateien:

Lesenswert?

Sodele, ich hab noch mal ein wenig am Konzept getüftelt. Das 
Tonabspielen lasse ich nun eine Funktion übernehmen, der ich die 
Parameter Tonlänge, Note und Oktave übergebe.

Den Quelltext gibs als Anhang.

von Falk B. (falk)


Lesenswert?

@ j. t. (chaoskind)

>    FloppyMusik.c (6 KB, 0 Downloads) | Codeansicht

Das Konzept der Arrays schon wieder vergessen? :-(

von J. T. (chaoskind)


Lesenswert?

Nein nicht vergessen, ich hatte angefangen es gar mit nem Struct zu 
versuchen.

struct AktuellerTon
{
uint8_t Laenge
uint8_t Ton
};

Und wollte dann Ton und Laenge aus unserem Array holen. Dabei kam mir 
das dann aber irgendwie total unflexibel vor, zu jedem Ton auch die Zeit 
festdabei zuhaben, statt die Töne einzeln gelistet, und dazu dann 
beliebige Zeiten zuzugeben.

Irgendwie gefällt mir das besser, wenn ich statt nem Toene[x] einfach 
Gis schreiben kann. Oder kann ich Arrays auch so benutzen das ich den 
Wert von Toene[x] mit Gis aufrufen kann? wäre das dann da nächste 
Konstrukt, nämlich das des Pointers? noch son den Ding, von dem ich 
bisher nur hörte, aber nichts mit anzufangen wusste. Wie man so ein 
Array benutzt, ist mir aus diesen abstrakten Tuts nämlich nie so ganz 
klar geworden, aber nun mit einem Beispiel wo ich einen Bezug zu und 
eure Hilfe hatte, ist mir klargeworden, wie man so ein Array einzusetzen 
hat. Wobei es dafür sicher noch mehr Verwendungsmöglichkeiten gibt die 
ich noch nicht kenne.

Ich hab die Töne nochmal ein klein wenig geändert. Sie sind nun einfach 
A-Gis und der Funktion wird noch eine Variable Oktave übergeben.

void TonSpielen(uint16_t Dauer, uint16_t Ton, uint8_t Oktave)
{
    Dauer = (Dauer * 10) + zeitAlt;
    Ton = Ton * Oktave;

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.