Forum: Mikrocontroller und Digitale Elektronik Auf der Suche nach Ordnung =)


von J. T. (chaoskind)


Angehängte Dateien:

Lesenswert?

MoinMoin,
dann versuche ich mal, wie im letzten Beitrag angekündigt, mein Problem 
zu formulieren:

Ich bin gerade dabei, meine Floppy-Musikmaschine zu erweitern, das sie 
Musik auf 8 Floppys gleichzeitig machen kann.

Dazu hab ich mir 8Structs geschaffen, die jeweils Tonhöhe des nächsten 
Tones, Tondauer des nächsten Tones und bisher gespielte Gesamtdauer 
beiinhalten.

Nachdem ein Ton gespielt wurde, und die Systemzeit die Zeit von 
Gesamtdauer auf dem entsprechenden Kanal erreicht hat, wird ein Flag 
gesetzt.

Dann wird die kürzeste Gesamtdauer herrausgesucht, (das ist der nächste 
zu ändernde Ton) im entsrpechenden Struct wird die Dauer des nächsten 
Tons rausgesucht, und zur Gesamtdauer hinzuaddiert.

Der Teil klappt soweit auch ganz gut.

Die Sortierfunktion benötigt im Worst-Case (alle Elemente falschrum, 
also in aufsteigenden Werten, wenn die Funktion absteigend sortiert) 
5155 CPU-Takte was bei 8MHz 644.38µs entspricht.

Da höchstens auf die Millisekunde genau neue Töne kommen, passt das 
soweit.

Jetzt kommt aber mein Problem. Ich muss ja nicht nur "umschalten" wenn 
der neue Ton kommt, sondern die Tonerzeugung selbst ist ja ein ständiges 
Umschalten mit einer Frequenz der Tonhöhe. Sprich wenn ich einen Ton von 
1KHz spielen möchte, müsste ich schon jede ms den Lesekopf einen Schritt 
ausführen lassen. Bei höheren entsprechend öfter.

Eigl wollte ich die Tonerzeugung genauso wie die Tonauswahl machen. Also 
ein Array in dem drin steht, bei welchem Zählerstand der Pin getoggelt 
werden muss, welches nach jedem toggeln neu sortiert wird, damit dann 
wieder der Ton mit dem niedrigsten Wert am Anfang steht. Nur kann ich 
hier ein wesentlich kleineres Zeitfenster haben als eine ms.

Nehmen wir an, ich hab 2 Tone, und mein Timer läuft mit Prescaler 
8(sprich ein Timertick = 1µs. Ton1 toggelt immer wenn der Timer 1000 
Schritte weiter ist, (also 1KHz) Ton2 ist ein Tick tiefer, sagen wir bei 
Timer 1050,(ein über den Daumen gepeilter Halbton tiefer).

Dann müsste ich ja die Liste mit den Umschaltzeiten nach 1000µs neu 
sortieren, was im Worst-Case aber 644.38µs entspricht (Es wird ja gleich 
sortiert wie oben, und die Elementezahl ist gleich), ich hätte hier aber 
nur 50 µs Zeit.

Meine Frage ist nun, ob es da ein besseres Konzept zu Sortierung gibt 
oder meinetwegen auch zum Auswählen gibt.

Ich hoffe, meine Problem ist halbewegs verständlich rübergekommen.

MfG Chaos.

Im Anhang gibs auch nochmal den Quelltext, den ich bisher geschrieben 
hab. Gerne auch hierzu Kommentare, Verbesserungsvorschlage usw. (Ich 
hoffe, dem ein oder anderen fällt auf, das ich langsam versuche, mir 
sowas wie strukturiertes Programmieren zu lernen.)

von J. T. (chaoskind)


Angehängte Dateien:

Lesenswert?

Direkt die nächste Frage:
Mir wurd das alles ein wenig unübersichtlich, und nach ein wenig lesen 
hab ich es tatsächlich hinbekommen, eine Funktion in eine extra .c Datei 
"auszulagern".

(Meine Sortierfunktion, bei der mir noch ein zwei Kniffe eingefallen 
sind, um sie kleiner zu machen. Zumindest an Codezeilen. Denn dank der 
Zeigerarithmetik kann ich die Funktion nun endlich so schreiben, das ich 
inkrementieren kann, statt jedes Element einzeln mit if abzufragen.=) so 
aber nun zurück zum Thema)

In meiner .h Datei habe ich mit:
void Sortieren (uint16_t SortierArray[], uint8_t HerkunftFlag)  meine 
Funktion deklariert. In dieser tauchen noch weitere Variablen auf, 
welche ich aber nicht in der .h deklarieren kann, wenn ich das tue 
bekomme ich den Fehler die Variable sei unbekannt. So wie ich das 
verstanden hab, soll man in der .h alle Variablen deklarieren, aber 
nicht definieren. (Ich meine auf einer Seite stands andersrum, ich hab 
beide Varianten probiert). Aber anscheinend klappt das hier nur, wenn 
ich lediglich das bekannt mache, was ich der Funktion übergebe... woran 
könnte das liegen?


P.S. Könnte ein Mod das bitte ins GCC-Unterforum schieben? ich glaube, 
da ists doch wieder besser aufgehoben. Danke =)

P.P.S. In der main frage ich noch einzeln per if ab, wie könnte ich das 
per Inkrementierung lösen?

: Bearbeitet durch User
von Kai S. (zigzeg)


Lesenswert?

j. t. schrieb:
> Die Sortierfunktion benötigt im Worst-Case (alle Elemente falschrum,
> also in aufsteigenden Werten, wenn die Funktion absteigend sortiert)
> 5155 CPU-Takte was bei 8MHz 644.38µs entspricht.

Ich denke nicht dass es noetig ist, das ganze Array zu sortieren da da 
ja nur die kleinste Zeit suchst. Um das Minimum zu suchen must du nur 
einmal ueber das Array laufen, und den Index des kleinsten Wertes merken 
!

ZigZeg

von J. T. (chaoskind)


Lesenswert?

bisher siehts so aus:



    if (SysTick_ms == Melodie0.Gesamtdauer)
    {
      NeuerTonFlag |= (1 << 0);
    }

    if (SysTick_ms == Melodie1.Gesamtdauer)
    {
      NeuerTonFlag |= (1 << 1);
    }


Das hier ist mein struct

struct Melodie{
  uint16_t Ton[50];
  uint16_t Dauer[50];
  volatile uint16_t Gesamtdauer;
}Melodie0, Melodie1, Melodie2, Melodie3, Melodie4, Melodie5, Melodie6, 
Melodie7;


Wäre das dann



for (i = 0; i < 8; i++)
{
    ptr = &(Melodie + ((i+1)*(50 *2)+1) //i+1 damit beim nullten Element
    if (*ptr = SysTick_ms)            //nicht mal null, 100 mal einmal 
für
    {                                 //jedes Element Ton und Dauer, 
plus
        do blabla                     //ein für die Gesamtdauer
    }
}

könnte das so klappen?

von J. T. (chaoskind)


Lesenswert?

Kai S. schrieb:
> j. t. schrieb:
>> Die Sortierfunktion benötigt im Worst-Case (alle Elemente falschrum,
>> also in aufsteigenden Werten, wenn die Funktion absteigend sortiert)
>> 5155 CPU-Takte was bei 8MHz 644.38µs entspricht.
>
> Ich denke nicht dass es noetig ist, das ganze Array zu sortieren da da
> ja nur die kleinste Zeit suchst. Um das Minimum zu suchen must du nur
> einmal ueber das Array laufen, und den Index des kleinsten Wertes merken
> !
>
> ZigZeg

Hey das ist mal ein kluger Tip!! Danke dafür, d.h. ich kann direkt nach 
dem ersten Durchlauf abbrechen, da ja automatisch dann schon das 
kleinste Element da ist!!

Werd ich direkt mal umsetzen

von Kai S. (zigzeg)


Lesenswert?

j. t. schrieb:
> P.P.S. In der main frage ich noch einzeln per if ab, wie könnte ich das
> per Inkrementierung lösen?

j. t. schrieb:
> könnte das so klappen?

1. Mach aus Melodie ein Array:
1
struct Melodie{
2
  uint16_t Ton[50];
3
  uint16_t Dauer[50];
4
  volatile uint16_t Gesamtdauer;
5
}Melodie[8];

und dann einfach:
1
int i;
2
for(i = 0; i < 8; ++i) {
3
if (SysTick_ms == Melodie[i].Gesamtdauer)
4
    {
5
      NeuerTonFlag |= (1 << i);
6
    }

und fertig. Brauchst keine wilden ptr hier ...

von J. T. (chaoskind)


Lesenswert?

j. t. schrieb:
> for (i = 0; i < 8; i++)
> {
>     ptr = &(Melodie + ((i+1)*(50 *2)+1) //i+1 damit beim nullten Element
>     if (*ptr = SysTick_ms)            //nicht mal null, 100 mal einmal
> für
>     {                                 //jedes Element Ton und Dauer,
> plus
>         do blabla                     //ein für die Gesamtdauer
>     }
> }

soll das hier heißen:
1
for (i = 0; i < 8; i++)
2
{
3
    ptr = &(Melodie + ((i+1)*(50 *2)+1) //i+1 damit beim nullten Element
4
    if (*ptr = SysTick_ms)        //nicht mal null, 100 mal einmal für
5
    {                             //jedes Element Ton und Dauer, plus
6
        do blabla                 //ein für die Gesamtdauer
7
    }
8
}

: Bearbeitet durch User
von Easylife (Gast)


Lesenswert?

Mit einem Interrupt alle 1ms wirst du nicht weit kommen (für den 
"Klangerzeuger").
Damit kannst du max. Frequenzen von 500 Hz abspielen, und die Abstufung 
der Tonhöhen ist sehr grob.

Ich mache mal einen Vorschlag:

Du implementierst eine Interrupt Routine, die nur für die Klangerzeugung 
zuständig ist.
Diese wird (nach Möglichkeit) so 20000x pro Sekunde aufgerufen.

In der Interrupt Routine hast du 8 Counter, die runterzählen.
Immer wenn ein Counter auf 0 ist, setzt du ihn wieder auf einen Wert, 
den du dir von einer globalen Variable holst, und schubst den 
entsprechenden Stepper-Motor einen Schritt.
Du brauchst dann noch flags, ob der Kanal überhaupt einen Ton spielen 
soll.
Zusätzlich inkrementierst du in der Interrupt-Routine einfach eine 
globale 32-bit Variable, das dient dir als "Timecode".
Bei 20KHz reicht das für 1h Musik.

Dein Hauptprogramm macht dann nichts anderes mehr, als die 8 globalen 
Variablen und die "Kanal-soll-überhaupt einen ton spielen"-flags 
entsprechend zum Timecode zu setzen.

Dazu brauchst du eigentlich in der Firmware auch nichts sortieren.
Lege einfach zwei arrays an. Eines mit einem 32-bit Wert für den 
Timecode, und ein 8-dimensionales für die Tonhöhen, die zu diesem 
Timecode gehören.

Dein Hauptprogramm wartet jetzt einfach, bis der Timecode, den die 
Interrupt Routine erzeugt, dem ersten Timecode in deinem Musik-Array 
entspricht (bzw. >= ist).
In dem Moment setzt du die globalen Counter-Werte und gehst in deinen 
Arrays einen Schritt weiter... bis die letzte Note erklungen ist.

Den Counterwert für eine Note errechnest du dir, indem du im Internet 
eine Frequenztabelle für Noten suchst, f sei die Frequenz der Note, dann 
wäre der zu setzende Counter-Wert 20000/2/f

Also zum Beispiel für ein 440Hz A wäre der Counter 22.73, aufgerundet = 
23.

von J. T. (chaoskind)


Lesenswert?

Genial danke... ich merkte grad, das meins genau daran scheitert, das 
Melodie[i] ja grad nicht funktioniert!!!

von J. T. (chaoskind)


Lesenswert?

@easylife:
Danke für den Hinweis, aber dessen bin ich mir bewusst, das ist nur das 
Zeitraster, in dem die Töne geändert werden können. Die Töne selbst werd 
ich mit timer1 mit prescaler 8, also 1µs Zeitfenster erzeugen. Siehe 
auch mein Projekt mit nur einem Floppy.

Beitrag "Musikalisches Diskettenlaufwerk"

aber nun stehe  ich halt vor dem Problem, in das 1ms Zeitfenster auch 
noch ne Sortierfunktion reinzuquetschen, die mir den kleinsten Wert 
raussucht, wann es auf Timer1 an welchem Port Zeit ist, dem Lesekopf 
"Beweg dich!!!" zu sagen...

aber der Tip die Schleife nur einmal durchlaufen zu müssen, bringt mich 
schonmal ne Ecke nach vorn

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


Lesenswert?

@Easylife

Ich hab erst beim 2ten mal lesen begriffen, was für ein geniales 
Konstrukt das ist.

wobei das wären doch pro Tonänderung 4byte für den Timecode+ 8byte für 
die Tonhöhen, wobei ich fast glaube, das 8bit Auflösung noch zu gering 
ist, ich hab in der ersten Version mit 16bit gearbeitet. Wären also 12 
byte pro Tonänderung repsektive 20Byte bei 16bit. Ich hab grad nochmal 
gelesen 8-dimensional nicht bit. dann wäre ich bei ca 15k Tonwechseln. 
Damit sollte was zu machen sein, knapp 2000 pro Kanal, verteilt auf ne 
stunde macht das einen Tonwechsel alle 1,8 Sekunden auf jedem Kanal.

Ist die "Schätzung" soweit Richtig? der Mega32 hat ja 32k ca 2k gehen 
fürs programm drauf, macht ca 30k für Tonwechsel.

oder sind das etwa kilobit Speicherplatz? ~panisch nachm Datenblatt wühl

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


Lesenswert?

Ich stelle grad fest, structs werden im Ram und nicht im Flash 
abgelegt... naja eigentlich ja logisch, ständig am flash rumwerkeln wäre 
wohl nicht so gut. (der hat doch "nur" ein paar 1000-10000 writecycles?)

als ich Testhalber mal
1
struct Tonaenderung
2
{
3
  uint32_t TimeCodePosition;
4
  uint16_t Tonhoehen[7];
5
  uint8_t AnAusFlag;
6
    
7
}Tonaenderung[1000];
probiert hab, war mein ram zu 900irgendwas % voll. D.h. wohl doch eher 
nur um die 100 Tonänderungen? =(

von Easylife (Gast)


Lesenswert?

j. t. schrieb:

> wobei das wären doch pro Tonänderung 4byte für den Timecode+ 8byte für
> die Tonhöhen, wobei ich fast glaube, das 8bit Auflösung noch zu gering
> ist,

Ja, ich hab kurz nach dem "absenden" drücken auch festgestellt, dass das 
8-dimensionale Array doof ist.
Guck dir mal MIDI files an.
Es reicht pro Note aus einen Timecode zu speichern, und die Tonhöhe.
Du kannst als Tonhöhe ja direkt die (Halbton-)Note nehmen, dazwischen 
gibts ja nichts.
Dann brauchst du nur noch ein Lookup-Table, das dir zu jeder Note den 
entsprechenden Counterwert liefert.

Die 8 Dimensionen des Arrays sind natürlich Mist, da du jede Menge 
Informationen über Kanäle speicherst, die sich gar nicht ändern.

Es spricht nichts dagegen, die "Noten-Liste" so zu gestalten, dass der 
gleiche Timecode mehrmals hintereinander vorkommt.
Du brauchst also einen Timecode und dazugehörig die Kanal-Nummer, wohin 
die Note soll, und die Note an sich. Bei MIDI gibts dann zusätzlich noch 
die "Velocity", in deinem Fall ja unerheblich. Eine Tonhöhe 0 auf einem 
Kanal kann ja bedeuten: Sound aus.
Wenn sich also 4 Stimmen gleichzeitig ändern, hast du also 4 Einträge 
mit gleichem Timecode, jeweis aber mit unterschiedlichen Kanal-Nummern 
und Noten.
Dadurch kommst du mit 3 eindimensionalen Arrays für das Musikstück klar:
32-bit Timecode, 8-bit Kanalnummer, 8-bit Note (0=off)
+ Lookuptable für Note->Timerwert

von Gaestchen (Gast)


Lesenswert?

j. t. schrieb:
> uint32_t TimeCodePosition;

Du brauchst keine 32bit. Du must ja nur speichern wieviel zeit vergehen 
muß  bis zur nächsten änderung. Sprich statt bei Minute 32 7 Sekunden, 
speicherst du die Information in 50ms.
 Da meistens auch nur bestimmte Längen erforderlich sind, kannst du 
diese auch in eine Tabelle ablegen und kommt dann evt sogar mit 8 Bit 
(oder noch weniger) für die Zeit hin. Das erspart Dir 3 Byte.

von J. T. (chaoskind)


Lesenswert?

Easylife schrieb:
> Du brauchst also einen Timecode und dazugehörig die Kanal-Nummer, wohin
> die Note soll, und die Note an sich. Bei MIDI gibts dann zusätzlich noch
> die "Velocity", in deinem Fall ja unerheblich. Eine Tonhöhe 0 auf einem
> Kanal kann ja bedeuten: Sound aus.
> Wenn sich also 4 Stimmen gleichzeitig ändern, hast du also 4 Einträge
> mit gleichem Timecode, jeweis aber mit unterschiedlichen Kanal-Nummern
> und Noten.
> Dadurch kommst du mit 3 eindimensionalen Arrays für das Musikstück klar:
> 32-bit Timecode, 8-bit Kanalnummer, 8-bit Note (0=off)
> + Lookuptable für Note->Timerwert

Ja das seh ich dann aber das gleich Problem an anderer Stelle. Nehmen 
wir an ich hab pro Kanal 3 Tonänderungen. jeweils zu Unterschiedlichen 
Zeiten. Sind im Falle von 32Bit + 8Dimensioal 8Bit  3 Tonänderungen  8 
Kanäle.

96Bit pro Tonwechsel.

Oder ich hab 32bit + 8Bit  3Tonänderungen  8 Kanäle, komme dann aber 
aufs Problem, den 32bitWert mehrmals speichern zu müssen, wenn ich auf 
mehreren Kanälen eine Tonänderung zur gleichen Zeit habe.

Also xBit pro Tonwechsel je nach Anzahl gleichzeitiger Tonänderung....

Gaestchen schrieb:
> Du brauchst keine 32bit. Du must ja nur speichern wieviel zeit vergehen
> muß  bis zur nächsten änderung. Sprich statt bei Minute 32 7 Sekunden,
> speicherst du die Information in 50ms.
>  Da meistens auch nur bestimmte Längen erforderlich sind, kannst du
> diese auch in eine Tabelle ablegen und kommt dann evt sogar mit 8 Bit
> (oder noch weniger) für die Zeit hin. Das erspart Dir 3 Byte.

So habich es mit dem einzelnen Floppy gelöst =). Aber das auf 8 
aufzuteilen, ist ja doch komplizierter als ich dachte.

So wie ich es zur Zeit versuche, braucht die ISR ncoh 54µs, nicht 
unendlich weit von 50.... aber auch 50 wär noch zu lang, wenn sie alle 
50 aufgerufen werden soll. Sonst bleibt ja zwischen den ISR keine Zeit 
mehr für anderes

von Falk B. (falk)


Lesenswert?

@ Kai S. (zigzeg)

>1. Mach aus Melodie ein Array:

Der OP ist lernresistent. Nomen est Omen!

Beitrag "Re: Musikalisches Diskettenlaufwerk"

von J. T. (chaoskind)


Lesenswert?

Falk Brunner schrieb:
>>1. Mach aus Melodie ein Array:
>
> Der OP ist lernresistent. Nomen est Omen!

Son Quatsch, das ist schon lange umgesetzt. Der OP ist nur resistent 
dagegen, Sachen zu benutzen, die er nach Ansicht anderer schon gelernt 
haben müsste, sie aber noch nicht begriffen hat. Denn
 Sachen die man nicht begriffen zu benutzen... Nun sowas machen eher 
weniger helle Leute.

: Bearbeitet durch User
von Gaestchen (Gast)


Lesenswert?

j. t. schrieb:
> Oder ich hab 32bit + 8Bit  3Tonänderungen  8 Kanäle,
 Wenn du mein Posting verstanden hätteste wären es nur 8+8 Bit. Ich gebe 
zu ich war mir nicht ganz sicher ob es verständlich genug formuliert 
war.
 Darum nochmal der Hinweis darauf.

Generell ist das Thema Datenkompression. Dafür ist es gut zu wissen 
welche
Daten man hat die man Komprimieren will.
Wieviel Töne hast du insgesammt die du darstellen kannst(Incl Halbtöne)? 
Reichen Dir 32 Töne, dann könntest du in den anderen 3 Bit den Kanal 
ablegen.
Wieviel verschiedene Tonlängen hast du? reichen da 16? dann würdest du 
auch nur 4 Bit benötigen.

Generell solltest du dir überlegen was günstiger ist, zu speichern wann 
die nächste Änderung ist, oder die einzelnen Töne mit deren Länge.

> aufs Problem, den 32bitWert mehrmals speichern zu müssen, wenn ich auf
> mehreren Kanälen eine Tonänderung zur gleichen Zeit habe.
 Du kannst auch eine Maske davorlegen. <8Bit Zeitcode>, <8Bit Maske>, { 
für jedes Bit in der Maske <ein Byte Ton> } Je nachdem wo das Bit 
gesetzt wird der Ton dem Kanal zugeordnet.

>
> So wie ich es zur Zeit versuche, braucht die ISR ncoh 54µs, nicht
> unendlich weit von 50.... aber auch 50 wär noch zu lang, wenn sie alle
> 50 aufgerufen werden soll. Sonst bleibt ja zwischen den ISR keine Zeit
> mehr für anderes
 In der ISR nur die Töne abspielen, die auswertung des stream, wann 
welche Töne geändert werden, im Hauptprogramm. Zeig mal deine aktuelle 
ISR.

 Überlege Dir welche Quarzfrequenz für dich gut ist. Eine auf den ersten 
Blick sehr krumme Frequenz kann deutlich besseres Timing liefern. z.B. 
könnte ein 7,3728MHz Quarz günstiger sein als ein 8MHz Quarz, da er noch 
eine zwei mal den Faktor 3 beinhaltet. Die Töne dann besser getroffen 
werden würden. Aber ob das so ist müßte an mal nachrechnen.

von Falk B. (falk)


Lesenswert?

@ j. t. (chaoskind)

>> Der OP ist lernresistent. Nomen est Omen!

>Son Quatsch, das ist schon lange umgesetzt. Der OP ist nur resistent
>dagegen, Sachen zu benutzen, die er nach Ansicht anderer schon gelernt
>haben müsste, sie aber noch nicht begriffen hat.

Das Ergebnis ist das Gleiche.
1
// es gibt auch Arrays aus structs
2
3
typedef struct
4
{
5
  uint16_t Ton[50];
6
  uint16_t Dauer[50];
7
  volatile uint16_t Gesamtdauer;
8
} melodie_t;
9
10
melodie_t melodie[8];
11
12
    for (i=0; i<8; i++) {
13
      if (SysTick_ms == melodie[i].Gesamtdauer)
14
      {
15
        NeuerTonFlag |= (1 << i);
16
      }
17
    }


>Denn
> Sachen die man nicht begriffen zu benutzen... Nun sowas machen eher
>weniger helle Leute.

Grammatik? Oh, welch Ironie! ;-)

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


Lesenswert?

Momentan siehts so aus:
1
#define KopfPort PORTB
2
3
4
#include <avr/io.h>
5
#include <avr/interrupt.h>
6
7
8
struct Tonaenderung
9
{
10
  uint32_t TimeCodePosition;
11
  uint16_t Tonhoehen[8];
12
  uint8_t AnAusFlag;
13
  
14
}Tonaenderung[80];
15
uint32_t TimeCode = 0;
16
void init(void);
17
18
19
ISR(TIMER1_COMPA_vect)
20
{
21
  uint8_t i = 0;
22
  uint8_t n = 0;
23
  
24
  uint8_t TonhoehenZaehler[8];
25
  
26
  TimeCode++;
27
  
28
  for (i = 0; i < 8; i++)
29
  {
30
    TonhoehenZaehler[i]--;
31
    if (TonhoehenZaehler[i] == 0)
32
    {
33
      TonhoehenZaehler[i] = Tonaenderung[n].Tonhoehen[i];
34
      KopfPort ^= (1 << i);
35
    }
36
  }
37
}
38
39
int main(void)
40
{
41
  uint8_t i = 0;
42
  uint8_t n = 0;
43
  
44
  uint8_t TonhoehenZaehler[8];
45
  
46
  
47
  Tonaenderung[0].TimeCodePosition = 0;
48
  
49
  Tonaenderung[0].Tonhoehen[0] = 20;
50
  Tonaenderung[0].Tonhoehen[1] = 21;
51
  Tonaenderung[0].Tonhoehen[2] = 22;
52
  Tonaenderung[0].Tonhoehen[3] = 23;
53
  Tonaenderung[0].Tonhoehen[4] = 24;
54
  Tonaenderung[0].Tonhoehen[5] = 25;
55
  Tonaenderung[0].Tonhoehen[6] = 26;
56
  Tonaenderung[0].Tonhoehen[7] = 27;
57
  
58
  Tonaenderung[0].AnAusFlag = 0b11111111;
59
  
60
  
61
  Tonaenderung[1].TimeCodePosition = 100;
62
  
63
  Tonaenderung[1].Tonhoehen[0] = 10;
64
  Tonaenderung[1].Tonhoehen[1] = 11;
65
  Tonaenderung[1].Tonhoehen[2] = 12;
66
  Tonaenderung[1].Tonhoehen[3] = 13;
67
  Tonaenderung[1].Tonhoehen[4] = 14;
68
  Tonaenderung[1].Tonhoehen[5] = 15;
69
  Tonaenderung[1].Tonhoehen[6] = 16;
70
  Tonaenderung[1].Tonhoehen[7] = 17;
71
  
72
  Tonaenderung[1].AnAusFlag = 0b11111111;
73
  
74
  for (i = 0; i < 8; i++)
75
  {
76
    TonhoehenZaehler[i] = Tonaenderung[n].Tonhoehen[i];
77
  }
78
  
79
  
80
  while(1)
81
  {
82
    if (TimeCode == Tonaenderung[n].TimeCodePosition)
83
    {
84
      n++;
85
    }
86
  }
87
  
88
}
89
void init (void)
90
{
91
  OCR0 = 50;
92
  TCCR0 = (1 << WGM01) | (1 << CS01);
93
  
94
  sei();
95
}

@Gästchen:
Du meinst also, das ich das zuweisen des neuen Tones auch ausserhalb der 
ISR machen sollte? Also ein Flag setzen, statt den neuen Ton zu laden?

: Bearbeitet durch User
von Gaestchen (Gast)


Lesenswert?

1
uint8_t TonhoehenZaehler[8];  // nicht innerhalb sonst gehen die Daten verloren
2
uint16_t Tonhoehenhoehe[8] = {0,0,0,0,0,0,0,0};   // Hier schreibst du von der Hauptprogramm die Werte rein.
3
#define TIMECODETICKS 20      // Wert so das er bei der kürzesten Note zählt 
4
                              // also z.B. 1/32 Note
5
6
uint32_t TimeCode = 0;  // Wird im IRQ gezählt
7
uint32_t TimeNextEvent=0; // Nächstes Ereigniss
8
9
// Die Musikdaten lägen bei mir im Char Feld
10
// Dieses Feld würde ich später ins Flash legen, für kurze sachen geht auch RAM.
11
unsigned char *pMusikstream;   
12
13
ISR(TIMER1_COMPA_vect)
14
{
15
static unt8_t cnt=0;
16
//  uint8_t i = 0;
17
18
  
19
  if ( ! --cnt )
20
  {
21
    TimeCode++; // Dein Timecode soll nur die Notenlänge auflösen  kann also langsammer zählen
22
    cnt=TIMECODETICKS;
23
  }
24
25
26
// diesen Code schreibst du jetzt 8 Mal und in jedem Block änderst du idx in  0..7 ab
27
// Also ersten Block alles 0, im Zweiten alles 1 usw. im letzen alles 7
28
// du kannst zum Code testen  auch erst mal die Schleife lassen
29
// die Schleife kostet aber zusätzlich Zeit , da dieser Bereich zeitkritisch ist ist
30
// das hier ok. Lesbarer wäre es mit de Schleife
31
32
    TonhoehenZaehler[idx]--; //evt geht es schneller wenn du --TonhoehenZaehler[i] schreibst
33
                             //, ist Compilerabhängig ausprobieren und im Assemblerlisting schaun
34
    if ( ! TonhoehenZaehler[idx] ) && ( Tonhoehe[idx] ) )
35
    {  // falls Tonhöhe =0 wird nichts gemacht kein ton, zähler läuft so weiter
36
        TonhoehenZaehler[idx] = Tonhoehe[idx];
37
        KopfPort ^= (1 << idx);
38
    }
39
  
40
}
41
42
...
43
44
45
// der code zum abspielen
46
// ich würde den in eine Funktion packen und den pointer auf die Musik übergeben
47
48
{
49
uint8_t midx=0; // index 
50
  ResetMusik();  // Anfangszustand definieren
51
 while ( midx <8 ) 
52
 {
53
    while ( TimeNextEvent > TimeCode ) ;  // Warten bis was zu tun ist.
54
    midx=*pMusikstream++;
55
    if (midx<8)  // nix tun und abbrechen wenn der index zu groß ist
56
    { 
57
      Tonhoehenhoehe[midx]= TabelleTonhoehe[*pMusikstream++]; // hier
58
      TimeNextEvent +=  TabelleLaenge[*pMusikstream++];   // und hier
59
             // solltest du den Index auch überprüfen oder die Tabellen 256 Einträge lang machen
60
    }
61
  }
62
   // hier noch aufräumen z.B. Tonhoehenhoehe[8] löschen
63
}
64
65
Dieses ist jetzt unter der Annahme das der stream immer den Aufbau hat
66
 nextevent, kanal, tonhöhe , nextevent, kanal, tonhöhe , .... jeweils ein Byte
67
nextevent - Zeitindex der vergeht bis der Eintrag abgearbeitet werden soll
68
kanal - Der in dem der Ton erzeugt werden soll
69
tonhöhe - Index auf eine Tabelle in der die Zeit steht die für IRQ benötigt wird
70
 
71
Die Tabellen mußt du erstellen in einem die Tonhoehen zum entsprechenden index, in der anderen die Tonlänge.
72
73
74
Zum testen legst du das so an
75
uint8_t Musik1[] = {  0,0, 7  ,0,1,8  // fängt mit 2 töenen an
76
                    , 4,2, 12 ,0,1,0   // ein dritter dazu ,gleichzeitig cannel 1 stumm
77
                    , 12,99,0 }         // Ende da Channel 99 zu groß ist

von Bastler (Gast)


Lesenswert?

>> uint8_t TonhoehenZaehler[8];  // nicht innerhalb sonst gehen die Daten verloren
Aber nur wenn main() verlassen wird. Dann ist aber alles verloren!

von J. T. (chaoskind)


Lesenswert?

@Gästchen:
Danke dir, das ist ja ne Menge auf einmal, da muss ich mich jetzt 
erstmal durcharbeiten.

Gleich eine kleine Frage dazu:
Was bedeutet "if (! --1)?

Falls nicht einer abgezogen wird??? Es macht gerade sehr laut 
"hääääääääää??!!??!!" in meinem Kopf :D

von Gaestchen (Gast)


Lesenswert?

j. t. schrieb:
> Gleich eine kleine Frage dazu:
> Was bedeutet "if (! --1)?

du meinst   if ( ! --cnt ) ... ?

die variable cnt wird eins runtergezält, dann wird sie mit ! (not) auf 
== 0 getestet.
 Sprich cnt wird eins runtergezählt und wenn sie Null ist, wird die If 
Anweisung ausgeführt.

von J. T. (chaoskind)


Lesenswert?

Achso, das war also nur ein Schreibfehler und sollte "if (! ==1)" 
heißen?

oder ist das wieder so eine trickreiche Kurzschreibweise a la "i++" für 
"i=i+1", und heißt tatsächlich "if (! --1). Dann verstehe ich aber 
nicht, worauf da getestet wird? Fehlt da dann nicht noch eine Variable?

: Bearbeitet durch User
von Gaestchen (Gast)


Lesenswert?

j. t. schrieb:
> Achso, das war also nur ein Schreibfehler und sollte "if (! ==1)"
> heißen?

(! ==1) macht keinen Sinn, auch (! --1) macht keinen, auch (! ==cnt) 
würde keinen machen. Ich habe nichts davon geschrieben, worauf beziehst 
du Dich?

von J. T. (chaoskind)


Lesenswert?

ohmein gott, ich schlaf zuwenig... da steht cnt und nicht 1.....

jetzt versteh ichs! so halb zumindest.

in welcher Reihenfolge wird da jetzt geprüft? Erst ob das ! zutrifft, 
und wenn ja wird cnt ein kleiner gemacht? oder wirds erst ein kleiner 
gemacht?

Hast du ja schon gesagt, erst wird dekrementiert, dann geprüft. Gut, ich 
glaub, ich habs doch

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

j. t. schrieb:
> in welcher Reihenfolge wird da jetzt geprüft? Erst ob das ! zutrifft,
> und wenn ja wird cnt ein kleiner gemacht? oder wirds erst ein kleiner
> gemacht?

zuerst komt --cnz, das ist equivalent zu (cnt-=1) oder (cnt=cnt-1), also 
wird zuerst cnt um eins verkleinert. Danach wird das logisch nicht 
darauf angewendet, equivalent zu x==0

bei x-- wird zuerst x zurückgegeben und irgendwann innerhalb der 
anweisung dekrementiert, deshalb ist x=x-- undefiniert

von Daniel A. (daniel-a)


Lesenswert?

tipp:
constante arrays aus structs sofort initialisieren:
1
struct x {
2
  int x;
3
  int y[3];
4
};
5
const struct x y[] = {
6
  {
7
    0,
8
    {1,2,3}
9
  },{
10
    4,
11
    {5,6,7}
12
  }
13
}

das erspart einem jedes arrayelement einzeln zu initialisieren

von Gaestchen (Gast)


Lesenswert?

Bastler schrieb:
>>> uint8_t TonhoehenZaehler[8];  // nicht innerhalb sonst gehen die Daten 
verloren
> Aber nur wenn main() verlassen wird. Dann ist aber alles verloren!

Seit wann bleiben Variablen die innerhalb Funktionen angelegt werden 
solange erhalten bis die main() verlassen wird? Dir ist schon klar das 
der Kommentar sich auf seinen Code bezieht? Wo er das Feld innerhalb 
einer Funktion angelegt hat aber immer wieder darauf zugegriffen hat.

von Bastler (Gast)


Lesenswert?

Wenn die Funktion, um die es geht, () heißt z.B.
Aber ich sehe gerade, in der ISR deklariert er die ja noch mal lokal. 
Wie war noch mal der Titel: "Wege aus dem Chaos", oder so ;-)

von J. T. (chaoskind)


Lesenswert?

Wäre auch ein schöner Titel gewesen :D

von J. T. (chaoskind)


Angehängte Dateien:

Lesenswert?

Heureka!

Es funktioniert soweit, ich kann unabhängig unterschiedliche Töne 
ausgeben. Nun muss ich nur noch austüfteln, welche Werte zu welchen 
Tönen gehören.

Vielen Dank für die Denkanstöße an euch, ich werd den Fortschritt hier 
weiter dokumentieren, zur Zeit siehts so aus: siehe Anhang.

von Easylife (Gast)


Lesenswert?

Hat doch schon viel Schönes.
Trotzdem würde ich dir empfehlen, das 8-dimensionale Array wieder 
aufzugeben, um Speicher zu sparen.

Die Counterwerte zu den Tonhöhen legst du am besten in einem lookup 
table ab.
Dann kannst du in deiner Melodiestruct für die Tonhöhe einen Notenwert 
ablegen. Da würde ich mich an MIDI orientieren, später kannst du dir 
dann einen Converter schreiben, der dir direkt MIDI Files in dein Struct 
umwandelt.
1
struct Melodien
2
{
3
  uint8_t offset;
4
  uint8_t kanal;
5
  uint8_t note;
6
};
7
8
const struct Melodien Melodie1[] =
9
{
10
  { 16, 0, 200 }, // 16
11
  {  0, 1, 200 },
12
  {  0, 2, 200 },
13
  {  0, 3, 200 },
14
  {  0, 4, 200 },
15
  {  0, 5, 200 },
16
  {  0, 6, 200 },
17
  {  0, 7, 200 },
18
19
  { 16, 0, 0 },    // 32
20
  {  0, 1, 0 },
21
  {  0, 2, 0 },
22
  {  0, 3, 0 },
23
  {  0, 4, 0 },
24
  {  0, 5, 0 },
25
  {  0, 6, 0 },
26
  {  0, 7, 0 },
27
  
28
  { 12, 0, 12 },  // 44
29
  
30
  {  4, 1, 16 },  // 48
31
 
32
  {  8, 0, 18 },  // 56
33
  {  0, 1, 14 },
34
 
35
  {  8, 1, 16 },  // 64
36
 
37
  {  8, 0, 12 },  // 72
38
  {  0, 1, 14 },
39
  
40
  {  8, 1, 16 },  // 80
41
  
42
  {  8, 0, 14 },  // 88
43
  
44
  {  8, 0, 12 },  // 96
45
  {  0, 1, 13 },
46
  
47
  {  4, 0, 16 },  // 100
48
  {  0, 1, 14 },
49
50
  {  2, 1, 12 },  // 102
51
  
52
  {  2, 1, 18 },  // 104
53
  
54
  {  2, 1, 12 },  // 106
55
56
  {  1, 1, 18 },  // 107
57
  
58
  {  1, 1, 12 },  // 108
59
60
  {  2, 1, 18 },  // 110
61
  
62
  {  8, 0, 0 },    // 118
63
  {  0, 1, 0 },
64
  
65
  {255, 0, 0 }    // offset=255: Ende vom Lied
66
};
67
68
69
const uint16_t[] freq_lookup =
70
{
71
  0,
72
  1,
73
  2,
74
  3,
75
  4,
76
  (...)
77
};
78
79
80
(...)
81
82
int main(void)
83
{
84
  
85
(...)
86
87
  uint8_t  offset;
88
  uint16_t n = 0;
89
  uint16_t position = 0;
90
91
    do()
92
    {
93
      offset = Melodie1[n].offset;
94
      
95
      position += offset;
96
      
97
      while (SysTick_64tel < position)  // warten bis nächste Position erreicht
98
        ;
99
        
100
      Kanal.Tonhoehe[Melodie1[n].kanal] = freq_lookup[Melodie1[n].note];
101
      n++;
102
    } while (offset < 255);
103
}

von Easylife (Gast)


Lesenswert?

"do()"
ist quatsch, muss natürlich nur "do" heissen.

von J. T. (chaoskind)


Lesenswert?

Haha du bist genial!

Ich mache mir grad Gedanken darüber, was ich anders machen könnte, um 
auf ein sinnvolleres Timing zu kommen. Nach genau sowas hab ich 
gesucht!! =).

Dann hab ich eine weitere Timingfrage, aber dazu mach ich n neues Thema 
auf.

Irgendwas mit den BPM haut nicht hin, und ich komm nicht drauf was...

von J. T. (chaoskind)


Lesenswert?

Und wie ich die Frage schreibe, und mir das Problem nochmal vor Augen 
führe, kommt mir die Lösung!

Das Problem war, das ich meine BPM noch mal 4 nehmen musste, und ich mir 
die 4 nicht erklären konnte. Bis ich drauf kam, das ein einzelner Beat 
ja 4 64tel Noten lang ist, und nicht eine!

von Easylife (Gast)


Lesenswert?

Nicht ganz.
BPM gibt die Anzahl der Beats = Takte pro Minute an.
1/64 Note ist 1/64 Takt lang, also ist ein Beat 64 64tel Noten lang.

von Easylife (Gast)


Lesenswert?

Korrektur: BPM sind nicht Takte pro Minute, sondern das 3 oder 4 fache 
davon (je nachdem, ob das Musikstück im 3/4 oder 4/4 Takt ist).
BPM bezieht sich auf also 1/4 Noten.
Daher passen 16 1/64-tel Noten zwischen zwei Beats.

von J. T. (chaoskind)


Lesenswert?

Ja genau das war ja mein Denkfehler. Das BPM eben nicht Takte pro 
Minute, sondern quasi 1/4Noten pro Minute sind.

