Está en la página 1de 13

Antonio Tallón Zurita (GITI)

El objetivo de este proyecto es implementar un “repetidor de vuelta” para Scalextric, de


manera que sea capaz de grabar y reproducir una vuelta. Para ello, haremos uso del ADC
integrado en el micro que irá registrando periódicamente la tensión en los raíles durante
la vuelta, y almacenando esos valores en la memoria flash, para posteriormente,
reproducirlos haciendo uso de una salida PWM que varíe su duty cycle con el mismo período.
Vamos por partes:

Primera parte: Configuración del timer para un período de 20ms.

En la rutina de interrupción de este timer se realizará el manejo de los botones que


actualizan el estado, además, se incrementará la variable i, que servirá de índice para la
grabación o reproducción de los voltajes. Al final de dicha rutina se despierta al micro
del bajo consumo.

#pragma vector=TIMER1_A0_VECTOR
__interrupt void TIMER1_A0_ISR_HOOK(void)
{
// Manejo de los botones:

if ((!(P1IN&BIT3))|(!(P1IN&BIT2)))
{ ciclospuls++; // si alguno de los 2 botones está pulsado, cuenta ciclopuls
i=0; /// cada vez que se cambie de estado, i=0;

if(ciclospuls>=10) // PULSACIÓN SIMPLE (0.2s)


{
if(!(P1IN&BIT3))
{ estado=1;
i=0;
// npulsa++;

// if(npulsa>=2) // segundo toque


// {
// //para de grabar
// estado=3;
// ivuelta=i;
// i=0;
// npulsa=0;
// }

}
if(!(P1IN&BIT2))
{
estado=2;
i=0;
ciclospuls=0;
}
}
}

if (estado!= 0)
{
i++; // el voltaje pwm cambiará cada 0.02 s, y también se grabará un voltaje cada 0,02s
}

LPM0_EXIT;
}

Segunda parte: Grabación del voltaje.

Para conocer la velocidad del coche en cada punto del trayecto, mediremos la tensión en
los raíles, que oscila entre 0 y 14 voltios. Dado que el ADC puede leer tensiones en un
rango de 0 a 3,6 voltios, hay que montar un divisor resistivo que reduzca el valor a
medir. Para ello se usan 4 resistencias de 330 Ohm en serie, que dejan el máximo en 3,5
voltios. Se conectan de la siguiente forma:

Ahora configuraremos el ADC:

Más adelante, cuando queramos realizar la grabación en la rutina del programa, lo


dispararemos con el bit ADC10SC:
Tercera parte: Gestión de la memoria.

Para ir almacenando los valores leídos por el ADC, se planteaba el problema de que un
array de digamos, 500 ints, (10s) no cabía en la ram ni en la memoria de información del
micro. Para ello, hubo que reservar un espacio en la flash, que en un principio se pensó
de 2 segmentos de 512 bytes. En el archivo .CMD, se modificaron las siguientes líneas:
MEMORY
{
SFR : origin = 0x0000, length = 0x0010
PERIPHERALS_8BIT : origin = 0x0010, length = 0x00F0
PERIPHERALS_16BIT : origin = 0x0100, length = 0x0100
RAM : origin = 0x0200, length = 0x0200
INFOA : origin = 0x10C0, length = 0x0040
INFOB : origin = 0x1080, length = 0x0040
INFOC : origin = 0x1040, length = 0x0040
INFOD : origin = 0x1000, length = 0x0040
FLASHDATOS : origin = 0xC000, length = 0x0400
FLASH : origin = 0xC400, length = 0x3BDE
BSLSIGNATURE : origin = 0xFFDE, length = 0x0002, fill = 0xFFFF
INT00 : origin = 0xFFE0, length = 0x0002
INT01 : origin = 0xFFE2, length = 0x0002
INT02 : origin = 0xFFE4, length = 0x0002
INT03 : origin = 0xFFE6, length = 0x0002
INT04 : origin = 0xFFE8, length = 0x0002
INT05 : origin = 0xFFEA, length = 0x0002
INT06 : origin = 0xFFEC, length = 0x0002
INT07 : origin = 0xFFEE, length = 0x0002
INT08 : origin = 0xFFF0, length = 0x0002
INT09 : origin = 0xFFF2, length = 0x0002
INT10 : origin = 0xFFF4, length = 0x0002
INT11 : origin = 0xFFF6, length = 0x0002
INT12 : origin = 0xFFF8, length = 0x0002
INT13 : origin = 0xFFFA, length = 0x0002
INT14 : origin = 0xFFFC, length = 0x0002
RESET : origin = 0xFFFE, length = 0x0002
}

