将可变字符打印到 UART 不起作用,常量工作正常

电器工程 微控制器 图片 C 微芯片
2022-01-16 13:54:06

我对 PIC18F27K40 微控制器上的 XC8 有一个相当奇怪的问题。在 PIC16F1778 上它可以工作我已经定义:

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

在我的main循环中,当我调用uart_putch('a');时,这工作正常。但是,当我定义const char c = 'a';和调用时uart_putch(c);,它不起作用。它打印了一些东西,虽然不是a- 我认为它们是0x00我从中得到的字符hexdump -x /dev/ttyUSB0这不是我计算机上的串行端口的问题;我用示波器看了看,信号不同(左边有效,右边无效):

在此处输入图像描述

代码很简单:

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

什么也不起作用是使用我认为相关的任何字符串函数(puts,printf等) - 所以在这个问题中,我用字符做了一个最小的工作示例。

当我使用变量时生成的程序集c有:

_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

并且它在_main块中有一个常数:

    movlw   (061h)&0ffh 
    call    _putch

我正在使用 MPLAB XC8 C 编译器 V1.41(2017 年 1 月 24 日),部分支持版本为 1.41。

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

非常感谢任何帮助来完成这项工作。

2个回答

你的程序很好,这是 PIC18F27K40 上的一个错误。

请参阅http://ww1.microchip.com/downloads/en/DeviceDoc/80000713A.pdf

使用 XC8 编译器 V1.41 和 mplabx IDE,选择 XC8 Global options / XC8 linker 并选择“Additional options”,然后+nvmreg在 Errata 框中添加,一切都会好的。

摘自链接文档,关键字标记为粗体:

TBLRD 要求 NVMREG 值指向适当的内存

受影响的 PIC18FXXK40 器件的硅版本不正确地要求设置寄存器NVMREG<1:0>中的位以访问各种存储区域。当用户定义const 类型并且编译器使用指令从程序闪存 (PFM) 中检索数据时,该问题在编译的 C 程序中最为明显。当用户在 RAM 中定义一个数组时,该问题也很明显,编译器为其创建启动代码,在之前执行,该代码使用指令从 PFM 初始化 RAM。NVMCONTBLRDTBLRDmain()TBLRD

const 字符存储在程序内存(闪存)中,看起来编译器看到您没有将其用作变量(因为它永远不会更改)并将其优化到程序内存中,无论您是否使用 const。

尝试将其声明为volatile char c= 'a';. 这将迫使它存储在 SRAM 而不是闪存中。

为什么这很重要?

在 PIC18 上,使用带有奇数字节(如您的情况)的 db 指令(databyte 在程序存储器中存储一个字节)将自动用零填充它。这种行为与 PIC16 的行为不同,这可能是它适用于其中一个而不适用于另一个的原因。由于这个原因,存储在闪存中的字符串或字符也不能与任何标准字符串函数一起使用,例如 strcpy 或 printf。将某些内容存储在程序存储器中并不是自动类型安全的。

根据程序集,很明显加载了错误的 8 个字节。这是 0x00,所以它正确地发送了 0x00(正如你已经彻底确认它正在做的那样)。

这些天来,通过疯狂的编译器优化可能很难预测你会得到什么,所以我不确定这是否会奏效。volatile 技巧应该可以工作,但如果你真的想将它存储在闪存中,试试这个:

TXREG = data & 0xff;

或者可能

TXREG = data & 0x0ff;

我知道从理论上讲,这应该什么都不做。但是我们正试图改变编译器的汇编输出来做我们想要的,而不是那种但不是我们真正想要的。

从 MPASM 用户指南:

在此处输入图像描述

我还建议您自己检查一下,以及 PDF 中的 code_pack。第 65 页。