Donaciones

¿Te ha gustado la página? Si es así, por favor considera hacer una donación para contribuir a su continuidad y mantenimiento. Gracias!

viernes, marzo 17, 2006

8052 y servo: Segunda parte

Este hilo es la continuación Manejando un servo: Conocimientos previos .

Propondré una posible implementación en ensamblador del 8052 para controlar un servo por PWM.

En primer lugar haremos un breve recordatorio del funcionamiento de los timers en general.

Un timer es un contador interno de ciclos. Funcionan incrementando en una unidad el valor de un registro por cada ciclo de instrucción. Cuando llegan al máximo provocan un overflow que suele generar una petición de interrupción.

En 8052 nos proporciona 3 timers: T0, T1 y T2, aunque para este ejemplo sólamente vamos a usar T0 y T1.
Cada timer se compone de los siguientes registros:

  • TH0 y TL0: Ya que los timers se pueden usar en modo 8 bit y modo 16 bits posee dos registros de 8 bits que almacenaran el valor del contador en cada instante.
  • TCON: Registro global para todos los timers. Su nombre es Timer Control. En el nos encontraremos 4 bits significativos:
    • TF0, TF1, TR0, TR1. Los bits TF0 y TR0 se refieren al T0 y TF1 y TR1 al T1 consecutivamente. TRx sirve para poner a contar el timer y TFx para ver si ha provocado overflow.
  • TMOD: Tambien es globar para todos los timers. Su nombre es Timer Mode. Se usa para definir el comportamiento del timer.
Cada timer tiene tres modos de funcionamiento posibles:
  • Modo 13 bit: El contador funciona en modo 13 bit, por lo que podremos contar de 0 a 2^13 ciclos de instruccion. Este modo no se suele usar.
  • Modo 16 bit: Lo mismo pero en 16 bitm por lo que tendremos 2^16 que son 65536 ciclos de instruccion.
  • Modo 8 bit con autoreload: Es un timer con autorecarga. En el THx colocamos el valor con el que queremos que se recargue. Entonce será el TLx quien se ira incrementando en cada ciclo de instrucción, y cuando provoque overflow, se recargará automáticamente con el valor contenido en THx.
  • Modo Split: Tampoco lo usaremos pero sirve para crear dos timers separados de 8 bit con uno solo.
En este ejemplo vamos a usar dos timers de 16 bits.

Recordando el hilo anterior, para controlar el motor servo queríamos generar un pulso de 50hz, con el que variando el ancho de pulso controlaríamos la dirección del motor.

Como el pulso es de 50hz, tenemos 1/50 = 20ms. Necesitamos generar un evento periódico de 20 ms por lo que usaremos el Timer0.
Primero tenemos que calcular el número de ciclos de instrucción que tendrán que transcurrir para que hayan pasado 20 ms.
El 8052 ejecuta 1 ciclo de instrucción por cada 12 ciclos de reloj. De esta forma si tenemos un reloj a 12 Mhz, tendremos un ciclo de instruccion de(12 * 10^6) /12 = 1 * 10 ^ 6. La frecuencia es la inversa del periodo por lo que ejecutará: 1 ciclo de instrucción cada 1 ps.Con esto ya sabemos que si queremos que transcurran 20 ms, tendrán que transcurrir
  • 20.000 ciclos de instrucción.
Recordaré muy brevemente el funcionamiento mediante interrupciones:
Cuando sucede un determinado evento que requiere un tratamiento especialcomo una division por 0, desbordamiento en el registro del timer o interrupcion externa, la cpu identifica este evento y salta a ejecutar una rutina especifica. Esta rutina tiene el nombre de RTI o "rutina de tratamiento de interrupcion".
Esto nos interesa ya que la forma de contar los 20ms será inicializando el contador con un valor determinado para que produzca desbordamiento justo cuando hayan transcurrido los 20ms.

Como vamos a usar un timer de 16 bits tendremos hasta 0xFFFF o 65.536. El timer se autoincrementa en una unidad en cada ciclo de instrucción por lo que si queremos que ocurra un overflow tras 20.000 ciclos de instrucción, tendremos que inicializar el contador con el valor:
65.536 - 20.000 = 45.536.
Si queremos que el evento ocurra de forma periódica tendremos que reiniciarlo con ese mismo valor justo en el instante en que entremos en la RTI.

