PRIMER PROYECTO CON EL PIC 16F84A
Esta es la primera incursión en este apartado de programación para microcontroladores PIC, pretendo mostrar algunos proyectos con los que el interesado en aprender a programar microcontroladores PIC, consiga ciertos conocimientos y cierta fluidez, al mismo tiempo que disponga de una 'librería' de rutinas en la que inspirarse a la hora de plantear un proyecto o la solución a un posible problema que le surja.
Esta pretende ser una aproximación muy lineal a la escritura de un programa. Son muchos los que escriben y producen subrutinas muy pequeñas con saltos (GOTO's) de una subrutina a otra, de modo que el programa salta por todas partes como 'palomitas de maíz'. El micro es capaz, perfectamente capaz de saltar por un programa, sin embargo, éste tipo de disposición es muy difícil de seguir incluso por un lector avezado.
Es mucho más fácil seguir la secuencia de acontecimientos y así, muy pronto seremos capaces de escribir programas similares a los de la sección de proyectos de cualquier empresa." - Así, empieza otro de los artículos que tengo en esta web.
En incontables sitios se utiliza un listado de apoyo al que se inicia en la programación, dicho listado describe qué hace dicho listado. En este caso describe, cómo controlar el encendido y apagado de un punto de luz mediante un diodo LED. Después de considerar otros modos de empezar este camino, he llegado a la conclusión que, por algo hay que empezar y ese camino trazado por otros puede que sea el mejor y en ello estamos.
Se debe utilizar un editor de texto que no añada códigos extraños, puede utilizarse el Bloc de Notas, aunque hay otros más adaptados a la programación que ofrecen mejores prestaciones como el EditPlus2, eso lo dejo a elección del lector. El MPLAB integra un editor eficiente, aunque tiene sus "cosas" y distingue entre mayúsculas y minúsculas, así que, decídase por un criterio y sigamos.
El listado del programa en lenguaje ensamblador se muestra en las líneas siguientes, recuérdese que cualquier línea que empieza por un ';' (punto y coma) se interpretará como un comentario sin efecto en el programa, estas líneas sirven de ayuda y recomiendo utilizarlas para una mejor comprensión de qué hace el programa en ciertos momentos.
El listado debe ser guardado con la extensión .asm
01 ;******************************************************************* 02 ; Autor: 03 ; Fecha:06/07/2005 04 ; Titulo: rutina1.asm 05 ; Programa de ejemplo para detallar las lineas que lo integran y 06 ; detallar rutinas. 07 ;******************************************************************* 08 list p=16F84 09 #include "p16F84.inc" ;Encabezado para el MPLAB 10 11 ; Igualdades o "equ" y Mapa de memoria 12 13 port_a equ 0x05h ;registro del puerto A 14 port_b equ 0x06h ;registro del puerto B 15 trisa equ 0x05h ;registro trisa 16 trisb equ 0x06h ;registro trisb 17 status equ 0x03h ; 18 count1 equ 0x0Ch ;variables que usemos siempre a 19 count2 equ 0x0Dh ; partir de la direccion 0Ch 20 count3 equ 0x0Eh 22 org 0x00 ;origen del programa 23 goto Primero ;se pone este GOTO para que salte al principio ; 25 Primero: bsf status, RP0 ;selecciona banco 1 para configurarlo 26 bcf tris_b, 03 ;pone a 0 el bit 3 (RB3) como salida 27 bcf status, RP0 ;selecciona banco 0, para continuar programa 29 inicio: 30 call flash ;Llama la subrutina flash para destellar brevemente LED 31 call ret_largo ;Llama la subrutina ret_largo, donde espera un tiempo 32 goto inicio ;Hazlo otra vez, y otra vez, ... 34 ;Subrutina para destellar el LED, una vez 25 flash: 36 bsf port_b, 03 ;pone a 1 el bit 3 del puerto B, que enciende el LED 37 call ret ;llama la subrutina para retardo corto, mientras el LED luce 38 bcf port_b, 03 ;pone a 0 el bit 3 del puerto B, que apaga LED 39 return ;retorna al punto de llamada. 41 ;Retardo largo, lazo de subrutina que llama la subrutina de retardo corto 42 ret_largo: ;inicializar contador retardo largo 43 movlw 0x03Fh ;carga el acumulador con el valor 3Fh 44 movwf count3 ;mueve el contenido del acumulador a count3, 45 largo1: call ret ;llama la subrutina retardo corto 46 decfsz count3, 1 ;Decrementa count3, guarda el resultado en F y ;si no es cero salta la siguiente instruccion. 47 goto largo1 ;vuelve a decrementar mientras largo1 no sea 0 48 return ;si es 0, regresa al punto de llamada. 50 ;La subrutina de retardo corta con dos lazos anidados. - 51 ret: movlw 0xFF ;Carga el acumulador con el valor FF 52 movwf count1 ;mueve contenido del acumulador a count1 ;para iniciar lazo externo. 54 ret1: movlw 0x0Fh ;Carga el acumulador con el valor 0Fh 55 movwf count2 ;mueve contenido del acumulador a count2 ;para iniciar lazo interno 57 ret2: decfsz count2, 1 ;decrementa contador de lazo interno, guarda el resultado en F ;si no es cero salta una instruccion. 58 goto ret2 ;volver a la subrutina ret2 otra vez (lazo ret2) 59 decfsz count1, 1 ;decrementa count1 (lazo externo) si contador no es cero ; salta una instruccion. 60 goto ret1 ;vuelve a decrementar mientras ret1 no sea 0. 61 return ;si es 0, retorna al punto de llamada. 63 END ; Termina el programa
Recordar que todo lo que hay en una línea, detrás de un punto y coma (';') son comentarios que no influyen en el programa MPLAB, sólo se usan como ayuda. Para que el listado tenga cierta coherencia, se recomienda utilizar el tabulador (8) para crear los espacios necesarios y que haya claridad en el listado. En el listado anterior, se ha incluido el número de línea como ayuda, no lo debe poner en su programa.
En las primeras 6 líneas se hacen los comentarios que describen cual es el motivo del archivo.
;************************************************************* ; Autor: V. Garcia Fecha: 06/07/2005 ; Titulo: rutina1.asm ; Prueba para detallar las líneas que lo integran ; ;*************************************************************
En las líneas 8 y 9 se indica al ensamblador que vamos a trabajar con el PIC 16F84, es conveniente su inclusión. La definición de etiqueta estándar de PIC16F84A es leída según la directriz INCLUDE. Los archivos de definición generalmente son instalados en el lugar ; C:\Program Files\Mplab\p16f84a.inc
Al respecto del MPLAB, como este programa no admite cabeceras mayores de 60 dígitos, es conveniente que creemos un carpeta, para contener nuestros ejemplos o proyectos, comoC:\Archivos de Programa\Mplab\proyetos\, donde guardaremos los listados, esto nos evitará algunos errores, cuando vayamos a compilar posteriormente.
;**************************************** 8 list p=16F84 ;Define tipo de procesador 9 #include "p16F84.inc" ;Encabezado ;****************************************
En las líneas 13 a 17 se declaran las "equ" o igualdades, el mapa de memoria.
;**************************************** 13 port_a equ 0x05h ;registro del puerto A 14 port_b equ 0x06h ;registro del puerto B 15 trisa equ 0x05h ;registro trisa 16 trisb equ 0x06h ;registro trisb 17 estado equ 0x03h ;****************************************
En las siguientes líneas se definen las etiquetas y direcciones de uso personal llamadas de propósito general, las direcciones de los registros personales u otros que vayamos a utilizar en el programa, según el mapa de memoria que empieza en 0Ch, en el caso del F84. Debe observarse, donde empieza dicho mapa de memoria que, puede variar en cada modelo de microcontrolador.
;****************************************** 18 conta1 equ 0x0Ch ;define contador 1.- Variables que usemos siempre a 19 conta2 equ 0x0Dh ;define contador 2 partir de la dirección 0Ch 20 conta3 equ 0x0Eh ;define contador 3 ;******************************************
En la línea 22, se encuentra el origen del programa, es decir, al iniciar el programa o al dar tensión de la fuente de alimentación al micro, es donde irá el programa cada vez que se inicie o cada vez que se resetea o hay un corte de suministro. Le sigue la orden de salto GOTO que hace saltar al programa hasta la etiqueta que le sigue, en este caso Primero
;****************************************** 22 org 0x00 ; origen del programa 23 goto Primero ;se no este GOTO para que salte al principio ;******************************************
El las líneas siguientes se definen donde empiezan las posiciones de memoria; los bancos 0 y 1, los PuertoA, PuertoB y los TRISA y el TRISB con sus posiciones. Al referirnos al Puerto A o Puerto B, nos estamos refiriendo implícitamente al Banco 0, ya que el banco 0 no dispone de los tres registros TRIS y OPTION, por este motivo se llama de 'memoria paginada', ver el mapa de memoria siguiente.
Según las hojas de especificaciones, la memoria de datos se divide en múltiples bancos, que contiene los GPRs (Registros de Objetivo General) y los SFRs (Registros de Función Especial). Los Bits RP0 y RP1 son los bits de selección de banco, el 'F8A tiene los banco0 y banco1, con un bit se direccionan 2 bancos, con dos bits 4 bancos, etc.
La tabla anterior, muestra que los bancos vienen definidos por el estado del bit (5) RP0, de modo que, si RP0 = 0 el Banco 0 es el banco operativo y si el bit 5 RP0 = 1 el Banco 1 es el banco operativo. Esto naturalmente depende del microcontrolador que estemos usando. Sugiero que se lean las hojas de especificaciones del micro que vayamos a usar. Se sorprenderá, de las información que contienen, ya que es del interés del fabricante.
Nota.
- Los valores expresados se harán en hexadecimal acompañados al final de una h, en caso de hacerlo en binario con una b y cuando sea en decimal anteponer un (.) punto.
- Las etiquetas y nombres de registros se expresarán en una silaba de 8 letras, se admite el subguión '_' para unir dos.
- Los nombres de los registros, conviene definirlos en minúsculas, así pues, Port A o Puerto A, lo pondremos siempre como una sola palabra porta o puertoa o también porta o puerto_a, el modo de definición es importante ya que, siempre nos tendremos que referir del mismo modo, véase el archivo inc del micro usado para más detalles. Otro ejemplo, en lugar de 'PuertoA equ 05' podemos poner 'porta equ 05' y cada vez que nos referimos al Puerto A, lo haremos como 'porta'.
A continuación, definimos los pines de entrada y salida en las siguientes líneas:
;****************** Mapa de memoria ******************* 22 Primero: bsf status, RP0 ;selecciona banco 1 para configurarlo 23 bcf tris_b, 3 ;pone a 0 el bit 3 (RB3) como salida 24 bcf status, RP0 ;selecciona banco 0, para continuar programa ;***************************************************
Las instrucciones bsf (bit set file) ponen un bit RP0 del registro (en este caso el registro 'status') a 1. Como ya se dijo, si el bit 5 del registro status está a 1, entonces estamos trabajando en el banco 1, se hace esto para definir los pines de entrada/salida de los puertos del micro.
En esta sección se puede encontrar una disposición algo modificada aunque no presenta diferencias de cara al micro. Me estoy refiriendo a una definición como:
;****************** Mapa de memoria ******************* 22 BSF 03,5 ;Ir al banco 1 23 MOVLW 0F ;Pon 0000 1111 en W para 24 MOVWF 05 ; hacer RA0, RA1, RA2 y RA3 entradas 25 BCF 03,5 ;Ir a banco 0 26 GOTO Principal ;***************************************************
Incluso como:
;****************** Mapa de memoria ******************* 22 bank1 ;Seleccion del Banco 1 23 movlw 0x01 ;w = 0x01 24 movwf trisb ;Bit 0 de trisb en "1" RB0 = IN; RB1-RB7 = 0 25 clrf trisa ;trisa = 0, Todo el Puerto RA = Salida 26 bank0 ;seleccion del Banco 0 ;***************************************************
Aunque para esto, antes habremos definido bank1 y bank0, en la sección "equs", como sigue:
;****************** Mapa de memoria ******************* 18 #define BANK1 bsf status,5 ;Macro para abreviar el BANCO 1 19 #define BANK0 bcf status,5 ;Macro para abreviar el BANCO 0 ;***************************************************
Ahora, sigue la subrutina principal que hace llamadas mediante la instrucción CALL, a distintas subrutinas.
;************************************************************ 27 inicio: ;Etiqueta de la subrutina 28 call flash ;Llama la subrutina flash para destellar brevemente LED 29 call ret_largo ;Llama la subrutina ret_largo, donde espera un tiempo 30 goto inicio ;Hazlo otra vez, y otra vez, ... ;*************************************************************
Le sigue la rutina es la encargada de encender y apagar el LED (en este caso)
;************************************************************ 32 ;Subrutina para destellar el LED una vez, brevemente 33 flash: 34 bsf port_b, 03 ;pone a 1 el bit 3 del puerto B, que enciende el LED 35 call ret ;llama a subrutina retardo corto, mientras, luce LED 36 bcf port_b, 03 ;pone a 0 el bit 3 del puerto B, que apaga LED 37 return ;retorna al punto de llamada. ;************************************************************
Si es el caso, definiremos nuevas rutinas que realicen nuevas tareas, como es un retardo corto y uno largo u otra tarea.
;************************************************************ 39;Retardo largo lazo de la subrutina que llama la subrutina retardo corto 40 ret_largo: ;inicializar contador retardo largo 41 movlw 0x03Fh ;carga el acumulador con el valor 3Fh 42 movwf count3 ;mueve el contenido del acumulador a count3, 43 44 largo1: 45 call ret ;llama la subrutina retardo corto 46 decfsz count3, 1 ;Decrementa count3, guarda el resultado en F y ;si no es cero salta la siguiente instruccion. 47 goto largo1 ;vuelve a decrementar mientras largo1 no sea 0 48 return ;si es 0, regresa al punto de llamada. ;************************************************************
El resto del listado una vez más se describe así.
50 ;La subrutina de retardo corta con dos lazos anidados. 51 ret: movlw 0xFF ;Carga el acumulador con el valor FF 52 movwf count1 ;mueve contenido del acumulador a count1 ;para iniciar lazo externo. 54 ret1: movlw 0x0Fh ;Carga el acumulador con el valor 0Fh 55 movwf count2 ;mueve contenido del acumulador a count2 ;para iniciar lazo interno 57 ret2: decfsz count2, 1 ;decrementa count2 lazo interno, guarda el resultado en F ; y si es cero salta la siguiente instruccion. 58 goto ret2 ;volver a la subrutina ret2 otra vez (lazo ret2) 59 decfsz count1, 1 ;decrementa count1 (lazo externo) y si count1 es cero ; salta la siguiente instruccion. 60 goto ret1 ;vuelve a decrementar mientras ret1 no sea 0. 61 return ;si es 0, retorna al punto de llamada. 63 END ; Termina el programa
Este listado nos ha servido para describir ciertas partes interesante en el desarrollo de un programa escrito en ensamblador para los micros de la familia PIC. En próximos trabajos desarrollaremos otros interesantes proyectos que tengan utilidad práctica.
Una vez terminado el listado mnemónico con las instrucciones que hayamos considerado necesarias para el proyecto, debemos ensamblarlo con una herramienta que la misma empresa creadora del PIC nos proporciona gratis como es el MPLAB©, esta la podemos encontrar en este enlace, si el lector no está familiarizado con esta herramienta, le recomiendo que lea con atención esta guía rápida, con la que puede empezar a probar con este listado que acabamos de describir, ya que entiendo que por su extensión e importancia merece un artículo a parte.
El siguiente es el listado en hex hexadecimal, el que tendremos que quemar en el PIC16F84A, con el quemador que dispongamos.
:1000000083168611831206200A20032886151020E5 :10001000861108003F308E0010208E0B0C2808003F :10002000FF308C000F308D008D0B14288C0B1228A4 :020030000800C6 :00000001FF
Copiar y pegar en el programa del quemador. Puede leer una referencia a esta herramienta del IcPorg, en este enlace.
Aquí se muestra el esquema y circuito eléctrico necesario para utilizar el programa que posteriormente grabaremos 'quemaremos' en un PIC, el proyecto pretende ser el primero de una serie que nos permita aprender cómo producir un programa. Para empezar será necesario disponer de las hojas de datos del PIC16F84A que hay en este sitio, de modo que el estudiante pueda seguir mejor las descripciones.
Según las características del PIC16F84A, el pin de reposición o Reset, es el pin 4, el cual puede conectarse a la línea positiva de Vcc. Sin embargo personalmente no me parece muy seguro esto y opté por utilizar una red RC típica, formada por una resistencia y un condensador que aseguren la correcta reposición (Reset) del 'F84A, cuando se ponga a 0 hasta cargarse el condensador.
El motivo del circuito es muy simple y se trata de hacer que un diodo LED destelle. Evidentemente tenemos que empezar desde abajo, nadie nace con conocimientos avanzados, creo que es conveniente empezar por lo más simple y procurando asimilar los pasos, para ascender en los conocimientos de la programación.
Nota. Cuando tengamos terminado el listado del programa y lo hayamos pasado a HEX, debemos tener presente que a la hora de quemar el chip, recordemos si usaremos para el reloj un cuarzo o una red RC, en caso de RC en el programador activaremos la opción RC para que funcione. En caso de usar un cuarzo activar la opción XT en el programador y lo quemaremos con estas opciones, de lo contrario no funcionará y no encontraremos fallos en el programa.
Para mantener las cosas sencillas y fáciles, utilizaremos la opción de oscilador RC, si bien, este oscilador no es particularmente estable, servirá para este proyecto al igual que en otros muchos. El esquema (muy simple) que se utilizará es el anterior y el pcb este:
Aunque la línea de Vcc del esquema dice ser de 5V, puede ser utilizada una tensión menor, una de 3V o 4' 5V que, son valores más normalizados, aun así, he intercalado un diodo 1N4007 con el que asegurar la polaridad y la tensión incluso para 6V. Como puede apreciarse el esquema es muy sencillo, en esencia debido a la presencia del PIC y en la siguiente figura se muestra el circuito muy simple ejecutado sobre la placa de pruebas. Probablemente el lector haya programado anteriormente y le parezca esto demasiado elemental, espero que sepa disculpar y tenga paciencia, no todos están a la misma altura.
Es muy sencillo como ya se mencionaba, pero es una práctica que pretende ayudarnos a comprender los pasos a seguir en la programación en ensamblador (.asm) de un proyecto y su puesta en funcionamiento. En próximos artículos se tratan nuevos temas, hay mucho que desarrollar y practicar.