MSP430 provides a very rich interrupt source, including IO interrupt, timer interrupt and some interface interrupt (SPI, UART, I2C) and so on.
Let me share with you some features of MSP430 interrupt.
First, the priority of MSP430 interrupt.
MSP430 supports interrupt priority, but how to know the priority? The interrupt vector table of 430 starts from address value 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 position corresponding to the interrupt occurs is 1. I will describe 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. The interrupt with the best priority is selected 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. When the status register SR is cleared, any low-power state will be terminated, and the global interrupt enable will be 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, _EINT() needs to be used 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.
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. What is the address value of the interrupt service function pointed to in the interrupt vector at this time? 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, and the machine crashed! ! ! !
So I hope that friends who are new to 430 will not enable interrupts if they are not used. If you enable them, you must write an interrupt service function, even if it is an empty function!
1. Interrupt nesting and priority
The control bit of the 430 total 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 be responded to. Maskable interrupts are divided into single interrupt source and multiple interrupt sources. For a single interrupt source, the interrupt flag bit is automatically cleared after responding to the interrupt service program, while for multiple interrupt sources, the interrupt flag bit 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 a 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 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 2 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. On this issue, the application detour often occurs in how to combine TA and AD to implement timed sampling. Many people open AD in the TA interrupt to do this. 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 trigger, and then set TA to PWM output mode. Of course, the output PWM wave is a special function pin, but it is not needed here, so the pin setting does not need to be paid attention to. What is worth caring about is the frequency of PWM, that is, the sampling rate of your AD.
3. Watchdog reset
The watchdog has two working modes: timer, watchdog
timer working mode WDTIFG has a flag bit automatically reset in response to the interrupt service program, and in watchdog mode, the flag bit can only be cleared by software. But how to judge whether the reset is caused by the timing overflow of WDT working in watchdog mode, or the error of watchdog writing key? ………………………………
The answer is no method, at least I have not seen any method, nor have I seen any method around me. If anyone knows the method, thank you for sharing.
4. People often ask the function of this statement MOV.B #LPM0,0(SP). If you are in standby mode in LPM0 before entering the interrupt function, and you want to enter standby mode in LPM3 after executing the interrupt function, writing MOV.B #LPM3,SR in the interrupt function is invalid. Because when entering the interrupt, 430 will push PC and SR to the stack (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 setting 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).
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, it stores the starting address of the main function in the FLASH. If the main function is stored in the FLASH block with 0x1100 as the starting address, 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, 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 special content of the 32-byte interrupt vector. But there are a few things that are unchanged in 430, that is, after the conditions for triggering an interrupt are met, where it goes to address the entry address of the interrupt service function is fixed when TI makes 430. For example, when powering on and resetting, it knows to find the address in FFFE, FFFF units instead of FFE0, FFE2. This mapping relationship is fixed 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 method is to make 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.
|