Ahora pondremos a nivel alto la señal del servo.
Recordando el tema de los pulsos teníamos que según la dirección, el tiempo a nivel alto sería:
  • 0.5 ms para izquierda
  • 1.5 ms para centro
  • 2.5 ms para derecha
  • Izquierda: 65536 - 500
  • Derecha: 65536 - 2500
  • Centro: 65536 - 1500
Al entrar a timer 1 pondremos a nivel bajo la señal referente al servo y se desactivará a si mismo (timer 1).

De esta forma hemos conseguido generar el pulso periódico en función de la dirección.

El programa a continuación propone una implementación muy elemental:

----------------------------
SERVOSIMPLE.ASM
sEnC
GPL
-----------------------------
MOTOR_01 EQU P2.0 ;Pin asignado al servo
MOTOR_01_STATUS BIT 01H ; Bit de estado (1 = On, 0 = Off)
MOTOR_01_DIRECCION BIT 00AH ; Bit de direccion (1 = Izq, 2 = Der)


;Constantes de tiempo
TIME_MOTOR_20 EQU 45536 ; 65536 - 20000
TIME_MOTOR_LEFT EQU 65036
TIME_MOTOR_RIGHT EQU 63036
TIME_MOTOR_STOP EQU 64036

ORG 0000H
JMP START

ORG 000BH
LJMP RTI_TIMER0

ORG 001Bh
LJMP RTI_TIMER1

RTI_TIMER0:
PUSH ACC
PUSH PSW
MOV TH0,#HIGH TIME_MOTOR_20 ;
MOV TL0,#LOW TIME_MOTOR_20 ;

SETB MOTOR_01 ; Ponemos a nivel alto la señal del motor

;Salto en funcion de los bits de estado

JB MOTOR_01_STATUS, RTI_TIMER0_MOVE
JMP RTI_MOTOR_STOP

RTI_TIMER0_MOVE:

JB MOTOR_01_DIRECCION,RTI_MOTOR_LEFT
JMP RTI_MOTOR_RIGHT

RTI_MOTOR_LEFT: MOV TH1,#HIGH TIME_MOTOR_LEFT
MOV TL1,#LOW TIME_MOTOR_LEFT
SETB TR1
LJMP RTI_MOTOR_EXIT

RTI_MOTOR_STOP: MOV TH1,#HIGH TIME_MOTOR_STOP
MOV TL1,#LOW TIME_MOTOR_STOP
SETB TR1
LJMP RTI_MOTOR_EXIT

RTI_MOTOR_RIGHT: MOV TH1,#HIGH TIME_MOTOR_RIGHT
MOV TL1,#LOW TIME_MOTOR_RIGHT
SETB TR1

RTI_MOTOR_EXIT: SETB ET1

POP PSW
POP ACC
RETI

RTI_TIMER1: PUSH ACC
PUSH PSW
CLR MOTOR_01 ; Ponemos a nivel bajo la señal del motor
CLR ET1
CLR TR1
POP PSW
POP ACC
RETI

START:
;Estado del motor
SETB MOTOR_01_STATUS ;Encendido
CLR MOTOR_01_DIRECCION ;Izquierda

;Inicializacion del contador e interrupciones

SETB ET0
MOV TL0,#LOW TIME_MOTOR_20
MOV TH0,#HIGH TIME_MOTOR_20
MOV TMOD,#011H
SETB EA ; Perm
SETB TR0
SETB ET0

;Bucle infinito
ETI: NOP
JMP ETI
END


Observaciones:
No estamos contando el tiempo que transcurre entre que entramos en la RTI hasta que iniciamos los contadores.

En el próximo hilo propondré una solución a este problema, y además podrá manejar más de un servo en cualquier estado simultaneamente.

3 comentarios:

Anónimo dijo...

Buenas Soy Estudiante de Ing.Electronica y Estoy Haciendo un proyecto con 89c51ed2 y necesitaba saber como inicializar los timers. Muchas Gracias Por La info. Muy Util.
"El Conocimiento es Poder Solo si se Comparte"
Sdos.y Exitos.

Javier Quevedo dijo...

Hola.
Lo siento pero no estoy familiarizado con ese microcontrolador.
Espero que hayas conseguido que funcione,
mucha suerte.

Alberto Gomez dijo...

Eres excelente amigo, llevo semanas tratando de hacer esto con un AT89S52, me haz salvado mi proyecto de titulacion, ahora solo lo aplicare para 3 servomotores, de nuevo muchas gracias,