Problem mit dem Explorer 16-Entwicklungsboard

Ich habe das Explorer 16-Entwicklungsboard, das mit dem PIC24FJ128GA010 ausgestattet ist. Ich habe einen C-Code geschrieben, um etwas auf das LCD zu schreiben, und danach blinkt eine LED. Ich habe den gleichen Code in Assembly geschrieben und beide Codes funktionieren. Nachdem ich die Platine mit dem Assembler-Code programmiert habe, ist mir aufgefallen, dass beim Trennen und erneuten Anschließen der Stromversorgung auf dem LCD nichts angezeigt wird. Ich muss das Gerät erneut von MPLAB X aus programmieren, aber auf dem C-Code funktioniert die Platine korrekt.

Und noch etwas Seltsames, in Assembly habe ich eine einfache Verzögerungsfunktion geschrieben, die vom Dekrementieren eines Werts und einer Verzweigung, wenn nicht Nullanweisung, abhängt. Es scheint, dass der Code in eine Endlosschleife geht, weil der Wert niemals irgendwie erhöht wird! Das habe ich beim Debuggen herausgefunden.

C-Code:

#define FOSC    (8000000ULL)
#define FCY     (FOSC/2)

#pragma config JTAGEN=OFF, GCP=OFF,GWRP=OFF , COE=OFF , FWDTEN=OFF, ICS=PGx2
#pragma config FCKSM=CSDCMD, OSCIOFNC=OFF,POSCMOD=XT, FNOSC=PRI

#include <libpic30.h>
#include <p24FJ128GA010.h>
#include <xc.h>

#define LCD_RS  PORTBbits.RB15
#define LCD_RW  PORTDbits.RD5
#define LCD_E   PORTDbits.RD4


void LCD_SendChar(char value)
{
    PORTE = value;
    LCD_RS = 1;
    LCD_E = 1;
    Nop();
    LCD_E = 0;
    __delay_ms(5);
}

void LCD_SendCmd(int cmd)
{
    PORTE = cmd;
    LCD_RS = 0;
    LCD_E = 1;
    Nop();
    LCD_E = 0;
    __delay_ms(5);
}

void LCD_SendString(char* value)
{
    while(*value != 0)
        LCD_SendChar(*value++);
}

void LCD_Init()
{
    TRISE = 0x00;
    TRISDbits.TRISD4 = 0;
    TRISDbits.TRISD5 = 0;
    TRISBbits.TRISB15 = 0;

    __delay_ms(50);

    // Function Set
    // 0   0   1   8/4 2/1 10/7    x   x | 0x20 - 0x3F
    LCD_SendCmd(0x38);

    // Display ON/OFF and Cursor
    // 0  0   0   0   1   D   U   B  |  0x08 - 0x0F
    LCD_SendCmd(0x0E);

    // Character Entry Mode
    // 0  0   0   0   0   1   1/D S  |  0x04 - 0x07
    LCD_SendCmd(0x06);

    // Clear Display
    // 0  0   0   0   0   0   0   1
    LCD_SendCmd(0x01);

    // Display and Cursor Home
    // 0  0   0   0   0   0   1   x
    LCD_SendCmd(0x02);
}

int main()
{
    LCD_Init();
    LCD_SendCmd(0x82);
    LCD_SendString("Hello World!");

    TRISA = 0x00;
    LATA = 0xAA;

    while(1) {
        LATA ^= 0xFF;
        __delay_ms(500);
    }

    return (0);
}

ASM-Code:

;.include "p24fj128ga010.inc"
.include "xc.inc"
config __CONFIG2, POSCMOD_XT & OSCIOFNC_OFF & FCKSM_CSDCMD & FNOSC_PRI 
config __CONFIG1, JTAGEN_OFF & GCP_OFF & GWRP_OFF & COE_OFF & FWDTEN_OFF & ICS_PGx2
.global _main

.text

_main:
    CALL    LCD_INIT

    MOV     #0x82, W0
    CALL    LCD_SendCmd

    MOV     #'H', W0
    CALL    LCD_SendChar

    MOV     #'E', W0
    CALL    LCD_SendChar

    MOV     #'L', W0
    CALL    LCD_SendChar

    MOV     #'L', W0
    CALL    LCD_SendChar

    MOV     #'O', W0
    CALL    LCD_SendChar

    MOV     #' ', W0
    CALL    LCD_SendChar

    MOV     #'W', W0
    CALL    LCD_SendChar

    MOV     #'O', W0
    CALL    LCD_SendChar

    MOV     #'R', W0
    CALL    LCD_SendChar

    MOV     #'L', W0
    CALL    LCD_SendChar

    MOV     #'D', W0
    CALL    LCD_SendChar

    MOV     #'!', W0
    CALL    LCD_SendChar

    CLR     TRISA
    CLR     LATA

    COM     LATA

