9
\$\begingroup\$

I have a rather strange issue with XC8 on a PIC18F27K40 microcontroller. On a PIC16F1778 it works. I have defined:

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

When, in my main loop, I call uart_putch('a');, this works fine. However, when I define const char c = 'a'; and call uart_putch(c);, it does not work. It prints something, though not an a - I think they are 0x00 characters, which I get from hexdump -x /dev/ttyUSB0. This is not an issue with the serial port on my computer; I looked with a scope and the signal is different (left works, right does not):

enter image description here

The code is simple:

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

What does not work either is using any of the string functions (puts, printf, etc.), which I think is related - so in this question I made a minimal working example with characters.

The generated assembly when I use a variable c has:

_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

And with a constant it has in the _main block:

    movlw   (061h)&0ffh 
    call    _putch

I'm using MPLAB XC8 C Compiler V1.41 (Jan 24 2017), with part support version 1.41.

The relevant parts of my Makefile:

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 $<

Any help to get this working would be very much appreciated.

\$\endgroup\$
12
  • 1
    \$\begingroup\$ Define your uart_putch as "uart_putch (const char& c)". This is called "passing by reference". \$\endgroup\$ Jan 29, 2017 at 18:40
  • 1
    \$\begingroup\$ @RohatKılıç That is C++ \$\endgroup\$ Jan 29, 2017 at 19:57
  • 1
    \$\begingroup\$ @tcrosley I meant to include that, sorry. It does not make a difference (still doesn't work). I tried all unsigned char, char, const unsigned char and const char. \$\endgroup\$
    – user17592
    Jan 29, 2017 at 21:22
  • 1
    \$\begingroup\$ In your definition of putch(), what happens if you rename the argument byteTx instead? I'm concerned that byte might be defined elsewhere as a data type. (Seems like that would generate a compiler diagnostic, but clearly something strange is going on here.) And as another test, does putch(0x61) misbehave the same way as putch('a')? I'm wondering whether the table read instruction is reading 8-bit or 16-bit data. PIC W register is only 8 bits though, right? \$\endgroup\$
    – MarkU
    Jan 30, 2017 at 3:48
  • 2
    \$\begingroup\$ @MarkU so I tried on a PIC16F1778 and there the same thing works fine. (which makes it a much less bad problem for me as I'm fine with either chip, but still I would be interested to know how to get the 18F27K40 to work..) \$\endgroup\$
    – user17592
    Jan 30, 2017 at 15:57

2 Answers 2

3
\$\begingroup\$

Your program is fine, It's a bug on the PIC18F27K40.

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

Use XC8 compilier V1.41 and mplabx IDE, select XC8 Global options / XC8 linker and select "Additional options", then add +nvmreg in the Errata box and all will be fine.

Excerpt from the linked doc, keywords marked bold:

TBLRD requires NVMREG value to point to appropriate memory

The affected silicon revisions of the PIC18FXXK40 devices improperly require the NVMREG<1:0> bits in the NVMCON register to be set for TBLRD access of the various memory regions. The issue is most apparent in compiled C programs when the user defines a const type and the compiler uses TBLRD instructions to retrieve the data from program Flash memory (PFM). The issue is also apparent when the user defines an array in RAM for which the complier creates start-up code, executed before main(), that uses TBLRD instructions to initialize RAM from PFM.

\$\endgroup\$
2
\$\begingroup\$

const chars are stored in program memory (flash), and it looks like the compiler is seeing that you are not using it as a variable (since it never changes) and optimizing it into program memory regardless of if you use const or not.

Try declaring it as volatile char c= 'a';. This will force it to be stored in SRAM rather than flash.

Why does this matter?

On PIC18s, using the db directive (databyte to store a byte in program memory) with an odd number of bytes (like in your case) will automatically pad it with zeros. This behavior differs from that of the PIC16, which is probably why it works on one but not the other. Because of this reason, strings or chars stored in flash memory also will not work with any of the standard string functions, such as strcpy or printf. Storing something in program memory is not automatically type safe.

Based on the assembly, it is pretty clear that is loading the wrong 8 bytes. Which is 0x00, so it correctly sending 0x00 (as you've thoroughly confirmed it is doing).

It can be difficult to predict what you'll get with the insane amount of compiler optimization these days, so I am not sure if this will work. the volatile trick should work, but if you really want it stored in flash, try this:

TXREG = data & 0xff;

or possibly

TXREG = data & 0x0ff;

I know that in theory, this should do nothing. But we are trying to change the assembly output of the compiler to do what we want, and not sort of but not really what we want.

From the MPASM Users Guide:

enter image description here

I also recommend checking it out yourself, as well as code_pack, in the PDF. Page 65.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.