Sevomotor. Parte 1 de 2.



Un servomotor está formado por un motor de corriente continua y una electrónica de control, su finalidad es girar cierto ángulo (indicado por una señal de mando) el eje del motor que debe mantener esa posición aunque apliquemos una fuerza contraria al movimiento del eje, por lo que se suele usar para la dirección de los coches de radiocontrol, en el timón de los aviones rc, etc.. En el proyecto hexápodo cada pata del robot está formada por 3 servomotores que otorgan 3 grados de libertad a la pata, combinando el movimiento de todos los servomotores el robot conseguirá desplazarse por el suelo.

Las dos principales características del servomotor son las mismas que las del motor de corriente continua, el par y la velocidad, es decir que fuerza es capaz de hacer en su eje y la velocidad ángular de éste. En el hexápodo del proyecto la velocidad no es tan importante pero el par sí lo es debido a que el robot va a pesar varios kg, además el punto de apoyo va a estar a varios centímetros del eje del motor debido a la longitud de las extremidades, por lo que tendremos que seleccionar un servo de por lo menos 10 Kg-cm para los puntos que vayan a soportar mayor par.

El servomotor está formado por un motor de corriente continua, un tren de engranajes para aumentar el par de salida a la vez que se disminuye la velocidad del eje  y una electrónica para controlar la posición del eje del motor en función de la señal de entrada.

Tapa superior, tren de engranajes.


Tapa inferior, electrónica de control soldada al motor dc.


La señal de control que indica al eje del servomotor cuántos grados debe girar está formada por un pulso que se genera de manera periódica, el servo debe recibir la señal cada 10-30 ms y la duración del pulso (tiempo que la señal de control está a uno lógico dentro del período) determina el ángulo que debe girar. Para que el servo mantenga la posición hay que mandan un pulso de la misma duración de manera periódica, la duración del pulso suele estar comprendida entre 0.3 ms y 2.1 ms correspondiendose con el ángulo máximo y mínimo de giro, cada modelo de servomotor tiene sus tiempos pero suelen ser similares a los anteriores. Si mandamos una señal con un período mayor o menor, o un pulso demasiado largo o corto el servo comienza a vibrar o emitir un zumbido.

El servomotor tiene tres cables para su conexión, dos son de alimentación y el otro de la señal de control por donde debemos mandar el pulso. Por lo que en principio su control es muy sencillo, sólo hay que generar un pulso de una duración determinada cada 10-30 ms que indica el ángulo que debemos girar.

Para contar el tiempo de 30 ms lo mejor es usar la interrupción de un timer del microcontrolador que avisa cuando han transurrido los 30 ms mientras el micro hace otras labores, si no utilizamos la interrupción el micro estaría ocupado de manera continua en contar los tiempos necesarios.

Atmega 1280, Timers.


Timer 0. 8 bits.
Un timer es una parte del hardware del microcontrolador que se encarga de contar pulsos de una señal de reloj permitiendo ejecutar código cada cierto tiempo de manera exacta. El Atmega 1280 cuenta con dos timers de 8 bits (pueden contar hasta 256 pulsos) y 4 timers de 16 bits (2^16 pulsos). El timer0 también se puede utilizar para generar una señal de PWM pero esta vez sólo se va a ver lo necesario para generar la interrupción de 30 ms requerida para el funcionamiento del servo.

La señal de reloj para el timer se puede obtener a partir del reloj del micro teniendo un prescaler para reducir su frecuencia o también se puede obtener a través de una señal de reloj conectada a un pin externo (T0 pin).

El esquema del registro encargado de llevar la cuenta es el siguiente:

TCNT0 es el registro de 8 bits donde se lleva la cuenta de los pulsos del reloj (interno o externo)

Señales:
count: incrementa o decrementa la cuenta de TCNT0 con cada pulso de reloj.
direction: selecciona entre incremento o decremento de TCNT0.
clear: pone todo los bits de TCNT0 a 0.
CLKtn:  la señal de reloj.
top: indica que TCNT0 ha alcanzado su valor máximo, el valor máximo lo podemos asignar en el registro OCR0A.
bottom: TCNT0 ha alcanzado el valor mínimo 0x00.

El hardware del timer compara de manera continua la cuenta de TCNT0 con el valor de los registro OCR0A y OCR0B.


El modo más simple de funcionamiento del timer consiste en incrementar el valor del registro TCNT0 con cada pulso de reloj, cuando se alcanza su valor máximo (0xFF) el registro se pone a 0x00 y se activa el flag de interrupción TOV0, una vez que la interrupción es atendida este flag se limpia. En el registro OCR0A podemos almacenar un valor y seleccionar el modo de funcionamiento en el que el timer se resetea a 0x00 cuando el valor de la cuenta de TCNT0 coincide con el valor almacenado en OCR0A, cada vez que esto ocurre se pone a 1 el flag de interrupción OCF0A pudiendo lanzar la interrupción correspondiente.

Los times pueden ser configurados como pines de salida, en la imagen 0Cnx es un pin del microcontrolador, podemos hacer que el timer ponga a 1 ó 0 el pin de salida cada vez que el valor de TCNT0 coincide con el de los registros de comparación, lo que nos sirve para generar distintas señales liberando de instrucciones que ejecutar al microcontrolador.

Diagramas de tiempos.



Registros Timer0 para generar la interrupción requerida por el servo.




Los bits 7 y 6, COM0A1 y COM0A0 controlan el comportamiento del pin de salida OC0A, en el registro DDR el pin debe de estar con 1 para que pueda funcionar como salida. En el modo del timer que vamos a usar (contador, no para generar una señal PWM) sus posibles configuraciones son las siguientes:


Lo que nos permite que el pin OC0A se ponga a cero, se ponga a uno, o se invierta cuando el valor de los registros OCR0A y TCNT0 coincidan, también podemos seleccionar que no haga nada que es el caso 00 y el que nos interesa para controlar el servo y lanzar la interrupción.

Bits 5 y 4, COM0B1 y COM0B0: lo mismo que en el caso anterior pero para el pin de salida OC0B en lugar de OC0A, el registro con el que se compara TCNT0 es OCR0B.

Bits 1 y 0, WGM01:0: (Waveform Generation Mode): junto con el bit WGM02 del registro TCCR0B determinan como cuenta TCNT0, la situación del valor máximo a contar y la forma de onda generada en los pines de salida. Los modos de funcionamiento soportados por el timer son los siguientes: Normal Mode, Clear Time on Compare Match (CTC)  y otros dos modos de salida para generar una señal de PWM que de momento no interesan.



En el modo 0 se cuenta hasta 255 y cuando se pase este valor se lanza la interrupción de desbordamiento si está habilitada, en el modo 2 se cuenta hasta el valor almacenado en OCRA reseteando la cuenta cuando lo alcanzamos y lanzando la interrupción correspondiente, si por cualquier motivo sobrepasamos el valor de OCRA en TCNT0, puede ser porque realizamos una operación de escritura en alguno de los registros, el registro TCNT0 alcanzaría el valor 255 lanzando la interrupción de desbordamiento al pasar a 0x00.





Bit 7,  FOC0A ( Force Output Compare A): sólo está activo cuando tenemos seleccionado un modo distinto a los de PWM (modo normal o CTC).  Poniendo el bit a uno la salida OC0A es cambiada conforme su configuración en los bits COM0A1:0, no generá ninguna interrupción ni limpia el registro que lleva la cuenta.

Bit 6, FOC0B (Force Output Compare B): lo mismo que el bit anterior pero para OC0A con los mismos registros cambiando B por A.



Bits 2:0, CS02:0 (Clock Select): seleccionar el reloj del timer y por cuanto dividimos su frecuencia (prescaler):



Podemos generar la cuenta mediante soft cambiando el estado del pin T0, un paso de 1 a 0 o al revés incrementarían o decrementarían en uno la cuenta del registro TCNT0.




El registro donde se lleva la cuenta, podemos leer y escribir sobre él, si escribimos sobre él mientras se está contando deshabilitamos todas las operaciones de comparación que vayan a tener lugar en el siguiente ciclo de reloj, por lo que corremos el riesgo de saltarnos una comparación y que el reloj siga contando hasta 0xFF pasando a 0x00, y seguiría incrementandose la cuenta alcanzando el valor del registro OCR0x a comparar.


En este registro tenemos un valor que se compara de manera continua con el registro anterior, cuando éstos coinciden se dispará una interrupción o se cambia el estado del pin de salida OCA0.


