PIC 16F87X
Juan González Andrés Prieto-Moreno Ricardo Gómez
Escuela Politécnica Superior Flir Networked Systems Flir Networked Systems
Universidad Autónoma de Madrid
Curso de microcontroladores PIC. Semana del 25-29 Mayo 2009. 1
PIC 16F87X
MÓDULO 8:
Control de servos
2
Servos
3
Introducción
● Los servos que usaremos son los Futaba 3003
● Tienen un rango de giro de 180 grados
● Control de la posición mediante señales PWM
●Se conectan directamente a los pines del PIC sin necesidad de ningún
circuito de potencia
● Nosotros los conectaremos al PUERTO B
● Usaremos el Timer 0 para generar el PWM
Conector
● Cable negro: GND
● Cable Rojo: +5v
● Cable blanco: señal de control
4
Señal de control
● Señal PWM de 50 Hz
● El ancho del pulso varía entre 0.3ms y 2.3ms
●Cada anchura se corresponde con una posición angular del eje de salida del
servo
● Trabajaremos con ángulos comprendidos en el rango de -90 a 90 grados
5
PWM y Timer 0
● Con el Timer 0 el tiempo máximo que medidos es de 13.1ms
●¿Cómo hacemos para medir los 20 – 0.3 = 19.7ms que la seña tiene que
estar a nivel bajo (0) en el caso peor?
● Una solución es dividir los 20ms en ventanas temporales de 2.5ms
● Denominaremos T a la anchura en ticks de estas ventanas de 2.5ms
● Su valor es: Divisor=64, T=195 ticks.
6
Conversión entre grados y tiempo en ticks
● El ángulo del servo lo expresamos en grados, en el rango [-90,90]
● La anchura del pulso está comprendida entre 0.3ms y 2.3ms
● La anchura en ticks (con divisor a 64) está comprendida entre 24 y 180
ticks
● Establecemos una relación lineal entre el ángulo y la anchura (Ton)
78
T on = . pos102
90
Ancho del pulso Posición del servo
(en ticks) (en grados)
7
Ejemplo 1: Moviendo un servo (I)
servo1.c (parte I) Secuencia de movimiento.
Llevar el servo a las
#include <pic16f876a.h>
posiciones -90, 0 y 90
#define T 195
#define T0INI_2_5ms 256195
T es la anchura en ticks de la ventana (2.5ms)
void timer0_delay() {...} Valor del timer para conseguir pausa de 2.5ms
void servo_pos(int pos) Función de pausa. Usada en los ejemplos del
{ timer 0
unsigned int ciclos; Función para llevar el servo a la posición
unsigned int i; indicada (en grados)
char Ton; Calcular el tiempo Ton que debe estar activa
la señal PWM para alcanzar esa posición
Ton = (78*pos)/90 + 102;
for (ciclos=0; ciclos<100; ciclos++) {
Poner señal a 1
PORTB=0xFF;
timer0_delay(255Ton); Esperar tiempo igual a Ton
PORTB=0x00; Poner señal a 0
timer0_delay(255T+Ton); Esperar tiempo igual a Toff
for (i=0; i<7; i++) Esperar un tiempo igual a 2.5ms
timer0_delay(T0INI_2_5ms); para cada una de las 7 ventanas
restantes
}
8
}
Ejemplo 1: Moviendo un servo (II)
servo1.c (parte II)
void main(void)
{ Configurar timer 0
TRISB=0;
T0CS=0; PSA=0; Prescaler a 64
PS2=1; PS1=0; PS0=1;
while(1) {
Llevar servo a un extremo
servo_pos(90);
servo_pos(0); Llevar servo al centro
servo_pos(90);
Llevar servo al otro extremo
}
}
9
Moviendo 8 servos con el timer 0
Servo 1
●En cada ventana de 2.5ms
actuamos sobre un servo
●Al terminar con la ventana 8,
Servo 2 comenzamos de nuevo con la 1
●En cada instante hay como
Servo 3 mucho un único servo activo
Servo 4
Servo 5
Servo 6
Servo 7
Servo 8
10
Ejemplo: Moviendo 8 servos (I)
servo8.c (parte I)
T es la anchura en ticks de la ventana (2.5ms)
#include <pic16f876a.h>
#define T 195 Tabla con las posiciones para cada servo
Máscaras para activar cada uno de los pines
int pos[]={0,0,0,0,0,0,0,0}; donde están los servos
unsigned char mask[]={0x01,0x02,0x04,
0x08,0x10,0x20,0x40,0x80}; Función para posicionar los 8 servos
void servo_pos()
Bucle para recorrer las 8 ventanas
{
unsigned int ciclos;
unsigned int i;
char Ton; Obtener la anchura del pulso del servo de
la ventana i, en ticks, a partir de la
for (ciclos=0; ciclos<100; ciclos++) {
posición en grados
for (i=0; i<8; i++) {
Ton = (78*pos[i])/90 + 102; Activar la señal del servo i
PORTB=mask[i];
Esperar tiempo igual a Ton
timer0_delay(255Ton);
Poner a 0 la señal del servo i
PORTB=0x00;
Esperar un tiempo igual a Toff
timer0_delay(255T+Ton);
}
}
} 11
Ejemplo: Moviendo 8 servos (II)
servo8.c (parte II)
void main(void) Configurar el timer 0 (prescaler a 64)
{
TRISB=0;
T0CS=0; PSA=0; Llevar los servos 0 y 1 a sus posiciones de los
extremos (el resto permanecerán en el centro)
PS2=1; PS1=0; PS0=1;
while(1) {
Llevar los servos 0 y 1 a sus otros extremos
pos[0]=90; pos[1]=90;
servo_pos();
pos[0]=90; pos[1]=90;
servo_pos();
}
}
12
Moviendo 8 servos mediante interrupciones
●Vamos a mover los 8 servos mediante interrupciones para poder
realizar otras tareas desde el bucle principal
● Usamos un autómata de estados para cada ventana:
● ESTADO ON: la señal del servo i está activa
● ESTADO OFF: la señal del servo i está desactivada
● Al salir del estado OFF se pasa al estado ON pero de la siguiente ventana
13
servo8-int.c (parte I) Ejemplo 3: Mover 8 servos mediante int. (I)
#include <pic16f876a.h>
Definición de los estados para el
#define T 195 autómata
#define ESTADO_ON 0
Posiciones para los servos (grados)
#define ESTADO_OFF 1
int pos[]={0,0,0,0,0,0,0,0}; Máscara para activar los servos (igual que en el
ejemplo anterior)
unsigned char mask[]={..};
unsigned char estado=0; Estado del autómata
unsigned char servo=0; Apunta al servo actual
char Ton; Rutina de atención a la interrupción del Timer 0
void isr() interrupt 0
{ Cuando estamos en el estado ON
if (estado==ESTADO_ON)
Calcular el tiempo Ton en ticks para el servo
Ton = (78*pos[servo])/90 + 102; actual
PORTB=mask[servo]; Activar la señal del servo actual
TMR0=255Ton;
Configurar timer para esperar tiempo Ton
estado=ESTADO_OFF;
} else { Pasar al siguiente estado
PORTB=0x00; Cuando estamos en el estado OFF
TMR0=255T+Ton; Poner señal del servo a 0
servo=(servo + 1)%8; Configurar timer para esperar tiempo Toff
estado=ESTADO_ON; Pasar a la siguiente ventana
}
Pasar al estado ON 14
T0IF=0; Poner el flag a 0
}
Ejemplo 3: Mover 8 servos mediante int. (II)
servo8-int.c (parte II)
void main(void)
{ Configurar temporizador (prescaler 64)
TRISB=0;
T0CS=0; PSA=0;
PS2=1; PS1=0; PS0=1; Activar las interrupciones
TMR0IE=1;
GIE=1;
while(1) { Llevar los servos 0 y 1 a sus posiciones de los
pos[0]=90; pos[1]=90; extremos (el resto permanecerán en el centro)
pausa(14); Pausa
pos[0]=90; pos[1]=90;
Llevar los servos 0 y 1 a sus otros extremos
pausa(14);
Pausa
}
}
15
Ejemplo 4: Servos y puerto serie (I)
●Como los servos se mueven mediante interrupciones, podemos realizar
otras tareas
●Por ejemplo atender el puerto serie para mover los servos según las
opciones seleccionadas por el usuario
● Mediante las teclas 1-8 el usuario selecciona el servo activo
●Con las teclas 'o' y 'p' se incrementa o decrementa la posición del servo
activo en 5 grados
● Con las teclas 'q' y 'a' se lleva el servo activo a sus extremos
● Con la tecla espacio se posiciona en el centro
16
servo-sci.c Ejemplo 4: Servos y puerto serie (II)
#include <pic16f876a.h>
Igual que en ejemplo anterior
#include "sci.h"
(...) Igual que en ejemplo anterior
void main(void) Incrementar la
{ posicion en 5
case 'p':
unsigned char c; grados y
pos[i]+=5; comprobar
unsigned char i=0; if (pos[i]>90) pos[i]=90; que no se
rebasan los 90
break;
grados
sci_conf(); case 'o':
(...) pos[i]=5;
while(1) { if (pos[i]<90) pos[i]=90; decrementar
c=sci_read(); break; la posicion en
switch(c) { 5 grados y
default:
comprobar
case ' ': if (c>='1' && c<='8') { que no se
pos[i]=0; i = c '1'; rebasan los
break; -90 grados
sci_cad("Servo ");
case 'q': sci_write(c);
pos[i]=90; sci_write('\n');
break; } Con las teclas
case 'a': } del 1-8 se
pos[i]=90; cambia el
}
servo activo
break; } 17
Ejercicio:
● Reproducir una secuencia de movimiento en un servo
MEJORA
●Seleccionar entre varias secuencias de movimiento a través de un
menú por el puerto serie
18