Problem with interrupts on NXP LPC2138 - Page 1
Forum section: electronics / general

author: AussieBruce
author_rank: Regular Contributor
timestamp: March 07, 2022, 02:12:49 pm
content: I’m having trouble getting a simple LED flasher program to run. My actual applications are more complex, but I've had to back out to something trivial because no interrupt code that I write will run. I'm stuck with the 2138 platform for specific reasons.I build on Crossworks with GCC for ARM, and load a bin file into the target flash or RAM via JTAG through Segger Jlink – AFAIAA the code is running pretty much on the metal, but with ARM it seems difficult to be sure what else might be going on lower down. The program uses timer0 (have also tried Timer1), a polled version runs perfectly, but a counterpart using timer interrupts doesn’t. When the program runs, the ISR doesn’t appear to get called, and if I force a program exit by incorporating a DI pin in the main() wait loop, the CPU seems to be in some sort of locked state – I have to do a PDPU to get it back.There's nothing else loaded on the target. Flash and RAM loads behave the same. I’ve tried every combination of the MEMMAP settings so that isn’t the problem.I’ve spent endless hours with the chip manual. I’ve also searched extensively, the web brings up some reports on ISR problem with the LPC2138, but they are all vague enough to be of minimal use. I’m wondering if there’s something lower down, may be in the C primitive (.s) that Crossworks automatically adds to the project, and which needs to be ‘tweaked’ for some reason. That’s an area I’d prefer not to get into.Can any ARM gurus see an obvious problem?Here’s the ISR version code. Aside from the ISR handling, it’s identical to the polled version, which works.// Standard Crossworks includes for LPC213*.#include <targets/LPC2000.h>#include <intrinsics.h>char d ; // LED statevoid T0ISR() __attribute__ ((interrupt ("IRQ")));void T0ISR(){long int Regval ;Regval = T0IR ;if (d == 0){d = 1 ;IO0SET = 0x80010000; // LED on}else{d = 0 ;IO0CLR = 0x80010000; // LED off}T0MR0 = 1000000; // reset timerT0IR = Regval ; // reset interrupt flagVICVectAddr = 0 ;}int main(void){char c ;MEMMAP = 2 ; // Have tried every possible valuePINSEL2 = 0 ; // Ensure debug mode offPCONP = 2 ; // TMR0 onIO0DIR = 0x80000000; // turn on the LED driver (P0.31)// Set up timer (works on polled version)T0TC = 0 ;T0PR = 2 ; // 1uS to main counterT0MR0 = 1000000 ;T0MCR = 3 ;T0TCR = 1 ; // start timer// Set up interrupt handling hereVICVectAddr0 = (unsigned long) T0ISR ;VICIntSelect = 0 ; // All interrupts set as IRQ (not fast)VICVectCntl0 = (0x20 | 4 ); // 20 = enable slot. 4 = T0VICIntEnable |= (1UL << 4); // Enable T0 Interruptd = 0 ;T0TCR = 1 ; // start timer__enable_interrupt() ;while(1){}return 0 ;}

author: nctnico
author_rank: Super Contributor
timestamp: March 07, 2022, 05:10:27 pm
content: You'll need to have an actual interrupt handler somewhere in assembler code (likely startup.S) . The vectored interrupt controller calls a single hardware interrupt vector. The code from that hardware interrupt needs to read the vectored interrupt controller to see which address to call and go from there.

author: rstofer
author_rank: Super Contributor
timestamp: March 07, 2022, 07:14:58 pm
content: Don't you have to make some changes to crt.s?I have attached an interrupt driven blinky for the LPC2106 and it is quite similarIt has been a long time since I used CrossWorks and an ATM7TDMI device but I certainly like both.Anyway, paw through the attachment and see if anything jumps out.

author: rstofer
author_rank: Super Contributor
timestamp: March 07, 2022, 10:51:30 pm
content: Here's some demo code for the LPC2148, a close cousin of the LPC2138https://jcwren.com/arm/There's a lot to learn here, especially the Makefile which recurses through subdirectories used to keep the code tidy. I used this site as a reference when I was playing with the LPC2148 Olimex board, still one of my favorite chips/boards. I haven't used it in years but I certainly would if something came up that fit the board.

author: AussieBruce
author_rank: Regular Contributor
timestamp: March 10, 2022, 05:55:32 am
content: Thanks a lot rstofer. It helped that the IRQ_Wrapper code in your attachment is well commented, but even without that, with a bit of help from the web I managed to decode what's going on. If I can handle x86 assembler, this ARM stuff can't be that hard, and it looks like it isn't.Still rather amazed that so much with ARM seems to be assumed, in some ways it's far from a beginner platform, my problem is an example of that.Another aspect of this, and I think others will agree, is that nowadays code on the metal is out of fashion. In parallel I posted a request on the Xworks forum for a pointer to some 2138 code using interrupts, someone came back with a link, when I checked it out it's just a bundle of high level C functions in their ctl_*_* library, which contains all the usual high level real time stuff such as semaphores and mutexes. Likely to be memory hungry for small systems, and definitely another learning curve I can do without at my age!Thanks again.

author: nctnico
author_rank: Super Contributor
timestamp: March 10, 2022, 05:17:18 pm
content: Quote from: AussieBruce on March 10, 2022, 05:55:32 amYes and no. Many microcontrollers need some assembly code to get a C program started. Your problem is that you didn't got the right one for the microcontroller you choose. On top of that the NXP LPC2x00 series uses a general purpose ARM core. The newer NXP LPC1x00 series are based on the ARM Cortex-M cores and those can start a C program right of the bat. But you'll still need to do typical C initialisation like zero-ing the data area and copying the initialised variable values from flash to RAM. Typically this is a memset() and memcpy() call.