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.
AT91F_AIC_ConfigureIt(pAic, irq_id, priority, src_type, ST_interrupt);
src_type and priority don't have any values assigned to them !
I'm pretty sure that the compiler is issuing warnings about variables being used before having been assigned a value here. Compiler warnings are there for a reason and usually indicate that there is a problem with your code.
void ST_interrupt(void) { AT91C_BASE_AIC->AIC_IDCR = 0xFFFFFFFF; //disable all interrupts AT91C_BASE_AIC->AIC_ICCR = 0x00000001;
This is unnecessary and useless at best. Since you probably want the ST interrupt to be level-sensitive, the AIC_ICCR will have no effect. The interrupt will be cleared by reading ST_SR instead.
You still have uninitialized variables. Have you turned on all compiler warnings, and then made sure to clean out any warnings you get?
Another thing:
while(1) { while (tick == *(AT91C_ST_CRTR)); tick = *(AT91C_ST_CRTR); }
Notice 1: The semicolon at the end of the while() line menas that you have a while statement with an empty body - the indented second line isn't part of the while loop...
Notice 2: Is your loop intended as a delay? If all you want is the tick variable to be updated when the timer steps, you should let the interrupt service routine (ISR) assign a new value to tick on each interrupt. But then again - if the specific register in the timer is tha value you need, there is no reason to make a copy of it to a variable.
ok - now I get no warnings or errors... but the call for the ST_interrupt routine doesn't work...
void timer() { unsigned int pit_interval; // intveral pit timer initialize_usart1(); initialize_pit(); *(AT91C_ST_PIMR) = pit_interval; // value for the pit timer while(1) { //do nothing } }
The ST_SR register tell's me only if there has been a interrupt since if red it the last time..
irq_id = 1; priority = 1; src_type = 0x01; AT91F_AIC_ConfigureIt(pAic, irq_id, priority, src_type, ST_interrupt);
that is my configuration for the AIC.
No - the ST_SR is the direct cause for the ST interrupt line being asserted. There's a "hard-wired" connection on the chip that says "(ST_SR AND ST_IMR) -> Status of the ST interrupt line.
You can find #defined values for the source type and the priority in AT91RM9200_inc.h.
0x01 for the source type isn't even a valid value, since the source type field consists of bits 5 and 6 in the AIC_SMR.
I set the value for the src_type to 1.
but what's the reason, that my ST_interrupt(void) method never run?
Is the only solution to say if(ST_SR == 1) go to ST_interrupt(void) ?
johannes
AT91F_AIC_EnableIt(pAic, irq_id);
Where's the definition of pAic, and where does it get a value assigned to it ?
The src_type field consists of bits 5 and 6 of the AIC_SMRx. So valid values for src_type are 0x00, 0x20, 0x40 and 0x60.
Something's still wrong with your code.
No, that would just mess up things completely. The solution is getting the AIC and the ST programmed correctly. Maybe you need to recapitulate the appropriate chapters in the datasheet instead of trying to depend on others trying poke through code snippets and find out what exactly is wrong with your code. Maybe having another look inside a C textbook might be a good idea, too - might be a good idea judging from the use of uninitialized local variables.
AT91PS_AIC pAic = AT91C_BASE_AIC;
that's the definition for AIC
__inline unsigned int AT91F_AIC_ConfigureIt ( AT91PS_AIC pAic, // \arg pointer to the AIC registers unsigned int irq_id, // \arg interrupt number to initialize unsigned int priority, // \arg priority to give to the interrupt unsigned int src_type, // \arg activation and sense of activation void (*newHandler) (void) ) // \arg address of the interrupt handler { unsigned int oldHandler; unsigned int mask ; oldHandler = pAic->AIC_SVR[irq_id]; mask = 0x1 << irq_id ; //* Disable the interrupt on the interrupt controller pAic->AIC_IDCR = mask ; //* Save the interrupt handler routine pointer and the interrupt priority pAic->AIC_SVR[irq_id] = (unsigned int) newHandler ; //* Store the Source Mode Register pAic->AIC_SMR[irq_id] = src_type | priority ; //* Clear the interrupt on the interrupt controller pAic->AIC_ICCR = mask ; return oldHandler; }
irq_id = 1; priority = 1; src_type = 1; AT91F_AIC_ConfigureIt(pAic, irq_id, priority, src_type, ST_interrupt);
What do you think happen when you do:
priority = 1; src_type = 0x01;
and then:
AT91F_AIC_ConfigureIt(pAic, irq_id, priority,src_type, ST_interrupt);
Think about the line:
With src_type = 1 and priority = 1, what use do you think that or operation makes? Are you spending any time trying to understand the answers you receive? What does the data sheet say about initializing the AIC_SMR register with the value 1?
Also, you may want to use the __irq keyword for the interrupt service routine (or use some other way to make sure the the interrupt service routine is compiled in ARM mode and not in THUMB mode. I do not know what setting uVision uses as a default. Interrupt service routines must always be in ARM mode, however).
The priority level can be between 0 (lowest) and 7 (highest).
so I select the value 1 for the priority - I don't see any disadvantages about it.
SCR_TYPE 0 0 high-level sensitive 0 1 positive edge-triggerd (is this not right to use this mode)?
0 1 are the bits 5 and 6 so it's 0x0000 0001 or not?
Or is this configuration false for the ST timer?
Very wrong. Look again at the data sheet. You are trying to store source type and priority in the same bits. Obviously that does not work. Have you read the data sheet? They normally very clearly shows what data is stored in the different bits of a register.
Also, Christoph Franck have already written in an earlier post what values you may use for the source type. I can't see how you can manage to call the least significant bit "bit 5" or "bit 6".
You just posted the piece of code that provides the answer to the question:
There is nothing in here that shifts src_type 5 bit positions to the left, is there ?
Also, you may want to work with the #define constants in the #include file instead of numeric values:
#define AT91C_AIC_SRCTYPE (0x3 << 5) // (AIC) Interrupt Source Type #define AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE (0x0 << 5) // (AIC) Internal Sources Code Label Level Sensitive #define AT91C_AIC_SRCTYPE_INT_EDGE_TRIGGERED (0x1 << 5) // (AIC) Internal Sources Code Label Edge triggered #define AT91C_AIC_SRCTYPE_EXT_HIGH_LEVEL (0x2 << 5) // (AIC) External Sources Code Label High-level Sensitive #define AT91C_AIC_SRCTYPE_EXT_POSITIVE_EDGE (0x3 << 5) // (AIC) External Sources Code Label Positive Edge triggered
thanks for the hint...
void initialize_st() { unsigned int flag, period, irq_id, priority; Timer0 = AT91C_BASE_ST; /* Base Address for the ST Timer */ //period for the pit timer period = 1; AT91F_ST_SetPeriodInterval(Timer0, period); // enable interrupts flag=1; AT91F_ST_EnableIt( Timer0, flag); //config AIC irq_id = 1; priority = 1; AT91F_AIC_ConfigureIt(pAic, irq_id, priority, AT91C_AIC_SRCTYPE_INT_EDGE_TRIGGERED, ST_interrupt); //enable AIC AT91F_AIC_EnableIt(pAic, irq_id); }
I don't work - I think there are many other mistakes in my code...
__irq keyword for the interrupt service routine?
do you mean I have to use this for the interrupt routine?
void ST_interrupt(void) { AT91F_US_PutChar (COM1_1,'u'); while( !(COM1_1->US_CSR & AT91C_US_TXRDY) ) continue; }
Could you explain it a little bit for me?
AT91F_AIC_ConfigureIt(pAic, irq_id, priority, AT91C_AIC_SRCTYPE_INT_EDGE_TRIGGERED, ST_interrupt);
As I mentioned earlier, you probably want the interrupt to be level-triggered, not edge-triggered. If the interrupt line was high before you enabled the interrupt, the AIC will never see an edge and hence never call your interrupt service routine.
You will also need to read ST_SR somewhere in your interrupt routine in order to clear the interrupt. One way of doing this is doing something along the lines of AIC_EOICR = ST_SR, which both reads ST_SR (clearing the ST interrupt) and writes to AIC_EOICR (telling the AIC that the interrupt service routine has finished).
thank for your answer...
AT91PS_ST pST = AT91C_BASE_ST; void ST_interrupt(void) { volatile int status; //clear ST_SR status = pST->ST_SR; AT91F_US_PutChar (COM1_1,'u'); while( !(COM1_1->US_CSR & AT91C_US_TXRDY) ) continue; // Here acknowledge the end of interrupt AT91C_BASE_AIC->AIC_EOICR = 0x0; }
and I changed the value
irq_id = 1; priority = 1; AT91F_AIC_ConfigureIt(pAic, irq_id, priority, AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE, ST_interrupt);
AT91PS_ST Timer0; /* Variable (pointer) Timer0 für die Struct AT91S_ST definieren */ AT91PS_AIC pAic = AT91C_BASE_AIC; AT91PS_ST pST = AT91C_BASE_ST; void ST_interrupt(void) { volatile int status; //clear ST_SR status = pST->ST_SR; AT91F_US_PutChar (COM1_1,'u'); while( !(COM1_1->US_CSR & AT91C_US_TXRDY) ) continue; // Here acknowledge the end of interrupt AT91C_BASE_AIC->AIC_EOICR = 0x0; } void initialize_st() { unsigned int flag, period, irq_id, priority; Timer0 = AT91C_BASE_ST; /* Base Address for the ST Timer */ //period for the pit timer period = 1; AT91F_ST_SetPeriodInterval(Timer0, period); // enable interrupts flag=1; AT91F_ST_EnableIt( Timer0, flag); //config AIC irq_id = 1; priority = 1; AT91F_AIC_ConfigureIt(pAic, irq_id, priority, AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE, ST_interrupt); //enable AIC AT91F_AIC_EnableIt(pAic, irq_id); } void timer() { unsigned int st_interval; // intveral st timer initialize_usart1(); initialize_st(); *(AT91C_ST_PIMR) = st_interval; // value for the st timer while(1) { } }
Is there another stupid mistake in this code - because it doesn't work...
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?
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.
View all questions in Keil forum