Forum: Mikrocontroller und Digitale Elektronik Pin-Zustand von PIC16LF722 ändert sich zufällig


von Christoph B. (christophbudelmann) Benutzerseite


Lesenswert?

Hallo,

ich muss bei einem Projekt einen PIC16LF722 einsetzen und weiß langsam, 
warum ich bislang immer mit anderen Controllern gearbeitet habe... ;-)

Folgendes Problem habe ich mit diesem Code:

#include <pic.h>

int main (void)
{
  TRISC6 = 0;
  RC6 = 0;

  TRISC0 = 0;
  RC0 = 0;

  unsigned int i;
  for (i = 0; i < 1000; i++)
  {
    CLRWDT();

    RC0 = 1;
    DelayUs(125);
    RC0 = 0;
    DelayUs(125);
  }

  for(;;)
  {
    CLRWDT();
  }
}

Sobald ich RC0 ein wenig toggle, wechselt RC6 auf high, obwohl das 
nirgends im Code geschrieben steht. Das ganze kann ich mit dem Simulator 
nicht nachvollziehen, dort bleibt RC6 konstant low.

Der PIC16LF722 wird mit einem 4MHz-Quarz betrieben, VCC ist 3,3V und 
Abblockkondensatoren sowie Reset-Beschaltung (RC-Glied) sind auch 
vorhanden.

Der Zustand ändert sich nicht immer, sondern scheinbar zufällig. Es ist 
aber auch nichts EMV-kritisches auf der Platine - alle anderen 
Komponenten wie beispielsweise ein Display und einige Taster werden noch 
garnicht benutzt.

Vielleicht habe ich etwas im Datenblatt übersehen, aber momentan fällt 
mir schlichtweg nichts mehr ein. Ich benutze übrigens MPLAB IDE v8.40 
mit dem Hi-Tech C-Compiler in der Lite-Version.

Danke schonmal im vorraus.

von Maik W. (werner01)


Lesenswert?

wie kommsdn du zu der Erkenntnis? mit den LAT-"Registern ist das so im C 
auch richtig, wenn er welche hat?

von Christoph B. (christophbudelmann) Benutzerseite


Lesenswert?

Maik Werner schrieb:
> wie kommsdn du zu der Erkenntnis?

Nach dem Toggeln von RC0 liegt an RC6 ein High-Pegel an - auch wenn der 
Pin niemals auf high gesetzt worden ist.

> mit den LAT-"Registern ist das so im C
> auch richtig, wenn er welche hat?

Die PIC16F haben kein LAT-Register.

von Didi S. (kokisan2000)


Lesenswert?

Moin Christoph,

bei kleinen PICs gibt es immer wieder drei prinzipielle Punkte die vor 
dem Sourcecode abgehandelt sein sollten:
1) Schalte ALLES (!) an den Pins ab, was Du nicht benötigst (analoge 
Eingänge, Comparatoren, ...)
2) Definiere alle Interrupts, besonders die mit Pinfunktionen
3) arbeite grundsätzlich mit einem Schattenregister (read-modify-write 
Problematik, Manual Seite 73), wenn Du DAten an den Pins ausgeben 
möchtest. Bedeutet soviel wie schreibe immer den ganzen Port, keine 
einzelnen Pins.

Habe viele Projekte mit der PIC Familie verwirklicht. Wenn obige Punkte 
berücksichtigt werden, laufen die kleinen bestens.

Gruß
kokisan

von Christoph B. (christophbudelmann) Benutzerseite


Lesenswert?

Hallo Dierck,

leider komme ich noch nicht ganz weiter:

> 1) Schalte ALLES (!) an den Pins ab, was Du nicht benötigst (analoge
> Eingänge, Comparatoren, ...)
> 2) Definiere alle Interrupts, besonders die mit Pinfunktionen

Das habe ich gemacht, auch wenn es oben noch nicht explizit stand.

> 3) arbeite grundsätzlich mit einem Schattenregister (read-modify-write
> Problematik, Manual Seite 73), wenn Du DAten an den Pins ausgeben
> möchtest. Bedeutet soviel wie schreibe immer den ganzen Port, keine
> einzelnen Pins.

Auch das habe ich gemacht, aber weder Konstrukte wie

unsigned char port;
port  = PORTC |  (1<<4);
PORTC = port;

noch so simple Sachen wie

PORTC |= (1<<4);

funktionieren richtig - es ändern sich immer andere Pins noch mit.

Liegt das eventuell am Compiler? Der generierte Assembler-Code sieht 
eigentlich in Ordnung aus.

von Harald (Gast)


Lesenswert?

Poste bitte mal das generierte Assembler-File.

von Harald (Gast)


Lesenswert?

Der Bitzugriff auf einzelne Portbits ist eigentlich gerade eine Stärke 
des PICs. Ich vermute, dass man dem Compiler das explizit sagen muss. So 
nach deiner Beschreibung macht er daraus einen read-modify-write für den 
ganzen Port.

von Christoph B. (christophbudelmann) Benutzerseite


Lesenswert?

Harald schrieb:
> Poste bitte mal das generierte Assembler-File.

Voilà:

--

