Прерывание PIC UART не срабатывает

У меня есть PIC16F628A, который я пытаюсь прочитать с UART. Без прерываний он нормально читает первые 3 байта, но выдает ошибку OERR. Чтобы бороться с этим, я подумал, что было бы хорошо прерывание и загрузка любых полученных байтов в переменную буфера, которую можно было бы прочитать позже (кольцевой буфер типа массива char). Но прерывание не срабатывает, и у меня закончились идеи.

CMCON = 0x07;           //16F627/8 spcial function reg (RAx is port)
CCP1CON = 0b00000000;   //Capt/Comp/PWM off

OPTION_REG = 0b00000000;

T1CON = 0;
INTCON = 0;
PIR1 = 0;
GIE = 0;

PIE1 = 0;

BRGH = 1;   /* high baud rate */
SPBRG = 19200;  /* set the baud rate */

SYNC = 0; //Async
TXEN = 0; //Disable transmit
TXIE = 0; //Disable transmit interrupt
RCIE = 1; //Enable Receive interrupt
SPEN = 1; //Enable serial pins
CREN = 1; //Enable continuous receive
SREN = 0;
TX9  = ninebits?1:0;    /* 8- or 9-bit transmission */
RX9  = ninebits?1:0;    /* 8- or 9-bit reception */

PEIE = 1; //Enable external interrupt
GIE = 1; //Enable global interrupt

Я упростил прерывание до включения света:

extern interrupt isr(void)
{
    RB5 = 1;
}

Но это не срабатывает. Проект считывает сканер штрих-кода по серийному номеру и обрабатывает штрих-код. Кто-нибудь может предложить некоторую помощь?

РЕДАКТИРОВАТЬ

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

void initialize()
{
    CMCON = 0x07;           //16F627/8 spcial function reg (RAx is port)
    CCP1CON = 0b00000000;   //Capt/Comp/PWM off

    OPTION_REG = 0b00000000;

    T1CON = 0;
    INTCON = 0;
    PIR1 = 0;
    GIE = 0;
    PEIE = 0;
    PIE1 = 0;

    sci_Init(BAUDRATE ,SCI_EIGHT);// Baud set and Bit set

    TMR0 = 1000;
    T0IE = 0;
    PEIE = 1; //Enable external interrupt
    GIE = 1; //Enable global interrupt

    //Set inputs to input
    SetButtons();

    //Set relays to output
    SetRelays();

    TRISB5 = 0;

    LEDStatus = 0;
}

unsigned char sci_Init(unsigned long int baud, unsigned char ninebits)
{
    int X;
    unsigned long tmp;

    /* calculate and set baud rate register */
    /* for asynchronous mode */
    tmp = 16UL * baud;
    X = (int)(FOSC/tmp) - 1;
    if((X>255) || (X<0))
    {
        tmp = 64UL * baud;
        X = (int)(FOSC/tmp) - 1;
        if((X>255) || (X<0))
        {
            return 1;   /* panic - baud rate unobtainable */
        }
        else
            BRGH = 0;   /* low baud rate */
    }
    else
        BRGH = 1;   /* high baud rate */
    SPBRG = X;  /* set the baud rate */

    SYNC = 0; //Async
    TXEN = 0; //Disable transmit
    TXIE = 0; //Disable transmit interrupt
    RCIE = 1; //Enable Receive interrupt
    SPEN = 1; //Enable serial pins
    CREN = 1; //Enable continuous receive
    SREN = 0;
    TX9  = ninebits?1:0;    /* 8- or 9-bit transmission */
    RX9  = ninebits?1:0;    /* 8- or 9-bit reception */

    rxBuffIndex = 0;
    rxBuffRead = 0;

    return 1;
}

void sci_LoadBuffer(void)
{
    rxBuffer[rxBuffIndex] = RCREG;

    rxBuffIndex = ++rxBuffIndex % MAXBUFFER;
}

unsigned char sci_ReadBuffer()
{
    unsigned char byte;

    do
    {
        byte = rxBuffer[rxBuffRead];
    }while( byte == 0 ); //Block until valid data

    rxBuffer[rxBuffRead] = 0;
    rxBuffRead = (++rxBuffRead) % MAXBUFFER;

    return byte;
}

void interrupt isr(void)
{
    if(RCIF) sci_LoadBuffer();
    LEDStatus = 1;
}

Я знаю, что это не ВСЕ, но этого должно быть достаточно, чтобы диагностировать, почему прерывания не срабатывают. ЭТО ВСЕ, ЧТО МНЕ НУЖНО! Запуск прерываний.

Я использую MPLab с компилятором Hi-Tech C. Который из мануала автоматически сохраняет состояние и восстанавливает его при входе/выходе прерывания.

