Печать переменных символов в UART не работает, константы работают нормально

У меня довольно странная проблема с XC8 на микроконтроллере PIC18F27K40. На 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 Compiler V1.41 (24 января 2017 г.) с частичной поддержкой версии 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 $<

Любая помощь в получении этой работы будет очень признательна.

что произойдет, если вы опустите "const" и просто определите как "char c = 'a';"
Определите свой uart_putch как «uart_putch (const char& c)». Это называется "передача по ссылке".
@RohatKılıç Это С++
@tcrosley Я хотел включить это, извините. Это не имеет значения (по-прежнему не работает). Я пробовал все unsigned char, charи . const unsigned charconst char
@RohatKılıç void putch(char data)на самом деле является подписью, которую вы должны использовать для переопределения поведения stdout (см. Руководство пользователя MLAB XC8, 3.5.7).
byteTxВ вашем определении putch(), что произойдет, если вместо этого вы переименуете аргумент ? Я обеспокоен тем, что это byteможет быть определено в другом месте как тип данных. (Кажется, это вызовет диагностику компилятора, но здесь явно происходит что-то странное.) И в качестве еще одного теста, ведет ли putch(0x61)себя так же, как putch('a')? Мне интересно, читает ли инструкция чтения таблицы 8-битные или 16-битные данные. Регистр PIC W всего 8 бит, верно?
@MarkU спасибо, хорошие мысли. На данный момент у меня нет с собой чипа, но я проверил сгенерированную сборку, и при использовании нет никакой разницы, byteTxкроме того, что это имя изменено и в сборке. Использование 0x61вместо 'a'(в присваивании переменной или putchвызове) не имеет никакого значения. Сегодня днем, вернувшись домой, я попытаюсь запустить тот же код на совершенно другом процессоре, чтобы посмотреть, работает ли он там, и сравнить сборку, если она работает.
Правильно ли инициализирована ваша c-runtime? (очищен ли .bss, установлены ли .rodata и .data в правильном месте?) Также кажется, что вы не включили всю разборку _main, так как она отсутствует call _putch.
@domen ты прав, исправлено. Как проверить, правильно ли инициализирована среда выполнения? Я использую простой make-файл, который компилирует все исходники, xc8 --pass1а затем связывает все вместе с помощью простого вызова xc8.
Ассемблер меня устраивает. Как выглядит файл компоновщика? Есть ли у вас правильный размер памяти программы и адреса разделов?
@Jon, какой файл компоновщика? Я прикреплю его. (XC8 говорит, что я использовал 0,6% пространства программы и 0,5% пространства данных, поэтому я ожидаю, что это будет в первых банках.)
@MarkU, поэтому я попробовал PIC16F1778, и там то же самое работает нормально. (что делает это гораздо менее серьезной проблемой для меня, поскольку я в порядке с любым чипом, но все же мне было бы интересно узнать, как заставить работать 18F27K40 ..)

Ответы (2)

Ваша программа в порядке, это ошибка на PIC18F27K40.

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

Используйте компилятор XC8 V1.41 и mplabx IDE, выберите XC8 Global options / XC8 linker и выберите «Дополнительные параметры», затем добавьте +nvmregв поле Errata, и все будет хорошо.

Выдержка из связанного документа, ключевые слова выделены жирным шрифтом:

TBLRD требует, чтобы значение NVMREG указывало на соответствующую память

Затронутые ревизии кремния устройств PIC18FXXK40 неправильно требуют, чтобы NVMREG<1:0>биты в NVMCONрегистре были установлены для TBLRDдоступа к различным областям памяти. Проблема наиболее очевидна в скомпилированных программах на C, когда пользователь определяет константный тип , а компилятор использует TBLRDинструкции для извлечения данных из программной флэш-памяти (PFM). Проблема также очевидна, когда пользователь определяет массив в ОЗУ, для которого компилятор создает код запуска, выполняемый до main(), который использует TBLRDинструкции для инициализации ОЗУ из PFM.

константные символы хранятся в памяти программы (флэш-памяти), и похоже, что компилятор видит, что вы не используете их как переменную (поскольку она никогда не меняется) и оптимизирует ее в памяти программы независимо от того, используете ли вы константу или нет.

Попробуйте объявить его как volatile char c= 'a';. Это заставит его хранить в SRAM, а не во флэш-памяти.

Почему это важно?

На PIC18 использование директивы db (байт данных для хранения байта в памяти программы) с нечетным количеством байтов (как в вашем случае) автоматически дополняет его нулями. Это поведение отличается от поведения PIC16, возможно, поэтому оно работает на одном, но не на другом. По этой причине строки или символы, хранящиеся во флэш-памяти, также не будут работать ни с одной из стандартных функций обработки строк, таких как strcpy или printf. Сохранение чего-либо в памяти программ не является автоматически безопасным по типу.

Судя по сборке, довольно ясно, что загружаются неправильные 8 байтов. Это 0x00, поэтому он правильно отправляет 0x00 (как вы полностью подтвердили).

Может быть трудно предсказать, что вы получите с безумным количеством оптимизации компилятора в наши дни, поэтому я не уверен, что это сработает. трюк с volatile должен работать, но если вы действительно хотите сохранить его во флэш-памяти, попробуйте следующее:

TXREG = data & 0xff;

или, возможно,

TXREG = data & 0x0ff;

Я знаю, что теоретически это ничего не должно делать. Но мы пытаемся изменить ассемблерный вывод компилятора, чтобы он делал то, что мы хотим, а не что-то вроде, но не совсем то, что мы хотим.

Из руководства пользователя MPASM:

введите описание изображения здесь

Также рекомендую ознакомиться с ним самостоятельно , а также с code_pack в PDF. Страница 65.