Forum: Mikrocontroller und Digitale Elektronik Übernahme eines durch Impulse übertragenen Binärwerts auf PIC16F1827


von Kai M. (kamelle)


Lesenswert?

Hallo zusammen,

nach erfolgloser Suche, im allgemeinem Netz und im speziellen hier, habe 
ich mich zu einer Anmeldung entschieden. Bisher konnte ich meine 
Probleme immer selbst irgendwie lösen, wenn ich ersteinmal einen kleinen 
Anstoß gefunden hatte. Diesmal komme ich einfach nicht mehr weiter und 
erhoffe mir den kleinen Anstoß auf diesem Wege. Aber genug geschwaffelt, 
nun zu meinem Problem:


Ich arbeite momentan an einem Board mit einem PIC16F1827, dass einen 
Spannungswert per ADC aufnimmt, den Wert zur Temperatur umwandelt und 
entsprechend per PWM einen Heizer regelt. Das funktioniert tadellos.

Dieses Board ist auf ein Mainboard eingesteckt, dass pro Sekunde einmal 
einen binären "Sollwert" ausgibt. Ausgegeben wird mit einem Clk, Data 
und Rdy/Ack.
Pro Sekunde einmal 8 Clk-Pulse, zu jedem Clk-Puls soll der Wert auf Data 
übernohmen werden. Nach den 8 Clk-Pulsen kommt einmalig Rdy als eine Art 
EOF.
Die 3 Leitungen liegen auf IOC-Pins und lösen dementsprechend Interrupts 
aus. Ich möchte also, das in meiner ISR die 8 Clks erkannt werden, die 8 
Data-Bits in einem Wert gespeichert werden und dieser dann an meine 
Regelung übergeben wird.

Interrupts werden sicher ausgelöst. Neue Werte in der ISR werden auch in 
der Main erkannt und übernommen.
Nur scheint es mir nicht möglich die Signale des Mainboards übernehmen 
zu können.

Ich hänge einfach mal den Code meiner ISR an, denn dort spielt sich ja 
alles wesentliche ab.
1
void interrupt ISR(void)
2
{
3
int soll_bak, soll_old, soll_new;
4
5
soll_new = soll;
6
soll_bak = soll;
7
8
9
10
 
11
12
  if (IOCBF2 == 1 && IOCBF1 == 1){        // Interrupt on Clk2(RB1) and Data2(RB2) == 1
13
    soll_new |= 1 << 0;                // Write 1 to Bit 0 of soll_new
14
    soll_new << 1;                // Shift 1 Bit
15
    IOCBF1 = 0;                  // Clear Interruptflag on Clk2
16
    IOCBF2 = 0;                  // Clear Interruptflag on Data2
17
    }
18
  
19
  if (IOCBF1 == 1 && IOCBF2 == 0){        // Interrupt on Clk2 and Data2 == 0
20
    soll_new &= ~(1 << 0);                // Write 0 to Bit 0
21
    soll_new << 1;                // Shift 1 Bit
22
    IOCBF1 = 0;                  // Clear Interruptflag on Clk2
23
    IOCBF2 = 0;                  // Clear Interruptflag on Data2
24
    }
25
26
27
  if (IOCBF4 == 1 && d == 8){            // Interrupt on Clk2 and Rdy2
28
    soll_bak = soll_new;            // Replace old soll-value
29
    IOCBF = 0x00;                // Clear Interruptflag on Rdy
30
    }
31
32
  // if (IOCBF1 == 1 && d != 8){            // Interrupt on Clk2 and Rdy2
33
    // soll_bak = soll_old;            // Replace old soll-value
34
    // IOCBF = 0x00;                // Clear Interruptflag on Rdy
35
    // }
36
37
38
IOCBF = 0x00;
39
IOCIF = 0;
40
41
soll = soll_bak;
42
43
44
}
Ich bin kein wirklich guter Programmierer und ich bitte dies zu 
berücksichtigen. "Quick'n'Dirty" geht, aber hiermit... eher Neuland für 
mich.

Ich bin für jeden Tip, jeden Hinweis dankbar.

Für weitere Fragen stehe ich natürlich zur Verfügung.

Besten Dank schon mal...

von Karl H. (kbuchegg)


Lesenswert?

Kai M. schrieb:

> Dieses Board ist auf ein Mainboard eingesteckt, dass pro Sekunde einmal
> einen binären "Sollwert" ausgibt. Ausgegeben wird mit einem Clk, Data
> und Rdy/Ack.
> Pro Sekunde einmal 8 Clk-Pulse, zu jedem Clk-Puls soll der Wert auf Data
> übernohmen werden. Nach den 8 Clk-Pulsen kommt einmalig Rdy als eine Art
> EOF.
> Die 3 Leitungen liegen auf IOC-Pins und lösen dementsprechend Interrupts
> aus.

Auf der Datenleitung brauchst du keinen Interrupt. Wozu soll der gut 
sein? Von dieser Leitung interessiert dich nur der Pegel und der 
interessiert dich auch erst dann, wenn der Clock Puls kommt. Zwischen 2 
Clockpulsen kann die Datenleitung machen was sie will, selbst wenn sie 
mittels Rechteckschwingungen LaPaloma pfeift. Den Empfänger interessiert 
nur eines: Welcher Pegel liegt an, wenn der Clockpuls kommt. Alles 
andere ist uninteressant.

