3638 views|1 replies

2015

Posts

0

Resources
The OP
 

MSP430 interrupt priority and interrupt nesting (and easy to understand error instructions) [Copy link]

The interrupt priority of MSP430 is arranged according to the size of the vector where it is located. The higher the interrupt vector address, the higher the priority. However, the default MSP430 cannot nest interrupts. In order to respond to higher priority interrupts when executing a certain interrupt, it is necessary to manually turn on the global interrupt enable bit in the low-priority interrupt program, because the global interrupt enable bit is cleared when entering the interrupt service subroutine, that is, it is forbidden to respond to other interrupts. In the instructions of msp430, DINT and EINT refer to turning off and on all interrupts respectively, that is, all interrupt enable bits including P1IE, P2IE, WDTIE, TAIE, TBIE, ADC12IE, and serial port interrupts are "0" and "1". When there are multiple interrupts at the same time, the priority is considered (the priority order can be checked in the vector table). After the interrupt is responded, the general interrupt is automatically turned off. At this time, even if a higher priority interrupt comes, it will not be responded. If you want to nest interrupts, you must turn on the general interrupt in the interrupt. In the instructions of msp430, DINT and EINT refer to turning off and on all interrupts, that is, all interrupts allowed by P1IE, P2IE, WDTIE, TAIE, TBIE, ADC12IE, and serial port interrupts are "0" and "1". The following points should be noted when implementing interrupt nesting: 1) 430 turns off interrupt nesting by default, unless you turn on the general interrupt EINT again in an interrupt program; 2) When entering the interrupt program, as long as you do not turn on the interrupt again in the interrupt, the general interrupt is turned off. At this time, the interrupt will not be executed regardless of whether it is higher or lower than the current interrupt priority; 3) If the general interrupt is turned on in interrupt A, you can respond to the subsequent interrupt B (regardless of whether B has a higher or lower priority than A), and continue to execute A after B is executed. Note: After entering interrupt B, the general interrupt will also be turned off. If interrupt C needs to be responded to when interrupt B is executed, the general interrupt must also be turned on at this time. If no interrupt is needed, the interrupt does not need to be turned on. After B is executed, when the interrupt program is jumped out and the program A is entered, the general interrupt will be automatically turned on; 4) If the general interrupt is turned on in the interrupt, and there are multiple interrupts at the same time, they will be executed according to the priority, that is, the interrupt priority will only work when multiple interrupts arrive at the same time! Interrupt service does not implement the preemptive principle. 5) For single-source interrupts, as long as the interrupt is responded, the system hardware automatically clears the interrupt flag. For the comparison/capture interrupt of the TA/TB timer, as long as TAIV/TBIV is accessed, the flag is automatically cleared. For multi-source interrupts (multiple interrupt sources share an interrupt vector), the flag must be cleared manually. For example, for P1/P2 port interrupts, the corresponding flag must be cleared manually. If you use "EINT();" to open the interrupt in this type of interrupt, and do not clear the flag before opening the interrupt, the same interrupt will be continuously embedded, causing the stack overflow to cause a reset. Therefore, in this type of interrupt, the flag must be cleared before the interrupt switch is turned on. Special attention: It is this paragraph that is easy to be fooled. The priority of MSP430 interrupts is not the same as the priority of other CPU interrupts. 1. There is a priority only when there are multiple interrupts at the same time. In fact, this rarely happens. 2. After the interrupt is responded, the general interrupt is automatically turned off. At this time, no one can interrupt again, even if you are the king of the world. 3. If you turn on the general interrupt, you will still be dizzy. Anyone can interrupt you. Isn't it annoying? Interrupts reflect the performance of a microcontroller to a large extent. From this point of view, MSP430 has done a good job in interrupts, mainly providing a very rich interrupt source, including IO interrupts, timer interrupts and some interface interrupts (SPI, UART, I2C) and so on. Now I will talk about some characteristics of MSP430 interrupts, mainly the issues that I feel are more useful in project experience, and share them with you. First, the priority of MSP430 interrupts. MSP430 supports interrupt priority, but how to know the priority level? There is a very interesting statement in its manual. I quoted it in the original text, "The nearer amodule is to the CPU/NMIRS, the higher thepriority", which means that the closer to the CPU/NMIRS, the higher the priority. So how do we know which module is close to the CPU? Look at the block diagram given in the datasheet? I always feel that this is impossible to reassure an electronic person, for example, if the block diagram is in the same distance from the CPU, how to distinguish it? So we have another more reliable way. IAR provides corresponding header issues for each model of 430, and you can know it just by looking at the interrupt vector address. The interrupt vector table of MSP430 starts from address 0xFFC0 and ends at 0XFFFF. There are 32 entries in total (each interrupt vector corresponds to 2 bytes). The interrupt vector corresponding to 0XFFCO has the highest priority, and the interrupt vector corresponding to 0XFFFE has the highest priority, that is, from 0xFFCO to 0xFFFF, the 32 interrupt priorities are from low to high. In this way, it is easy to figure out the priority of each interrupt. Second, the response process of MSP430 interrupt. First of all, of course, the flag corresponding to the interrupt is set to 1. I will explain the process in detail at this time. It is actually a translated user manual, but it is still good to understand it. 1. The CPU will execute the current instruction. 2. The PC pointing to the next instruction is pushed onto the stack. 3. The status register SR is pushed onto the stack. 4. Select the interrupt with the best priority for service. 5. The interrupt flag of a single-source interrupt will be automatically cleared. Be careful here because the interrupt flags of P1 and P2 will not be automatically cleared, because the IO interrupts of P1 and P2 belong to multi-source interrupts, that is, the 8 IOs of P1 or P2 correspond to an interrupt vector. The MCU knows that an interrupt has occurred in P1 or P2. No matter which IO of P1 occurs, it will point to the interrupt vector of P1. The same is true for P2, so it needs to be manually cleared in the code. 6. The status register SR is cleared, which will terminate any low-power state, and the global interrupt enable is turned off (GIE). This place is quite different from 51. After responding to the interrupt, 430 will turn off the global interrupt enable and will not respond to any other interrupts including those with high priority. That is to say, there is no interrupt nesting in the default state. If interrupt nesting is used, you need to use _EINT() to turn on the global interrupt. 7. The interrupt vector is loaded into PC and the interrupt service function begins to execute. The above is the entire interrupt reception process. I have marked the more important parts with color fonts. Interrupt return is relatively simple. The interrupt service function will return by the RETI instruction, SR will be popped up, the microcontroller will be restored to the state before the interrupt, and PC will be popped up to continue executing instructions. Third, open the interrupt and interrupt service function. This is the part that made me entangled in the project, and please be careful. Once the MSP430 opens the peripheral interrupt, such as the SPI receive interrupt. When the SPI receive interrupt is enabled, the microcontroller will load the interrupt vector once it finds that the SPI receive flag is set. But what if we don't use the SPI receive interrupt? Since it is not used, there is no SPI receive interrupt service function. At this time, what is the value of the interrupt service function address in the interrupt vector? It is all 0. The CPU fetches instructions from 0-01FFh, and only one thing will happen. PUC, power-on clear. Then PC will load the content of the 0xFFFE interrupt vector, that is, the reset vector, and the program will jump to the startup code we made for IAR. The program will execute to the first sentence of the main() of the code we wrote. This is how the tragedy was born, the machine is swung! ! ! ! So I hope that friends who are new to 430 will not enable interrupts if they are not used. If they are enabled, they must write an interrupt service function, even if it is an empty function! 1.Interrupt nesting, the control bit of the priority 430 general interrupt is the GIE bit in the status register (this bit is in the SR register). When this bit is in the reset state, all maskable interrupts will not respond. Maskable interrupts are divided into single interrupt source and multiple interrupt sources. Generally, the interrupt flag bit of a single interrupt source is automatically cleared after responding to the interrupt service program, while the interrupt flag bit of multiple interrupt sources is required to be cleared after querying a certain register. Since the first microcontroller that most people come into contact with is usually 51, if a higher priority interrupt occurs during the response of the 51 microcontroller CPU to the low priority interrupt program, the microcontroller will execute the high priority, and this process has generated interrupt nesting. The 430 microcontroller is different. If a higher priority interrupt service request comes when responding to the low priority interrupt service program, the 430 will ignore it until the low priority interrupt service program is executed, and then it will respond to the high priority interrupt. This is because when the 430 responds to the interrupt program, the general interrupt GIE is in the reset state. If you want to generate interrupt nesting similar to 51, you can only set the GIE bit again in the interrupt function. 2. Timer TA TimerA has two interrupt vectors. TIMERA0, TIMERA1 TIMERA0 only counts the overflow of CCR0. After querying TAIV, you can know whether it is caused by CCR1, CCR2, or TAIFG. As for the situation in which TAIFG is set, it depends on the working mode of TA. Please refer to the user manual. Another point is that TA itself has PWM output function, and there is no need to borrow the interrupt function. The application detour on this issue is how to combine TA and AD to implement timing sampling. Many people open AD in TA interrupt to do so. This is not appropriate, because the ADC10 and ADC12 (SD16 is not familiar, so I have no say) modules of 430 have pulse sampling mode and extended sampling mode. Just select AD to be sampled by TA, and then set TA to PWM output mode. Of course, the special function pins are used to output PWM waves, but it is not needed here, so the pin settings do not need to be paid attention to. What is worth paying attention to is the frequency of PWM, that is, the sampling rate of your AD. 3. Watchdog reset The watchdog has two working modes: timer. In watchdog timer working mode, WDTIFG has a flag bit that automatically resets in response to the interrupt service program, while in watchdog mode, the flag bit can only be cleared by software. But how to judge whether the reset is caused by the timer overflow of WDT working in watchdog mode, or the error of the watchdog writing key? ……………………………… The answer is no method, at least I have not seen any method, nor have I seen any method from people around me. If anyone knows the method, thank you for sharing. 4. People often ask about the function of this statement MOV.B #LPM0,0 (SP). If you are in standby mode in LPM0 before entering the interrupt function, if you require to enter standby mode in LPM3 after executing the interrupt function, writing MOV.B #LPM3,SR in the interrupt function is invalid. Because 430 will push PC and SR to the stack when entering the interrupt (SR stores the low power mode settings). Even if you write MOV.B #LPM3,SR, SR will be reset to low power 0 when exiting the interrupt. To achieve this goal, you can only change the SR settings in the stack: MOV.B #LPM0,0(SP). 5 Interrupt vector: The interrupt vector of 430 is FFE0H-FFFFH, a total of 32 bytes, which is the last segment of FLASH. The FLASH of 430 is large or small, but the last address is definitely FFFFH (except for large FLASH exceeding 64K), so their starting addresses are different. Generally, IAR defaults to compiling and putting the program at the beginning of FLASH (excluding the information segment). A question worth clarifying is: What is an interrupt vector? The interrupt vector is actually the storage unit space that stores the entry address of the interrupt function. Just like FFFEH+FFFFH, these two bytes are the reset interrupt vector, then it stores the starting address of the main function in FLASH. If the main function is saved in the FLASH block with 0x1100 as the starting address, then you will find that FFFFH stores 0x11 and FFFE stores 0x00. Other TimerA, ADC12, etc. are all the same. It's just that the length of the program you write each time is different, and the location of the interrupt function is different. IAR compiler will set it for you, and then when you use JTAG to burn the program, this address will be burned to the corresponding interrupt vector. Because the address of the interrupt function can be customized by the user or automatically compiled by IAR, this address is unknown to others except the source code developer. BSL is the password set by the particularity of the content in the 32-byte interrupt vector. However, there are a few things that are unchanged in 430, that is, after the condition for triggering the interrupt is met, where it goes to address the entry address of the interrupt service function is solidified and fixed when TI made 430. For example, when powering on a reset, it knows to look for the address in the FFFE, FFFF units instead of the FFE0, FFE2 units. This mapping relationship is fixed in the 430. But sometimes you just need to change the "interrupt vector". What should you do? This problem sometimes occurs in the 430 FLASH program self-upgrade. The method is to make a jump operation in the original default interrupt vector table of the 430. Take the power-on reset as an example: ORG 0x2345 PowerReset: mov.w &0xFCFE, PC ………………………… ………………………… ORG 0xFFFE DW PowerReset In this way, 0xFCFE is equivalent to the mapping of 0xFFFE. This is in the TI application report for the 430 program self-upgrade.There are 32 bytes in total, which is the last section of FLASH. The 430 FLASH may be large or small, but the final address is definitely FFFFH (except for large FLASH exceeding 64K), so their starting addresses are different. Generally, IAR defaults to compiling and puts the program at the beginning of FLASH (excluding the information section). There is a question worth clarifying: What is an interrupt vector? The interrupt vector is actually the storage unit space that stores the entry address of the interrupt function. Just like the two bytes FFFEH+FFFFH are the reset interrupt vector, then it stores the starting address of the main function in FLASH. If the main function is stored in the FLASH block with 0x1100 as the starting address, then you will find that 0x11 is stored in FFFFH and 0x00 is stored in FFFE. Other TimerA, ADC12, all are the same. It's just that the length of the program you write each time is different, and the location of the interrupt function is different. The IAR compiler will set it for you, and then when you use JTAG to burn the program, burn this address to the corresponding interrupt vector. Because the address of the interrupt function can be customized by the user or automatically compiled by IAR, this address is unknown to others except the source code developer. BSL is a password set based on the particularity of the content in the 32-byte interrupt vector. However, there are a few things that remain unchanged in 430, that is, after the conditions for triggering the interrupt are met, where it goes to address the entry address of the interrupt service function is fixed and fixed when TI made 430. For example, when powering on and resetting, it knows to look for the address in the FFFE, FFFF units instead of FFE0, FFE2. This mapping relationship is fixed and unchanged in 430. But sometimes you just need to change the "interrupt vector", what should you do? Sometimes you will encounter this problem in the 430FLASH program self-upgrade. The solution is to do a jump operation in the original default interrupt vector table of 430. Take the power-on reset as an example: ORG 0x2345 PowerReset: mov.w &0xFCFE, PC ………………………… ………………………… ORG 0xFFFE DW PowerReset In this way, 0xFCFE is equivalent to the mapping of 0xFFFE. This is in the TI application report of the 430 program self-upgrade.There are 32 bytes in total, which is the last section of FLASH. The 430 FLASH may be large or small, but the final address is definitely FFFFH (except for large FLASH exceeding 64K), so their starting addresses are different. Generally, IAR defaults to compiling and puts the program at the beginning of FLASH (excluding the information section). There is a question worth clarifying: What is an interrupt vector? The interrupt vector is actually the storage unit space that stores the entry address of the interrupt function. Just like the two bytes FFFEH+FFFFH are the reset interrupt vector, then it stores the starting address of the main function in FLASH. If the main function is stored in the FLASH block with 0x1100 as the starting address, then you will find that 0x11 is stored in FFFFH and 0x00 is stored in FFFE. Other TimerA, ADC12, all are the same. It's just that the length of the program you write each time is different, and the location of the interrupt function is different. The IAR compiler will set it for you, and then when you use JTAG to burn the program, burn this address to the corresponding interrupt vector. Because the address of the interrupt function can be customized by the user or automatically compiled by IAR, this address is unknown to others except the source code developer. BSL is a password set based on the particularity of the content in the 32-byte interrupt vector. However, there are a few things that remain unchanged in 430, that is, after the conditions for triggering the interrupt are met, where it goes to address the entry address of the interrupt service function is fixed and fixed when TI made 430. For example, when powering on and resetting, it knows to look for the address in the FFFE, FFFF units instead of FFE0, FFE2. This mapping relationship is fixed and unchanged in 430. But sometimes you just need to change the "interrupt vector", what should you do? Sometimes you will encounter this problem in the 430FLASH program self-upgrade. The solution is to do a jump operation in the original default interrupt vector table of 430. Take the power-on reset as an example: ORG 0x2345 PowerReset: mov.w &0xFCFE, PC ………………………… ………………………… ORG 0xFFFE DW PowerReset In this way, 0xFCFE is equivalent to the mapping of 0xFFFE. This is in the TI application report of the 430 program self-upgrade.

This post is from Microcontroller MCU

Latest reply

Thumbs up, very useful. Thanks for sharing!  Details Published on 2018-12-20 15:41
 

1

Posts

0

Resources
2
 
Thumbs up, very useful. Thanks for sharing!
This post is from Microcontroller MCU
 
 

Guess Your Favourite
Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list