Bascom Idiotisches Bascom-Programm will nicht korrekt laufen

Uwe H.

Neues Mitglied
27. Juli 2011
264
0
0
Hinter die Grenze :-)
Sprachen
  1. BascomAVR
  2. ANSI C
  3. Assembler
Gruesst euch,

heute mal ein ganz besonderer Fall, der sich mir nicht so richtig erklaeren will. Ich hab ne Steuerzentrale fuer ne Ventilationsanlage gebaut, die nichts anderes tut, als vier Thermostate, die jeweils drei 24VAC-Signale senden koennen (Ventilation, Heizen, Kuehlen) auszuwerten und drei entsprechende Pins daraufhin zu schalten. Das ganze System hat also zwoelf Eingaenge (4x3, ueber Wechselstrom auf NPN-Optokoppler) und drei Ausgaenge (ueber 2803 an Relay). Klingt einfach, ist es auch. Hier das Programm dazu:



CodeBox BascomAVR

 $regfile = "m644pdef.dat"
 $crystal = 1000000

 $swstack = 70
 $hwstack = 70
 $framesize = 70


'----------------------------------- Ports -------------------------------------

 Config Pina.0 = Input
  Porta.0 = 1
   T4w Alias Pina.0                            'Thermostat 4, Heizsignal

 Config Pina.1 = Input
  Porta.1 = 1
   T4y Alias Pina.1                             'Thermostat 4, Kuehlsignal

 Config Pina.2 = Input
  Porta.2 = 1
   T4g Alias Pina.2                             'Thermostat 4, Lueftersignal

 Config Pina.3 = Input
  Porta.3 = 1
   T3w Alias Pina.3                             'Thermostat 3, Heizsignal

 Config Pina.4 = Input
  Porta.4 = 1
   T3y Alias Pina.4                              'Thermostat 3, Kuehlsignal

 Config Pina.5 = Input
  Porta.5 = 1
   T3g Alias Pina.5                              'Thermostat 3, Lueftersignal

 Config Pinc.4 = Input                         
  Portc.4 = 1
   T2w Alias Pinc.4                              'Thermostat 2, Heizsignal

 Config Pinc.3 = Input
  Portc.3 = 1
   T2y Alias Pinc.3                              'Thermostat 2, Kuehlsignal

 Config Pinc.2 = Input
  Portc.2 = 1
   T2g Alias Pinc.2                              'Thermostat 2, Lueftersignal

  Config Pinc.1 = Input
  Portc.1 = 1
   T1w Alias Pinc.1                              'Thermostat 1, Heizsignal

 Config Pinc.0 = Input
  Portc.0 = 1
   T1y Alias Pinc.0                              'Thermostat 2, Kuehlsignal

 Config Pind.7 = Input
  Portd.7 = 1
   T1g Alias Pind.7                              'Thermostat 2, Lueftersignal

'...............................................................................

 Config Portd.0 = Output
  Portd.0 = 0
   Heat Alias Portd.0                           'Ausgang zur Waermepumpe

 Config Portd.1 = Output
  Portd.1 = 0
   Vent Alias Portd.1                           'Ausgang zur Lueftungsanlage

 Config Portd.2 = Output
  Portd.2 = 0
   Cool Alias Portd.2                           'Ausgang zum Klimakompressor


'--------------------------------- Interrupts ----------------------------------

 Config Timer1 = Timer , Prescale = 1024            'Looptimer, 63 sek.
  On Ovf1 Timer1_isr
   Stop Timer1

'--------------------------------- Variabeln -----------------------------------

 Dim Systemstatus As Byte

 Const Heating = &H80
 Const Cooling = &H40
 Const Ventilation = &H20
 Const Standby = &H00
 Const Timer1reload = 65534

'--------------------------------- Main ----------------------------------------

 Systemstatus = Standby

 Do

 Main:

   Enable Timer1
    Start Timer1
     Enable Interrupts
      Power Idle
       Disable Interrupts
        Stop Timer1
         Disable Timer1



 If T1w = 0 Or T2w = 0 Or T3w = 0 Or T4w = 0 Then      'Prioritaeres Heizen: Wenn eines der Thermostate auf Heizen geht, 
                                                     'werden die anderen Signale uebersprungen und das System geht in den Heizmodus  
 Systemstatus = Heating
   Goto Set_system
 End If

 If T1y = 0 Or T2y = 0 Or T3y = 0 Or T4y = 0 Then                 
  Systemstatus = Cooling
   Goto Set_system
 End If

 If T1g = 0 Or T2g = 0 Or T3g = 0 Or T4g = 0 Then
  Systemstatus = Ventilation
   Goto Set_system
 End If



 Set_system:


 Select Case Systemstatus
  Case Heating : Heat = 1
                  Vent = 0
                   Cool = 0
  Case Cooling : Heat = 0
                  Vent = 0
                   Cool = 1
  Case Ventilation : Heat = 0
                      Vent = 1
                       Cool = 0
 End Select



 Loop

 End

