Forum: Mikrocontroller und Digitale Elektronik AVR, Sinustabelle zu groß für den Speicherplatz


von Daniel (Gast)


Angehängte Dateien:

Lesenswert?

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

von Chris (Gast)


Lesenswert?


von Klaus W. (mfgkw)


Lesenswert?

Wieso braucht man für eine nSinus 2000 Stützstellen (womöglich noch als 
float)?
Das Ding ist glatt wie eine Kindera...

von Norbert (Gast)


Lesenswert?

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

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

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
}

von Stephan B. (matrixstorm)


Lesenswert?

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

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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.

von amateur (Gast)


Lesenswert?

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.

von Christoph db1uq K. (christoph_kessler)


Lesenswert?

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.

von Daniel (Gast)


Lesenswert?

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ß

von Cyblord -. (cyblord)


Lesenswert?

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.

von amateur (Gast)


Lesenswert?

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;-)

von Daniel (Gast)


Lesenswert?

Okay, du hast recht.
Das war mir nicht klar.
Gibts denn eine Möglichkeit oder einen Befehl aus dieser Tabelle zu 
lesen?

von Cyblord -. (cyblord)


Lesenswert?

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

von Le X. (lex_91)


Lesenswert?

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?

von amateur (Gast)


Lesenswert?

>Gibts denn eine Möglichkeit oder einen Befehl aus dieser Tabelle zu
>lesen?

Dein:

result = pgm_read_byte(&sinusTabelle[i]);

tut das doch.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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

von amateur (Gast)


Lesenswert?

Ich habe vorher aber auch:

>const uint16_t sinusTabelle[] PROGMEM = {...};
>result = pgm_read_word(&sinusTabelle[i++]);

geschrieben...

von Andreas (Gast)


Lesenswert?

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. ;)

von EFA (Gast)


Lesenswert?

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.

von EFA (Gast)


Lesenswert?

Waqrum verwendest du überhaupt pgm_read_byte?

result = sinusTabelle[i];

sollte das gleiche Ergebnis bringen.

von EFA (Gast)


Lesenswert?

Bitte den letzten Beitrag von mir ignorieren. Ich habs selbst gemerkt :)

von holger (Gast)


Lesenswert?

>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;)

von Amateur (Gast)


Lesenswert?

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

von Winner (Gast)


Lesenswert?

Es wurde ja schon geschrieben: Nimm nicht so einen Looser controller, 
sonden etwas richtiges. Z. B. ARM oder MSP.

von Le X. (lex_91)


Lesenswert?

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
Noch kein Account? Hier anmelden.