www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik FFT auf ATmega128


Autor: Matthias B. (caleb)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe bei youtube ein nettes video gesehen 
(http://www.youtube.com/watch?v=GTb6RsJd2yc) und hab mir gedacht diesen 
Aufbau einfach mal nachzubauen.
Ich hab mir dann das Projekt einfach, wie in seiner Notiz beschrieben, 
bei sourceforge geladen. http://sourceforge.net/projects/avrfftlcd/

Meinen ATmega128 betreibe ich auf einem STK501 mit externem 16MHz Quarz.
Die UART Kommunikation (er meldet das UART, LCD und ADC erfolgreich 
initialisiert worden) funktioniert auch soweit, doch sieht es bei mir 
nicht so schön aus wie im Video =/ picture attached

Hab die lcd.h an meine Ports angepasst ...
#define  DATA_PORT   PORTC
#define  DATA_DDR    DDRC

#define  CTRL_PORT   PORTE
#define  CTRL_DDR    DDRE
#define  CTRL_RST    7 
#define  CTRL_CS2    4 
#define  CTRL_CS1    3 
#define  CTRL_E      2 
#define  CTRL_RW     1 
#define  CTRL_RS     0 
#define  CTRL_BL     5

Leider wird weder ein ordentliches Spektrum noch eine vernünftige 
Signalform angezeigt.

Ich bin ganz ehrlich und hätte auch nicht gedacht, dass es auf anhieb 
funktioniert doch bin ich ein wenig überfragt an was es liegen könnte.

Irgendwie scheint auch die linke LCD-Seite zu "hinken" ...

Würde mich freuen, wenn mir jemand ein Denkanstoß in die richtige 
Richtung geben könnte =)

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das wird wohl hier dran liegen;)

18                 .global  lcd_delay_us
  20                 lcd_delay_us:
  21                 .LFB2:
  22                 .LM1:
  23                 /* prologue: frame size=0 */
  24                 /* prologue end (size=0) */
  25                 .LVL0:
  26                 /* epilogue: frame size=0 */
  27 0000 0895          ret
  28                 /* epilogue end (size=1) */
  29                 /* function lcd_delay_us size 1 (0) */


lcd_delay_us() wird komplett wegoptimiert.
Mach i und j mal volatile. Stimmen tun die Zeiten
dann aber wohl auch nicht.

Probiers besser mal mit _delay_us() aus util/delay.h.

Autor: Matthias B. (caleb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das sieht doch viel besser aus =)
Habs gleich mit _delay_us() gemacht.
Aber die Zeiten stimmen noch nicht ... kuck ich mir gleich nochmal an.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du die avr-libc 1.7.0 aus dem Zeitraum vor Mitte November 2010 
hast, denk an den delay-Bug in _delay_ms() und _delay_us() und dessen 
Bugfix: 
http://www.mikrocontroller.net/articles/AVR-GCC-Tu...

Autor: Matthias B. (caleb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erstmal vielen Dank für Eure Hilfe =)

Ich hätte da aber noch ein paar grundlegende Fragen.

1)
In der dazugehörigen ReadMe schreibt er was von ...

>The ADC clock is set to XTAL frequency/128 and "A normal conversion
>takes 13 ADC clock cycles",which means the sample rate is about 9.6 kHz

Das leuchtet mir auch soweit ein ...
16MHz-CPU-Clock/128-Prescaler/13Zyklen = 9,61kHz Samplerate.
Aber warum initialisiert er dann den Prescaler auf 64?
ADCSRA = ((ADCSRA & 0b11111000) | 0b00000110); // clk/64
Oder täusche ich mich?
Dann würde der ADC mit 250kHz fahren ... und was hätte das zur Folge?
"Nur" einen erhöhten Fehler beim Samplen?

2)
>Chan developed this nice FFT routines for megaAVRs. Number of sample points 
>(FFT_N) can be change in ffft.h

Wenn ich da was anderes einstelle als N = 256, z.B. 64 oder 512, kommt 
nur ein gepflegter Zebrastreifen auf dem LCD.
Was könnte da das Problem sein?

Habt ein wenig Nachsehen mit mir =)
Bin mehr oder weniger Anfänger und würde mich wieder über Denkanstöße 
freuen =)

Grüße

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Aber warum initialisiert er dann den Prescaler auf 64?

Das tut er tatsächlich;) Kannst du ja ändern.

>Dann würde der ADC mit 250kHz fahren ... und was hätte das zur Folge?

