Pages: [1] 2   Go Down
Author Topic: 32Bit-Zahl als Bit-Array / --> Einlesen eines Absolutwertgebers  (Read 1655 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Sr. Member
****
Karma: 2
Posts: 381
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,
bräuchte mal wieder Hilfe:

Ich bekomme eine 32 Bit Zahl über einen Eingang auf den Arduino Bit für Bit übertragen.
Jetzt stell ich mir die Frage, wie ich das am elegantesten umrechne.
Mir ist bis jetzt nur die Möglichkeit eingefallen, die 32 Bit in ein "bool myarray[32]" abzuspeichern und dann händisch durchzurechnen: myarray[1]*2^0+myarray[2]*2^1+myarray[3]*2^2+......

Gibts da nicht eine andere Möglichkeit? Irgendwie die Bits "durchzuschieben"? Bitshift hab ich mir mal angeschaut, aber wie bring ich das neue Bit dann in die Zahl rein?

Habt ihr da eine Idee?

Gruß/hk007

« Last Edit: January 01, 2013, 11:08:49 am by hk007 » Logged

Arduino 1.0.5 | Arduino UNO & MEGA | Arduino 1.54r2 mit DUE

Germany S-H
Offline Offline
Faraday Member
**
Karma: 145
Posts: 3019
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Habt ihr da eine Idee?

http://arduino.cc/en/Reference/BitWrite
Logged

Offline Offline
Sr. Member
****
Karma: 2
Posts: 381
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey,
das hört sich gut an. Werds mal testen.
Sind die Befehle neu? Die hab ich noch nie gesehen/gebraucht. smiley-red
Logged

Arduino 1.0.5 | Arduino UNO & MEGA | Arduino 1.54r2 mit DUE

Forum Moderator
BZ (I)
Offline Offline
Brattain Member
*****
Karma: 260
Posts: 21566
+39 349 2158303
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Nein, nicht so neu, kenne sie seit mindestens 1/2 Jahr.
grüße Uwe
Logged

Germany S-H
Offline Offline
Faraday Member
**
Karma: 145
Posts: 3019
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sind die Befehle neu? Die hab ich noch nie gesehen/gebraucht. smiley-red

Wenn Du auf der verlinkte Referenzseite "Page History" anklickst, kannst Du sehen, dass die Dokumentation der Bit-Manipulationsbefehle im Jahr 2008 angelegt wurde.
Logged

Offline Offline
Edison Member
*
Karma: 21
Posts: 1419
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Wenn Du die einzelnen Bits nacheinander (seriell) bekommst, kannst Du auch tatsächlich Bits schieben.
Kleines Beispiel:
Code:
int bits[20] = {1,0,1,1,0,0,1,0,0,0,1,1,1,0,1,0,0,1,1,1};

void setup() {
  Serial.begin(9600);
  long data = 0;
  for (int i = 0; i< 20; i++)
    //alle Bits eins weiter schieben und aktuelles Bit dazu addieren
    data = ( data << 1 ) + bits[i];
  Serial.println(data,BIN);
}

void loop() {}
Logged

Offline Offline
Sr. Member
****
Karma: 2
Posts: 381
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,

also das mit den bitWrite klappt ganz prima :-)
zu meiner Geschichte hätte ich jetzt noch zwei Zusatzfragen:
1.)
Gibts irgendwo eine Aufstellung, wie viel Laufzeit ein Befehl hat?
Hintergrund:
Ich hab den Befehl in einer Interruptroutine:
Code:
long distance = 0;
int ctr;
const int data = 3;
.
.
.
void Takt() {
  newdata = HIGH;
  bitWrite(distance, ctr, !digitalRead(data));
  ctr=ctr+1;
}
Da würd mich jetzt interessieren, ob ich berechnen kann, wie lange die dauert. OK, ich könnte am Anfang und am Ende die micros() abfragen. Aber ob das so genau ist?
Wenn die Routine länger als 0,4 ms dauert, dann hab ich nämlich ein Timing-Problem.

2.)
Ich hab Anfangs von einer 32 Bit Zahl geschrieben.
Kann man eigentlich auch Zahlenformate selber definieren? Mir würde nämlich für "distance" ein signed Format mit 20Bit + Vorzeichen für den Anwendungsfall genau passen.
Logged

Arduino 1.0.5 | Arduino UNO & MEGA | Arduino 1.54r2 mit DUE

Germany
Online Online
Faraday Member
**
Karma: 57
Posts: 3029
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Kann man eigentlich auch Zahlenformate selber definieren? Mir würde nämlich für "distance" ein signed Format mit 20Bit + Vorzeichen für den Anwendungsfall genau passen.
Im Prinzip schon:
Code:
struct Distance
{
 signed value:20;
 unsigned sign:1;
};

