www.mikrocontroller.net

Forum: Mikrocontroller und Elektronik ATmega 328 zu langsam für 40kHz TLC549?


Autor: detsbet (Gast)
Datum:

Hallo Leute,

ich habe gestern einen gebrauchen Arduino Duemilanove bekommen, und
direkt ein wenig darum herumgespielt. Der AVR ist ein ATmega 328 mit den
normalen 16Mhz Taktrate. Jetzt habe ich, weil er hier herumflog mal
einen TLC 549 drangesteckt und versuche den auszulesen.
#include <TimerOne.h>
int CS_pin = 4;   // Pin für das CS Signal
int IO_clock = 2; //Pin für das I/O Clock Signal
int DATA_pin = 3; //Der Pin für die ausgelesenen Daten
int val = 0;     //Ausgelesener Wert
int rounds = 0;

void setup()
{
  pinMode(CS_pin, OUTPUT);   //CS ist Wandlungsanzeige
  pinMode(IO_clock, OUTPUT); //IO ist Taktsignal
  pinMode(DATA_pin, INPUT);  //Hier fallen dann später die konvertierten Werte heraus
  Serial.begin(9600); //Serielle Ausgabe anmachen
  Timer1.initialize(150);
  Timer1.attachInterrupt(getAnalogValues);
}

void getAnalogValues()
{
  digitalWrite(CS_pin, HIGH); //Wandlung auf aus
  digitalWrite(CS_pin, LOW); //Wandlung auf an
  
  digitalWrite(IO_clock, LOW); //Taktsignal auf LOW
  
  val = digitalRead(DATA_pin)*128; //Höchstwertigstes Bit lesen

  digitalWrite(IO_clock, HIGH); //Einen Taktzyklus
  digitalWrite(IO_clock, LOW);
  val = val + digitalRead(DATA_pin)*64; //Bit 6 lesen und addieren
  
  digitalWrite(IO_clock, HIGH); //Einen Taktzyklus
  digitalWrite(IO_clock, LOW);  
  val = val + digitalRead(DATA_pin)*32; //Bit 5 lesen und addieren

  digitalWrite(IO_clock, HIGH); //Einen Taktzyklus
  digitalWrite(IO_clock, LOW);  
  val = val + digitalRead(DATA_pin)*16; //Bit 4 lesen und addieren
  
  digitalWrite(IO_clock, HIGH); //Einen Taktzyklus
  digitalWrite(IO_clock, LOW);  
  val = val + digitalRead(DATA_pin)*8; //Bit 3 lesen und addieren

  digitalWrite(IO_clock, HIGH); //Einen Taktzyklus
  digitalWrite(IO_clock, LOW);  
  val = val + digitalRead(DATA_pin)*4; //Bit 2 lesen und addieren
  
  digitalWrite(IO_clock, HIGH); //Einen Taktzyklus
  digitalWrite(IO_clock, LOW);
  val = val + digitalRead(DATA_pin)*2; //Bit 1 lesen und addieren
  
  digitalWrite(IO_clock, HIGH); //Einen Taktzyklus
  digitalWrite(IO_clock, LOW);
  val = val + digitalRead(DATA_pin); //Bit 0 lesen und addieren
  
  rounds = rounds + 1;
}

void loop(){ //Das wird ausgeführt, wenn noch Zeit ist
    if (rounds > 1000){
      Serial.print(rounds); //Wert an den Rechner senden
      Serial.print("\n");
      rounds = 0;
      }
}


Das funktioniert auch ganz gut. Die loop führt er natürlich nur aus,
wenn getAnalogValues() nicht länger als 150ms braucht. Das ist auch mein
Problem. Wenn ich den Timer herunterdrehe, so das er alle 140ms oder
weniger den Interrupt auslöst, ist Stille auf der seriellen Leitung. Er
findet also keine Zeit mehr die Bedingung unten zu durchlaufen.

Jetzt stellt sich mit die Frage: Wie macht man sowas "richtig"?
Irgendwie ist das ja alles ziemlich eklig, aus dem IO_clock fällt z.B.
irgendein verkrüppeltes Rechteck statt eines sauberen Taktes mit immer
gleicher Frequenz raus. Erzeugt man, wenn man schneller sein will eine
PWM auf dem IO_clock und wie syncronisiert man das dann mit dem
Auslesen? Und wie speichert man die Werte richtig heraus, das
multiplizieren mit den Vorfaktoren dürfte ja auch ziemlich langsam sein.

