PIC18F4550.pdf

July 9, 2017 | Author: Edipame | Category: Microcontroller, Pic Microcontroller, Bit, Computer Memory, Assembly Language
Share Embed Donate


Short Description

Download PIC18F4550.pdf...

Description

Índice general 1. Introducción 1.1. Estructura de la memoria . . . . . . . . . . . . . . . . . . . . . . . . . 2. Los microcontroladores PIC 2.1. ¿Qué es un microcontrolador? . . . . . . 2.2. Aplicaciones . . . . . . . . . . . . . . . . 2.3. Historia de los microcontroladores PIC . 2.4. Las gamas de PIC . . . . . . . . . . . . . 2.5. Los PIC de gama alta . . . . . . . . . . . 2.6. El PIC18F4550 . . . . . . . . . . . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

3. Arquitectura PIC 3.1. Diseño del PIC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2. CPU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3. ALU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.4. Organización de la memoria . . . . . . . . . . . . . . . . . . . . 3.4.1. Memoria de programa . . . . . . . . . . . . . . . . . . . 3.4.2. Memoria de datos . . . . . . . . . . . . . . . . . . . . . 3.5. Juego de instrucciones . . . . . . . . . . . . . . . . . . . . . . . 3.6. Periféricos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.6.1. Puertos de Entrada/Salida . . . . . . . . . . . . . . . . 3.6.2. Universal Serial Bus . . . . . . . . . . . . . . . . . . . . 3.6.3. SPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.6.4. MSSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.6.5. EUSART . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.6.6. Memoria EEPROM . . . . . . . . . . . . . . . . . . . . . 3.6.7. Memoria Flash . . . . . . . . . . . . . . . . . . . . . . . 3.6.8. Timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.6.9. Capturador, comparador y modulador de ancho pulso 3.6.10. Conversor A/D . . . . . . . . . . . . . . . . . . . . . . . 1

. . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . .

. . . . . . . . . . . . . . . . . .

5 7

. . . . . .

9 9 10 10 11 11 12

. . . . . . . . . . . . . . . . . .

13 13 14 14 15 15 17 21 30 30 32 32 33 33 35 39 44 46 47

Compilador de C para el microcontrolador Microchip PIC18F4550 3.6.11. Comparador Analógico . . . . . . 3.6.12. Módulo de Tensión de Referencia 3.6.13. Otros . . . . . . . . . . . . . . . . . 3.7. Características especiales de la CPU . . . 3.7.1. Interrupciones . . . . . . . . . . . . 3.7.2. Perro Guardián . . . . . . . . . . . 3.7.3. Modo de reposo . . . . . . . . . . . 3.7.4. ICSP . . . . . . . . . . . . . . . . . 3.8. Características eléctricas . . . . . . . . . . 4. GNU Compiler Collection 4.1. Historia de GCC . . . . 4.2. Características . . . . . 4.3. Estructura . . . . . . . 4.3.1. Front-end . . . 4.3.2. Middle-end . . 4.3.3. Back-end . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

5. Diseño de la solución 5.1. Registros . . . . . . . . . . . . . . . . . . 5.2. Memoria . . . . . . . . . . . . . . . . . . 5.3. Paso de argumentos a funciones . . . . 5.4. Retorno de valores de funciones . . . . . 5.5. Variables globales y estáticas . . . . . . 5.6. Flujo de ejecución (memoria de código) 5.7. Pila . . . . . . . . . . . . . . . . . . . . . 5.8. Biblioteca de funciones matemáticas . . 6. Implementación 6.1. Creación de patrones . . . . . . . . 6.1.1. define_insn . . . . . . . . . 6.1.2. Ejemplo de define_insn . . 6.1.3. Define_expand . . . . . . . 6.2. Asignación - los patrones mínimos. 6.3. Los patrones del back-end . . . . . 6.4. Especificación . . . . . . . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

2 . . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . .

49 50 50 51 51 53 53 53 53

. . . . . .

56 56 57 58 58 59 59

. . . . . . . .

60 60 61 62 63 64 64 66 67

. . . . . . .

69 75 75 76 79 80 88 93

7. Análisis del código generado 105 7.1. Asignación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

Compilador de C para el microcontrolador Microchip PIC18F4550 7.2. Operaciones matemáticas simples . . . . . . 7.2.1. Suma . . . . . . . . . . . . . . . . . . . 7.2.2. Resta . . . . . . . . . . . . . . . . . . . 7.2.3. Negación . . . . . . . . . . . . . . . . . 7.2.4. Valor absoluto . . . . . . . . . . . . . . 7.3. Operaciones lógicas . . . . . . . . . . . . . . . 7.3.1. AND, OR y XOR . . . . . . . . . . . . 7.3.2. Negación Lógica o Complemento a 1 7.3.3. Desplazamientos . . . . . . . . . . . . 7.4. Conversión de tipos o casting . . . . . . . . . 7.4.1. Extensión de signo . . . . . . . . . . . 7.4.2. Extensión de ceros . . . . . . . . . . . 7.5. Operaciones matemáticas complejas . . . . . 7.5.1. Multiplicación . . . . . . . . . . . . . . 7.5.2. División . . . . . . . . . . . . . . . . . 7.6. Estructuras condicionales o de selección . . . 7.6.1. if . . . . . . . . . . . . . . . . . . . . . 7.6.2. switch . . . . . . . . . . . . . . . . . . 7.7. Estructuras iterativas o bucles . . . . . . . . . 7.7.1. while . . . . . . . . . . . . . . . . . . . 7.7.2. do-while . . . . . . . . . . . . . . . . . 7.7.3. for . . . . . . . . . . . . . . . . . . . . 7.8. Funciones . . . . . . . . . . . . . . . . . . . . 7.8.1. Genéricas . . . . . . . . . . . . . . . . 7.8.2. main . . . . . . . . . . . . . . . . . . . 7.9. Archivos . . . . . . . . . . . . . . . . . . . . . 7.9.1. Genéricos . . . . . . . . . . . . . . . . 7.9.2. Principal . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

3 . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

108 108 110 112 112 113 113 113 114 115 115 116 117 117 118 119 119 124 127 127 128 129 131 131 134 135 135 136

8. Optimizando código con GCC 141 8.1. Tipos de optimización . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 9. Conclusiones

147

Bibliografía

149

A. PIC18-GCC 150 A.1. Compilando GCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 A.2. Utilidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

Compilador de C para el microcontrolador Microchip PIC18F4550

4

A.3. Argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 A.4. Parámetros de GCC específicos para el PIC18 . . . . . . . . . . . . . . 153 A.5. Compilando con pic18-gcc . . . . . . . . . . . . . . . . . . . . . . . . . 153 B. GNU PIC Utilities B.1. Introducción . . . . . . . . . . . . . . . . . . B.2. Herramientas . . . . . . . . . . . . . . . . . B.2.1. gpasm . . . . . . . . . . . . . . . . . B.2.2. Código fuente admitido por gpasm B.2.3. gplink . . . . . . . . . . . . . . . . . B.2.4. gplib . . . . . . . . . . . . . . . . . . B.3. Compilando ensamblador . . . . . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

155 155 156 156 157 160 161 162

C. Programas de ejemplo

165

D. Manual de usuario D.1. GCC . . . . . . . . . . . . . . . D.1.1. Ensamblador en línea D.2. GNU Pic Utilities . . . . . . . D.2.1. gpasm . . . . . . . . . D.2.2. gplink . . . . . . . . . D.2.3. gplib . . . . . . . . . . D.2.4. Programador . . . . . D.2.5. Archivos auxiliares . .

170 170 171 172 173 173 173 174 175

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

Capítulo 1 Introducción La mayoría de los programadores de microcontroladores prefieren usar lenguaje ensamblador en lugar de un lenguaje de alto nivel. Sus argumentos son el desperdicio de memoria que introduce un compilador y la mayor optimización que llegan a conseguir escribiendo su propio código ensamblador personalizado, así como la elevada predecibilidad del tiempo de ejecución de los programas escritos en ensamblador. Pese a que puede demostrarse que es cierto en la mayoría de los casos, hay otros muchos factores que inclinan la balanza hacia un lenguaje más alejado de la arquitectura de la máquina, como es el lenguaje C. Para propósitos de experimentación o aprendizaje, la elección de un lenguaje u otro puede apoyarse únicamente en las preferencias personales del desarrollador. Sin embargo, en entornos de desarrollo de productos comerciales hay que considerar dos factores muy importantes: tiempo de desarrollo y coste del mismo. Reducir el coste es un objetivo común en todas las empresas y, en un principio, no tiene relación directa con el lenguaje de programación elegido. En cambio, el tiempo invertido en el desarrollo sí está ligado al lenguaje por el que se opte, especialmente cuando las opciones son lenguaje ensamblador o uno de alto nivel, en nuestro caso C. El desarrollo en lenguaje ensamblador siempre depende del dispositivo elegido para realizar el proyecto. En el caso de dispositivos integrados como los microcontroladores, cambiar de modelo puede implicar un comienzo desde cero, con la enorme pérdida de tiempo y dinero que esto supondría. En cambio, un código en lenguaje C es independiente de la arquitectura del procesador, por lo que sólo habría que preocuparse del control de los dispositivos periféricos. Desarrollando en un lenguaje de alto nivel, un cambio de modelo de microcontrolador a otro (de la misma familia y compatible en dispositivos) resultaría totalmente transparente, siempre suponiendo que el nuevo dispositivo disponga de suficiente memoria para el programa. Incluso sería posible cambiar de familia de dispositivos, por ejemplo 5

Compilador de C para el microcontrolador Microchip PIC18F4550

6

de PIC18 a PIC16, sin más cambios que adaptar el código específico que gestiona los dispositivos periféricos. Relacionado con el punto anterior está el hecho de que el código escrito en un lenguaje de alto nivel como C es fácilmente reutilizable. Por consiguiente, si disponemos de un algoritmo ya implementado, probado, depurado y, por supuesto, comentado, podremos utilizarlo en otros proyectos sin necesidad de invertir grandes cantidades de tiempo en reescribirlo. Si el mismo algoritmo estuviese escrito en código ensamblador para un dispositivo concreto, su reutilización sería mucho más cara, ya que portarlo a otra arquitectura supone, prácticamente, reescribirlo desde cero. Además de las ventajas de portabilidad y reutilización de código, los lenguajes de alto nivel facilitan la depuración de errores. Debido a la dificultad de analizar y verificar un programa en ensamblador, la fase depuración resulta larga y tediosa, lo que lleva a un tiempo de desarrollo en ocasiones excesivo y a que los clientes con menos paciencia opten por cancelar el proyecto. En cambio, los lenguajes de alto nivel permiten técnicas de desarrollo y depuración más eficientes, e incluso es posible emplear técnicas formales de verificación. Por ello el tiempo necesario para esta fase del desarrollo es menor que para el mismo proyecto escrito en ensamblador. Todos estos son motivos de peso que justifican el esfuerzo invertido en este proyecto, cuyo objetivo es disponer de un compilador de lenguaje C completo que permita mejorar la calidad de los programas escritos para el dispositivo escogido, el microcontrolador PIC18F4550 de Microchip, así como reducir el tiempo necesario para desarrollar proyectos de diversa índole que empleen este microcontrolador. Los motivos de escoger el compilador de GNU, GCC, como punto de partida han sido su buen diseño, que después de 25 años de historia sigue siendo una pieza de ingeniería del software digna de admiración; su desarrollo activo por parte de una gran comunidad de desarrolladores experimentados; su amplia difusión y aceptación por usuarios, profesionales y empresas de todo el mundo; su naturaleza de código abierto y, por supuesto, la licencia libre bajo la cual se distribuye, la General Public License (Licencia Pública General) de GNU, que permite en última instancia que este proyecto final de carrera sea posible. El objetivo del proyecto ha sido desarrollar el backend de GCC para el microcontrolador PIC18F4550. Aunque, por tratarse de un proyecto de la Ingeniería Técnica acotamos la dificultad del proyecto en un principio, excluyendo funciones, punteros y matrices de este proyecto, finalmente hemos implementado también funciones, punteros y matrices, dando soporte completo al frontend de C para el PIC18F4550. Para ello, y aunque en principio el plan era ampliar el backend para microcontroladores PIC16 desarrollado en [8], hemos terminado reescribiendo desde cero un