Lo mismo que para el registro anterior cambiando la letra A por B.



Bit 2, OCIE0B (Timer/Counter Output Compare Match B Interrupt Enable): cuanto está puesto a 1 si el valor de OCR0B coincide con TCNT0 se dispará una interrupción, además debe estar puesto a 1 el bi  GIE, saltaríamos al vector de interrupción TIMER0_COMPA_vect.
Bit 1, OCIE0A (Timer/Counter0 Output Compare Match A Interrupt Enable): lo mismo que el anterior cambiando B por A.
Bit 0, TOIE0 (Timer/Counter0 Overflow Interrupt Enable):  la interrupción por desbordamiento del timer 0 está habilitada, es decir cuando TCNT0 alcance 255 y se incremente 1 pasando a 0 se disparará la interrupción, debe esta puesto a 1 GIE e iremos al vector de interrupción TIMER0_OVF_vect (consultar).



Son los flags de las interrupciones anteriores, cuando están activos junto a los bits anteriores y GIE salta la interrupción, el flag se limpiar por hardware una vez que la interrupción es atendida.



El timer 0, 1, 3, 4, y 5 comparten el mismo prescaler, pero puede haber distintas configuraciones de prescaler para cada uno de ellos.  Si se usa el reloj del micro como entrada al prescaler (que es lo que se va a usar) podemos obtener una señal de reloj a la salida del prescaler de Fosc/8, Fosc/64, Fosc/256 y Fosc/1024.

Para el servo se necesita generar una interrupción de manera periódica cada 10-30 ms, en nuestro caso vamos a elegir 30 ms lo que permite que el micro tenga más tiempo entre interrupción e interrupción para ejecutar código. El timer 0 sólo usa un registro (TCNT0 ) para llevar la cuenta y por tanto sólo tenemos 8 bits, podemos contar hasta 255, el cristal externo del microcontrolador tiene una frecuencia de 16 MHz, si dividimos esta frecuencia entre el valor de prescaler máximo la frecuencia de reloj que entrar al módulo del timer 0 es de  15625 Hz, es decir cada incremento en el registro TCNT0 se corresponde con un tiempo de 64 microsegundos, por lo que se podrán contar 256 pulsos de reloj hasta que el registro TCNT0 pase de valer 0xFF a valer 0x00 y dispare la interrupción de desbordamiento que genera la señal necesaria para el servomotor. 256*64uS= 16.384 ms  es el tiempo más largo que podemos conseguir con el timer 0 y un reloj de 16 MHz, se nos queda corto ya que necesitamos casi el doble, por lo que este timer no valdrá para nuestro fin y aún hay que esperar para empezar a programar...

Timer 1, 3, 4 y 5. 16 bits.

La principal diferencia de estos timers con el anterior es el uso de registros de 16 bits (2 registros de 8 bits) para llevar la cuenta de pulsos y hacer las comparaciones en lugar de los de 8 bits del timer 0, lo que nos permite contar tiempos mucho más largos que en el caso anterior. Para acceder a los registros de 16 bits de los timers debemos hacerlo mediante el bus de datos del microcontrolador y éste tiene sólo 8 líneas, por lo que debemos hacerlo en dos pasos, se apoyan en un registro temporal que tiene cada timer para guardar el byte alto cuando se accede al byte bajo, programando en C el compilador maneja toda la operación de lectura/escritura por lo que para leer y escribir en ellos sólo tenemos que usar el operador '='. Lo que si hay que tener en cuenta es deshabilitar las interrupciones que puedan modificar los registros de los timers a los que accedemos desde el main y modifique la ISR, para no coger un byte actualizado por la interrupción y otro no actualizado leído o escrito antes de la interrupción.

Al igual que en el caso anterior la señal de reloj puede ser externa o interna.

El contador, el significado de las señales es el mismo que en el anterior.


El esquema es como el anterior, dos registros para llevar la cuenta en lugar de uno y un registro temporal de apoyo para mandar los datos en dos pasos a través del bus de 8 bits, cuando se lee o se escribe el byte bajo se mete el byte alto en el registro temporal, lo que permite al micro leer o escribir todo el valor del registro de 16 bit utilizando sólo un ciclo de reloj.

Estos timers se pueden usar para generar señales como en el timer 0 y además tienen un hardware para capturar una señal de entrada y determinar su frecuencia, duty cicle, etc.. pero sólo se va a comenar lo necesario para generar la interrupción cada 30 ms requerida por el servo.

El registro que lleva la cuenta es comparado continuamente con tres registros de 16 bits (3 pines de salida por timer) para generar los diversos eventos.


Cuando ambos registros contienen el mismo valor se activa el flag OCFnx en el siguiente ciclo de reloj que dispara la interrupción si ésta está habilitada, se limpia cuando la interrupción es ejecutada o escibiendo un 1 en su bit en el registro correspondiente. Cualquier operación de escritura que hagamos sobre TCNTn bloquea cualquier operación de comparación que vaya a tener lugar en el siguiente ciclo de reloj.

Entre los distintos modos de funcionamiento de los timers los que nos interesan para el servo son el Normal Mode y el CTC Mode que funcionan igual que en el timer anterior.

Diagrama de tiempos y eventos.





Registros timers 16 bits.




 Bit 7:6, (COMnA1:0: Compare Output Mode for Channel A)
 Bit 5:4,  (COMnB1:0: Compare Output Mode for Channel B)
 Bit 3:2, (COMnC1:0: Compare Output Mode for Channel C)

Establecen el comportamiento de los 3 pines de salida asociados a cada timer igual que en el timer 0.



Bit 1:0, WGMn1:0 (Waveform Generation Mode): junto con los bits WGMn3:2 del registro TCCRnB seleccionan el modo de funcionamiento del timer.




Bit 7, ICNCn (Input Capture Noise Canceler): se utiliza para el modo en el que el timer captura una señal, no lo usamos aquí.
Bit 6, ICESn (Input Capture Edge Select):  igual que el anterior.
Bit 4:3, WGMn3:2 (Waveform Generation Mode): comentados en los registros anteriores, seleccionan el modo de funcionamiento del timer según la tabla anterior.
Bit 2:0, (CSn2:0: Clock Select): seleccionan el reloj que entra al timer y su prescaler.




Bit 7, FOCnA (Force Output Compare for Channel A)
Bit 6,  FOCnB (Force Output Compare for Channel B)
Bit 5,  FOCnC (Force Output Compare for Channel C)

Sólo tienen efecto cuando el timer está en un modo distinto al de PWM, fuerza una comparación y los pines de salida asociados al timer toman un valor según la tabla anterior 17-3. La interrupción asociada a la comparación entre el registro que lleva la cuenta y los registros de 16 bits no salta y la cuenta no se pone a cero.



Estos registros son donde llevamos la cuenta de los pulsos y su valor representa el tiempo que ha pasado.





En estos registros se almacena el valor que se compara continuamente con los registros anteriores que llevan la cuenta, y cuando tenemos una coincidencia en ambos se activa el flag de interrupción correspondiente y se dispara si está habilitada, los pines de salida del timer cambian su valor dependiendo de su modo de funcionamiento.




Registros utilizados para el modo de captura del timer, ya se vera cuando se requiera.




Bit 5 – ICIEn: Timer/Countern, Input Capture Interrupt Enable
Bit 3 – OCIEnC: Timer/Countern, Output Compare C Match Interrupt Enable
Bit 2 – OCIEnB: Timer/Countern, Output Compare B Match Interrupt Enable
Bit 1 – OCIEnA: Timer/Countern, Output Compare A Match Interrupt Enable
Bit 0 – TOIEn: Timer/Countern, Overflow Interrupt Enable

Estos bits se corresponden con las máscaras de interrupción, si el bit está a 1 junto al bit GIE y la condición de la interrupción tiene lugar se activa el flag de interrupción y saltamos a su vector de interrupción. Cada timer tiene 5 interrupciones, 3 ocurren cuando el valor de la cuenta es igual al valor almacenado en uno de los 3 registros que se comparan constantemente con TCNTn, 1 interrupción cuando se desborda  (incrementamos el valor máximo 0xFFFF y pasamos a 0x0000) el registro TCNTn en el que almacenamos la cuenta de pulsos y otra interrupción asociada al modo de captura de señal.




Bit 5 – ICFn: Timer/Countern, Input Capture Flag
Bit 3– OCFnC: Timer/Countern, Output Compare C Match Flag
Bit 2 – OCFnB: Timer/Counter1, Output Compare B Match Flag
Bit 1 – OCF1A: Timer/Counter1, Output Compare A Match Flag
Bit 0 – TOVn: Timer/Countern, Overflow Flag

Estos bits son los flags de las interrupciones anteriores, se limpian al atender a la interrupción o escribiendo un 1 en ellos.

Cada bit tiene su vector de interrupción: (ejemplo para el timer 4).

TIMER4_CAPT_vect
TIMER4_COMPA_vect
TIMER4_COMPB_vect
TIMER4_COMPC_vect
TIMER4_OVF_vect

Lista de vectores de interrupción.

 
Tenemos 4 timers y un montón de opciones que configurar, por lo que vamos a ver que necesitamos para generar la interrupción cada x tiempo.

Nuestro microcontrolador debe generar un pulso entre 0.3 y 2 ms cada 10-30 ms, vamos a poner un tiempo máximo de 40 ms así podemos ir asignando frecuencias por encima y por debajo de las indicadas para ver que efectos tiene hacer ésto en el servomotor. Partimos de un reloj de 16 MHz y tenemos un registro de 16 bits para llevar la cuenta, por lo que podemos contar 2^16 = 65536 pulsos. Cada incremento en este registro representa un tiempo de 1/(16 MHz) = 0.0625 us, por lo que el tiempo máximo con un prescaler de 1 que podemos contar será 65536*62.5^(-9) = 4.096 ms si no me he confundido con la calculadora.. por lo que tenemos que seleccionar un valor de prescaler mayor, el siguiente valor que podemos seleccionar es 8, la frecuencia del reloj que entra a nuestro timer en lugar de 16 MHz ahora sera de 2 MHz, por lo que el tiempo máximo que podemos contar con el registro de 16 bits siguiendo los pasos anteriores es de 4.096*8 ms = 32.768 ms.

Si buscamos la frecuencia de 40 ms tendremos que seleccionar el prescaler de 64 que proporciona una cuenta máxima de 262.144 ms según los cálculos anteriores. Nosotros buscamos un rango de frecuencias/períodos de entre 5ms y 40 ms, por lo que cuando llegue el tiempo de cuenta deseado el registro que lleva ésta se debe resetear y lanzar la interrupción, para hacer esto debemos seleccionar el CTC mode y habilitar los bits de interrupciones correspondientes, al seleccionar el modo CTC el valor al que se resetea el registro TCNTn y activa la interrupción de comparación  se encuentra en OCRnA.

No necesitamos ningún pin externo por lo que no tenemos que mirar las conexiones al exterior de la placa, en principio cualquiera de los timers de 16 bits nos vale, así que vamos a seleccionar el timer 4 por ejemplo. Con un reloj de 16/64 MHz = 250 KHz cada incremento en TCNT4L se corresponde con un tiempo de 4 microsegundos, por lo que el valor con el que debemos comparar para contar hasta 40 ms es: 40ms/4us = 10000, por lo que en OCR4AL deberemos escribir  00010000 y en OCR4AH  00100111 (OCR4A=0x2710; se debe asignar así, de la otra forma byte a byte da un resultado no esperado en C), con estos valores de comparación tendremos una interrupción cada 40 ms. El valor de estos registros se irá variando en el programa para tener períodos comprendidos entre 5 ms y 40 ms para ver como reacciona el servo.

En el registro TIMSK4 debemos poner a 1 el bit OCIE4A y poner a 1 el bit GIE en SREG para habilitar la interrupción que tendrá lugar cuando el valor de la cuenta coincida con el valor de comparación.

Lo primero es escribir un programa simple que configure el timer y la interrupción para comprobar todo lo anterior.

Los registros involucrados en la configuración y sus bits son los siguientes:
TIMSK4 : OCIE4A
SREG: GIE
OCR4AL: todos.
OCR4AH: todos.
TIFR4: OCF4A
TCCR4B: WGM43:2, CS42:0
TCCR4A: COM4A1:0, WGM4:0

//Programa para configurar el timer 4, T=40ms. Salida Pin A7.
#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>
#include <avr/interrupt.h>