'-------------------------------------------------------------------------------

 Timer1_isr:

  Load Timer1 Timer1reload
     Stop Timer1

 Return





Was ist das Problem bei so einem bloeden Programm? Jetzt kommts: Es geht immer in den Heizmodus. Egal, was die Thermostate senden, bzw. egal ob sie ueberhaupt angeschlossen sind. Mein erster Gedanke: Ein Optokoppler zieht ohne Signal den Pin nach GND. Leider hat sich das nicht bestaetigt. Alle Optokoppler halten die Pinspannung, es gibt keine Spikes, keine Wellen, keine Stoersignale (ueber mehrere Testlaeufe mit Oszilloskop ueberwacht). Und doch schaltet der Mega in den Heizmodus.
Hab daraufhin alle Kabel abgeklemmt, die Optokoppler entfernt (zum Glueck hab ich Sockel verloetet) und nochmal getestet. Und wieder in den Heizmodus. Bevor ich jetzt die Platte vom Balkon werf, frag ich euch: Was kann das sein? Hab ich was uebersehen? Ich mach mich jetzt ans reassemblieren, wollen doch mal sehen, was der Mega im Speicher hat.
 
Code:
 Config Portd.0 = Output
  Portd.0 = 0
   Heat Alias Portd.0

Muß das nicht Pind.0 heißen?
Hab das zumindest bei mir anders gemacht.
 
Dein Systemstatus ist nicht initialisiert, oder? Was soll sein, wenn kein Eingang auf low gezogen ist? Dann landest Du nach den 3 abgelehnten IF's ja trotzdem im Select Case... (eben mit nicht initialisiertem Systemstatus)

Warum machst Du Dir das eigentlich so kompliziert?
Du scheinst 4 Beine zu haben, die den Heizausgang setzen sollen,
4 die die Ventilation setzen sollen
und 4, für die Kühlung.
Also wenn mindestens 1 Bein aus der Gruppe low ist, soll die entsprechende Reaktion erfolgen, wobei die Prioritätsfolge heizen > ventilieren > kühlen ist
Abgesehen davon, daß man die je 4 Eingänge über ein externes Oder-Gatter zusammennehmen könnte, geht das doch auch intern einfacher:
Man lege die Eingänge einer Gruppe möglichst auf einen PORT, dann kann man mittels AND die restlichen Bits wegmaskieren und prüfen ob die Restlichen Bits gesetzt sind (ist dann ja nur ein Vergleich).

Und warum diese komische Konstruktion mit dem Timer? Warum läßt Du den nicht durchlaufen? Also den Schlafmodus einfach am Anfang der loop auslösen, und dann die Auswertung der Beine. Die ISR könnte dann leer sein - sauberer wäre aber 'n Flag, welches in der main loop gepollt wird, oder die Auswertung direkt in der Timer-ISR

Deine Sleep-Instruktion ist laut Bascom-Hilfe
NOT RECOMMENDED. Please use CONFIG POWERMODE instead.

Hmm... wie ist das mit den verwendeten Pullups eigentlich im Idle... bleiben die aktiviert?
Nachschlag:
Code:
 Config Portd.0 = Output
  Portd.0 = 0
   Heat Alias Portd.0

Muß das nicht Pind.0 heißen?...
Ursprünglich waren die PIN-Register nur-Lese-Register, es wird der tatsächliche Zustand des Pins ausgelesen. Bei den etwas aktuelleren Controllern können die Register allerdings beschrieben werden - eine 0 hat keine Auswirkung, eine 1 toggelt hingegen das korrespondierende Bit im PORT-Register (auch dann, wenn das Bein alternativ genutzt wird - es wirkt sich auf das Bein natürlich dann nicht aus (habe auf diese Art bereits mal 'ne Checksumme im PORT-Bit des Reset-Beines kumuliert)).
Oder meintest Du das ALIAS? Heat ist doch der Ausgang der gesetzt werden soll (oder eben nicht)
 
Dein Systemstatus ist nicht initialisiert, oder? Was soll sein, wenn kein Eingang auf low gezogen ist? Dann landest Du nach den 3 abgelehnten IF's ja trotzdem im Select Case... (eben mit nicht initialisiertem Systemstatus)

Warum machst Du Dir das eigentlich so kompliziert?
Du scheinst 4 Beine zu haben, die den Heizausgang setzen sollen,
4 die die Ventilation setzen sollen
und 4, für die Kühlung.
Also wenn mindestens 1 Bein aus der Gruppe low ist, soll die entsprechende Reaktion erfolgen, wobei die Prioritätsfolge heizen > ventilieren > kühlen ist
Abgesehen davon, daß man die je 4 Eingänge über ein externes Oder-Gatter zusammennehmen könnte, geht das doch auch intern einfacher:
Man lege die Eingänge einer Gruppe möglichst auf einen PORT, dann kann man mittels AND die restlichen Bits wegmaskieren und prüfen ob die Restlichen Bits gesetzt sind (ist dann ja nur ein Vergleich).

Und warum diese komische Konstruktion mit dem Timer? Warum läßt Du den nicht durchlaufen? Also den Schlafmodus einfach am Anfang der loop auslösen, und dann die Auswertung der Beine. Die ISR könnte dann leer sein - sauberer wäre aber 'n Flag, welches in der main loop gepollt wird, oder die Auswertung direkt in der Timer-ISR

Deine Sleep-Instruktion ist laut Bascom-Hilfe

Hmm... wie ist das mit den verwendeten Pullups eigentlich im Idle... bleiben die aktiviert?
Nachschlag:
Ursprünglich waren die PIN-Register nur-Lese-Register, es wird der tatsächliche Zustand des Pins ausgelesen. Bei den etwas aktuelleren Controllern können die Register allerdings beschrieben werden - eine 0 hat keine Auswirkung, eine 1 toggelt hingegen das korrespondierende Bit im PORT-Register (auch dann, wenn das Bein alternativ genutzt wird - es wirkt sich auf das Bein natürlich dann nicht aus (habe auf diese Art bereits mal 'ne Checksumme im PORT-Bit des Reset-Beines kumuliert)).
Oder meintest Du das ALIAS? Heat ist doch der Ausgang der gesetzt werden soll (oder eben nicht)

1 Systemstatus: Bei Start &H00 (vor dem "Do"). Das Select case wird uebersprungen, weil keine Bedingung wahr ist. Und genau das passiert eben nicht. Beim Start und nach Ablauf des Timers springt das Programm in den Heizmodus.

2 Pinbelegung: Kuck mal das Pinout vom 644PA an. PortA, PortC und ein Pin von D sind auf der rechten Seite des Megas. Das Platinendesign ist so gestaltet, dass die rechte Seite des Megas ausliest und die linke schaltet. Anders war es aufgrund der beim Kunden vorhandenen Platzverhaeltnisse und Kabellaengen nicht moeglich. Die Steuerung ersetzt eine veraltete und muss von den Anschluessen her identisch aufgeaut sein.

3 Timer_isr: Das hier dargestellte Programm ist eine momentan gekuerzte Fassung, die ich vor Ort auf die schnelle getippt habe, um das Problem mit der falschen Schaltung einzukreisen. In der entgueltigen Version laeuft der Timer 30 Sekunden und es werden noch Stellmotoren in den Lueftungskanaelen angesteuert. Da darf der Timer nicht weiterlaufen. Diese sind aber z.Z. noch nicht installiert.

4 Flagensetzung: Die Signale der Thermostate liegen bei Ausloesung min. 30min dauerhaft an, also ist der Pin quasi meine Flagge.

5 Sleep-Mode: Idle ist bei fast allen Megas eine Standardfunktion, aber nicht alle Bascomversionen verarbeiten ihn. Meine kennt ihn und sie funktioniert auch. Das ist auch nicht das Problem. Und ja, die Pins behalten ihren Status (Pullup on) waehrend des Sleeps.
 
Ok, das Initialisieren hab ich übersehen...
Mit dem Case und so meinte ich, daß Du nach der Auswertung der Taster immer bei "Set_system:" landest (egal ob ein Case erfüllt ist). Sauberer wäre, wenn Du Set_system als Subroutine außerhalb der loop platzierst, mit einem Return abschließt, und es mittels Gosub aufrufst (wenn Du es schon so Hochsprachen-umständlich umsetzt). Dann müßten die 3 IF's natürlich als "if..then.. else if.. then.. else .. if.. then .. end if" realisiert werden...

Daß nie wieder in den Standby zurückgekehrt werden können soll, ist korrekt?

bleibt eigentlich nur simples debugging: wenn Dir der Simulator nicht hilft, eben am Controller selbst.
Du platzierst einfach in der Heating-IF vor dem Goto/Gosub ein "END", und Manipulierst dort Deine 3 Ausgänge. Die ganze Select-Case-Geschichte wird auskommentiert. Die Eingänge läßt Du offen (sollten also über die Pullups high sein).
vor dem End in der Heating-If schaltest Du nun einen der Ausgänge high, und Abhängig von T1W..T4W weitere Ausgänge

Nach dem Start misst Du die Eingänge durch (müssen alle High sein), und die Ausgänge (müssen alle low sein)

Legst Du einen der vier Heat-Eingänge auf low, sollte er in der if landen, und dort bei dem end steckenbleiben, vorher halt die Ausgänge manipulieren, was Du messen kannst...
Wenn Du andere Beine als Ausgang fürs Debugging nutzen kannst, bzw UART oder ein Display gehts natürlich schneller...
 
Ok, das Initialisieren hab ich übersehen...
Mit dem Case und so meinte ich, daß Du nach der Auswertung der Taster immer bei "Set_system:" landest (egal ob ein Case erfüllt ist). Sauberer wäre, wenn Du Set_system als Subroutine außerhalb der loop platzierst, mit einem Return abschließt, und es mittels Gosub aufrufst (wenn Du es schon so Hochsprachen-umständlich umsetzt). Dann müßten die 3 IF's natürlich als "if..then.. else if.. then.. else .. if.. then .. end if" realisiert werden...

Daß nie wieder in den Standby zurückgekehrt werden können soll, ist korrekt?

bleibt eigentlich nur simples debugging: wenn Dir der Simulator nicht hilft, eben am Controller selbst.
Du platzierst einfach in der Heating-IF vor dem Goto/Gosub ein "END", und Manipulierst dort Deine 3 Ausgänge. Die ganze Select-Case-Geschichte wird auskommentiert. Die Eingänge läßt Du offen (sollten also über die Pullups high sein).
vor dem End in der Heating-If schaltest Du nun einen der Ausgänge high, und Abhängig von T1W..T4W weitere Ausgänge

Nach dem Start misst Du die Eingänge durch (müssen alle High sein), und die Ausgänge (müssen alle low sein)

Legst Du einen der vier Heat-Eingänge auf low, sollte er in der if landen, und dort bei dem end steckenbleiben, vorher halt die Ausgänge manipulieren, was Du messen kannst...
Wenn Du andere Beine als Ausgang fürs Debugging nutzen kannst, bzw UART oder ein Display gehts natürlich schneller...


Das Set_system als GOTO und nicht als GOSUB umgesetzt ist, hat seinen Sinn. Schau mal, wie kurz das Programm ist. Ein Gosub, Flagensetzung, etc. macht das Programm unnoetig groesser aber nicht unbedingt effektiver. Oder siehst Du irgendwo eine Verbesserung bei Verwendung eines GOSUBS? Da das Heizsignal Prioritaet vor dem Kuehlen hat, muss das Select case beim Auftreten eines Heizsignals verlassen werden, sonst wuerde eine weitere Abarbeitung erfolgen und falls ein anderes Thermostat dann "Kuehlen" sendet, wuerde das Kuehlsignal ebenfalls ausgeloest werden. Um das zu verhindern, muessen dann wiederum Flaggen ein ungewolltes Umschalten blockieren, etc.
Deshalb sehe ich das Programm absolut nicht als "hochsprachen-umstaendlich" an, sondern kurz, klar und uebersichtlich. Die endlosen If-Elseif-Else-Konstruktionen mit +500 Flaggen dazu hab ich auch mal programmiert. Dann hab ich mich mal intensiv mit einem Assembler-Tutorial auseinandergesetzt und erkannt, dass GOTO oder RJMP in vielen Faellen Flaggen ueberfluessig macht und z.B. Menuesteuerungen um 50% verkuerzt. Eine Variable fuer den Systemstatus, entsprechend klar definierte Sprungmarken und komprimierte Anweisungen sind heute meine Prinzipien. GOSUB macht Sinn bei wiederkehrenden Funktionen auf unterschiedlichen Ebenen, aber nicht bei einem Programm mit grad mal 15 Anweisungen/Vergleichen.


Das das System nicht zum Standby zurueckkehrt, ist korrekt, da die Ventilatorsteuerung eigentlich immer an ist. Im Simulator laeuft das Programm einwandfrei und korrekt. Nur in der Realitaet hakt es.
 
Pinbelegung: Kuck mal das Pinout vom 644PA an. PortA, PortC und ein Pin von D sind auf der rechten Seite des Megas. Das Platinendesign ist so gestaltet, dass die rechte Seite des Megas ausliest und die linke schaltet. Anders war es aufgrund der beim Kunden vorhandenen Platzverhaeltnisse und Kabellaengen nicht moeglich. Die Steuerung ersetzt eine veraltete und muss von den Anschluessen her identisch aufgeaut sein...
??
Rechte obere Ecke ist PortA, rechte untere PortC, unten links PortD und oben links PortB. Je alle 8 Bits. Außerdem an jeder Kante ein Stromversorgungspaar (rechts analog), links die Oszillatorpins und Reset, rechts Aref.
Was spricht dagegen, alle Heizungseingänge in ein Register (am besten ein Nibble), alle Vent-Eingänge, alle Cool ebenso. macht anderthalb Ports voll. Oder kommen die Leitungen genau so auf die Platine (ist das etwa 'ne fertige Schaltung, und Du kannst nicht mehr routen? DAS hattest Du nicht gesagt.)

Eigentlich soll das keine Effizienz-Grundsatzdiskussion werden... willst Du nun in der Hochsprache übersichtlich programmieren, oder speichereffizient oder zeiteffizient sein, oder was?

Am Anfang initialisierst Du zB Deine Ein- und Ausgänge. Jedes Bit einzeln. Je sechsmal DDRA und PORTA, je fünfmal DDRC und PORTC, je viermal DDRD und PORTD. Da diese Register alle direct bit accessible sind, nutzt Bascom SBI/CBI, benötigt also einen Takt pro Bit (also hier 30 Takte). Du kannst aber auch alle Bits eines Registers simultan beschreiben, was 2 Takte braucht (Maske Laden+Register schreiben) - wären hier also 6 Takte (2 pro Register). Jetzt weißt Du vielleicht nicht, was in den nicht verwendeten Bits der Register bleiben muß, willst also mit RMW erst das Register lesen und nur bestimmte Bits löschen oder setzen -> lesen, Maske löschen oder (!) setzen, schreiben wären 3 Takte pro Register, zusammen also 12 Takte. Sollen beim RMW gleichzeitig einige Bits gelöscht, andere gesetzt werden hast Du mit laden, Maske setzen, Maske löschen, schreiben 4 Takte pro Register -> wären hier 16.
Generell gilt (für direct bit accessible Register, alle anderen müssen eh als ganzes geschrieben werden):
Ein Bit setzen/löschen geht am effizientesten direkt Bitweise.
Ab dem dritten Bit ist das beschreiben des ganzen Registers effizienter (wenn kein RMW erforderlich ist)
Ist RMW erforderlich, und sollen nur bits geschrieben oder gelöscht werden, ist ab dem vierten Bit das ganze Register zusammen effizienter.
Soll gleichzeitig RMW gesetzt und gelöscht werden, ist das ab dem 5ten über das ganze Register effizienter als direkt.

Der Select-Case-Block IST doch ein IF..THEN..ELSE..IF..ENDIF-Konstrukt, letztendlich hast Du folgendes nach dem aufwachen (und dem Timer-Kram):
Wenn keiner der Heat-Eingänge betätigt ist, springe nach (1 (endif Heat))
(sonst) setze den Status auf "Heating" und springe nach (4 (set_system))
(1) Wenn keiner der Cool-Eingänge gesetzt ist, springe nach (2 (endif Cool))
(sonst) setze Status auf "Cooling" und springe nach (4 (set_system))
(2) Wenn keiner der Vent-Engänge gesetzt ist, springe nach (3 (endif Vent))
(sonst) setze Status auf "Ventilation" und springe nach (4 (set_system))
(3)=(4) Wenn Status ungleich "Heating", springe nach (5 (endcase heating))
(sonst) setze/lösche die 3 Ausgänge und springe nach (8 (endselect))
(5)Wenn Status ungleich "Cooling", springe nach (6 (endcase cooling))
(sonst) setze/lösche die 3 Ausgänge und springe nach (8 (endselect))
(6) Wenn Status ungleich "Ventilation", springe nach (5 (endcase ventilation))
(sonst) setze/lösche die 3 Ausgänge und springe nach (8 (endselect))
(7) hier würde case else kommen, leer
(8)loop

Warum also nicht gleich die drei IFs als Konstrukt wie den Case-Block gestalten, und dort direkt die Ausgänge manipulieren? Spart 'n Haufen "Wenn"s und die Statusvariable...

Und wie diese IFs mit den 4 verORrten Bits implementiert werden, will ich gar nicht wissen. Wenn die 4 Bits aus einem Register kämen, könnte man die wie gesagt durch Register lesen und ein Compare mit 'ner Konstante prüfen, wenn die zu fragenden Bits so wild durch mehrere Ports gehen, könnte es effizienter sein, jedes einzeln direkt zu prüfen, und ein Bit im GPIOR als Merker zu nutzen
(also GPIOR0.0 löschen, die 4 Bits mittels SKIP if Bit is Set/Clear prüfen und als zu skippenden Befehl das GPIOR0.0 setzen -> nach den 4 Bits GPIOR0.0 für bedingten Sprung nutzen (SKIP+RJMP) -> sollte das IF mit den vielen ORs mit 10 Takten erschlagen, ob Bascom das schneller macht, weiß ich nicht...

Aber das ist nicht Dein Problem, Du mußt prüfen ob (und warum) Du in der Heating-IF landest, ist das nicht der Fall, ob (und warum) Du trotzdem in dem entsprechendem Case landest.
Also wie schon angedeutet das Programm einfach mit 'ner Endlosschleife dort stoppen, und vorher über die Ausgänge irgendwas ausgeben und nachmessen...
 
??
Rechte obere Ecke ist PortA, rechte untere PortC, unten links PortD und oben links PortB. Je alle 8 Bits. Außerdem an jeder Kante ein Stromversorgungspaar (rechts analog), links die Oszillatorpins und Reset, rechts Aref.
Was spricht dagegen, alle Heizungseingänge in ein Register (am besten ein Nibble), alle Vent-Eingänge, alle Cool ebenso. macht anderthalb Ports voll. Oder kommen die Leitungen genau so auf die Platine (ist das etwa 'ne fertige Schaltung, und Du kannst nicht mehr routen? DAS hattest Du nicht gesagt.)

Eigentlich soll das keine Effizienz-Grundsatzdiskussion werden... willst Du nun in der Hochsprache übersichtlich programmieren, oder speichereffizient oder zeiteffizient sein, oder was?

Am Anfang initialisierst Du zB Deine Ein- und Ausgänge. Jedes Bit einzeln. Je sechsmal DDRA und PORTA, je fünfmal DDRC und PORTC, je viermal DDRD und PORTD. Da diese Register alle direct bit accessible sind, nutzt Bascom SBI/CBI, benötigt also einen Takt pro Bit (also hier 30 Takte). Du kannst aber auch alle Bits eines Registers simultan beschreiben, was 2 Takte braucht (Maske Laden+Register schreiben) - wären hier also 6 Takte (2 pro Register). Jetzt weißt Du vielleicht nicht, was in den nicht verwendeten Bits der Register bleiben muß, willst also mit RMW erst das Register lesen und nur bestimmte Bits löschen oder setzen -> lesen, Maske löschen oder (!) setzen, schreiben wären 3 Takte pro Register, zusammen also 12 Takte. Sollen beim RMW gleichzeitig einige Bits gelöscht, andere gesetzt werden hast Du mit laden, Maske setzen, Maske löschen, schreiben 4 Takte pro Register -> wären hier 16.
Generell gilt (für direct bit accessible Register, alle anderen müssen eh als ganzes geschrieben werden):
Ein Bit setzen/löschen geht am effizientesten direkt Bitweise.
Ab dem dritten Bit ist das beschreiben des ganzen Registers effizienter (wenn kein RMW erforderlich ist)
Ist RMW erforderlich, und sollen nur bits geschrieben oder gelöscht werden, ist ab dem vierten Bit das ganze Register zusammen effizienter.
Soll gleichzeitig RMW gesetzt und gelöscht werden, ist das ab dem 5ten über das ganze Register effizienter als direkt.

Der Select-Case-Block IST doch ein IF..THEN..ELSE..IF..ENDIF-Konstrukt, letztendlich hast Du folgendes nach dem aufwachen (und dem Timer-Kram):
Wenn keiner der Heat-Eingänge betätigt ist, springe nach (1 (endif Heat))
(sonst) setze den Status auf "Heating" und springe nach (4 (set_system))
(1) Wenn keiner der Cool-Eingänge gesetzt ist, springe nach (2 (endif Cool))
(sonst) setze Status auf "Cooling" und springe nach (4 (set_system))
(2) Wenn keiner der Vent-Engänge gesetzt ist, springe nach (3 (endif Vent))
(sonst) setze Status auf "Ventilation" und springe nach (4 (set_system))
(3)=(4) Wenn Status ungleich "Heating", springe nach (5 (endcase heating))
(sonst) setze/lösche die 3 Ausgänge und springe nach (8 (endselect))
(5)Wenn Status ungleich "Cooling", springe nach (6 (endcase cooling))
(sonst) setze/lösche die 3 Ausgänge und springe nach (8 (endselect))
(6) Wenn Status ungleich "Ventilation", springe nach (5 (endcase ventilation))
(sonst) setze/lösche die 3 Ausgänge und springe nach (8 (endselect))
(7) hier würde case else kommen, leer
(8)loop

Warum also nicht gleich die drei IFs als Konstrukt wie den Case-Block gestalten, und dort direkt die Ausgänge manipulieren? Spart 'n Haufen "Wenn"s und die Statusvariable...

Und wie diese IFs mit den 4 verORrten Bits implementiert werden, will ich gar nicht wissen. Wenn die 4 Bits aus einem Register kämen, könnte man die wie gesagt durch Register lesen und ein Compare mit 'ner Konstante prüfen, wenn die zu fragenden Bits so wild durch mehrere Ports gehen, könnte es effizienter sein, jedes einzeln direkt zu prüfen, und ein Bit im GPIOR als Merker zu nutzen
(also GPIOR0.0 löschen, die 4 Bits mittels SKIP if Bit is Set/Clear prüfen und als zu skippenden Befehl das GPIOR0.0 setzen -> nach den 4 Bits GPIOR0.0 für bedingten Sprung nutzen (SKIP+RJMP) -> sollte das IF mit den vielen ORs mit 10 Takten erschlagen, ob Bascom das schneller macht, weiß ich nicht...

Aber das ist nicht Dein Problem, Du mußt prüfen ob (und warum) Du in der Heating-IF landest, ist das nicht der Fall, ob (und warum) Du trotzdem in dem entsprechendem Case landest.
Also wie schon angedeutet das Programm einfach mit 'ner Endlosschleife dort stoppen, und vorher über die Ausgänge irgendwas ausgeben und nachmessen...



Leute Leute, lasst uns nicht streiten bitte... Lotada, die Routine war anfangs anders geschrieben, bereits komplett mit der Steuerung fuer die Dumper (Stellmotoren). Dort wurden die Pins z.B. komplett als Portregister initialisiert und per AND maskiert. Die hier gepostete Routine ist eine Testroutine, weil bereits in der urspruenglichen Version der selbe Fehler auftrat. Da die urspruengliche Version eine Mischung aus Assembler und Bascom war und ich aufgrund einer laengeren Praxispause in Assembler von einem Programmierfehler meinerseits ausgegangen bin, hab ich schnell und innerhalb von 10min. eine reine Bascomversion getippt (eben die hier vorgestellte), um das Signalverhalten zu pruefen. Das ist also nicht die Endversion, sondern eine Testroutine. Wenn ich irgendetwas fuer einen Kunden "nachbaue", schreibe ich meist erst eine ASM-Bascom-Version, weil die leichter zu ueberarbeiten ist. Ist der Kunde zufrieden und alles laeuft, schreib ich das Programm oft nochmal komplett in Assembler und optimiere es so gut es geht. Denn die besten Ideen kommen mir immer, wenn ich das Programm nochmal durcharbeite :D


Manche werden diese Arbeitsweise nicht verstehen, aber leider bleibt mir nie Zeit fuer groessere Tests vor der Montage. In dem Fall fiel am Donnerstag die originale Steuerzentrale beim Kunden nach 10 Jahren Treue aus. Der Kunde betreibt einen Grosshandel fuer Baeckereibedarf und im Buero war es Freitag 12 Grad, weil die Heizung wegen der defekten Zentrale nicht mehr ansprang. Freitag war ich dort (120km Anfahrt), alte Platine ausbauen, Samstag und Sonntag neue bauen, Montag morgen um 5.00 Montage. Und so laeuft das die ganze Zeit. Rapid Prototyping am Fliessband. Heute kam der naechste Auftrag fuers Wochenende. Ein PID-Regler fuer ne Waermepumpe, bei der der Wasser-Ruecklauf genau zwischen 28-30 Grad gehalten werden muss, damit am Ende 50 Grad im Vorlauf sind. Ansteuerung ueber Drei-Wege-Ventil an einen Standkuehler, dessen Ventilatoren auch noch einzeln angesteuert werden sollen (3x 3-Phasen) und noch ein zusaetzliches 3-Wege-Ventil fuer Brauchwasser-Erwaermung mit Prioritaetssteuerung und was weiss ich noch alles. Eine fertige Loesung gibts fuer diese Anforderung nicht zu kaufen, also mach mal Uwe. Bis Heiligabend muss der Mist installiert sein und laufen. Noch Fragen?
:yu:
 
Wie gesagt, statt dem Kram mit der Statusvariable und set_system einfach:


CodeBox BascomAVR
...
If T1w = 0 Or T2w = 0 Or T3w = 0 Or T4w = 0 Then      'Prioritaeres Heizen: Wenn eines der Thermostate auf Heizen geht, 
                                                     'werden die anderen Signale uebersprungen und das System geht in den Heizmodus  
                 Heat = 1
                 Vent = 0
                 Cool = 0
 Elseif T1y = 0 Or T2y = 0 Or T3y = 0 Or T4y = 0 Then                 
                Heat = 0
                Vent = 0
                Cool = 1
 Elseif T1g = 0 Or T2g = 0 Or T3g = 0 Or T4g = 0 Then
                Heat = 0
                Vent = 1
                Cool = 0
 End If
...
Wenn das denselben Effekt hat, muß ja zwingend eine der IF-Bedingungen erreicht worden sein, also einer der Tnw=0 sein. Also dann das Programm da stoppen, und über die Ausgänge herausbekommen, welcher der Eingänge als low ausgelesen worden ist. Hast Du denn mal am Bein nachgemessen, ob die wirklich alle high sind?
 
Wie gesagt, statt dem Kram mit der Statusvariable und set_system einfach:


CodeBox BascomAVR
...
If T1w = 0 Or T2w = 0 Or T3w = 0 Or T4w = 0 Then      'Prioritaeres Heizen: Wenn eines der Thermostate auf Heizen geht, 
                                                     'werden die anderen Signale uebersprungen und das System geht in den Heizmodus  
                 Heat = 1
                 Vent = 0
                 Cool = 0
 Elseif T1y = 0 Or T2y = 0 Or T3y = 0 Or T4y = 0 Then                 
                Heat = 0
                Vent = 0
                Cool = 1
 Elseif T1g = 0 Or T2g = 0 Or T3g = 0 Or T4g = 0 Then
                Heat = 0
                Vent = 1
                Cool = 0
 End If
...
Wenn das denselben Effekt hat, muß ja zwingend eine der IF-Bedingungen erreicht worden sein, also einer der Tnw=0 sein. Also dann das Programm da stoppen, und über die Ausgänge herausbekommen, welcher der Eingänge als low ausgelesen worden ist. Hast Du denn mal am Bein nachgemessen, ob die wirklich alle high sind?

Ja, die Eingaenge hab ich alle mit dem Oszi abgetastet. Das uC hat definitiv kein gueltiges Signal. Uebrigens macht er genau dasselbe momentan auf meinem Schreibtisch, ohne die Platine und nur am Netz angeschlossen. Der Fehler liegt also definitiv in der Software. Zu deinem Code hab ich eine Frage: Wenn in deiner If-Konstruktion die erste Bedingung wahr ist, wird dann der Rest uebersprungen? Wird das folgende ELSEIF nicht auch geprueft und bei positivem Ergebnis ausgefuehrt? Soweit ich mich erinnere, werden die Bedingungen der Reihe nach abgearbeitet und es koennen mehrere als wahr gewertet werden. Bin mir aber nicht mehr sicher, ob das bei IF-ELSEIF der Fall wahr oder bei SELECT CASE. Vor einem Jahr hatte ich durch so einen Fall einen Super-Bug, der mich tagelanges Suchen gekostet hat. Irgendwo hab ich dann einen Artikel gefunden, der beschrieb, dass entweder bei SELECT oder IF weiter verglichen wird, auch wenn eine Bedingung bereits erfuellt wurde. Deshalb verlasse ich die den Pruefblock via Goto, wenn mehrere Bedingungen wahr sein koennen (was hier der Fall ist)


Nachtrag: Deine Code macht das gleiche. Ich hab ihn um vier weitere IFs ergaenzt:

If T1w = 0 Then
Set Portb.1
End If

If T2w = 0 Then
Set Portb.2
End If

If T3w = 0 Then
Set Portb.3
End If

If T4w = 0 Then
Set Portb.4
End If

Wenn man den vier IFs Glauben schenken will, entsteht das Signal an T2w, also Pinc.4. Nur seh ich dieses Signal nicht... auch nicht auf meinem Fluke-Oszi. Hab nochmal die Fuses geprueft, JTAG ist deaktiviert.
 
Nachtrag 2: Der Prozessor hat ne Macke. Bin grad rueber ins Magazin und hab nen neuen Mega programmiert, jetzt laeuft es einwandfrei. Aus Spass hab ich nochmal den vorherigen mit Oszi, 5V direkt vom Netzteil am Pinc.4 und Loopwiederholung im 3-Sek-Takt angeklemmt. Und wieder bekomme ich ein Schaltsignal, wo keines ist. Da muss intern was abgefackelt sein oder was weiss ich. Egal, ich mach jetzt ein Bier auf und hau mich in die Kiste. Danke an alle fuer die Hilfe, besonders an LotadaC :)
 
Wäre ein JTAG-Pin, aber das hast Du ja laut #3 bereits per Fuse deaktiviert...

Dann mach doch mal'n ganz simplen Test:
An alle Beine der PORTs B und D LEDs mit Vorwiderständen gegen Gnd, diese dann im Programm als Ausgang (DDRB=DDRC=0xFF)
PORTA=PORTC=0xFF, also alle Engänge lassen, und Pullups an.
In der Programmschleife dann PORTB=PINA und PORTD=PINC
Dann sollten(!) alle LEDs leuchten.
Jetzt nimmst Du'n 4K7, und legst damit einzeln die Beine von PortA und PortC auf Gnd - dann sollte die korrespondierende LED solange ausgehen.

Danach kannst Du die Rollen von A und B bzw C und D tauschen (also in Soft und Hardware)...

Edit: wie vermutet - Hardwaredefekt, haste ja selbst rausbekommen...
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)