Напишите UART на PIC18

Мне нужна помощь с коммуникацией uart, которую я пытаюсь реализовать в своей симуляции Proteus. Я использую PIC18f4520 и хочу отображать на виртуальном терминале значения, рассчитанные микроконтроллером.

Вот снимок моего дизайна на Proteus

Прямо сейчас мой код UART выглядит так:

#define _XTAL_FREQ  20000000
#define _BAUDRATE   9600

void Configuration_ISR(void) {
    IPR1bits.TMR1IP = 1;        // TMR1 Overflow Interrupt Priority - High
    PIE1bits.TMR1IE = 1;        // TMR1 Overflow Interrupt Enable
    PIR1bits.TMR1IF = 0;        // TMR1 Overflow Interrupt Flag
                            //   0 = TMR1 register did not overflow
                            //   1 = TMR1 register overflowed (must be cleared in software)
    RCONbits.IPEN   = 1;        // Interrupt Priority High level
    INTCONbits.PEIE = 1;        // Enables all low-priority peripheral interrupts
    //INTCONbits.GIE  = 1;          // Enables all high-priority interrupts
}

void Configuration_UART(void) {
    TRISCbits.TRISC6 = 0;
    TRISCbits.TRISC7 = 1;

    SPBRG = ((_XTAL_FREQ/16)/_BAUDRATE)-1;

    //RCSTA REG
    RCSTAbits.SPEN = 1;     // enable serial port pins    
    RCSTAbits.RX9 = 0;

    //TXSTA REG
    TXSTAbits.BRGH = 1;     // fast baudrate
    TXSTAbits.SYNC = 0;     // asynchronous
    TXSTAbits.TX9 = 0;      // 8-bit transmission
    TXSTAbits.TXEN = 1;     // enble transmitter
}

void WriteByte_UART(unsigned char ch) {
    while(!PIR1bits.TXIF);  // Wait for TXIF flag Set which indicates
                            // TXREG register is empty
    TXREG = ch;             // Transmitt data to UART
}

void WriteString_UART(char *data) { 
       while(*data){ 
          WriteByte_UART(*data++); 
       }
}

unsigned char ReceiveByte_UART(void) {
    if(RCSTAbits.OERR) {
        RCSTAbits.CREN = 0;
        RCSTAbits.CREN = 1;
    }
    while(!PIR1bits.RCIF); //Wait for a byte
    return RCREG;
}

И в основном цикле:

while(1) {
    WriteByte_UART('a'); // This works. I can see the As in the terminal
    WriteString_UART("Hello World !"); //Nothing displayed :(
}//end while(1)

Я пробовал другое решение для WriteString_UART, но пока ни одно из них не помогло.

Я не хочу использовать printf, потому что это влияет на другие операции, которые я выполняю с PIC, добавляя задержку. Поэтому я действительно хочу, чтобы он работал с WriteString_UART. В конце я хотел бы иметь что-то вроде «Уровень ошибок: [значение]%» на терминале.

Спасибо за помощь, и, пожалуйста, скажите мне, если что-то неясно.


