Что не так с этим обнаружением смены контакта PIC?

У меня есть два PIC18F4620, подключенных через SPI + Slave Select + дополнительная линия IRQ. Оба контроллера управляются одним и тем же кварцевым генератором с одинаковыми настройками часов. Мастер отправляет один байт, а затем ждет, пока ведомый не переключит эту дополнительную линию IRQ. Продолжительность переключения составляет 4 командных цикла. Все края хорошо видны на осциллографе, и SPI-связь работает правильно, за исключением обнаружения переключения ( while(!PORTBbits.RB1);).

Это мой код отправки SPI:

    while (spi_out_msg_buffer.write_cursor > spi_out_msg_buffer.read_cursor)
    {
        DisableInterrupts;
        SSPBUF = spi_out_msg_buffer.data[spi_out_msg_buffer.read_cursor];
        LATBbits.LATB0 = 1;
        while(!PORTBbits.RB1); // wait for toggle on IRQ line
        LATBbits.LATB0 = 0;
        EnableInterrupts;
        spi_out_msg_buffer.read_cursor++;
    }

Переводится в while(!PORTBbits.RB1);две инструкции:

BTFSS PORTB, 1, ACCESS
BRA 0x188

Я вставил эту B0строку в целях отладки, вы можете увидеть ее в самом низу этой временной диаграммы:

выбор времени

Вы можете видеть переключение линии IRQ (вторая снизу) и то, как она остается незамеченной, потому что B0линия отладки остается высокой. Когда я останавливаю выполнение через ICD, оно зависает внутри файла while. Стоит отметить, что обычно он работает несколько байтов, а затем останавливается, как вы можете видеть здесь:

все время

Я измерил, что импульс на самом деле составляет 4 цикла команд (= 16 циклов PLL = 4 тактовых цикла):

длительность импульса

Я думаю, что этого должно быть достаточно для обнаружения пульса. Даже если первый BTFSS его пропустит, потому что порт сэмплируется в начале цикла инструкций, то второй должен его получить:

еще больше времени

10 МГц -> PLL -> 40 МГц -> 10 млн инструкций в секунду -> 100 нс на инструкцию.

Разве этого не должно быть достаточно, чтобы выйти из while?

проверьте свой ассемблерный код, это может быть больше 4 инструкций, хотя в этом случае их может быть меньше.
Почему бы не использовать средство изменения уровня ряда линий ввода-вывода PIC? Это обновляет флаг (и/или запускает прерывание) при изменении значения на выводе. Это не зависит от часов инструкций и должно сделать вашу систему более надежной.

Ответы (3)

Вы используете компилятор, поэтому понятия не имеете, сколько циклов занимает цикл опроса. В ассемблере вы могли сократить это число до 3 циклов, но вы отказались от права считать циклы, когда написали код на языке высокого уровня.

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

Гораздо лучше использовать аппаратное обеспечение, которое у вас уже есть, чтобы поймать сбой, а затем проверить это аппаратное обеспечение прошивкой. Проще всего было бы подключить сбой к одной из линий INTx, а затем искать флаг INTxIF. Контакты обнаружения изменения также будут работать, но имейте в виду, что флаг устанавливается на обоих краях, и вам нужно сбросить несоответствие, чтобы сбросить условие.

Вы все еще можете считать циклы, когда используете язык высокого уровня, просто нужно немного покопаться. Я согласен, однако, использование аппаратного обеспечения было бы лучшим способом в целом.
@Kevin: Вы можете посчитать циклы, немного покопавшись, но ответ не очень значим. Он скажет вам только, что делает эта версия компилятора с этой настройкой оптимизации и другими, возможно, неочевидными параметрами. Если вам нужно гарантировать количество циклов в цикле, вы должны отключить прерывания и написать это на ассемблере.
Я отключил прерывания, как вы можете видеть в коде, и я скопировал фрагмент сборки в вопросе прямо из разборки, так что это именно то, что выполняется на PIC, не так ли?
Я уже намеренно использовал контакт B1 для линии IRQ, так что я действительно могу воспользоваться его возможностью прерывания. Однако мне очень любопытно, почему это не работает таким образом.

Последняя инструкция это безусловная ветвь, верно? Обычно ветки и доступ к памяти (выполненные в первой инструкции, верно?) занимают примерно в 2-3 раза больше времени для выполнения, поэтому в вашем случае выполнение этих инструкций может занять около 16 циклов, что составляет 400 нс (в лучшем случае). ).

Можете ли вы дать больше информации о том, как OP (оригинальный постер) может выяснить это. Как вы можете обойти это? Давайте решим проблему! Я указал в своем комментарии, что они должны проверить свой ассемблерный код.
Я не эксперт PIC18, но, согласно этому сайту, у вас есть флаг, который обновляется, когда заканчивается операция отправки/получения SPI. Попробуйте заменить эту строку на while(!SSPSTATbits.BF);.
О, действительно я не знал о том, что BRA занимает два командных цикла, но все же 400 нс должно быть достаточно или нет? Я обновил вопрос, указав, как я представляю себе наихудший случай.
@AndreKR: PORTB, это адрес, верно? Если это так, то BRFSS, вероятно, займет то же время, что и переход (поскольку MCU должен загрузить значение PORTB в регистр). Почему бы вам не попробовать мое предложение по комментарию выше, которое, я считаю, то же самое, что и @MattJenkins в первом ответе?
Из таблицы данных: все инструкции, состоящие из одного слова, выполняются в одном командном цикле, если условная проверка не верна или программный счетчик не изменяется в результате выполнения инструкции. В этих случаях выполнение занимает два командных цикла, при этом дополнительные командные циклы выполняются как NOP. Ваше предложение не имеет ничего общего с моей проблемой. MattJenkins' - это то же самое, что и предложение Олина, см. мой комментарий там.
@AndreKR: см. таблицу 24-2 руководства.
Вы имеете в виду «1 (2 или 3) циклы» BTFSS? Это 1 цикл, если условие не выполнено (бит не установлен), 2 цикла, если и только если бит установлен, поэтому он должен быть пропущен, и 3 цикла, чтобы пропустить инструкцию из двух слов (что не является BRA).
@AndreKR: Извините, я неправильно понял, я думал, что когда условие не выполняется, он должен пропустить следующую инструкцию. Пока я просматривал это руководство, я увидел некоторый ассемблерный код для записи в SPIbuf, вы можете взглянуть.
Нет проблем, на самом деле у них есть инструкция для этого, но я ею не пользовался. В любом случае, это совсем не про SPI, может быть, мне вообще не стоило упоминать SPI.

Настоящая причина заключалась в том, что я был идиотом. Мой DisableInterruptsмакрос сделал INTCONbits.GIE=1.

Я нашел это, переключив булавку во время ожидания:

while(!PORTBbits.RB1)
{
    LATBbits.LATB0 = 0;
    LATBbits.LATB0 = 1;
}

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

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

Никто из вас не мог этого знать, тем не менее +1 за ваши ответы за то, что вы указали мне на количество циклов и прерывание смены контактов.