>
> Ich bin für jeden Tip, jeden Hinweis dankbar.

So schlimm ist der Code nicht. Die Grundidee ist schon erkennbar.
Was ich tun würde:
  Die Data Leitung vom Interrupt weg. Den braucht kein Mensch.

Und dann (Pseudoecode)
1
Interrupt()
2
{
3
  static unsigned char Zwischenbyte;
4
5
  if( Interrupt von der Clock Leitung )
6
  {
7
    Zwischenbyte <<= 1;
8
9
    if( Pegel an der Datenleitung == High )
10
      ZwischenByte |= 0x01;
11
  }
12
13
  else if( Interrupt an der Ready Leitung )
14
    Ergebnisvariable = Zwischenbyte;
15
16
  Aufräumen, also die Interrupt Flags löschen, falls das überhaupt
17
  notwendig sein sollte.
18
}

sieht fast so aus, wie dein Code :-)
Für die Details wie Registernamen bist du zuständig, ich sprech kein 
PICisch


Edit: Du hast du einen Denkfehler in deinem Code. Du musst zuerst um 1 
Stelle nach links schieben (um Platz für das nächste Bit zu schaffen) 
und erst dann das neue Bit an die unterste Stelle reinodern (wobei du 
eine 0 überhaupt nicht einsetzen musst, die steht durchs schieben schon 
dort). Also: Reihenfolge! Du ziehst ZUERST den Papierstreifen ein wenig 
aus dem Halter ehe du in dem so erzeugten freien Platz am Streifen 
DANACH was reinschreiben kannst.


Edit2: Und komm bitte von der Vorstellung weg, dass 'Eingangsleitung' 
automatisch Interrupt heißt. Genau das hat dich dazu gebracht, alle 3 
Leitungen einen Interrupt auslösen zu lassen. Input -> Interrupt bringt 
in den meisten Fällen mehr Probleme als es löst. In deinem Fall ist das 
mit der Clock und der Ready Leitung ok, weil dann die Übertragung 
transparent im Hintergrund läuft, aber Taster bzw. sonstige 
Eingangsleitungen benötigen in den seltensten Fällen einen Interrupt.

von Peter D. (peda)


Lesenswert?

Wenn der PIC ein Slave-SPI hat, dann nimm es doch einfach.


Peter

von Michael S. (rbs_phoenix)


Lesenswert?

Also was ich so auf die schnelle sehe:
Du schiebst JEDES mal das soll_new um eins nach links. Wenn du jetzt das 
letzte Bit bekommst, dann setzt du dieses und schiebst danach nochmal 
weiter, sprich du hast IMMER das LSB auf 0.

Ebenso finde ich ein Interrupt auf der Datenleitung überflüssig. Deine 
Clockleitung gibt an, wenn die Datenleitung ausgewertet werden soll.

Mein Vorschlag wäre:
1
int counter = 0;
2
3
void interrupt ISR(void)
4
{
5
  if (IOCBF1 == 1){        // Interrupt on Clk2(RB1)
6
    if(counter==0) soll_new = 0;
7
    if(PortB.Bit2) soll_new |= (1 << counter);
8
    counter++;
9
    if(counter == 8) counter = 0;
10
    IOCBF1 = 0;                  // Clear Interruptflag on Clk2
11
  }
12
}

Wenn das erste bit kommt, dann löst der Takt auf Clock den Interrupt 
aus. In dem moment ist counter noch auf 0 und setzt zuerst die Variable 
soll_new auf 0. Danach wird geguckt, ob die Datenleitung auf 1 ist. Wenn 
nicht, braucht er ja nichts machen, da jedes Bit auf 0 ist. Wenn ja, 
dann soll er das bit an der Position "counter" setzen. Danach wird 
counter um eins erhöht. Wenn der counter jedoch 8 erreicht, muss er 
wieder auf 0 gesetzt werden, da im nächsten durchgang dann die eins zu 
viel verschoben wird.

So wie ich es geschrieben hab, kommt das LSB zuerst, bei dir das MSB. 
Wenn du das wieder so haben willst, musst du lediglich "(1 << counter)" 
in "(1 << (7-counter))" ändern.

von Karl H. (kbuchegg)


Lesenswert?

Michael Skropski schrieb:

> So wie ich es geschrieben hab, kommt das LSB zuerst, bei dir das MSB.
> Wenn du das wieder so haben willst, musst du lediglich "(1 << counter)"
> in "(1 << (7-counter))" ändern.


Machs nicht so kompliziert mit einer 'counter' Variablen. Solche 
variablen Shifts sind oft nicht besonders schlau und können immer 
dadurch ersetzt werden, dass man das neue Bit an einer fixen Position 
einodert und dafür im Gegenzug das entstehende Byte darunter 
links/rechts verschiebt.

Als Analogie:
Du kannst natürlich mit einem Bleistift und aufwändiger Mechanik einen 
Papierstreifen von links nach rechts beschreiben, in dem deine Mechanik 
den Stift für jeden Buchstaben um 1 weiterrückt. Einfacher ist es aber, 
du lässt den Stift an Ort und Stelle und ziehst im Gegenzug das Papier 
unter dem Stift durch. Das Ergebnis ist dasselbe aber der Aufwand ist 
geringer. Ein Tintenpisser macht auch nichts anderes. Zwar wird der 
Druckkopf links/rechts gefahren aber für die nächste Zeile wird nicht 
die Druckmechanik versetzt, sondern das Papier weitergeschoben.

