Das Drucken variabler Zeichen auf UART funktioniert nicht, Konstanten funktionieren einwandfrei

Ich habe ein ziemlich seltsames Problem mit XC8 auf einem PIC18F27K40-Mikrocontroller. Auf einem PIC16F1778 funktioniert es . Ich habe definiert:

void uart_putch(unsigned char byte) {
    while (!PIR3bits.TX1IF);
    TX1REG = byte;
}

Wenn ich in meiner mainSchleife anrufe uart_putch('a');, funktioniert das gut. Wenn ich jedoch definiere const char c = 'a';und aufrufe uart_putch(c);, funktioniert es nicht. Es druckt etwas, wenn auch kein a- ich denke, es sind 0x00Charaktere, die ich von bekomme hexdump -x /dev/ttyUSB0. Dies ist kein Problem mit der seriellen Schnittstelle meines Computers; Ich habe mit einem Zielfernrohr nachgesehen und das Signal ist anders (links funktioniert, rechts nicht):

Geben Sie hier die Bildbeschreibung ein

Der Code ist einfach:

void main(void) {
    init(); // Sets up ports and UART control registers
    while (1) {
        uart_putch('a'); // or c
    }
}

Was auch nicht funktioniert, ist die Verwendung einer der Zeichenfolgenfunktionen ( puts, printf, etc.), die meiner Meinung nach verwandt sind - daher habe ich in dieser Frage ein minimales Arbeitsbeispiel mit Zeichen erstellt.

Die generierte Assembly, wenn ich eine Variable verwende, chat:

_c:
    db  low(061h)
    global __end_of_c

_main:
    ; ...
    movlw   low((_c))
    movwf   tblptrl
    if  1   ;There is more than 1 active tblptr byte
    movlw   high((_c))
    movwf   tblptrh
    endif
    if  1   ;There are 3 active tblptr bytes
    movlw   low highword((_c))
    movwf   tblptru
    endif
    tblrd   *
    movf    tablat,w
    call    _putch

Und mit einer Konstanten hat es im _mainBlock:

    movlw   (061h)&0ffh 
    call    _putch

Ich verwende MPLAB XC8 C Compiler V1.41 (24. Januar 2017) mit Teilunterstützungsversion 1.41.

Die relevanten Teile meines Makefiles:

CC:=xc8
CFLAGS:=-I. --chip=18F27K40 -Q -Wall

SRC:=main.c uart.c
DEP:=uart.h
PRS:=$(subst .c,.p1,$(SRC))
OBJ:=main.hex

all: $(OBJ)

$(OBJ): $(PRS)
    $(CC) $(CFLAGS) $^

$(PRS): %.p1: %.c $(DEP)
    $(CC) $(CFLAGS) -o$@ --pass1 $<

Jede Hilfe, um dies zum Laufen zu bringen, wäre sehr willkommen.

Was passiert, wenn Sie "const" weglassen und einfach als "char c = 'a';" definieren?
Definieren Sie Ihren uart_putch als "uart_putch (const char& c)". Dies wird als "Übergabe durch Referenz" bezeichnet.
@RohatKılıç Das ist C++
@tcrosley Ich wollte das einschließen, sorry. Es macht keinen Unterschied (funktioniert immer noch nicht). Ich habe alle unsigned char, char, const unsigned charund versucht const char.
@RohatKılıç void putch(char data)ist eigentlich die Signatur, die Sie verwenden sollten, um das stdout-Verhalten zu überschreiben (siehe MLAB XC8-Benutzerhandbuch, 3.5.7).
Was passiert in Ihrer Definition von putch(), wenn Sie byteTxstattdessen das Argument umbenennen? Ich mache mir Sorgen, dass bytedies an anderer Stelle als Datentyp definiert werden könnte. (Scheint so, als würde das eine Compiler-Diagnose generieren, aber hier geht eindeutig etwas Seltsames vor.) Und als weiterer Test, verhält es putch(0x61)sich genauso schlecht wie putch('a')? Ich frage mich, ob der Tabellenlesebefehl 8-Bit- oder 16-Bit-Daten liest. Das PIC-W-Register hat jedoch nur 8 Bit, richtig?
@ MarkU danke, gute Gedanken. Ich habe den Chip gerade nicht dabei, aber ich habe die generierte Assembly überprüft und es gibt keinen Unterschied bei der Verwendung byteTx, außer dass dieser Name auch in der Assembly geändert wird. Die Verwendung 0x61von statt 'a'(bei der Variablenzuweisung oder dem putchAufruf) macht keinen Unterschied. Heute Nachmittag zu Hause werde ich versuchen, den gleichen Code auf einem völlig anderen Prozessor auszuführen, um zu sehen, ob er dort funktioniert, und die Assembly vergleichen, wenn dies der Fall ist.
Ist Ihre c-Laufzeit richtig initialisiert? (ist .bss gelöscht, .rodata und .data an der richtigen Stelle gesetzt?) Es scheint auch, dass Sie nicht die gesamte Disassemblierung von _mainaufgenommen haben, da sie fehlt call _putch.
@domen du hast recht, fest. Wie überprüfe ich, ob die Laufzeit korrekt initialisiert ist? Ich verwende ein einfaches Makefile, das alle Quellen mit kompiliert xc8 --pass1und dann alle mit einem einfachen Aufruf von verknüpft xc8.
Der Assembler sieht für mich gut aus. Wie sieht die Linker-Datei aus? Haben Sie die richtige Programmspeichergröße und Abschnittsadressen?
@Jon was ist die Linker-Datei? Ich werde es anhängen. (XC8 sagt, ich habe 0,6 % des Programmspeicherplatzes und 0,5 % des Datenspeicherplatzes verwendet, also würde ich erwarten, dass dies in den ersten Bänken ist.)
@MarkU, also habe ich einen PIC16F1778 ausprobiert und dort funktioniert das gleiche gut. (was es für mich zu einem viel weniger schlimmen Problem macht, da ich mit beiden Chips gut zurechtkomme, aber trotzdem würde es mich interessieren, wie ich den 18F27K40 zum Laufen bringe.)

