#16 Interrupts Part-1: What are interrupts, and how they work
HTML-код
- Опубликовано: 1 авг 2024
- In this lesson, I'll finally tackle the subject of interrupts. Today, you will learn what interrupts are, and how they work.
------
Resources:
Companion web page for this video course:
www.state-machine.com/quickstart
GitHub repository for projects for this video course:
github.com/QuantumLeaps/moder...
Transcript of this lesson:
www.state-machine.com/course/...
Music credits:
The background music comes from:
www.bensound.com/royalty-free... Наука
The Modern Embedded Systems Programming course has reached a very "round" milestone of 0x10 lessons. This milestone lesson provides an introduction to the very interesting subject of interrupts.
Thank you very much for your efforts, I really enjoy your courses.
You are awesome. Thank you very much.
Thank you a milllion times for your efforts Miro. You are a gem
Thank you so much for putting these lessons on the inter tubes. This information is extremely valuable and very well explained. As a systems designer, I only wish I could work with you on a daily basis.
Absolutely neat! I wish I could have taken these kind of lessons a long time ago. Thank you very much Samek
Very comprehensive lesson. Finally somebody which explains the entire interrupt system form a practical perspective. I found out about interrupts the hard way:)) (just from a datasheet)
Crystal clear explanation. Thanks for your efforts
Finally!! Thanks alot, I will watch it immediatly, and as always, waiting for the next one :) Thanks alot again
the most excellent course. 10000000000 x thanks
Yes it is very well explained , keep up and do not forget we are waiting for the next adventure :)
Fantastic! Thank you so much for these videos!
Great lesson, thank you very much!
Your videos are awesome man thanks a lot
Thank you dr. Samek! Davor
You are amazing, full stop.
Amazing.. Thank you sir
this is amazing..thanks
Hi Miro
We were waiting since February, but never mind, do not forget us
These lessons are phenomenal, thanks Miro! When I added the inline function "__enable_interrupt();" line to my main.c, I received an Linker error stating no definition for "__enable_interrupt". I fixed the error by adding #include "Intrinisics.h". I'm using IAR 9.20.4. But interestingly, I opened Miro's workspace and did not need the include statement. Curious as to what workspace setting would be causing my error.
I'm glad that you like the lessons. The IAR header file is actually included at the top of main.c (at least in the project accompanying this lesson #16). The facility "__enable_interrupt()" is interesting, because it is a so-called "intrinsic" function that does not expand to a function call. Instead, the compiler expands it in-line thus avoiding the function call (and return) overhead. You can of course verify that by checking the disassembly in the debugger. When the header file is missing, the compiler does not recognize "__enable_interrupt()" and so it generates a regular function call to it. Then the linker tries to find this "function" and fails. --MMS
@@StateMachineCOM Thanks for the explanation! I was trying not to use any of your files as I went through the lessons; And yes, I now see the after checking your github. Thanks so much for continuing to respond and support others questions for these lessons!
Thanks. I'm experiencing the same problem of @AbdulSattar. When I comment out `__enable_interrupt()`, the code still works and blinks like that PRIMASK is cleared by default. Why is this happening?
Mr Miro do you recommand a book to well understand the start-up code and unterrrupts ?
HI nice tutorials, but I have a question, I am using my own header file for the registers definition, so when I use an interrupt I need a file to be able to use the handler functions (ISR) and I am unable to use the IAR core files because they are compatible only with the IAR lm4f120h5qr.h file, what can I do now ?
Finally new update.
Dr. Samek, thank you again for your lessons. I am interested in inspecting the __enable_interrupt() function. I would think it can be found in one of the .a files in the IAR Workbench arm/lib directory. I saw your example the other day of the ielfdump.exe tool for inspecting .o files. Is there a good way to find / inspect the contents of .a files? Or am I going in the wrong direction? Thank you very much for your time!
Actually no. The special functions like __enable_interrupt()/__disable_interrupt() cannot be found in any library. This is because they are *inlined*, meaning that the compiler inserts the code for such functions directly at the place of use. This means that the function does not exist as a separate entity. Such "inlining" of functions eliminates the function call overhead (branching into some code and branching back), because in that case the overhead is larger than the actual work to be done. Now, if you want to inspect the function __enable_interrupt(), the only way is to see the disassembly. For that, you can set a breakpoint at the function and run the code. When the breakpoint is hit, just see the disassembly. You will see something like "CPSIE I", and not "BL __enable_interrupt". --MMS
When I used the simulator the Interrupt doesn't fire at all, any suggestions?
Hi, Miro! Could you explain why the interrupt stack has to be 8-byte aligned? What are the constraints exactly that led to this requirement? What would happen if we don't align it? Thanks. E
As mentioned in the video, the exception entry is highly optimized in the Cortex-M CPU and takes only some 12 cycles. It's very fast, if you consider that 8 registers are copied to the stack memory during those cycles. But the price for this speed is that the stack pointer must be aligned at 8-byte boundary. If the stack were not aligned, the hardware would add an extra "aligner" word to make it so (see the "aligner" in the exception stack frame in the TivaC data sheet).
At 8min 48s, why the delay function doesnt get invoked on the disassembly view? Thanks!!!
The delay() function *gets* called! You don't see it, because the debugger stops only at a breakpoint at the top of the loop. Please note that the delay incurred by the delay() function is only 1/2 second, which is about the time between hitting the breakpoint. --MMS
I noticed that the SysTick_Handler is getting called even without __enable_interrupt(). Could this be because primask is cleared by default in the new Tiva C Series launchpad ?
Actually, the ARM Cortex-M CPU comes out of hardware reset with PRIMASK cleared. The __enable_interrupt() call is just a precaution in case the startup code, some library code, or your own code happens to disable interrupts. In that case, an interrupt-driven application like the project for this lesson will appear completely "dead". For that reason, it is just safer to explicitly (re)enable interrupts to be sure. I hope this makes sense to you. --MMS
Just a heads up, after adding the __enable_interrupt() function to clear the PRIMASK register, my code would not link properly in IAR.
After looking in the help section, I found out the header file "intrinsics.h" must be included to use __enable_interrupt(). Once I included
the header file it compiled and linked properly.
Hi Michael. Thanks for watching. The system header files like , , and "tm4c_cmsis.h" *are* included in main.c. I don't know how you could miss this. Please check the project downloads at state-machine.com/quickstart
@@StateMachineCOM Ahh, I believe that's where the confusion was. I didn't have in my main.c file because I copied the code from the previous project to start project 16 instead of downloading from the website. Thank you for the response and the phenomenal work you do with this channel. Your content has been of great value to me as my computer science curriculum does not delve deeply into embedded systems.
Dear Miro Samek,Today I downloaded this lesson (lesson 0x10) from state-machine.com. and run code it worked as expected. In the last of this presentation you said that final touch is to globally enable interrupt by clearing PRIMASK register. For this IAR EW provide intrinsic function __enable_interrupt(). But interesting thing is that I deleted that function and recompile and burn to tiva Launchpad, and interrupt is still firing without this function, would you please explain how is this happening. I tried to solve this mystery but could not. Thanks,Abdul Sattar
May be because you forgot to clear the flag...well its been 3 years now.!!!! you probably know it now anyways!
Hi. Thank you so much for this tutorial. Since the last lesson (lesson 15), I wasn't able to successfully flash a program to my launchpad anymore. Do you think I have done something wrong? Or is my launchpad already damaged?
Your TivaC board might be "locked up", but you can recover it. Please visit the companion web-page to this video course at www.state-machine.com/quickstart/ , where you will find the Application Note "Troubleshooting TivaC LaunchPad". This AppNote describes how to recover your locked-up TivaC LaunchPad. --MMS
@@StateMachineCOM wow. Thank you so much. That was cool troubleshooting indeed. More power.
I hadn't run the code on the board for a bit but decided to at the end of this lesson. When I did, the LED was blinking between blue and both blue and red, like it had been set up to do in previous lessons. Turns out this was because the LED_BLUE bit had never been cleared. It carried its value despite the board being off for a full day. Goes to show you can't assume initial values will be 0 (at least for AHB it seems). This makes me wonder though, would it be good practice to, for example, explicitly set LED_BLUE and LED_GREEN to 0 in your code if you're trying to only blink the red LED, so that you can avoid unexpected behavior?
This is strange and I would first check your code. But yes, it is considered a good practice to explicitly set or clear the GPIO lines to the desired initial state right after configuring them. --MMS
I had the same issue. I ended up resetting the board using the troubleshooting guide, and this seems to have done the trick...
When configuring the SysTick why didn't you use the bitwise operators? Also in turning on the LED_BLUE before the delay loop you dont use them, but when toggling the LED_RED you use them (I understand why they are required for toggling). I am a bit confused on when to use them and when not.
Davor Juric
SysTick has only three registers (LOAD, VAL, and CTRL) and I was sure that they were not shared with the rest of the application, so they were simply written-to.
The turning of LED on and off was done without the need to read the previous state of the LED, because you know that you want to turn it on or off.
But toggling LED is fundamentally different. You want to change the current *state* of the LED, so you don't know if you want to turn it on or off. You want to toggle, so you must read the current state of the LED. Hence the use of the ^= bit-wise operator.
Quantum Leaps, LLC I see. Thank you!
If there is any way I can contribute for your excellent work please let me know!!! will you talk about PWM any time soon??
*****
I hope I will talk about PWM some day, but for now I still have a lot to cover about interrupts, race conditions, critical sections, context switch, etc. All of this leads directly to real-time kernels, for which I also received a lot of requests.
As far as PWM is concerned, for now I would highly recommend this article:
www.barrgroup.com/Embedded-Systems/How-To/PWM-Pulse-Width-Modulation
--MMS
Quantum Leaps, LLC Thank you!!
In which file can I find the definition of __enable_interrupt()?
The intrinsic function __enable_interrupt() is defined in the IAR file . This file is included indirectly through the following include chain: tm4c_cmsis.h -> core_cm4.h -> core_cmFunc.h -> cmsis_iar.h -> intrinsics.h. --MMS
Thank you very much!
I'm using IAR v. 8.30.1. I copied the entire 'lesson16' folder from the url you provide. I ran the workspace.eww file. Even though "tm4c_cmsis.h" is included, I still had to manualy include "intrinsics.h" because otherwise the function __enable_interrupts() was 'declared implicitly' and I received the compiler error.
what is the equivalent of the __enable_interrupt() in GNU ? For example when I am using say CCS instead of IAR ?
Found it in the video #19, we can use __enable_irq(); Thanks for moving the course to GNU!
In GNU-ARM you can use __disable_irq()/__enable_irq() (see also stm32f4-discovery.net/2015/06/how-to-properly-enabledisable-interrupts-in-arm-cortex-m/ ) --MMS
The "__enable_interrupt()" function is specific to IAR? Is there a generic way to do this? Seems like something everyone needs to do. Is this series of videos funded by IAR?
Yes, the __enable_interrupt() function is specific to the IAR compiler and other compilers for ARM might call this operation differently. For example, in lesson #19 I switch to the GNU-ARM compiler, where this function is called __enable_irq(). Both these intrinsic functions end up generating the same machine instruction "CPSIE i". In the future lesson 21 (still under development) I will demonstrate how to accomplish such operations generically. No: this video course is not funded by IAR. Why are you oozing so much negativity? --MMS
Actually it's a great feature of this course that Miro changes development tools in #19 and walks through the impact on the project. He also provides the source code for his QP framework online if you want to take a look at that and see how he supports a wide variety of tools and devices.
I plan to introduce the QP framework in the new group of upcoming lessons. This new group will cover topics like: foreground/background architecture, Real-Time Operating System (RTOS), object-oriented programming (yes, we will see how virtual functions work!), event-driven programming, state machines, and automatic code generation. The new lessons are coming soon. Stay tuned!
Dr. Samek, the interrupt handler must be in a separate file because it's similar to a normal function in C? I tried to keep the interrupt handlers prototypes in a separate file and the handler itself in the main.c function and got a very strange error: Error[Pe065]: expected a ";" The problem is that this error points to the end of the definition and before the start "{", like this:
here ------------>|
void SysTick_Handler(void) { GPIOF_AHB->DATA ^= 0x04; }
This seems to be a cryptic error. Defining an interrupt handler in main.c causes the function to be called twice?
Thanks.
High Altitude Observer
Interrupt handlers are regular C functions in ARM Cortex-M. You can define these functions anywhere you like. You could put them in main.c or in any other module, such as bsp.c. The prototypes of all interrupt handlers are provided in the TM4C123GH6PM.h header file, so you should include this header in the module with the definition(s) of any interrupt handlers.
I'm not sure why you get this error, but I would strongly recommend that you start with the working project provided with this lesson. Only after you make sure that you can build and run this project, I would apply any changes.
Quantum Leaps, LLC
Thank you very much for your response.
+Quantum Leaps, LLC
So what I'm reading here is that you can define your own interrupt handler without CMSIS? Because the key issues I'm having trouble with is how would the Interrupt call Systick_Handler? There seems to be no connection especially when you can define your own interrupt handler.
+Helios Fire The connection is there. When the SysTick interrupt occurs, the hardware will jump to the address at offset 15 from the beginning of the vector table. You can place an address to your own function there. The only requirement of CMSIS is that you call this function SysTick_Handler. But you could just as well call it Fred (in which case you will not be compliant with CMSIS).
OK, it's making a little bit more sense. But I have to add the function inside the CMSIS vector table first? I can call it "Fred", or "Cat" alsong as its in at offset 15 of the vector table in CMSIS?
Has anyone tried to implement the IRQ for either of the pushbuttons? I have been trying to do it but I have had no success. I configured GPIOF as follows:
SYSCTL->RCGC2 |= 0x20; // Disable clock gating of GPIOF
SYSCTL->GPIOHSCTL |= 0x20; // Enable high speed bus
GPIOF_HS->LOCK |= GPIO_KEY; // Unlock GPIOF
GPIOF_HS->CR |= 0xFF; // Enable GPIOF commits
GPIOF_HS->PUR |= 0x11; // Set GPIOF push button pins to pull-up
GPIOF_HS->DIR |= 0x0E; // Set GPIOF LED pins to output
GPIOF_HS->DEN |= 0x1F; // Set GPIOF pins to digital enabled
// Configure interrupt for push buttons
__enable_interrupt();
__NVIC_SetPriority(30, 1);
GPIOF_HS->IM &= 0x00; // Mask all GPIOF interrupts, preventing iterupts
GPIOF_HS->IS |= 0x11; // Configure GPIOF push button pins for low level detection
GPIOF_HS->IM |= 0x11; // Un-mask GPIOF push button interrupts, enabling them
As far as I can tell this should enable the GPIOF IRQ when either of the push buttons is pressed, but when I set a break point to my handler function:
void GPIOPortF_IRQHandler(void)
{
while(!GPIOF_HS->DATA_Bits[0x1] || !GPIOF_HS->DATA_Bits[0x10])
{
GPIOF_HS->DATA_Bits[LED_W] = LED_ON;
printf("On
");
}
}
The IRQ never executes. Any ideas?
The GPIO interrupt handling will be shown in the new upcoming lesson #21 about foreground/background systems. --MMS
can u solve this problem ??
Design 8051 based system to count total number of persons getting-in and
going-out with a single door through INT0 and INT1 and display the number of
people in the Hall on P1. While generate 100KHz, 250KHz and 375KHz on P2.0,
P2.1 and P2.2 respectively and transmitting persons info via UART at 4800bps.
In the video sound is low please increase sound level
nic
If a hardware interrupt occurs, does this pause the timer? Also, can the timer be used to do context switches to mimic threaded behavior? Any chance you will do a video on that?
+Putins Cat The SysTick timer is not paused when interrupts occur. However, when the SysTick interrupt itself occurs, the SysTick counter is re-set to the original pre-programmed value. Regarding context switches, almost any interrupt can trigger them. I plan to talk about RTOS kernels and context switches in the future.
Quantum Leaps, LLC Is this dangerous?
asm("movs r10,#0");
asm("ldr r11,[r10]");
asm("mov sp,r11");
asm("ldr r11,[r10,#4]");
asm("bx r11");
I am simulating an ARM with the IAR. During an interrupt, I want to handle it, and then call these instructions. I believe they restart ( clear the stack and then end up at the beginning of main ). Now my problem is, how can I initialize something just the first time and not during each reset? Should I put some secret value in some memory address? Is there a startup routine before main? Thanks.
Quantum Leaps, LLC I was watching lesson 13. What I need is a way to clear the stack and go back to main I guess. While my code does this, it seems like if I follow lesson 13, my code will do a complete reset. I couldn't get the program to jump to main. Do you know the command that would allow that??
This is for a robot about to go over a cliff situation. I need its behavior to restart instead of jumping back to whatever it was doing before interrupt. But, I don't want to rerun initialization code.
+Putins Cat If you wish to reset the ARM Cortex-M CPU, the recommended way is to call the CMSIS function: NVIC_SystemReset().
Quantum Leaps, LLC I do and I don't. I want to start main() over, but I don't want to reinitialize everything necessarily. I need to come back to a known state after an interrupt. How do you handle that typically?