Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Adding timer for digital clock.

Xenon02

Full Member level 3
Joined
Nov 12, 2022
Messages
157
Helped
0
Reputation
0
Reaction score
0
Trophy points
16
Activity points
2,205
Hello !

I've been wondering if adding timer for my digital clock is a good idea.

I've added RTC to calculate the time and then every second the timer triggers the interrupt to update the time on my LCD screen and I wondered if it's a good idea.
Because there are other processes in the main and wondered if they are in the middle of executing the code and then it is interupted then if it's ok or not. I also added the timer to make sure that it will update every 1 second just thinking if I couldn't make it better but didn't want to add the update function into main while place because it would update with different time than constant 1 second. But also interupting the code that is executed right in the middle or anywhere else (for example uart I want to send 8 bytes and then interrupt occures and while he was sending second byte or third byte).

I mean I know that I have to remember about times etc. but wondered if it's a good idea because I couldn't find a better solution and DMA I guess, didn't know how to trigger update function.
 
Whenever you deal with real time events, even your one second interrupts, you have to be ready before the event next occurs. Interrupts normally have higher priority than other operations, they tell the MCU something needs immediate attention so even if main() is doing something else it will jump to the ISR.

Making a variable volatile does two related things, mostly to do with the compilers optimization routines. Firstly, it tells the compiler not to cache the variable in an internal register, secondly it tells it not to optimize out an operation that doesn't seem to change a value.

For example:
int A = 1;
if(A == 1) {... do something};

the compiler might look at that and say "I know A will always be 1 so don't compile the == part. Now consider what would happen if an interrupt occurred and the ISR contained the line "A = 2;" , the main program would have no knowledge that A could be changed elsewhere and after returning from the ISR, A would hold the wrong value. Declaring it as volatile prevents the instruction being ignored by the compiler and also tells it not to assume A still holds 1. It actually makes code that stores A in memory instead of a register and reads it again before using it.

Brian.
 
For example:
int A = 1;
if(A == 1) {... do something};

the compiler might look at that and say "I know A will always be 1 so don't compile the == part. Now consider what would happen if an interrupt occurred and the ISR contained the line "A = 2;" , the main program would have no knowledge that A could be changed elsewhere and after returning from the ISR, A would hold the wrong value. Declaring it as volatile prevents the instruction being ignored by the compiler and also tells it not to assume A still holds 1. It actually makes code that stores A in memory instead of a register and reads it again before using it.

So basically even though the program jumped into ISR and changed the A value the main program won't know it without volatile because the compiler optimized the code ?
It is a bit hard to believe because putting a breakpoint there shows that it sees this change but then at the same time it didn't. Well I guess this is about code optimization what he interprets etc. I don't know :D

But okey, so basically the code optimalization sees this A value that is not being changed or rather sees the function that changes the A value but is not called in the main function so the optimalization thinks it will be constant all the time, won't be changed. That's why I need to use volatile.
The only thing was that breakpoint I putted into that ISR that changes the variable value, it shows that it sees that instruction interesting. But as you said maybe optimalization ignores this part of the code.

Well I think I partly get it, although I don't know how could I better write my display screen update.
I gave some of my worries like using the update in the main but there will be a delay if I add more programs, or if different part of the programs is being finished then it goes to the screen there is a bigger delay (because if the program leaves ISR it lands where he was before in main program).
And other stuff. The flag thing is a thing I will do but the delays I see using it might be a problem like adding a flag for LCD display, flag for mp3 etc. And while mp3 is being executed and the clock display will also get an updated flag, the mp3 must be finished (because it was already in the middle of executing) and it cannot be stopped because it is in main function then after mp3 there comes the clock display, massive delay I guess.
 