Ich hoffe, jemand kann mit da etwas weiterhelfen,
detsbet
Autor: spess53 (Gast)
Datum:

Hi

>Jetzt stellt sich mit die Frage: Wie macht man sowas "richtig"?

Man benutzt SPI.

MfG Spess
Autor: H.joachim Seifert (crazyhorse)
Datum:

Ohne jetzt verstanden zu haben, wo dein eigentliches Problem liegt:
deine getAnalogValues() solltest du noch mal überarbeiten.
1. Schleife benutzen
2. es ist unnötig, erst zu multiplizieren und dann zu addieren, schieben
und einlesen reicht :-)

for (loop=0, val=0;loop<8;loop++)
   {clock=1;
    clock=0;
    val=val<<1;
    if (Data_Pin) val++;;
    }

so in etwa, es könnten Fehler enthalten sein :-)
Autor: Peter Dannegger (peda)
Datum:

digitalWrite() ist ein schnarchlangsamer Funktionsaufruf.
Man kann die Pins auch direkt setzen, dürfte mindestens Faktor 20
schneller sein.


Peter
Autor: Karl Heinz Buchegger (kbuchegg) (Moderator)
Datum:

Ich schätze mal, dass die Funktionen digitalWrite und digitalRead ob
ihrer Allgemeinheit jede Menge Taktzyklen verbrutzeln werden. Da greift
man direkt auf die Portpins zu und ermöglicht so dem Compiler, die
schnellen Bit Instuktionen für Ports zu benutzen.

Allerdings sind 150ms eine Menge Holz. Das sind über den Daumen
120-TAUSEND Prozessor Instruktionen bei 1Mhz. So umständlich kann
digitalWrite bzw digitalRead gar nicht implementiert sein, dass du da in
Zeitnot kommst.

Bist du sicher, dass das hier

Timer1.initialize(150);

tatsächlich Millisekunden von einem Interrupt zum nächsten angibt?
Autor: ... (Gast)
Datum:

nicht zu vergessen die CLKDIV8 Fuse zu controllieren, sonst werden aus
den 16MHz mal schnell 2MHz.
Autor: Tom (Gast)
Datum:

detsbet schrieb:
> as ist auch mein
> Problem. Wenn ich den Timer herunterdrehe, so das er alle 140ms oder
> weniger den Interrupt auslöst, ist Stille auf der seriellen Leitung. Er
> findet also keine Zeit mehr die Bedingung unten zu durchlaufen.

RTFM:

initialize(period)

You must call this method first to use any of the other methods. You can
optionally specify the timer's period here (in microseconds), by
default it is set at 1 second. Note that this breaks analogWrite() for
digital pins 9 and 10 on Arduino.
Autor: digitaler fritz (Gast)
Datum:

wer Assembler benuetzen kann ist hier klar im Vorteil !
Autor: detsbet (Gast)
Datum:

Erst einmal danke für die Anregungen.
Habe da wohl "ms" mit Mikrosekunden verdreht, ich meinte mit "ms"
eingentlich 10^-6 Sekunden. Was ist denn die konforme Abkürzung für
diese Zeiteinheit?
#include <TimerOne.h>

/*
CS_pin = 4    Pin für das CS Signal
IO_clock = 2  Pin für das I/O Clock Signal
DATA_pin = 3  Pin für die ausgelesenen Daten
*/
int val = 0;     //Ausgelesener Wert
int rounds = 0;

void setup()
{
  pinMode(4, OUTPUT);   //CS ist Wandlungsanzeige
  pinMode(2, OUTPUT); //IO ist Taktsignal
  pinMode(3, INPUT);  //Hier fallen dann später die konvertierten Werte heraus
  
  Serial.begin(9600); //Serielle Ausgabe anmachen
  Timer1.initialize(30);
  Timer1.attachInterrupt(getAnalogValues);
}

