You are on page 1of 20

Introduccion al PIC-Basic

sep de Rodriguez Navarro

Introduccin al lenguaje PIC-BASICPRO


Variables
La programacin sera prcticamente imposible sin el uso de variables. Podemos hacernos una imagen mental de la variable consistente en una caja en la que podemos guardar algo. Esa caja es una de las muchas que disponemos, y tiene en su frente pegada una etiqueta con su nombre. Estas cajas tienen ciertas particularidades, que hace que solo se puedan guardar en ellas determinados tipos de objetos. En esta analoga, cada caja es una variable, su contenido es el valor que adopta, y la etiqueta es el nombre de la variable. Como su nombre indica, y como veremos mas adelante, el contenido de una variable puede ser modificado a lo largo delprograma

Tipos de datos
En PBP tenemos distintos tipos de variables, segn el dato que puedan almacenar:

Bit :un bit de longitud, almacena 0 o 1 nicamente Byte :un byte de longitud, almacena nmeros enteros entre 0 y 255. Word: dos bytes de longitud, almacenan nmeros enteros entre 0 y 65,535(accederemos individualmente a cada uno de los bytes que componen un Word mediante las extensiones . HB y . LB) .

VAR

Las variables deben ser declaradas antes de utilizarlas, mediante la instruccin var, pero a diferencia de otros lenguajes en PBP, la declaracin de variables puede ser hecha en cualquier parte del programa, y todas las variables son consideradasglobales, es decir, su valor es accesible desde todas las subrutinas y zonas del programa, estando lgicamente limitado su nmero al tamao de memoria RAM disponible en cada microcontrolador. En el programa de este proyecto utilizamos los siguientes variables: ticks var byte: almacena el numero de pulsos que sern utilizados para calcular los segundos hora var byte :almacenara la hora actual horaal VAR BYTE: almacenara la hora de la alarma minuto var byte : almacenara los minutos de la hora actual minutoal VAR BYTE : almacenara el minuto de la alarma segundo var byte : almacenara los segundos de la hora actual semana VAR BYTE :almacenara el da de semana actual delay var byte : define el retardo para evitar lecturas errneas en los pulsadores DESC VAR BYTE : almacenara el modo de activacin PATTERN VAR BYTE: almacenara el tipo de da en dos caracteres lunes var bit: define si la alarma se activara los lunes martes var bit: define si la alarma se activara los martes miercoles var bit : define si la alarma se activara los mircoles jueves var bit : define si la alarma se activara los jueves

viernes var bit : define si la alarma se activara los viernes sabado var bit : define si la alarma se activara los sbados domingo VAR Bit : define si la alarma se activara los domingos

PUERTOS
Poco nos servira un PIC si no tuvisemos posibilidad de conectar ste con elmundo real como displays, pulsadores, leds, rels, etc., cometido que se hace a travs de los llamados puertos de entrad/salida. Todos los puertos estn disponibles para usar en los programas PBP, como si se tratase de variables del tipo BYTE con el nombre del registro utilizado en las hojas de caractersticas (PORTA, PORTB, TRISA, etc..). Tambin se puede acceder a bits individuales simplemente usando las variables tipo byte RA, RB, RC, RD, RE o bien las tipo bit RA0, RA1, RA2,, RE6, RE7. Como en la mayora de los controladores de E/S en PBP debe definirse como emplearemos los bits de cada puerto (como entrada o como salida), cometido que se realizar a travs del comando TRIS seguido del nombre del puerto asignndolo a un valor que en binario transcribe cada bit segn est a cero o uno como ceros, si funciona ese bit como salidas o como entrada. En el programa de este proyecto utilizamos la siguiente definicin de bits en los puertos: TRISA=0 : define todos los bits del PORTA como SALIDAS

TRISB= $F4 define algunos bits del PORTB como SALIDAS( los bits que estn a cero: BO, B1 Y B3 EL RESTO SERIAN ENTRADAS), vemoslo mas detalladamente: f4 en Hexadecimal es 11110100 en binario por lo que si miramos de izda a derecha este numero , los bits b0,b1 y b3 estn a cero y el resto ( b2,,b4,b5,b6 ,b7 ) a uno, por eso las primeras RB0,RB1 Y RB3 se define como bits de salida (a las que conectaremos los circuitos de aplicacin que nos interesen: buzzer,

leds, rels etc.) y las otras se definen como entradas RB2,RB4,RB5,RB6 y RB7 (a los que conectaremos los pulsadores en el proyecto).

