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
Datum:
Hi
>Jetzt stellt sich mit die Frage: Wie macht man sowas "richtig"?
Man benutzt SPI.
MfG Spess
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 :-)
Datum:
digitalWrite() ist ein schnarchlangsamer Funktionsaufruf. Man kann die Pins auch direkt setzen, dürfte mindestens Faktor 20 schneller sein. Peter
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?
Datum:
nicht zu vergessen die CLKDIV8 Fuse zu controllieren, sonst werden aus den 16MHz mal schnell 2MHz.
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.
Datum:
wer Assembler benuetzen kann ist hier klar im Vorteil !
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.
Datum:
detsbet schrieb: > 10^-6 Sekunden. Was ist denn die konforme Abkürzung für > > diese Zeiteinheit? µs
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.
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.