Distance d;
...
  d.value = 0xFFFFF;
  d.sign = 1;

aber ob die Variable d tatsächlich nur 3 byte belegt statt 4, bliebe zu prüfen. Auch Wertebereichsüberschreitungen werden weder beim Compilieren noch zur Laufzeit geprüft, fürchte ich.

Quote
Gibts irgendwo eine Aufstellung, wie viel Laufzeit ein Befehl hat?
Ja, für die Maschinenbefehle ( Assembler )
" Die meisten Befehle brauchen nur 1 Zyklus ", meine ich irgendwo gelesen zu haben, aber genauer weiss ich nicht. Tip: Datenblatt .
Für unsereins ist eher interessant, wie gut der Compiler arbeitet, bzw. wie man ihn unterstützen kann.

Dein Takt() braucht sicher keine 400 µs, könnte aber durch ein optimiertes bitWrite evtl. noch verbessert werden:

#define data 3

Code:
long bit = 0x00001;
long distance = 0;
...
void Takt() {
 newdata = HIGH;
 if ( !digitalRead(data)) distance |= bit;
 bit <<= 1;
}

Ob es da noch wirklich was bringt, statt mit zwei long mit einem von 3 Bytes und einer 1 byte Bitmaske zu arbeiten, weiss ich nicht.
digitalRead mit einer konstanten Pin-Nummer sollte schon ziemlich per Macros optimiert sein.

Quote
...ich könnte am Anfang und am Ende die micros() abfragen. Aber ob das so genau ist?
Sagt dir ziemlich genau, wie lang 2 mal micros() abfragen dauert, mit deinem bitWrite() wird es etwas länger smiley-wink

Deine ganze Frage klingt übrigens ziemlich nach shiftIn().
(Wodurch ändert sich denn der Wert des data - Pins ? Da ist doch sicher irgendwo ein Clock Puls...)
Probierst du gerade, das Rad neu zu erfinden? (Das soll keine Kritik sein, nur sollte man vor dem Neu-Erfinden wissen, was es schon gibt)
Logged

Offline Offline
Sr. Member
****
Karma: 2
Posts: 381
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi michael_x

das ist viel Input. Das muss ich erst mal sacken lassen.
Quote
(Wodurch ändert sich denn der Wert des data - Pins ? Da ist doch sicher irgendwo ein Clock Puls...)
Probierst du gerade, das Rad neu zu erfinden? (Das soll keine Kritik sein, nur sollte man vor dem Neu-Erfinden wissen, was es schon gibt)
Gott beware. Ich will sicher nichts neu erfinden, was es bereits gibt. Bin aus angeborener Faulheit immer froh, wenn ich was vorhandenes verwenden kann.

Du hast richtig erkannt: Ich habe zwei Eingänge. eine Clk- und einen Data-Impuls. Um genauer zu sein: Ich lese will eine Absoluwertgeber einlesen. Bei jeder positiven Flanke des CLK wird der Data-Eingang gelesen.
Das "Telegramm" ist ca. 10ms lang. In dem "Telegramm" bekomme ich ein 24 Bit Datenpaket.

shiftin() kann (so viel ich aus der Reference gelesen habe)  nur ein Byte??