L:  GOTO    L

LCD_INIT:
    CLR     TRISE
    BCLR    TRISD, #4
    BCLR    TRISD, #5
    BCLR    TRISB, #15

    CALL    Delay_1

    ;Function Set
    ;0   0   1   8/4 2/1 10/7    x   x | 0x20 - 0x3F
    MOV     #0x38, W0
    CALL    LCD_SendCmd

    ;Display ON/OFF and Cursor
    ;0  0   0   0   1   D   U   B  |  0x08 - 0x0F
    MOV     #0x0E, W0
    CALL    LCD_SendCmd

    ;Character Entry Mode
    ;0  0   0   0   0   1   1/D S  |  0x04 - 0x07
    MOV     #0x06, W0
    CALL    LCD_SendCmd

    ;Clear Display
    ;0  0   0   0   0   0   0   1
    MOV     #0x01, W0
    CALL    LCD_SendCmd

    ;Display and Cursor Home
    ;0  0   0   0   0   0   1   x
    MOV     #0x02, W0
    CALL    LCD_SendCmd

    RETURN

LCD_SendCmd:
    MOV     W0, PORTE
    BCLR    PORTB, #15
    BSET    PORTD, #4
    NOP
    BCLR    PORTD, #4
    CALL    Delay_1
    RETURN

LCD_SendChar:
    MOV     W0, PORTE
    BSET    PORTB, #15
    BSET    PORTD, #4
    NOP
    BCLR    PORTD, #4
    CALL    Delay_1
    RETURN

Delay_1:
    REPEAT  #0xD00
    NOP
    RETURN

Delay:
    REPEAT  #16383
    NOP
    RETURN

Delay_1s:
    MOV     #0xFFFF, W0
    MOV     W0, G
R1: DEC     G
    REPEAT  #0x3D
    NOP
    BRA     NZ, R1
    RETURN

.end

UPDATE - Das Gerät liest beim Zurücksetzen der Stromversorgung keinen programmierten LCD-Treiber (C oder Assembly) und zeigt daher nichts auf dem LCD an, aber alles andere funktioniert :\

Zeigen Sie uns Ihren Code.
Dies scheint eine schlampige Initialisierungsroutine für den LCD-Controller zu sein.
venny: Bitte schön, ich habe die Frage aktualisiert
Es funktioniert sowohl auf C als auch auf ASM, aber beim Zurücksetzen der Stromversorgung zeigt das LCD in der Baugruppe nichts an, das Gerät muss neu programmiert werden
In der ASM-Datei gibt es eine Prozedur namens Delay_1s, die das Problem verursacht und nie dekrementiert wird, ich habe keine Ahnung warum. Obwohl ein Dekrement in C als normaler DEC-Befehl beim Disassemblieren erscheint.
Da Sie bereits über funktionierenden C-Code verfügen, kann es hilfreich sein, sich die Disassemblierungsansicht für den C-Code anzusehen und zu sehen, was der Compiler generiert.
Ich bin nicht in der Lage, den gesamten Code zu disassemblieren, die Disassemblierung wird für jede Funktion separat angezeigt. :\
Ist G eine Variable? Wo wird es gespeichert?
L: GOTO L in deinem .ASM schreit für mich 'Endlosschleife', da es eigentlich zu sich selbst geht ...
@AdamLawrence Ja, es ist eine Endlosschleife am Ende des Programms.
Leute, was ich mit Endlosschleife gemeint habe, ist, dass der Code Delay_1s nie beendet wird, er wiederholt sich immer wieder, da die BRA-Anweisung immer wahr ist und niemals falsch wird.
Ich verstehe. Aber was ist G und wo befindet es sich?
Ich habe vergessen, seine Definition einzugeben: D das G ist eine 4-Byte-Position im Speicher (deklariert in der .bss-Direktive), die den Wert 0xFFFF haben sollte, und sobald es Null ist, sollte es aus dem Zweig gehen. Sie können es weglassen und stattdessen DEC W0, W1 verwenden, es wird Ihnen das gleiche Ergebnis liefern.
Meinten Sie DEC W0,W0?
Unabhängig von Quelle und Ziel spielt es keine Rolle, da die Operation niemals 0 erreicht.
Viele LCD-Module erfordern nach dem Start eine Wartezeit, die ziemlich lange dauern kann, bevor Sie Befehle senden. Können Sie einen Link zum Datenblatt des LCD-Moduls setzen?
Danke für Ihr Interesse. Es ist der Standard Hitachi HD44780. Mein Problem wurde gelöst, ich glaube, es lag an einem kurzen Verzögerungswert zwischen Befehlen, die an das LCD gesendet wurden.