von Christian K. (Firma: Atelier Klippel) (mamalala)


Lesenswert?

Hallo,

also, wie die anderen schon geschrieben haben: Du musst zuerst schieben 
und dann das Bit setzen. Allerdings gibt es auch noch einige andere 
Sachen zu berücksichtigen.

Ich habe jetzt das Datenblatt des betreffenden PIC's nicht angeschaut, 
aber: Welche Art von Interrupt wird da ausgelöst? Definierte 
Pegeländerung, also von low auf high, oder von high auf low 
(edge-triggered)? Oder ein allgemeiner "interrupt on change"? Letzterer 
würde dir nämlich die Routine 16 mal aufrufen.

Du musst also den Interrupt nur bei einer bestimmten Änderung auslösen, 
oder alternativ auch schauen welchen Pegel die Clock-Leitung hat wenn Du 
nur einen interrupt-on-change zur Verfügung hast. Falls IRQ bei 
bestimmter Änderung: Das muss dann natürlich auch zu dem passen was die 
Quelle erzeugt. Die Datenleitung ist je nach Quelle entweder beim 
Übergang low->high zu lesen, oder beim Übergang high->low.

Gleiches gilt natürlich auch für die RDY Leitung.

Je nach Umgebung in der die Schaltung zum Einsatz kommt kann es sich 
evtl. auch lohnen noch einen Bit-Zähler zu haben der dann sciherstellt 
das auch wirklich 8 Bits empfangen wurden wenn die RDY Leitung auslöst. 
Also bei jedem empfangenen Bit den Zähler um 1 erhöhen. Wenn RDY kommt, 
dann prüfen ob es 8 Clocks waren. Wenn nein, ausgelesenen Wert nicht 
übernehmen. Am Ende der Auswertung des RDY wird dann der Zähler immer 
auf 0 zurückgesetzt. Somit hat man eine krude Absicherung für den Fall 
das durch Störungen mal die Clock oder RDY Leitung "wackelt".

Grüße,

Chris

von Michael S. (rbs_phoenix)


Lesenswert?

Ich muss zugeben, dass deine Möglichkeit eleganter ist ;) Ich hab 
allerdings sowas auch noch nie wirklich selber geschrieben, sondern eher 
I²C oder SPI benutzt. Doch da ist die Frage von Peter schon berechtigt. 
Warum nicht das SPI-Modul nehmen? Da is doch schon alles fertig.

von Kai M. (kamelle)


Lesenswert?

Erstmal ganz herzlichen Dank für all die Antworten in dieser kurzen 
Zeit!

Ich war mittlerweile nicht untätig und die Geschichte mit dem Schieben 
nach dem Auslesen ist mir bei Zettel, Stift und Malstunde auch 
aufgegangen. Allerdings bin ich nicht auf die Idee gekommen, dass ich 
erst schieben und dann setzen kann. Ich hätte mein neues Int beim 
Rdy-Flag einmal nach rechts geschoben... schon mal besten Dank für den 
Hinweis :D

Das Data kein Interrupt sein muss habe ich auch schon übernommen und ist 
natürlich vollkommen richtig.

Bei den Interrupt-On-Change habe ich die Möglichkeit die Reaktion auf 
spezifische Flanken zu beschränken. Stehen auf "positive Edge".

Bus-Module kann ich leider nicht nehmen, da ich mich nach dem Mainboard 
richten muss und dort keinen eigenen Zugriff habe.

Ich hänge meinen momentane, deutlich schlankere ISR nochmal an. 
Vielleicht hat der ein oder andere ja noch mehr Hinweise oder 
Anmerkungen. Bin durchaus lernwillig!
1
int volatile soll = 100;
2
int volatile soll_help = 0;
3
4
5
main{...}
6
7
void interrupt ISR(void)
8
{
9
int soll_new;
10
11
soll_new = soll_help;
12
13
if (IOCBF1 == 1){            // Interrupt on Clk2(RB1) == 1
14
  soll_new << 1;            // Shift 1 Bit to the LEFT
15
  soll_new |= RB2 << 0;        // Write Data(RB2) to Bit 0 of soll_new
16
  IOCBF1 = 0;              // Clear Interruptflag on Clk2
17
  soll_help = soll_new;        // Pre-save value globally
18
  }
19
  
20
if (IOCBF4 == 1){
21
  soll = soll_new;
22
    }
23
  
24
IOCIF = 0;
25
IOCBF = 0x00;
26
27
}

Leider scheint dies immernoch nicht zu funktionieren.
Ich weiß aufgrund Debug-LED und Wertvorgabe bei der Initialisierung von 
soll_help, dass dieser Wert in die ISR und am Ende auch von soll_new und 
soll übernommen wird. Der gesendete Wert wird aber leider nicht 
übernommen. Allerdings kann das ja auch ein anderes Problem sein und 
nicht zwingend mit dem Code zusammenhängen.

