Hallo, ich habe die Sinusfunktion und die Arcuscosinusfunktion mit Hilfe einer Tabelle programmiert. Leider sind die Tabellen mit 2000 Werten jeweils zu groß, so dass der compiler immer folgende Speicherbelegung angibt. Eigentlich sollen die Tabellen auch noch doppelt so groß werden als sie jetzt sind. Gibt es eine Möglcihkeit die Tabellen im anderen Speicherbereich abzulegen? Gruß, Daniel
Leg sie im Flash ab. http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmspeicher_.28Flash.29
Wieso braucht man für eine nSinus 2000 Stützstellen (womöglich noch als float)? Das Ding ist glatt wie eine Kindera...
Wenn es nicht so sehr auf Laufzeit ankommt, nimm nen CORDIC und berechne Deine Stellen. Dann kommst Du mit deutlich weniger Speicher aus. Ansonsten würde es auch reichen z.B. bei einem Sinus nur eine viertel Periode zu speichern und dann zu spiegeln/invertieren um die anderen vier Quadranten zu bekommen. Grüße Norbert
legs im Flash oder EEROM ab... Sieht dann so aus (tiny2313):
1 | #include <avr/interrupt.h> |
2 | #include <avr/pgmspace.h> |
3 | #include <avr/io.h> |
4 | |
5 | const uint8_t samples[] PROGMEM = {127, 133, 139, 146, 152, 158, 164, 170, 176, 181, 187, 192, |
6 | 198, 203, 208, 212, 217, 221, 225, 229, 233, 236, 239, 242, 244, 247, 249, 250, 252, 253, 253, |
7 | 254, 254, 254, 253, 253, 252, 250, 249, 247, 244, 242, 239, 236, 233, 229, 225, 221, 217, 212, |
8 | 208, 203, 198, 192, 187, 181, 176, 170, 164, 158, 152, 146, 139, 133, 127, 121, 115, 108, 102, |
9 | 96, 90, 84, 78, 73, 67, 62, 56, 51, 46, 42, 37, 33, 29, 25, 21, 18, 15, 12, 10, 7, 5, 4, 2, 1, |
10 | 1, 0, 0, 0, 1, 1, 2, 4, 5, 7, 10, 12, 15, 18, 21, 25, 29, 33, 37, 42, 46, 51, 56, 62, 67, 73, |
11 | 78, 84, 90, 96, 102, 108, 115, 121 }; |
12 | |
13 | volatile uint8_t samplewert = 127; |
14 | volatile uint8_t samplesprung = 20; |
15 | volatile uint8_t samplezeiger = 0; |
16 | |
17 | |
18 | int main (void) { |
19 | |
20 | //Portinit
|
21 | DDRB = 0xff; |
22 | PORTB = 127; |
23 | DDRD = (1<<PD5); //PWM |
24 | |
25 | //Timer0 init (PWM DAC)
|
26 | TCCR0A = (1<<COM0B1)|(1<<WGM01)|(1<<WGM00); //noninv. OC0B fast PWM |
27 | TCCR0B = (1<<CS00); //CLK/1 |
28 | OCR0B = 127; |
29 | |
30 | //Timer1 init (10kHz Samplerate)
|
31 | TCCR1A = 0; |
32 | TCCR1B = (1<<WGM12)|(1<<CS10); //CTC Mode (OCR1A), CLK/1 |
33 | TIMSK |= (1<<OCIE1A); //CTC ISR |
34 | OCR1A = 613; //CLK/1228 = 10kHz //CLK/614 = 20kHz |
35 | |
36 | sei(); |
37 | |
38 | while(1) { |
39 | |
40 | ;
|
41 | |
42 | }
|
43 | |
44 | |
45 | return 0; |
46 | }
|
47 | |
48 | ISR (TIMER1_COMPA_vect){ |
49 | |
50 | samplezeiger += samplesprung; |
51 | samplezeiger &= 127; |
52 | samplewert = pgm_read_byte(&samples[samplezeiger]); |
53 | OCR0B = samplewert; |
54 | PORTB = samplewert; |
55 | |
56 | }
|
Hi. Dein IC hat 256KiB Flash - ich stimme Chris zu: Leg deine Stuetzpunkte als "PROGMEM" im Flash ab. Der Lesezugriff verlangsamt sich lediglich um 2 Takte pro Byte und deine Tabelle passt da locker rein. Details gibt es auch in: 1) Beitrag "32 bit Zeiger auf Flash AVR 8-Bit GCC" 2) Beitrag "ATMega Flash: Array" MfG
Norbert schrieb: > Ansonsten würde es auch reichen z.B. bei einem Sinus nur eine viertel > Periode zu speichern und dann zu spiegeln/invertieren um die anderen > vier Quadranten zu bekommen. Und man könnte Stützstellen speichern und dazwischen interpolieren. Wieviele Stützstellen nötig sind kommt auf den maximal erlaubten Fehler an. Aber man glaubt nicht, wie gut man einen viertel Sinus mit 10 nichtlinearen (nicht äquidistanten) Stützpunkten hinbekommt. Denn man muss nur dort die Stützpunkte eng setzen, wo sich was tut. Und das würde beim Sinus bedeuten: von 0 bis pi/8 ist das fast eine Gerade, von pi3/8 bis pi/2 trotz gleichem Abstand aber ganz und gar nicht. Deshalb müssen in diesem Bereich die Stützstellen enger sitzen. So arbeiten z.B. auch Logikanalyzer: wenn 10ms ein '1' Pegel anliegt, dann wird z.B. trotz 100MHz Samplerate nur 1 Datenpaket abgespeichert: '1' für 10ms.
Ich würde die Werte genauso wie Du sie der PWM übergibst ablegen. Also im Ganzzahl-Wortformat oder, wenn Du kein Problem mit dem Klingel in den Ohren hast, im 8-Bit-Format. Darüber hinaus sollte der Bereich von 0 bis pi/2 reichen. Danach passiert ja sowieso nichts neues mehr. Wenn dein RAM jammert, ab ins FLASH.
http://www.mikrocontroller.net/articles/Digitale_Sinusfunktion Eine sehr ausführliche Arbeit zum Thema. http://www.mikrocontroller.net/articles/Sinusgenerator_mit_IIR-Filter Den Artikel hab ich mal angefangen. Scheint aber nicht die ideale Sinusgeneratormethode zu sein. Alle Methoden, die durch Rundungsfehler Amplitudenänderungen erfahren, sind zu kompliziert. Auch Tabellenverfahren haben dicht unterhalb der Nyquistfrequenz ein Problem, ein DDS (hat auch eine Tabelle) macht in den obersten Oktaven Schwebungen, wenn er nicht auf ganzzahlige Teiler der Taktfrequenz eingestellt ist.
Danke für die Hinweise! Ich habe meine Tabelle jetzt mit PROGMEM auf den Flash gelegt, wenn ich jetzt aber wieder aus dieser Tabelle lesen will, werden teilweise Werte zurückgeliefert, welche nicht in der Tabelle stehen. Meine Tabelle sieht folgendermaßen aus: const int sinusTabelle[] PROGMEM = {...}; //enthält zurzeit 1000 Einträge von 0° bis 90° Kann ich nun mit der folgenden Zeile wieder aus dieser Tabelle lesen, oder muss noch etwas anderes beachtet werden? result = pgm_read_byte(&sinusTabelle[i]); Gruß
Du nutzt eine read_BYTE Funktion um integers zu lesen? Und da klingelt nix? Man sollte halt schon ein bisserl nachdenken über das was man da tut.
Es wäre wohl nicht schlecht, wenn Du den Zugriff und die Definition im gleichen Format machen würdest. Also: const uint8_t sinusTabelle[] PROGMEM = {...}; result = pgm_read_byte(&sinusTabelle[i++]); oder const uint16_t sinusTabelle[] PROGMEM = {...}; result = pgm_read_word(&sinusTabelle[i++]); ... muss aber nicht sein;-)
Okay, du hast recht. Das war mir nicht klar. Gibts denn eine Möglichkeit oder einen Befehl aus dieser Tabelle zu lesen?
Daniel schrieb: > Okay, du hast recht. > Das war mir nicht klar. > Gibts denn eine Möglichkeit oder einen Befehl aus dieser Tabelle zu > lesen? Hast du dir mal die API zu den pgm_ Befehlen angeguckt? Da kann man auch größere Einheiten als Bytes lesen. Oder du legst deine Werte als Bytes ab, wobei 2 Bytes dann ein Wort ergeben und halt 2 mal read_byte machst und zusammenbaust. Du kannst den Pointer auch einfach auf uint8_t umbiegen und liest dann Byteweise aus deinen Ints. Es gibt genug Möglichkeiten. gruß cyblord
Oder du tust dir diesen Schund nicht an und verwendest den __flash - Qualifier. Dann kümmert sich der Compiler um den Rest. (eine hinreichend aktuelle avr-gcc Version vorrausgesetzt, imho ab 4.7.x) Ich frag mich eh, wieso hier immer noch PROGMEM gelehrt wird. Weils schon immer so war?
>Gibts denn eine Möglichkeit oder einen Befehl aus dieser Tabelle zu >lesen? Dein: result = pgm_read_byte(&sinusTabelle[i]); tut das doch.
amateur schrieb: > result = pgm_read_byte(&sinusTabelle[i]); > tut das doch. Aber nicht das ganze int16 Wort... le x. schrieb: > den __flash - Qualifier > wieso hier immer noch PROGMEM gelehrt wird. http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#flash_und_Embedded-C
Ich habe vorher aber auch: >const uint16_t sinusTabelle[] PROGMEM = {...}; >result = pgm_read_word(&sinusTabelle[i++]); geschrieben...
le x. schrieb: > Ich frag mich eh, wieso hier immer noch PROGMEM gelehrt wird. Weils > schon immer so war? Weils in C++ nichts anderes gibt. ;)
Andreas schrieb: > le x. schrieb: >> Ich frag mich eh, wieso hier immer noch PROGMEM gelehrt wird. Weils >> schon immer so war? > > Weils in C++ nichts anderes gibt. ;) Mit C++ hat das gar nichts zu tun. Das sind compiler/target-spezifische defines.
Waqrum verwendest du überhaupt pgm_read_byte? result = sinusTabelle[i]; sollte das gleiche Ergebnis bringen.
Bitte den letzten Beitrag von mir ignorieren. Ich habs selbst gemerkt :)
>result = sinusTabelle[i]; >Bitte den letzten Beitrag von mir ignorieren. Ich habs selbst gemerkt :) Mit einem ARM geht das. Da gibt es diesen PROGMEM Unsinn nicht mehr. Das liegt im Flash: const uint16_t sinusTabelle[] = {...}; Das liegt im RAM: uint16_t sinusTabelle[] = {...}; Beides lesen (keine Sonderbehandlung für Flash nötig): result = sinusTabelle[i]; Alleine das ist es schon wert auf ARM zu wechseln. Keine doppelten Funktionen mehr. Wie wollt ihr eigentlich jemals die Galaxie verlassen wenn ihr euch mit Controllern beschäftigt die nicht fliegen können? Immer ein Bremsklotz am Bein. Nimm ARM statt Red Bu... Dann kannst auch du fliegen;)
>Wie wollt ihr eigentlich jemals die Galaxie verlassen wenn
Also mir gefällt’s hier, ich gehöre nicht zu denen die per se meinen es
ist andernorts schöner, bequemer, besser oder ........ (bitte
zutreffendes Eintragen) ist.
Aus diesem Grunde werde ich den Unsinn auch weiterhin machen. Übrigens
bei der Gelegenheit wird man immer mal wieder mit den zugrundeliegenden
Funktionen konfrontiert und daran, dass es auch sinnvoll sein kann die
anderen, zu diesem Bereich gehörenden Funktionen, zu verwenden. Compiler
sind nämlich doof – wenn sie auch in den letzten 30 Jahren hinzugelernt
haben.
Auch vergisst man bei der Gelegenheit nicht, warum Zugriffe aufs Flash
langsamer sind als z.B. Zugriffe auf Konstanten im RAM – für die, die
auf 'nen Quicky stehen.
Es wurde ja schon geschrieben: Nimm nicht so einen Looser controller, sonden etwas richtiges. Z. B. ARM oder MSP.
holger schrieb: >>result = sinusTabelle[i]; > > Mit einem ARM geht das. Da gibt es diesen PROGMEM Unsinn nicht mehr. > > Das liegt im Flash: > const uint16_t sinusTabelle[] = {...}; > > Das liegt im RAM: > uint16_t sinusTabelle[] = {...}; > > Beides lesen (keine Sonderbehandlung für Flash nötig): > result = sinusTabelle[i]; > > Alleine das ist es schon wert auf ARM zu wechseln. > Keine doppelten Funktionen mehr. Wie gesagt, bei den ATmegas geht das mittlerweile über "__flash" genauso. Die letzten drei Beiträge vor mir, die den ARM so hervorheben, ignorieren das allerdings.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.