Creo hacer notar que Arduino no esta pensado para esto (me refiero a su comunidad o el entorno dado), si quisieras realmente ir por la precision deberias acercarte mas al microcontrolador y olvidarte un poco de la gran abstracción que te provee arduino.
Como veo decis, que usas el Timer5 para eso. Y micros usa el Timer0.
Como pudiste calcular es correcto el valor , es decir
(2^16 - 34286) * 256 / 16Mhz = 0.5s
Y hasta aca parece perfecto.
El problema esta en como funciona internamente el micro.
Si el timer activa la interrupcion, y por ejemplo esta realizando un salto ( del while del loop principal) como un JMP que le lleva hasta 4 ciclos, ahi tenemos un desfasaje respecto a otra interrupcion, si ocurre a en el primer ciclo de esta instruccion, seguramente deba terminarla, y luego acceder a la interrupcion.
Acceder a la interrupcion tambien ocupa tiempo, es decir el micro una vez activa la señal de interrupcion debe ir a los vectores de interrupcion, de alli ejecutar el salto incondicional lo cual suma mas ciclos, al entrar a la interrupcion tal ves sea necesario guardar el contexto ( ya no recuerdo bien como se manejan por dentro estos), y finalmente recien comenzar a ejecutar las instrucciones que le dimos.
Pero hay mas, vos estar cargando el timer, que estaba en 0 pero que habian pasado ya varios ciclos desde que se puso en 0, le pones un valor y ocurre que.. esos ciclos que se fueron guardando se borraron al ponerle el nuevo valor.
Sigamoslo complicando un poco mas.
micros() millis() y su flia. Utilizan el TMR0 y su interrupcion, lo interesante es que mientras se atiende una interrupcion la otra debe esperar. Supongamos que ocurre la interrupcion del TMR0 y 3 ciclos mas tarde el de TMR5, El TMR0 supongamos que ocupa 30 ciclos, sale, y recien alli entra al TMR5, esto es un error de la cuenta del TMR5, por que esta ves vas a tambien sobreesrcibir esos ciclos mas.
micros() hasta que llegue desde que ocurrio la interrupcion pasaron vaaarios microsegundos y esos son contados.
Entonces tenemos todas esas fuentes de error y que seguro hay mas. Lo cual es correcto que no te va a coincidir.
Una de las formas de evitar esto o tener un valor mas constante, es usar el Timer en modo CTC ( Aunque no se si se puede obviar la salida, imagino que deberia)
Esto hace que el Timer cuente desde 0 hasta un valor, y cuando ese valor coincida, se resetee solo por hardware y active una interrupcion, es decir vos no cargarias el valor en cada interrupcion, simplemente contarias cuantas ocurrieron, Los valores a cargarlo son parecidos a lo que tenes:
preescale =256
Cristal =16MHz
Cargo el registro OCRnx del timer con el valor (65536 - 34286 - 1) Es decr no el complemento sino lo que debo contar menos 1.
Poner a 0 el timer.
Timer de 16 bit
Frecuencia resultante de 2hz
Lo bueno de este sistema es que vos no tocas nada , solo contas las veces que se activo la interrupcion de comparacion. El unico error que tendrias es al punto de tomar una accion, la cual serian unos cuantos microsegundos.
-------
Finalmente, queres controlar de la forma mas exacta al tiempo?, deberias pensar en ASM y no en C o C++