1. Is the AVR doing anything
else? Or is it dedicated to this?
Namely, should be pulsing be continuous, or can it be interrupted when a number is received?
2. The details of the V/I protection, depend on the circuit. Can the analog comparator be used? Is there external hardware (comparators, or more in-depth stuff)?
FWIW, AVR is kind of slow for protection purposes, and I would prefer an analog solution here (assuming the application is something roughly SMPS related). Something equally cheap, but faster, would do a fine job -- like an LPC or STM32 family part.
3. The most typical approach, is to use the timer-counters. If the duty cycle is 50%, then a free-running oscillator setting can be used. Set the interrupt to trigger on every overflow, and in the interrupt, count (in a static variable) the number of overflows (half cycles). If the duty cycle is different, you can alternate MAX_COUNT every interrupt, or use a one-shot timer (restart the timer on every interrupt), or use the compare register.
In all cases, when the count variable reaches the correct number of cycles, disable the output, set a new MAX_COUNT and reset the interrupt count. Use this to time the off duration.
The periodic interrupt can also be used to drive other things in the system, that need periodic sampling. In that case, you'd want to keep MAX_COUNT constant, and use the different compare and WGM modes to get the output right. This may restrict how precisely the waveform can be timed, say if the off duration needs to be a non-integer multiple of the pulse period. You could cheat this by shaving a few LSBs here or there on MAX_COUNT, as long as the periodic-sampled system doesn't mind a minor tweak to its timing. Or if throwing in a runt count is okay, then, sure, that too!
Doing the timing with interrupts and timers, should be pretty robust, and you can just read in the COUNT settings from some state variables (note: use atomic reads!). Update the variables from a received serial transaction (which can be polled in the main() loop, or also interrupt driven, to a buffer and such), and the transition should be pretty seamless. Or you can set a flag to reset the count state, or whatever you like.
Aside: "all and all", does that count as an eggcorn? (Correct phrase is "all in all", but it's interesting to see phrases evolve. Look up "eggcorn", it's an amusing quirk of language.
)
Tim