Printing variable characters to UART does not work, constants work fine

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.


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.