У меня есть два 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
?
Вы используете компилятор, поэтому понятия не имеете, сколько циклов занимает цикл опроса. В ассемблере вы могли сократить это число до 3 циклов, но вы отказались от права считать циклы, когда написали код на языке высокого уровня.
Однако реальная проблема заключается в общем подходе. Просить код поймать короткий сбой — не лучшая идея. Даже если бы вы могли гарантировать, что время цикла меньше, чем время сбоя, теперь вы не можете включать прерывания во время ожидания. Позже это может создать архитектурные проблемы.
Гораздо лучше использовать аппаратное обеспечение, которое у вас уже есть, чтобы поймать сбой, а затем проверить это аппаратное обеспечение прошивкой. Проще всего было бы подключить сбой к одной из линий INTx, а затем искать флаг INTxIF. Контакты обнаружения изменения также будут работать, но имейте в виду, что флаг устанавливается на обоих краях, и вам нужно сбросить несоответствие, чтобы сбросить условие.
Последняя инструкция это безусловная ветвь, верно? Обычно ветки и доступ к памяти (выполненные в первой инструкции, верно?) занимают примерно в 2-3 раза больше времени для выполнения, поэтому в вашем случае выполнение этих инструкций может занять около 16 циклов, что составляет 400 нс (в лучшем случае). ).
while(!SSPSTATbits.BF);
.Настоящая причина заключалась в том, что я был идиотом. Мой DisableInterrupts
макрос сделал INTCONbits.GIE=1
.
Я нашел это, переключив булавку во время ожидания:
while(!PORTBbits.RB1)
{
LATBbits.LATB0 = 0;
LATBbits.LATB0 = 1;
}
Я заметил, что переключение через равные промежутки времени прекратилось, и когда импульс уведомления попадает в одно из этих окон, он остается незамеченным.
Кортук
Маженко