How complex can your software be if it's only controlling a valve?
It's not my code - it is 100% assembler - about 5200 lines of it including blank lines and comments. It isn't particularly complex but it has to regulate temperature, manage user input from switches to set time and temperatures, drive an LCD etc.
I don't use a hardware debugger because:-
1. Printf or toggling a pin will tell you 99% of what you want to know in real time.
2. AVR has a simulator that lets you step through code without any hardware.
Fair enough, everyone has their own preferences. I like to use debuggers for some problems because it's usually much easier and faster than writing shed loads of printfs into the code, rebuilding, programming, testing, repeat ad nauseum. And spending two days tearing your hair out trying to work out why it doesn't work only to find it was one of your bits of test code that overflowed the stack or whatever.
The simulator may be a useful option however. Thanks for pointing that out.
3. If the your program isn't working properly then you should review the source. A hardware debugger is only required when the code is bug free but the device still doesn't work (ie. never!).
Sorry, but that is total rubbish - but apologies if it was a tongue in cheek comment. Sure review your code for the thousandth time and still you can't see what is wrong because you missed the small print in the footprint on page 1147 of the reference manual that says that flag xyz can be cleared by some unrelated peripheral in some obscure conditions. All your printfs and port twiddling didn't help because they changed the timing/memory layout/stack usage etc.
Or your processor simply runs off into the ether because you misconfigured one of the clock domains or failed to ramp the clock speed up sufficiently slowly to allow the PLL to track it.
Or you use inadvertantly use an uninitialised pointer in an interrupt routine which mangles the stack. Good luck finding that with printfs.
Or you get a hard fault (on an ARM Cortex) when you call an assembler function, supplied by ARM, with ".section text" instead of ".section .text" which causes the linker to fixup the call with a branch opcode which is invalid for that target type. Examining assembler output from GCCs compilation of your C code doesn't help because it was the linker that threw a wobbly. Examining the diassembled code in the debugger revealed the problem. No amount of printfs would help.
In all these cases your code is wrong but a good debugger can make your life very much easier.