Datum:
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.
void interrupt ISR(void) { int soll_bak, soll_old, soll_new; soll_new = soll; soll_bak = soll; if (IOCBF2 == 1 && IOCBF1 == 1){ // Interrupt on Clk2(RB1) and Data2(RB2) == 1 soll_new |= 1 << 0; // Write 1 to Bit 0 of soll_new soll_new << 1; // Shift 1 Bit IOCBF1 = 0; // Clear Interruptflag on Clk2 IOCBF2 = 0; // Clear Interruptflag on Data2 } if (IOCBF1 == 1 && IOCBF2 == 0){ // Interrupt on Clk2 and Data2 == 0 soll_new &= ~(1 << 0); // Write 0 to Bit 0 soll_new << 1; // Shift 1 Bit IOCBF1 = 0; // Clear Interruptflag on Clk2 IOCBF2 = 0; // Clear Interruptflag on Data2 } if (IOCBF4 == 1 && d == 8){ // Interrupt on Clk2 and Rdy2 soll_bak = soll_new; // Replace old soll-value IOCBF = 0x00; // Clear Interruptflag on Rdy } // if (IOCBF1 == 1 && d != 8){ // Interrupt on Clk2 and Rdy2 // soll_bak = soll_old; // Replace old soll-value // IOCBF = 0x00; // Clear Interruptflag on Rdy // } IOCBF = 0x00; IOCIF = 0; soll = soll_bak; } |
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...
Datum:
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)
Interrupt()
{
static unsigned char Zwischenbyte;
if( Interrupt von der Clock Leitung )
{
Zwischenbyte <<= 1;
if( Pegel an der Datenleitung == High )
ZwischenByte |= 0x01;
}
else if( Interrupt an der Ready Leitung )
Ergebnisvariable = Zwischenbyte;
Aufräumen, also die Interrupt Flags löschen, falls das überhaupt
notwendig sein sollte.
}
|
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.
Datum:
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:
int counter = 0; void interrupt ISR(void) { if (IOCBF1 == 1){ // Interrupt on Clk2(RB1) if(counter==0) soll_new = 0; if(PortB.Bit2) soll_new |= (1 << counter); counter++; if(counter == 8) counter = 0; IOCBF1 = 0; // Clear Interruptflag on Clk2 } } |
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.
Datum:
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.
Datum:
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
Datum:
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.
Datum:
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!
int volatile soll = 100; int volatile soll_help = 0; main{...} void interrupt ISR(void) { int soll_new; 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 IOCBF1 = 0; // Clear Interruptflag on Clk2 soll_help = soll_new; // Pre-save value globally } if (IOCBF4 == 1){ soll = soll_new; } IOCIF = 0; IOCBF = 0x00; } |
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 ;)
Beitrag #2725075 wurde vom Autor gelöscht.
Datum:
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.
Datum:
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.
Datum:
> 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)
Datum:
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.
Datum:
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 ;)
Datum:
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.
Datum:
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...
Datum:
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.
Datum:
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.
Datum:
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.
Datum:
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:
void interrupt ISR(void) { if (IOCBF2){ // Interrupt on Clk2(RB2) != 0 if(RB1){ soll_help <<= 1; // Shift 1 Bit to the LEFT soll_help |= 1 << 0; // Write 1 to Bit 0 of soll_help } if(RB1 == 0){ soll_help <<= 1; // Shift 1 Bit to the LEFT } IOCBF = 0x00; // Clear Interruptflags } if (IOCBF4 == 1){ soll = soll_help; IOCBF = 0x00; // Clear Interruptflags } IOCBF = 0x00; IOCIF = 0; } |
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!
Datum:
void interrupt ISR(void) { if (IOCBF2){ // Interrupt on Clk2(RB2) != 0 if(RB1){ soll_help <<= 1; // Shift 1 Bit to the LEFT soll_help |= 1 << 0; // Write 1 to Bit 0 of soll_help } if(RB1 == 0){ soll_help <<= 1; // Shift 1 Bit to the LEFT } IOCBF = 0x00; // Clear Interruptflags } ... |
* 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.
soll_help <<= 1; // Shift 1 Bit to the LEFT
if(RB1){
soll_help |= 1 << 0; // Write 1 to Bit 0 of soll_help
}
else{
}
|
Tja. Jetzt hast du aber einen leeren else Teil, d.h. du brauchst den eigentlich gar nicht
if (IOCBF2){ // Interrupt on Clk2(RB2) != 0 soll_help <<= 1; // Shift 1 Bit to the LEFT if(RB1){ soll_help |= 1 << 0; // Write 1 to Bit 0 of soll_help } IOCBF = 0x00; // Clear Interruptflags } .... |
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.
Datum:
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 ;)
if (IOCBF2){ soll_help <<= 1; if(RB1){ soll_help |= 1 << 0; } IOCBF = 0x00; } |
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." ;)
Datum:
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.
Datum:
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.
Datum:
> 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.
Datum:
> 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.