Compilador de C para el microcontrolador Microchip PIC18F4550

7

backend propio; ya que, aunque las diferencias en el código en ensamblador son pequeñas, las diferencias en el manejo de la memoria lo han justificado, y además nos ha permitido utilizar las instrucciones específicas del 18F4550, que suponen una mejora de rendimiento del código resultante muy importante. Hemos realizado el proyecto para la plataforma Linux, aunque por su naturaleza y por el grado de integración con la GCC toolchain alcanzado puede ser recompilado para cualquier plataforma que queramos emplear y que permita ejecutar la GCC toolchain. Finalmente, hemos hecho un sobreesfuerzo para cumplir las líneas de desarrollo de código de GCC, y no afectar código de GCC fuera del que se espera que sea modificado en el desarrollo de un porting limpio, con objeto de su futura integración en la línea principal de desarrollo de GCC.

1.1.

Estructura de la memoria

La estructura de la memoria es la siguiente: En el primer capítulo presentamos el trabajo desarrollado y sus partes. En el segundo capítulo introducimos los microcontroladores y la arquitectura PIC. En el tercer capítulo analizamos la arquitectura del compilador GCC. En el cuarto capítulo planteamos el diseño de nuestra solución, así como las opciones que hemos tomado y justificamos dichas opciones. En el quinto capítulo estudiamos con más detalle aspectos destacables de las decisiones que hemos tomado al implementar el backend, incluyendo análisis de las optimizaciones de código soportadas. En el sexto capítulo comentamos las conclusiones a las que hemos llegado en nuestro trabajo. Después planteamos cuatro apéndices, que incluyen: En el primer apéndice, la mecánica (que no es trivial) para incorporar nuestro código en GCC y compilarlo. En el segundo apéndice describimos las utilidades de GNU para microcontroladores PIC, imprescindibles para utilizar nuestro proyecto: GPUTILS.

Compilador de C para el microcontrolador Microchip PIC18F4550

8

En el tercer apéndice incluimos algunos programas de ejemplo. Finalmente, en el cuarto y último apéndice, incluimos el manual de usuario del programa desarrollado.

Capítulo 2 Los microcontroladores PIC 2.1.

¿Qué es un microcontrolador?

Un microcontrolador es un dispositivo integrado que proporciona las funcionalidades de un pequeño ordenador. Los microcontroladores están compuestos por un procesador, memoria y varios periféricos. Las grandes ventajas de los microcontroladores son su reducido tamaño, bajo coste y gran variedad. Los dispositivos que proporcionan datos de entrada al microcontrolador o reciben información de salida desde el mismo pueden ser muy diversos, desde líneas digitales sencillas que sólo trabajan con dos valores (cero o uno) hasta puertos complejos, como los que se emplean en ordenadores, que permiten la comunicación del microcontrolador con otros dispositivos externos, tales como otros microcontroladores o un ordenador personal. Existen numerosos fabricantes que ofrecen una gran variedad de modelos diferentes. Cada modelo tiene unas características: tamaño, cantidad de memoria, potencia de cálculo, periféricos, consumo de energía, resistencia al calor y resistencia a la humedad. El objetivo de esta diversidad es la reducción de costes, ya que no es razonable exigir un dispositivo potente y completo que se adecúe a cualquier proyecto y que además sea barato. Cuantas más características tenga un dispositivo, mayor será su coste de producción; y, por consiguiente, mayor precio de adquisición. Por ello, la clave reside en hacer diseños sencillos y con unas características limitadas. Dada la variedad de microcontroladores existente, no nos resultará difícil encontrar un modelo que se ajuste a los requisitos de nuestro proyecto y al mismo tiempo resulte económico.

9

Compilador de C para el microcontrolador Microchip PIC18F4550

2.2.

10

Aplicaciones

La gran diversidad de modelos disponibles en el mercado nos permite afrontar infinidad de diseños diferentes, por simples o complejos que estos sean. Algunos ejemplos de uso real de microcontroladores son teléfonos móviles, controles de acceso a edificios, mandos a distancia, sistemas de freno ABS, micro-robótica, redes de sensores o sistemas de control de riego. A la hora de realizar un proyecto hardware la dificultad no reside en el uso de un microcontrolador concreto, sino en la elección del fabricante y el modelo adecuado para nuestro proyecto. Aunque el uso de microcontroladores nos facilitará la tarea en muchos aspectos, debemos tener siempre presente el objetivo de minimizar los costes. En ocasiones encontraremos aplicaciones en las que no será factible emplear un único microcontrolador o en las que un diseño distribuído con varios microcontroladores resultará más eficiente y económico. Por este motivo, resulta conveniente realizar un análisis de costes antes de comenzar el desarrollo.

2.3.

Historia de los microcontroladores PIC

En el mercado de microcontroladores, tres empresas son las más conocidas: Atmel, Motorola y Microchip. Existen muchas otras, como Intel, Texas Instruments o Renesas Technology. Microchip es el fabricante de los microcontroladores PIC. Todos ellos son heredados del PIC1650, que fue desarrollado originalmente por General Instruments. El nombre PIC es la abreviatura de PICmicro, aunque por lo general se considera acrónimo de Peripheral Interface Controller (Controlador de Interfaz Periférico). En un principio, el PIC se diseñó para combinarse con el procesador de 16 bits CP16000 -de ahí que el nombre original fuese Programmable Interface Controller (Controlador de Interface Programable)-. El CP16000, a pesar de ser un buen microprocesador, carecía de una buena E/S y por ello, en 1975, surgió el PIC de 8 bits, pensado para mejorar el rendimiento del sistema conjunto mediante la descarga de operaciones de E/S del CP16000. El PIC original empleaba un microcódigo simple almacenado en ROM para ejecutar su tarea y, a pesar de que el término no existía en aquella época, poseía características típicas de los diseños RISC. Cuando la división de microelectrónica de General Instruments se separó del resto de la empresa, en 1985, el nuevo propietario canceló casi todos los desarrollos, que para esa época estaban obsoletos. Sin embargo, el PIC se mejoró con EPROM, convirtiéndose en un controlador programable de E/S. A día de hoy existen multitud de modelos de PIC que incorporan varios puertos

Compilador de C para el microcontrolador Microchip PIC18F4550

11

de comunicación con el exterior (puertos serie, controladores de motores o conversores analógico-a-digital) y con memorias de programa de hasta 32.000 palabras.

2.4.

Las gamas de PIC

Los modelos de PIC constituyen gamas distintas, en función del tamaño de instrucción que emplean. Actualmente, Microchip comercializa sus microcontroladores clasificados en cuatro gamas: La gama baja la componen la serie PIC10 y una parte de las series PIC12 y PIC16. Utilizan palabras de instrucción de 12 bits, su tamaño es reducido, así como sus características, y su coste es muy bajo. La gama media está compuesta por casi toda la serie PIC16 y una porción de los PIC12. Utilizan un ancho de palabra de instrucción de 14 bits. Ésta es la gama más popular por su buena relación calidad/precio. Además, programarlos en lenguaje ensamblador resulta bastante sencillo, dentro de la complejidad del lenguaje, y es por ello que son una buena opción de cara al aprendizaje. La gama alta o de alto rendimiento la forma la serie de microcontroladores PIC18. Emplean palabras de instrucción de 16 bits y están basados en los PIC de gama media pero con mejoras sustanciales: más puertos de E/S, más conversores A/D o interfaces USB. La gama de 24 bits la componen de las series dsPIC30 y PIC24. Utilizan 24 bits como palabra de instrucción, usan palabras de memoria de datos de 16 bits (y no 8 bits), y son los que ofrecen más memoria y mayor rendimiento.

2.5.

Los PIC de gama alta

La gama alta ofrece microcontroladores adecuados para proyectos relativamente complejos, tales como adquisición de datos, redes de sensores distribuidos e incluso algunas aplicaciones de tiempo real. Además, incorpora varias características que, combinadas con un buen compilador, hacen de esta gama una opción muy interesante para ejecutar programas estructurados y modulares, escritos en lenguajes de alto nivel y haciendo uso de técnicas más avanzadas de ingeniería del software, con el ahorro de tiempo y dinero que esto supone. En el capítulo 3 estudiaremos la arquitectura de los PIC de gama alta.

Compilador de C para el microcontrolador Microchip PIC18F4550

2.6.

12

El PIC18F4550

De entre todos los modelos que componen la gama alta de PIC, el elegido para realizar el port de GCC es el 18F4550. Implementa ocho bancos de memoria de datos, 2Kbytes en total, y 32Kbytes de memoria de programa. Además incorpora 256 bytes de EEPROM para uso general. Cuenta con cuatro temporizadores, dos unidades de comparación, captura y modulación de ancho de pulso, un conversor A/D de 10 bits con trece entradas. Además, cuenta con puertos SPI, I 2 C, USART, SPP, USB. Junto con estos dispositivos, tiene -multiplexados- un total de 5 puertos de E/S. Todas estas características hacen del PIC18F4550 un gran microcontrolador con el que se pueden llevar a cabo infinidad de proyectos de diversa índole.

Capítulo 3 Arquitectura PIC 3.1.

Diseño del PIC

Los microcontroladores PIC emplean la arquitectura Harvard, en la que la memoria de programa y la memoria de datos se encuentran separadas, con sus propios buses de direcciones y datos. Una ventaja de esta arquitectura frente a la Von Neumann es que permite el acceso simultáneo a ambas memorias, con lo que es posible solapar las etapas de búsqueda de la siguiente instrucción mientras la instrucción actual accede al espacio de memoria de datos. El principal inconveniente de esta arquitctura surge con el uso de memoria caché, dado que cada espacio de memoria necesitará su propia caché independiente, y el rendimiento máximo sólo se alcanzará con programas que hagan un número similar de accesos a memorias de programa y datos. En caso contrario, el espacio menos usado tendrá parte de su caché sin utilizar, mientras que la caché del otro se encontrará siempre llena, con las posibles penalizaciones ocasionadas por los fallos de caché que esto conlleva. En cualquier caso, los PIC no incorporan memoria caché, así que este problema no les afecta y sólo obtienen los beneficios de la arquitectura Harvard. Otra mejora de la arquitectura Harvard es que el tamaño de palabra de cada banco de memoria puede ser diferente. En los microcontroladores PIC el banco de memoria de datos emplea un tamaño de palabra típico, 8 bits, mientras que las palabras de la memoria de programa son de distinto tamaño en función de la gama: la gama alta emplea palabras de 16 bits para este espacio de memoria. De este modo, es posible incluir en una sola palabra todos los datos para la ejecución de la instrucción, mejorando la eficiencia ya que se necesita un único acceso a la memoria de programa para cada instrucción. Entre las decisiones de diseño que han permitido a los PIC lograr su alto rendi-

13

Compilador de C para el microcontrolador Microchip PIC18F4550

14

miento sin que esto penalice en su precio se encuentra el pipeline segmentado en dos etapas: búsqueda de instrucción y ejecución. Ambas etapas se ejecutan en un único ciclo de reloj. Gracias a la división de memoria, estas dos etapas se superponen en el tiempo sin ninguna restricción, con lo que se consigue completar la ejecución de una instrucción con cada ciclo de reloj en la mayoría de los casos. Las únicas instrucciones donde no es posible este solape son las que modifican el contador de programa (en inglés, program counter o PC), ya que la búsqueda de la próxima instrucción se efectúa en paralelo a la ejecución de la actual y, hasta que no termine la ejecución de ésta y se cargue el PC, no será posible hacer la búsqueda de la siguiente instrucción. Así, todas las instrucciones de salto emplearán dos ciclos de reloj para ejecutarse. El conjunto de instrucciones es reducido gracias a la ortogonalidad de las mismas, que permiten mover contenidos desde y hacia cualquier registro. La curva de aprendizaje del ensamblador del PIC es, por consiguiente, reducida. Esto, unido a que todos los registros con funciones especiales se encuentran mapeados en memoria (incluído el PC) nos proporciona la potencia y flexibilidad necesarias para manejar, de manera sencilla y elegante, todas las funciones del microcontrolador.

3.2.

CPU

La Unidad Central de Proceso -en adelante, CPU- es el cerebro del microcontrolador. Su cometido es ejecutar la instrucción apuntada por el PC en la memoria de programa mediante la generación de la señales de control apropiadas sobre los buses, registros internos y unidad aritmético-lógica (ALU).