Quote
Ja, für die Maschinenbefehle ( Assembler )
" Die meisten Befehle brauchen nur 1 Zyklus ", meine ich irgendwo gelesen zu haben, aber genauer weiss ich nicht. Tip: Datenblatt .
Für unsereins ist eher interessant, wie gut der Compiler arbeitet, bzw. wie man ihn unterstützen kann.
Hab einige "Tests" gemacht:
Das Unterprogramm in der loop 50 mal aufgerufen. Am Ende der loop die Zeit mit millis() gemessen. Dann das Unterprogramm 100x aufgerufen. Den Zeitunterschied berechnet.... durch 50 geteilt....
--> Meine Unterprogramm "Takt ()" dauert ~21µs.(Beim Schreiben auf Bit"31" des "Long". Seltsam: Beim Schreiben auf Bit "0" ist es  ~1,6µs schneller.

Das mit der Frage nach den 3Byte Datenformat war nur deshalb, weil dann das Vorzeichenbit automatisch an die richtigen Stelle gekommen wäre.

Quote
long bit = 0x00001;
long distance = 0;
...
void Takt() {
 newdata = HIGH;
if ( !digitalRead(data)) distance |= bit;
 bit <<= 1;

}

Sorry: das hab ich leider überhaupt nicht verstanden. Dazu fehlts mir wohl an C-Grundlagen.
Logged

Arduino 1.0.5 | Arduino UNO & MEGA | Arduino 1.54r2 mit DUE

Germany
Online Online
Faraday Member
**
Karma: 57
Posts: 3029
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
das hab ich leider überhaupt nicht verstanden. Dazu fehlts mir wohl an C-Grundlagen.

Sorry, ja vermutlich:

distance |= bit;  ist eine Kurzschreibweise für distance = distance | bit; // setze ein bit in distance
bit <<= 1;   ist eine Kurzschreibweise für   bit  = bit << 1; // schiebe bit eins nach links

Das geht mit anderen Operatoren  ( + , * , / usw.) auch, und wird oft direkt von der Hardware unterstützt.

(
Allerdings wird, wenn's die Hardware unterstützt, das wohl auch vom Compiler optimiert, so dass z.B.
bit = bit << 1; weniger Befehle sind als bit2 = bit1 << 1;
)

Quote
shiftin() kann (so viel ich aus der Reference gelesen habe)  nur ein Byte??
Ja, das stimmt. Mit 3 shiftIn Befehlen kriegst du 24 bit.

Wenn du ein C - Experte werden willst, schau dir "union" an. Damit kannst du eine Speicherstelle ( Variable ) wahlweise als 3 bytes oder als 24 bit interpretieren. ( Oder 20 bit in deinem Fall )

Logged

Offline Offline
Sr. Member
****
Karma: 2
Posts: 381
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
distance |= bit;  ist eine Kurzschreibweise für distance = distance | bit; // setze ein bit in distance
bit <<= 1;   ist eine Kurzschreibweise für   bit  = bit << 1; // schiebe bit eins nach links

Das geht mit anderen Operatoren  ( + , * , / usw.) auch, und wird oft direkt von der Hardware unterstützt.

Oh ja, mit "+" kenn ich das auch. aber "|=" war mir etwas zu kryptisch.

Quote
Ja, das stimmt. Mit 3 shiftIn Befehlen kriegst du 24 bit.
Hmm.... aber dann muss ich mich irgendwie drum kümmern, daß ich dann ins richtige der 3 Bytes schreibe, und daß ich die 3 Bytes am Ende dann zusammenfüge.
Oder denk ich da falsch?

Ich poste jetzt mal meinen Code. Ist vllt. etwas hölzern, aber geht.
Allerdings erkennt er nicht jedes Datenpaket :-( Manchmal verwirft er bis zu 4 Pakete, bis er eins dekodiert.  Darum dachte ich zunächst, daß die Int-Routine "Takt()" zeitlich an der Grenze ist.
Das mit den millis() hab ich eingebaut , damit ich erkenne wann ein neues Paket startet. Wenn seit 20ms kein Takt mehr da ist, weiss ich daß ich in den 120ms Pause bin, und bei der nächsten Flanke ein neues Paket beginnt.
Code:
// Takteingang: PIN2
// Dateneingang: PIN3
// Länge Paket: 24Bit in ca. 10ms
// Anschliessend ca. 120ms Pause
// Das erste Bit des Datenpakets wird verworfen

const int data = 3;
boolean newdata = LOW;
int ctr;
unsigned long time;
long distance = 0;


void setup(){
  Serial.begin (57600);
  pinMode(data, INPUT);
  attachInterrupt(0, Takt, RISING);   //(UNO: Int0 = PIN 2)
}

void loop(){
  if (newdata) {
    time = millis();
  }
  newdata = LOW;
  
  if ((millis() - time) >= 20) {
    if (ctr >= 24) {
     distance = distance >>1;
     Serial.println (distance);
    }
    ctr = 0;
  }
}

void Takt() {
  newdata = HIGH;
  bitWrite(distance, ctr, !digitalRead(data));
  ctr=ctr+1;
}




« Last Edit: January 01, 2013, 11:07:40 am by hk007 » Logged

Arduino 1.0.5 | Arduino UNO & MEGA | Arduino 1.54r2 mit DUE

Germany
Online Online
Faraday Member
**
Karma: 57
Posts: 3029
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Manchmal verwirft er bis zu 4 Pakete
"verwerfen" bedeutet, dass länger als 20 ms nichts mehr gekommen ist, aber ctr nicht bis 24 gezählt hatte, richtig?

Was passiert, wenn du statt
if (ctr >= 24) { ...   zum Test  if (ctr > 0) { ... nimmst und mitschreibst, wieviele Pulse gekommen sind ?


Wenn der Clock - Puls nur zu lesen ist, ist shiftIn() übrigens nicht passend.
Logged

Offline Offline
Sr. Member
****
Karma: 2
Posts: 381
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Manchmal verwirft er bis zu 4 Pakete
"verwerfen" bedeutet, dass länger als 20 ms nichts mehr gekommen ist, aber ctr nicht bis 24 gezählt hatte, richtig?
Ja


Quote
Was passiert, wenn du statt
if (ctr >= 24) { ...   zum Test  if (ctr > 0) { ... nimmst und mitschreibst, wieviele Pulse gekommen sind ?
Code:
    if (ctr > 0) {
     Serial.print (millis());
     Serial.print (": ");
     Serial.println (ctr);
    }
--> Ergebnis:
19892: 24
20025: 23
20159: 24
20300: 24
20433: 24
20537: 1
20566: 22
20698: 24
20831: 23
20963: 24
21067: 1
21098: 22
21230: 24
Ich bin gerade am überlegen, ob ich vllt. ein Problem mit den Eingangssignal habe?
Zur Info: Die Signale kommen als 1,5V-Signal aus dem Geber. Damit ich es im Arduino einlesen kann, hab ich Sie durch eine Transistorbeschaltung verstärkt:
Gebersignal mit 5K an Basis des BC547C, 5V über 10K an Collector und Arduino-Eingang, Emitter an GND. (Schema wie im Bild, allerdings mit anderen Werten)
Da die Schaltung invertiert frag ich auch nach Rising und !digitalRead.
Im Anhang seht ihr mal ein Oszi-Bild des Signals.

Quote
Wenn der Clock - Puls nur zu lesen ist, ist shiftIn() übrigens nicht passend.
Soll heissen, dass dafür der CLK ein Ausgang sein soll?
Ne, bei mir sind beides Eingänge. Also der Geber sendet auf beiden Leitungen.


* NewFile0.bmp (146.3 KB, 320x234 - viewed 25 times.)

* Transistor.JPG (14.04 KB, 429x223 - viewed 24 times.)
Logged

Arduino 1.0.5 | Arduino UNO & MEGA | Arduino 1.54r2 mit DUE

Offline Offline
Sr. Member
****
Karma: 2
Posts: 381
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ich glaub jetzt eher, daß ich meinen Code noch optimieren muss.
Mit dieser Änderung:
Code:
 if ((millis() - time) >= 20 [color=red]&& !newdata[/color]) {
    if (ctr > 0) {
     Serial.print (millis());
     Serial.print (": ");
     Serial.println (ctr);

sieht es schon wesentlich besser aus:
Code:
4483: 24
4615: 24
4748: 24
4881: 24
5014: 24
5147: 23
5251: 1
5280: 22
5412: 24
5545: 24
5679: 23
5812: 24

Aber es sind immer noch "Fehltelegramme".
Sieht aus, als ob es immer noch Timingprobleme beim Zusammenspiel zwischen Interruptroutine und Hauptprogramm gibt.
Den Int während der Auswertung im Hauptprogramm zu deaktivieren trau ich mich nicht, da ich dann fürchte einen Interrupt zu verpassen.
« Last Edit: January 01, 2013, 05:04:23 pm by hk007 » Logged

Arduino 1.0.5 | Arduino UNO & MEGA | Arduino 1.54r2 mit DUE

Germany
Online Online
Faraday Member
**
Karma: 57
Posts: 3029
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Wenn das
Code:
  if (newdata) {
    time = millis();
  }
noch drin ist, sollte deine Änderung nicht viel ausmachen, aber auch nicht stören smiley-wink

Das Oszi-Bild krieg ich nicht mit deiner Beschreibung zusammen: 24 Bit in 10 ms, dann 120 ms Pause
Was ist Clock, was Daten ? Wie sieht es bei einer Auflösung von z.B. 200 ms aus ?

4881: 24
5014: 24
5147: 23
5251: 1
5280: 22  --> diese Pulse müssen zwischen 5251 und 5260 gekommen sein, danach Ruhe bis 5280.    ???

Nimm mal time = millis(); in die Interrupt-Routine
und probier sowas ähnliches aus:

Code:
void loop ()
{
    noInterrupts();
    unsigned long tmp = time;
    byte count = ctr;
    if ( millis() - tmp > 60 && count > 0)
    {
        ctr = 0;
        distance = 0;
      interrupts();
        Serial.print (tmp);
        Serial.print(": ");
        Serial.println(count);
    }
    else
      interrupts();
}
( newdata brauchts dann gar nicht )
Ausserdem sollten distance und ctr als volatile deklariert werden. ( Damit der compiler berücksichtigt, dass Takt() und loop() gleichzeitig laufen und er keine falschen Optimierungen vornimmt.)
Logged

Pages: [1] 2   Go Up
Jump to: