В процессе работы над проектом неожиданно перестало отрабатывать прерывание UART0 (раньше работало). Это преывание должно срабатывать при поступлении символов на COM-порт отладочной платы. Ниже привожу исходный код инициализации прерывания и его обработчика: Инициализация прерывания: Код (Text): DWORD UART0Init( DWORD baudrate ) { DWORD Fdiv; // PINSEL0 = 0x00000050; /* RxD0 and TxD0 */ PINSEL0 &= ~0x000000F0; /* RxD0 and TxD0 */ PINSEL0 |= 0x00000050; /* RxD0 and TxD0 */ U0LCR = 0x83; /* 8 bits, no Parity, 1 Stop bit */ // U0LCR = 0x87; /* 8 bits, no Parity, 2 Stop bit */ Fdiv = ( Fpclk / 16 ) / baudrate ; /*baud rate */ U0DLM = Fdiv / 256; U0DLL = Fdiv % 256; U0LCR = 0x03; /* DLAB = 0 */ U0FCR = 0x07; /* Enable and reset TX and RX FIFO. */ if ( install_irq( UART0_INT, (void *)UART0Handler, HIGHEST_PRIORITY ) == FALSE ) { return (FALSE); } U0IER = IER_RBR | IER_THRE | IER_RLS; /* Enable UART0 interrupt */ return (TRUE); } Обработчик прерывания: Код (Text): void UART0Handler (void) __irq { BYTE IIRValue, LSRValue; volatile BYTE Dummy; // IENABLE; /* handles nested interrupt */ IIRValue = U0IIR; IIRValue >>= 1; /* skip pending bit in IIR */ IIRValue &= 0x07; /* check bit 1~3, interrupt identification */ if ( IIRValue == IIR_RLS ) /* Receive Line Status */ { TFT_printf(10,255,"%d",IIR_RLS); LSRValue = U0LSR; /* Receive Line Status */ if ( LSRValue & (LSR_OE|LSR_PE|LSR_FE|LSR_RXFE|LSR_BI) ) { /* There are errors or break interrupt */ /* Read LSR will clear the interrupt */ UART0Status = LSRValue; Dummy = U0RBR; /* Dummy read on RX to clear interrupt, then bail out */ IDISABLE; VICVectAddr = 0; /* Acknowledge Interrupt */ return; } if ( LSRValue & LSR_RDR ) /* Receive Data Ready */ { /* If no error on RLS, normal ready, save into the data buffer. */ /* Note: read RBR will clear the interrupt */ UART0Buffer[UART0Count] = U0RBR; UART0Count++; TFT_printf(10,255,"test"); if ( UART0Count == BUFSIZE ) { UART0Count = 0; /* buffer overflow */ } } } else if ( IIRValue == IIR_RDA ) /* Receive Data Available */ { /* Receive Data Available */ UART0Buffer[UART0Count] = U0RBR; UART0Count++; //в этом месте установлена точка останова if ( UART0Count == BUFSIZE ) { UART0Count = 0; /* buffer overflow */ } } else if ( IIRValue == IIR_CTI ) /* Character timeout indicator */ { /* Character Time-out indicator */ UART0Status |= 0x100; /* Bit 9 as the CTI error */ } else if ( IIRValue == IIR_THRE ) /* THRE, transmit holding register empty */ { /* THRE interrupt */ LSRValue = U0LSR; /* Check status in the LSR to see if valid data in U0THR or not */ if ( LSRValue & LSR_THRE ) { UART0TxEmpty = 1; } else { UART0TxEmpty = 0; } } // IDISABLE; VICVectAddr = 0; /* Acknowledge Interrupt */ } Абсолютно убежден что ошибка программная. Вопрос - как ее найти и в чем может быть дело?
Как показывает практика, очень многие ошибки связаны именно с неверными частотами. У приятеля, например, такой же процессор не всегда стартовал из-за неправильной настройки PLL: он поставил слишком низкую для неё частоту. Выяснилось это, когда он сравнил значения, записываемые в регистры, с моими. Ну а у меня дисплюй работать не хотел из-за ошибки в синхронизации...
И снова возник вопрос прерывания работают, все хорошо. проблема в другом - при выводе нескольких байт на COM (UART0) внутри обработчика прерывания выводится только первый символ, после этого контроллер зависает, при этом аналогичный код в функции main() выводит всю последовательность. сначала я думал, что это из за стоп-бита, контроля четности и т.п., но убедился, что и на компьютере, и на контроллере выставлены одинаковые настройки (115200, 8 бит, контроль четности отсутствует). Подозреваю, что это как-то связано с особенностями обработки прерываний (типа обработка прерывания и вывод ведутся на один и тот же порт), но более подробной информации найти не могу. Кто-нибудь знает, в чем здесь дело?
Lecko У тамошних УАРТов есть неприятная особенность (вот не помню, скопирована она с 550, с коими они типа совместимы, или же это "личная" ошибка НХП): чтобы прерывания по готовности передатчика действительно генерировались, надо сначала разрешить их, и только потом помещать в регистр передатчика первый символ. В противном случае, если передатчик пришёл в готовность (завершил отправку символа) раньше, чем прерывания были разрешены, прерывание не произойдёт, хотя флаг готовности передатчика будет установлен. Т.е. там реализована не банальная схема "логическое И" (если установлен и флаг запроса, и разрешение прерывания, то выдать прерывание), а некий триггер. Кстати, обратите внимание и на то, что считывание регистра состояния сразу сбрасывает все флаги ошибок приёма, т.е. возможные ошибки надо фиксировать каждый раз, когда читаете этот регистр, даже если чтение производится в процессе передачи, а не приёма (чтобы посмотреть, готов ли передатчик).
h0t,SII, спасибо за ответы! h0t Насколько я понял, строчка, определяющая набор событий для порта, при которых происходит прерывание, выглядит так: Код (Text): U0IER = IER_RBR | IER_THRE | IER_RLS; /* Enable UART0 interrupt */ Очевидно, флаг IER_THRE (0x02) - это и есть флаг, отвечающий за срабатывание прерывания при передаче в порт. однако, при его удалении контроллер вообще не запускается. Код инициализации я выше уже приводил. SII я правильно понимаю, что прерывания разрешаются один раз функцией install_irq()? в таком случае на момент срабатывания они уже точно разрешены.
Мне анализировать чужой код попросту лениво, да и не пишу я на Си -- мне этот рассадник ошибок ни к чему. Прерывания разрешаются действительно этой строчкой. Для 24хх (а возможно, и для других НХПшных контроллеров), чтобы организовать вывод данных через УАРТ по прерываниям, надо делать так: - запретить прерывания от УАРТа средствами контроллера прерываний ВИК (ну или вообще все внешние прерывания маской в ПСР); - разрешить прерывания по готовности передатчика на самом УАРТе (записью бита ТХРЕ в ИЕР, что эта строчка и делает; здесь она разрешает и другие прерывания, но в данном случае это неважно); - если передатчик готов, записать в его регистр первый символ; - разрешить прерывания от УАРТа в ВИКе и идти заниматься своими делами. Когда передатчик опять станет готов, произойдёт прерывание. Лишь при выполнении такой последовательности прерывания по готовности УАРТа будут доходить до контроллера прерываний, а значит, и до процессора. В противном случае, если передатчик уже был готов на момент разрешения прерываний по его готовности, это прерывание не возникнет, и будет казаться, что УАРТ висит. В обработчике прерываний от УАРТа нужно: - запретить прерывания через ВИК (если планируется разрешить прерывания вообще, сбросив маску в ПСР); - убедиться, что прерывание вызвано готовностью передатчика, после чего сунуть ему очередной байт; - при нужде обработать другие причины прерываний; - разрешить прерывания и выйти из обработчика. Лишь тогда, когда передача полностью завершена, прерывания по готовности передатчика в УАРТе должны быть запрещены. В АРМах других фирм (во всяком случае, от АТМЕЛ и СТМ) подобной проблемы нет, там прерывания вырабатываются нормально, поэтому нет нужды запрещать прерывания посредством контроллера прерываний или общей маской процессора, достаточно лишь манипулировать маской прерываний в самом УАРТе. Здесь же это не годится из-за упомянутой проблемы, вот и приходится устраивать такие танцы с бубном... Пы.Сы. И лучше не пользуйтесь готовыми библиотеками, а пишите весь код полностью самостоятельно. Во-первых, изучите как следует железо, а во-вторых, избежите проблем, связанных с ошибками в этих самых библиотеках. Понятно, что придётся искать и устранять свои ошибки, но это проще, чем гадать о причинах неправильного поведения программы, опирающейся на чужой код. Ну а то, что библиотеки разрабатываются фирмами... там столько багов бывает, что диву даёшься: пишут-то те же самые индусские быдлокодеры.
SII Таким образом, обработчик прерываний стал выглядеть следующим образом: Код (Text): VICIntEntClear=1<<UART0_INT; //0x6 - отключаем прерывание в VIC UART_printf(0,"OK"); VICIntEnable=1<<UART0_INT; //включаем прерывание обратно 1) насколько я понимаю, этого недостаточно, потому как не работает 2) что значит "по готовности"? Как эту самую готовность обработать? Чему должен быть равен U0IIR? - IIR_PEND 0x01 - IIR_RLS 0x02 - IIR_RDA 0x04 - IIR_CTI 0x06 - IIR_THRE 0x01 сорри, если туплю, но кодить под МК только начал
Пишите на ассемблере и без всяких левых функций, созданных неизвестно кем и неизвестно как -- и всё будет работать. А заодно документацию внимательно читайте, назначение всех битов там описано достаточно внятно. Готовность передатчика -- это отсутствие символа в его буферном регистре (насколько помню, бит THRE, документации сейчас у меня под рукой нет), готовность приёмника -- наличие символа в буферном регистре.
И снова доброго времени суток! Как выяснилось, причина предыдущей проблемы, а также некоторых других - некорректная обработка вложенных прерываний. Включение обработки вложенных прерываний осуществляется макросом IENABLE, отключение, соответственно, IDISABLE, которые определены следующим образом: Код (Text): static DWORD sysreg; /* used as LR register */ #define IENABLE __asm { MRS sysreg, SPSR; MSR CPSR_c, #SYS32Mode } #define IDISABLE __asm { MSR CPSR_c, #(IRQ32Mode|I_Bit); MSR SPSR_cxsf, sysreg } Пример с сайта ARM выглядит следующим образом: Код (Text): #define IENABLE /* Nested Interrupts Entry */ __asm { MRS LR, SPSR } /* Copy SPSR_irq to LR */ __asm { STMFD SP!, {LR} } /* Save SPSR_irq */ __asm { MSR CPSR_c, #0x1F } /* Enable IRQ (Sys Mode) */ __asm { STMFD SP!, {LR} } /* Save LR */ #define IDISABLE /* Nested Interrupts Exit */ __asm { LDMFD SP!, {LR} } /* Restore LR */ __asm { MSR CPSR_c, #0x92 } /* Disable IRQ (IRQ Mode) */ __asm { LDMFD SP!, {LR} } /* Restore SPSR_irq to LR */ __asm { MSR SPSR_cxsf, LR } /* Copy LR to SPSR_irq */ Как видно, в примере с сайта ARM сохраняется в стек регистр SPSR_irq, а в моем примере - нет Документация на компилятор утверждает, что ...unlike Keil CARM Compiler, in ARM's RealView compiler, don't save and restore registers into the stack in RVD as the compiler does that for you. See RVD ARM compiler Inline and embedded assemblers, "Rules for using __asm and asm keywords. Далее, при запуске ARM'овского примера он виснет на строчке Код (Text): __asm { MSR CPSR_c, #0x1F } /* Enable IRQ (Sys Mode) */ . при этом при переводе в любой другой режим (например IRQ Mode) зависания не происходит Почему оно виснет и вообще где я туплю?