3.3.

ALU

Los microcontroladores PIC incorporan una ALU de 8 bits. Además tienen un registro de trabajo, también de 8 bits, llamado WREG (Working Register). La ALU efectúa operaciones aritméticas y lógicas de propósito general entre el registro WREG y cualquier otro registro de la memoria de datos, o entre WREG y un valor inmediato, tomado de la palabra de la instrucción en curso. La salida de la ALU puede almacenarse en el registro WREG o en el registro de la memoria de datos empleado como parámetro. En función de la instrucción ejecutada, la ALU actualizará los bits apropiados del registro de estado (STATUS), indicando acarreo, acarreo de dígito, desbordamiento, resultado negativo o cero. Con las operaciones que soporta la ALU y la información que devuelve es posible implementar cualquier operación matemática, como se verá más adelante en este

Compilador de C para el microcontrolador Microchip PIC18F4550

15

documento. La gama alta de PIC incorpora, además de la ALU estándar, un multiplicador hardware de valores de 8 bits que permite efectuar multiplicaciones en un sólo ciclo de reloj y facilita el desarrollo de rutinas de multiplicación para datos más grandes, así como de otras operaciones matemáticas avanzadas.

3.4.

Organización de la memoria

Como ya hemos visto, la memoria está dividida en dos espacios, uno para datos y otro para programa. La memoria de datos, a su vez, se encuentra organizada en registros que podemos clasificar en registros de propósito general (GPR) y registros de función especial (SFR), que controlan las funciones del núcleo del microcontrolador. Más adelante estudiaremos los registros especiales para controlar los periféricos.

3.4.1.

Memoria de programa

La memoria de programa emplea palabras de 16 bits con una alineación a nivel de byte. Está conectada a un bus también de 16 bits. Dado que una instrucción ocupa una palabra de la memoria de programa, podemos almacenar tantas instrucciones como palabras de memoria tenga nuestro microcontrolador. Para direccionar este banco de memoria se usa el contador de programa (PC), que direcciona bytes, no palabras. Este registro tiene un ancho de 21 bits, lo que permite direccionar hasta 2Mbytes de memoria. Ante la posibilidad de que el PC quede desalineado respecto a las palabras, su bit menos significativo es siempre 0 y las instrucciones de ejecución secuencial incrementan el contador de programa en dos unidades. De este modo, el PC siempre apuntará a una palabra de 16 bits alineada a nivel de palabra. Pese a que siempre podemos direccionar el máximo de memoria, los microcontroladores incorporan una cantidad de memoria inferior a la máxima que soportan. Todos los accesos de lectura a posiciones de memoria por encima del límite de memoria incorporada devolverán el valor cero. PC El contador de programa apunta a la dirección de la memoria de programa que será accedida en el próximo acceso. Como todos los registros de función especial, está mapeado en la memoria de datos y ésta emplea palabras de 8 bits. Dado que el PC tiene un tamaño de 21 bits, está dividido en tres registros mapeados en la memoria de datos: PCL, PCH y PCU. PCL corresponde al byte bajo del PC (PC[7:0]),

Compilador de C para el microcontrolador Microchip PIC18F4550

16

PCH al byte alto (PC[15:8]) y PCU al byte superior, que almacena los 5 bits más significativos del PC (PC[20:16]). El registro PCL permite lectura y escritura de manera directa. En cambio los otros dos registros, PCH y PCU, no son directamente accesibles por el programador. Para modificar estos registros hacemos uso de otros dos registros especiales, que sí permiten lectura y escritura: PCLATH y PCLATU. Estos registros se comportan como registros temporales que almacenan los valores que cargarán PCH y PCU cuando se realice una escritura en PCL. Por tanto, una modificación del PC requiere tres operaciones, no necesariamente unidas, que tendrán efecto tras la carga de PCL. De manera análoga, una lectura de PCL produce que se carguen en PCLATH y PCLATU los valores de PCH y PCU respectivamente. Instrucciones que modifican el PC Además del procedimiento anterior, el ensamblador de PIC proporciona varias instrucciones que modifican el PC: GOTO, CALL, RCALL y RETURN. Todas ellas modifican el PC directamente, sin tocar los registros PCLATH y PCLATU. La instrucción GOTO efectúa un salto incondicional en el flujo de ejecución del programa a cualquier dirección del rango de 2Mbytes que permite el PIC18. Es fácil deducir que se trata de una instrucción codificada mediante dos palabras de memoria, ya que, de lo contrario, el rango de salto sería menor. El comportamiento de la instrucción CALL es similar al de GOTO. La diferencia entre ellas es que CALL almacena en la pila interna la dirección de la instrucción que se encuentra a continuación de ella (PC+4). Podemos considerar la instrucción RCALL como una versión recortada de la anterior. Produce saltos en el programa de hasta 1K hacia delante o hacia atrás, respecto al valor, incrementado en dos, que almacena el PC en el momento de ejecutarse. La cuarta instrucción en realidad es un conjunto de tres instrucciones (RETURN, RETLW y RETFIE) que, grosso modo, cumplen el mismo cometido. Cuando el microcontrolador ejecuta una de estas instrucciones, el PC carga el valor que se encuentra almacenado en la cima de la pila. Pila Los PIC18 implementan una pila de direcciones de 31 niveles en una memoria aparte de los espacios de programa y datos. Es posible leer y modificar el puntero a la cima de la pila (STKPTR), así como la dirección almacenada en la cima, mediante los registros de función especial de cima de pila. Hay que tener en cuenta que tiene un comportamiento circular, de modo que tras efectuar 31 acciones seguidas

Compilador de C para el microcontrolador Microchip PIC18F4550

17

de apilamiento el puntero a la cima habrá dado la vuelta y quedará apuntando a la primera posición, lo que podría no ser el comportamiento deseado por el programado. Por ello es necesario tener mucha precaución de no sobrepasar los 31 niveles de anidamiento de subrutinas. Vectores Para terminar con la descripción de la memoria de código, veremos dos posiciones importantes para la programación de los dispositivos. En el momento de inicializarse el microcontrolador, la ejecución del programa comienza en la posición de memoria de programa 0x000000h, llamada vector de reset. Es desde esta posición donde debemos insertar un salto al inicio real de nuestro código (el inicio deseado), a través de un salto con GOTO u otro de los métodos alternativos. Existen otros dos vectores ubicados en 0x000008h y 0x000018h, correspondientes a las interrupciones de alta y baja prioridad, respectivamente. Cada vez que se active una señal de interrupción, el microcontrolador deshabilitará las interrupciones, guardará el valor del PC en pila, cargará el nuevo valor de PC a partir del almacenado en uno de estos dos vectores, en función de la señal de interrupción que se haya activado, y continuará ejecutando instrucciones. Esta rutina de tratamiento de interrupción (ISR) deberá terminar con una instrucción RETFIE, con la que el PC recupera su valor a partir de la pila, el registro GIE vuelve a estar habilitado y con él, las interrupciones. Hay que tener en cuenta que disponemos de dos niveles de interrupciones: alta y baja prioridad. La distinción de prioridades permite que los acontecimientos de alta prioridad reciban atención inmediata aún cuando el micro esté tratando otra interrupción -siempre que ésta sea de prioridad baja- y que su rutina de servicio no se vea interrumpida por otros eventos de menor prioridad.

3.4.2.

Memoria de datos

La memoria de datos está compuesta por registros de propósito general (GPR) y registros de función especial (SFR). Los primeros sirven para almacenar cualquier valor sin un propósito establecido de antemano. Los segundos tienen tareas definidas y fijadas en el diseño del hardware: comunicar y controlar los diversos dispositivos integrados en el microcontrolador y controlar el funcionamiento de éste. Debido a la diversidad de microcontroladores, el mapa de registros difiere para cada modelo. A pesar de que existe una tendencia a igualar la ubicación en memoria de los SFR para microcontroladores de una misma familia y gama, es conveniente consultar la hoja de características del modelo, incluso de nuevas revisiones del mismo, para tener la certeza de que el dispositivo funcionará de la manera espe-

Compilador de C para el microcontrolador Microchip PIC18F4550

18

Banco Bits 3:0 accesible de BSR 0 0000 1 0001 2 0010 3 0011 4 0100 5 0101 6 0110 7 0111 Cuadro 3.1: Valores del BSR para acceso a los bancos del microcontrolador PIC18F4550.

rada. Los registros de los periféricos los describiremos más adelante, en la sección correspondiente a cada periférico. Los PIC18 pueden direccionar hasta 4Kbytes de memoria de datos que, como ocurre con el espacio de memoria de programa, pueden estar implementados en su totalidad o disponer de menos memoria. Esta memoria está dividida en varias partes, llamadas bancos, de 256 bytes cada una. El PIC18F4550 implementa 8 bancos completos, lo que resulta en un total de 2Kbytes de memoria de datos. Igual que ocurre con la memoria de programa, todo acceso de lectura a una posición de memoria fuera del rango implementado devuelve un valor 0. Los accesos de escritura a direcciones de memoria no implementadas no tienen ningún efecto más allá de actualizar el registro de estado STATUS como si la operación hubiese tenido éxito. La mayoría de instrucciones de los PIC18 que efectúan accesos a memoria construyen la dirección absoluta a partir de una dirección base (el número de banco) y un desplazamiento relativo. El número de banco al que accederá la próxima instrucción lo indica el registro de selección de banco (BSR), el cual habrá que cambiar antes de hacer un acceso a una posición de memoria ubicada en un banco diferente al que se encuentre seleccionado en el momento actual. El desplazamiento dentro del banco, en cambio, es una dirección de 8 bits incluída en la palabra de instrucción. Banco de acceso Aunque el uso del BSR junto a un desplazamiento de 8 bits permite direccionar todo el rango de memoria de datos, también obliga a tener especial cuidado con qué banco se encuentra seleccionado antes de cada acceso. Podría resultar desastroso que por error escribiésemos en un SFR en lugar de un GPR. Además, la tarea de

Compilador de C para el microcontrolador Microchip PIC18F4550

19

verificar y cambiar el BSR para cada acceso a memoria llega a resultar muy ineficiente. Para agilizar los accesos a las posiciones de memoria que se usan con mayor frecuencia, la memoria de datos está configurada con un banco de acceso, que permite acceder a un bloque de memoria sin hacer uso del BSR. Este banco está compuesto por los primeros 96 bytes del banco 0 y los últimos 160 bytes del banco 15. La primera mitad recibe el nombre de RAM de acceso y está compuesta de registros GPR. La mitad superior, en cambio, está compuesta por los SFR del microcontrolador. Ambas áreas se encuentran mapeadas de manera contigua en el banco de acceso y permiten accesos de modo lineal empleando una dirección de 8 bits. Las instrucciones que hacen uso del banco de acceso son aquellas que incluyen un parámetro a. Cuando este parámetro vale 1, la instrucción accede a memoria de la manera habitual, empleando el BSR y un desplazamiento de 8 bits. Cuando vale 0, el acceso a memoria lo efectúa mediante el banco de acceso, con lo que el valor que tenga el BSR no tiene efecto alguno. El uso de este direccionamiento forzado permite a la instrucción operar sobre una dirección de memoria en un único ciclo de instrucción sin tocar primero el BSR. Para direcciones a partir de la 0x0060h, esto significa un acceso más eficiente a los SFR. La RAM de acceso por debajo de la posición 0x0060h es un buen lugar para valores de datos que necesitemos acceder rápidamente, como resultados de operaciones recientes o variables globales del programa. Registros de propósito general Los registros de propósito general (GPR), como su nombre indica, no tienen un propósito prefijado de antemano: los podemos usar para lo que consideremos oportuno (datos temporales, variables y valores constantes, entre otros usos). La modificación de uno u otro no tiene más relevancia que el cambio de su valor, por lo que nos proporciona la libertad necesaria para ejecutar las acciones que deseemos en el microcontrolador. No todos los dispositivos implementan la totalidad de registros GPR. En estos modelos es posible acceder a todo el mapa de registros pero los accesos de lectura devolverán siempre un valor 0 y las escrituras no tendrán más efecto que actualizar los bits convenientes del registro STATUS en caso de que sean resultado de una operación de la ALU.

