Ловушки программы простого прерывания / замораживание процессора (USART / PIC18F / MICROCHIP / XC8)

Я использую следующее:

  • pic18f4550,
  • компилятор xc8,
  • mplab x ide v3.20,
  • модуль передатчика и приемника (протестирован основной программой без перебоев и работает).
  • Светодиоды подключены к RD0, RD1 и RD2 (1 = светится)
  • Кнопки, подключенные к RB0, RB1 и RB2 (0 = кнопка нажата) // на этом этапе не имеет значения

Судя по всему, в этом коде все работает нормально, но с прерываниями "void interrupt SerialComm(void)"
(я сделал программу без прерывания и загорается светодиод).

Основная даже не загружается; Я поставил "PORTD = 0x0F",
(строка 3 основной программы).

Таким образом, если светодиод загорается, это означает, что работает как минимум 3-я строка основной программы.
(по крайней мере, до этой строки, но светодиод не работал).

Есть ли какой-нибудь регистр, который я должен сначала отключить, который я пропустил из-за этого?
Кроме того, я следил за большинством руководств, может быть что-то я пропустил? Я видел много программ с #pragma, но не уверен, что мне нужно тогда, когда я использую компилятор XC8.

    /*
 * File:   transmit.c
 * Author: steve 
 *
 * Created on September 25, 2016, 12:36 AM
 */
#define _XTAL_FREQ 48000000

#include <xc.h>
#include <pic18f4550.h>
void DelayMs(int x);
char ButtonsChecker();
char ButtonsCheckValue = 0; //returned value
char data_received = 0;


void main(void) {
    TRISB = 0x0F;
    TRISD = 0b00000000;

    TRISCbits.TRISC2 = 0; //TXD Power
    TRISCbits.TRISC6 = 0; //RC6 
    TRISCbits.TRISC7 = 1; //RC7
    PORTCbits.RC2 = 1;

    RCSTA = 0x90;
    TXSTA = 0x20;
    SPBRG = 77;

    RCREG = 0;
    RCIF = 0;


    PORTDbits.RD0 = 1;
    PORTDbits.RD1 = 1;
    PORTDbits.RD2 = 1;

    RCIE = 1;
    TXIE = 0;

    PEIE = 1;
    GIE = 1;

    while (1) {

        while (ButtonsChecker()) {

            TXREG = ButtonsChecker();
        }


        //while (!TRMT); // waiting for a whole data frame to be ready for a transmission
        //TXREG = PORTB;

        //while (!RCIF); // waiting for a whole data frame to be received
        //PORTD = RCREG;

    }
}

void DelayMs(int x) {
    while (x > 0) {
        __delay_ms(1);
        x--;
    }
}

char ButtonsChecker() {
    if (PORTBbits.RB0 == 0) {
        ButtonsCheckValue = 1;
    } else if (PORTBbits.RB1 == 0) {
        ButtonsCheckValue = 2;
    } else if (PORTBbits.RB2 == 0) {
        ButtonsCheckValue = 4;
    }//else if (PORTBbits.RB3 == 0) {
        // ButtonsCheckValue = 8;}
    else ButtonsCheckValue = 0;

    return (ButtonsCheckValue);
}


void interrupt ReceiveData() {
    if (RCIF == 1) {
        RCIF = 0;
        ~PORTDbits.RD1;
    }
}

person Steve Quek    schedule 16.01.2017    source источник
comment
Вы зарегистрировали прерывание в таблице векторов прерываний?   -  person Lundin    schedule 16.01.2017
comment
Вначале вы делаете PORTD = 0x0F, но внезапно позже это заменяется PORTD = 0x01. Что подключено к PORTD.0 (lsb bit)? Проверьте, есть ли напряжение на этом контакте (вероятно, вы его найдете, так что вы в основном входите в while ()).   -  person linuxfan says Reinstate Monica    schedule 16.01.2017
comment
@linuxfan на самом деле есть 3 контакта, подключенных к светодиодам, если бит 0 = 1, один из светодиодов загорится, бит 1 и бит 2 делают то же самое, поэтому 0x0F все 3 загораются, но если основная программа запускается идеально, 0x0F будет перезаписан очень быстро на 0x01 в цикле while, поэтому светодиод в RD0 загорится, а остальные погаснут, но вместо этого ничего не включается, поэтому я предполагаю, что это созданная мной функция прерывания. // то, что я думаю, что-то захватывает процессор в созданную мной функцию прерывания, но я не знаю, что   -  person Steve Quek    schedule 16.01.2017
comment
@Lundin Я прочитал руководство XC8 C Compiler о написании функции прерывания, и в этой части говорится, что код, сгенерированный компилятором, будет помещен по адресу вектора прерывания, который будет выполнять эту функцию после любого переключения контекста, которое требуется. вы имеете в виду это?   -  person Steve Quek    schedule 16.01.2017
comment
Я не знаю этой конкретной части, но почти каждый микроконтроллер имеет таблицу векторов прерываний, которая представляет собой фрагмент флэш-памяти, в котором MCU ищет адреса ваших ISR. Этот фрагмент флэш-памяти может быть либо автоматически запрограммирован сценарием компоновщика / компоновщика, либо вручную программистом в виде массива указателей на функции, размещенных по заданному адресу. Если эта область векторной таблицы не запрограммирована должным образом с адресом вашего ISR, и прерывание включено + запущено, MCU попытается перейти на какой-то мусорный адрес, и тогда может случиться что угодно. Это очень частая ошибка.   -  person Lundin    schedule 16.01.2017
comment
@lundin Многие PIC имеют только один вектор IRQ (в позиции 4, если я хорошо помню), поэтому любой размещенный там код автоматически будет обработчиком прерывания; этот обработчик должен проверить, что было причиной прерывания, и соответствующим образом сбросить флаги. Кажется, что компилятор XC8 справляется с этим, когда используется void interrupt ... ().   -  person linuxfan says Reinstate Monica    schedule 16.01.2017