Deswegen wäre es grandios, wenn ihr mir vllt noch ein wenig die Hand 
haltet und wir zusammen die Fehlerquelle "Code" ausschließen können ;)

von Karl H. (kbuchegg)


Lesenswert?

Kai M. schrieb:

> void interrupt ISR(void)
> {
> int soll_new;

Wozu?
Alles was du mit soll_new machst, kannst du auch gleich mit soll_help 
machen. Du brauchst diese Variable nicht. Die ist nur eine potentielle 
Fehlerquelle.

>
> soll_new = soll_help;
>
> if (IOCBF1 == 1){            // Interrupt on Clk2(RB1) == 1
>   soll_new << 1;            // Shift 1 Bit to the LEFT
>   soll_new |= RB2 << 0;        // Write Data(RB2) to Bit 0 of soll_new

No.
Je nachdem, was hinter RB2 steckt, passiert da irgendwas ganz anderes.

Nicht künsteln!
Mach den if, und du hast in der Beziehung deine Ruhe.

: Wiederhergestellt durch User
von Karl H. (kbuchegg)


Lesenswert?

Ich würde mich auch niemals bei der Auswertung digitaler Signal darauf 
versteifen, dass etwas 1 sein muss!

In C gilt glücklicherweise die Regelung
  0           ist der Wert für logisch falsch
  ungleich 0  ist der Wert für logisch wahr

'ungleich 0'
ein Pin, der eine 1 abliefert ist ungleich 0. Aber je nachdem, wie der 
Pin ausmaskiert und zurechtgeschoben wurde, muss da nicht 
notwendigerweise 1 rauskommen.

 d.h

  if( IOCBF1 == 0)
oder
  if( RB2 == 0)

testet sauber ab, ob der Pin (das Flag) tatsächlich 0 ist. Aber

  if( IOCBF1 == 1 )
bzw
  if( RB2 == 1 )
kpriziert sich darauf, dass dein System das alles sauber 
zurechtgeschoben hat, während

  if( IOCBF1 )
bzw
  if( RB2 )
lediglich fordert, dass die jeweiligen Signale ungleich 0 sein müssen.
D.h. hier reicht es schon, dass das jeweilige Flag richtig ausmaskiert 
wurde. Zurechtgeschoben muss da nichts werden.


Edit:
Bist du sicher, dass du mit zb RB2 an das tatsächliche Portbit 
rankommst? Ich kenne den Compiler nicht. Kann schon sein, dass das 
korrekt ist. Kann aber auch sein, dass RB2 bzw IOCBF1 lediglich eine 
Maske ist, die du auf eines der µC-Register anwenden musst um damit das 
interessierende Bit zu extrahieren.

von Karl H. (kbuchegg)


Lesenswert?

>   soll_new << 1;            // Shift 1 Bit to the LEFT

No.
Das hier shiftet zwar soll_new korrekt um 1 Stelle nach links. Aber: Du 
machst nichts mit dem Ergebnis. Das ist so wie wenn du rechnen würdest

   a + 2;

Da steht zwar eine Addition, aber das Ergebnis davon wird nicht 
verwendet.

Also. Entweder

   soll_new = soll_new << 1;

oder eben die C-typische Kurzschreibweise

   soll_new <<= 1;

(und anstelle von soll_new dann natürlich soll_help, wenn du die 
Variable errst mal los bist)

von Karl H. (kbuchegg)


Lesenswert?

Und noch was:

Wenn du auf Byte-Ebene unterwegs bist, dann ist der Datentyp der Wahl 
ein unsigned char und kein int. Mit int (noch dazu einem signed int), 
zwingst du deinem Compiler auf einer 8-Bit Maschine eine Menge 
Mehrarbeit auf, für nichts und wieder nichts. Und 'signed' ist bei 
Bitoperationen meistens sowieso ganz schlecht.

von Kai M. (kamelle)


Lesenswert?

Vielen Dank für den Haufen an Hinweisen.
Vermutlich sind das für die meisten hier einfache, grundsätzliche Dinge, 
aber irgendjemand muss einem das ja auch mal sagen ;) Weiß ich wirklich 
zu schätzen.

Ich habe auch alle Hinweise bereits in meinem Code "verwurstet". Schon 
erstaunlich wie sehr das alles dazu beigetragen hat, dass zumindest 
meine ISR sehr viel schlanker geworden ist.

Insbesondere den Hinweis bezüglich der if-Bedingungen fand ich extrem 
hilf- und lehreich. Werde ich mir ganz sicher für die Zukunft merken.

RB2 ist in der Tat eine Maske, die über die passende Header-Datei des µC 
eingebunden wird. Allerdings scheint das beim Hi-Tech-Compiler so 
Standard zu sein. Jedenfalls habe ich das in den wenigen Application 
Notes, die C-Code hatten, ebenfalls so gelesen. Denke, dass sollte so 
okay sein.

Allerdings muss ich gestehen, dass mir die Sache mit dem char vs int 
nicht ganz klar ist.
Kannst du mir da vielleicht eine gute Quelle zum nachlesen empfehlen?
Ich bekomme ja von meinem ADC ein int geliefert, oder?
Müßte ich das dann nicht noch umwandeln? Kann ich den ADC-Wert auch als 
char ausgeben lassen? Ich habe ja eine Gleichung, die mir aus den 
ADC-Wert eine Temperatur berechnet, geht das mit einem char?
Es scheint mir, dass ich vllt der Einfachheit halber lieber alles als 
int behandel, diese aber zumindest dann als unsigned.
Speichereffiezienz spielt bei meiner Anwendung keine Rolle.

Nochmals vielen Dank für die Geduld ;)

