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
voidinterruptISR(void)
2
{
3
intsoll_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...
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.
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
intcounter=0;
2
3
voidinterruptISR(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.
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.
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
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.
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
intvolatilesoll=100;
2
intvolatilesoll_help=0;
3
4
5
main{...}
6
7
voidinterruptISR(void)
8
{
9
intsoll_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 ;)
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.
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.
> 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)
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.
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 ;)
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.
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...
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.
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.
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.
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
voidinterruptISR(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!
* 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.
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." ;)
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.
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.
> 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.
> 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.