void getAnalogValues()
{
  val = 0;
  digitalWrite(4, HIGH); //Wandlung auf aus
  digitalWrite(4, LOW); //Wandlung auf an
   
  if (PIND & (1<<PD3)) val++; //MSB lesen, also addieren wenn auf PD3 eine 1 anliegt
    
  for (int i=0; i<7; i++){
    //Ein Taktzyklus für den IO_clock
    PORTD = PORTD | B00000100; // Setzt nur den Pin 2 auf High, der Rest bleibt erhalten (OR)
    PORTD = PORTD ^ B00000100; // Setzt nur den Pin 2 auf Low, der Rest bleibt erhalten (XOR)

    val <<= 1; //Ein Bit nach links schieben
    if (PIND & (1<<PD3)) val++; //restliche Bits lesen
  }
    
  rounds = rounds + 1;
}

void loop(){ //Das wird ausgeführt, wenn noch Zeit ist
    if (rounds > 1000){
      Serial.print(val); //Wert an den Rechner senden
      Serial.print("\n");
      rounds = 0;
      }
}

Jetzt kann man den Timer problemlos auf 30 Mikrosekunden stellen, und es
kommen immer noch die richtigen Werte aus dem ADC gefallen. Das obere
digitalWrite habe ich gelassen, wenn ich das ersetzt habe gabs Probleme.

Danke auf jeden Fall, so ist die Lösung auch deutlich eleganter.
Autor: ... (Gast)
Datum:

detsbet schrieb:
> 10^-6 Sekunden. Was ist denn die konforme Abkürzung für
>
> diese Zeiteinheit?

µs
Autor: Karl Heinz Buchegger (kbuchegg) (Moderator)
Datum:

Tu dir selbst einen Gefallen
#include <TimerOne.h>

#define CS_PIN    4    // Pin für das CS Signal
#define IO_CLOCK  2    // Pin für das I/O Clock Signal
#define DATA_PIN  3    // Pin für die ausgelesenen Daten

...


void setup()
{
  pinMode(CS_PIN, OUTPUT);   //CS ist Wandlungsanzeige
  pinMode(IO_CLOCK, OUTPUT); //IO ist Taktsignal
  pinMode(DATA_PIN, INPUT);  //Hier fallen dann später die konvertierten Werte heraus

....


void getAnalogValues()
{
  val = 0;
  digitalWrite(CS_PIN, HIGH); //Wandlung auf aus
  digitalWrite(CS_PIN, LOW); //Wandlung auf an
   
  if (PIND & (1<<DATA_PIN))
    val++; //MSB lesen, also addieren wenn auf PD3 eine 1 anliegt
    
  for (int i=0; i<7; i++){
    //Ein Taktzyklus für den IO_clock
    PORTD |= (1<<CS_PIN);
    PORTD &= ~(1<<CS_PIN);

    val <<= 1;               //Ein Bit nach links schieben
    if (PIND & (1<<DATA_PIN))
      val++;                 //restliche Bits lesen
  }
    
  rounds = rounds + 1;
}

liest sich doch gleich viel besser und wenn du mal andere Pins benutzen
willst/musst brauchst du nur an 1 Stelle entsprechend ändern.

Und das XOR zum löschen war zwar hier nicht so ganz falsch, ist aber vom
vorhergehenden Statement abhängig, was keine so gute Idee ist. Wenn du 1
Bit auf 0 setzen willst, dann schreib das auch so: Bit auf 0 setzen.
Alles andere kann in der Zukunft nämlich auch schon mal ins Auge gehen,
wenn man die genauen Zusammenhänge nicht mehr im Kopf hat.
Autor: detsbet (Gast)
Datum:

Danke Karl Heinz, das ist natürlich übersichtlicher. Deinen (wohl
gewollten) Fehler, das du unten den CS_pin und den IO_clock verdreht
hast habe ich aber erst nach 5 Minuten Suche gefunden. Ist so auch
wirklich sauberer zu Lesen und ggf. einfacher zu Ändern.

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




Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder GIF-Format hochladen.
Siehe Bildformate

Mit dem Abschicken erkennst du die Nutzungsbedingungen an.

webmaster@mikrocontroller.netImpressumNutzungsbedingungenWerbung auf Mikrocontroller.net