Compilador de C para el microcontrolador Microchip PIC18F4550 Registro INDFx POSTINCx POSTDECx PREINCx PLUSWx FSRxH FSRxL TMRxH TMRxL TxCON STATUS PRODH PRODL PCLATU PCLATH PCL INTCON INTCON2 INTCON3 PIR1 PIR2 PIE1 PIE2 IPR1 IPR2 RCON

20

Uso

Registros para acceso indirecto

Punteros a memoria para acceso indirecto Cuenta de los temporizadores Configuración de los temporizadores Registro de estado Byte alto del resultado de multiplicador Byte bajo del resultado de multiplicador Parte superior del PC Parte alta del PC Parte baja del PC Configuración de las interrupciones Estado de las interrupciones Habilitación de interrupciones Asignación de prioridad de las fuentes de interrupción Control de Reset y prioridad de interrupciones

Cuadro 3.2: Resumen de registros de función especial del núcleo del microcontrolador PIC18F4550.

Registros de función especial Los registros de función especial (SFR) son los registros que emplean la CPU y los módulos periféricos para controlar el funcionamiento del microcontrolador. Están mapeados en las últimas posiciones del banco 15 de memoria de datos, desde la posición 0x0F60h hasta la 0x0FFFh. Podemos clasificar los SFR en dos conjuntos: los registros correspondientes a funciones del núcleo (ALU, Reset e interrupciones) y los que corresponden a funciones de los periféricos. La tabla 3.2 presenta un resumen de los registros de función especial del núcleo. Los registros de funciones de periféricos los veremos más adelante.

Compilador de C para el microcontrolador Microchip PIC18F4550

21

Direccionamiento indirecto Existe otro modo de acceso a los registros: el acceso indirecto. Este modo permite acceder a posiciones de memoria sin indicar una dirección fija como parámetro de la instrucción. Para ello hacemos uso de los pares de registros de selección de archivo (FSR), los cuales actúan como punteros a las direcciones de memoria en las que deseamos leer o escribir. En total, es posible direccionar 4K posiciones, luego hacen falta 12 bits para este cometido. Los ocho bits menos significativos están almacenados en el registro FSRxL, mientras que los más significativos son los cuatro bits más bajos del registro FSRxH correspondiente. El PIC18F4550 ofrece 3 pares de FSR, de modo que podemos emplear hasta tres apuntadores independientes para acceso indirecto a memoria. Una vez indicada la dirección en un par FSR, podemos acceder al contenido de esta posición de memoria a través del registro INDF asociado al FSR. Los registros INDFx podemos considerarlos virtuales, ya que no son registros físicos implementados en memoria, a pesar de que están mapeados en ésta. Por ejemplo, un acceso a INDF0 resulta ser un acceso al registro apuntado por FSR0H:FSR0L. Cabe destacar que tanto los FSR como sus INDF están mapeados en el banco de acceso, por lo que no es necesario preocuparse por la selección de banco a la hora de utilizarlos. Además, la dirección de memoria empleada para direccionamiento indirecto se encuentra almacenada completamente en el par FSR, por lo que la gestión de bancos tampoco es necesaria cuando hagamos accesos indirectos a memoria.

3.5.

Juego de instrucciones

El conjunto de instrucciones de los PIC18 está dividido en cuatro categorías básicas: Orientadas a byte. Orientadas a bit. De literales. De control. Orientadas a tabla. La mayoría de instrucciones orientadas a byte tienen tres operandos: 1. Registro objetivo. f

Compilador de C para el microcontrolador Microchip PIC18F4550

22

2. Destino del resultado (WREG o un GPR). d 3. Banco de memoria accedido (datos o acceso). a El registro objetivo f indica sobre qué registro operará la instrucción. El destino d especifica dónde almacenar el resultado de la operación. Si d es cero, el resultado se guarda en WREG. Si d es uno, se almacena en el mismo registro objetivo. El bit de acceso a puesto a cero indica que el registro objetivo pertenece al banco de acceso. Si a vale uno, entonces el registro objetivo corresponde al banco de memoria indicado en BSR. Todas las intrucciones orientadas a bit tiene tres operandos: 1. Registro objetivo. f 2. Bit del registro. b 3. Banco de memoria accedido (datos o acceso). a El operando b indica el bit afectado por la instrucción. Los otros dos parámetros tienen el mismo significado que en las instrucciones orientadas a byte. Las instrucciones de literales pueden tener uno de los siguientes operandos o los dos: Valor literal para cargar en un registro. k Registro FSR donde cargar el valor literal. f Las instrucciones de control pueden tener uno o varios de los siguientes operandos: Dirección de memoria de programa. n Modo de las instrucciones CALL o RETURN. s Las instrucciones orientadas a tabla tan sólo tienen un operando de modo, que indica si se trata de un acceso con pre-incremento, post-incremento, post-decremento o un acceso simple que no modifica el apuntador de tabla. Las instrucciones ensamblador las veremos con una simple tabla-resumen. La mejor fuente para ampliar información, una vez más, es el datasheet del PIC18F4550, que proporciona ejemplos para cada instrucción de las distintas posibilidades de ejecución. La tabla 3.5 muestra todas las instrucciones separadas por formato de instrucción mostrando una pequeña descripción. La tabla 3.5 complementa a ésta, mostrando los ciclos que necesita cada instrucción, los bits del registro STATUS afectados y la palabra genérica de código máquina correspondiente a cada nemotécnico.

Compilador de C para el microcontrolador Microchip PIC18F4550

Nemotécnico

Descripción

Orientadas a byte ADDWF f,d,a ADDWFC f,d,a ANDWF f,d,a CLRF f,a COMF f,d,a CPFSEQ f,a CPFSGT f,a CPFSLT f,a DECF f,d,a DECFSZ f,d,a DCFSNZ f,d,a INCF f,d,a INCFSZ f,d,a INFSNZ f,d,a IORWF f,d,a MOVF f,d,a MOVFF fs,fd MOVWF f,a MULWF f,a NEGF f,a RLCF f,d,a RLNCF f,d,a RRCF f,d,a RRNCF f,d,a SETF f,a SUBFWB f,d,a SUBWF f,d,a SUBWFB f,d,a SWAPF f,d,a TSTFSZ f,a XORWF f,d,a

Suma de WREG y f Suma con acarreo de WREG y f And lógico de WREG y f Puesta a 0 de f Complemento a 1 de f Compara f y WREG, salta si igual Compara f y WREG, salta si mayor Compara f y WREG, salta si menor Decrementa f Decrementa f, salta si cero Decrementa f, salta si no cero Incrementa f Incrementa f, salta si cero Incrementa f, salta si no cero Or lógico de WREG y f Carga f en WREG Mueve de fs a fd Almacena WREG en f Multiplica WREG con f Complemento a 2 de f Rotación a la izquierda con acarreo Rotación a la izquierda sin acarreo Rotación a la derecha con acarreo Rotación a la derecha sin acarreo Puesta a 0xFFh de f Resta con acarreo WREG menos f Resta sin acarreo f menos WREG Resta con acarreo f menos WREG Intercambia los nibbles de f Comprueba f, salta si cero Xor lógico de WREG y f

Cuadro 3.3: Resumen del conjunto de instrucciones del microcontrolador PIC18F4550.

23

Compilador de C para el microcontrolador Microchip PIC18F4550

Orientadas a bit BCF f,b,a BSF f,b,a BTFSC f,b,a BTFSS f,b,a BTG f,d,a

Limpia un bit de f Activa un bit de f Comprueba un bit de f, salta si es 0 Comprueba un bit de f, salta si es 1 Conmuta un bit de f

De literales ADDLW k ANDLW k IORLW k LFSR f,k MOVLB k MOVLW k MULLW k SUBLW k XORLW k

Suma k a WREG And lógico de k y WREG Or lógico de k y WREG Carga k en el FSR indicado por F Carga k en el BSR Carga k en el WREG Multiplica k con WREG Resta k menos WREG Xor lógico de WREG y k

Cuadro 3.3: Resumen del conjunto de instrucciones del microcontrolador PIC18F4550.

24

Compilador de C para el microcontrolador Microchip PIC18F4550

De control BRA n BC n BZ n BOV n BN n BNC n BNN n BNOV n BNZ n CALL n,s CLRWDT DAW GOTO n NOP POP PUSH RCALL n RESET RETFIE s RETLW k RETURN s SLEEP

Salto relativo incondicional Salto relativo si acarreo Salto relativo si cero Salto relativo si desbordamiento Salto relativo si negativo Salto relativo si no acarreo Salto relativo si no negativo Salto relativo si no desbordamiento Salto relativo si no cero Llamada a subrutina Puesta a 0 del temporizador del WDT Ajuste decimal de WREG Salto absoluto No operation Extrae de la pila y carga en PC Introduce en la pila el valor de PC Llamada a subrutina, relativa a PC Reset por software del dispositivo Fin de ISR Fin de subrutina y carga k en WREG Fin de subrutina Pasa el dispositivo al modo de reposo

Cuadro 3.3: Resumen del conjunto de instrucciones del microcontrolador PIC18F4550.

25

Compilador de C para el microcontrolador Microchip PIC18F4550

Orientadas a tabla TBLRD* TBLRD*+ TBLRD*TBLRD+* TBLWT* TBLWT*+ TBLWT*TBLWT+*

Lectura de tabla Lectura con post-incremento Lectura con post-decremento Lectura pre-incremento Escritura de tabla Escritura con post-incremento Escritura con post-decremento Escritura con pre-incremento

Cuadro 3.3: Resumen del conjunto de instrucciones del microcontrolador PIC18F4550.

26

Compilador de C para el microcontrolador Microchip PIC18F4550

Nemotécnico

Ciclos Bis de STATUS afectados

Palabra de instrucción

Orientadas a byte ADDWF f,d,a ADDWFC f,d,a ANDWF f,d,a CLRF f,a COMF f,d,a CPFSEQ f,a CPFSGT f,a CPFSLT f,a DECF f,d,a DECFSZ f,d,a DCFSNZ f,d,a INCF f,d,a INCFSZ f,d,a INFSNZ f,d,a IORWF f,d,a MOVF f,d,a

1 1 1 1 1 1-3 1-3 1-3 1 1-3 1-3 1 1-3 1-3 1 1

MOVFF fs,fd

2

MOVWF f,a MULWF f,a NEGF f,a RLCF f,d,a RLNCF f,d,a RRCF f,d,a RRNCF f,d,a SETF f,a SUBFWB f,d,a SUBWF f,d,a SUBWFB f,d,a SWAPF f,d,a TSTFSZ f,a XORWF f,d,a

1 1 1 1 1 1 1 1 1 1 1 1 1-3 1

C,DC,Z,OV,N C,DC,Z,OV,N Z,N Z Z,N

C,DC,Z,OV,N

C,DC,Z,OV,N

Z,N Z,N

C,DC,Z,OV,N C,Z,N Z,N C,Z,N Z,N C,DC,Z,OV,N C,DC,Z,OV,N C,DC,Z,OV,N

Z,N

0010 01da ffff ffff 0010 00da ffff ffff 0001 01da ffff ffff 0110 101a ffff ffff 0001 11da ffff ffff 0110 001a ffff ffff 0110 010a ffff ffff 0110 000a ffff ffff 0000 01da ffff ffff 0010 11da ffff ffff 0100 11da ffff ffff 0010 10da ffff ffff 0011 11da ffff ffff 0100 10da ffff ffff 0001 00da ffff ffff 0101 00da ffff ffff 1100 ffff ffff ffff 1111 ffff ffff ffff 0110 111a ffff ffff 0000 001a ffff ffff 0110 110a ffff ffff 0011 01da ffff ffff 0100 01da ffff ffff 0011 00da ffff ffff 0100 00da ffff ffff 0110 100a ffff ffff 0101 01da ffff ffff 0101 11da ffff ffff 0101 10da ffff ffff 0011 10da ffff ffff 0110 011a ffff ffff 0001 10da ffff ffff

Cuadro 3.4: Instrucciones del microcontrolador PIC18F4550: ciclos, bits de STATUS afectados y palabra de instrucción.

27

Compilador de C para el microcontrolador Microchip PIC18F4550

Orientadas a bit BCF f,b,a BSF f,b,a BTFSC f,b,a BTFSS f,b,a BTG f,d,a