person Daymov    schedule 02.08.2016    source источник
comment
Где заявлено PIR1bits?   -  person Daniel Margosian    schedule 02.08.2016
comment
Это в другой функции. Я сейчас отредактирую пост.   -  person Daymov    schedule 02.08.2016
comment
В своей программе я использую один высокий ISR. В рутине я сравниваю два значения, которыми обмениваются два PIC (используя SPI). Я бы хотел вывести результат сравнения на терминал :)   -  person Daymov    schedule 02.08.2016
comment
Какой тип PIR1bits? Покажи мне декларацию, а не определение.   -  person Daniel Margosian    schedule 02.08.2016
comment
Извините, но я не уверен, что понимаю, что вы имеете в виду.   -  person Daymov    schedule 03.08.2016
comment
Откуда берется PIR1bits?   -  person Daniel Margosian    schedule 03.08.2016
comment
PIR1 - это регистр 1 запроса периферийного прерывания. Он состоит из 8 бит. В моем случае я не использую их все. Таким образом, с помощью PIR1bits.xxxx я могу установить только один бит. (информация из таблицы pic18f4520) PIR1bits.RCIF = Бит флага прерывания приема Eusart; PIR1bits.TXIF = бит флага прерывания передачи Eusart; PIR1bits.TMR1F = Бит флага прерывания переполнения TMR1.   -  person Daymov    schedule 03.08.2016
comment
Привет, мир! должен быть нулевым завершением? Как Hello World! \ 0.   -  person Jeff    schedule 03.08.2016
comment
@ Джефф на всякий случай попробовал. Безуспешно.   -  person Daymov    schedule 03.08.2016
comment
Опорожняется лиTXREG автоматически после передачи на UART?   -  person Daniel Margosian    schedule 03.08.2016
comment
@DanielMargosian Я так не думаю. Из того, что я мог наблюдать при запуске моделирования, TXREG сохраняет последнее значение в памяти после завершения передачи в UART. Но если я добавлю строку, в которой специально сбрасываю TXREG, то она снова пуста.   -  person Daymov    schedule 04.08.2016
comment
@Tealyf, когда вы добавляете строку для сброса TXREG и очищаете ее, вы все еще видите ту же проблему?   -  person Daniel Margosian    schedule 04.08.2016
comment
@DanielMargosian Да, верю.   -  person Daymov    schedule 04.08.2016


Ответы (3)


В вашей функции WriteByte_UART() попробуйте опросить бит TRMT. В частности, измените:

while(!PIR1bits.TXIF);

to

while(!TXSTA1bits.TRMT);

Я не знаю, является ли это вашей конкретной проблемой, но существует состояние гонки из-за того, что TXIF не очищается сразу после загрузки TXREG. Другой вариант - попробовать:

...
Nop();
while(!PIR1bits.TXIF);
...

РЕДАКТИРОВАТЬ НА ОСНОВЕ КОММЕНТАРИЙ

Проблема связана с тем, что PIC18 использует два разных типа указателей на основе памяти данных и памяти программ. Попробуйте изменить свое объявление на void WriteString_UART(const rom char * data) и посмотрите, что произойдет. Вам также нужно будет изменить свое WriteByte_UART() объявление на void WriteByte_UART(const unsigned char ch).

person embedded_guy    schedule 03.08.2016
comment
Я попробовал два ваших предложения. WriteByte_UART() как всегда работает отлично. Если я попробую предопределенную функцию putsUSART("Hello World !"), у меня будет текст на терминале. Теперь, если я объявлю переменную unsigned char MyString = "Hello World\r\n";, а затем сделаю WriteByte_UART(MyString), это сработает! Почему WriteByte_UART("Hello World") не будет, это вопрос на 1 миллион долларов. - person Daymov; 04.08.2016
comment
@Tealyf, ваша проблема может заключаться в том, что отдельные адресные пространства для памяти программ и памяти данных используют разные типы указателей. Попробуйте изменить свое объявление на void WriteString_UART(const rom char * data) и посмотрите, что произойдет. Вам также нужно будет изменить свое WriteByte_UART() объявление на void WriteByte_UART(const unsigned char ch). - person embedded_guy; 04.08.2016
comment
@Tealyf, просто из любопытства, у вас были проблемы с типами указателей? - person embedded_guy; 15.08.2016
comment
да. Я изменил, как вы предложили в своем комментарии от 4 августа, и вот как я заставил его работать - person Daymov; 22.08.2016
comment
Спасибо, обновится, чтобы убедиться, что ответ записан правильно. - person embedded_guy; 22.08.2016

  1. Добавьте задержку в несколько миллисекунд после строки TXREG = ch;

  2. убедитесь, что данные указателя * в WriteString_UART (char * data) действительно указывают на строку «Hello World!».

person Oren Fivlovich    schedule 03.08.2016
comment
Боюсь, что нет. Независимо от того, на что указывает адрес *data, его значение остается 0 ... например: data 0x025E - ›*data 0x025E '\0' (адрес, значение) - person Daymov; 04.08.2016

Кажется, вы нашли решение, но причина, по которой оно не работало, до сих пор не ясна. Какой компилятор вы используете?

