IAR, Cortex M3 и C++
При перетаскивании проекта с LPC2368 (ядро ARM7TDMI) на LPC1768, который является pin-to-pin совместимым с LPC2368, но на ядре Cortex M3, столкнулся со следующими проблемами и особенностями:
В LPC1768 существенно изменена архитектура VIC (Vectored Interrupt Controller), т.е. контроллера прерываний.
В проекте LPC2368 были две функции, одна из которых предназначалась для инициализации VIC и сброса всех векторов прерываний в 0 (VIC_Init), а вторая для установки функции в качестве вектора прерывания с назначением ему нужного приоритета (VIC_SetVectoredIRQ):
/************************************************************************* * Function Name: VIC_Init * Parameters: void * Return: void * * Description: Initialize VIC * *************************************************************************/ void VIC_Init(void) { volatile unsigned long * pVecAdd, *pVecCntl; int i; // Assign all interrupt channels to IRQ VICINTSELECT = 0; // Disable all interrupts VICINTENCLEAR = 0xFFFFFFFF; // Clear all software interrupts VICSOFTINTCLEAR = 0xFFFFFFFF; // VIC registers can be accessed in User or privileged mode VICPROTECTION = 0; // Clear interrupt VICADDRESS = 0; // Clear address of the Interrupt Service routine (ISR) for vectored IRQs // and disable all vectored IRQ slots for(i = 0, pVecCntl = &VICVECTPRIORITY0, pVecAdd = &VICVECTADDR0; i < 32; ++i) { *pVecCntl++ = *pVecAdd++ = 0; } } ************************************************************************* * Function Name: VIC_SetVectoredIRQ * Parameters: void(*pIRQSub)() * unsigned int VicIrqSlot * unsigned int VicIntSouce * * Return: void * * Description: Init vectored interrupts * *************************************************************************/ void VIC_SetVectoredIRQ(void(*pIRQSub)(), unsigned int Priority, unsigned int VicIntSource) { unsigned long volatile *pReg; // load base address of vectored address registers pReg = &VICVECTADDR0; // Set Address of callback function to corresponding Slot *(pReg+VicIntSource) = (unsigned long)pIRQSub; // load base address of ctrl registers pReg = &VICVECTPRIORITY0; // Set source channel and enable the slot *(pReg+VicIntSource) = Priority; // Clear FIQ select bit VICINTSELECT &= ~(1<<VicIntSource); }
В примерах кода в IAR обработчиками прерываний заведует файл «cstartup_M.s», которого не было в проекте LPC2368. Он определяет имена дефолтных обработчиков, в таком стиле:
__vector_table_0x1c DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD SVC_Handler ; SVCall Handler DCD DebugMon_Handler ; Debug Monitor Handler DCD 0 ; Reserved DCD PendSV_Handler ; PendSV Handler DCD SysTick_Handler ; SysTick Handler DCD WDT_IRQHandler ; Watchdog Handler DCD TMR0_IRQHandler ; TIMER0 Handler DCD TMR1_IRQHandler ; TIMER1 Handler DCD TMR2_IRQHandler ; TIMER2 Handler DCD TMR3_IRQHandler ; TIMER3 Handler DCD UART0_IRQHandler ; UART0 Handler DCD UART1_IRQHandler ; UART1 Handler DCD UART2_IRQHandler ; UART2 Handler DCD UART3_IRQHandler ; UART3 Handler DCD PWM1_IRQHandler ; PWM1 Handler
Т.е. обработчик прерывания таймера 0 описывается в прогамме как функция TMR0_IRQHandler(void). Допустим, что функции-обработчики находятся в файле main.c.
Проблема в том, что, пока файл main имеет расширение «.с», всё работает, как только мы меняем расширение на «.срр», обработчики прерываний работать перестают.
Решение оказалось очень простым: перед обработчиками нужно поставить «extern «C»»:
extern "C" void TMR0_IRQHandler (void)
и они начинают работать нормально.
Собственно, всё, что требуется от программиста, помимо описания функций-обработчиков, это назначение им необходимого приоритета такой функцией:
/************************************************************************* * Function Name: NVIC_ClrPend * Parameters: IntNumber - Interrup number, Interrupt Priority * Return: void * * Description:Sets Interrupt priority * * *************************************************************************/ void NVIC_IntPri(Int32U IntNumber, Int8U Priority) { volatile Int8U * pNVIC_IntPri = (Int8U *)&IP0; assert((NVIC_WDT <= IntNumber) && (NVIC_PLL1 >= IntNumber)); IntNumber -= NVIC_WDT; pNVIC_IntPri += IntNumber; *pNVIC_IntPri = Priority; }
-
name