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.
1
#include<TimerOne.h>
2
intCS_pin=4;// Pin für das CS Signal
3
intIO_clock=2;//Pin für das I/O Clock Signal
4
intDATA_pin=3;//Der Pin für die ausgelesenen Daten
5
intval=0;//Ausgelesener Wert
6
introunds=0;
7
8
voidsetup()
9
{
10
pinMode(CS_pin,OUTPUT);//CS ist Wandlungsanzeige
11
pinMode(IO_clock,OUTPUT);//IO ist Taktsignal
12
pinMode(DATA_pin,INPUT);//Hier fallen dann später die konvertierten Werte heraus
13
Serial.begin(9600);//Serielle Ausgabe anmachen
14
Timer1.initialize(150);
15
Timer1.attachInterrupt(getAnalogValues);
16
}
17
18
voidgetAnalogValues()
19
{
20
digitalWrite(CS_pin,HIGH);//Wandlung auf aus
21
digitalWrite(CS_pin,LOW);//Wandlung auf an
22
23
digitalWrite(IO_clock,LOW);//Taktsignal auf LOW
24
25
val=digitalRead(DATA_pin)*128;//Höchstwertigstes Bit lesen
26
27
digitalWrite(IO_clock,HIGH);//Einen Taktzyklus
28
digitalWrite(IO_clock,LOW);
29
val=val+digitalRead(DATA_pin)*64;//Bit 6 lesen und addieren
30
31
digitalWrite(IO_clock,HIGH);//Einen Taktzyklus
32
digitalWrite(IO_clock,LOW);
33
val=val+digitalRead(DATA_pin)*32;//Bit 5 lesen und addieren
34
35
digitalWrite(IO_clock,HIGH);//Einen Taktzyklus
36
digitalWrite(IO_clock,LOW);
37
val=val+digitalRead(DATA_pin)*16;//Bit 4 lesen und addieren
38
39
digitalWrite(IO_clock,HIGH);//Einen Taktzyklus
40
digitalWrite(IO_clock,LOW);
41
val=val+digitalRead(DATA_pin)*8;//Bit 3 lesen und addieren
42
43
digitalWrite(IO_clock,HIGH);//Einen Taktzyklus
44
digitalWrite(IO_clock,LOW);
45
val=val+digitalRead(DATA_pin)*4;//Bit 2 lesen und addieren
46
47
digitalWrite(IO_clock,HIGH);//Einen Taktzyklus
48
digitalWrite(IO_clock,LOW);
49
val=val+digitalRead(DATA_pin)*2;//Bit 1 lesen und addieren
50
51
digitalWrite(IO_clock,HIGH);//Einen Taktzyklus
52
digitalWrite(IO_clock,LOW);
53
val=val+digitalRead(DATA_pin);//Bit 0 lesen und addieren
54
55
rounds=rounds+1;
56
}
57
58
voidloop(){//Das wird ausgeführt, wenn noch Zeit ist
59
if(rounds>1000){
60
Serial.print(rounds);//Wert an den Rechner senden
61
Serial.print("\n");
62
rounds=0;
63
}
64
}
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
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 :-)
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?
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.
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?
1
#include<TimerOne.h>
2
3
/*
4
CS_pin = 4 Pin für das CS Signal
5
IO_clock = 2 Pin für das I/O Clock Signal
6
DATA_pin = 3 Pin für die ausgelesenen Daten
7
*/
8
intval=0;//Ausgelesener Wert
9
introunds=0;
10
11
voidsetup()
12
{
13
pinMode(4,OUTPUT);//CS ist Wandlungsanzeige
14
pinMode(2,OUTPUT);//IO ist Taktsignal
15
pinMode(3,INPUT);//Hier fallen dann später die konvertierten Werte heraus
16
17
Serial.begin(9600);//Serielle Ausgabe anmachen
18
Timer1.initialize(30);
19
Timer1.attachInterrupt(getAnalogValues);
20
}
21
22
voidgetAnalogValues()
23
{
24
val=0;
25
digitalWrite(4,HIGH);//Wandlung auf aus
26
digitalWrite(4,LOW);//Wandlung auf an
27
28
if(PIND&(1<<PD3))val++;//MSB lesen, also addieren wenn auf PD3 eine 1 anliegt
29
30
for(inti=0;i<7;i++){
31
//Ein Taktzyklus für den IO_clock
32
PORTD=PORTD|B00000100;// Setzt nur den Pin 2 auf High, der Rest bleibt erhalten (OR)
33
PORTD=PORTD^B00000100;// Setzt nur den Pin 2 auf Low, der Rest bleibt erhalten (XOR)
34
35
val<<=1;//Ein Bit nach links schieben
36
if(PIND&(1<<PD3))val++;//restliche Bits lesen
37
}
38
39
rounds=rounds+1;
40
}
41
42
voidloop(){//Das wird ausgeführt, wenn noch Zeit ist
43
if(rounds>1000){
44
Serial.print(val);//Wert an den Rechner senden
45
Serial.print("\n");
46
rounds=0;
47
}
48
}
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.
#define IO_CLOCK 2 // Pin für das I/O Clock Signal
5
#define DATA_PIN 3 // Pin für die ausgelesenen Daten
6
7
...
8
9
10
voidsetup()
11
{
12
pinMode(CS_PIN,OUTPUT);//CS ist Wandlungsanzeige
13
pinMode(IO_CLOCK,OUTPUT);//IO ist Taktsignal
14
pinMode(DATA_PIN,INPUT);//Hier fallen dann später die konvertierten Werte heraus
15
16
....
17
18
19
voidgetAnalogValues()
20
{
21
val=0;
22
digitalWrite(CS_PIN,HIGH);//Wandlung auf aus
23
digitalWrite(CS_PIN,LOW);//Wandlung auf an
24
25
if(PIND&(1<<DATA_PIN))
26
val++;//MSB lesen, also addieren wenn auf PD3 eine 1 anliegt
27
28
for(inti=0;i<7;i++){
29
//Ein Taktzyklus für den IO_clock
30
PORTD|=(1<<CS_PIN);
31
PORTD&=~(1<<CS_PIN);
32
33
val<<=1;//Ein Bit nach links schieben
34
if(PIND&(1<<DATA_PIN))
35
val++;//restliche Bits lesen
36
}
37
38
rounds=rounds+1;
39
}
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.
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.