SYMBOL
Una forma de escribir programas que nos resulte mucho ms fciles de entender es el uso de nombres simblicos, o SYMBOL. Un symbol es una cadena que contiene cdigo, asignado a un nombre. Al momento de compilar, PBP hace la bsqueda y reemplazo de nuestros smbolos y luego genera el cdigo ASM y el HEX. Supongamos que tiene un buzzer conectado al bit uno del puerto B. Mediante SYMBOL podemos hacer: SYMBOL ALARMA1 = PORTB.1, Luego, si queremos encender la alarma1, en lugar de PORTB.1 = 1, podemos hacer alarma1 = 1 que es mucho mas claro y fcil de leer. Por supuesto, el cdigo que aparece a la derecha del igual no puede contener instrucciones. En el programa de este proyecto utilizamos los siguientes smbolos: Symbol hora_boton=portb.4 : lo usaremos para modificar el nmero de semana y la hora. symbol prog_boton=portb.5 : lo usaremos para modificar la hora de la programacin. symbol periodo_boton=portb.6 : lo usaremos para modificar el periodo de funcionamiento del programador. SYMBOL alarma_boton=portb.0 : lo usaremos para programar la hora de la alarma. symbol luz_boton=portb.7 : lo usaremos para encender la luz de fondo del LCD (la encenderemos durante un cierto periodo de tiempo siempre que pulsemos una tecla) y tambin para parar la alarma. symbol alarma1=portB.1 :lo usaremos para hacer sonar un buzzer.

symbol luz=portb.2

: lo usaremos para encender la luz de fondo.

Las constantes (valores que usamos en nuestro programa, y que, por ejemplo, asignamos a las variables) pueden ser escritas en decimal (directamente el valor), en hexadecimal (anteponiendo 0x o posponiendo H al valor) o en binario (anteponiendo % al valor). Hay tres instrucciones para el manejo individual de bits, que si bien no hacen nada que no se puede resolver con otras instrucciones o smbolos, ayudan mucho en la lectura del cdigo. Se tratan de HIGH, LOW y TOGGLE, que ponen el bit en alto, bajo o lo invierten, respectivamente. Importante: Si el bit implicado como argumento de una de estas instrucciones es un bit de un PORT, el mismo bit en el TRIS correspondiente es puesto en cero, y dicho pin queda configurado como salida. Por ejemplo low alarma1 : desactiva el buzzer de alarma low luz :apaga la luz de fondo

GOSUB
GOSUB significa literalmente IR A Subrutina, y sirve justamente para eso: desviar el flujo del programa a otro punto donde estar la subrutina. Para usar GOSUB, es necesario poner una etiqueta en el lugar al que queremos saltar. Las etiquetas son simplemente nombres terminados en :, y tambin en muy importante terminar el cdigo de la subrutina con la sentencia RETURN (para que una vez ejecutada la subrutina vuelva a la instruccin siguiente de donde se hizo la llamada). Como ejemplo veamos una seccin del programa presentado donde se utiliza una llamada a subrutina:

if hora=horaal and minuto=minutoal and segundo=1 and ( semana= lunes*1 or semana=martes*2 or semana=miercoles*3 or semana=jueves*4 or semana=viernes*5 or semana=sabado*6 or semana=domingo *7) then activacion gosub alarma_on endif .. alarma_on: subrutina de activacin de alarma y luz de fondo ACTIVACION ALARMA

high alarma1 activa bit de alarma high luz active bit de luz de fondo return devuelve el control .. En el ejemplo anterior, si la condicin del IF es cierta el programa ejecutar la llamada a la subrutina alarma_on, la cual desviar la ejecucin al bloque alarma_on (que vendr delimitado por su etiqueta) hasta encontrar la sentencia RETURN que har que el programa devuelva el control a la siguiente instruccin donde se haba realizado la llamada. Como se est viendo la utilizacin de subrutinas es un modo muy poderoso de optimizar y ahorrar cdigo ya que un mismo bloque (subrutina) podr emplearse en mltiples sitios del programa sin por ello tener que repetirlo tantas veces como se necesite.

OPERACIONES LGICAS Y MATEMTICAS