von Karl H. (kbuchegg)


Lesenswert?

Kai M. schrieb:


> Allerdings muss ich gestehen, dass mir die Sache mit dem char vs int
> nicht ganz klar ist.

ein int sind auf deinem System 16 Bit.
Du hast es aber sowieso nur mit 8 Bit (= 1 Byte) Einheiten zu tun. Also 
wozu den µC durch 16 Bit Arithmetik durchjagen, wenn es gar nicht 
notwendig ist? Du schleppst ja auch nicht ein 500 Seiten Notizbuch mit 
dir rum, wenn du dir nur kurz 3 Zahlen notieren musst.

> Ich bekomme ja von meinem ADC ein int geliefert, oder?

Das ist eine andere Geschichte.
Aber wenn dir dein Counterpart mit diesem Mechanismus einen 8 Bit Wert 
schickt, dann hast du erst mal nur 1 Byte.

> char ausgeben lassen? Ich habe ja eine Gleichung, die mir aus den
> ADC-Wert eine Temperatur berechnet, geht das mit einem char?

Das passt alles. (glaub ich mal). Da ist int ok.

Es geht hier nur um den Datentransfer von deinem Mainboard zu dir. Dein 
Mainboard schickt dir ein Byte. Also wozu den µC dazu zwingen ständig 
mit 2 Bytes (nämlich einem int) zu hantieren, wenn du bei dieser Aktion 
sowieso immer nur 1 Byte (8 Bit) in Arbeit hast?

> Es scheint mir, dass ich vllt der Einfachheit halber lieber alles als
> int behandel,

Das ist zuuuuu einfach gedacht.
Die Sache ist doch nicht schwer. Bei dieser Datenübertragung - womit 
hast du es da zu tun? Mit 8 Bit. 8 Bit sind ein Byte. Also reicht es 
völlig aus einen Datentyp zu benutzen, mit dem du genau das, nämlich 1 
Byte, abbilden kannst. Mehr braucht es nicht. Alles andere ist Overkill. 
Du mietest keinen 40-Tonner LKW, wenn du einen Kasten Bier nach Hause 
fahren willst. Wenn es nach deinem Argument ginge, würden alle Leute mit 
einem 40-Tonner rumfahren. Der Einfachheit halber.

von Kai M. (kamelle)


Lesenswert?

Vielleicht habe ich mich unglücklich oder gar falsch ausgedrückt.

Mir ist die Idee hinter der Benutzung von char im Gegensatz zu int schon 
klar. Den 1'en und 0'en ist es natürlich erstmal egal in welchem 
Datentyp sie stehen. Von daher kann ich sicher auch char nutzen, um 
meine empfangenen Daten zu speichern
Allerdings frage ich mich, ob das wirklich einen konkreten Nutzen für 
mich hat.
Mein ADC-Wert ist ein int, meine Temperatur wird als int berechnet - 
jedenfalls im Moment, denn eigentlich müßte es sogar ein float sein 
wegen der Auflösung.
Zum Zwecke der Regelung vergleiche ich meine berechnete Temperatur mit 
meinem Sollwert und lass über diesen Vergleich meine duty time der PWM 
steuern/regeln.
Nun würde ich aus meiner ISR einen char bekommen und muss den mit einem 
int aus meiner Temperaturformel vergleichen.
Ist das nicht problematisch bzw. unmöglich. Meines Wissens ist ein 
Vergleich zwischen zwei verschiedenen Datentyp ohne weiteres doch nicht 
möglich. Also muss ich zuerst meinen char wieder in ein int verwandeln? 
Zumindest würde ich dies ersteinmal annehmen.

Aus dieser Überlegung resultiert auch meine Aussage, dass es "einfacher" 
wäre alles als int zu haben...

von Karl H. (kbuchegg)


Lesenswert?

Kai M. schrieb:

> Nun würde ich aus meiner ISR einen char bekommen und muss den mit einem
> int aus meiner Temperaturformel vergleichen.

Langsam.
In der ISR hast du diese Sequenz

if (IOCBF4 == 1){
  soll = soll_new;
    }


es spricht nichts dagegen, dass hier an dieser Stelle die implizite 
Umwandlung von 8-Bit soll_new (oder eben dem 8-Bit soll_help) zu 16-Bit 
soll (den du dann für die Vergleiche benutzt) erfolgt.
Das ist an einer Stelle und nur an dieser Stelle konzentriert. Der 
weiterverarbeitende Code arbeitet mit 16 Bit int, der Empfangscode 
arbeitet mit 8 Bit unsigned char. Und wenn der Empfangscode das Byte 
fertig hat, wird es auf 16 Bit aufgeblasen und dem Rest zur Verfügung 
gestellt.

> Vergleich zwischen zwei verschiedenen Datentyp ohne weiteres doch nicht
> möglich. Also muss ich zuerst meinen char wieder in ein int verwandeln?