Antworten (2)

Ehrlich gesagt, saugt Ihr Code. Sie müssen anhalten und einige Codierungstechniken lernen, bevor Sie fortfahren. Je länger Sie darauf bestehen, Dinge auf diese Weise zu tun, desto schmerzhafter wird es sein.

Zunächst einmal ist kein einziger Kommentar in Sicht. Das ist nicht nur offensichtlich verantwortungslos, beraubt Sie eines First-Pass-Sanity-Checks, wird Ihnen in Zukunft Arbeit bereiten, sondern ist auch geradezu unhöflich, andere zu bitten, sich das anzusehen.

Eine weitere schlechte Programmiertechnik ist das Timing per Busy-Wait:

Verzögerung_1s:
    MOV #0xFFFF, W0
    MOV W0, G
R1: DEZ G
    WIEDERHOLEN #0x3D
    NOP
    BRA NZ, R1
    ZURÜCKKEHREN

Probleme damit sind:

  1. Dies ist abhängig von der Taktrate und der Architektur. Beispielsweise unterscheidet sich der Verzögerungswert zwischen einer 24 F- und einer 24 EP-Serie, selbst bei derselben Befehlsfrequenz (die nicht mit derselben Taktfrequenz identisch ist). Auch wenn Interrupts auftreten, was bei allem anderen als einem Spielzeugprojekt der Fall ist, wird die Verzögerungszeit verlängert.

  2. Es gibt keinen Hinweis darauf, dass G gezwungen wird, sich in der Nähe des RAM zu befinden, dennoch wird der DEC-Befehl direkt auf G verwendet. Dies ist ein Fehler, der darauf wartet, passiert zu werden, wenn das Projekt wächst und G zufällig in der Nähe von RAM platziert wird.

  3. Es macht keinen Sinn, die REPEAT-Schleife zwischen dem Dekrement von G und dem Test auf 0 einzufügen. Haben Sie die Details von REPEAT nachgeschlagen, um sicherzustellen, dass es das Z-Statusbit nicht verstopft?

Ein viel besserer Weg, um eine Verzögerung von 1 Sekunde zu erreichen, ist die Verwendung eines Timers. Der Zeitgeberperiodenwert würde dann zur Erstellungszeit aus dem gewünschten Wert und der Befehlstaktrate berechnet. Die Befehlstaktrate würde mit anderen Konstanten an einer zentralen Stelle definiert, die jemand leicht überprüfen kann, wenn die Taktrate geändert oder das Projekt auf einen anderen PIC portiert wird. Eine Verzögerung von bis zu 1 s kann das Zählen mehrerer kürzerer Ticks erfordern, die von einem Timer abgeleitet werden.

Das Timing kann an verschiedenen Stellen in einem Projekt erforderlich sein, so dass es nützlich sein kann, einen periodischen Interrupt von einem Timer einzurichten und dann die Interrupt-Routine verschiedene Uhren abzuleiten oder Flags zu setzen, die vom Rest des Systems verwendet werden. 1 ms (1-kHz-Rate) ist dafür oft ein nützlicher Zeitraum. Um beispielsweise 1 Sekunde zu warten, würden Sie 1000 Ticks der globalen 1-ms-Uhr zählen. Das Timer-Setup in dieser einen Interrupt-Routine muss möglicherweise angepasst werden, wenn sich die Hardware ändert, aber der Rest des Systems verlässt sich auf die regulären 1-ms-Ticks und ist völlig unabhängig von Befehlszykluszeit, Interrupt-Verzögerungen usw.

Ihr Problem ist, dass Sie in Ihrem ASM-Code eine "Schleife" haben! Sie sollten stattdessen einen Endbefehl haben. Es scheint, dass der Endbefehl falsch platziert ist, er sollte am Ende von main stehen, NICHT am Ende der Unterroutinen.

Ohne Hängeschleife wird der PC neu gestartet, und dies ist nicht mein Problem, siehe mein Update zu der Frage