1 1 1-3 1-3 1

1001 bbba ffff ffff 1000 bbba ffff ffff 1011 bbba ffff ffff 1010 bbba ffff ffff 0111 bbba ffff ffff

De literales ADDLW k ANDLW k IORLW k

1 1 1

LFSR f,k

2

MOVLB k MOVLW k MULLW k SUBLW k XORLW k

1 1 1 1 1

C,DC,Z,OV,N Z,N Z,N

C,DC,Z,OV,N Z,N

0000 1111 kkkk kkkk 0000 1011 kkkk kkkk 0000 1001 kkkk kkkk 1110 1110 00ff kkkk 1111 0000 kkkk kkkk 0000 0001 0000 kkkk 0000 1110 kkkk kkkk 0000 1101 kkkk kkkk 0000 1000 kkkk kkkk 0000 1010 kkkk kkkk

Cuadro 3.4: Instrucciones del microcontrolador PIC18F4550: ciclos, bits de STATUS afectados y palabra de instrucción.

28

Compilador de C para el microcontrolador Microchip PIC18F4550

De control BRA n BC n BZ n BOV n BN n BNC n BNN n BNOV n BNZ n

2 1-2 1-2 1-2 1-2 1-2 1-2 1-2 1-2

CALL n,s

2

CLRWDT DAW

1 1

GOTO n

2

NOP NOP POP PUSH RCALL n RESET RETFIE s RETLW k RETURN s SLEEP

1 1 1 1 2 1 2 2 2 1

TO,PD C

Todos GIE/GIEH, PEIE/GIEL

TO,PD

1101 0nnn nnnn nnnn 1110 0010 nnnn nnnn 1110 0000 nnnn nnnn 1110 0100 nnnn nnnn 1110 0110 nnnn nnnn 1110 0011 nnnn nnnn 1110 0111 nnnn nnnn 1110 0101 nnnn nnnn 1110 0001 nnnn nnnn 1110 110s kkkk kkkk 1111 kkkk kkkk kkkk 0000 0000 0000 0100 0000 0000 0000 0111 1110 1111 kkkk kkkk 1111 kkkk kkkk kkkk 0000 0000 0000 0000 1111 xxxx xxxx xxxx 0000 0000 0000 0110 0000 0000 0000 0101 1101 1nnn nnnn nnnn 0000 0000 1111 1111 0000 0000 0001 000s 0000 1100 kkkk kkkk 0000 0000 0001 001s 0000 0000 0000 0011

Cuadro 3.4: Instrucciones del microcontrolador PIC18F4550: ciclos, bits de STATUS afectados y palabra de instrucción.

29

Compilador de C para el microcontrolador Microchip PIC18F4550

30

Orientadas a tabla TBLRD* TBLRD*+ TBLRD*TBLRD+* TBLWT* TBLWT*+ TBLWT*TBLWT+*

2 2 2 2 2 2 2 2

0000 0000 0000 1000 0000 0000 0000 1001 0000 0000 0000 1010 0000 0000 0000 1011 0000 0000 0000 1100 0000 0000 0000 1101 0000 0000 0000 1110 0000 0000 0000 1111

Cuadro 3.4: Instrucciones del microcontrolador PIC18F4550: ciclos, bits de STATUS afectados y palabra de instrucción.

3.6.

Periféricos

A continuación estudiaremos de forma resumida los distintos periféricos que podemos encontrar en un PIC. Debido a la gran variedad de dispositivos integrados que llegan a formar parte de un microcontrolador PIC y las diferencias existentes en un dispositivo entre los distintos modelos, nos centramos en los dispositivos contenidos en el microcontrolador 18F4550.

3.6.1.

Puertos de Entrada/Salida

El puerto de E/S es el dispositivo más sencillo de los existentes en un microcontrolador y también es el más utilizado. Los PIC ofrecen puertos de E/S de hasta 8 bits de tamaño. El 18F4550 dispone de 5 puertos de E/S, etiquetados con las letras de la A hasta la E, de los cuales B y D son puertos de 8 bits, A y C son de 7 bits, mientras que el puerto E tiene un ancho de 4 bits. Cada puerto dispone de tres registros que permiten su manejo. El registro TRISx (donde x es la letra que identifica a cada puerto) indica el sentido (entrada o salida) de cada pin, de modo que un bit puesto a 1 indica que el pin correspondiente es de entrada, mientras que un 0 indica que dicho pin es de salida. Por tanto, para emplear como pines de entrada los dos menos significativos del puerto B y como salida el resto de pines del mismo puerto basta con cargar en TRISB el valor binario

Compilador de C para el microcontrolador Microchip PIC18F4550

31

00000011. Una vez configurada la dirección, la lectura y escritura de un puerto de E/S resulta tan sencilla como leer o escribir en el registro PORTx correspondiente. El tercer registro asociado a un puerto, LATx, hace las funciones de latch de salida. Cuando efectuamos una operación de escritura sobre PORTx, en realidad el registro escrito es LATx. Sin embargo, una operación de lectura sobre PORTx no efectúa una lectura del registro LATx, sino que produce la carga del dato de entrada en otro latch (destinado únicamente a datos entrantes). Acto seguido, el microcontrolador almacena el valor de este latch de entrada en el registro destino de la instrucción. Gracias a esta combinación de latches para entrada y salida es posible obtener el valor que sale del puerto sin necesidad de circuitería extra ni de un puerto de entrada adicional. Un ejemplo que aprovecha esta configuración es el par de instrucciones BSF/BCF. Estas instrucciones cambian el valor de un bit almacenado en un registro cualquiera del microcontrolador. En el caso de un puerto de E/S, primero leen el valor del registro (LATx en este caso), activan o desactivan el bit indicado y, por último, almacenan el nuevo valor en el latch de salida del puerto en cuestión (LATx). Un detalle muy a tener en cuenta es que si un puerto de entrada, que haya leído un valor en algún momento, cambia de modo y pasa a funcionar en modo de salida, el valor que tendrá será el almacenado en LATx (latch de salida de PORTx), no el que había en latch de entrada de PORTx. Otro punto a tener en cuenta es que debido al coste que suponen las patillas de comunicación en los PIC y, en general, en cualquier dispositivo integrado, los puertos de E/S están multiplexados con otros dispositivos. Cuando uno de estos dispositivos esté activado, los pines compartidos con el puerto de E/S no podrán usarse con tal propósito, con lo que el puerto tendrá un tamaño efectivo menor. Un ejemplo de esto es el puerto A, cuyo pin 0 está multiplexado con el canal 0 del conversor analógico-digital. Cuando esté activo el módulo de conversión A/D, habrá que tener cuidado de no modificar los registros TRISA, TRISB y TRISE, ya que afectan a los pines que emplea el conversor y podría resultar en errores de conversión. Por último, hay que recordar que la escritura en los registros ocurre al final del ciclo de instrucción, mientras que la lectura se produce al principio. Debido a esto, puede darse el caso de que el valor a la salida de un puerto no esté estabilizado antes del próximo acceso. Por este motivo, Microchip recomienda intercalar entre dos operaciones consecutivas sobre un puerto concreto una instrucción que no haga uso del mismo, para tener la seguridad de que los valores del puerto se han estabilizado antes de un acceso de lectura.

Compilador de C para el microcontrolador Microchip PIC18F4550

3.6.2.

32

Universal Serial Bus

En la actualidad, uno de los interfaces de comunicación con periféricos más utilizados es el USB. El PIC18F4550 incorpora un motor de interface serie (SIE) que soporta el estándar USB 2.0 en modos de baja y alta velocidad. El SIE puede canalizarse directamente a dispositivos USB haciendo uso de su transceptor interno o conectarse a un transceptor externo, proporcionando una notable flexibilidad a la hora de diseñar del hardware. El subsistema USB del 18F4550 también incorpora un regulador de tensión de 3.3V con las resistencias de polarización exigidas por la especificación USB 2.0. Además, ofrece la posibilidad de usar polarización, e incluso alimentación externa, con el transceptor interno. El control del subsistema USB recae sobre un total de 22 registros: UCON; registro de control. UCFG; registro de configuración. USTAT; registro de estado de transferencia. UADDR; registro de dirección del dispositivo. UFRMH y UFRML; registros de número de marco. UEP0 a UEP15; registros de control de los terminadores. El medio de intercambio de datos entre el controlador y el SIE es una memoria de 1Kb de tamaño, denominada USB RAM. Se trata de una memoria de 1Kb de puerto doble, es decir, una memoria que permite accesos simultáneos desde dos dispositivos independientes, cada uno con sus propios buses de direcciones, datos y señales de control. El puerto correspondiente al controlador está mapeado en los bancos 4 a 7 de la memoria de datos.

3.6.3.

SPP

Este modelo de PIC incorpora un puerto paralelo orientado a flujos de datos, en inglés Streaming Parallel Port (SPP), pensado para utilizarse como interface de alta velocidad para intercambiar grandes cantidades de datos con un sistema externo. El SPP opera como puerto paralelo maestro completo, con señales de control y de reloj para controlar el dispositivo esclavo conectado a él. Es posible configurarlo de modo que el control del SPP recaiga sobre la CPU del PIC o sobre el controlador USB descrito en la sección anterior.

Compilador de C para el microcontrolador Microchip PIC18F4550

33

Los registros empleados para configurar el SPP son SPPCON, el cual controla el modo de operación y determina si el control recae sobre la CPU o sobre el SIE, y el SPPCFG, que se encarga de la configuración de temporización y de habilitar las señales de control hacia el exterior. A estos se le suman los registros TRISD, TRISE y TRISB, que indican en qué modo operarán los pines correspondientes a señales de datos del SPP. Cuando el SPP está configurado para que sea la CPU quien lo maneje, el registro SPPEPS es el que proporciona información de estado y control sobre el puerto, mientras que SPPDATA es el registro objetivo de la lectura/escritura de datos transmitidos a través del puerto paralelo.

3.6.4.

MSSP

El puerto serie síncrono maestro (MSSP) es un interface serie que resulta útil para comunicar el PIC con periféricos externos o con otros microcontroladores. Tiene dos modos de funcionamiento diferentes: SPI (interface de periférico serie) o como I2 C. Los registros empleados para controlar el MSSP son SSPSTAT, SSPCON y SSPCON2, el primero de los tres proporciona información sobre el estado del módulo y los dos restantes cumplen las funciones de control. Los registros encargados de albergar los datos a enviar o recibir son SSPSR y SSPBUF, mientras que el registro SSPADD es el responsable de operaciones de direccionamiento cuando el módulo está configurado en modo I2 C. Para conocer los detalles de su uso es conveniente acudir a la sección correspondiente del datasheet del microcontrolador. Además, la web de Microchip contiene diversos ejemplos de aplicación con cada uno de los modos de este módulo.

3.6.5.

EUSART

Además del MSSP, este PIC ofrece un segundo módulo para comunicaciones serie: el transmisor/receptor síncrono/asíncrono universal mejorado (EUSART). Puede funcionar en modos half-duplex y full-duplex, y además incorpora características extra sobre los USART convencionales, como detección y ajuste automáticos de la tasa de transferencia. El control del módulo EUSART recae sobre tres registros: TXSTA (control y estado de la transmisión), RCSTA (control y estado de la recepción) y BAUDCON (control de la tasa de transferencia). El par de registros SPBRGH y SPBRG controlan el período empleado por temporizador del generador de símbolos del EUSART. Una vez que el módulo está configurado para un modo de funcionamiento, el envío

Compilador de C para el microcontrolador Microchip PIC18F4550

34