Größer werden, ist niemals ein Problem.

von Kai M. (kamelle)


Lesenswert?

Ahh, ich verstehe... denke ich ;)

Implizite Umwandlung heißt in diesem Fall, dass ich meinem int einfach 
ein char zuweise und somit die Umwandlung stattfindet:

  soll = soll_new;

soll als mein int und soll_new als mein char und die Zuweisung ist die 
implizite Umwandlung, das Auflblasen auf 16bit.
Vielen Dank nochmals, war mir nicht klar, dass man das so machen kann. 
Allerdings vermutlich nur in bestimmten Fällen, in denen ich - wie hier 
- weiß, dass mein char das korrekte "Format" hat.

Ich werde das alles erst einmal verarbeiten und mich mit dem Rest 
alleine auseinandersetzen. Allerdings behalte ich mir vor mich ggf 
nochmal hier zu melden, sollte ich wieder im trüben fischen ;)

Vielen Dank nochmal an alle, die sich die Mühe gemacht haben, zu helfen.

von Karl H. (kbuchegg)


Lesenswert?

Kai M. schrieb:
> Ahh, ich verstehe... denke ich ;)
>
> Implizite Umwandlung heißt in diesem Fall, dass ich meinem int einfach
> ein char zuweise und somit die Umwandlung stattfindet:
>
>   soll = soll_new;
>
> soll als mein int und soll_new als mein char und die Zuweisung ist die
> implizite Umwandlung, das Auflblasen auf 16bit.
> Vielen Dank nochmals, war mir nicht klar, dass man das so machen kann.
> Allerdings vermutlich nur in bestimmten Fällen, in denen ich - wie hier
> - weiß, dass mein char das korrekte "Format" hat.

Einen Punkt noch, mag sein das du das nicht beabsichtigt hast.

Verwende niemals einfach nur 'char' wenn du Bitschubserei betreibst! Ob 
char ein Vorzeichen hat oder nicht entscheidet der Compiler und je 
nachdem kann das unterschiedliche Ergebnisse bringen.
Am besten machst du es dir zur Regel, dass du 3(!) unterschiedliche 
Datentypen zur Verfügung hast

   char                für alles was mit Textverarbeitung zusammen
                       hängt. Also alles was mit Strings etc. zu tun
                       hat

   signed char         wenn du einen kleinen INteger zum Rechnen 
brauchst,
                       der auch ein Vorzeichen hat

   unsigned char       wenn du einfach nur 8 Bit, vulgo 1 Byte brauchst
                       und das manipulierst. Also alles was mit
                       Bitschubserei zu tun hat.


Hier ist in der Empfangsroutine ganz eindeutig Bitschubserei angesagt. 
Der Datentyp ist daher unsigned char.

Du kannst ja mal nachsehen, ob du den Header stdint.h hast. Dann gibt es 
für unsigned char auch den Datentyp uint8_t. Ist kürzer zu schreiben und 
sagt präzise auf den Punkt, was du haben willst: einen unsigned Wert mit 
8 Bit.

von Kai M. (kamelle)


Lesenswert?

Der Tip mit dem uint8_t war echt Gold wert. Mir war die Klasse nicht 
unbekannt, zumindest darüber gelesen habe ich schon, aber irgendwie war 
nie eine wirklich vernünftige Erklärung dabei.
Wie dem auch sei... Ich habe uint8_t in meinen Code übernommen, ein 
wenig angepasst und siehe da: läuft! :D

Falls es jemanden interessieren sollte, hier nochmal mein mittlerweile 
schlanker und schöner Interrupt:
1
void interrupt ISR(void)
2
{
3
  if (IOCBF2){          // Interrupt on Clk2(RB2) != 0
4
    if(RB1){
5
      soll_help <<= 1;    // Shift 1 Bit to the LEFT
6
      soll_help |= 1 << 0;  // Write 1 to Bit 0 of soll_help
7
      }
8
    
9
    if(RB1 == 0){
10
      soll_help <<= 1;    // Shift 1 Bit to the LEFT
11
      }
12
    IOCBF = 0x00;        // Clear Interruptflags
13
    }
14
    
15
  if (IOCBF4 == 1){
16
    soll = soll_help;
17
    IOCBF = 0x00;        // Clear Interruptflags
18
    }
19
  
20
  IOCBF = 0x00;
21
  IOCIF = 0;
22
}

Funktioniert tadellos und im Vergleich zu meinem anfänglichen Versuch 
sicherlich wesentlich straffer und eleganter.

Nochmals ganz herzlichen Dank für die Hilfsbereitschaft. So etwas ist ja 
heute nicht unbedingt selbstverständlich!

von Karl H. (kbuchegg)


Lesenswert?