Моя реальная функция прерывания:void interrupt isr(void) { if(RCIF) sci_LoadBuffer(); LEDStatus = 1; } void sci_LoadBuffer(void) { rxBuffer[rxBuffIndex] = RCREG; rxBuffIndex = ++rxBuffIndex % MAXBUFFER; }
Извините за форматирование...
исправлено форматирование.
Я не знаком с PIC, но многие из этих типов устройств также где-то имеют глобальную маску прерывания, а иногда в самом ядре ЦП есть глобальные элементы управления включением/отключением прерываний. Конечно, если вы можете получать прерывания из других источников, тогда эта теория опровергается. Что-то думать о.
Существует множество возможных источников проблем. Переполнение стека, проблемы с синхронизацией, неправильная настройка TRISxx, неправильная скорость передачи данных (SPBRG = 19200 выглядит странно) и т. д. Можете ли вы разбить и опубликовать дамп SFR?
Народ, этот вопрос - пустая трата времени. ОП не появлялся здесь с 29 июля. Выглядит как очередной рекламный плакат.

Ответы (4)

TRISB1 необходимо установить на 1, чтобы настроить RB1 (RX) в качестве входа. Я не уверен, что такое по умолчанию, так что это может быть нормально.

Вам необходимо очистить флаг прерывания приема (RCIF), прочитав регистр приема (RCREG). Кроме того, поскольку регистр приема имеет двойную буферизацию, вам может потребоваться прочитать его более одного раза.

Таким образом, ваша процедура прерывания должна выглядеть примерно так:

extern interrupt isr(void)
{
    while (RCIF)
    {
        char ch;

        RB5 = 1;
        ch = RCREG;    // normally would go into an array and increment a counter
    }
}

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

=======================================

РЕДАКТИРОВАТЬ:

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

ch = RCREG;    // clear FIFO  
ch = RCREG;
ch = RCREG;

// then enable interruupts ...
Я упомянул, что смог получить символы из приемного буфера без прерываний, используя такой же код: unsigned char sci_GetByte() { while(!RCIF) continue; //Block until data in return RCREG; }Моя проблема заключается в том, чтобы заставить прерывания запускаться.

Хорошо, две вещи.

Во-первых, в вашей подпрограмме обработки прерывания вам обычно приходится сбрасывать флаг IF RCIF, чтобы разрешить повторное срабатывание прерывания.

Это не причина, по которой прерывание вообще не срабатывает.

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

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

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

Я не читал весь ваш длинный пост, но случайно заметил это при беглом просмотре:

внешнее прерывание isr(void)
{
    РБ5 = 1;
}

Это определенно неправильно. Я не знаю, что это за условие прерывания, но вы его не очищаете. Процессор зависнет на первом прерывании, потому что он повторно войдет в процедуру прерывания сразу после ее завершения, поскольку условие прерывания все еще активно.

Разве большинство современных PIC не очищают состояние прерывания автоматически? Я не работал с PIC16, но это кажется ненужным бременем для программиста. По крайней мере, этот код может быть автоматически сгенерирован компилятором...
@Kevin: Нет, процессор, входящий в процедуру прерывания, не очищает условие прерывания на PIC или любом другом процессоре, на который я когда-либо смотрел. Он временно отключает прерывания, но они снова включаются в конце обычной процедуры прерывания. На PIC 16 это делается с помощью инструкции RETFIE. Нет, вы не хотите, чтобы компилятор делал это за вас. В некоторых случаях важно очистить условие прерывания в правильной последовательности с другими взаимодействиями с устройством.
Интересный; TI DSP, с которым я недавно работал, и Cortex-M3, с которым я работал до этого, автоматически сбрасывали флаг прерывания. Кажется, я припоминаю, что были варианты отключить эту функцию, хотя...
@Kevin: Вы уверены, что флаг прерывания для конкретного периферийного устройства был очищен или просто установлен какой-то общий флаг на переднем фронте любого прерывания?
MSP430 также автоматически сбрасывает флаги прерывания, которые имеют один источник, и, кроме того, сбрасывает, например, прерывание передачи UART при записи буфера передачи.
@Andrekr: Некоторые прерывания PIC очищаются такими вещами, как чтение регистра приемника UART, но вам все равно нужно это сделать. Простая установка RB5 в 1 не очистит прерывание.
Бит флага RCIF доступен только для чтения и сбрасывается аппаратно. Он очищается, когда регистр RCREG прочитан и пуст. Спецификация: PIC16F877A Страница: 117.

Вы пытались посмотреть на сигнал RS-232 на осциллографе, чтобы убедиться, что скорость передачи данных правильная? Попробуйте передать несколько символов от PIC, чтобы убедиться, что UART имеет правильную настройку скорости передачи данных.