Programación de Microcontroladores C
Programación de Microcontroladores C
Usted seguramente sabe que no es suficiente sólo conectar el microcontrolador a los otros componentes y encender una
fuente de alimentación para hacerlo funcionar, ¿verdad? Hay que hacer algo más. Se necesita programar el
microcontrolador. Si cree que esto es complicado, está equivocado. Todo el procedimiento es muy simple. Basta con leer el
texto para entender de lo que estamos hablando.
El microcontrolador ejecuta el programa cargado en la memoria Flash. Esto se denomina el código ejecutable y está
compuesto por una serie de ceros y unos, aparentemente sin significado. Dependiendo de la arquitectura del
microcontrolador, el código binario está compuesto por palabras de 12, 14 o 16 bits de anchura. Cada palabra se interpreta
por la CPU como una instrucción a ser ejecutada durante el funcionamiento del microcontrolador. Todas las instrucciones
que el microcontrolador puede reconocer y ejecutar se les denominan colectivamente Conjunto de instrucciones. Como es
más fácil trabajar con el sistema de numeración hexadecimal, el código ejecutable se representa con frecuencia como una
serie de los números hexadecimales denominada código Hex. En los microcontroladores PIC con las palabras de programa
de 14 bits de anchura, el conjunto de instrucciones tiene 35 instrucciones diferentes.
LENGUAJE ENSAMBLADOR
Como el proceso de escribir un código ejecutable era considerablemente arduo, en consecuencia fue creado el primer
lenguaje de programación denominado ensamblador (ASM). Siguiendo la sintaxis básica del ensamblador, era más fácil
escribir y comprender el código. Las instrucciones en ensamblador consisten en las abreviaturas con significado y a cada
instrucción corresponde una localidad de memoria. Un programa denominado ensamblador compila (traduce) las
instrucciones del lenguaje ensamblador a código máquina (código binario).
HEste programa compila instrucción a instrucción sin optimización. Como permite controlar en detalle todos los procesos
puestos en marcha dentro del chip, este lenguaje de programación todavía sigue siendo popular.
Los lenguajes de programación de alto nivel (Basic, Pascal, C etc.) fueron creados con el propósito de superar las
desventajas del ensamblador. En lenguajes de programación de alto nivel varias instrucciones en ensamblador se
sustituyen por una sentencia. El programador ya no tiene que conocer el conjunto de instrucciones o características del
hardware del microcontrolador utilizado. Ya no es posible conocer exactamente cómo se ejecuta cada sentencia, de todas
formas ya no importa. Aunque siempre se puede insertar en el programa una secuencia escrita en ensamblador.
Si alguna vez ha escrito un programa para un microcontrolador PIC en lenguaje ensamblador, probablemente sepa que la
arquitectura RISC carece de algunas instrucciones. Por ejemplo, no hay instrucción apropiada para multiplicar dos
números. Por supuesto, para cada problema hay una solución y éste no es una excepción gracias a la aritmética que
permite realizar las operaciones complejas al descomponerlas en un gran número operaciones más simples. En este caso,
la multiplicación se puede sustituir con facilidad por adición sucesiva (a x b = a + a + a + ... + a). Ya estamos en el comienzo
de una historia muy larga... No hay que preocuparse al utilizar uno de estos lenguajes de programación de alto nivel como
es C, porque el compilador encontrará automáticamente la solución a éste problema y otros similares. Para multiplicar los
números a y b, basta con escribir a*b.
LENGUAJE C
El lenguaje C dispone de todas las ventajas de un lenguaje de programación de alto nivel (anteriormente descritas) y le
permite realizar algunas operaciones tanto sobre los bytes como sobre los bits (operaciones lógicas, desplazamiento etc.).
Las características de C pueden ser muy útiles al programar los microcontroladores. Además, C está estandarizado (el
estándar ANSI), es muy portable, así que el mismo código se puede utilizar muchas veces en diferentes proyectos. Lo que
lo hace accesible para cualquiera que conozca este lenguaje sin reparar en el propósito de uso del microcontrolador. C es
un lenguaje compilado, lo que significa que los archivos fuentes que contienen el código C se traducen a lenguaje máquina
por el compilador. Todas estas características hicieron al C uno de los lenguajes de programación más populares.
La figura anterior es un ejemplo general de lo que sucede durante la compilación de programa de un lenguaje de
programación de alto nivel a bajo nivel.
A continuación vamos a presentar a los elementos principales del lenguaje mikroC desarrollado por Mikroelektronika. Este
lenguaje es muy similar al C estándar, no obstante en determinados aspectos difiere del ANSI estándar en algunas
características. Algunas de estas diferencias se refieren a las mejoras, destinadas a facilitar la programación de los
microcontroladores PIC, mientras que las demás son la consecuencia de la limitación de la arquitectura del hardware de los
PIC. Aquí vamos a presentar características específicas del lenguaje mikroC en la programación de los microcontroladores
PIC. El término C se utilizará para referirse a las características comunes de los lenguajes C y mikroC.
Este libro describe una aplicación muy concreta del lenguaje de programación C utilizado en el compilador mikroC PRO for
PIC. En este caso, el compilador se utiliza para la programación de los microcontroladores PIC.
FASES DE COMPILACIÓN
El proceso de compilación consiste en varios pasos y se ejecuta automáticamente por el compilador. Por con, un
conocimiento básico del funcionamiento puede ser útil para entender el concepto del lenguaje mikroC.
El archivo fuente contiene el código en mikroC que usted escribe para programar el microcontrolador. El preprocesador se
utiliza automáticamente por el compilador al iniciarse el proceso de la compilación. El compilador busca las directivas del
preprocesador (que siempre empiezan por ‘#’) dentro del código y modifica el código fuente de acuerdo con las directivas.
En esta fase se llevan a cabo inclusión de archivos, definición de constantes y macros etc, lo que facilita el proceso. Más
tarde vamos a describir estas directivas en detalle. El analizador sintáctico (parser) elimina toda la información inútil del
código (comentarios, espacios en blanco). Luego, el compilador traduce el código a un archivo binario denominado archivo
.mcl. El enlazador (linker) recupera toda la información requerida para ejecutar el programa de los archivos externos y la
agrupa en un solo archivo (.dbg). Además, un proyecto puede contener más de un archivo fuente y el programador puede
utilizar funciones predefinidas y agrupadas dentro de los archivos denominados librerías. Por último, el generador .hex
produce un archivo .hex. Es el archivo que se va a cargar en el microcontrolador.
El proceso entero de la compilación que incluye todos los pasos anteriormente descritos se le denomina “building”.
ESTRUCTURA DE PROGRAMA
La idea principal de escribir un programa en C es de “romper” un problema mayor en varios trozos más pequeños.
Supongamos que es necesario escribir un programa para el microcontrolador para medir la temperatura y visualizar los
resultados en un LCD. El proceso de medición se realiza por un sensor que convierte temperatura en voltaje. El
microcontrolador utiliza el convertidor A/D para convertir este voltaje (valor analógico) en un número (valor digital) que luego
se envía al LCD por medio de varios conductores. En consecuencia, el programa se divide en cuatro partes, de las que
cada una corresponde a una acción específica:
1. Activar y configurar el convertidor A/D incorporado;
2. Medir el valor analógico;
3. Calcular temperatura; y
4. Enviar los datos en el formato apropiado al LCD;
Los lenguajes de programación de alto nivel como es C le permiten solucionar este problema con facilidad al escribir cuatro
funciones que se ejecutarán cíclicamente sin parar.
La idea general es de dividir el problema en varios trozos, de los que cada uno se puede escribir como una sola función.
Todos los programas escritos en mikroC contienen por lo menos una función llamada main() que encierra entre llaves {} las
sentencias a ser ejecutadas. Esto es la primera función a ser ejecutada al iniciarse la ejecución de programa. Las otras
funciones se pueden llamar dentro de la función main. En otras palabras, podemos decir que la función main() es
obligatoria, mientras que las demás son opcionales. Si todavía no ha escrito un programa en C, es probable que todo le
resulte confuso. No se preocupe, acéptelo tal como es por el momento y más tarde entenderá la sintaxis.
¡Y ahora, su primer programa ‘real’! La figura muestra la estructura de programa, señalando las partes en las que consiste.
La manera de escribir el código en C es muy importante. Por ejemplo, C difiere entre minúsculas y mayúsculas, así que la
función main() no se puede escribir MAIN() o Main(). Además, note que dos líneas del código dentro de la función terminan
con un punto y coma. En C todas las sentencias deben terminar con un punto y coma ‘;’, así el compilador puede aislarlas y
traducirlas a código máquina.
COMENTARIOS
Los comentarios son las partes del programa utilizados para aclarar las instrucciones de programa o para proporcionar más
información al respecto. El compilador no hace caso a los comentarios y no los compila al código ejecutable. Dicho de
manera sencilla, el compilador es capaz de reconocer los caracteres especiales utilizados para designar dónde los
comentarios comienzan y terminan y no hace nada de caso al texto entre ellos durante la compilación. Hay dos tipos de
tales caracteres. Unos designan los comentarios largos que ocupan varias líneas de programa marcados por la secuencia
especial /*...*/, mientras que otros designan los comentarios cortos que caben en una sola línea //. Aunque los comentarios
no pueden afectar a la ejecución de programa, son tan importantes como cualquier otra parte de programa. Aquí está el
porqué... Con frecuencia es necesario mejorar, modificar, actualizar, simplificar un programa... No es posible interpretar
incluso los programas simples sin utilizar los comentarios.
2.3 TIPOS DE DATOS EN MIKROC
En el lenguaje C, los datos tienen un tipo, o sea, cada dato utilizado en el programa debe tener su tipo especificado. Esto
permite al compilador conocer el tamaño de dato (número de bytes requerido en la memoria) y su representación. Hay
varios tipos de datos que se pueden utilizar en el lenguaje de programación mikroC dependiendo del tamaño de dato y del
rango de valores. La tabla muestra el rango de valores que los datos pueden tener cuando se utilizan en su forma básica.
de ±1.17549435082·10-38 a
float Valores en punto flotante 32
±6.80564774407·1038
*Debido a las limitaciones impuestas por el hardware del microcontrolador, es imposible alcanzar una mayor precisión de
datos que la del tipo float. Por eso, el tipo double en mikroC equivale al tipo float.
Al añadir un prefijo (calificador) a cualquier tipo de dato entero o carácter, el rango de sus posibles valores cambia así como
el número de los bytes de memoria necesarios. Por defecto, los datos de tipo int son con signo, mientras que los de tipo
char son sin signo. El calificador signed (con signo) indica que el dato puede ser positivo o negativo. El prefijo unsigned
indica que el dato puede ser sólo positivo. Note que el prefijo es opcional.
de -2147483648 a
signed long int 32
2147483647
0. // = 0.0
-1.23 // = -1.23
23.45e6 // = 23.45 * 10^6
2e-5 // = 2.0 * 10^-5
3E+10 // = 3.0 * 10^10
.09E34 // = 0.09 * 10^34
59 // entero
'p' // carácter ASCII 'p'
Una secuencia de caracteres es denominada cadena (string). Las cadenas están encerradas entre comillas dobles, por
ejemplo:
Una constante tiene las mismas características que una variable excepto el hecho de que su valor asignado no puede ser
cambiado durante la ejecución de programa. A diferencia de las variables, las constantes se almacenan en la memoria
Flash del microcontrolador para guardar el mayor espacio posible de memoria RAM. El compilador las reconoce por el
nombre y el prefijo const. En mikroC, el compilador reconoce automáticamente el tipo de dato de una constante, así que no
es necesario especificar el tipo adicionalmente.
Cada variable o constante debe tener un identificador que lo distingue de otras variables y constantes. Refiérase a los
ejemplos anteriores, a y A son identificadores.
Reglas para nombrar
En mikroC, los identificadores pueden ser tan largos como quiera. Sin embargo, hay varias restricciones:
Los identificadores pueden incluir cualquiera de los caracteres alfabéticos A-Z (a-z), los dígitos 0-9 y el carácter
subrayado '_'. El compilador es sensible a la diferencia entre minúsculas y mayúsculas. Los nombres de
funciones y variables se escriben con frecuencia con minúsculas, mientras que los nombres de constantes se
escriben con mayúsculas.
Los identificadores no pueden empezar con un dígito.
Los identificadores no pueden coincidir con las palabras clave del lenguaje mikroC, porque son las palabras
reservadas del compilador.
El compilador mikroC reconoce 33 palabras clave:
temperatura_V1 // OK
Presión // OK
no_corresponder // OK
dat2string // OK
SuM3 // OK
_vtexto // OK
7temp // NO -- no puede empezar con un número
%más_alto // NO -- no pueden contener caracteres especiales
if // NO -- no puede coincidir con una palabra reservada
j23.07.04 // NO -- no puede contener caracteres especiales (punto)
nombre de variable // NO -- no puede contener espacio en blanco
Declaración de variables
Cada variable debe ser declarada antes de ser utilizada en el programa. Como las variables se almacenan en la memoria
RAM, es necesario reservar el espacio para ellas (uno, dos o más bytes). Al escribir un programa, usted sabe qué tipo de
datos quiere utilizar y qué tipo de datos espera como resultado de una operación, mientras que el compilador no lo sabe.
No se olvide de que el programa maneja las variables con los nombres asignados. El compilador las reconoce como
números en la memoria RAM sin conocer su tamaño y formato. Para mejorar la legibilidad de código, las variables se
declaran con frecuencia al principio de las funciones:
<tipo> variable;
Es posible declarar más de una variable de una vez si tienen el mismo tipo.
Aparte del nombre y del tipo, a las variables se les asignan con frecuencia los valores iniciales justamente enseguida de su
declaración. Esto no es un paso obligatorio, sino ‘una cuestión de buenas costumbres’. Se parece a lo siguiente:
Un método más rápido se le denomina declaración con inicialización (asignación de los valores iniciales):
Si hay varias variables con el mismo valor inicial asignado, el proceso se puede simplificar:
Tenga cuidado de no declarar la misma variable otra vez dentro de la misma función.
Puede modificar el contenido de una variable al asignarle un nuevo valor tantas veces que quiera
Al declarar una variable, siempre piense en los valores que la variable tendrá que contener durante la ejecución de
programa. En el ejemplo anterior, peso1 no se puede representar con un número con punto decimal o un número
con valor negativo.
Declaración de constantes
Similar a las variables, las constantes deben ser declaradas antes de ser utilizadas en el programa. En mikroC, no es
obligatorio especificar el tipo de constante al declararla. Por otra parte, las constantes deben ser inicializadas a la vez que
se declaran. El compilador reconoce las constantes por su prefijo const utilizado en la declaración. Dos siguientes
declaraciones son equivalentes:
Las constantes de enumeración son un tipo especial de constantes enteras que hace un programa más comprensible al
asignar los números ordinales a las constantes. Por defecto, el valor 0 se asigna automáticamente a la primera constante
entre llaves, el valor 1 a la segunda, el valor 2 a la tercera etc.
int Velocidad_de_ascensor
enum motor_de_ascensor {PARADA,INICIO,NORMAL,MÁXIMO};
Velocidad_de_ascensor = NORMAL; // Velocidad_de_ascensor = 2
typedef unsigned int positivo; // positivo es un sinónimo para el tipo sin signo int
positivo a,b; // Variables a y b son de tipo positivo
a = 10; // Variable a equivale a 10
b = 5; // Variable b equivale a 5
A las variables globales se les puede acceder de cualquiera parte en el código, aún dentro de las funciones con tal de que
sean declaradas. El ámbito de una variable global está limitado por el fin del archivo fuente en el que ha sido declarado.
El ámbito de variables locales está limitado por el bloque encerrado entre llaves {} en el que han sido declaradas. Por
ejemplo, si están declaradas en el principio del cuerpo de función (igual que en la función main) su ámbito está entre el
punto de declaración y el fin de esa función. Refiérase al ejemplo anterior. A las variables locales declaradas en main() no
se les puede acceder desde la Función_1 y al revés.
Un bloque compuesto es un grupo de declaraciones y sentencias (que pueden ser bloques también) encerradas entre
llaves. Un bloque puede ser una función, una estructura de control etc. Una variable declarada dentro de un bloque se
considera local, o sea, ‘existe’ sólo dentro del bloque. Sin embargo, las variables declaradas fuera del ámbito todavía son
visibles.
Aunque las constantes no pueden ser modificadas en el programa, siguen las mismas reglas que las variables. Esto
significa que son visibles dentro de su bloque a excepción de las constantes globales (declaradas fuera de cualquier
función). Las constantes se declaran normalmente en el inicio del código fuera de cualquier función (como variables
globales).
Clases de almacenamiento
Las clases de almacenamiento se utilizan para definir el ámbito y la vida de variables, constantes y funciones dentro de un
programa. En mikroC se pueden utilizar diferentes clases de almacenamiento:
auto es una clase de almacenamiento por defecto para las variables locales, así que se utiliza raramente. Se
utiliza para definir que una variable local tiene duración local. La clase de almacenamiento auto no se puede
utilizar con variables globales.
static es una clase de almacenamiento por defecto para las variables globales. Especifica que una variable es
visible dentro del archivo. A las variables locales declaradas con el prefijo static se les puede acceder dentro del
archivo fuente (o sea se comportan como variables globales).
extern: la palabra clave extern se utiliza cuando el programa está compuesto por diferentes archivos fuente. Esto
le permite utilizar una variable, una constante o una función declarada en otro archivo. Por supuesto, para
compilar y enlazar este archivo correctamente, el mismo debe ser incluido en su proyecto. En los siguientes
ejemplos, el programa consiste en dos archivos: File_1 y File_2. El File_1 utiliza una variable y una función
declaradas en File_2.
File 1:
void main(){
PORTA = cnt++; // Cualquier modificación de cnt en File_1 será visible en File_2
hello(); // Función hello()se puede llamar desde aquí
}
File 2:
int cnt = 0;
void hello();
void hello(){ // Modificaciones que afectan a la
. // cnt en File_1 son visibles aquí
.
}
2.5 OPERADORES
Un operador es un símbolo que denota una operación aritmética, lógica u otra operación particular. Dicho de manera
sencilla, varias operaciones aritméticas y lógicas se realizan por medio de los operadores. Hay más de 40 operaciones
disponibles en el lenguaje C, pero se utiliza un máximo de 10-15 de ellas en práctica. Cada operación se realiza sobre uno
o más operandos que pueden ser variables o constantes. Además, cada operación se caracteriza por la prioridad de
ejecución y por la asociatividad.
OPERADORES ARITMÉTICOS
Los operadores aritméticos se utilizan en las operaciones aritméticas y siempre devuelven resultados numéricos. Hay dos
tipos de operadores, los unitarios y los binarios. A diferencia de las operaciones unitarias que se realizan sobre un
operando, las operaciones binarias se realizan sobre dos operandos. En otras palabras, se requieren dos números para
ejecutar una operación binaria. Por ejemplo: a+b o a/b.
OPERADOR OPERACIÓN
+ Adición
- Resta
* Multiplicación
/ División
% Resto de la división
OPERADORES DE ASIGNACIÓN
Hay dos tipos de asignación en el lenguaje C:
Los operadores simples asignan los valores a las variables utilizando el carácter común '='. Por ejemplo: a =8
Las asignaciones compuestas son específicas para el lenguaje C. Consisten en dos caracteres como se muestra
en la tabla a la derecha. Se utilizan para simplificar la sintaxis y habilitar la ejecución más rápida.
EJEMPLO
OPERADOR
Expresión Equivalente
+= a += 8 a=a+8
-= a -= 8 a=a-8
*= a *= 8 a=a*8
/= a /= 8 a=a/8
%= a %= 8 a=a%8
++a
++ Variable "a" es incrementada por 1
a++
--b
-- Variable "a" es decrementada por 1
b--
int a, b, c;
a = b = 5;
c = 1 + a++; // c = 6
b = ++c + a // b = 7 + 6 = 13
OPERADORES RELACIONALES
Los operadores relacionales se utilizan en comparaciones con el propósito de comparar dos valores. En mikroC, si una
expresión es evaluada como falsa (false), el operador devuelve 0, mientras que si una oración es evaluada como verdadera
(true), devuelve 1. Esto se utiliza en expresiones tales como ‘si la expresión es evaluada como verdadera, entonces...’
int prop;
int var = 5;
prop = var < 10; // Expresión es evaluada como verdadera, prop = 1
OPERADORES LÓGICOS
Hay tres tipos de operaciones lógicas en el lenguaje C: Y (AND) lógico, O (OR) lógico y negación - NO (NOT) lógico. Los
operadores lógicos devuelven verdadero (1 lógico) si la expresión evaluada es distinta de cero. En caso contrario, devuelve
falso (0 lógico) si la expresión evaluada equivale a cero. Esto es muy importante porque las operaciones lógicas se realizan
generalmente sobre las expresiones, y no sobre las variables (números) particulares en el programa. Por lo tanto, las
operaciones lógicas se refieren a la veracidad de toda la expresión.
Por ejemplo: 1 && 0 es igual a (expresión verdadera) && (expresión falsa). El resultado 0, o sea - Falso en ambos casos.
OPERADOR FUNCIÓN
&& Y
|| O
! NO
OPERADORES DE MANEJO DE BITS
A diferencia de las operaciones lógicas que se realizan sobre los valores o expresiones, las operaciones de manejo de bits
se realizan sobre los bits de un operando. Se enumeran en la siguiente tabla:
a = 11100011
& Y lógico para manejo de bits c=a&b c = 11000000
b = 11001100
a = 11100011
| O lógico para manejo de bits c=a|b c = 11101111
b = 11001100
a = 11100011
^ EXOR lógico para manejo de bits c=a^b c = 00101111
b = 11001100
Note que el resultado de la operación de desplazamiento a la derecha depende del signo de la variable. En caso de que el
operando se aplique a una variable sin signo o positiva, se introducirán los ceros en el espacio vacío creado por
desplazamiento. Si se aplica a un entero con signo negativo, se introducirá un 1 para mantener el signo correcto de la
variable.
¿CÓMO UTILIZAR LOS OPERADORES?
Aparte de los operadores de asignación, dos operadores no deben estar escritos uno junto al otro.
+- de izquierda a derecha
== != de izquierda a derecha
^ de izquierda a derecha
| de izquierda a derecha
|| de derecha a izquierda
?: de derecha a izquierda
int a, b, res;
a = 10;
b = 100;
res = a*(a + b); // resultado = 1100
res = a*a + b; // resultado = 200
CONVERSIÓN DE TIPOS DE DATOS
Algunas operaciones implican conversión de datos. Por ejemplo, si divide dos valores enteros, hay una alta posibilidad de
que el resultado no sea un entero. El mikroC realiza una conversión automática cuando se requiera.
Si dos operandos de tipo diferente se utilizan en una operación aritmética, el tipo de operando de la prioridad más baja se
convierte automáticamente en el tipo de operando de la prioridad más alta. Los tipos de datos principales se colocan según
el siguiente orden jerárquico:
La autoconversión se realiza asimismo en las operaciones de asignación. El resultado de la expresión de la derecha del
operador de la asignación siempre se convierte en el tipo de la variable de la izquierda del operador. Si el resultado es de
tipo de la prioridad más alta, se descarta o se redondea para coincidir con el tipo de la variable. Al convertir un dato real en
un entero, siempre se descartan los números que siguen al punto decimal.
Para realizar una conversión explícita, antes de escribir una expresión o una variable hay que especificar el tipo de
resultado de operación entre paréntesis.
ESTRUCTURAS CONDICIONALES
Las condiciones son ingredientes comunes de un programa. Las condiciones permiten ejecutar una o varias sentencias
dependiendo de validez de una expresión. En otras palabras, ‘Si se cumple la condición (...), se debe hacer (...). De lo
contrario, si la condición no se cumple, se debe hacer (...)’. Los operandos condicionales if-else y switch se utilizan en las
operaciones condicionales. Una sentencia condicional puede ser seguida por una sola sentencia o por un bloque de
sentencias a ser ejecutadas.
OPERADOR CONDICIONAL if-else
El operador if se puede utilizar solo o asociado al operador else (if-else).
Ejemplo del operador if:
if(expresión) operación;
Si el resultado de la expresión encerrada entre paréntesis es verdadero (distinto de 0) la operación se realiza y el programa
continúa con la ejecución. Si el resultado de la expresión es falso (0), la operación no se realiza y el programa continúa
inmediatamente con la ejecución.
Como hemos mencionado, la otra forma combina tanto el operador if como el else:
Si el resultado de la expresión es verdadero (distinto de 0), se realiza operación1, de lo contrario se realiza la operación2.
Después de realizar una de las operaciones, el programa continúa con la ejecución.
La sentencia if-else se parece a lo siguiente:
if(expresión)
operación1
else
operación2
Si operación1 u operación2 está compuesta, escriba una lista de sentencias encerradas entre llaves. Por ejemplo:
if(expresión) {
... //
... // operación1
...} //
else
operación2
Si el valor de la expresión1 es distinto de 0 (verdadero), el resultado de la expresión entera será equivalente al resultado
obtenido de la expresión2. De lo contrario, si la expresión1 es 0 (falso), el resultado de la expresión entera será equivalente
al resultado obtenido de la expresión3. Por ejemplo:
Operador Switch
A diferencia de la sentencia if-else que selecciona entre dos opciones en el programa, el operador switch permite elegir
entre varias opciones. La sintaxis de la sentencia switch es:
break;
case constante2:
break;
...
default:
La operación switch se ejecuta de la siguiente manera: primero se ejecuta el selector y se compara con la constante1. Si
coinciden, las sentencias que pertenecen a ese bloque se ejecutan hasta llegar a la palabra clave break o hasta el final de
la operación switch. Si no coinciden, el selector se compara con la constante2. Si coinciden, las sentencias que pertenecen
a ese bloque se ejecutan hasta llegar a la palabra clave break etc. Si el selector no coincide con ninguna constante, se
ejecutarán las operaciones que siguen al operador default.
También es posible comparar una expresión con un grupo de constantes. Si coincide con alguna de ellas, se ejecutarán las
operaciones apropiadas:
La palabra clave de C ‘break’ se puede utilizar en cualquier tipo de bloques. Al utilizar ‘break’, es posible salir de un bloque
aunque la condición para su final no se haya cumplido. Se puede utilizar para terminar un bucle infinito, o para forzar un
bucle a terminar antes de lo normal.
BUCLES
A menudo es necesario repetir una cierta operación un par de veces en el programa. Un conjunto de comandos que se
repiten es denominado un bucle de programa. Cuántas veces se ejecutará, es decir cuánto tiempo el programa se quedará
en el bucle, depende de las condiciones de salir del bucle.
Bucle While
El bucle while se parece a lo siguiente:
while(expresión){
comandos
...
}
Los comandos se ejecutan repetidamente (el programa se queda en el bucle) hasta que la expresión llegue a ser falsa. Si la
expresión es falsa en la entrada del bucle, entonces el bucle no se ejecutará y el programa continuará desde el fin del bucle
while.
Un tipo especial del bucle de programa es un bucle infinito. Se forma si la condición sigue sin cambios dentro del bucle. La
ejecución es simple en este caso ya que el resultado entre llaves es siempre verdadero (1=verdadero), lo que significa que
el programa se queda en el mismo bucle:
Bucle For
El bucle for se parece a lo siguiente:
La operación se ejecutará cinco veces. Luego, al comprobar se valida que la expresión k<5 sea falsa (después de 5
iteraciones k=5) y el programa saldrá del bucle for.
Bucle Do-while
El bucle do-while se parece a lo siguiente:
do
operación
while (cambiar_condición);
La expresión cambiar_condición se ejecuta al final del bucle, que significa que operación se ejecuta como mínimo una vez
sin reparar en que si la condición es verdadera o falsa. Si el resultado es distinto de 0 (verdadero), el procedimiento se
repite.
Todos los siguientes ejemplos son equivalentes. Esta parte del código visualiza "hello" en un LCD 10 veces con un retardo
de un segundo. Note que en este ejemplo se utilizan funciones predefinidas, que se encuentran en las librerías del
compilador mikroC PRO for PIC. No obstante le aconsejamos que no trate de entenderlas en detalle. Su comportamiento
general dentro del bucle se explica por medio de los comentarios.
SENTENCIAS DE SALTO
SENTENCIA BREAK
A veces es necesario detener y salir de un bucle dentro de su cuerpo. La sentencia break se puede utilizar dentro de
cualquier bucle (while, for, do while) y en las sentencias switch también. En éstas la sentencia break se utiliza para salir de
las sentencias switch si la condición case es verdadera. En este ejemplo, “Esperar” está parpadeando en la pantalla LCD
hasta que el programa detecte un uno lógico en el pin 0 del puerto PORTA.
while(1){ // Bucle infinito
if(PORTA.F0 == 1) // Probar si el estado lógico del pin 0 del puerto
break; // PORTA es 1; si equivale, salir del bucle
Lcd_Out(1,3,"Esperar"); // Visualizar “Esperar” en el LCD
Delay_ms(1000); // Retardo de 1000 ms
Lcd_Cmd(_LCD_CLEAR); // Borrar LCD
Delay_ms(500); // Retardo de 500ms
}
SENTENCIA CONTINUE
La sentencia continue colocada dentro de un bucle se utiliza para saltar una iteración. A diferencia de la sentencia break, el
programa se queda dentro del bucle y las iteraciones continúan.
SENTENCIA GOTO
La sentencia goto le permite hacer un salto absoluto al otro punto en el programa. Esta característica se debe utilizar con
precaución ya que su ejecución puede causar un salto incondicional sin hacer caso a todos los tipos de limitaciones de
anidación. El punto destino es identificado por una etiqueta, utilizada como un argumento para la sentencia goto. Una
etiqueta consiste en un identificador válido seguido por un colon (:).
...
if(CO2_sensor) goto aire acondicionado; // Si se consta que el valor
... // de la variable CO2_sensor =1
// hacer salto a la línea de programa
// Aire acondicionado
...
Aire acondicionado: // Desde aquí sigue la parte del código que se ejecutará
// en caso de una concentración de CO2 demasiado alta
... // en el ambiente
Los elementos de una matriz se identifican por su posición. En C, el índice va desde 0 (el primer elemento de una matriz) a
N-1 (N es el número de elementos contenidos en una matriz). El compilador tiene que “saber” cuántas localidades de
memoria debe alojar al declarar una matriz. El tamaño de una matiz no puede ser una variable. Por eso, se pueden utilizar
dos métodos:
// método 1
int display [3]; // Declaración de la matriz display capaz de contener 3 enteros
// método 2
const DÍGITOS = 5;
char Matriz_nueva[DÍGITOS]; // Declaración de la matriz Matriz_nueva
// capaz de contener 5 enteros
Una matriz se puede inicializar a la vez que se declara, o más tarde en el programa. En ambos casos, este paso se realiza
al utilizar llaves:
Para leer o modificar un elemento de matriz del ejemplo anterior, basta con introducir su índice encerrado entre corchetes:
El siguiente programa cambia el orden de los elementos de una matriz. Note que el índice se puede expresar mediante
variables y operaciones básicas.
void main() {
const MUESTRAS_DE_AGUA = 4; // Valor de la constante MUESTRAS_DE_AGUA es 4
int i, temp; // Variables i y temp son de tipo int
int profunidad_de_sonda [MUESTRAS_DE_AGUA] = {24,25,1,1987};// Todos
MATRICES BIDIMENSIONALES
Aparte de las matrices unidimensionales que se pueden interpretar como una lista de valores, el lenguaje C le permite
declarar matrices multidimensionales. En esta parte vamos a describir sólo las matrices bidimensionales, también
denominadas tablas o matrices. Una matriz bidimensional se declara al especificar el tipo de dato de matriz, el nombre de
matriz y el tamaño de cada dimensión.
int Tabla [3][4]; // Tabla se define de modo que tenga 3 filas y 4 columnas
Similar a las matrices unidimesionales, es posible asignar los valores a los elementos de una tabla en la línea de
declaración. La asignación debe ser realizada línea a línea como en el siguiente ejemplo. Como hemos visto anteriormente,
esta matriz tiene dos filas y tres columnas:
3 42 1
7 7 19
PUNTEROS
Un puntero es una variable destinada a recibir una dirección. Un puntero “apunta” a una localidad de memoria, referenciada
por una dirección. En C, la dirección de un objeto se puede obtener por medio un operador unitario &. Para acceder al
contenido de la memoria en una dirección específica (también llamado objeto apuntado), se utiliza un operador de
indirección (*).
tipo_de_variable *puntero;
En esta etapa, el puntero mi_puntero apunta al valor almacenado en esta localidad de memoria, o sea, a un valor
desconocido. Así que, una inicialización es muy recomendable:
puntero = &variable;
Los punteros también pueden ser declarados con el prefijo ‘const’. En este caso, su valor no puede ser modificado
después de la inicialización, similar a una constante.
A diferencia de C, el mikroC no admite alojamiento dinámico.
ESTRUCTURAS
Ya hemos visto cómo agrupar los elementos dentro de matrices. No obstante, al utilizar este método todos los elementos
deben ser del mismo tipo. Al utilizar estructuras, es posible agrupar diferentes tipos de variables bajo el mismo nombre. Las
variables dentro de una estructura se le denominan los miembros de la estructura. Las estructuras de datos se declaran al
utilizar la siguiente sintaxis:
struct nombre_de_estructura {
tipo1_de_miembro1 miembro1;
tipo2_de_miembro2 miembro2;
tipo3_de_miembro3 miembro3;
..
};
struct generador {
int voltaje;
char corriente;
};
Entonces, podrá definir los objetos denominados ‘turbina’ en el código. A cada uno de estos tres objetos (turbinas) se le
asignan las variables ‘corriente’ y ‘voltaje’.
turbina_3.voltaje = 150;
turbina_3.corriente = 12;
Por supuesto, igual que al utilizar los punteros, todavía se le permite realizar operaciones por medio de operadores y
sentencias definidos en las partes anteriores.
Si está familiarizado con el lenguaje C, recuerde que mikroC no admite la inicialización de los miembros de estructura por
medio de las llaves. Por ejemplo, ‘conjunto_1 ={15,‘m’};’ devuelve un error en mikroC.
2.8 FUNCIONES
Una función es una subrutina que contiene una lista de sentencias a realizar. La idea principal es dividir un programa en
varias partes utilizando estas funciones para resolver el problema inicial con más facilidad. Además, las funciones nos
permiten utilizar las destrezas y el conocimiento de otros programadores. Una función se ejecuta cada vez que se llame
dentro de otra función. En C, un programa contiene como mínimo una función, la función main(), aunque el número de
funciones es normalmente mayor. Al utilizar funciones el código se hace más corto ya que es posible llamar una función
tantas veces como se necesite. En C, el código normalmente consiste en muchas funciones. No obstante, en caso de que
su programa sea muy corto y simple, puede escribir todas las sentencias dentro de la función principal.
FUNCIÓN PRINCIPAL
La función principal main() es una función particular puesto que es la que se ejecuta al iniciar el programa. Además, el
programa termina una vez completada la ejecución de esta función. El compilador reconoce automáticamente esta función
y no es posible llamarla por otra función. La sintaxis de esta función es la siguiente:
Esto significa que f es una función que recibe un número real x como parámetro y devuelve 2*x-y.
La misma función en C se parece a lo siguiente:
Cada función debe ser declarada apropiadamente para poder interpretarla correctamente durante el proceso de
compilación. La declaración contiene los siguientes elementos:
Tipo de resultado (valor devuelto): tipo de dato del valor devuelto
Nombre de función: es un identificador que hace posible llamar a una función.
Declaración de parámetros se parece a la declaración de variable regular (por ejemplo: float x). Cada parámetro
consiste en una variable, constante, puntero o matriz, precedidos por la etiqueta de tipo de dato. Se utilizan para
pasar la información a la función al llamarla. Los parámetros diferentes están delimitados por comas.
Cuerpo de función: bloque de sentencias dentro de llaves
Una función se parece a lo siguiente:
Una función no puede devolver más de un valor, pero puede devolver un puntero o una estructura. Tenga cuidado al utilizar
matrices y punteros. El siguiente ejemplo es un error típico:
Si la función no devuelve ningún valor, la palabra void debe ser utilizada como un tipo de resultado en la declaración. En
este caso, la sentencia return no debe ser seguida por ninguna expresión. Puede ser omitida como en el siguiente ejemplo:
Cuando se llama una función, el programa salta a la función llamada, la ejecuta, después vuelve a la línea desde la que fue
llamada.
Tenga en cuenta de que la función devuelve un valor, y no una variable. Además, se crean copias de los valores de los
parámetros, por lo que sus nombres en la función f pueden ser diferentes de los parámetros utilizados en la main(). La
mayor desventaja del ‘paso por el valor’ es que la única interacción que una función tiene con el resto del programa es el
valor devuelto de un solo resultado (o la modificación de las variables globales).
El otro método, denominado 'paso por dirección' le permite sobrepasar este problema. En vez de enviar el valor de una
variable al llamar a función, se debe enviar la dirección de memoria del valor. Entonces, la función llamada será capaz de
modificar el contenido de esta localidad de memoria.
// Función 'sort'ordena los miembros de la matriz por valor ascendente
// y devuelve el miembro con máximo valor
void main() {
int maximum, input[SIZE] = {5,10,3,12,0}; // Declaración de variables en la matriz
maximum = sort(input); // Llamar a función y asignarle el máximo
// valor a la variable maximum
}
En este ejemplo, por medio de una función se realizan dos operaciones: ordena los miembros de la matriz por valor
asdendente y devuelve el máximo valor.
Para utilizar una matriz en una función es necesario asignar la dirección a la matriz (o a su primer miembro). Vea el
siguiente ejemplo:
void main()
{
double promedio1, promedio2; // Declaración de las variables promedio1
// y promedio2
int voltaje [NÚMERO_DE_MEDICIONES] = {7,8,3,5,6,1,9}; // Declaración de la
// matriz voltaje
promedio1 = método_1(&voltaje[0]); // Parámetro de la función es la dirección
// del primer miembro
promedio2 = método_2(voltaje); // Parámetro de la función es la dirección de
// la matriz
}
//××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
float método_1(int voltaje[]) // Inicio de la función método_1
{
int i, suma; // Declaración de las variables locales i y suma
return(suma/NÚMERO_DE_MEDICIONES);
}
//××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
float método_2 (int *voltaje) //Inicio de la función método_2
{
int i, suma; // Declaración de las variables locales i y suma
return(suma/NÚMERO_DE_MEDICIONES);
}
Las funciones 'método_1' y 'método_2' son completamente equivalentes. Las dos devuelven el valor promedio de la matriz
'voltaje[]'. Después de declararla, la dirección del primer miembro se puede escribir como 'voltaje' o '&voltaje[0]'.
DIRECTIVAS FUNCIONES
También puede utilizar los parámetros para realizar substituciones más complejas:
Tanque_1 = (((Diámetro/2)*(Diámetro/2)*PI)*altura;
Por medio de la directiva #undef es posible quitar una definición de nombre de macro. Así se especifica que la substitución
que se ha definido anteriormente ya no va ocurrir en el siguiente código. Esto es útil cuando usted quiere restringir la
definición sólo a una parte particular del programa.
INCLUSIÓN DE ARCHIVOS
La directiva de preprocesador #include copia un archivo específico en el código fuente. El código incluido debe observar la
sintaxis de C para ser compilado correctamente.
Hay dos formas de escribir estas directivas. En el primer ejemplo, sólo el nombre de archivo se especifica, así que el
preprocesador lo buscará dentro del archivo include. En el segundo ejemplo, se especifica la ruta entera, así que el archivo
estará directamente incluido (este método es más rápido).
TRISB = 0; // todos los pines del puerto PORTB se configuran como salidas
PORTB = 0; // todos los pines del PORTB se ponen a 0
Para acceder a un bit individual, se puede utilizar '.FX' así como '.BX' (X es un entero entre 0 y 7 que representa la posición
de bit).
TIPO SBIT
Si quiere declarar una variable que corresponde a un bit de un SFR, hay que utilizar el tipo sbit. Una variable de tipo sbit se
comporta como un puntero y se debe declarar como una variable global:
TIPO BIT
El compilador mikroC PRO for PIC proporciona un tipo de datos bit que se puede utilizar para declarar variables. No se
puede utilizar en las listas de argumentos, punteros y los valores devueltos de funciones. Además, no es posible declarar e
inicializar una variable de tipo bit en la misma línea. El compilador determina el bit en uno de los registros disponibles para
almacenar las variables.
Una o más instrucciones en ensamblador están insertadas en el programa escrito en C, utilizando el comando asm:
asm
{
instrucciones en ensamblador
...
}
Los códigos escritos en ensamblador pueden utilizar constantes y variables anteriormente definidos en C. Por supuesto,
como el programa entero está escrito en C, sus reglas se aplican al declarar estas constantes y variables.
unsigned char maximum = 100; // Declarar variables: maximum = 100
asm
{ // Inicio del código ensamblador
MOVF maximum,W // W = maximum = 100
...
} // Final del código ensamblador
FUNCIÓN DE INTERRUPCIÓN
Una interrupción detiene la ejecución normal de un programa para ejecutar las operaciones específicas. Una lista de
sentencias a ejecutar debe estar escrita dentro de una función particular denominada interrupt(). La sintaxis de una
interrupción en mikroC se parece a lo siguiente:
void interrupt() {
cnt++ ; // Al producirse una interrupción
// la cnt se incrementa en 1
PIR1.TMR1IF = 0; // Poner a 0 el bit TMR1IF
}
A diferencia de las funciones estándar, no es necesario declarar el prototipo de la función interrupt(). Además, como la
ejecución de esta función no forma parte de la ejecución de programa regular, no se debe llamar de ninguna parte de
programa (se ejecutará automáticamente dependiendo de las condiciones que el usuario ha definido en el programa). En el
siguiente capítulo vamos a dar una clara explicación de la ejecución y definición de subrutinas de interrupción.
LIBRERÍAS
Usted probablemente ha notado que en los ejemplos anteriores hemos utilizado algunas funciones como son 'Delay_ms',
'LCD_out', 'LCD_cmd' etc. Estas funciones están definidas en las librerías contenidas en el compilador mikroC.
Una librería representa un código compilado, anteriormente escrito en mikroC, que contiene un conjunto de variables y
funciones. Cada librería tiene un propósito específico. Por ejemplo, la librería LCD contiene funciones de visualización de la
pantalla LCD, mientras que C_math proporciona algunas funciones matemáticas.
Antes de utilizar alguna de ellas en el programa, es necesario comunicárselo al compilador al marcarlas en la lista de las
librerías del compilador existentes. Si el compilador encuentra una función desconocida durante la ejecución de programa,
primero va a buscar su declaración en las librerías marcadas.
Aparte de las librerías existentes, es posible crear las librerías y luego utilizarlas en el programa. El procedimiento de cómo
crear librerías se describe en detalles en Help (Ayuda) del compilador.
El compilador mikroC incluye tres tipos de librerías:
- librerías ANSI C estándar:
L I BRARÍ A DESCRIPCIÓN
ANSI C Ctype Library Utilizada principalmente para probar o para convertir los datos
ANSI C Math Library Utilizada para las operaciones matemáticas de punto flotante
ANSI C String Library Utilizada para realizar las operaciones de cadenas y de manipulación de memoria
- librerías misceláneas:
L I BRARÍ A DESCRIPCIÓN
L I BRARÍ A DESCRIPCIÓN
CANSPI Library Utilizada para las operaciones con el módulo CAN externo (MCP2515 o MCP2510)
Compact Flash Library Utilizada para las operaciones con las tarjetas de memoria Compact Flash
EEPROM Library Utilizada para las operaciones con la memoria EEPROM incorporada
EthernetPIC18FxxJ60 Library Utilizada para las operaciones con el módulo Ethernet incorporado
Flash Memory Library Utilizada para las operaciones con la memoria Flash incorporada
Graphic Lcd Library Utilizada para las operaciones con el módulo LCD gráfico con resolución 128x64
I2C Library Utilizada para las operaciones con el módulo de comunicación serial I2C incorporado
Keypad Library Utilizada para las operaciones con el teclado (botones de presión 4x4)
Lcd Library Utilizada para las operaciones con el LCD (de 2x16 caracteres)
Multi Media Card Library Utilizada para las operaciones con las tarjetas multimedia MMC flash
One Wire Library Utilizada para las operaciones con los circuitos utilizando la comunicación serial One Wire
Port Expander Library Utilizada para las operaciones con el extensor de puertos MCP23S17
PS/2 Library Utilizada para las operaciones con el teclado estándar PS/2
PWM Library Utilizada para las operaciones con el módulo PWM incorporado
RS-485 Library Utilizada para las operaciones con los módulos utilizando la comunicación serial RS485
Software I2C Library Utilizada para simular la comunicación I2C con software
Software SPI Library Utilizada para simular la comunicación SPI con software
Software UART Library Utilizada para simular la comunicación UART con software
SPI Ethernet Library Utilizada para la comunicación SPI con el módulo ETHERNET (ENC28J60)
SPI Graphic Lcd Library Utilizada para la comunicación SPI de 4 bits con el LCD gráfico
SPI LCD Library Utilizada para la comunicación SPI de 4 bits con el LCD (de 2x16 caracteres)
SPI Lcd8 Library Utilizada para la comunicación SPI de 8 bits con el LCD
SPI T6963C Graphic Lcd Library Utilizada para la comunicación SPI con el LCD gráfico
UART Library Utilizada para las operaciones con el módulo UART incorporado
USB Hid Library Utilizada para las operaciones con el módulo USB incorporado
En las secciones anteriores hemos presentado el lenguaje mikroC, especialmente diseñado para programar los PIC. Ahora,
lo que hemos revisado es suficiente para empezar a programar, es hora de presentar el software que utilizará para
desarrollar y editar los proyectos. Este software se le denomina Entorno de desarrollo integrado (Integrated Developement
Environment - IDE) e incluye todas las herramientas necesarias para desarrollar los proyectos (editor, depurador etc.). Por
extensión, IDE es a veces llamado compilador. En esta sección le enseñaremos lo básico que debe saber para empezar a
desarrollar su primer proyecto en mikroC utilizando el IDE del compilador mikroC PRO for PIC.
Aparte de todas las características comunes de cualquier IDE, mikroC PRO for PIC contiene las informaciones de
arquitectura de los microcontroladores PIC (registros, módulos de memoria, funcionamiento de circuitos particulares etc.)
para compilar y generar un archivo legible por un microcontrolador PIC. Además, incluye las herramientas específicas para
programar los microcontroladores PIC.
El proceso de crear y ejecutar un proyecto contiene los siguientes pasos:
1. Crear un proyecto (nombre de proyecto, configuración de proyecto, dependencias entre archivos)
2. Editar un programa
3. Compilar el programa y corrección de errores
4. Depurar (ejecutar el programa paso a paso para asegurarse de que se ejecutan las operaciones deseadas).
5. Programar un microcontrolador (cargar el archivo .hex generado por el compilador en el microcontrolador utilizando el
programador PICflash).
Basta con seguir las instrucciones y pulsar sobre Next, OK, Next, Next... En general, es el mismo procedimiento menos la
última opción: 'Do you want to install PICFLASH v7.11 programmer?'. ¿Para qué sirve este software? De eso vamos a
hablar más tarde. Por ahora, basta con saber que es un software autónomo utilizado para cargar el programa en el
microcontrolador.
Una vez más: Next, OK, Next, Next...
Una vez completada la instalación del PICflash, el sistema operativo le preguntará a instalar otro programa similar, un
software para programar un grupo especial de los microcontroladores PIC que funcionan en modo de bajo consumo (3.3 V).
Salte este paso...
El último paso - ¡la instalación del controlador (driver)!
Desgraciadamente, una descripción detallada de todas las opciones disponibles de este IDE nos tomaría mucho tiempo.
Por eso vamos a describir sólo lo más importante del compilador mikroC PRO for PIC. De todos modos, para obtener más
informacion presione el botón de Ayuda (Help) [F1].
MANEJAR LOS PROYECTOS
Antes de empezar a escribir el código, usted debe crear un proyecto. Un programa escrito en el compilador mikroC PRO for
PIC no es un archivo fuente autónomo, sino que forma parte de un proyecto que incluye un código hex, un código
ensamblador, cabecera y otros archivos. Algunos de ellos se requieren para compilar el programa, mientras que otros se
crean durante el proceso de compilación. Un archivo con extensión .mcppi le permite abrir cualquiera de estos proyectos.
Para crear un proyecto, basta con seleccionar la opción Project/New Project, y un wizard aparecerá automáticamente.
¿Qué hacer entonces? Siga las instrucciones...
Device (dispositivo):
Al seleccionar el tipo de microcontrolador a utilizar permite al compilador extraer el archivo de definición (archivo .def)
asociado. El archivo de definición de un microcontrolador contiene las informaciones específicas de sus registros SFR, de
sus direcciones de memoria y algunas variables de programación específicas a ese tipo del microcontrolador. Es obligatorio
crear un archivo .hex compatible.
Oscillator (oscilador):
Se debe especificar la velocidad de operación del microcontrolador. Por supuesto, este valor depende del oscilador
utilizado. El compilador la necesita para compilar rutinas, lo que requiere información del tiempo (por ejemplo, la función
Delay_ms). Más tarde, el programador necesitará esta información también. La velocidad de operación se configura de
modo que permita al oscilador interno del microcontrolador funcionar a una frecuencia seleccionada.
Build/Debugger Type:
Todo el proceso de compilar (building) está compuesto por análisis sintáctico (parsing), compilar, enlazar (linking) y generar
los archivos .hex. El tipo de compilación le permite ajustar el modo de compilación. Dependiendo del modo seleccionado,
difieren los archivos generados a cargar en el microcontrolador.
Release: Al elegir esta opción , el compilador no puede afectar más a la ejecución de programa después de la compilación.
El programa a cargar en el microcontrolador no será modificado de ninguna manera.
ICD debug: Al elegir esta opción, una vez completado el proceso de la compilación y cargado el programa en la memoria
del microcontrolador, el compilador se queda conectado al microcontrolador por medio del cable USB y el programador, y
todavía puede afectar a su funcionamiento. El archivo .hex generado contiene los datos adicionales que permiten el
funcionamiento del depurador. Una herramienta denominada mikroICD (Depurador en circuito - In Circuit Debugger) permite
ejecutar el programa paso a paso y proporcionar un acceso al contenido actual de todos los registros de un
microcontrolador real.
El simulador no utiliza los dispositivos reales para simular el funcionamiento del microcontrolador, así que algunas
operaciones no pueden ser reproducidas (por ejemplo, interrupción). De todos modos, resulta más rápido depurar un
programa por medio de un simulador. Además, no se requiere ningún dispositivo destino.
Note que es posible modificar cualquier configuración en cualquier momento mientras se edita el programa. No se olvide de
recompilar y reprogramar su dispositivo después de modificar una configuración.
LIBRARY MANAGING (EDITOR DE LIBRERÍAS)
El compilador tiene que conocer todas las dependencias de su archivo fuente en mikroC para compilarlo apropiadamente.
Por ejemplo, si las librerías forman parte de su proyecto, debe especificar cuáles de ellas se utilizan.
Las librerías contienen un gran número de funciones listas para ser utilizadas. Las librerías en mikroC proporcionan muchas
facilidades para escribir programas para los microcontroladores PIC. Abra la ventana Library Manager, y marque las que
quiere utilizar en el programa. Al marcar una librería, se añade automáticamente al proyecto y se enlaza durante el proceso
de la compilación. Así, no necesita incluir las librerías manualmente en sus archivos del código fuente por medio de la
directiva del preprocesador #include.
Al escribir un programa, no espere que termine la redacción del programa para compilarlo. Compile su código de forma
regular con el propósito de corregir cuánto más errores de sintaxis. Asimismo usted puede compilar su programa cada vez
que se complete la redacción de una nueva función así como probar su comportamiento al utilizar modo de depuración (ver
la próxima sección). De este modo, resulta más fácil solucionar los errores de programa para no “tomar un camino erróneo”
en redactar su programa. De lo contrario, usted tendrá que editar el programa entero.
CODE EXPLORER (EXPLORADOR DEL CÓDIGO)
La ventana Code Explorer le permite localizar funciones y procedimientos dentro de los programas largos. Por ejemplo, si
usted busca una función utilizada en el programa, basta con pulsar dos veces sobre su nombre en esta ventana, y el cursor
estará automáticamente posicionado en la línea apropiada en el programa.
COMPILAR Y SOLUCIONAR LOS ERRORES
Para compilar su código, pulse sobre la opción Build en el menú Project. En realidad, el proyecto entero se ha compilado, y
si la compilación se ha realizado con éxito, se generarán los archivos de salida (asm, .hex etc.). Una compilación se ha
realizado con éxito si no se ha encontrado ningún error. Durante el proceso de compilación se generan muchos mensajes
que se visualizan en la ventana Messages. Estos mensajes consisten en información, advertencia y errores. Cada error
encontrado se asocia con su línea de programa y su descripción.
Como un error en su código puede generar mucho más errores, simplemente debe intentar solucionar el primer error en la
lista y después recompile su programa. En otras palabras, es recomendable solucionar los errores uno a uno.
En el ejemplo anterior hay dos errores y una advertencia: faltan un punto y coma y una declaración de variable La
advertencia le informa que falta el tipo del valor devuelto de la función main.
La compilación le permite corregir su programa por medio de solucionar todos los errores en mikroC. Cuando todos los
errores se solucionen, su programa está listo para ser cargado en el microcontrolador. De todas formas, su tarea todavía no
está terminada, porque aún no sabe si su programa se comporta como se esperaba o no.
DEPURAR EL PROGRAMA
La depuración es un paso muy importante ya que permite probar el programa después de una compilación realizada con
éxito, o solucionar los errores descubiertos mientras se ejecuta el programa. Como ya hemos visto, hay dos modos de
depurar: un depurador software que simula el funcionamiento del microcontrolador (modo por defecto) y depurador
hardware (mikroICD) que lee directamente el contenido de la memoria del microcontrolador. El procedimiento de
depuración es el mismo sin reparar en el modo elegido. En caso de elegir la opción ICD debug, hay que cargar el programa
en el microcontrolador antes de depurarlo.
Para iniciar la depuración, pulse sobre la opción Start debugger del menú Run. El editor del código será ligeramente
modificado automáticamente y aparecerá una ventana denominada Watch Values. El principio de depuración se basa en
ejecutar el programa paso a paso y monitorear el contenido de los registros y los valores de las variables. De este modo, es
posible comprobar el resultado de un cálculo y ver si algo inesperado ha ocurrido. Al ejecutar el programa paso a paso,
podrá localizar los problemas con facilidad.
Durante una depuración el programa será modificado, por lo que usted siempre debe recompilar el programa después de
cada corrección, y reiniciar el depurador para comprobar qué ha sido modificado.
Para visualizar esta ventana es necesario seleccionar la opción View/Debug Windows/Watch Values. Entonces usted puede
hacer una lista de registros/variables que quiere monitorear y la manera de visualizarlos.
STOPWATCH (CRONÓMETRO)
Si quiere saber cuánto tiempo tarda un microcontrolador en ejecutar una parte del programa, seleccione la opción Run/View
Stopwatch. Aparecerá una ventana como se muestra en la figura a la derecha. ¿Cómo funciona un cronómetro? Eso es pan
comido... El tiempo que tarda un comando (step into, step over, run/pause etc.) en ejecutarse por el depurador se mide
automáticamente y se visualiza en la ventana Stopwatch. Por ejemplo, se mide tiempo para ejecutar un programa, tiempo
para ejecutar el último paso etc.
PROGRAMAR EL MICROCONTROLADOR
Si ha solucionado todos los errores en su código y cree que su programa está listo para ser utilizado, el siguiente paso es
cargarlo en el microcontrolador. El programador PICflash se utiliza para este propósito. Es una herramienta diseñada para
programar todos los tipos de microcontroladores PIC. Está compuesto por dos partes:
La parte hardware se utiliza para introducir un código hexadecimal (el programa a ser cargado en el
microcontrolador) y para programar el microcontrolador por medio de niveles de voltaje específicos. Durante el
proceso de la programación, un nuevo programa se escribe en la memoria flash del microcontrolador, mientras
que el programa anterior se borra automáticamente.
La parte de software se encarga de enviar el programa (archivo .hex ) a la parte hardware del programador por
medio de un cable USB. A la interfaz de usuario de este software se le puede acceder desde IDE al pulsar sobre
la opción mE_Programmer del menú Tools o al pulsar [F11]. Por consiguiente, es posible modificar algunas
configuraciones del programador y controlar el funcionamiento de la parte hardware (Cargar, Escribir, Verificar...).
EDITOR EEPROM
Al seleccionar la opción EEPROM Editor del menú Tools, aparecerá una ventana como se muestra en la siguiente figura.
Así es cómo funciona la memoria EEPROM del microcontrolador. Si quiere cambiar de su contenido después de cargar el
programa en el microcontrolador, ésta es la forma correcta de hacerlo. El nuevo contenido es un dato de un tipo específico
(char, int o double), primero debe seleccionarlo, introducir el valor en el campo Edit Value y pulsar sobre Edit. Luego, pulse
sobre el botón Save para guardarlo como un documento con extensión .hex. Si la opción Use EEPROM in Project está
activa, los datos se cargarán automáticamente en el microcontrolador durante el proceso de la programación.
VENTANA ASCII CHART
Si necesita representar numéricamente un carácter ASCII, seleccione la opción ASCII chart del menú Tools. Aparecerá una
tabla, como se muestra en la figura que está a continuación. Usted probablemente sabe que cada tecla de teclado está
asociada con un código (código ASCII). Como se puede ver, los caracteres que representan los números tienen valores
diferentes. Por esta razón, la instrucción de programa para visualizar el número 7 en un LCD no visualizará este número,
sino el equivalente a la instrucción BEL. Si envía el mismo número en forma de un carácter a un LCD, obtendrá el resultado
esperado - número 7. Por consiguiente, si quiere visualizar un número sin convertirlo en un carácter apropiado, es
necesario añadir el número 48 a cada dígito en el que consiste el número a visualizar.
Un código generado que utiliza herramientas para controlar los visualizadores LCD y GLCD contiene funciones de la librería
Lcd. Si las utiliza en el programa, no se olvide de marcar la caja de chequeo junto a esta librería en la ventana Library
Manager. Así el compilador será capaz de reconocer estas funciones correctamente.