1
void interrupt ISR(void)
2
{
3
  if (IOCBF2){          // Interrupt on Clk2(RB2) != 0
4
    if(RB1){
5
      soll_help <<= 1;    // Shift 1 Bit to the LEFT
6
      soll_help |= 1 << 0;  // Write 1 to Bit 0 of soll_help
7
      }
8
    
9
    if(RB1 == 0){
10
      soll_help <<= 1;    // Shift 1 Bit to the LEFT
11
      }
12
    IOCBF = 0x00;        // Clear Interruptflags
13
    }
14
15
...

* etwas in der Form ...

   if( a )
     ...

   if( !a )    // oder if ( a == 0 )
     ...

  ... bei dem sich a in den abhängigen Teilen nicht verändern kann,
  ist unsinnig. Wenn du 2 Bedingungen hast, von denen die eine das
  genau Gegenteil von der anderen ist, dann benutze 'else'. Genau das
  ist sein Zweck

  if( a )
    ...
  else
    ...

* wenn du dir dann die beiden abhängigen Teile ansiehst, dann
  bemerkst du, dass sie beide gleich anfangen: mit dem Shiften
  um 1 Stelle nach links.
  Wenn aber sowohl in dem einen Fall geshiftet wird, als auch im
  anderen Fall, dann ist ja wohl der Shift nicht vom Ausgang des
  Vergleichs abhängig und kann daher vorgezogen werden.
1
    soll_help <<= 1;    // Shift 1 Bit to the LEFT
2
    if(RB1){
3
      soll_help |= 1 << 0;  // Write 1 to Bit 0 of soll_help
4
      }
5
    
6
    else{
7
      }

Tja. Jetzt hast du aber einen leeren else Teil, d.h. du brauchst den 
eigentlich gar nicht
1
  if (IOCBF2){          // Interrupt on Clk2(RB2) != 0
2
    soll_help <<= 1;    // Shift 1 Bit to the LEFT
3
    if(RB1){
4
      soll_help |= 1 << 0;  // Write 1 to Bit 0 of soll_help
5
      }
6
    IOCBF = 0x00;        // Clear Interruptflags
7
    }
8
9
    ....


Punkt 2:
Sieh dir deine Kommentare an. Die meisten davon sind sog. 0-Kommentare. 
Also sinnlose Kommentare.

      soll_help <<= 1;    // Shift 1 Bit to the LEFT

Als gelernter Österreicher sagt man da: No, na, 23 dazuzählen wird er. 
Im Source Code steht <<, also ein Shift nach links. Wozu musst du das 
noch kommentieren? Der Kommentar sagt mir nichts, was ich nicht auch im 
Source Code sehen würde. Im Source Code STEHT, dass um 1 Bit nach links 
geshiftet wird! Der Kommentar erzählt mir genau das gleiche!

Wenn du Kommentare schreibst, dann lass dich von der Maxime leiten:
Im Source Code steht das WIE.
Im Kommentar steht WARUM

Also: Warum wird hier um 1 Stelle nach links geshiftet? Was ist die 
Idee, um die es hier geht?

Genauso hier
      soll_help |= 1 << 0;  // Write 1 to Bit 0 of soll_help

Wieder: Im Kommentar steht genau das gleiche, wie im Source Code! Nicht 
gut. Denn irgendwann veränderst du den Source Code (zb wegen 
Fehlerbehebung) und vergisst den Kommentar anzupassen. Und dann hast du 
ein Programm bei dem der Kommentar etwas anderes aussagt, als im Code 
steht. Und dann ist das Rätselraten groß. Was stimmt denn jetzt? Ist der 
Kommentar richtig und handelt es sich um einen Bug, oder ist nur der 
Kommentar nicht nachgezogen worden.
Ein Kommentar soll die Idee vermitteln, um die es an dieser Stelle im 
Code geht. Die Umsetzung dieser Idee findet ausschliesslich im Code 
statt.

Wenn du hier
  if (IOCBF2){          // Interrupt on Clk2(RB2) != 0
im Kommentar das '!= 0' weglässt (kann man allerdings darüber streiten, 
notwendig an sich ist dieser Zusatz nicht), dann ist das ein guter 
Kommentar. Denn der erzählt mir etwas, was ich im Code selbst nicht 
sofort sehen kann. Dieser Kommentar erzählt mir, dass es hier darum geht 
zu erkennen, ob die Clock Leitung einen Interrupt ausgelöst hat und auch 
dass der am Anschluss RB2 stattgefunden haben muss. Was mir an dieser 
Stelle vielleicht noch fehlt ist die Angabe, ob es sich um eine Flanke 
gehandelt hat und ob die positiv oder negativ war.
Hier klärt mich der Kommentar über das WARUM auf. Das WIE, nämlich, dass 
IOCBF2 abgefragt werden muss, das steht im Code. Wenn du jetzt noch die 
Flanke ergänzt ...
  if (IOCBF2){          // positive Flanke an on Clk2(RB2)
... dann ist dieser Kommentar perfekt. Er erzählt mir alles, was ich an 
dieser Stelle wissen muss, um zu verstehen, warum da IOCBF2 steht.

von Kai M. (kamelle)


Lesenswert?

Konstruktive Kritik, wie ich sie gebrauchen kann.

Die Kommentare sollen eigentlich so sein, dass auch wenig bis gar nicht 
versierte Personen zumindest einen kleine Idee davon bekommen, was 
eigentlich passiert.
Ich bin mir im klaren, dass du weißt, was jede einzelne Zeile tut, aber 
ich muss es irgendwo erklären. Deswegen die Kommentare an ansonsten 
eindeutigen Befehlen.
Aber es stimmt sicherlich, dass die Kommentare verbessurungswürdig sind. 
Bisher habe ich die auch nur "so dahin geschrieben" damit wenigstens 
schon etwas brauchbares da steht ;)
1
if (IOCBF2){          
2
    soll_help <<= 1;  
3
    if(RB1){
4
      soll_help |= 1 << 0;  
5
      }
6
    IOCBF = 0x00;     
7
    }

Ich habe auch im Regelungsteil noch einige "Formulierungen", die ich 
noch nachbearbeiten werde, wie im obigen Teil.
Im Moment bin ich erstmal nur zufrieden, dass die Geschichte läuft. 
Schön kommt dann Montag ;)

Und zur Erklärung meiner Unzulängligkeiten in Bezug auf Programmierung: 
Ich bin eigentlich Elektrotechnik-Student und meine Welt ist eher die 
Hardware. Dies sind mehr oder minder meine ersten Versuche an 
"Software". Aber ich bin guten Mutes, dass mit etwas Übung sich auch bei 
mir ein bißchen das Auge einstellen wird, um solche Vereinfachungen, wie 
sie dir vermutlich sofort auffallen, zu erkennen.
Schaltplan und Layout für dieses Board stammen aus meiner Feder und ich 
war ehrlich gesagt ein wenig geschockt als es hieß:"Na, da kannste das 
Programm ja auch gleich machen." ;)

von Karl H. (kbuchegg)


Lesenswert?

Kai M. schrieb:
> Konstruktive Kritik, wie ich sie gebrauchen kann.
>
> Die Kommentare sollen eigentlich so sein, dass auch wenig bis gar nicht
> versierte Personen zumindest einen kleine Idee davon bekommen, was
> eigentlich passiert.

Eine Idee: ja
C-Kurs: Nein

Wer kein C kann, muss es eben lernen. Wer nicht weiß, was << an dieser 
Stelle tut, muss erst mal seine Hausaufgaben machen und das lernen oder 
seine Unterlagen rauspacken und nachsehen. Denn dem sagt 'Shift' 
genausowenig wie es << tut.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Kai M. schrieb:
>> Konstruktive Kritik, wie ich sie gebrauchen kann.
>>
>> Die Kommentare sollen eigentlich so sein, dass auch wenig bis gar nicht
>> versierte Personen zumindest einen kleine Idee davon bekommen, was
>> eigentlich passiert.
>
> Eine Idee: ja
> C-Kurs: Nein
>
> Wer kein C kann, muss es eben lernen(*). Wer nicht weiß, was << an dieser
> Stelle tut, muss erst mal seine Hausaufgaben machen und das lernen oder
> seine Unterlagen rauspacken und nachsehen. Denn dem sagt 'Shift'
> genausowenig wie es << tut.


(*) Das ist wie beim Lernen einer Fremdsprache.
Wer noch kein Englisch kann, wird sich vielleicht über den Kommentar

  Hallo, I am Ann.       // Hallo, Ich bin Anna

freuen. Aber lernt er dadurch irgendwas? Ist ihm deswegen klar, warum da 
'am' steht und nicht 'are'?
Für die ersten Versuche ist so ein Kommentar möglicherweise eine kleine 
Hilfe, aber spätestens nach der 3ten Unterrichtseinheit hilft ihm dieser 
Kommentar nicht wirklich. Er ist nur noch von der Sorte: sinnlos.
Nur frage ich mich: Warum versucht sich dann jemand, der auf derartige 
Kommentare angewiesen ist, daran 'Gone with the wind' ins Deutsche zu 
übersetzen? Wäre er nicht besser damit bedient, einen kleinen 
Schnelldurchlauf durch 'I am, You are, He/She/It is, We are, You Are, 
They are' zu machen und nach dem 5ten mal rekapitulieren, hat ers 
zumindest soweit verinnerlicht, dass er bei "We is german" erkennt, dass 
das nicht stimmen kann und das er bei obigen Satz besser den Kommentar

  Hallo, I am Ann.       // sich selbst jemandem vorstellen (Ann = Name)

bringen sollte um anzuzeigen, dass man das sagen kann, wenn man jemanden 
trifft.

von Karl H. (kbuchegg)


Lesenswert?

> war ehrlich gesagt ein wenig geschockt als es hieß:"Na, da kannste
> das Programm ja auch gleich machen." ;)

Nicht geschockt sein.
Programmieren können gehört heutzutage genauso dazu, wie Transistoren 
dimensionieren.
Kein Mensch erwartet von einem E-Technik Studenten den Aufbau eines 
relationalen Datenbanksystems, aber ein wenig Programmieren muss er 
schon können, seit die µC immer mehr Dinge ablösen, die früher diskret 
mit Spezial-IC gemacht wurden.

von Kai M. (kamelle)


Lesenswert?

> Nicht geschockt sein.
> Programmieren können gehört heutzutage genauso dazu, wie Transistoren
> dimensionieren.
> Kein Mensch erwartet von einem E-Technik Studenten den Aufbau eines
> relationalen Datenbanksystems, aber ein wenig Programmieren muss er
> schon können, seit die µC immer mehr Dinge ablösen, die früher diskret
> mit Spezial-IC gemacht wurden.

Vollkommen klar.
Den ganzen Kram mit der PWM habe ich ja auch problemlos hingekriegt.
Man muss sich halt einfach mal ransetzen, denke ich.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.