Forum: Mikrocontroller und Digitale Elektronik PIC 16F84A - Speichern und Ausgeben gleichzeitig


von Emilie S. (circle)


Lesenswert?

Guten Morgen,

ich wollte ein Programm für den PIC16F887 schreiben und habe mich dazu 
entschieden "Schiffe versenken" in Assembler zu programmieren.

Da ich die LEDs der LED-Matrix durch Multiplexing ansteuer, habe ich mir 
überlegt, erstmal eine kleine Matrix mit dem PIC16F84A aufzubauen.
Dies funktioniert auch soweit schon. Ich kann jede LED einzeln 
ansteuern.

Für die Koordinaten (z.B. A5, C7 etc.) habe ich jeweils eine LED-Reihe 
oben und eine links als Cursor. Um eine bestimmte Koordinate 
auszuwählen, benutze ich drei Taster (obere Reihe nach rechts, 
nebenstehende Reihe nach unten und den letzten Taster zum Bestätigen). 
Dies funktioniert ebenfalls.

Die eingegebene Koordinate müsste ja mit einem gespeicherten Wert 
verglichen werden, damit entschieden werden kann, ob ein Schiff 
getroffen wurde oder nicht (ich benutze zweifarbige LEDs, wo jede durch 
einen Port angesteuert wird).

Doch nun habe ich ein Problem:
Ich weiß nicht wie ich den EEPROM für den PIC16F84A/PIC16F887 
beschreiben, bzw. abrufen und darstellen soll.
Da ich ja nacheinander jede ausgewählte LED individuell anzeigen 
(getroffen/nicht getroffen) und gleichzeitig auch noch mit den 
Cursor-LEDs ansteuern möchte.

Ist das überhaupt zu realisieren?
Und kann die Matrix überhaupt problemlos durchlaufen und gleichzeitig 
noch ein Taster abgefragt werden?
Wenn ja, könntet ihr mir vielleicht einen Tipp geben?


MfG circle

von Carsten S. (dg3ycs)


Lesenswert?

Hi,

du hast jetztnicht gesagt, wie groß die Matrix werden soll, dann hätte 
ich den 16F84 schon mal völlig ausschließen können...
ABER TROTZDEM: Lasse die Finger von diesem OPA, der war zu seiner Zeit 
nicht schlecht, ist aber seit sicher 10Jahren und mehr durch bessere und 
leistungsfähigere ersetzt.
Wenn du nicht diekt mit den dicken (und teuren) Brummern wie dem 16F887 
arbeiten willst, dann nehme zum beispiel dem zum 16F84 Pinkompatiblen 
16F628. Der hat mehr Speicher, EEPROM und kann schneller laufen.
Ausserdem ist das Verhalten des EEPROM Zugriff hier mehr mit dem des 
16F887 vergleichbar.

Aber grundsätzlich: Der EEPROM Zugriff ist bei den 16er Pics sehr 
"langsam". Zuindest der Schreibzugriff! Du musst dein Program bei diesen 
Zugriffen ein wenig "bremsen". Dies geschieht mit einem Status Bit, das 
das jeweilige Zugriffsende anzeigt.
Das lesen erfordert zwar keine künstlichen Programmselbstkontrolle, ist 
aber trotzdem etwas aufwendig weil du mehrere Register verändern musst, 
bevor du EINEN Wert zurückbekommst.

Dies ist natürlich genau das was man bei einer Sache wie einer Matrix 
verhindern will. Natürlich, fast alles bis auf das ausbremsen beim 
Schreiben kann man so implementieren das man keine Probleme mit dem 
Display hat, aber das ist wirklich aufwendig.
DAHER: Schaue ob du nicht auf das EEPROM verzichte kannst, bzw. nutze 
dieses nur als statischen Speicher, aus dem zum Beispiel beim 
Programmstart oder unterbrechungen einmal die Daten ins RAM gelesen 
werden.