200kHz ist das was im Datenblatt steht für volle 10Bit.

>"Nur" einen erhöhten Fehler beim Samplen?

Ja.

Noch ein Tip:

Nimm aus lcd_print_point(), lcd_print_signal() und lcd_print_column()
mal das lcd_update() raus. Dann gehts zügiger zur Sache.

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Wenn ich da was anderes einstelle als N = 256, z.B. 64 oder 512, kommt
>nur ein gepflegter Zebrastreifen auf dem LCD.
>Was könnte da das Problem sein?

64 müsste gehen. Mach mal ein Clean Build. Also alles
neu übersetzen. Bei 512 fliegt dir das RAM um die Ohren;)

Autor: Matthias B. (caleb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab Dank holger.
Läuft jetzt schön flüssig.
Aber eine primitive Frage bleibt mir leider nicht erspart =/

Was zeigt mir denn das Display an?

Nochmal kurz zusammengefasst:

Ich taste das Analogsignal mit 9,61 kHz ab.
Ich freu mich und weiß, dass ich den AD besser mal nicht mit mehr als 
4,8kHz stressen sollte.

Ich nutze die 256-Punkte-FFT und lass mir die ersten 128 Spektralwerte 
ausgeben. "Zufällig" ist ja mein LCD auch 128 Dots breit.
Nun hab ich ein wenig Google benutzt und kann mir daraus ableiten, dass 
meine Frequenzauflösung delta_f = f_abtast / N ist.
Das würde dann ja rund 37,5 Hz Frequenzauflösung entsprechen, oder?
Damit könnte/müsste/sollte ich ja die 4,8kHz auf meinem 128Punkt-LCD 
darstellen können.

Nun komme ich aber nur bis knapp 3MHz und dann bin ich schon am 128ten 
Punkt angelangt.

Wo liegt denn da bitte mein Denkfehler?

Grüße

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Nun komme ich aber nur bis knapp 3MHz und dann bin ich schon am 128ten
>Punkt angelangt.

Du meinst 3kHz?

>Wo liegt denn da bitte mein Denkfehler?

Die Wandlung dauert halt etwas länger. Bei mir wird
(gemessen) mit 8,93kHz gesampelt. Liegt wohl am Schleifenoverhead
und möglicherweise auch am SingleConversion. Der letzte Balken
ist bei mir bei 4,5kHz. Passt also.

Wenn du den Zebrastreifen loswerden möchtest geh mal in lcd_cls()
und ändere folgende Zeile:

 vram[page][y] = 1 + page;

in

 vram[page][y] = 0;

Autor: Matthias B. (caleb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
holger schrieb:
>>Nun komme ich aber nur bis knapp 3MHz und dann bin ich schon am 128ten
>>Punkt angelangt.
>
> Du meinst 3kHz?
Jo meine ich. Sorry

Wie hast du denn die Samplerate gemessen?

Mit deinen Werten würde ich mich auch abfinden.
Ich frag mich nur warum das bei mir so stark abweicht =/

Autor: holger (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
>Wie hast du denn die Samplerate gemessen?

Pin toggeln und Osci dran.

>Mit deinen Werten würde ich mich auch abfinden.
>Ich frag mich nur warum das bei mir so stark abweicht =/

Keine Ahnung. Ich hab aber schon ein bisserl am Code
rumgefummelt. Inzwischen läufts im FreeRunning Mode
mit 9600Hz Samplerate. Siehe Anhang.

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>>Ich frag mich nur warum das bei mir so stark abweicht =/

>Keine Ahnung.

Nimm das sendchar aus der Sampleschleife;)

Autor: Matthias B. (caleb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok dank dir. Das läuft ja soweit ganz gut.

Wenn mich nun nur Frequenzen bis 1kHz interessieren bzw. eine bessere 
Auflösung bis 1kHz?
Wie geht man da vor?

-> dann müsste ich mit mind. 2kHz abtasten
-> die Samplerate errechnet sich ja aus Clock/Prescaler/Zyklen

Die einzige Variable an der ich drehen kann sind ja die Zyklen oder?
Die normale Wandlung dauert 13 Zyklen ... kann ich diesen Prozess 
definiert mit Zyklen "verlängern"? Damit meine Samplerate auf rund 2kHz 
bekomme?

Quasi: 16MHz/128/(13+X)=2kHz
In diesem Fall könnte ich ja den Prozess um 49 Zyklen "verlängern" 
(wenns überhaupt funktioniert?) und komme dann auf eine Auflösung von 
rund 8Hz.

Oder bin ich da falsch rangegangen?

Edit: Irgendwelche NOPs evtl.?

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Wenn mich nun nur Frequenzen bis 1kHz interessieren bzw. eine bessere
>Auflösung bis 1kHz?
>Wie geht man da vor?

Siehe unten.

>-> dann müsste ich mit mind. 2kHz abtasten

Richtig.

>-> die Samplerate errechnet sich ja aus Clock/Prescaler/Zyklen

Nur mit dem Prescaler kommst du aber nicht auf die Samplerate.

So müsste das gehen:
1. ADC auf SingleConversion einstellen
2. Timer auf CTC mit 0.5ms Interruptrate einstellen
3. Im Timerinterrupt Conversion starten
4. Im ADC Interrupt die Daten abholen

Mit dem Timer kann man dann verschiedene Sampleraten einstellen.

Autor: Matthias B. (caleb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aso ... dank Dir.
Werds wohl heut nicht mehr schaffen es auszuprobieren (Geschenke kaufen 
etc.)

Probier ich gleich nach den Feiertagen mal aus.

Frohes Fest
Matze

Autor: Matthias B. (caleb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab mir das nun nochmal angekuckt =)

holger schrieb:
> So müsste das gehen:
> 1. ADC auf SingleConversion einstellen
> 2. Timer auf CTC mit 0.5ms Interruptrate einstellen
> 3. Im Timerinterrupt Conversion starten
> 4. Im ADC Interrupt die Daten abholen

1) Der Timer stand/steht auf SingleConversion

2) Hab mir den 8bit-Timer/Counter0 herbei genommen
  // Timer 0 in CTC-Modus bringen
  TCCR0 |= (1<<WGM01); // CTC Modus  
  
  TCCR0 |= ((1<<CS01) | (1<<CS00)); // Timer0 Prescaler 64

  // Timer0 bis 125 zählen lassen ... müsste 0,5ms entsprechen
  OCR0 = 250-1;   //125-1;
      
  // CTC-Interrupt erlauben
  TIMSK |= (1<<OCIE0);

Tsja bei den Interrupts bin ich ein wenig ins straucheln geraten ...
Letztendlich bin ich bei folgender Variante angelangt ...
ISR (TIMER0_COMP_vect)
{
    flag = 1;
}
Von dem Interrupt lass ich mir halt die volatile Variable "flag" auf 1 
setzen, um dann ...
void adc_capture (unsigned char channel, unsigned int *voltage)
{
  unsigned int i;

  ADMUX = 0x40 + channel;  // selects single-ended conversion on pin PF0 ~ PF7 (ADC0 ~ ADC7)
              // selects AVCC as Vref
              // selects right adjust of ADC result
                        
  for (i = 0; i < FFT_N; i++)  // averaging the ADC results
  {
    while (flag != 1)
    {
         
    }
    
    sbi(ADCSRA,ADSC); // start a conversion by writing a one to the ADSC bit (bit 6)
    while(!(ADCSRA & 0b00010000)) // wait for conversion to complete: ADIF = 1
      {
        //sentchar1('.');    // for debugging
      }
    flag = 0;
    *voltage++ = ((ADCL) | (ADCH<<8));
    
  }
}
immer mit Timer-Interrupt die Conversion zu starten.

Den Interrupt hab ich vor/nach dem Aufruf von adc_capture an/aus 
geschalten.
  sei();
  
  // ADC 5v to 10-bit digital (0x00 - 0x3FF)
  adc_capture(channel, voltage);
    
  cli();

Nun mal zur Sache =)
Es ist sicherlich völlig grauenhaft programmiert, aber ich bin wie 
gesagt noch recht frisch in der Materie.
Mein LCD löst mir jetzt den Frequenzbereich von 0-1000 Hz nun mit einem 
Frequenzraster von 8Hz auf ...
ABER nur wenn ich den Zähler bis 255 laufen lasse ? =/
Warum denn das?
Ich arbeite mit einem Clock von 16MHz den ich, wie oben gezeigt, durch 
64 teile. Dann komme ich auf 250kHz, so das mein Timer/Counter0 bis 125 
zählen müsste um die von mir gewollte 0,0005s Interruptrate zu 
erreichen.
Wenn ich bis 125 zählen lasse stellt mir das Display 2MHz dar.
Wo hab ich denn den Faktor 2 liegen lassen?

Grüße Matze
#hält für verbale Ohrfeigen seine Wange hin#

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.