void inicializar_timer4(void);

int main(void)
{
    inicializar_timer4();
    while(1)
    {
    }
}
void inicializar_timer4(void) //Configura el timer y la interrupción.
{

    DDRA |= (1<<PA7);    //Puerto A7 pin osciloscopio
    OCR4A= 0x2710;
    TCCR4B |=((1<<WGM42)|(1<<CS41)|(1<<CS40));    //Los bits que no se tocan a 0 por defecto
    TIMSK4 |= (1<<OCIE4A);
    sei();
}

ISR(TIMER4_COMPA_vect)
{
    PORTA |=(1<<PORTA7);
    _delay_ms(20);
    PORTA &= ~(1<<PORTA7);
    TIFR4 |= (1<<OCF4A);
}


La salida que se puede observar en el pin A7 del micro es la buscada, una señal con un período de 40 ms creado por el Timer4.




Se ha hecho un programa que permite variar el período entre 4ms y 50 ms y el ancho del pulso entre 50 us y 3 ms para ver como afectan los distintos períodos a un servo analógico y un servo digital, el código debo empezar a ordenarlo. código completo.

Hay varias cosas a tener en cuenta, la interrupción del timer debería ser una de las más importantes del hexápodo por lo que hemos de elegir el timer con una prioridad más alta, cuando entramos en una ISR se deshabilitan todas las interrupciones, por lo que si en esa ISR tenemos mucho código y es lenta nuestra interrupción del timer que genera los pulsos para el servo no lo haría hasta salir de la ISR anterior, con un resultado posiblemente desastroso para el robot. Por ello las ISR deben de ser lo más rápidas posibles sólo con el código necesario (para no dejar esperando a las demas), si hay alguna interrupción vital (y de mayor prioridad) pues habrá que habilitar el bit GIE nada más entrar en la ISR, ésto se puede hacer pasandole el siguiente argumento ISR(XXX_vect, ISR_NOBLOCK) .

En el programa hay que hacer que en la pantalla salga el valor real del tiempo en microsegundos del ancho de pulso en lugar del valor del adc del potenciometro (que es el que genera este tiempo).

Respecto a los resultados los servos digitales parecen bastante mejores que los analógicos, en el analógico nada más pasar el ancho de pulso mínimo o máximo el servo se pone a vibrar y dependiendo del período lo hará más o menos. Sin embargo el digital lleva un microcontrolador en su electrónica que procesa la señal y cuando se le manda un pulso que no está dentro de sus límites lo ignora, por lo que no vibra al sobrepasar los valores de pulso max y min, con un período muy por encima o por debajo de los 10-30 ms los movimientos parecen más bruscos, pero la primera impresión del servo chino ha sido buena (tengo poca experiencia con servos). Otra ventaja de los digitales es que el microcontrolador de su placa manda más pulsos al motor en el período que el analógico (un pulso), por lo que su par es "más continuo"  y vibrará menos con carga, el analógico sólo hace fuerza cuando le llega la señal del pulso y durante el Ton.

Me queda aprender mucho de servos ya que no he visto ninguno por dentro para poder interpretar mejor los resultados, he de hacer las mismas pruebas con carga y hacer el programa bien para que salga el tiempo de pulso en lugar del valor del ADC, pero ya volveré a ello que de momento me interesa más ir viendo periféricos y registros del AVR, además tengo que googlear mucho para buscar información sobre los servos y poder realizar pruebas coherentes. En principio y sin tener ni idea del tema diría que los servos chinos son opción, pero prefiero esperarme a realizar pruebas con par a vencer en el eje.

Respecto a los tiempos del servo chino el centro está apróximadamente sobre los 1.5 ms, su tiempo max y min en los que ha alcanzado el giro máximo en cada dirección son los de abajo, creo que no llega a girar 180º, se debe quedar sobre los 150º, el Hitec si me ha girado los 180º. Tengo los servos justos pero probablemente desmonte uno del todo para ver lo que lleva dentro.

Tiempo ángulo mínimo.


Tiempo ángulo máximo.


Video del funcionamiento del programa y efecto en los servos, el pequeño es un servo analógico de HITEC (es buena marca).


La segunda parte cuando sepa más del mundo de los servos...

Para cualquier comentario:

blog comments powered by Disqus