Además, hubo que indicarle al compilador que excluyera esos segmentos del borrado cada vez
que cargase el programa en el micro.

Más tarde, se decidió cambiar el tipo de dato a char, dado que el rango de un int (1024)
es excesivo para la aplicación que queremos (el mando no tiene tanta precisión), por lo
que tanto espacio se volvió innecesario. Además, para simplificar más el problema, se
redujo el tiempo máximo de grabación de 10s a 6s, por lo que el volcado del vector
temporal volt (guardado en ram) a la flash, se puede realizar en un solo golpe con la ram
que queda disponible con el resto del programa en funcionamiento. (300 * 8 = 2400bits).

El cambio de precisión de 10 bits del adc a 8 bits se realiza de la siguiente forma:

x= ADC10MEM>>2; // pasamos a precisión 8 bits (despreciando los 2 bits menos significativos)


volt[i]=(char) x; // al hacer cast, nos quedamos sólo con los 8 bits del formato char

Y para escribir y leer componentes de un array en la flash, se crearon las siguientes


funciones:

// FUNCIONES DE MANEJO DE FLASH:

char * Puntero1 = (char *) 0xc000; // Apunta al segmento FLASHDATO QUE HE RESERVADO


char * Puntero2 = (char *) 0xc200; // Apunta a la segunda parte del segmento FLASHDATO
int lee_volt(int k)
{
char r= Puntero1[k];
return (int)r;

void guarda_flash_vector(char *dato,int numdato){

// se le pasa un vector de datos char (alojado en la ram temporalmente), y su tamaño

// PRIMERO BORRO TODO


int k;
FCTL1 = FWKEY + ERASE; // activa Erase
FCTL2= FWKEY + FSSEL_2 + 48; // SMCLK , Div de velocidad escritura para funcionar
con el reloj config a 16;

FCTL3 = FWKEY; // Borra Lock (pone a 0)


*Puntero1 = 0; // Escribe algo para borrar el SEGMENTO 1
*Puntero2 = 0; // Escribe algo para borrar el SEGMENTO 2

//EMPIEZA LA ESCRITURA

FCTL1 = FWKEY + WRT; // Activa WRT y borra ERASE

for(k=0;k<=numdato;k++)
{

Puntero1[k]=(dato[k]);
}

// " CIERRO" LA FLASH


FCTL1 = FWKEY; // Borra bit WRT
FCTL3 = FWKEY + LOCK; //activa LOCK
}

Cuarta parte: regulación del voltaje de salida con PWM y amplificación con puente en h.

Para mandar tensión al circuito, configuramos el timer A0 de manera que TA0CCR0 sea
múltiplo de 256. Para tener un valor aceptable de frecuencia, se escogió un factor 63.

En el bucle del programa, este valor se irá actualizando al compás del timer 1 de la
siguiente forma:

TA0CCR1=lee_volt(i)*63; // va reproduciendo el voltaje marcado en función del tiempo


del timer;
// el 63 es la constante por la que hemos multiplicado TA0CCR0 para hacerlo multiplo de
256 (valor máximo de la entrada ADC en 8 bits)
Para amplificar de este valor (0-3,6 voltios) al voltaje que pondremos en la pista de
scalextric, se emplea un puente en h l293d:

Que se conecta de la siguiente forma:

1- Enable (pin 1.1)


2- Salida PWM (pin 1.6)
3- Salida al circuito (irá conectado al positivo del mando, con el otro cable a
tierra)
 Pines 4,5,13,12 a tierra
8- Alimentación 14 V
16 – Alimentación 5 V (con los 3,6 V del micro también funcionaba)
Quinta parte: Máquina de estados, botones, y pines de salida:

//CONFIGURACIÓN PINES:
//
// // P1.6 PWM: (salida al puente h)
// P1DIR|=BIT6; //P1.6 salida
// P1SEL|= BIT6; //P1.6 pwm
// P1SEL2&=~BIT6; //P1.6 pwm

//P1.1 (enable puente en h)


P1DIR|=BIT1;
P1OUT|=BIT1;

P1DIR|=BIT7; // luz de grabadora


P1OUT&=~BIT7;

// Botón Rojo (grabar)

P1DIR &=~ BIT2; // Entrada


P1REN |= BIT2; // P1.2 con resistencia
P1OUT |= BIT2; // P1.2 pull up

// Botón Amarillo (reproducir)

P1DIR &=~ BIT3; // Entrada


P1REN |= BIT3; // P1.3 con resistencia
P1OUT |= BIT3; // P1.3 pull up

En el bucle infinito, los casos principales de la máquina de estados son grabar y


reproducir. En ambos casos, una vez se ha realizado el ciclo de 6s, se vuelve al
estado de reposo.

// MÁQUINA DE ESTADOS
switch (estado)
{

case 0: // Reposo. No interfiere en el funcinamiento normal del scalextric

// APAGAR BIT DE PWM Y PONERLO COMO SALIDA Y A CERO:


P1SEL &=~ BIT6; // salida
P1SEL2&=~BIT6;
P1OUT &=~BIT6; // apagado
P1OUT&=~BIT1; // Enable puente h =0;
break;

case 1: // Grabación de vuelta

// APAGAR BIT DE PWM Y PONERLO COMO SALIDA Y A CERO:


P1SEL &=~ BIT6; // salida
P1SEL2&=~BIT6;
P1OUT &=~BIT6; // apagado
P1OUT&=~BIT1; // Enable puente h =0; (NO FUNCIONA)

if(i<=ivuelta)
{ P1OUT|=BIT7;
ADC10CTL0 |= ADC10SC; // Empieza conversión del ADC
while( ADC10CTL1 & ADC10BUSY ); // Espero a la conversión
x= ADC10MEM>>2; // pasamos a precisión 8 bits
(despreciando los 2 bits menos significativos)
volt[i]=(char) x; // al hacer cast, nos quedamos sólo con
los 8 bits del formato char
}
if(i==ivuelta)
{ guarda_flash_vector(volt,300); // vuelca el vector en la flash

i=0;
estado=0;
P1OUT&=~BIT7;
a=lee_volt(5); // para comprobar que está escribiendo
}

break;

case 2: //Reproducción de vuelta

// ACTIVAR PWM:

P1DIR|=BIT6; //P1.6 salida


P1SEL|= BIT6; //P1.6 pwm
P1SEL2&=~BIT6; //P1.6 pwm

P1OUT|=BIT1; // Enable puente;


if(i<=ivuelta)
{
TA0CCR1=lee_volt(i)*63; // va reproduciendo el voltaje marcado en función del tiempo
del timer;
// el 63 es la constante por la que hemos multiplicado
TA0CCR0 para hacerlo multiplo de 256 (valor máximo de la entrada ADC en 8 bits)
}
else estado =0;

break;

}
}

Cambios de última hora:

- En la demostración, había un fallo en la configuración de ambos timer, que sin


querer había copiado de una prueba antigua del proyecto, con valores de TA0CCR0 Y
TA1CCR0 equivocados.

- Se cambió el tipo de la función lee_volt a int mediante cast, ya que al asignar el


valor de TA0CCR1, se producía desbordamiento y daba lugar a comportamientos
extraños del PWM.

En este documento ya se ha corregido dicho fallos. Se incluyen también vídeos del proyecto
en funcionamiento.
Mejoras posibles:

 Añadir un tercer botón/segunda pulsación/sensor de paso para tener en cuenta otros


tiempos de vuelta. (Sería definir un 3er estado para las vueltas cortas, y una
variable ivuelta = a la i de la pulsación).

 Ampliar el tiempo de grabación creando una función que permita guardar datos en la
flash en más de un volcado, inicializando a cero el array auxiliar volt cada vez.
También habría que modificar la función guarda_flash para poder continuar el
guardado en el siguiente segmento, de manera que se aproveche todo el espacio
reservado.

 Añadir un semáforo de salida con un led, que avise de cuando empieza el tiempo de
grabación/reproducción.

También podría gustarte