23:                int main (void)
24:                {
25:                  // Ausgänge
26:                  TRISC = 0b01101111;
   001    306F     MOVLW 0x6f
   002    1683     BSF 0x3, 0x5
   003    1303     BCF 0x3, 0x6
   004    0087     MOVWF 0x7
27:                  PORTC = 0;
   005    1003     BCF 0x3, 0
   006    1283     BCF 0x3, 0x5
   007    1303     BCF 0x3, 0x6
   008    3000     MOVLW 0
   009    1803     BTFSC 0x3, 0
   00A    3001     MOVLW 0x1
   00B    0087     MOVWF 0x7
28:
29:                  // Eingang
30:                  ANSELB = 0;
   00C    1003     BCF 0x3, 0
   00D    1683     BSF 0x3, 0x5
   00E    1703     BSF 0x3, 0x6
   00F    3000     MOVLW 0
   010    1803     BTFSC 0x3, 0
   011    3001     MOVLW 0x1
   012    0086     MOVWF 0x6
31:                  TRISB  = 0b00000001;
   013    3001     MOVLW 0x1
   014    1683     BSF 0x3, 0x5
   015    1303     BCF 0x3, 0x6
   016    0086     MOVWF 0x6
32:
33:                  for(;;)
34:                  {
35:                    CLRWDT();
36:
   017    0064     CLRWDT
37:                    PORTC |=  (1<<4);
   018    1283     BCF 0x3, 0x5
   019    1303     BCF 0x3, 0x6
   01A    1607     BSF 0x7, 0x4
38:                    DelayMs(100);
   01B    3064     MOVLW 0x64
   01C    27E8     CALL 0x7e8
39:                    PORTC &= ~(1<<4);
   01D    30EF     MOVLW 0xef
   01E    00F5     MOVWF 0x75
   01F    1283     BCF 0x3, 0x5
   020    1303     BCF 0x3, 0x6
   021    0875     MOVF 0x75, W
   022    0587     ANDWF 0x7, F
40:                    DelayMs(100);
   023    3064     MOVLW 0x64
   024    27E8     CALL 0x7e8
41:                  }
   025    2817     GOTO 0x17

--

Vor der Zeile 23 steht nur die Delay-Routine, die aber keinen Einfluss 
auf die Port-Pins nimmt. In diesem Beispiel sollen die Ausgänge RC7 und 
RC4 als Ausgänge benutzt werden, RB0 als Eingang. Immer wenn ich RC4 
toggel, ändert sich der Zustand an RB0. Am Eingang hängt ein 
DCF77-Modul. Es machen auch andere Pins Probleme und die Hardware ist 
soweit in Ordnung.

von Christoph B. (christophbudelmann) Benutzerseite


Lesenswert?

Harald schrieb:
> Der Bitzugriff auf einzelne Portbits ist eigentlich gerade eine Stärke
> des PICs. Ich vermute, dass man dem Compiler das explizit sagen muss. So
> nach deiner Beschreibung macht er daraus einen read-modify-write für den
> ganzen Port.

Weißt du wo? Ich arbeite zum ersten Mal mit dem Hi-Tech-C-Compiler und 
habe bislang noch nichts gefunden.

von Harald (Gast)


Lesenswert?

Das war nur eine Vermutung mit der Einstellbarkeit.

Die Zeile

PORTC &= ~(1<<4);

wird ja tatsächlich in einen r-m-w übersetzt. Erstmal wird 0xEF ins 
Scratch-RAM geschrieben (Adresse 0x75). Dann erfolgt das leidige wählen 
der richtigen Page. Anschliessend wird das W-Register (Akku) mit dem 
Inhalt von 0x75 geladen (also dem Wert 0xEF). In Zeile 022 passiert es 
dann. Das Register 0x07 (Port C) wird mit W verundet und nach 0x07 
zurückgeschrieben.

   01D    30EF     MOVLW 0xef
   01E    00F5     MOVWF 0x75
   01F    1283     BCF 0x3, 0x5
   020    1303     BCF 0x3, 0x6
   021    0875     MOVF 0x75, W
   022    0587     ANDWF 0x7, F


Erstmal ist das ein Wahnsinnsaufwand, denn es gibt auch eine 
Direktzuweisung für den Akku W --> MOVLW 0xEF hätte es auch getan, ganz 
ohne Scratch-RAM.

In korrekter Bitzuweisung müsste es wie folgt aussehen:

     BCF 0x3, 0x5
     BCF 0x3, 0x6
     BCF 0x7, 0x4

Witzig, beim Setzen des Bits hat der Compiler das hinbekommen:

37:                    PORTC |=  (1<<4);
   018    1283     BCF 0x3, 0x5
   019    1303     BCF 0x3, 0x6
   01A    1607     BSF 0x7, 0x4


Die von dir verwendete Schreibweise zum Setzen und Löschen von Portbits 
ist sehr AVR-typisch, da keine Bitunterstützung. Bei jedem Compiler, der 
für bitoptimierte Maschinen geschrieben ist (eben auch PIC) gibt es 
meist spezielle "language extensions" oder Makros, die dann bei der 
Übersetzung zum gewünschten Resultat führen.

Beim Keil C51 oder bei den C167-Compilern (Tasking/Keil) gibt es so 
etwas auch.

Leider kenne ich mich bei dem von dir genutzten Compiler nicht aus. Gibt 
es ein Handbuch oder mindestens ein paar Demo-Codes?

von Harald (Gast)


Lesenswert?

Ach so, ja beim PIC ist die Adresse für Port-Input und -Output 
identisch. Beim AVR warst (bzw. bist) Du es ja gewohnt, die Register 
PORTx und PINx zu benutzen.

Normalerweise ist das in den meisten Fällen unproblematisch, 
vorausgesetzt der Compiler baut keinen Mist. Beim AVR indes ist das auch 
nicht ohne Probleme, da hat man auf anderer Ebene die r-m-w Problematik.

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