Я на собственном горьком опыте узнал, что C18 и XC8 по-разному используются в отношении пространств памяти. В обоих компиляторах строка, объявленная буквально как char string[]="Hello!", будет сохранена в ПЗУ (программной памяти). Они различаются способом использования строк в функциях.

Строковые функции C18 будут иметь варианты доступа к строкам в RAM или ROM (например, strcpypgm2ram, strcpyram2pgm и т. Д.). XC8, с другой стороны, выполняет эту работу за вас, и вам не нужно использовать определенные функции, чтобы выбрать, к какой памяти вы хотите получить доступ.

Если вы используете C18, я настоятельно рекомендую вам перейти на XC8, который является более новым и с которым проще работать. Если вы все еще хотите использовать C18 или другой компилятор, который требует, чтобы вы работали с пространствами памяти программ / данных, то вот два решения, которые вы можете попробовать. В таблице данных C18 указано, что putsUSART выводит строку из памяти данных в USART. Функция putrsUSART распечатает строку из памяти программы. Таким образом, вы можете просто использовать putrsUSART для печати своей строки.

Вы также можете попробовать следующее, которое заключается в копировании вашей строки из памяти программ в память данных (хотя это может быть пустой тратой памяти, если ваше приложение ограничено в памяти):

char pgmstring[] = "Hello";
char datstring[16];
strcpypgm2ram(datstring, pgmstring);
putsUSART(datstring);

В этом примере указатели pgmstring и datstring будут храниться в памяти данных. Строка "Hello" будет сохранена в памяти программы. Таким образом, даже если указатель pgmstring сам находится в памяти данных, он изначально указывает на адрес памяти (адрес "Hello"). Единственный способ указать на эту же строку в памяти данных - создать ее копию в памяти данных. Это связано с тем, что функция, принимающая строку, хранящуюся в памяти данных (например, putsUSART), НЕ может использоваться непосредственно со строкой, хранящейся в памяти программ.

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

person Asct20    schedule 25.08.2016
comment
Спасибо @ A.Saucet. Поскольку я работаю только над одной частью большого проекта с использованием C18, я не могу изменить компилятор. Я попробую то, что вы любезно предложили. Чтобы обобщить свои мысли, я также хотел найти способ использовать putsUASRT с переменной (например, напечатать другое значение счетчика). - person Daymov; 29.08.2016
comment
Итак, я пробовал putsUSARTи putrsUSART. Вот результат: char datstring[] = "Hello PIC\r\n"; char datstring2[] = "Test log\r\n"; putsUSART(datstring); - ›Я получаю Hello PIC _6 _--› Я получаю ÍŽÍšÍ˜Í † Í „Í’Í € ÏjÎj - person Daymov; 29.08.2016
comment
К сожалению, сейчас я не могу протестировать реальное оборудование, так как я только что переехал и жду доставки компонентов, но сделаю это, как только смогу. - person Asct20; 29.08.2016
comment
Вы пробовали другой предложенный мной метод? Он состоит из копирования строки ПЗУ в ОЗУ и отправки строки ОЗУ через UART с putsUSART(ramstr). Что касается вашего другого вопроса, вы можете распечатать переменную, используя putsUSART, выполнив функцию sprintf(): int value = 2; char string[16]; sprintf(string, "Value=%i", value); putsUSART(string); sprintf() работает как printf, но выводит результат в первом переданном аргументе. - person Asct20; 29.08.2016
comment
В конце концов, вчера вечером мне удалось использовать putrsUSART и putsUSART, преобразовав мое значение с itoa (datvalue,printdatvalue); . Я еще не пробовал метод с использованием копии. У вас есть представление о количестве командных циклов для копирования строки ПЗУ в ОЗУ? - person Daymov; 30.08.2016
comment
Рад слышать, что у вас все получилось! Я постараюсь посмотреть, сколько циклов инструкций потребуется, как только я получу свои компоненты, что, надеюсь, должно произойти в ближайшие пару дней. - person Asct20; 30.08.2016
comment
@Tealyf: Итак, я проверил в симуляторе MPLABX, сколько циклов инструкций занимает функция sprintf (). В следующем примере у меня 982 цикла: int value = 2; char string[16]; sprintf(string, "Value=%i", value); - person Asct20; 16.09.2016