comienza con la escritura del dato a enviar en el registro TXREG, mientras que una recepción con éxito termina con el dato recibido en RCREG. El proceso para transmitir un dato consta de los siguientes pasos: 1. Inicializar SPBRGH:SPBRG con el valor correspondiente a los baudios deseados para la frecuencia actual del oscilador. 2. Configurar el módulo EUSART como síncrono o asíncrono según la comunicación. 3. Habilitar la transmisión activando el bit TXEN de TXSTA. 4. Cargar TXREG con el valor que se desea transmitir. Esto hace que comience la transmisión. 5. Comprobar el bit TRMT de TXSTA. Cuando esté puesto a uno, la transmisión habrá finalizado. De manera análoga, los pasos a seguir para recepción son: 1. Inicializar SPBRGH:SPBRG con el valor correspondiente a los baudios deseados para la frecuencia actual del oscilador. 2. Configurar el módulo EUSART como síncrono o asíncrono según la comunicación. 3. Habilitar la recepción activando el bit SREN de RCSTA. 4. Comprobar el bit RCIF de PIR1. Cuando esté puesto a uno, la recepción habrá finalizado y el registro RCREG contendrá el dato recibido. El valor que deberá contener el par de registros SPBRGH:SPBRG establece el período del generador de símbolos, produciendo una velocidad en baudios diferente según la frecuencia de oscilador empleada. Cada extremo de la línea de comunicaciones serie debe emplear la misma configuración, de forma que transmisor y receptor funcionen a la misma velocidad y en el mismo modo para que sea posible establecer con éxito la comunicación entre ambos. Para determinar el valor de SPBRGH:SPBRG hace falta conocer la velocidad en baudios a la que debe funcionar el EUSART. Una vez conocido este detalle, el valor de los registros SPBFGH:SPBRG se calcula a partir de una de las fórmulas siguientes, según el modo de funcionamiento en que vaya a trabajar el módulo EUSART: Como se deduce de la tabla anterior, el valor de SPBRGH:SPBRG depende también del modo empleado para la comunicación. Cabe destacar que la configuración de la comunicación serie debe ser la misma en ambos extremos de la línea.

Compilador de C para el microcontrolador Microchip PIC18F4550

35

Bits de configuración Modo SPBRGH:SPBRG Baudios Sync BRG16 BRGH 0 0 0 8-bit asíncrono (Fosc/(64*Baud))-1 Fosc/(64*(n+1)) 0 0 1 8-bit asíncrono (Fosc/(16*Baud))-1 Fosc/(16*(n+1)) 0 1 0 16-bit asíncrono 0 1 1 16-bit asíncrono 1 0 x 8-bit síncrono (Fosc/(4*Baud))-1 Fosc/(4*(n+1)) 1 1 x 16-bit síncrono Cuadro 3.5: Tabla de configuración del EUSART del microcontrolador PIC18F4550.

Debido al redondeo al entero más próximo, aparece un porcentaje de error respecto a la tasa de transferencia teórica deseada. Una práctica muy recomendable es realizar la operación contraria, tomando el valor de SPBRGH:SPBRG como dato de partida y la tasa de baudios como incógnita, para calcular el error introducido por el redondeo. Un error demasiado grande producirá una desincronización entre emisor y receptor, e imposibilitará la comunicación entre ellos. El registro TXSTA es el encargado del control del modo de transmisión. El bit CSRC indica qué fuente de reloj empleará el EUSART para comunicación síncrona. TX9 permite seleccionar entre palabras de 8 o 9 bits. TXEN es el bit que habilita la transmisión. SYNC, como indica su nombre, es el bit de selección entre modo síncrono o asíncrono; este bit también afecta al modo de recepción. SENDB permite forzar el envío, en comunicaciones asíncronas, de un caracter de sincronización (Sync Break). El control de la tasa de baudios (alta o baja velocidad) recae en el bit BRGH. TRMT es un bit de estado, no de control; indica cuándo está vacío el buffer de salida. Por último, el bit TX9D del registro TXSTA alberga el noveno bit de la palabra a enviar cuando el EUSART está configurado para enviar palabras de 9 bits. Por otro lado, el registro RCSTA controla la recepción y da información sobre la misma. RX9 indica el tamaño de palabra, 8 o 9 bits. El bit SREN habilita la recepción simple en modo síncrono, mientras que CREN habilita la recepción múltiple, también llamada continua. El bit de FERR indica si hubo un error de marco (frame) en la recepción, mientras que OERR avisa de que ha habido un error de desbordamiento. RX9D es el bit análogo a TX9D: almacena el noveno bit de la palabra recibida, si el EUSART está trabajando con palabras de 9 bits.

3.6.6.

Memoria EEPROM

El 18F4550 dispone de una pequeña memoria EEPROM a la que es posible acceder, tanto para lectura como escritura, desde el programa en ejecución. Su tamaño

Compilador de C para el microcontrolador Microchip PIC18F4550

36

es reducido, sólo 256 bytes. Esta memoria no está mapeada directamente sobre la memoria de datos; la única manera de acceder a ella es mediante direccionamiento indirecto, a través de varios SFR. EEADR es el registro de direccionamiento para accesos a la EEPROM. Complementando a éste se encuentra el registro EEDATA; en él aparecerá el dato leído de la EEPROM tras una operación de lectura y es el registro que aloja el dato a cargar en EEPROM con la próxima operación de escritura. Aparte de estos dos registros, de direccionamiento y datos, respectivamente, hay dos registros más, que cumplen las funciones de control de acceso: EECON1 y EECON2. EECON1 es un registro de control convencional, con varios bits que cumplen distintos cometidos; es responsable del control de acceso a las memorias EEPROM, flash de programa y a los bits de configuración del PIC. El bit EEPGD selecciona a memoria accederán las operaciones de lectura/escritura: flash de programa (uno) o EEPROM de datos (cero). EECFGS determina si los accesos serán a los registros de configuración o a alguna de las dos memorias antes mencionadas. WRERR indica si la última operación de escritura terminó con error. WREN es el bit que habilita la operación de escritura. La operación de lectura comienza con la carga de un uno en el bit RD del registro EECON1; una vez completada la operación, el hardware del microcontrolador carga de manera automática un cero en este bit. La operación de escritura tiene un comportamiento análogo con el bit WR. El registro EECON2 no es un registro físico. Su función se reduce a hacer de seguro antes de una operación de escritura o borrado. La única manera de efectuar una escritura es escribir el valor 0x55h en EECON2, acto seguido escribir 0xAAh en este mismo registro, y, una vez hecho esto, poner a cero el bit WR de EECON1. En caso de no seguir exactamente esta secuencia, el PIC no ejecutará la operación de escritura. Por supuesto, el bit WREN debe estar puesto a uno antes de efectuar estos pasos. Como precaución adicional, es muy recomendable tener deshabilitadas las interrupciones durante la ejecución de este fragmento de código. A continuación, un ejemplo de cómo efectuar una lectura de la EEPROM: ; Cargar EEADR con la dirección deseada. MOVLW dirección MOVWF EEADR ; Seleccionar memoria EEPROM de datos. BCF EECON1, EEPGD ; Activar acceso a memoria especial.

Compilador de C para el microcontrolador Microchip PIC18F4550 BCF

37

EECON1, CFGS

; Leer de la EEPROM. BSF EECON1, RD ; Cargar en W el dato leído. MOVF EEDATA, W El código anterior resulta sumamente sencillo. Comienza con la carga en el registro EEADR de la dirección de memoria EEPROM a la que acceder en esta operación de lectura. Acto seguido, pone a cero el bit EECFGS del registro de control EECON1, con lo que indica al PIC que el próximo acceso a memoria especial será a EEPROM o flash. Lo siguiente es seleccionar la memoria EERPOM, que se consigue poniendo a cero el bit EEPGD. Por último, inicia la operación de lectura mediante la puesta a uno del bit RD. Este fragmento de código concluye con la carga en WREG del dato leído de la EEPROM, el cual aparece alojado en EEDATA tras una lectura exitosa. Un ejemplo práctico de código que efectúa una operación de escritura:

noWR

; Mientras no haya terminado la posible ; escritura en proceso, hacer espera activa. BTFSC EECON1, WR GOTO noWR

; Cargar EEADR con la dirección deseada. MOVLW dirección MOVWF EEADR ; Cargar EEDATA con el dato a almacenar. MOVLW valor MOVWF EEDATA ; Seleccionar EEPROM de datos. BCF EECON1, EEPGD ; Activar acceso a memoria especial. BCF EECON1, CFGS ; Habilitar las operaciones de escritura.

Compilador de C para el microcontrolador Microchip PIC18F4550 BSF

38

EECON1, WREN

; Deshabilitar las interrupciones. BCF INTCON, GIE

; Paso 1: EECON2 MOVLW 0x55 MOVWF EECON2 ; Paso 2: EECON2 MOVLW 0xAA MOVWF EECON2 ; Escribir en la BSF EECON1,

> y; Este ejemplo se traduce en el siguiente código: movf F_REG+.16, W bra _L3 _L2 bcf STATUS, C rrcf F_REG+.20+1, F rrcf F_REG+.20, F addlw 0xFF _L3 btfss STATUS, Z bra _L2 Como podemos ver, la única diferencia con el desplazamiento de valores con signo es que en el cuerpo del bucle no se efectúa la comprobación del bit 7 del MSB. En los desplazamientos hacia la izquierda no existe ninguna diferencia entre valores con o sin signo, ya que en ambos casos entra un cero por la derecha. El código resultante es idéntico al desplazamiento hacia la derecha de valores sin signo, tan sólo cambiando las instrucciones rrcf por rlcf.

7.4.

Conversión de tipos o casting

7.4.1.

Extensión de signo

Normalmente, cuando queremos usar datos de mayor rango necesitamos usar tipos nuevos. Por ejemplo, si queremos calcular un número por encima del valor máximo de los enteros con signo, 32767, necesitaremos utilizar un tipo que lo permita, como long (entero largo de 4 bytes), cuyo valor máximo es 2147483647. Para convertir de uno al otro, en C hacemos uso de lo que se denomina casting de tipos. int x = 10; long y = x;

Compilador de C para el microcontrolador Microchip PIC18F4550

116

Para esta sencilla operación, que básicamente consiste en cargar en la variable y el valor de x, necesitamos rellenar los bytes de la variable y, de cuatro bytes, que no quedan cubiertos por la variable x, ya que ésta es de sólo dos bytes. GCC utiliza la operación de extensión de signo, necesaria entre todos los tipos a los que se quiera tener la posibilidad de ampliación. El código generado para este ejemplo es: movff Frame_pointer+1, FSR0H movff Frame_pointer, FSR0L movff INDF0, F_REG+.18 infsnz FSR0L, F incf FSR0H, F movff INDF0, F_REG+.18+1 movlw btfsc movlw movwf movwf

0x00 F_REG+.18+1, 7 0xff F_REG+.18+2 F_REG+.18+3

Como vemos, el primer bloque del resultado se encarga de cargar en registros el valor de la variable de entrada, de sólo dos bytes. Sin embargo, la variable de salida, la asignada, es de cuatro bytes. El valor de los dos bytes más significativos dependerá del bit de signo de la variable de entrada. El cometido del segundo bloque es comprobar el valor de dicho bit y extenderlo por los dos bytes adicionales que conforman la variable asignada.

7.4.2.

Extensión de ceros

La extensión de valores sin signo se denomina, dentro del proyecto GCC, zeroextension que podemos traducirla como extensión de ceros. El algoritmo de la extensión de ceros es tan sencillo como poner a cero los bytes de la variable asignada que no corresponden con la variable de entrada. unsigned int x = 10; long y = x; El resultado de la zero-extension anterior es: movff Frame_pointer+1, FSR0H movff Frame_pointer, FSR0L

Compilador de C para el microcontrolador Microchip PIC18F4550

117

movff INDF0, F_REG+.18 infsnz FSR0L, F incf FSR0H, F movff INDF0, F_REG+.18+1 clrf F_REG+.18+2 clrf F_REG+.18+3 Igual que ocurre con la extensión de signo, para la zero-extension primero se carga en registros el valor de entrada. A continuación, el segundo bloque de código se limita a poner a cero los registros asignados a los bytes de más peso de la variable de salida. Dada la limitación de memoria de un PIC, lo más aconsejable es trabajar siempre con el tipo de dato más pequeño que permita el rango de valores necesario para cada propósito; es un desperdicio de memoria, por ejemplo, emplear una variable de tipo long para un dato leído del conversor analógico-digital configurado a 8 bits, cuando el tipo short permite almacenar todos los valores de ese rango. Lo aconsejable es mantener el dato en una variable del tipo más pequeño que pueda albergarlo hasta que sea necesario, por el motivo que sea, un mayor rango numérico.

7.5.

Operaciones matemáticas complejas

7.5.1.

Multiplicación