Antworten (2)

Ihr Programm ist in Ordnung, es ist ein Fehler auf dem PIC18F27K40.

Siehe http://ww1.microchip.com/downloads/en/DeviceDoc/80000713A.pdf

Verwenden Sie XC8-Compilier V1.41 und mplabx IDE, wählen Sie XC8 Global options / XC8 linker und wählen Sie "Additional options", fügen Sie dann +nvmregdas Errata-Feld hinzu und alles wird gut.

Auszug aus dem verlinkten Dokument, Stichworte fett markiert:

TBLRD erfordert, dass der NVMREG-Wert auf den entsprechenden Speicher zeigt

Die betroffenen Siliziumversionen der PIC18FXXK40-Bausteine ​​erfordern fälschlicherweise, dass die NVMREG<1:0>Bits im NVMCONRegister für den TBLRDZugriff auf die verschiedenen Speicherbereiche gesetzt werden. Das Problem tritt am deutlichsten in kompilierten C-Programmen auf, wenn der Benutzer einen konstanten Typ definiert und der Compiler TBLRDAnweisungen verwendet , um die Daten aus dem Programm-Flash-Speicher (PFM) abzurufen. Das Problem wird auch offensichtlich, wenn der Benutzer ein Array im RAM definiert, für das der Compiler Startcode erstellt, der vor ausgeführt main()wird und TBLRDAnweisungen zum Initialisieren des RAM von PFM verwendet.

const-Zeichen werden im Programmspeicher (Flash) gespeichert, und es sieht so aus, als ob der Compiler sieht, dass Sie es nicht als Variable verwenden (da es sich nie ändert) und es in den Programmspeicher optimiert, unabhängig davon, ob Sie const verwenden oder nicht.

Versuchen Sie, es als zu deklarieren volatile char c= 'a';. Dadurch wird es erzwungen, dass es im SRAM statt im Flash gespeichert wird.

Warum ist das wichtig?

Bei PIC18s wird die Verwendung der db-Direktive (Datenbyte zum Speichern eines Bytes im Programmspeicher) mit einer ungeraden Anzahl von Bytes (wie in Ihrem Fall) automatisch mit Nullen aufgefüllt. Dieses Verhalten unterscheidet sich von dem des PIC16, weshalb es wahrscheinlich auf dem einen funktioniert, auf dem anderen jedoch nicht. Aus diesem Grund funktionieren im Flash-Speicher gespeicherte Strings oder Zeichen auch nicht mit den Standard-String-Funktionen wie strcpy oder printf. Etwas im Programmspeicher zu speichern ist nicht automatisch typsicher.

Basierend auf der Assembly ist es ziemlich klar, dass die falschen 8 Bytes geladen werden. Das ist 0x00, also sendet es korrekt 0x00 (wie Sie es gründlich bestätigt haben).

Es kann schwierig sein, vorherzusagen, was Sie heutzutage mit der wahnsinnigen Menge an Compiler-Optimierung erreichen werden, daher bin ich mir nicht sicher, ob dies funktionieren wird. Der flüchtige Trick sollte funktionieren, aber wenn Sie wirklich wollen, dass er im Flash gespeichert wird, versuchen Sie Folgendes:

TXREG = data & 0xff;

oder evtl

TXREG = data & 0x0ff;

Ich weiß, dass das theoretisch nichts bringen sollte. Aber wir versuchen, die Assembly-Ausgabe des Compilers so zu ändern, dass sie das tut, was wir wollen, und nicht irgendwie, aber nicht wirklich, was wir wollen.

Aus dem MPASM-Benutzerhandbuch:

Geben Sie hier die Bildbeschreibung ein

Ich empfehle auch , es sich selbst sowie code_pack im PDF anzusehen. Seite 65.