Hello,
Could you help to install the ST timer on the AT91Rm9200? I don't know how to setup the AIC registers correct. Maybe there are more than one big mistake.
void initialize_pit() { unsigned int flag, period, irq_id, mask, oldHandler, src_type, priority; volatile int status; void *newHandler; //period Timer0->ST_PIMR = period << 5; AT91F_ST_SetPeriodInterval(Timer0, period); //state //status = Timer0->ST_SR; //enable interrupt flag=1; Timer0->ST_IER = flag; AT91F_ST_EnableIt( Timer0, flag); //enable PMC AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC, 1 << AT91C_ID_IRQ0); //config AIC irq_id = 0; oldHandler = pAic->AIC_SVR[irq_id]; mask = 0x1 << irq_id; pAic->AIC_IDCR = mask ; pAic->AIC_SVR[irq_id] = (unsigned int) newHandler ; //* Store the Source Mode Register pAic->AIC_SMR[irq_id] = src_type | priority ; AT91F_AIC_ConfigureIt(pAic, irq_id, priority, src_type, newHandler); //enable AIC AT91F_AIC_EnableIt(pAic, 1); }
I hope somebody could help me with this problem...
best regards johannes
Timer0->ST_PIMR = period << 5;
Your code doesn't show the definition/declaration of Timer0. If the problem is there, it's impossible for anyone not having access to the code to spot.
//enable PMC AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC, 1 << AT91C_ID_IRQ0);
I'm not sure what this line is supposed to do. The AIC is continuously clocked (see device datasheet, p. 241, 22.6.2.).
irq_id = 0;
IRQ ID 0 is the interrupt of the AIC itself. The system interrupts have the IRQ ID 1. Refer to the device datasheet, p. 21.
pAic->AIC_SVR[irq_id] = (unsigned int) newHandler ;
I don't think the code assigns a value to newHandler anywhere. Basically, this sets the interrupt vector to a random address. That's a surefire way to get undefined behavior.
pAic->AIC_SMR[irq_id] = src_type | priority ;
priority never has a value assigned to it anywhere. This doesn't just set the priority to a random value, it may also set the source type to a random value. Oh. src_type also never has a value assigned to it, either.
AT91F_AIC_ConfigureIt(pAic, irq_id, priority, src_type, newHandler);
The code first tries to configure the interrupt "by hand" and then does so again by using the function.
thanks for your answer
AT91PS_ST Timer0;
AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC, 1 << AT91C_ID_IRQ0);
enable the peripherie clock for the ST timer.
>The code first tries to configure the interrupt "by >hand" and then does so again by using the function.
it is enough to use this line of code?
I don't know what I have to use for "newHandler"? Moreover I changed the irq_id to 1.
irq_id = 1; priority = 1;
you must write 0x1 instead of 1.
No, that is desinformation. 0x1 and 1 are exactly the same - just two numeric representations of the same value.
Per could you tell me which parts of the code are not correct?
johannes
I'm not working with that line of chips myself. I am using NXP chips.
To tell you why your code does not work, I would have to retrieve the datasheet for your chip - and probably also take a peek at the sample code or application notes for your chip.
What time have you spent with the datasheet and with the available sample code? Have you looked for application notes?
I'm not too much into distributed programming/debugging. I prefer distributed computing.
It looks fairly correct now.
How do you test it ? Are you working with an emulator/ICE and are able to set a breakpoint in the ISR ? Are you waiting for the chip to send something over the USART (which may or may not be initialized correctly, your code doesn't show that part nor any calls to an initialization function) ?
Personally, I am working with the fairly similar AT91SAM7S, and did get the PIT to work.
the usart is working correct - I test it. Therefore there must be still a problem that the ST_interrupt function don't run.
AT91PS_USART COM1_1; /* Variable COM1 für die Struct _AT91S_USART definieren */ void initialize_usart1() { COM1_1= AT91C_BASE_US1; /* AT91C_BASE_US1 ist die Base-Adresse für den UART1 */ /* Define RXD and TXD as peripheral */ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOB,AT91C_PB21_RXD1 | AT91C_PB20_TXD1,0); // First, enable the clock of the PIOB (for the USART) AT91F_PMC_EnablePeriphClock ( AT91C_BASE_PMC, 1<<AT91C_ID_US1 ) ; // Usart Configure AT91F_US_Configure (COM1_1, MCK,AT91C_US_ASYNC_MODE, 115200, 0); // Enable usart COM1_1->US_CR = AT91C_US_RXEN | AT91C_US_TXEN; // setzen der beiden Variablen im US_CR Register AT91F_US_PutChar (COM1_1,'X'); while( !(COM1_1->US_CSR & AT91C_US_TXRDY) ) continue; }
I'm working with uvision3 and ulink2.
when I start the program, I can send one character 'u' via the usart - but that's it.
AT91F_AIC_ConfigureIt(pAic, irq_id, priority, AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE, ST_interrupt);
Try configuring the AIC before doing everything else (especially enabling interrupts) during the initialization of ST (or any other peripheral). It might not be the problem here, but sticking with this convention may save you some debugging headaches later on.
void ST_interrupt(void) {
You may want to add the __irq keyword here, like this:
void ST_interrupt(void) __irq {
Also:
void timer() { unsigned int st_interval; // intveral st timer initialize_usart1(); initialize_st(); *(AT91C_ST_PIMR) = st_interval; // value for the st timer
You are still assigning the value of an uninitialized local variable to something else. This is a very bad idea. C convention does not require uninitialized local variables to have any particular value by default - they could have _any_ value. You're basically using a (bad) random number generator to set the value of ST_PIMR here.
The compiler should be printing a warning message here. Warning messages usually have a good reason.
Do you have access to an emulator (i.e. can you set breakpoints in the program and single-step through it) ?
If so, try setting a breakpoint inside the ST interrupt handler. Also, try setting one inside the ST initialization function and single-step through it (you should also be able to watch the contents of the ST registers change).
thanks for your help...
I set breakpoints in the initialize_st() and the ST_interrupt(void) function. When I run the programm, the initialization and the ST_interrupt method are executed and then the program ends in the while(1) loop - doing nothing. Therefore I get only a message via the usart. the status register is 1 after the line
status = Timer0->ST_SR;
here is the whole code again
#define MCK 60000000 /* Clock-Rate für USART */ AT91PS_USART COM1_1; /* Variable COM1 für die Struct _AT91S_USART definieren */ void initialize_usart1() { COM1_1= AT91C_BASE_US1; /* AT91C_BASE_US1 ist die Base-Adresse für den UART1 */ /* Define RXD and TXD as peripheral */ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOB,AT91C_PB21_RXD1 | AT91C_PB20_TXD1,0); // First, enable the clock of the PIOB (for the USART) AT91F_PMC_EnablePeriphClock ( AT91C_BASE_PMC, 1<<AT91C_ID_US1 ) ; // Usart Configure AT91F_US_Configure (COM1_1, MCK,AT91C_US_ASYNC_MODE, 115200, 0); // Enable usart COM1_1->US_CR = AT91C_US_RXEN | AT91C_US_TXEN; } AT91PS_ST Timer0 = AT91C_BASE_ST; AT91PS_AIC pAic = AT91C_BASE_AIC; void ST_interrupt(void) { volatile int status; //clear ST_SR - muss ausgelesen werden status = Timer0->ST_SR; //usart1 while( !(COM1_1->US_CSR & AT91C_US_TXRDY) ) continue; AT91F_US_PutChar (COM1_1,'n'); //clear the interrupt - zurücksetzen des Interrupts AT91F_AIC_ClearIt( AT91C_BASE_AIC,AT91C_ID_SYS ); //the end of interrupt durch lesen dieses Registers - letzte Aktion AT91C_BASE_AIC->AIC_EOICR = 0x0; } void initialize_st() { //config AIC AT91F_AIC_ConfigureIt(pAic, AT91C_ID_SYS, AT91C_AIC_PRIOR_HIGHEST - 6, AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE, ST_interrupt); //unsigned int period; Timer0 = AT91C_BASE_ST; /* Base Address for the ST Timer */ //period = 0x0; // Set Periodic Interval Interrupt (AT91C_ST_PITS = Period Interval Timer Interrupt; Timer0 = Basis Adresse für ST) AT91F_ST_SetPeriodInterval(Timer0, AT91C_ST_PITS); //#define AT91C_ST_PITS ((unsigned int) 0x1 << 0) -> 0x1 = 0001 -> gesetzt ST_IMR Register (Interrupt enabled) AT91F_ST_EnableIt(Timer0, AT91C_ST_PITS); AT91F_AIC_EnableIt( pAic, AT91C_ID_SYS); // enable all mIRQ interrupts AT91C_BASE_AIC->AIC_CISR = 0x1 << 1; } void timer() { initialize_usart1(); initialize_st(); while(1); }
When I add this line
what's the right request in the line
AT91F_AIC_ConfigureIt(pAic, AT91C_ID_SYS, AT91C_AIC_PRIOR_HIGHEST - 6, AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE, ST_interrupt);
Could there be something wrong in the startup-file? I sue the startup-file from keil.
You most definitely must use the __irq keyword for the interrupt handler - unless you write in assembler and yourself write the correct code for entering/leaving the interrupt handler.
Please don't sue the startup file - this world is already too full of busy lawyers ;)
could you explain me how I have to change this line
AT91F_AIC_ConfigureIt(pAic, AT91C_ID_SYS, AT91C_AIC_PRIOR_HIGHEST - 6, AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE, ST_interrupt __irq);
Please read all the comments above, and then explain why you think that you should use the __irq keyword in the function call.
The information you have received is that your Interrupt Service Routine (ISR) should be declared with the __isr keyword. You have even received an explicit example how that source line should be written.
Have you looked at _any_ example code for your processor? You can hardly find any example code that doesn't contain at least one ISR.
yes, but when I write
void ST_interrupt(void) __irq
I get a error in this line
because the typ void (*)() __irg is not compatible with parameter of type void (*) ()
When I run the programm, the initialization and the ST_interrupt method are executed and then the program ends in the while(1) loop - doing nothing.
The ST interrupt handler is executed exactly once, is that correct ?
If so, that's already a good lead. It means that the interrupt is enabled and responsive.
AT91F_AIC_ClearIt( AT91C_BASE_AIC,AT91C_ID_SYS );
The AIC_ICCR has no effect on level-triggered interrupts. This line is unnecessary, as the PITS interrupt will be cleared by reading ST_SR. Try the following line to read ST_SR at the end of the ISR:
AT91C_BASE_AIC->AIC_EOICR = AT91C_BASE_ST->ST_SR;
--------
AT91F_ST_SetPeriodInterval(Timer0, AT91C_ST_PITS);
Setting the ST_PIMR to "AT91C_ST_PITS" doesn't make much sense. AT91C_ST_PITS is defined as
#define AT91C_ST_PITS (0x1 << 0) // (ST) Period Interval Timer Interrupt
and intended to be used when accessing ST_SR or the various registers dealing with the ST interrupts (ST_I?R). The second parameter given to AT91F_ST_SetPeriodInterval should be the number of milliseconds that you want the PIT interval to be.
Also, be aware that AT91F_ST_SetPeriodInterval disables the PITS interrupt (which shouldn't matter here just yet, since you enable it after the call to the function. I'm just mentioning this for the record.)
AT91C_BASE_AIC->AIC_CISR = 0x1 << 1;
AIC_CISR is a read-only register (see datasheet, p. 256, 22.8.9 ). Writing to it is pointless.
Adding the __irq keyword doesn't change anything in the AIC configuration. It just tells the compiler that this function will be used as an ISR, and must be compiled in ARM mode.
I don't believe so. The startup file only does some fairly generic initialization.
thanks a lot - now it is working...
How can I see in which mode the prozessor is working? I thought that would be correct initialised in the startup file?
That's true - I just see this code by a example in the www, and I wondered...
How can I see in which mode the prozessor is working?
You mean whether it's in ARM mode or in THUMB mode ?
Various compiler settings and preprocessor commands can influence this. You can set a "default" mode in the project settings, but then override the default by using preprocessor commands (see the compiler manual on how to do this). THUMB mode code is generally smaller and usually somewhat slower than ARM code (it can be faster if your system is working with a 16-bit memory interface instead of a 32-bit memory interface). However, interrupt service routines must always contain ARM code since the processor switches to ARM mode when it enters an ISR.
If you write programs in assembly, you have finer (and basically complete) control over the processor mode. In assembly, the mode can be changed with special branch instructions.
yes I read the datasheet about thumb and arm code...
ok - that means that without __irq the prozessor don't run in the right mode (irq mode which can only be in arm mode). I thought that when I create a irq that the prozessor knows that he has to run this code in arm mode... ok but that was the mistake...
I thought that when I create a irq that the prozessor knows that he has to run this code in arm mode... <p>
The __irq keyword does some other things besides telling the compiler that the function needs to be compiled in ARM mode. Any of these things might have caused the problem - without knowing what your compilers settings were, there's no way to know. Since your program sat in the while() loop after executing the ISR once, I would assume that it wasn't a problem related to the processor mode - if the processor encounters code that doesn't fit the mode the processor is in, the program will crash in various horrible ways instead of quietly sitting in a while loop.
And yes, the processor "knows" that it needs to switch to ARM mode when it vectors to an ISR. However, the compiler doesn't know which functions are ISRs (or are called by ISRs - remember that bit for later) unless you tell it. If the function you tell the AIC to use as an ISR is compiled in THUMB mode by the compiler, and the processor vectors there and expects code in ARM mode, then exactly one thing will happen: garbage.
I just use the RealView Compiler and I only select using RealView Compiler in the components, enviroment and books menue.. that's all.. so I will read the manual for the realview compiler - maybe I will find a solution for this problem...
I understand - __irq tells the compiler which manner of function that is... to know what to do...
Is it a very good and common way to write only the initialisation and the while(1) loop in the main routine, so that the processor is always in idle mode, only when a interrupt occurs?
That means that the main routine is very small. and everything is working with interrupts.
To be in idle mode, your while loop must specifically put the processor to sleep. If it doesn't, then the processor will always run at full power.
that's sounds good.
UND_Stack_Size EQU 0x00000000 SVC_Stack_Size EQU 0x00000008 ABT_Stack_Size EQU 0x00000000 FIQ_Stack_Size EQU 0x00000000 IRQ_Stack_Size EQU 0x00000080 USR_Stack_Size EQU 0x00000400
in the startup-code, there are mentioned the size of the memory for each interrupt type. How can I calculate the storage I need for the interrupts I programm?
e.g. in the code above; it's a irq interrupt (IRQ_Stack_Size). Why is the USR_Stack_Size so big and when will I use this mode?
Almost. The usual way to write uC/DSP programs is to only use ISRs for doing the things that are absolutely necessary when an interrupt occurs, and have them use flags to signal to the main endless loop that there is further processing to be done.
At least that's the way for programs that do not use an operating system. Things get a bit more complex when an operating system is involved and you need to deal with tasks, software interrupts, preemption and the like.
What you should avoid is putting any longer processing (especially any loops that wait for events) into ISRs. A good rule to follow is KISS - Keep ISRs Short and Simple.
However, sitting in a while(1); loop does not put the processor into an acutal idle (i.e. power-saving) state. How to do this can be found (as usual) in the datasheet (on page 271, to be precise).
How can I calculate the storage I need for the interrupts I programm?
That depends on many factors - but the most important are the number of local variables that your ISRs use (they shouldn't use all that many if you stick with the KISS guideline) and whether you allow nesting of ISRs (i.e. one higher-priority ISR can interrupt lower-priority ISRs).
USR_Stack_Size is the stack size available in USER mode - which is basically all of your program except for ISRs (FIQ/IRQ), operating-system like parts (SVC), and handlers for specific errors (ABT/UND). You will find more about processor modes in the datasheet, on page 36, in the ARM920T Technical Reference Manual, and the ARM9TDMI Technical Reference Manual (which you should all have a copy of, and read, if you want to do any serious development on that chip).
void ST_interrupt(void) __irq { AT91C_BASE_AIC->AIC_EOICR = AT91C_BASE_ST->ST_SR; while( !(COM1_1->US_CSR & AT91C_US_TXRDY) ) continue; AT91F_US_PutChar (COM1_1,'n'); //the end of interrupt durch lesen dieses Registers - letzte Aktion AT91C_BASE_AIC->AIC_EOICR = 0x0; }
It's not the best idea to read the AIC_EOICR register in the first line of the ISR, isn't it?
Therefore is it a good solution to install a variable status and read both registers ST_SR und AIC_EOICR with status = AT91C_BASE_ST->ST_SR at the beginning of the ISR and status = AT91C_BASE_AIC->AIC_EOICR at the end of the ISR
The AIC_EOICR is being written to in this line. And since the process of writing to AIC_EOICR ends the interrupt function, this line should be the last line of the ISR.
Using an intermediate variable is not necessary. AIC_EOICR needs to be written to in order to end the interrupt, and ST_SR needs to be read in order to clear the ST interrupt. The one line, placed at the end of the ISR, takes care of both.
and there's no problem if a interrupt occured when the processor is working in the ISR of the same typ of interrupt - but the ST_SR register is not read in the actual ISR?
1. Interrupt1 occured 2. ISR_interrupt1 --> second interrupt occured 3. ST_SR interrupt1 is read
The second interrupt is safed in a register (I don't know it by heart)- although the ST_SR is not read - and waits until the AIC_EOICR register is read.
No, you can safely wait with reading ST_SR until the last line of the ISR.
An ISR can only be interrupted (leading to interrupt nesting) by an interrupt of higher priority. This excludes the possibility of an ISR being interrupted by its own interrupt signal.
However, you should not create an ISR that is takes enough CPU time for the ST_SR interrupt to occur again while the ISR is still running. This would cause you to miss one "tick" of the PITS. Since the PITS runs quite slowly, any ISR that runs long enough should be considered bloated and trimmed down to an acceptable running time immediately.
View all questions in Keil forum