Durante la etapa de diseño tomamos la decisión de implementar una solución combinada para la multiplicación: para datos pequeños haremos uso del multiplicador hardware, por lo que el código resultante es análogo al producido para la suma y la resta. Para datos grandes optaremos por hacer una llamada a la biblioteca de funciones, de modo que el tamaño de nuestro programa no creciese más de lo estrictamente necesario. Veamos un ejemplo de multiplicación de datos grandes. long x; int y = 10; int z = 25; x = y * z; Esto se traduce en lo siguiente:

Compilador de C para el microcontrolador Microchip PIC18F4550

118

movff F_REG+.18+1, F_REG+.26+1 movff F_REG+.18, F_REG+.26 movff F_REG+.16+1, F_REG+.28+1 movff F_REG+.16, F_REG+.28 call _mulhisi movff movff movff movff

F_REG+.28, F_REG+.16 F_REG+.28+1, F_REG+.16+1 F_REG+.28+2, F_REG+.16+2 F_REG+.28+3, F_REG+.16+3

Las funciones matemáticas tienen un comportamiento particular respecto a las funciones definidas por el usuario en lo que se refiere al paso de argumentos, pero eso lo veremos más adelante. Ahora centrémonos en las funciones matemáticas de nuestra biblioteca. El paso de argumentos y el retorno del resultado es específico para cada función de la biblioteca, y lo mismo sucede con las variables que emplea cada una de estas funciones. GCC no decide sobre la marcha qué registros servirán para cada propósito dentro de estas funciones, esta decisión es fija en el código de la biblioteca y GCC sabe cómo comunicarse con estas funciones, sin interferir en el buen funcionamiento de éstas y evitando que dichas funciones hagan lo propio con el código que sí genera el compilador. En el código resultante del ejemplo que estamos tratando distinguimos cuatro bloques. Los dos primeros cargan en los registros 26 y 27 uno de los operandos y en los registros 28 y 29 el segundo operando. Esto es así porque la función mulhisi exige que los operandos estén almacenados en esos registros. Al finalizar su ejecución (la llamada a mulhisi) encontraremos el resultado de la multiplicación en los registros 28 a 31 (la elección de estos registros no la efectúa GCC de manera dinámica, sino que están predefinidos en el cuerpo de esta función). El cuarto bloque carga en los registros asignados a la variable x el resultado de multiplicar y por z.

7.5.2.

División

Veamos ahora un ejemplo de la operación de división. int x; int y = 10;

Compilador de C para el microcontrolador Microchip PIC18F4550

119

int z = 25; x = y / z; El resultado de traducir la división de dos números enteros de 2 bytes es un código como éste: movff F_REG+.18+1, F_REG+.30+1 movff F_REG+.18, F_REG+.30 movff F_REG+.16+1, F_REG+.28+1 movff F_REG+.16, F_REG+.28 call _divmodhi movff F_REG+.30, F_REG+.16 movff F_REG+.30+1, F_REG+.16+1 Encontramos el mismo esqueleto que para la operación de multiplicación: carga de un operando, carga del otro, llamada a la función de la biblioteca y recuperación del resultado deseado. Las funciones de división, además de las particularidades que tienen por tratarse de código ensamblador escrito a mano, son peculiares porque en realidad tienen un doble cometido: obtener cociente y resto de una operación de división. Una única llamada a una de estas funciones, como divmodhi, deja el valor del cociente resultante en unos registros y el resto en otros, por lo que las sentencias en C de división y módulo resultan en un código ensamblador prácticamente idéntico, con la diferencia de que, tras la llamada a función, cogerán el resultado de unos registros u otros.

7.6.

Estructuras condicionales o de selección

7.6.1.

if

Empezamos a examinar el código generado para las estructuras de control. La más sencilla es la estructura de ejecución condicional if. En este salto, la expresión del if es evaluada para decidir si se ejecutará el bloque then siguiente o no. La implementación de las estructuras if tiene dos partes: comparación y salto. Cuando la condición de la estructura if es una comparación entre dos datos de un

Compilador de C para el microcontrolador Microchip PIC18F4550

120

byte, el código ensamblador en el que se traduce tiene bien diferenciados los bloques de comparación y de salto. Sin embargo, cuando se trata de comparar, por ejemplo, dos valores de tipo int, ambas partes se mezclan dando lugar a un pequeño combinado de comparaciones, pequeños saltos y un gran salto, que es el que marca el final de la comparación de la estructura if. Lo vemos con un ejemplo de comparación entre dos valores de tipo int. if (x != y) { ... } Esto se traduce en el código siguiente:

_L3

movf F_REG+.16, W xorwf F_REG+.18, W btfss STATUS, Z goto _L3 movf F_REG+.16+1, W xorwf F_REG+.18+1, W btfsc STATUS, Z goto _L1 ...

_L1 Los puntos suspensivos representan el bloque then, cuyo contenido es irrelevante para nuestra explicación. No obstante, para mostrar dónde aparece el código correspondiente al bloque then, también los hemos incluído en el ensamblador generado por GCC. En este ejemplo sólo se ejecutará el bloque then si las dos variables evaluadas son diferentes. GCC genera una comparación de igualdad y un salto a la etiqueta L1 en caso afirmativo, es decir, salta si no se cumple la condición. Las dos primeras líneas comparan, mediante la instrucción de exclusión lógica, los bytes menos significativos que componen las variables implicadas y si el resultado es cero (bit Z del registro de estado puesto a uno), es que ambos son iguales y hay que comparar los bytes más significativos; en caso contrario los valores son distintos (no hay necesidad de comparar los otros dos bytes) y salta a la etiqueta L3, donde se efectúa la comparación final y evita, o no, la ejecución del bloque then. En

Compilador de C para el microcontrolador Microchip PIC18F4550

121

la comparación de los bytes altos, tras ejecutar la instrucción de exclusión lógica, comprueba otra vez si el bit Z está puesto a cero y, en caso de que fuese así (porque los valores sean iguales), ejecutaría el salto incondicional goto hacia la etiqueta L1, lo que significa no ejecutar el bloque then. Por analogía, para el caso de que la comparación fuese de igualdad, el código resultante sería exactamente igual en forma y tan sólo cambiaría la última comprobación, que pasaría a efectuar el salto incondicional si el bit Z no está puesto a cero. btfss STATUS, Z Existen otras ocho posibles comparaciones entre dos valores aparte de las dos que acabamos de analizar. En realidad son sólo cuatro, pero hay que distinguir entre comparaciones de valores con signo y sin él. Estas comparaciones son mayor-que, mayor-o-igual-que, menor-que y menor-o-igual-que. La implementación de estas comparaciones se apoyan en la instrucción de resta. El procedimiento es tan sencillo como restar los operandos y comprobar el signo del resultado. if (x y) movf F_REG+.18, W subwf F_REG+.16, W movf F_REG+.18+1, W btfss STATUS, C incfsz F_REG+.18+1, W subwf F_REG+.16+1, W andlw 0x80 btfsc STATUS, Z goto _L1 ; (x >= y) movf F_REG+.16, W subwf F_REG+.18, W

Compilador de C para el microcontrolador Microchip PIC18F4550

123