Ответы (1)


Все выглядит нормально. за исключением того, что вы не включили прерывания TX и RC. Поэтому добавьте при инициации USART:

PIE1bits.RCIE = 1;
PIE1bits.TXIE = 1;

В конце раздела инициации также добавьте:

INTCONbits.GIE = 1; 

... чтобы разрешить глобальные прерывания.

person GJ.    schedule 16.01.2017
comment
Боже, спасибо, не могу поверить, что забыл об этом, я на самом деле повторно использовал этот старый шаблон, который сделал довольно давно, на самом деле в моей предыдущей версии было что-то подобное, но с отключенным битом TXIE, просто чтобы проверить принимающую часть прерывания, я попробую еще раз завтра - person Steve Quek; 16.01.2017
comment
Я действительно обновил код, где это была предыдущая версия той, которую вы просматривали, с включенным битом RCIE, но тоже не работающей - person Steve Quek; 16.01.2017
comment
Амм? RCREG регистр должен быть доступен только для чтения, так почему вы пишете в него? А RCIE - это битовая переменная, можете ли вы установить ее таким образом? - person GJ.; 16.01.2017
comment
О, я видел какой-то учебник, в котором говорится, что установка RCREG = 0; - это сброс регистра, заставляющий его быть равным 0 при запуске или что-то в этом роде, и способ, которым я установил RCIE, может быть благодаря xc.h или файлу pic18f4550.h, по-видимому, некоторые люди на YouTube тоже делали это, так как я не вижу ошибки при создании файлов проекта, я думаю, это можно инициализировать таким образом? - person Steve Quek; 16.01.2017
comment
@Steve Quek: Правильный метод сброса буфера RCREG - это чтение из него! - person GJ.; 17.01.2017
comment
о, я пробовал их сегодня раньше, но все еще не работает, я также проверил адрес программы (кодирования сборки): тот, который перехватывает процессор, и тот, который без программы прерывания. Программа с прерыванием начинается примерно с 0x0000 до (длина программы), а программа без прерывания перескакивает с нуля на 7000H (примерно) и начинается оттуда, извините, но мое программирование на ассемблере все еще очень слабое. - person Steve Quek; 17.01.2017
comment
@Steve Quek: вектор прерывания с высоким приоритетом начинается с адреса 0x0008, поэтому проверьте содержимое этого адреса! Запуск прерывания по этому адресу. Что касается ошибок RC, проверьте бит RCSTA.OERR в основном цикле, если он установлен, затем прочитайте RCREG или очистите бит RCSTA.CREN и снова установите бит RCSTA.CREN. Есть ли у вас подтягивающий резистор на контакте RC? - person GJ.; 17.01.2017
comment
очевидно, по адресу 0x0008 есть метка ReceiveData, поэтому я предполагаю, что она загружена в вектор прерывания, и, согласно отладчику, бит RCSTA.OERR всегда равен 0; // я использую пошаговый режим в отладчике из первой строки памяти программы - person Steve Quek; 17.01.2017
comment
о да, и около PORTC контактов порт RC6 подключается к передающему контакту модуля передатчика, RC7 принимает контакт в модуле приемника, а RC2 предназначен для управления питанием Transmitter, потому что я могу захотеть выключить его на некоторое время, но кроме этого ничего не подключено, никакая другая цепь не создается и не подключается к PORTC. - person Steve Quek; 17.01.2017
comment
@Steve Quek: где-нибудь в основном цикле поместите предложение RCIF = 1; и включите точку останова на первом предложении в прерывании. После выполнения RCIF = 1; программа должна немедленно перейти к прерыванию, если прерывания инициированы правильно! - person GJ.; 17.01.2017
comment
ну, я попробовал, и я получил Launching Initializing simulator User program stopped User program running, и он не перешел в ту часть, программа просто зависла там, я оставил ее там примерно на 15 минут, скорее всего, она не дойдет, в симуляции это продолжает выходить: W0004-CORE: Watchdog Timer has caused a Reset. W9201-UART: Write attempted to a full FIFO buffer, data lost. - person Steve Quek; 19.01.2017