Granularidad del ángulo ATtiny85 Servo PWM

Actualmente estoy trabajando en el control de servomotores con un ATtiny85 . Me cuesta entender cómo lograr una granularidad fina para los ángulos del servo.

Estoy usando una técnica similar a la indicada por KyranF .

El ángulo de un servo está determinado por el ancho de pulso entre 1 y 2 ms de un PWM de 50 Hz.

Estoy usando el Timer 1 con un prescaler de 1 con un reloj de 8 Mhz. Esto significa que un desbordamiento del temporizador (tick) toma (1/8 MHz) * 256 = 32 microsegundos.

Un período de 20 ms requiere 625 desbordamientos. Esto significa que mi granularidad de 1 ms es 625/20 ms = 31,25 = ~31 tics por ms.

Debido a que el ángulo de un servo se controla configurando el ancho del pulso entre 1 y 2 ms, solo puedo tener una precisión de 31/180 ° = ~ 6 grados.

¿Hay alguna manera de lograr una precisión de 1°?

Cambie a un dispositivo con un temporizador de 16 bits.
¿Se le ocurre otra solución, si no existe la opción de cambiar a un dispositivo temporizador de 16 bits?
Nada en lo que confiaría un dispositivo que funciona a menos de 32 MHz para poder lograrlo.
@IgnacioVazquez-Abrams Con un PIC de 4MHz (ciclo de CPU de 1MHz) y sin temporizadores de hardware, puedo generar 2 pulsos de servo de 10 bits simultáneos con una resolución de 1us. ¡No debería ser un problema para un AVR de 8MHz!
@BruceAbbott: Pero no lo estamos haciendo todo en el software, lo estamos haciendo con un temporizador de 8 bits y manejando el desbordamiento en el software.

Respuestas (2)

Debido a que el ángulo de un servo se controla configurando el ancho del pulso entre 1 y 2 ms, solo puedo tener una precisión de 31/180 ° = ~ 6 grados.

¿Hay alguna manera de lograr una precisión de 1°?

La mayoría de los servos solo se mueven ~120º con 1-2ms. Sin embargo, asumiendo 1-2ms = 180º, necesita 180 conteos de 5.55us por conteo.

El Attiny85 tiene un generador PWM de 8 bits que puede hacer hasta 256 conteos, pero no tiene una relación de división de preescalador que pueda hacer pasos de 5.55us a 8MHz. El más cercano es 8us por conteo con una preescala de 1/64, lo que corresponde a 125 conteos en 1ms para una resolución de 1,44º por conteo (si 1ms = 180º). Para obtener esto, puede configurar Timer0 para producir un pulso PWM de un solo disparo de 125-250 que equivale a 1-2ms, y usar timer1 para repetir el pulso en intervalos de aproximadamente 20ms. La mayoría de los servos deberían acercarse a 1º de resolución con estos pasos de 8us.

Si 8us no es lo suficientemente bueno, tendrá que usar un retraso de software. A 8Mhz, 44 ciclos de CPU toman 5.5us, que es el 99% de los 5.55us deseados y proporciona suficientes instrucciones para hacer un bucle de tiempo variable preciso de 1-2ms. Puede intentar escribir el código para esto en C (con algunos NOP para ajustar el tiempo), pero podría ser más fácil hacerlo en ensamblador.

Para generar un cuadro completo, primero configuraría un temporizador para interrumpir a intervalos de ~ 20 ms. En el temporizador ISR, iniciaría el pulso del servo, esperaría una variable de 1 a 2 ms usando el temporizador de su software y luego finalizaría el pulso del servo. Durante este tiempo, la CPU no puede hacer nada más, pero aún tiene ~ 18-19 ms disponibles por cuadro de 20 ms.

Aunque no me gusta tener una rutina de bloqueo completo, creo que esto es apropiado para lo que quiero hacer. Todavía es mejor que la idea que se me ocurrió. Que está usando CTC1 con OCR1C cargado con 42 en modo PWM1A, lo que genera una interrupción de desbordamiento cada 5.25us.
AVR tiene una latencia de interrupción alta cuando se codifica en C (¡tantos registros que deben guardarse y restaurarse!), por lo que es posible que no guarde muchos (o ninguno) ciclos usando una interrupción de temporizador, y tendría más fluctuaciones. Otras tareas aún estarían 'bloqueadas' porque no habría suficiente tiempo entre interrupciones para hacer mucho más, y todas las demás interrupciones tendrían que desactivarse para evitar alterar el tiempo.
Un par de otras variaciones de esta idea: puede elegir una frecuencia de cristal/resonador que se adapte mejor (si puede prescindir de los pines), y puede hacer el intervalo entre pulsos contando los desbordamientos del temporizador en el software si lo desea. para guardar el segundo temporizador para otra cosa.

algunas de tus matemáticas están en mal estado.

si configura un preescalador 1: 1, cristal de 8 Mhz + preescalador 8: 1, el temporizador 1 puede tener un máximo de 20,000, y de 1 ms a 2 ms, tiene 1000 conteos o 180 grados / 1000 conteo = 0.18 grados / conteo.

mucho más de lo que necesitas.

aquí hay un enfoque para el mismo problema que puede resultarle útil: https://dannyelectronics.wordpress.com/2017/02/18/driving-multiple-servos-off-a-pic-timer2-ranged-extended/

editar: uno de los enlaces anteriores mostró el enfoque básico -> carga repetidamente el objetivo o, en este caso, avanza el registro de coincidencia de compensación hasta alcanzar la longitud deseada.

Los ejemplos a los que me vinculé anteriormente usan las interrupciones de desbordamiento, pero lo mismo se aplica para comparar coincidencias también.

//TIMER 2 COMPARE match A
ISR(TIMER2_COMPA_vect){
    //check for _OCR2A exhaustion
    if (_OCR2A) {                   //target exhaused?
        _OCR2A -= 0x100;            //decrement
        OCR2A = 0;
    } else {                        //compValue has been exhausted
        //do user stuff
        positiveTimeoutCheck = false;
        digitalWrite( DEBUG_PIN, LOW );
        //timerOneCompADisable();
        TCCR2B = 0;                     //stop timer2
    }
}

el código anterior sigue la lógica básica y borra DEBUG_PIN una vez que se ha agotado _OCR2A (un tipo de 16 bits establecido por el usuario).

Aquí hay un ejemplo de su implementación en TIMER2 de un ATMEGA328p (que ejecuta 1MIPS) donde el preescalador de timer2 está configurado en 1: 1 y _OCR2A en 25000 -> lo que produce un retraso de 25 ms para DEBUG_PIN en cada transición en PCINT0.ingrese la descripción de la imagen aquí

diferentes formas de hacer lo mismo.

¿Cómo puede el temporizador 1 contar hasta 20000 cuando el registro del contador del temporizador (TCNT1) en la página 91 de la hoja de datos es un registro de 8 bits?
En su respuesta, usó el atmega328, que tiene temporizadores de 16 bits, pero el attiny85 no. Entonces, el valor máximo que puedo establecer para comparar registros es 255