Ist dir das RAM zu klein und du willst eine richtig große Matrix bauen, 
dann würde ich statt auf den EEPROM einfach auf dem Programspeicher 
zugreifen und dort eine oder mehrere Tabellen vorkonfigurieren, diese 
zur Laufzeit abändern und dann im Ausgabeteil (Matrixdaten) einfach mit 
"Table Read" die Daten holen. Das ändern des Programmspeicher ist aber 
auch gefährlich, denn wenn etwas schief geht ist wenn es ganz dumm läuft 
das Programm gecrasht. Geht auch nicht mit allen PICs.

Zum Zugriff auf das EEPROM (und dem Programmspeicher) findest du die 
vorgehensweise im jeweiligen Datenblatt und EEPROM zugriff. Das ganze 
incl. Codebeispiel. Beim PIC16F887 zum Bleistift ab seite 113.

Als Ergänzung hier auch mal Codeschnipsel die ich gerade aus einem 
Jahrealten Programm von mir gezogen habe. (Eine Morsebake wem das was 
sagt)
Dort stand ein zu morsender Text im EEPROM, einmal mit PC Anwendung 
eingestellt, beim Einschalten wurde der dann jeweils ins RAM Kopiert und 
im programmierten Abstand gemorst. ISt nicht die eleganteste Lösung aber 
funktioniert. auf jeden Fall. Im Ergebniss hast du dann eine Kopie des 
eingestellten EEPROM Bereiches im RAM
1
; Hier werden die Daten ins RAM Kopiert
2
; Die Daten stehen dann später ab Adresse Im Ram
3
4
  movlw   0x30          ; Variablen
5
  movwf  zähl          ; mit Vorgabewerten
6
  movwf   FSR           ; Initialisieren 
7
    movlw  0x00       