von J. T. (chaoskind)


Lesenswert?

So ich hatte nun mal Zeit, mir deinen Vorschlag in Ruhe anzugucken, 
dabei sind mir einzwei Fragen aufgekommen.

Easylife schrieb:
1
> const struct Melodien Melodie1[] =
2
> {
3
>   { 16, 0, 200 }, 
4
5
.
6
.
7
.
8
9
10
> const uint16_t[] freq_lookup =
11
> {
12
>   0,
13
>   1,
14
>   2,
15
>   3,
16
>   4,
17
>   (...)
18
> };
19
20
.
21
.
22
.
23
24
> Kanal.Tonhoehe[Melodie1[n].kanal] = freq_lookup[Melodie1[n].note];
25
>       n++;
26
>     } while (offset < 255);

Als erstes:

const uint16_t[] freq_lookup == const uint16_t freq_lookup[] ????

falls nicht, wo liegt der Unterschied?


und dann:

nehmen wir an n ist gerade 0. Dann würde er hier doch

Kanal.Tonhoehe[Melodie1[n].kanal] = freq_lookup[Melodie1[n].note];

"nachgucken" freq_lookup[Element an der Stelle, was an der Adresse von 
Melodie1[n].note steht] an Melodie1[0].note ist 200. Also steht da bei n 
= 0:
freq_lookup[200] ???

wäre da nicht sinnvoller:

#define NOTE1 0
#define NOTE2 1
.
.

const struct Melodien Melodie1[] =
{
  { 16, 0, NOTE1},
.
const uint16_t[] freq_lookup =
{
200,
210,
.
.
.
}
????


Kanal.Tonhoehe[Melodie1[n].kanal] = freq_lookup[Melodie1[n].note];

und ganz klar ist mir auch nicht, was denn nun genau auf den Wert 
gesetzt.



Bei mir ist es ja bis jetzt:

Kanal.Tonhoehe[i] und i wird inkrementiert... 0-7

und in Melodie1[n].kanal steht ja auch immer maximal 0-7.. ah ok den 
Teil hab ich glaub ich begriffen.

: Bearbeitet durch User
von Easylife (Gast)


Lesenswert?

Gedacht ist es so, dass du in deinem Melodie-Array Notenwerte ablegst, 
keine krummen Counterwerte.
Für die Notenwerte würde ich mich am MIDI-Standard orientieren:
http://www.berkhan.com/basic/manual_7/manual_d/data_d/chap7-3.htm

Um jetzt die Counter deines Klangerzeugers entsprechend zur Note zu 
setzen, gibt es das freq_lookup table.

Note 0 sollte eine Ausnahme bleiben, und auch in der freq_lookup table 
eine 0 haben (damit der Klangerzeuger weiss, dass kein Ton gespielt 
werden soll).

Aber schon bei Note 1 kannst du anfangen, einen sinnvollen Counterwert 
in die Tabelle einzufügen.
Meine Tabelle (1, 2, 3, 4, 5) ist nur exemplarisch.

Nehmen wir an, dein Klangerzeugungsinterrupt wird 10000x pro Sekunde 
aufgerufen.
MIDI Note 1 ist C#1 = 34.8 Hz 
(http://www.thundermix.de/files/frequenztabelle.gif)
Counterwert für C#1 = 10000  2  34.8 = 144 (gerundet).

MIDI Note 2 ist D1 = 37.1 Hz -> Counterwert 10000  2  37.1 = 135

und so machst du gerade weiter, bis du bei MIDI Note 86 bist (D8)
D8 = 4752 Hz -> Counterwert = 10000  2  4752 = 1

Deine lookuptable sähe bei 10000Hz Interrupt-Frequenz also in etwa so 
aus:

const uint16_t[86] freq_lookup =
{
  0,
  144,
  135,
  (...)
  1
}

Mit "const" deklariert man in der Regel Arrays, die nur gelesen werden, 
also nie ein Wert verändert werden soll.
Der Compiler meckert dann, wenn man versuchen sollte dies zu tun. Nur 
diesen Sinn hat es.

Die Zeile
Kanal.Tonhoehe[Melodie1[n].kanal] = freq_lookup[Melodie1[n].note];

ist in der Tat nicht ganz einfach zu verstehen.

Auf der linken Seite passiert folgendes:
Aus deinem Melodie-Array wird der Wert des Kanals rausgesucht, der sich 
ändern soll.
Nimm z.B. die erste Zeile deiner Melodie
  { 16, 0, 200 },

Sobald also Position 16 erreicht ist, ist Melodie[0].kanal = 0
Jetzt wird also Kanal.Tonhoehe[0] ein neuer Wert zugewiesen.
Den Wert holt sich das Programm aus freq_lookup.
Da Melodie1[0].note 200 ist, würde es jetzt freq_lookup[200] nehmen.
Die Note 200 macht tatsächlich in deinem Fall aber keinen Sinn, da du ja 
bei Note 86 schon bei 5000 Hz ankommst.
Aber als Beispiel taugt es ja.

Im nächsten Schritt deine Melodie steht dann
  {  0, 1, 200 },

Da der zeitliche Offset hier 0 ist, wird dieser Schritt direkt nach dem 
ersten ausgeführt, also quasi passiert sie gleichzeitig.

Jetzt ist der Kanal 1, d.h. diese Note wird dem Kanal 1 zugewiesen.
Und so weiter.

Etwas schwierig per Text zu erklären. Guck dir den Quellcode nochmal 
genau an, was der macht.
In der do-while Schleife wird nur dann gewartet, wenn sich "position" 
ändert. Solange die offsets in deinem Musikstück 0 sind, läuft die 
do-while Schleife sehr schnell durch, und ändert die Tonhöhe mehrerer 
Kanäle quasi gleichzeitig.

von Easylife (Gast)


Lesenswert?

Boah ist das nervig. Mathematische Formeln werden gleich mal in 
Text-Formatierungen umgewandelt.

Hier nochmal die Berechnung der freq_lookup table:
1
MIDI Note 1 ist C#1 = 34.8 Hz 
2
(http://www.thundermix.de/files/frequenztabelle.gif)
3
Counterwert für C#1 = 10000 / 2 / 34.8 = 144 (gerundet).
4
5
MIDI Note 2 ist D1 = 37.1 Hz -> Counterwert 10000 / 2 / 37.1 = 135
6
7
und so machst du gerade weiter, bis du bei MIDI Note 86 bist (D8)
8
D8 = 4752 Hz -> Counterwert = 10000 / 2 / 4752 = 1

von Easylife (Gast)


Lesenswert?

Und wenn du das Ganze dann mal am Laufen hast, dann hätte ich noch eine 
Erweiterungsidee für dich:
Das Protokoll, dass MIDI Keyboards sprechen ist sehr einfach zu parsen.
Du brauchst im Wesentlichen einen RS232 Eingang mit passender Baudrate.
Da fallen dann direkt Note-ON und Note-OFF Messages raus, mit besagter 
MIDI-Noten-Nummer.
Damit könntest du deinen Klangerzeuger direkt live mit einem 
MIDI-Keyboard spielen ;-)

von J. T. (chaoskind)


Lesenswert?

Wunderbar, dank dir nochmal für deine Erläuterungen! =)

Und die live-Spielbarkeit ist genau das "Langzeitziel"

Kleine Ergänzung:

Counterwert für C#1 = 10000  2  34.8 = 144 (gerundet).

Die durch 2 ist hier glaub ich unnötig, da ich hier ja kein Rechteck 
ausgebe, bei dem ich für eine "Schwingung" einmal hin und zurückschalten 
muss, sondern jeder einzelne Step entspricht schon einer Schwingung.

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


Lesenswert?

Irgendwas funktioniert nicht so richtig an deinem Vorschlag....

Irgendwie spielt er mir nur noch die ersten beiden Töne ab....

Wenn ich das debugge, dann fängt er mit erreichen von 16 bei 
Systick_64tel an, die nächsten Werte zu laden, aber braucht er da pro 
Wert 1/64 Note. Sprich obwohl ich beim offset null bin, ist der 7te Ton 
von zweiten Block nicht innerhalb einer 64tel geladen, sondern erst bei 
23/64tel.

Ich vermute aber, das liegt nur daran, das die Timer weiterlaufen, auch 
wenn ich gerade kein Schritt mache... Also hier nochmal die Frage, wie 
kann ich die Timer beim debuggen "ausstellen", das der Prescaler also 
immer nur ein CPU-Takt sieht, wenn ich einen Step mache. bzw so viele 
CPU-Takte, wie der Befehl braucht.

P.S.
Du hast recht, solche Probleme/Lösungen schreibend zu beschreiben, ist 
garnicht so einfach. Während man den einen Teil schreibt, fällt einem 
zum anderen noch was ein, bis man den einen Teil fertig hat, ist das vom 
anderen aber schon wieder vergessen.

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


Angehängte Dateien:

Lesenswert?

P.P.S.

Hier noch die aktuelle Version im Anhang

von J. T. (chaoskind)


Lesenswert?

Hat sich geklärt, ich hatte nur keine Notenwerte geladen... Die 
Betriebsblindheit.

Wobei die Frage nach dem "run timer in stopped mode" immer noch 
ungeklärt ist.

von J. T. (chaoskind)


Angehängte Dateien:

Lesenswert?

Ich hab angefangen einen Kommentar zu schreiben, ein wenig meine 
Gedanken zu sortieren und aufzuschreiben, dabei ist mir etwas 
aufgefallen. Der Kommentar ist dann mehr oder weniger ein Fragebeitrag 
geworden, also kopier ich ihn mal hierein.


Programm das 8 Töne erzeugen und über die Leseköpfe von 8 3,5" 
Floppylaufwerken ausgeben kann. Die Ansteuerung der Laufwerke
erfolgt über die Pins 12(floppy on/off), 18(direction) und 24(step). Pin 
12 wird auf Masse gelegt um die Floppys zu aktivieren.
(evtl später noch per Schieberegister anzusteuern, müssen aktiviert 
sein, damit der Kopf Schritte machen kann, schalten
 also langsam --> Schiebereg sollte also reichen.)
Die direction-Pins werden an PORTD angeschloßen, die step-Pins an PORTB. 
Direction-Pin auf LOW bedeutet Vorwärts und umgekehrt.
Ein Schritt vom Lesekopf wird mit einer LOW-->HIGH Flanke ausgelöst.

Um einen Ton zu erzeugen muss der Lesekopf x mal einen Schritt machen, 
um auf x Hz zu kommen. Zeitbasis für die Tonerzeugung ist 100µs, d.h. 
der höchste "sinnvolle" Ton dürfte bei rund 1kHz liegen, da der Lesekopf 
bei 1kHz alle 10 Zeitbasen einen Schritt macht. 1Schritt mehr oder 
weniger entspricht schon einer Tonhöhenänderung von 10% was bereits 
knapp 2 Halbtönen entspricht (ein Halbton entspricht etwa 5,9% ( x * 
2^(1/12)). Konzept zur Tonerzeugung ändern? Zeitbasis ändern? Wie lang 
ist die ISR?

Mein USB-oszi lässt sich lustigerweise nicht mehr installieren, ich hab 
aber noch ein Multimeter das Frequenz und Pulsweite messen kann. Sollte 
ich damit nicht auch die Länge der ISR bestimmen können? Pin am Anfang 
high setzen, am Ende auf low, dann müsste ich doch an der Frequenz sehen 
können ob meine 100µs stimmen (entspricht 10kHz), und wenn die Pulsweite 
sagen wir mal 5% beträgt, hätte ich doch noch genügend Luft um die 
Zeitbasis auf 10µs zu verkürzen. Die Länge der ISR ändert sich nicht, 
das hieße dann eine Frequenz von 100kHz bei 50% Pulsweite und somit 50% 
Rechenzeit. Die anderen 50 sollten doch für die Hauptschleife reichen? 
Ah bei der kann ich die Länge ja Sogar im Simulator ermitteln...

von J. T. (chaoskind)


Lesenswert?

10.01kHz und 13% Pulsweite. Mh fünteln fiele mir da als erstes ein.

das wären dann 50kHz bei 65% Pulsweite. Ist eine ISR-Zeit von 65% noch 
vertretbar? Da müsste ich wohl schauen, wie lange der "Idle-State" ist.
Schön von wenn funktioniert was man sich ausdenkt =)

P.S.

Der ist fast durchgehend an.

    PORTA |= 0b00000010;
while (SysTick_64tel < position);
    PORTA &= 0b11111110;

Nur zu den Tonwechseln zuckt die Frequenzanzeige mal kurz, die Spannung 
ist wird durchgehend als 4.96V angezeigt. Das heißt doch, das ich von 
den 87% der Zeit, die ich nicht in der ISR bin, bin ich fast durchgehend 
am nichts tun, also sollte die Zeitbasis doch verzehnfachbar sein?

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


Lesenswert?

bei ner verzehnfachung hat er statt den erwarteten 100kHz nur 69 
ausgeben, bei 70% pulsweite, ich bin nun doch bei mal 5, da komm ich auf 
50.07kHz und 50% Pulsweite.

Langsam kann ich mich daran machen, die OCR-Werte für die Töne zu 
berechnen.

Kannst du oder wer anders, mir evtl noch ein paar Takte zu ISR-Dauern 
erzählen? Ich weiß, sie sollen kurz sein, keine unnötigen Berechnungen 
usw.

Aber ich mein allgemeinere Sachen, so in Richtung Rechenzeitverwaltung.

: 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.