PBP dispone de cinco operaciones matemticas bsicas, disponibles para las variables tipo Byte y Word. Estas son la suma (operador +), la sustraccin (operador -), el producto (operador *), el cociente (operador /) y el mdulo (operador MOD), siendo posible calcular

races cuadradas (aunque el resultado debe ser entero) con la funcin SQR, e incluso para las variables de tipo Bit existen siete operaciones lgicas disponibles. Solo es posible efectuar una operacin lgica por instruccin (aunque es muy posible que prximas versiones permitan ms flexibilidad.

IF THEN ELSE ENDIF


En cualquier programa medianamente complejo que queramos realizar, seguramente necesitaremos en algn punto tomar alguna decisin basndonos en el estado de una entrada o en el valor de una variable. PBP incorpora instrucciones que nos permiten este tipo de comportamiento, siendo la ms sencilla y frecuentemente utilizada la sentencia IF THEN ELSE ENDIF. Caso 1: IF condicin THEN instruccin, donde IF significa SI., y THEN significa LUEGO o ENTONCES. El caso anterior puede leerse como SI se cumple la condicin, entonces ejecuto la instruccin .La condicin es una expresin lgica que puede ser verdadera o falsa. En caso de ser verdadera, la instruccin a continuacin del THEN ser ejecutada. En caso de la condicin sea falsa, el programa seguir su ejecucin con la instruccin siguiente al IF THEN. Caso 2: IFTHENENDIF: Muchas veces, luego de evaluar la condicin necesitamos ejecutar ms de una instruccin. En los ejemplos vistos en el CASO 1 siempre se ejecutaba una sola instruccin cuando la condicin era cierta, ahora no varia prcticamente nada respecto del primer caso, salvo que esta vez se van a ejecutar todas las instrucciones que se encuentren entre el THEN y el ENDIF cada vez que condicin sea verdadera. Caso 3: IF.THEN.ELSEENDIF: Hay veces que de acuerdo a la condicin, queremos ejecutar un grupo u otro de instrucciones. Para eso, utilizamos el ELSE:,es decir, si la condicin es verdadera, se ejecutan las sentencias entre THEN y ELSE. Y si la condicin es falsa,

las que estn entre ELSE y ENDIF. ELSE puede ser traducido como en otro caso o si no. Caso4:IF..THEN..ELSEELSE ENDIFENIF: Por ultimo, tenemos que saber que es posible anidar instrucciones IF-THEN-ELSEENDIF, con lo que se pueden tomar decisiones verdaderamente complejas. Por supuesto, tenemos que ser cautos en el uso de esta caracterstica ya que debido a limitaciones en el tamao de la pila y cantidad de memoria disponible del PIC podemos ocasionar un desborde y el programa colapsara. Como ejemplo veamos una seccin del programa presentado donde se utilizan varios bloques IF..THEN..ELSE encadenados TICKS=0 SEGUNDO=SEGUNDO+1 IF SEGUNDO=60 THEN SEGUNDO=0 MINUTO=MINUTO+1 IF MINUTO=60 THEN MINUTO=0 HORA=HORA+1 IF HORA=24 THEN HORA=0 semana=semana+1 if semana=7 then semana=0

endif ENDIF ENDIF ENDIF Es decir: cada vez que la variable segundo toma el valor 60 se resetea su valor, y se incrementan el valor de los minutos. Si a su vez los minutos superan el valor de 60 se resetea su valor y se incrementa el valor de las horas. A su vez si se superan las 24 horas se resetea su valor y se incrementa el da de la semana. Este anlisis termina cuando se superan los 7 das de la semana que se resetea para empezar nuevamente el ciclo.

BUCLES
As como la toma de decisiones que vimos en el epgrafe anterior esta presente en casi todos nuestros programas, las estructuras que permiten repetir un grupo de instrucciones un nmero determinado de veces tambin son indispensables. En PBP hay dos de ellas. Veamos ahora la primera, FOR TO STEP NEXT. Esta estructura necesita una variable (tipo Byte o Word) para funcionar. En cada iteracin del bucle, la variable va cambiando su valor. Cuando el valor de la variable alcanza o supera el valor prefijado, el bucle termina.

WHILE WEND
La segunda estructura de control que proporciona PBP es WHILE WEND. Su propsito es el mismo que la que vimos en el apartado anterior, y su funcionamiento consiste en que mientras que la condicin sea verdadera, el grupo de instrucciones dentro del cuerpo del WHILE-WEND se ejecuta (las caractersticas de la condicin son las mismas que vimos para IF-THEN-ELSE-ENDIF).

Por supuesto, si no somos cuidadosos al momento de elegir la condicin, puede darse el caso de que el nmero de repeticiones del bucle sea infinito, y nunca salgamos de l. De hecho, esta circunstancia se aprovecha en algunos programas para repetir indefinidamente un grupo de instrucciones. Tambin hay que tener presente que si la condicin no es cierta al momento de ejecutar la primera vez el WHILE, el flujo del programa pasara directamente a la instruccin posterior al WEND y las instrucciones dentro del bucle no se ejecutarn ninguna vez.

SELECT CASE
La tercera estructura de control que proporciona PBP es select case end select. Su propsito es el simplificar las estructuras anidadas if..then else en los casos en que sea siempre la mima variable y se necesiten varias decisiones en funcin de los valores posibles, y su estructura es la siguiente: SELECT CASE variable CASE valor1: instruccion1 END SELECT Como ejemplo veamos una seccin del programa presentado donde se utilizan varios bloques case : select case desc case 0: LCDOUT <OFF>

gosub borra case 1: todos los dias

gosub llena

LCDOUT Todos LMXJVSD case 2: laborales gosub llena sabado=0 domingo=0 LCDOUT Labor LMXJV case 3 : libra lunes gosub llena lunes=0 LCDOUT NoLun case 4: LCDout Maan

GOSUB BORRA IF SEMANA=1 then martes=1 if semana=2 then miercoles=1 if semana=3 then jueves=1 if semana=4 then viernes=1 if semana=5 then sabado=1 if semana=6 then domingo=1 if semana=7 then lunes=1 case 5: LCDout <ON>

end select Es decir en funcin de los cinco valores que tome la variable desc se escribir en el LCD la modalidad de la temporizacin.

LOOKUP
La funcin LOOKUP puede ser utilizada para seleccionar un Byte desde una lista de constantes del mismo tipo, de acuerdo al valor de un ndice (tambin de tipo Byte). El resultado de la seleccin se almacena tambin en una variable tipo byte. La forma de la funcin LOOKUP es la siguiente: LOOKUP(byte0, byte1, , byteN), indice,variable. Esta funcin asignara el valor byteX a variable en funcin del valor de ndice ( es decir si ndice=0 se asignara a variable el valor de byte0, si por el contrario ndice=1, se asignara a variable el valor de byte1, y as sucesivamente). Veamos como se ha usado esta funcin en el programa que presentamos: LOOKUP semana,["0","L","M","X","J","V","S","D","M"] ,pattern LCDout pattern LOOKUP semana,["F","U","A","I","U","I","A","O","A"] ,pattern LCDout pattern En el primer Lookup, la variable pattern tomara el primer carcter del da de la semana, reservando el 0 para desconexin (por ejemplo tomara O para semana=0,L para semana =1,M para semana=2,M para semana etc.) En el segundo Lookup, la variable pattern tomara el segundo carcter del da de la semana, reservando el 0 para desconexin (por ejemplo tomara F para semana=0, U para semana =1, A para semana=2, I para semana etc.).

Por tanto con estas dos sentencias lookup y adyacentes, escribiremos en la segunda lnea del display los caracteres: OF, LU, MA, MI, JU, VI, SA o DO en funcin del valor de la variable semana .

SOPORTE DE DISPLAYS LCD


Pin LCD Pin Puerto Paralelo Funcin LCD

1 GND

Gnd

2 +5v

>

VCC

3 GND

>

Contraste LCD

16

RS

5 GND

>

Write

Enable

Data bit 0

Data bit 1

Data bit 2

10

Data bit 3

11

Data bit 4

12

Data bit 5

13

Data bit 6

14

Data bit 7

Lista de conexiones del LCD 216 A grandes rasgos, y a pesar de la simplicidad que brinda el disponer de un mismo integrado especializado (compatible con Hitachi 44480)en casi todos los modelos de displays, la escritura en estos es relativamente compleja, dado que se deben respetar protocolos de inicializacin, tiempos entre envo de datos, etc., lo que hace bastante tediosa su programacin en Assambler. Pero PBP dispone de un juego de instrucciones especiales para manejar displays en modo 8 bits y en modo 4 bits que nos evitan toda esa complejidad. El manejo de los LCD se hace mediante el uso de sentencias DEFINE, que le dicen al compilador a que pines del microcontrolador hemos conectado cada uno de los pines del LCD. La forma de la instruccin DEFINE es la siguiente: DEFINE parmetro = valor. Donde parmetro es el nombre del parmetro al que le queremos asignar el valor. Los parmetros disponibles para el manejo de LCD alfanumricos son los siguientes:

LCD_BITS: Define el nmero de bits de la interfaz de datos. Se pueden asignar valores de 4 u 8, siendo 4 el valor por defecto. LCD_DREG: Define a que puerto del PIC tenemos conectado el port de datos del LCD. Los valores permitidos son PORTA, PORTB, PORTC, etc. Por defecto se asume PORTB. LCD_DBIT: Define cual es el primer pin del puerto que usamos para enviar los datos al LCD cuando seleccionamos un bus de 4 bits. Solo puede ser el 0 (para los pines el 0, 1, 2 y 3) o 4 (para usar los pines 4, 5, 6 y 7. Por defecto se asume 4, y esta instruccin se ignora para LCD_BITS = 8. LCD_RSREG: Define a que puerto del PIC tenemos conectado el pin RS del LCD. Los valores permitidos son PORTA, PORTB, PORTC, etc. Por defecto se asume PORTB. LCD_RSBIT: Define a que pin del puerto tenemos conectado el pin RS del LCD. Por defecto se asume 3.

LCD_EREG: Define a que puerto del PIC tenemos conectado el pin E del LCD. Los valores permitidos son PORTA, PORTB, PORTC, etc. Por defecto se asume PORTB. LCD_EBIT: Define a que pin del puerto tenemos conectado el pin E del LCD. Por defecto se asume 2. LCD_RWREG: Define a que puerto del PIC tenemos conectado el pin RW del LCD. Los valores permitidos son 0, PORTA, PORTB, PORTC, etc. Por defecto se asume 0, que significa no usamos el pin RW. LCD_RWBIT: Define a que pin del puerto tenemos conectado el pin RW del LCD. Por defecto se asume 0, que significa no usamos el pin RW. LCD_COMMANDUS: Define cuantos microsegundos demora la escritura de un comando en el display. Por defecto, este valor es de 5000. La mayora de los LCD funcionan bien con un valor de 2000, lo que hace ms rpidos nuestros programas. LCD_DATAUS: Define cuantos microsegundos demora la escritura de un dato en el LCD. Por defecto, este valor es de 100. LCD_INITMS: Define cuantos microsegundos demora la inicializacin e la electrnica del LCD. Por defecto, este valor es de 100.

Toda esta sintaxis es completamente funcional, pero PBP para hacernos las cosas ms fciles, si lo deseamos podemos obviar todas las instrucciones anteriores (siempre que incluyamos al menos un comando LCDout), asumiendo PBP que el display funcionara en modo 4 bits, estando conectado a las siguientes pines:

RA0,RA1,RA2,RA3 a los pines de DATOS(PINES 11,12,13,14) RB3=al pin ENABLE (PIN 6) RA4=al pin RS (PIN 4) vcc (pin 2) gnd (pin 1) vee(pin 3) a vcc

Por ultimo tenemos una serie de instrucciones que manejan el envo de comandos e instrucciones al display:

LCDINIT debe utilizarse antes de enviar cualquier comando o dato al LCD. La forma de esta instruccin es al siguiente: LCDINIT n (donde n es el tipo de cursor que queremos que muestre el display. 0 significa que el cursor estar oculto, 1 significa que el cursor parpadeara, 2 nos mostrara un cursor subrayado, y 3 un cursor subrayado y parpadeando). o LCDClear: Borra el contenido del LCD. o LCDHome: Lleva el cursor a la primera posicin del primer rengln del LCD. o LCDLine2Home: Lleva el cursor a la primera posicin del segundo rengln del LCD. o LCDLeft: Mueve el cursor una posicin a la izquierda. o LCDRight: Mueve el cursor una posicin a la derecha. o LCDShiftLeft: Desplaza el contenido del LCD una posicin a la izquierda. o LCDShiftRight: Desplaza el contenido del LCD una posicin a la derecha. o LCDLine1Clear: Borra la primera lnea del LCD. o LCDLine2Clear: Borra la segunda lnea del LCD. o LCDLine1Pos(x): Coloca el cursor en la posicin x del primer rengln del LCD. X puede tener cualquier valor entre 1 y 40 o LCDLine2Pos(x): Coloca el cursor en la posicin x del segundo rengln del LCD. X puede tener cualquier valor entre 1 y 40 LCDOUT enva datos al display. Si son caracteres, simplemente los ponemos entre comillas a continuacin del comando. Si se trata de mostrar el contenido de una variable, se escribe la variable (precedida por #) a continuacin del comando. Si se necesitan imprimir varias variables, se pueden separar por comas.

A continuacin, mostramos como se ha gestionado un display LCD de dos lneas en el programa que proponemos LCDout $fe, 1 se inicializa el LCD pause 20 retardo LCDout $FE,2 nos situamos en la primera lnea LCDout dEC2 hora,:,DEC2 minuto ,:,DEC2 SEGUNDO, escribe en el LCD la hora LOOKUP semana,["0","L","M","X","J","V","S","D","M"] ,pattern LCDout pattern escribe en el LCD el contenido de patern LCDout $FE,2posiciona el LCD en la segunda linea LCDOut $FE, $C0 Nos situamos en Principio de la segunda lnea LCDout dEC2 horaAL,:,DEC2 minutoAL , escribe en el LCD ka hora de alarma RETURN

TEMPORIZADOR
Por ultimo, PPB tambin es capaz de gestionar el TIMER interno a travs del comando OPTION_REG seguido del prescaler, ayudndose adems de las funciones INTCON (que habilitara las interrupciones por hardware) y de la funcin ON INTERRUPT (que har que con llegada de una interrupcin el programa pare, donde se encuentre y se salte a la rutina de servicio de interrupcin, para luego volver al punto donde se paro)

Por ejemplo en el programa que presentaremos lo haremos as: OPTION_REG=$05 timer prescaler es cargado a 64 a travs de option_reg 00000101 ON INTERRUPT GOTO ISR INTCON=$A0 habilita INTERRUPCIONES POR TMR0

Lo cual significa que se generara una interrupcin que provocara que PBP llame a una rutina especial (llamada rutina de interrupcin) marcada con la etiqueta ISR Para esto el prescaler se ha puesta al valor 64 y a TMR se le permite que cuente desde 0 hasta 255, de modo que con la frecuencia de reloj de 4Mhz cada interrupcin ser generada cada 256 *64=16.384ms En cuanto al cdigo que debe contener dicha rutina de interrupcin, este debe ser lo ms breve posible (para que no ralentice la ejecucin del programa), y sobre todo debe venir precedida por DISABLE (para evitar que haya una nueva interrupcin sin terminar el cdigo de servicio de la primera) y debe ir terminada por ENABLE y un END. Para poder contar segundos la rutina ISR contara TICS hasta que alcance el valor 61 pues en ese momento 61*16,384=999.424ms, es decir aproximadamente seg. , Con lo cual podremos ir incrementando la cuenta de segundos, minutos, horas y das de semana sucesivamente. A continuacin se describe la rutina de servicio empleada en el programa : DISABLE desactiva interrupciones

ISR: comienzo de rutina de interrupciones TICKS=TICKS +1 incrementa el contador

if ticks<61 THEN SINACTUALIZAR se escapa hasta que el contador no supere el valor de 61 TICKS=0 SEGUNDO=SEGUNDO+1 se incrementa los segundos IF SEGUNDO=60 THEN un minuto tiene 60 segundos SEGUNDO=0 MINUTO=MINUTO+1 se incrementan los minutos IF MINUTO=60 THEN una hora tiene 60 minutos MINUTO=0 HORA=HORA+1 IF HORA=24 THEN un dia son 24 horas HORA=0 semana=semana+1 if semana=7 then cada semana son 7 das semana=0 endif ENDIF ENDIF ENDIF SINACTUALIZAR: INTCON.2=0 REACTIVA TMR0

RESUME ENABLE reactiva interrupciones END fin rutina interrupciones DESCARGA DIRECTA DEL FICHERO HEX PROGRAMADOR_SEMANAL_CON_LCD -hex(al descargarlo renombrar como .hex)

You might also like