8
  movwf  e2prom        ; EEPROM Adresse 0 Vorladen (erste Speicherzelle
9
10
lesen
11
  bsf  PORTB, 6       ; Debug LED an
12
  movf  e2prom, 0
13
  call   EERead
14
  incf  e2prom, 1
15
  movwf   INDF
16
  incf  FSR, 1
17
  bcf  PORTB, 6        ;Debug LED aus
18
19
  decfsz  zähl, 1
20
  goto lesen
21
22
;************************************************************
23
;* SUB EEREAD
24
;* Inhalt der Zelle deren Adresse in W steht nach W kopieren 
25
;***********************************************************
26
EERead 
27
        BSF    STATUS, RP0     ; EEADR liegt in der Bank 1  
28
        MOVWF  EEADR           ; schreibe die Adresse in EEADR  
29
        BSF    EECON1, RD      ; EEPROM Leseprozeß starten  
30
        MOVF   EEDATA, W       ; Die Daten der EEPROM Zelle nach W  
31
        incf   EEADR, 1
32
        BCF    STATUS, RP0     ; Bank 0  
33
        return 
34
        end

Einen Code mit Schreibzugriff müsste ichjetzt raussuchen, sind alle auf 
der alten Archivplatte. Steht aber alles ausführlich im Datenblatt.

Einen kompletten Code für eine LED Matrix mit Tastenabfrage und Table 
REad habe ich auch noch, aber den müsste ich auch noch raussuchen, kann 
ich aber gernem machen, den gibts aber dann nur per Mail.

Zur Umsetzung allegemein:
Du musst ja folgende Daten Speichern und Anzeigen:
1x Wo sind die Schiffe, Wo wurde getroffen, wo wurde danebengechossen.
Eine DUO Led reicht dafür ja aus, wenn dir der Unterschied zwischen den 
drei Farben reicht. (Z.B. Grün-"noch kein Schuss",  GrünRot "DANEBEN" 
und ROT "Treffer") Im "Programmiermodus halt nur ROT für die Schiffe)
Oder man nimmt für "unprobiert" Grün und schaltet bei einem Nichttreffer 
die LED ganz aus, dann könnte man am ende des Spiels die 
nichtgetroffenen Schiffsteile von den Getroffenen abziehen. Gefällt mir 
fast noch besser!

Das könnte man so machen:
Die jeweiligen LED Farben betrachtet man als getrennte Matrixen, also so 
als würde man für Rot und Grün (oder gelb..) eine eigene Matrix bauen.
Den Inhalt dieser Matrixen bildet man im Speicher dann in Tabellen nach.

Dazu brauchst du dann noch eine Tabelle in der die Schiffe abgelegt 
sind.
Ich würde das dann als erste Idee so verwirklichen das ich Spaltenzahl 
so lege das ich acht, (oder ein ganzes Vielfache von acht habe).Habe ich 
zum Beispiel zum einstieg nur acht Spalten, dann stellt je ein Bit eine 
LED dar.
Meine Tabellen haben dann jeweils soviele Bytes wie ich Zeilen habe.
In der ersten Tabelle speichere ich dann die Position der Schiffe. 
(0kein Schiff, 1 hier ist ein Teil von einem Schiff)
Tabelle zwei enthält die ungetesteten Felder (1 Ungetestet, 0= Schuss), 
tabelle drei = Treffer.

Im eigendlichen Programm schließlich hast du dann zwei Teile, einmal die 
Eingabe und berechnung der Spielzüge, quasi den Spielablauf - und die 
Darstellung des Bildschirms.

Ich würde im Spiel dann die Tastenabfrage und kontrolle der 
Ergebnisse/Manipulation der Tabellen in einer Schleife laufen lassen.

Daneben würde ich den internen Timer mittels Vorteiler so auslegen das 
alle paar ms ein Interrupt ausgelöst wird. In der Interruptschleife wird 
dann jeweils die nächste Zeile deines "multiplexbildes" gechrieben.
Wie oft nun der Interrupt ausgelöst wird hängt von deiner MAtrix ab.

Hast du mehr als 8 Spalten, dann sind deine Tabellen jeweils ein Bypte 
pro 8 Spalten (* Zeilen) groß.

Wird ein Spielzug gemacht, dann wird zuerst überprüft ob der Wert für 
dieses Feld noch in der "FREI" TABELLE gesetzt ist (Grüne LED noch an?). 
Ist er das nicht, so ist der Zug ungültig, ist der Wert aber noch 
gesetzt wird dieser nun gelöscht - Anschließend wird in der 
Schiffstabelle geprüft ob dieser Wert dort gesetzt ist. Ist er das dann 
wird auch der der Wert in der Trefferdatenbank gesetzt.
Am ende des Spiels kann dann die Anfrage der nichtgetroffenen Felder 
dadurch realisisert werden das einfach alle Felder der "Grün" Tabelle 
mit den Feldern der Schifftabelle VerODERt werden (Ergebniss dann in die 
ROT Tabelle eintragen).

Alternativ könnte man natürlich auch eine Spaltenanzahl von 4*X als 
sinnvoll annehmen und die Werte für "Rot" und "Grün" jeweils im oberen 
und unteren Nibble unterbringen.  Die Positionstabelle für die Schiffe 
kann man dann genauso aufbauen wobei dann jeweils ein Nibble leer 
bleibt, oder man rechnet die um.

ISt aber nur ein Vorschlag zur Realisierung! Sicher gibt es noch andere 
Wege und Ideen!

Gruß
Carsten

von usuru (Gast)


Lesenswert?

Vom 16F84 kann ich nur abraten, das ist der Urvater aller Flash-PICs.

Statt des 16F628 würde ich zum 16F886/887 raten, der hat neben 8 kWord 
Flash vor allem mehr RAM (368) und mehr EEPROM (256) als der 16F826, der 
auch schon 8 Jahre auf dem Buckel hat. Und die vielen Pins des 16F887 
sind bei "Schiffe versenken" auch nicht zu verachten.

von Carsten S. (dg3ycs)


Lesenswert?

usuru schrieb:
> Vom 16F84 kann ich nur abraten, das ist der Urvater aller Flash-PICs.
>
> Statt des 16F628 würde ich zum 16F886/887 raten, der hat neben 8 kWord
> Flash vor allem mehr RAM (368) und mehr EEPROM (256) als der 16F826, der
> auch schon 8 Jahre auf dem Buckel hat. Und die vielen Pins des 16F887
> sind bei "Schiffe versenken" auch nicht zu verachten.

Also grundsätzlich hast du natürlich recht, der 16F887 IST der mit mehr 
Möglichkeiten. Vom TE (eher von der TEin?) ist deshalb ja ausdrücklich 
auch der 16F887 als Zielprozessor vorgesehen.

Der 16F84 soll anscheinend nur für erste Gehversuche dienen.
Ich habe jetzt einfach mal vermutet, das dies evtl. entweder aufgrund 
des wesentlich geringeren Kaufpreises oder dem geringeren 
Hardwareaufwand so vorgesehen war. Immerhin kostet der 887 je nach 
Händer das doppelte bis das dreifache vom F84. Das tut schon weh wenn 
man dem Schrottet.
Daher meine Empfehlung für den 16F628. Der ist Pinkompatibel mit dem 
F84, das anscheinend schon vorhandene erste Testsystem kann ohne 
Änderung weiterbenutzt werden, der Code muss auch nur minimal verändert 
werden und er ist auch noch billiger als der F84, damit deutlich 
billiger als der F887.

Gruß
Carsten

von usuru (Gast)


Lesenswert?

> Immerhin kostet der 887 je nach Händer das doppelte bis das
> dreifache vom F84.

?????

Reichelt-Preise von heute (DIP-Gehäuse)

16F84A-20  3.15
16F628-20  2.75
16F887     2.65

von Emilie S. (circle)


Lesenswert?

Erstmal vielen Dank an Carsten für diese schnelle und ausführliche 
Antwort.

Den PIC16F84A habe ich nur zur Probe benutzt, da ich zum ersten Mal mit 
einer LED-Matrix und somit mit dem Multiplexing arbeite.
Anfangs hatte ich auch leider nicht die erforderliche Software um den 
PIC16F887 zu brennen. Nun habe ich zwar die Software und auch schon ein 
Testprogramm geschrieben, bei der jede LED der Reihe nach kurz 
aufleuchtet, allerdings wird am PORTB nichts ausgegeben. Im Datenblatt 
steht zwar eine Initialisierung für den PORTB, doch will die einfach 
nicht funktionieren. Irgendwas mach ich da wohl noch falsch.

Die Größe der Matrix hatte ich zuerst mit 9x9 LEDs realisiert, 
hinzukommend jeweils eine extra Reihe und Spalte für die Curser-LEDs 
(Beim 16F84A eine 3x3 LED-Matrix + Cursor-LEDs).
Die Matrix habe ich nun allerdings auf 8x8 LEDs (+ Cursor-LEDs) 
reduziert, da es mit einem Byte (8 Bit) pro Spalte, bzw. Reihe leichter 
ist.

Für die Eingänge (rechts, runter und bestätigen) benutze ich PORTE,0 bis 
PORTE,2. Die Reihen (Koordinate A-H) habe ich mit dem PORTD bestimmt. 
Für getroffen (rote LEDs / Koordinate 1-8) den PORTB und für Wasser 
(grüne LEDs / Koordinate 1-8) den PORTC. Von PORTA benutze ich nur Bit 0 
und 1 für die Cursor-LEDs.

Die Idee mit den Tabellen finde ich super, allerdings wüsste ich nicht 
genau wie ich das zu programmieren habe.
Es wäre sehr nett, wenn du mir den Programmausschnitt schicken würdest.

MfG
circle

von Emilie S. (circle)


Lesenswert?

> Nun habe ich zwar die Software und auch schon ein
> Testprogramm geschrieben, bei der jede LED der Reihe nach kurz
> aufleuchtet, allerdings wird am PORTB nichts ausgegeben. Im Datenblatt
> steht zwar eine Initialisierung für den PORTB, doch will die einfach
> nicht funktionieren. Irgendwas mach ich da wohl noch falsch.

Ein Problem weniger.
Das mit PORTB habe ich nun doch noch hinbekommen (Google sei Dank XD). 
=)


MfG
circle

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.