I will be interested to see the schematic. Operating a MOSFET as a linear device doesn't seem right but what do I know?
The short answer to "how to handle bounce back" is simple. You are in some state looking at the voltage while discharging and you decide that you have discharged low enough. So you transition to the next state where you remove the load by turning off the MOSFET. It's done and over with. You are no longer looking at the voltage during discharge so there is no chance you will turn the MOSFET back on. You are simply done and gone.
When you draw the state machine diagram or even just write sentences describing the state, what it does and how it transitions to other states, it will be very clear how to write the code.
Just remember, when you get to the state that shuts down the PWM and turns off the MOSFET that you will do those things and immediately transition to some other state where you will wait for some other condition.
I don't have a clue about what you are doing so I can't really say much about what the state after shutdown should do. Do you go back to the start state and wait for a button? Do you hang out in some idle state waiting for a power cycle (reset) or maybe wait for a different button. Blink an LED?
One thing about button presses, you need to have the inputs debounced and sometimes you need to wait for the button to be released. What you really want to find is an edge, not a level.
switch (state)
case press : if (button_pressed) { // this is pseudo code, button_pressed might be
// a pin input or a global variable or even a function call
state = release;
}
break;
case release: if (! button_pressed) {
state = begin; // we won't actually begin until the button is released
}
break;
case begin: // now we start the process
...
}
It depends on how you implement the button input routine. You could, for example, arrange to use the interrupt system to find the rising edge by using interrupt on change. In that case, the interrupt code would set a volatile global variable 'button_pressed'. It would only set it on the rising edge interrupt. Then, in the state machine, once you determine that 'button_pressed' has been set and you are just about to change state, set 'button_pressed' to 0. You have just 'eaten' the button pressed condition so if the switch is to be used later, it will need to generate another rising edge interrupt.
There is code out there in the wild that will show you how to use the interrupt-on-change feature. I didn't look at it.
https://playground.arduino.cc/Code/InterruptsI realize that throwing out the concept of a volatile global variable seems over the top. Unfortunately, that's the proper way to do things. The compiler needs to know that the variable is volatile (can change outside the current function) or it will merrily optimize it away and toss any related code. The variable needs to be global (declared outside of any function, usually at the top of the code) because it needs to be shared between the interrupt routine (which sets the value) and the main() where the state machine code tests and resets the value. Just be sure to debounce the input. The input pins have a Schmitt Trigger as part of the input structure so a simple low pass filter should work fine for debouncing.
volatile int button_pressed = 0;
void interrupt_handler
{
// somehow we determine that a rising edge has occurred
button_pressed = 1;
}
void setup()
{
// set up interrupt-on-change to branch to interrupt_handler()
// see library code for examples
// ... do other setup stuff
}
void loop()
{
switch (state)
case press : if (button_pressed==1) { // wait for button press interrupt to set value to 1
button_pressed = 0; // we need to clear the variable before moving on
state = begin;
}
break;
case begin: // now we start the process
...
}
}