Depending on the compiler, they generally will compile each function (including the ISRs) separately. That is because normally the code within a function is executed independently of other functions. Even when a function calls another function, the compiler has no understanding of that the called function may do and so ensures that all of the context that it needs is maintained across that call. (Think about when you call a library function or a function in a separately compiled module - where you make the call you only know the declared function header.)
When it comes to ISRs, the compiler has absolutely no way of knowing when the ISR will be triggered and therefor can't save any context across that call. (That is why the ISR must generally do the context saving and restoring itself - at least to save whatever internal registers it uses.)
In your case where you see the ISR change the value if 'A', it has no idea if that variable has been copied into a register somewhere else and so it will read the value from the variable address and write the value back to the address.
Now, in your 'normal' code, you set the value of the variable and then start to use it. The compiler 'knows" this and so puts the value of the variable in a register for easier access. If your ISR is then called, it will save that register value, update the variable but then restore the register. Now back in your main code, it has no real idea that the ISR has just been executed and so thinks that the register value is still 'valid' and starts to use that. Of course it is the value the variable had BEFORE the ISR updated it.
Using 'volatile' gets around many of these issues in that it tells the compiler to never assume a register stored value is still correct. (Of course if the ISR is called just after the variable is read then is will still be the 'old' value - and tis can be a particular problem when you are dealing with (say) 32-bit values in a 16-bit machine where two successive operations are required to access the variable - if the ISR occurs between those successive operations then you could get an invalid result. This is often referred to as non-atomic operations.)
As for setting a flag i the ISR and then processing it in the main loop, that is generally a good approach. However if you have some operation that is very quick - say moving the next audio sample to a DAC for your MP3 player or whatever - then you can do that directly in the ISR. The key think to consider is to keep the execution of ISRs as short as possible.
If the design of your application is such that you simply don't have enough processing time between interrupts to do all of the work triggered by that interrupt then you have an architectural problem, and either need to try to move more into the hardware modules (DMA etc.), increase the clock speed or use another MCU that might have additional cores.
Susan
 
Hi,

I wonder why nobody comments the OP´s code approach of post#20.
It shows a total misunderstanding of the interrupt concept.

There are at least these issues:
* missing "volatile" statement. (already mentioned)
* missing of default value for the flag
(I even call the "unconditional" setting of the flag as an issue)
* calling the ISR function from main()
* missing the loop in main (usually "while(1)")
* doing a busy_wait for the flag to become set
* missing to clear the flag in main()

****
concept on "volatile:
Let´s imagine in this example "you" are the main().
* let´s say you get a box. There is a chocolate bar inside.
* you open it once, see the chocolate bar, close the box.
* from now on you know there is a chocolate bar inside.
* you know that you did not eat it, so nothing changes.
* and if you eat one part of the chocolate .. you know how much is left.
So every time someone asks you what´s inside the box you could open the box to see what´s inside.
But that is not effective, bacause you already know what´s inside.
Thus you can optimize. Don´t open the box a second time. Just say "there is a chocolate bar inside"
This is what the compiler does: Without the "volatile" statement, it once reads the content of the flag ... but then never anymore.

Now same situation, but there is an egg instead of a chocolate bar in the box.
Open it once, you see there is an egg.
Close the box.
--> without the "volatile" statement: On request - you don´t open the box - just answer "there is an egg inside"
--> with the volatile satement: On request - you open the box - and answer whether there is still an egg inside ... or it "became" a chicken.

So the compiler per default optimizes the code for better efficiency. It omits code that periodically reads the "flag" state. It reads it once and then assumes "flag" does not change.
With the "volatile" set, the compiler does not optimize on reading the "flag". It really accesses the "flag" every single time.

Klaus
 
Depending on the compiler, they generally will compile each function (including the ISRs) separately. That is because normally the code within a function is executed independently of other functions. Even when a function calls another function, the compiler has no understanding of that the called function may do and so ensures that all of the context that it needs is maintained across that call. (Think about when you call a library function or a function in a separately compiled module - where you make the call you only know the declared function header.)
When it comes to ISRs, the compiler has absolutely no way of knowing when the ISR will be triggered and therefor can't save any context across that call. (That is why the ISR must generally do the context saving and restoring itself - at least to save whatever internal registers it uses.)
In your case where you see the ISR change the value if 'A', it has no idea if that variable has been copied into a register somewhere else and so it will read the value from the variable address and write the value back to the address.
Now, in your 'normal' code, you set the value of the variable and then start to use it. The compiler 'knows" this and so puts the value of the variable in a register for easier access. If your ISR is then called, it will save that register value, update the variable but then restore the register. Now back in your main code, it has no real idea that the ISR has just been executed and so thinks that the register value is still 'valid' and starts to use that. Of course it is the value the variable had BEFORE the ISR updated it.
Using 'volatile' gets around many of these issues in that it tells the compiler to never assume a register stored value is still correct. (Of course if the ISR is called just after the variable is read then is will still be the 'old' value - and tis can be a particular problem when you are dealing with (say) 32-bit values in a 16-bit machine where two successive operations are required to access the variable - if the ISR occurs between those successive operations then you could get an invalid result. This is often referred to as non-atomic operations.)
As for setting a flag i the ISR and then processing it in the main loop, that is generally a good approach. However if you have some operation that is very quick - say moving the next audio sample to a DAC for your MP3 player or whatever - then you can do that directly in the ISR. The key think to consider is to keep the execution of ISRs as short as possible.
If the design of your application is such that you simply don't have enough processing time between interrupts to do all of the work triggered by that interrupt then you have an architectural problem, and either need to try to move more into the hardware modules (DMA etc.), increase the clock speed or use another MCU that might have additional cores.
Susan

Although I wonder if for simple programs it works similarly.

If I had

Main()

{
a= 3
b= 2
S = Sum(a,b);

Return 0;
}

The program is optimized and I ignores the function and sets S to always be 5.

I mean this wondering comes when I usually give a breakpoint because it shows that the program goes from instruction to instruction. I haven't noticed that it ignores an instruction before.
But maybe it ignores. But usually in normal programs I don't have to worry about volatile. So I tried to understand the difference analyzing the code of ISR and a function from other modules called once or twice.

It is weird but I understand that ISR can be called anywhere and the compiler doesn't know about that so he might simplify it. Still weird because the breakpoint always lands there whenever there is an interrupt so I thought that the function sees it and executes the whole program instruction by instruction and I haven't used volatile before. It worked but as I said still weird.

But okey I will use volatile and the flag inside the ISR. But are still problems I have mentioned at the begging of post#20

Thanks for help
 
Optimizing the function might happen, it depends on the compiler and how vigorously it does the optimization. Certainly if the "Sum()" function was used in another part of the program it wouldn't remove it as the 'a' and 'b' might have different values in that instance.

Conceptual point: if you put a breakpoint in the ISR it will always show you the ISR was called - otherwise it wouldn't 'break'!

Remember , as Susan pointed out, the ISR can be called at any time without the main() knowing when it might be and when the ISR ends, it goes back to main() without main() knowing it ever happened. It's rather like the program flow freezes, the MCU does something else then the program thaws out. The program flow doesn't know the ISR code was ever executed or whether any values were changed by it. That's why some kind of 'flag' or indicator is used, the ISR sets it and then the main() code can check it and reset it.

Brian.
 
Optimizing the function might happen, it depends on the compiler and how vigorously it does the optimization. Certainly if the "Sum()" function was used in another part of the program it wouldn't remove it as the 'a' and 'b' might have different values in that instance.

Conceptual point: if you put a breakpoint in the ISR it will always show you the ISR was called - otherwise it wouldn't 'break'!

I also compared this sum in my example with the ISR function in post#20.
So I thought that if the ISR was called (I know no one would do it normally to use ISR function in the main it is just an example), then it would be still optimized even though It was used in main just like Sum() optimized. That's why the optimazation is kinda weird for me.

About the breakpoint yea you are right ;D
I just wanted to point out that if I breakpoint in ISR particularly in that variable flag, that it changes the value, the breakpoint pointer/arrow follows the whole ISR instruction as if he sees it and didn't ignore it ( I think so because he pointed into that variable).

Remember , as Susan pointed out, the ISR can be called at any time without the main() knowing when it might be and when the ISR ends, it goes back to main() without main() knowing it ever happened. It's rather like the program flow freezes, the MCU does something else then the program thaws out. The program flow doesn't know the ISR code was ever executed or whether any values were changed by it. That's why some kind of 'flag' or indicator is used, the ISR sets it and then the main() code can check it and reset it.

I do remember and that's why I gave some similar example or the breakpoint view.
That the main program freezes, it goes to ISR follow the whole instruction and goes back as if he didn't know it happened. I wondered how he doesn't know it, it freezed and pointed to the ISR and follow every instruction, read all of it and maybe even executed then he went back to the main.

I also remember the example where you've showed me the A variable example. Although I haven't experienced that the variable was ignored or so.

This whole concept is weird. Like the program sees it but ignores it ? He even points at it.
So I also tried to compare this ISR situation to normal functions why it doesn't need volatile when the optimization sees only the header of the function lie Susan has mentioned. Hmmm as I said I partly understand it.

But also I have asked about if I used the volatile and putted the display update into the main function and the problems I have spotted that could occur in post#20 at the beginning.
 
You are thinking of the ISR as a subroutine which is wrong. The ISR is a program in itself but it shares resources with your main program. You call a subroutine and may pass parameters to it and get a result back from it. An ISR isn't called, it is triggered by some event, either internally like from a timer or externally from a signal on a pin. An ISR never returns a value but it can change values that are stored in memory. Perhaps this will explain better:
1. You are watching a recorded movie on TV.
2. Your phone rings unexpectedly.
3. You hit the 'pause' button to freeze the movie.
4. You answer the phone call.
5. Your call finishes and you hang up.
6. You press 'play' and continue watching the movie.
What was unpredictable was when the phone call arrived, you didn't plan it and it could have happened anywhere during the movie, however you did something, talked or typed a message on the phone then resumed watching TV and didn't miss any of the movie.

Answering the phone call is like the ISR, it was an interruption to your viewing but didn't stop you seeing the whole movie.

Going back to the 'volatile' issue, the ISR uses the same registers in the MCU as in your main program and for your main program to resume after the ISR it needs to have the same values in the registers restored. If the registers were changed by the ISR code and you just returned with them as they are, the chances are the main program would crash or misbehave, especially if things like return addresses were changed. So the ISR has to save the MCU register values at its start and restore them just before ending. The problem is this can also apply to whole variables, the solution is to declare variables that might be changed by the ISR as volatile so the compiler is warned to generate extra code to check the value is still valid. You only have to use volatile on variables shared with the ISR and that might have their values changed within the ISR.

Brian.
 
You are thinking of the ISR as a subroutine which is wrong. The ISR is a program in itself but it shares resources with your main program. You call a subroutine and may pass parameters to it and get a result back from it. An ISR isn't called, it is triggered by some event, either internally like from a timer or externally from a signal on a pin. An ISR never returns a value but it can change values that are stored in memory. Perhaps this will explain better:
1. You are watching a recorded movie on TV.
2. Your phone rings unexpectedly.
3. You hit the 'pause' button to freeze the movie.
4. You answer the phone call.
5. Your call finishes and you hang up.
6. You press 'play' and continue watching the movie.
What was unpredictable was when the phone call arrived, you didn't plan it and it could have happened anywhere during the movie, however you did something, talked or typed a message on the phone then resumed watching TV and didn't miss any of the movie.

Answering the phone call is like the ISR, it was an interruption to your viewing but didn't stop you seeing the whole movie.

Going back to the 'volatile' issue, the ISR uses the same registers in the MCU as in your main program and for your main program to resume after the ISR it needs to have the same values in the registers restored. If the registers were changed by the ISR code and you just returned with them as they are, the chances are the main program would crash or misbehave, especially if things like return addresses were changed. So the ISR has to save the MCU register values at its start and restore them just before ending. The problem is this can also apply to whole variables, the solution is to declare variables that might be changed by the ISR as volatile so the compiler is warned to generate extra code to check the value is still valid. You only have to use volatile on variables shared with the ISR and that might have their values changed within the ISR.

I have used copilot as well to understand this topic.

Because I was just shocked that I've added the breakpoint inside the ISR function I see the variable updated and nothing crashed, and everything worked well.
Copilot said that it can optimize the code in a way that the debugger will show me that the variable was updated but the code that the compiler made ignores reading from this variable again, even though the variable was updated.

That's what I understood. So that the debugger won't show me whether the variable was updated and read correctly or not, because the compiler optimization made the code smaller etc.


So I was thinking that the debugger points into the variable A in main code, we stop there and it shows the A was updated but the result will be the old A. Because the optimized code ignores that the A was updated even if the variable A is in main and also the debugger says it is updated in the main code but it will ignore it.

I'll try to use the example :

1)without volatile

int A = 1;

ISR()
{
A++;
}
main()
{
while (A == 1)
{
//Do something
}
}


So here I put the breakpoint where is while, the ISR happened once, so the breakpoint will show me that A is equal 2 and the A is in the main code and the debugger shows that A is equal 2 and the while will not continue but in reality the A is still equal 1 ?
That's what I understood I guess. Because the debugger was the confusion for me. That is shows the program works fine but in reality it doesn't.

If I add volatile

1)with volatile

volatile int A = 1;

ISR()
{
A++;
}
main()
{
while (A == 1)
{
//Do something
}
}

Then the A will be updated and either the code will stop and the peripheral or anything inside of this while ?

I understand your analogy I try to understand why the debugger shows me something is updated and the code that it works correctly, but then what you said it could work differently in reality.

So the debugger will show me the code works correctly but in my board it doesn't have to as I understand ? Or rather the A is updated as 2 and the debugger sees that the A is equal 2 but the while is still being executed as if the A is equal to 1. And this problem is exactly the lack of volatile ?

I think so.
 
Hi,

"A" always becomes updated, independent of using volatile or not.

But A does not become checked in case of missing "volatile"

You need get the step from C to assembler.
You write C code, but the compiler generates Assembler code.
When writing C code you don´t see what happens in detail:
"A" is a variable stored in SRAM.
But in order to be checked ( ... == 1) "A" needs to be transferred to a microcontroller_Register first.


So the assembler pseudo code may be looks like this: (without "volatile")
1: transfer SRAM variable "A" to register1
2: compare register1 with 1
3: if equal jump to "do something"
4: jump to line 2
It is faster in processing, but if "A" changes it will have no effect, because A is only once transferred to a register, then never again.


The assembler pseudo code may be looks like this: (with "volatile")
1: transfer SRAM variable "A" to register1
2: compare register1 with 1
3: if equal jump to "do something"
4: jump to line 1
It is slower in processing, because "A" has to be transferred to a register on every loop.


Klaus
 
Hi,

"A" always becomes updated, independent of using volatile or not.

But A does not become checked in case of missing "volatile"

You need get the step from C to assembler.
You write C code, but the compiler generates Assembler code.
When writing C code you don´t see what happens in detail:
"A" is a variable stored in SRAM.
But in order to be checked ( ... == 1) "A" needs to be transferred to a microcontroller_Register first.


So the assembler pseudo code may be looks like this: (without "volatile")
1: transfer SRAM variable "A" to register1
2: compare register1 with 1
3: if equal jump to "do something"
4: jump to line 2
It is faster in processing, but if "A" changes it will have no effect, because A is only once transferred to a register, then never again.


The assembler pseudo code may be looks like this: (with "volatile")
1: transfer SRAM variable "A" to register1
2: compare register1 with 1
3: if equal jump to "do something"
4: jump to line 1
It is slower in processing, because "A" has to be transferred to a register on every loop.


Klaus

So I was partly right I guess ?
Because this is how I could detect an error I guess. That the debugger will point at while in which says A == 1 but A = 2 so and it still executes the while, so the debugger will go into while loop instead of skipping it. Because debugger will show the real values of the variables, and follows the code that is executed. I think so.

So it will show again that A = 2 and while will compare in debugger 2 == 1 but the debugger will say it's ok and go inside the debugger.

Or rather the debugger doesn't know which value is actually compared in while() it just follows what assembler code is actually executing.
 
Hi,

the debugger does exactly the same as the real hardware. It uses the same code.

But if you look at the SRAM cell of "A" you will see the actual "A" value.

Klaus
 
Hi,

the debugger does exactly the same as the real hardware. It uses the same code.

But if you look at the SRAM cell of "A" you will see the actual "A" value.

Klaus

I don't get it.

Then if debugger does the same as the code, he jumps to ISR, the A variable is updated, and comes back to the main function and the Variable A i reseted to 1 it goes to the while function and compares again 1 == 1 ? As if the A is restarted which would take another step to restart the value.

Hmmm I understand what you all try to say using analogy but I don't understand how it looks like in the code hmmmm.
I just use debugger usually to see if my program works correctly so I don't know how it would look then using breakpoints that my program works incorrectly. I just see using debugger that in a breakpoint of ISR the A is updated and when he leaves it goes back to the main function and what the A is restarted to value 1 after leaving ISR ? and in the while it compares correctly 1 == 1 ? I thought that maybe in debugger I would find the problem that 2 == 1 and it passed instead of skipping the while. I don't know if what I say makes sense though.

Or rather when the breakpoint sees that the A should change the value, it ignores it in debugger or it sees this value and changes it ? If yes then it goes back to the main function so it compares new value in the while ? I don't know how debugger/breakpoint behaves in this type of situation, is it possible to detect this error or something ? I just follow where the breakpoint points at and thats what debugger sees during this breakpoint, and I see that the debugger goes to the A, and it exits the ISR with updated A value but when it comes back to main function pufffff it forgets but it pointed at it. I mean I believe you see my confusion. I understand the TV and phone example but I mean more about how it looks while debugging/breakpoiting and following where the code goes next, which instruction he is executing right now etc. So it is weird that debugger sees the value, sees the instruction to update it but then when he leaves ISR he just doesn't know or something and something happens in the main while debugging/breakpoiting in while I don't know what.

I see that the actual A value is in SRAM, but what the debugger will show me and what he will be comparing because as you said the debugger/breakpoint follows the real hardware and uses the same code.

Its harder to understand than I thought. Sorry for wasting your time if I do so.
 
In some ways the debugger is working the same way as the ISR.
When the operation is transferred to the debugger at the breakpoint, the debugger will go to the variable location to read its value. Therefore it WILL read the updated value from the ISR.
However that is missing the point.

Take the following code:
Code:
while(m == 1)
{}

Lets imagine that the MCU has an internal register that we will call A. What the compiler will generate is code such as:
Code:
move the value of variable m into internal register A
label:
if A != 1 goto done
goto label
done:
carry on

It does this because accessing an internal register is faster than reading from memory.
The compiler thinks it can do this because:
1) nothing in the 'while' loop affects the value of the variable 'm'
2) nothing in the while loop means that it has to use the 'A' register for something else

Now imagine what happens if the ISR - which alters the variable 'm' - is triggered anytime after the 'label:' in the above pseudo code. The ISR *may* alter the 'A' register but, as has been mentioned before, the ISR will always save the context at the start and restore it at the end. Therefore the 'A' register will be unaffected by the execution of the ISR.

However the ISR is seeing the variable 'm' for the first time and so reads it from the memory. It updates it and, because it has no further use for the value in any internal register, it will write the value back to memory.

If you declare the variable 'm' to be volatile, the compiler is required to generate the code as:
Code:
label:
move the value of variable 'm' to internal register A
if A != 1 then go to done
go to label
done:

Why does it still move the value into the internal register? Probably because many CPUs - and certainly all RISC CPUs but many CISC ones as well - perform all operations on internal registers (except moving between registers and memory). Therefore it needs the value in an internal register to perform the comparison.

There is still a vulnerable point between the 'move' and the 'if' pseudo code statements above, and if the ISR is triggered between these then the 'if' wil still see the old value. However, when it loop is executed for the next time, it will pick up the new value of 'm' and use that.

Now if you hit a breakpoint, the same sort of thing happens as when the ISR is triggered. The debugger will 'save the context' because it shouldn't change anything (that the user has not done in the debugger) when you continue the program execution. Therefore it will read the value of 'm' from memory becsaue that is all it knows. However if you look at the code that the compiler generated for the 'while' loop, it will show you the register (that we have called 'A') that it assigned that value to in this instance. If you use the debugger to look at that register, you will see the value that the 'while' loop is using which will be the one read before entering the 'while' loop'.

All of this goes to show that you do need a good understanding of how the code is generated by the compiler, how ISRs work and how debuggers operate.

Susan
 
In some ways the debugger is working the same way as the ISR.
When the operation is transferred to the debugger at the breakpoint, the debugger will go to the variable location to read its value. Therefore it WILL read the updated value from the ISR.
However that is missing the point.

Then how it will look like in the debugging ?
I am confused in this point.
Now imagine what happens if the ISR - which alters the variable 'm' - is triggered anytime after the 'label:' in the above pseudo code. The ISR *may* alter the 'A' register but, as has been mentioned before, the ISR will always save the context at the start and restore it at the end. Therefore the 'A' register will be unaffected by the execution of the ISR.

This is the point what does it mean ISR saves the context at the start and restore it at the end ? Like okey he sees the value saves it and restore it at the end of ISR ? I don't see why therefore the A is not affected.

Now if you hit a breakpoint, the same sort of thing happens as when the ISR is triggered. The debugger will 'save the context' because it shouldn't change anything (that the user has not done in the debugger) when you continue the program execution. Therefore it will read the value of 'm' from memory becsaue that is all it knows. However if you look at the code that the compiler generated for the 'while' loop, it will show you the register (that we have called 'A') that it assigned that value to in this instance. If you use the debugger to look at that register, you will see the value that the 'while' loop is using which will be the one read before entering the 'while' loop'.

Okey let me think.
So basically the value of A changes when the debugger leaves the ISR ? From the new value to the old value ? Hmm but it takes another step to overwrite the A value that was updated. Or maybe as you said it only reads the context and didn't change the A value, but it shows in the debugger that the value was changed ??? I thought rather that what debugger shows is what happened in the program like he reads variable B as 5 so it is saved and used somewhere else. But now it is not ? And rather it just saves the context ... So I should use volatile in every function because when he exits the function it must know what the variable had or so, because the breakpoint showed that B had number 5 but it exits the function not ISR one.

That is weird. But I know you are right it is just It is hard to understand it, sorry.
Btw I used the flag in the ISR without volatile, and it worked for simple diode toggle when I push the blue button. So eee I don't know what to say ;D
So I can't detect this error using debugger ?
 
In some ways the debugger is working the same way as the ISR.
When the operation is transferred to the debugger at the breakpoint, the debugger will go to the variable location to read its value. Therefore it WILL read the updated value from the ISR.
However that is missing the point.

Take the following code:
Code:
while(m == 1)
{}

Lets imagine that the MCU has an internal register that we will call A. What the compiler will generate is code such as:
Code:
move the value of variable m into internal register A
label:
if A != 1 goto done
goto label
done:
carry on

It does this because accessing an internal register is faster than reading from memory.
The compiler thinks it can do this because:
1) nothing in the 'while' loop affects the value of the variable 'm'
2) nothing in the while loop means that it has to use the 'A' register for something else

Now imagine what happens if the ISR - which alters the variable 'm' - is triggered anytime after the 'label:' in the above pseudo code. The ISR *may* alter the 'A' register but, as has been mentioned before, the ISR will always save the context at the start and restore it at the end. Therefore the 'A' register will be unaffected by the execution of the ISR.

However the ISR is seeing the variable 'm' for the first time and so reads it from the memory. It updates it and, because it has no further use for the value in any internal register, it will write the value back to memory.

If you declare the variable 'm' to be volatile, the compiler is required to generate the code as:
Code:
label:
move the value of variable 'm' to internal register A
if A != 1 then go to done
go to label
done:

Why does it still move the value into the internal register? Probably because many CPUs - and certainly all RISC CPUs but many CISC ones as well - perform all operations on internal registers (except moving between registers and memory). Therefore it needs the value in an internal register to perform the comparison.

There is still a vulnerable point between the 'move' and the 'if' pseudo code statements above, and if the ISR is triggered between these then the 'if' wil still see the old value. However, when it loop is executed for the next time, it will pick up the new value of 'm' and use that.

Now if you hit a breakpoint, the same sort of thing happens as when the ISR is triggered. The debugger will 'save the context' because it shouldn't change anything (that the user has not done in the debugger) when you continue the program execution. Therefore it will read the value of 'm' from memory becsaue that is all it knows. However if you look at the code that the compiler generated for the 'while' loop, it will show you the register (that we have called 'A') that it assigned that value to in this instance. If you use the debugger to look at that register, you will see the value that the 'while' loop is using which will be the one read before entering the 'while' loop'.

All of this goes to show that you do need a good understanding of how the code is generated by the compiler, how ISRs work and how debuggers operate.

Susan
PS.
Maybe I understood it ?
So the debugger will see that the value of A was changed from 0 to 1, but when it exits the ISR and goes back to main or rather inside of while it compares the old A value which is after the label in the assembler. So exiting ISR will land me back inside the label (from that assemble code).
So the memory values aren't used in comparison rather the values in the registers. And so when we exit the ISR function and go back to the while comparison does it mean that the memory is overwritten with the old A value ? I know that internal register with non volatile variable has this static A value which is 0. But I mean the debugger what he will see of that A ? The memory will be overwritten and the internal register still stays the same which is 0 ?
This means that the memory is only for context but the registers are for the comparison. So every data that is used are stored in memory and if the program needs something it takes from memory and puts it into registers.
 
So the memory values aren't used in comparison rather the values in the registers.
As @KlausST has already said - yes
And so when we exit the ISR function and go back to the while comparison does it mean that the memory is overwritten with the old A value ?
No.

Perhaps I need to explain what is meant by the ISR saving and restoring the context.
When the compiler comes to create the code for an ISR, it 'knows' from your code what internal registers it will use. It has no knowledge of where the ISR might be called from and so it doesn't care about what is in the variable memory locations. It ONLY looks at the internal registers.

As the ISR starts, it will save some or all internal registers somewhere - typically on the stack. It is then free to use those internal registers in the ISR to execute your code. When that is done it will restore the saved values of the internal registers - typically copy them back from the stack.

Therefore, at this stage of the ISR, the internal registers are exactly as they were when the ISR was called. The ISR may have altered variable memory locations according to your code, but that is the reason you called the ISR in the first place - to do something specific.

When the ISR ends and your code resumes from where it was interrupted, the internal registers are now exactly the same as they were when the interrupt was triggered. Therefore your code can continue as though nothing has happened.

Susan
 
Hi,

it is mandatory for an ISR not to change register values, status register, stack pointer, stack content ... and so on ... from the perspective of main().

ISR:
* save return address (HW)
* save status register
* save registers (at least the ones that are used in the ISR)
* save something else?
* ... then perform the ISR code
* restore something else?
* restore registers
* restore status resgister
* jump back to return address

So from the view of the main() .. nothiing has changed. This is important

But for sure the ISR may change SRAM contents (variables).

******

What I often do:
I set up an ISR that runs every 10ms (or whatever).
This gives a nice and accurate grid for doing some routine stuff in the ISR:
* reading inputs (like pushbutton..) and performing debouncing
* controlling buzzer and LEDs, like automatic ON, automatic OFF, blinking
* detecting any events
* reading the ADC result of the previous conversion (battery voltage, touch ..). Mind, it does NOT: start a conversion, wait until finised, then read the result)
* then starting the next ADConversion - without waiting to be finished
* doing SW RTC including setting Flags for whatever I want: 10ms, 100ms, 1s, 1 minute, 1 hr...
... all this stuff above is very fast, just a couple of register access, NO waiting!
depeding on microcontroller it usually takes less than 10us. so just 1/1000 of processing power. The main() will not notice this.

how does it work:

Code:
volatile uint16_t buzzer = 0; (indeed no need to be volatile unless this variable is being READ in main.)

//ISR:
void Isr10ms()
{
    if (buzzer > 0)           // performs automatic switch ON and OFF after: (buzzer-1)*10ms
    {
    buzzer = ON;             // pseudo code set the port.buzzer.pin = 1
    if (--buzzer == 0)        // counts down once every 10ms
        {
        buzzer = OFF;        // if after count down it now it is zero, it switches OFF the buzzer
        }
    }
}

****
//main
void main()
{
    do_init;
    while(1)
    {
        do_someting;
        if (new_key_pressed) buzzer = 10;
        do_something else;
    }
}

The above is a simple, yet efficient way to do a short beep (10-1)*10ms=90ms.
If you want it to beep for a second just set buzzer = 100 (more accurate: 101)

So in main() it does nothing than just set a variable.
Nothing else. No port ON/OFF, no counting, no waiting. Nothing. Forget about the buzzer (The buzzer will work "magically").
It just effeciently goes on performing code.
Now sometimes you want to check in main() whether the buzzer is still ON: just use "if (buzzer)... ". Fast! (mind here you need "volatile")


In the ISR:
... when the buzzer is not activated, it just checks on the buzzer value. A couple of clock cycles every 10ms. Sees (buzzer = 0) and goes on.
... when the buzzer is activated,
* It switches ON the buzzer. Yes - every 10ms. But this causes less processing time than doing the "IF (its_not_already_on) switch_buzzer_on"
* then counts the variable down
* then checks on zero and in case switches OFF the buzzer.

Two (not perfect) issues with this code:
* the ON time is one count short
* when setting "buzzer = 1" in in main() it will result in one single short glitch on the buzzer port pin.

How would one do this without using interrupts? And how much processing time it would cost?
In the same manner you could do 100 "buzzers" individually without noticing a loss in processing power.

Klaus
 

LaTeX Commands Quick-Menu:

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top