movf F_REG+.16+1, W btfss STATUS, C incfsz F_REG+.16+1, W subwf F_REG+.18+1, W andlw 0x80 btfss STATUS, Z goto _L1 ; (x default goto _L2 _L8: movlw low D’100’ xorwf F_REG+.16, W btfss STATUS, Z goto _L18 movlw high D’100’ xorwf F_REG+.16+1, W

; x == ’d’?

_L18 btfsc STATUS, Z goto _L13 movlw low D’101’ xorwf F_REG+.16, W btfss STATUS, Z goto _L19 movlw high D’101’ xorwf F_REG+.16+1, W

; x == ’e’?

_L19 btfsc STATUS, Z goto _L14 _L2: ... goto _L1

; Bloque Default

... goto _L1

; Bloque C

... goto _L1

; Bloque A

... goto _L1

; Bloque B

...

; Bloque D

_L10:

_L11:

_L12:

_L13:

126

Compilador de C para el microcontrolador Microchip PIC18F4550

127

goto _L1 _L14: ... nop

; Bloque E

_L1: El código resultante es notablemente más extenso que el de un if simple, debido a que no deja de ser una sucesión de estructuras if-else. A pesar de su tamaño, es sencillo de entender; más aún con los comentarios añadidos para el ejemplo. GCC no efectúa las comparaciones en el orden en que aparecen los case en el código C original, sino que opta por ordenar los valores especificados en tiempo de compilación, y genera el código ensamblador correspondiente a una búsqueda binaria. Comenzando con la primera comparación, si la expresión, cuyo valor está previamente alojado en los registros 16 y 17, es igual que el valor comparado (99 en nuestro ejemplo, correspondiente al carácter c en código ASCII), ejecuta el salto a la etiqueta L10, que marca el comienzo del código ensamblador correspondiente al bloque C. En caso de no satisfacerse la igualdad, comprueba si la expresión es menor o mayor; y, según sea, desciende por una rama del árbol de búsqueda o por la otra. Este proceso se repite sucesivamente hasta llegar a alguna hoja del árbol de búsqueda binaria y, en caso de llegar a una hoja y que ésta no corresponda al valor de la expresión evaluada, ejecuta el código correspondiente al bloque default y concluye con un salto incondicional a la etiqueta L1, la cual corresponde a la primera instrucción fuera de la estructura switch y da por finalizada la ejecución de esta estructura de selección.

7.7.

Estructuras iterativas o bucles

Los bucles, junto con las estructuras condicionales y la ejecución secuencial, son las bases de la programación estructurada. Con ellos es posible implementar cualquier algoritmo existente, otorgándonos la potencia necesaria para enfrentar todos los problemas resolubles.

7.7.1.

while

La estructura while ejecuta su cuerpo una y otra vez mientras la condición de su cabecera sea verdadera. Veamos un ejemplo de cómo se traduce a ensamblador un bucle while: while (x < 10)

Compilador de C para el microcontrolador Microchip PIC18F4550

128

{ ... } El código mostrado arriba, una vez más, no muestra el contenido del cuerpo del bucle ya que es irrelevante para nuestra explicación. En su lugar hemos optado por representarlo mediante puntos suspensivos cuya traducción al ensamblador, igual que con los ejemplos anteriores, no tiene traducción, por lo que aparecerán tal cual. goto _L2 _L3: ... _L2: movf F_REG+.16, W sublw low D’9’ movf F_REG+.16+1, W btfss STATUS, C incfsz F_REG+.16+1, W sublw high D’9’ andlw 0x80 btfsc STATUS, Z goto _L3

7.7.2.

do-while

En la variante do-while, al contrario que con el while, la comprobación se efectúa al final de la primera ejecución del cuerpo, por lo que éste siempre se ejecuta al menos una vez. No es difícil deducir que eliminando la primera instrucción goto de la compilación anterior obtendríamos el comportamiento de una estructura do-while y así lo considera también GCC. do { ... } while (x> 4) & 0x0F Esta instrucción carga en WREG el valor 56 hexadecimal (86 decimal), desplazándolo 4 posiciones a la derecha y operando conjuntamente con 0x0F, quedando sólo los 4 bits menos significativos, en este caso 1010 binario (10 decimal). Tenemos suma, resta, desplazamientos, operaciones lógicas y comparaciones, entre otros operandos, que nos proporcinan una gran flexibilidad a la hora de utilizar expresiones matemáticas constantes, teniendo en cuenta que estas son evaluadas antes de traducir código. Analizando un poco más el preprocesador, presentamos tres directivas más. La directiva include permite incluir archivos externos en el código actual; es útil, por ejemplo, para separar las definiciones propias de cada procesador en distintos archivos e incluir en el código sólo la necesaria para el PIC con el que estamos trabajando. Otra directiva conocida en casi todos los precompiladores de diversos lenguajes es define, y su correspondiente undefine, que permite asociar (o desasociar respectivamente) símbolos significativos con valores menos manejables y utilizarlos directamente en el código. Por ejemplo: #define altura D’25000’

Compilador de C para el microcontrolador Microchip PIC18F4550

159

movlw altura Un uso habitual de la directiva define es dar nombres simbólicos a las direcciones de memoria de los distintos dispositivos que ofrece el microcontrolador. De este modo, la legibilidad y portabilidad del código es mayor que si empleásemos los valores numéricos. Veremos a continuación más directivas de forma rápida, dado que el objetivo de este apéndice es tener sólo una idea clara de las directivas que se usan para la generación de código en el compilador de C. Para obtener más información y conocer el resto de las directivas recomendamos leer el manual de GPUTILS. ORG posición. En modo absoluto indica la posición exacta donde se cargará el código escrito a continuación de esta directiva. Si no se especifica una posición, gpasm usará 0x000000. $: esta etiqueta indica la dirección de la instrucción que la contiene. Es útil para escribir bucles en distancias relativas a la instrucción actual. Por ejemplo: goto $ - 2. BANKISEL etiqueta. Esta macro sirve para crear el código que selecciona el banco de memoria de datos que contiene la dirección de etiqueta para poder efectuar accesos indirectos. BANKSEL etiqueta. Al igual que la anterior, genera el código de selección del banco de etiqueta pero para accesos directos. Actuará de forma diferente según las características del PIC utilizado. etiqueta CODE expresión. En modo relocalizable, indica que el bloque de código desde la directiva hasta el final del archivo o hasta el siguiente bloque constituye una nueva sección de código máquina independiente del resto. Si no se especifica una etiqueta, se usará .code. La etiqueta expresión es opcional e indica la posición exacta donde está localizada. Se usa a menudo para especificar los vectores de inicio e interrupciones. END. Marca el final del código fuente. EXTERN símbolo. En modo relocalizable, declara símbolo como definido en otro archivo objeto y será gplink quien se encargue de resolverlo. Se usa para importar los parámetros y resultados de las bibliotecas. GLOBAL símbolo. En modo relocalizable, declara símbolo como global, lo que permite que sea utilizado en otros archivos objeto donde se necesite, usando en estos la directiva EXTERN.

Compilador de C para el microcontrolador Microchip PIC18F4550

160

etiqueta RES n_bytes. Hace que gpasm reserve n_bytes de memoria en la posición en la que aparezca esta directiva. Útil para reservar espacio y referenciar el mismo haciendo uso de etiqueta. etiqueta UDATA expresión. Usado en modo relocalizable, indica que el bloque de memoria desde la directiva hasta el siguiente bloque o el final del archivo constituyen una nueva sección de memoria de datos sin inicializar. Es útil, junto con RES, para reservar espacio de memoria para las variables del código. La etiqueta expresión se usa opcionalmente para fijar la dirección donde comienza el bloque. Posteriormente veremos un ejemplo de código completo. Para completar información podemos revisar el manual de GPUTILS o consultar el manual de MPASM, dado que todas las directivas del ensamblador propietario de Microchip están disponibles en gpasm.

B.2.3.

gplink

gplink es el enlazador de código reubicable. Se encarga de relocalizar y enlazar los archivos objeto que forman un programa, y crear un único archivo listo para transferir a la memoria de nuestro microcontrolador. Su sintaxis es: gplink [opciones] [objetos] [bibliotecas] Entre sus opciones, las más importantes y útiles para el trabajo cotidiano son: -I directorio. Especifica un directorio para incluir en la búsqueda de archivos, pudiendo existir varios parámetros -I en una misma llamada a gplink. -o programa.hex. Establece un nombre alternativo al programa compilado. Por defecto será a.hex. -s archivo. Especifica el archivo con las definiciones necesarias para el proceso de enlazado, correspondientes al microcontrolador especificado. Este archivo, llamado linker script, indica la memoria disponible, su tipo y localización, y demás parámetros necesarios para que el enlazador haga su trabajo. Si no se especifica ningún archivo, gplink usará el indicado en los códigos objeto pasados como argumentos.

Compilador de C para el microcontrolador Microchip PIC18F4550

B.2.4.

161

gplib

gplib crea, modifica y extrae archivos objeto de bibliotecas. Con esto podemos agrupar una colección de objetos en un único archivo y pasar este único archivo a gplink. gplink tomará solamente los objetos que necesite de la biblioteca, por lo que el tamaño final del programa no excede más allá de lo estrictamente necesario. Su sintaxis es: gplib [opciones] biblioteca [objetos] Entre las opciones, las más importantes y útiles para el trabajo cotidiano son: -c. Crea una nueva biblioteca. -d. Borra objetos de una biblioteca. -r. Añade o reemplaza un objeto de una biblioteca. -s. Muestra los símbolos globales de una biblioteca. -t. Muestra los objetos de una biblioteca. -x. Extrae los objetos de una biblioteca. Supongamos como ejemplo que tenemos los siguientes archivos: mult.o add.o sub.o div.o La forma de crear una biblioteca con los tres primeros archivos objeto utilizando gplib es ésta: gplib -c math.a mult.o add.o sub.o Si posteriormente deseamos añadir div.o, o actualizarlo si ya estuviese en la biblioteca, lo haríamos de esta manera: gplib -r math.a div.o

Compilador de C para el microcontrolador Microchip PIC18F4550

B.3.

Compilando ensamblador

Veamos dos ejemplos de archivos ensamblador válidos. El archivo funcion1.s: extern suma extern op1, op2 global result resultado resul

UDATA 1

res

_funcion_llamante funcion1_ll: BANKSEL op1 movlw 0x01 movwf op1 movlw 0x04 movwf op2 call suma goto $ _reset

CODE

CODE goto funcion1_ll nop nop retfie

END Y el archivo add.s: extern global global

result op1, op2 suma

operadores op1 res op2 res

UDATA 1 1

0x0

162

Compilador de C para el microcontrolador Microchip PIC18F4550 Opsuma suma:

163

CODE BANKSEL op1 movf op1, W addwf op2, W BANKSEL result movwf result return

END Examinando los ficheros vemos como en funcion1.s están declarados suma, op1 y op2 como símbolos externos, indicando con ello que se pueden usar pero que no están declarados en este archivo. Al declarar result como global le damos visibilidad desde otros archivos. Observamos cómo en add.s se utilizan los mismos símbolos pero de manera contraria, dando visibilidad a op1, op2 y suma, y permitiendo el uso de result pese a no estar declarado. Con los segmentos UDATA designamos secciones, sin inicializar, de memoria de datos: un byte en el primer archivo y dos bytes en el segundo. No especificamos dónde se establecerán sino el tamaño que ocuparán y un nombre con el que indentificarlos a ellos. Es habitual emplear una única etiqueta para referirse a la dirección base de una sección y sumarle un offset cuando direccionemos bytes más alla del correspondiente a la base. Con los segmentos CODE ocurre exactamente lo mismo: establecemos una sección de código pero no damos ninguna indicación de dónde situarlo. En este segmento podemos ver como en el primer archivo, antes de poder usar el símbolo op1, debemos usar BANKSEL para seleccionar el banco correcto en un acceso directo. Dado que los operadores, op1 y op2, están definidos dentro de la misma sección, estos están en el mismo banco de memoria de datos, así que no es necesario usar BANKSEL una vez direccionado uno de ellos. Este primer archivo termina con “GOTO $”, el cual resulta en un bucle infinito. De esta manera el microcontrolador queda activo pero sin realizar ninguna operación aparte de esta espera activa sin fin. Existe otro segmento de código, definido en el primer archivo, llamado “_reset”. Con éste sí indicamos una posición donde debe ubicarse, la posición 0x0. Esta dirección corresponde al vector de inicialización del microcontrolador y es donde se encontrará la primera instrucción que ejecutará el PIC cuando se encienda. Examinados nuestros ficheros de ejemplo pasamos a ensamblarlos. Esto lo hacemos con las siguientes llamadas a gpasm:

Compilador de C para el microcontrolador Microchip PIC18F4550

164

gpasm -c -p18F4550 funcion1.s gpasm -c -p18F4550 add.s Obtendremos dos archivos objeto, add.o y funcion1.o. Estos dos archivos no son ejecutables hasta haber efectuado la fase de enlazado, ya que contiene símbolos sin resolver. Además de los ficheros objeto, tendremos dos archivos (uno por cada objeto) con el mismo nombre y de extensión .lst, que contendrán datos importantes para la depuración, como una lista de símbolos generados, el código máquina de las instrucciones o la posición relativa de las instrucciones respecto al segmento. Después de la fase de ensamblado, llamamos al enlazador para que complete el proceso de compilación de nuestro programa en ensamblador: gplink add.o funcion1.o -o programa.hex El resultado del proceso de enlazado son tres archivos. El primero de estos archivos contiene el programa ejecutable que escribiremos en la flash del PIC; su nombre, para este ejemplo, es programa.hex. Los otros dos archivos son programa.cod, que nos sirve para depurar el programa con algún software de simulación, y programa.lst, que contiene el listado de memoria de nuestro programa, con direcciones de memoria absolutas.

Apéndice C Programas de ejemplo El Hola, mundo en el campo de la programación de microcontroladores es un programa como el siguiente: #include void main(void) { TRISD = 0; PORTD = 1; while(1) { PORTD = ~PORTD & 0x1; delayms(500); } } Su propósito es hacer que parpadee un diodo LED conectado al pin 1 del puerto D con un período de un segundo. Un montaje electrónico sencillo para usar este programa consiste, además de las tomas de alimentación y la señal de reloj, en un LED conectado, junto a su resistencia de polarización, al pin 1 del puerto D. Este programa comienza inicializando el puerto D como salida y, acto seguido, entra en un bucle infinito, donde cambia el valor de salida del pin 1 y ejecuta un retardo de 500ms mediante la función de biblioteca delayms, ante de la siguiente iteración. El archivo de cabecera p18f4550.h, incluido al comienzo del código, define una serie de macros y constantes de preprocesador que facilitan la programación de este microcontrolador, dando nombres autodescriptivos para hacer referencia a los registros especiales del microcontrolador. Por ejemplo, la constante TRISD es reem165

Compilador de C para el microcontrolador Microchip PIC18F4550

166

plazada por la cadena “(* (unsigned char *) 0x088)”. De este modo, el programador no necesita aprenderse los detalles de direccionamiento del microcontrolador y puede escribir un código más legible y menos propenso a errores. Además, el código escrito de esta manera es mucho más sencillo de portar a otros modelos de microcontrolador, limitándose a cambiar el archivo de cabecera en la mayoría de casos. En la fase de enlazado de código debemos combinar nuestro programa, ya compilado y ensamblado, con el código objeto de la función delayms. Con esto obtendremos un programa ejecutable completo que hará que nuestro microncontrolador genere una onda cuadrada, la cual que se traduce en un parpadeo periódico del led conectado al pin de salida. Veamos un ejemplo basado en el programa anterior pero más vistoso que el sencillo LED parpadeante: una luz que rebota en los extremos de un segmento (conocido popularmente como efecto del coche fantástico). Para este ejemplo hará falta conectar un LED a cada uno de los 8 pines del puerto D. #include void main(void) { TRISD=0; while (1) { PORTD = 1; while (PORTD) { delayms(50); PORTD = PORTD 1; } } }

Compilador de C para el microcontrolador Microchip PIC18F4550

167

Un ejemplo más complicado que es posible implementar con el mismo montaje hardware que el ejemplo anterior es mostrar una secuencia cíclica con los primeros valores de la sucesión de Fibonacci codificados en binario. void main(void) { unsigned char f1, f2; TRISD = 0; while (1) { f1 = 1; f2 = 1; while (1) { PORTD = f1; f1 += f2; delayms(1000); if (f1 == 233) break; PORTD = f2; f2 += f1; delayms(1000); if (f2 == 233) break; } } } Este código hace uso de dos variables para almacenar los dos números anteriores en la secuencia, que serían los necesarios para construir la sucesión de Fibonacci. Aunque puede resultar un código algo complejo para una implementación de la sucesión de números de Fibonacci, sirve para ilustrar la manera de emplear sin problemas varias variables en conjunción con los registros-fila del microcontrolador. Recordemos que los registros-fila son punteros desreferenciados, por lo que se manejan como variables normales.

Compilador de C para el microcontrolador Microchip PIC18F4550

168

Para terminar, veamos un ejemplo que hace uso de una entrada desde el exterior del microcontrolador. Este ejemplo usará el USART para comunicarse con un PC convencional. void main(void) { unsigned char i; unsigned char tmp; char datoadc[4]; datoadc[3] = ’\0’; /* El led parpadeará tres veces, indicando el comienzo del proceso. */ TRISD = 0; for (i = 1 ; i
View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF