Sinuswerte in Array speichern

OP #3092826
Lesenswert?

Moin.
Ich versuche einen Sinus per PWM zu erzeugen. Bin noch ganz am Anfang. 
Ich habe mir gestern mal ausgerechnet, welche Sinusfrequenz ich 
höchstens (ohne Wert überspringen oder so) erzeugen kann.
Ich würde einen 8-Bit Timer nehmen und eine Taktfrequenz von 16MHz. 
Daraus resultiert bei einer EEPROM größe von 512 (ATMega8) ca. 60Hz.
Kann das hinkommen?
Naja, mein eigentliches Problem ist, das ich erstmal, bevor ich das 
EEPROM beschreibe, ein 512-Werte breites Array hernehmen wollte um zu 
sehen ob die Werte richtig berechnet werden.
Der Datentyp ist uint8_t, da ich nur die Positive Halbwelle speichern 
will und die EEPROM-Werte ja 8 bit breit sein sollen.
Die Berechnung gestaltet sich wie folgt:
1
#include <math.h>
2

3
uint8_t sin_arr[512];
4

5
void init_sin(void)
6
{
7
  uint8_t i;
8
  
9
for (i=0; i<512; i++)
10
{
11
  sin_arr[i] = (uint8_t)(255*sin(2*3.141*((double)i/1024.0)));
12
}
13
  
14
}
Ich finde im Moment absolut nicht herraus, warum mein Debugger das nicht 
mitmachen will.
Der zeigt an "Running" und macht dann nix mehr...
Denke das muss ein ganz dummer Fehler sein.

Hoffe Ihr könnt mir helfen

Grüße
Jan
(Firma: guloshop.de) #3092842
Lesenswert?

Hallo Jan, ich geb zu, ich hab mir deinen Code gar nicht angeschaut, 
aber ist es nicht sinnvoller, die Sinuswerte nicht im Mikrocontroller 
zu berechnen, sondern sie schon während der Erstellung des Programms zu 
ermitteln und sie dann in eine Tabelle in den Flashspeicher zu 
schreiben?
OP #3093104
Lesenswert?

Ich hab mir das mit dem Wiki angesehen. Scheint mir echt ne gut 
Möglichkeit zu sein die Werte zu erzeugen. Naja, EEPROM hatte ich mir 
nur gedacht, um damit mal ein bisschen rum zuexperimentieren, aber ich 
glaub ich werde ne bessere Anwendung dafür finden ;-)
Ich greif dann wohl doch auf den Flash-Speicher zurück. Davon is beim 
Mega8 ja auch genug vorhanden.
Danke für die Hilfreichen Kommentare.

Gruß
Jan
Gast #3093535
Lesenswert?

Lieber Brunner-Ossi,

wer damit Probleme hat, zwei Oktette zu adressieren und auszulesen, für 
den ist die Programmierung von Microcontrollern eventuell nicht das 
passende Hobby. Soweit ich in diesem Forum gelesen habe bist Du doch der 
Schlaumeier, der immer sagt "Wenn Du schon die Basics nicht beherrschst, 
wie willst Du dann erst $_Irgendwas machen?"

xxx
#3093569
Lesenswert?

Schast (Gast) schrieb:
> J.-u. G. schrieb:
>> Für 512 reicht uint16_t.
>
> Für 512 Werte reicht ein Oktett + Extrabit!

Also insgesamt neun Bit? Ein Oktett sind acht Bit, plus eins sind neun.

Und du willst allen ernstes 512 Werte zu jeweils 16 Bit in 9 Bit 
unterbringen? Das will ich sehen!

Da fällt mir folgendes ein: Mittlerweile ist die Programmierung von 
Kompressions-Algorithmen schon so weit fortgeschritten, daß schon der 
Inhalt aller Bücher einer Bibliothek auf 1 Bit reduziert werden kann.
Am Dekompressions-Algorithmus wird allerdings noch eine Weile 
gearbeitet.
Gast #3093577
Lesenswert?

Wenn du einen 8Bit Timer hast, dann rechnest du dir einen Sinus mit 100 
Werten mit Excel aus. y1 = 255 * sin (3,6 / 360) das ganze in 3,6er 
Schritten hochgezählt. Diese Tabelle speicherst du dann ins RAM. Wenn du 
nun die Werte über eine PWM ausgibst, z.B. mit 5kHz kommt hinten ein 
Sinus mit 50Hz raus. Ggf kann man die Stützstellen reduzieren, damit man 
die Frequenz steigern kann. Ein Sinus mit 30 Stützstellen sieht 
immernoch gut.
Gast #3093726
Lesenswert?

Bernd S. schrieb:
> Übrigens, auch wenn du die Zählvariable "i" aus dem Progamm mit deiner
> Aussage meinst, hat trotzdem Falk recht.

"i" und "Programm" interessiert mich nicht sonderlich. Es ging doch 
darum, daß sich 512 Werte mit 9 Bits repräsentieren lassen.

> Die nächstgrößere Variable nach uint8_t ist nun mal uint16_t
> und nicht uint9_t!

Was interessiert irgendein C-Konstrukt uint_irgendwas? Der Punkt war, 
daß sich 512 Werte mit 9 Bit abbilden lassen. Definieren wir ein Byte 
als ein Oktett, und nehmen wir an, daß 8 Variablen mit 512 
Wertemöglichkeiten verarbeitet werden müssen. Dann gibt es 2 
Möglichkeiten:
1.) Ich nehme 2 Bytes je Variable, macht 16 Byte (Gesamt: 128 Bit)
2.) Ich nehme je 1 Byte + 1 Bit und definiere eine Bitmask (Gesamt: 72 
Bit)
=> Ersparnis bei Fall 2: Mehr als die Hälfte des Speichers.

Ist immer ein Trade-Off zwischen Speicher, Speed und Codesize.

> Oder hast du schonmal eine 9-Bit-Variable gesehen?

Scheiße, ich komm' grade nicht drauf, welche Architektur das war, die
eine Bytesize von 13 Bits hatte.
Gast #3093770
Lesenswert?

Mal abgesehen vom der Bitzahl deiner Variablen,
frag ich mich was die Beschränkung auf die positive
Halbwelle bringen soll. ???

Willst du die Daten bei jedem PWM-Zyklus aus dem EEPROM
lesen? - Das dauert E-W-I-G!

Wenn man sie zum µC-Start vom EEPROM ins RAM kopiert,
spart man immerhin Flash-Speicher.

Mach lieber eine Tabelle von -90° bis +90°.

Und nutze nicht den gesamten Zahlenraum aus:
Du kannst dem Zähler nicht sagen "warte auf NULL", wenn
er schon auf 3 gezählt hat!
OP #3093784
Lesenswert?

Oh man, da hab ich ja wieder eine Diskussion ausgelöst...
Naja, bei mir funktionierts jetzt jedenfalls.
Ich habe den Code aus dem Wiki ein bisschen umgestrickt so das ich die 
Werte mit sin(...) berechne. Jeden berechneten Wert multipliziere ich 
mit 255 und speichere ihn im Headerfile ab. Ich habe statt einer 
while... eine for... genommen.
Also der Code in der for sieht so aus:
1
for(i=0; i<512; i++) 
2
{
3
sine = sin(2*PI*((double)i/1024));
4
sine_uint8_t = (char)(255*sine);
5
sprintf(buffer, "     %d\n", sine_uint8_t);
6
fputs(buffer, fp); 
7
}

Das erzeugte array muss dann natürlich noch uint8_t als typ bekommen.
Ich denke so kann man auch mit dem 8-Bit-PWM dingens gemütlich arbeiten 
ohne im nachhinein noch irgendwas umrechnen zu müssen. Soweit jedenfalls 
meine Hoffnung. Mal sehen was draus wird ;-)

Ich Bastel mir jetzt mal den PWM-Scheiß zurecht und dann mal sehen was 
passiert.

Trotzdem danke für die vielen hilfreichen Tipps. Achja, für irgendwelche 
Anmerkungen bin ich natürlich immer offen.

Grüße Jan
#3093791
Lesenswert?

Schast (Gast) schrieb:

> "i" und "Programm" interessiert mich nicht sonderlich. Es ging doch
> darum, daß sich 512 Werte mit 9 Bits repräsentieren lassen.
Damit läßt sich ein Wert von 0..511 darstellen
Übrigens ging es genau um das Programm und eine Lösung für die 
Sinustabelle.

> Was interessiert irgendein C-Konstrukt uint_irgendwas?
Das interessiert schon, weil der Fragesteller mit C programmiert.

> Ist immer ein Trade-Off zwischen Speicher, Speed und Codesize.
Eben, und hier geht es um eine möglichst schnelle Sinus-Erzeugung per 
PWM. Da kommt es eben auf die Geschwindigkeit an, da kann man sich keine 
Bit-Schiebereien leisten. Ich weiß, wie du das meinst, aber sowas kann 
man machen, wenn man viel Zeit hat. Hier eben nicht.

> Scheiße, ich komm' grade nicht drauf, welche Architektur das war, die
> eine Bytesize von 13 Bits hatte.
Daß es andere Architekturen gibt, weiß ich, aber hier geht es um einen 
ATmega und um C.
Gast #3093826
Lesenswert?

Es tut mir sehr Leid, dass ich mich hier einklinke und damit ggf. die 
Übersicht ein wenig zerstöre. Allerdings bin ich ebenfalls gerade dabei, 
eine Sinus PWM auf einem Atmega zu realisieren und befinde mich auch bei 
der Sinustabelle.

Der Timer auf dem uC zählt von Bottom (0x00) bis zum TOP Wert (sagen wir 
mal z.B. 0xFF oder einen Wert im OCR1x/ICR1 Register). Also ist der 
Wertebereich im Timer >=0, d.h. keine negativen Zahlen.

Der Sinus hingegen hat eine negative Halbwelle. Wie genau löst man das 
elegant?

Meinen Recherchen im Internet nach baut man sich eine Lookup Tabelle vom 
Sinus, der mit einem Offset nach oben versehen ist. Das heißt, dass die 
positive Amplitude irgendwo nahe dem TOP Wert ist und die negative 
Amplitude irgendwo nahe am Bottom (Je nach Modulationsindex). Der 
"Nulldurchgang" des Sinus ist also irgendwo über 0 im positiven Bereich. 
So macht es z.B. die Lookup Tabelle auf dieser Seite:
http://www.daycounter.com/Calculators/Sine-Generator-Calculator2.phtml

Sehe ich das richtig, dass das eine Variante zur Sinus PWM ist? Oder ist 
das zu umständlich und es geht auch wesentlich einfacher (wie hier 
bereits angedeutet mit lediglich einer Halbperiode, da sich die Werte 
wiederholen aber mit negativem Vorzeichen). Allerdings frage ich mich 
dann, wie die zweite/negative Halbwelle berücksichtigt wird.

Es wäre nett, wenn jemand zu meinem Anliegen Bezug nehmen könnte.

Gruß,
al3ko
Gast #3093864
Lesenswert?

@ al3ko

Na logisch! Was aus der PWM herauskommt, wenn der µC mit
positiver Versorgungsspannung betrieben wird, kann nur
positiv sein.

- Auch der unterste Punkt der negativen Halbwelle.

Das ist keine Variante, sondern das wahre Leben.

Willst du ein nullsymmetrisches elektrisches Signal haben,
musst du entweder mit einem Kondensator die Gleichspannung
abtrennen, oder einen Operationsverstärker mit passendem
Offset versehen.
Gast #3093913
Lesenswert?

Bernie schrieb:
> @ al3ko
>
> Na logisch! Was aus der PWM herauskommt, wenn der µC mit
> positiver Versorgungsspannung betrieben wird, kann nur
> positiv sein.
Richtig. Allerdings geht es mir eher um das Pulse Pattern für die 
Ansteuerung der FETs. Salopp gesagt so wie hier:
http://www.tgipmt.com/resource%20images/articles/motor%20speed%20control/mf32.gif

Dass der Ausgang am uC allerdings 0 bzw. VCC ist, stört mich weniger, 
denn das Signal kommt eh in einen Gatetreiber und dann an eine H-Brücke 
(wo dann am Ende +-V_DClink entsteht). Wie gesagt, das Pulse Pattern ist 
für mich wichtig und genau da sehe ich den Timer als Carriersignal und 
den Sinus als Referenzsignal ausschlaggebend. Um das Pulse Pattern aus 
dem oben verlinkten Bild zu bekommen, hätte ich jetzt den Timer von 0 
bis TOP zählen lassen sowie den Sinus mit einem Offset versetzt.

Sollte so richtig sein, oder?
Gast #3093946
Lesenswert?

Spess53 schrieb:
> Das beschreibt aber eine analoge PWM. Also Vergleich eines
> Eingangssignal mit einem Dreieckssignal.
Da muss ich noch mal nachhaken. Ich hätte jetzt den Timer im Phase 
correct modus laufen lassen, um das Dreiecksignal mit dual slope zu 
bekommen (na klar diskretisiert). Somit habe ich doch schon mal das 
hochfrequente Carriersignal.

Den Sinus als Referenzsignal würde ich über die Lookup Tabelle 
realisieren und den Wert zum jeweiligen Zeitpunkt in das OCR1A bzw. ICR1 
Register laden. Somit hätten wir doch die digitale Version von der 
analogen PWM, oder habe ich einen gravierenden Gedankenfehler?

Falls ja, wäre ich dir/euch sehr dankbar, wenn ihr mir auf die Sprünge 
helfen könntet, es richtig zu verstehen, denn scheinbar sehe ich den 
Wald vor lauter Bäumen nicht :)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren