Capitulo 2
Capitulo 2
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.
A pesar de todos los lados buenos, el lenguaje ensamblador tiene algunas desventajas:
Incluso una sola operación en el programa escrito en ensamblador consiste en muchas instrucciones,
haciéndolo muy largo y difícil de manejar.
Cada tipo de microcontrolador tiene su propio conjunto de instrucciones que un programador tiene que
conocer para escribir un programa
Un programador tiene que conocer el hardware del microcontrolador para escribir un programa
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.
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:
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.
*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.
Tipo de dato Tipo de dato con prefijo Tamaño (número de bits) Rango de valores
char signed char 8 de -128 a 128
unsigned int 16 de 0 a 65535
short int 8 de 0 a 255
int signed short int 8 de -128 a 127
long int 32 de 0 a 4294967295
signed long int 32 de -2147483648 a 2147483647
Un entero es un número sin parte fraccionaria que puede estar expresado en los siguientes formatos:
Hexadecimal (base 16): el número empieza con 0x (o 0X). Los enteros hexadecimales consisten en los
dígitos (de 0 a 9) y/o las letras (A, B, C,D, E, F). Por ejemplo: ‘0x1A’.
Decimal (base 10): el número consiste en los dígitos (de 0 a 9). El primer dígito no puede ser 0. En este
formato, se puede introducir el signo de número (‘+’ o ‘-’). Por ejemplo: 569, -25, +1500.
Octal (base 8): los números se representan a base 8 utilizando sólo 8 dígitos (de 0 a 7). Los enteros
octales empiezan con 0. Por ejemplo: ‘056’.
Binario: cuando un entero empieza con 0b (o 0B) se representan como una serie de bits (‘0’ y ‘1’). Por
ejemplo: 0B10011111
El tipo punto flotante (float) se utiliza para los números reales con el punto decimal. Los datos de tipo float se
pueden representar de varias maneras. Un dato float es siempre consigno (signed).
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
El tipo char es considerado como un entero por el compilador. No obstante, se utiliza normalmente para los
datos de tipo carácter. Un dato de tipo carácter está encerrado entre comillas y codificado en un carácter ASCII.
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 variable es un objeto nombrado capaz de contener un dato que puede ser modificado durante la ejecución
de programa. En C, las variables tienen tipo, que significa que es necesario especificar el tipo de dato que se le
asigna a una variable (int, float etc.). Las variables se almacenan en la memoria RAM y el espacio de memoria
que ocupan (en bytes) depende de su tipo.
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.
/* dos líneas de programa consecutivas */
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.
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.
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:
Es posible introducir directamente el valor de una constante dentro de la lista de enumeraciones. El incremento
se detiene al asignar un valor a un elemento de matriz, después se reinicia a partir del valor asignado. Vea el
siguiente ejemplo:
int Velocidad_de_ascensor
enum motor_de_ascensor {PARADA,INICIO,NORMAL,MÁXIMO};
Velocidad_de_ascensor = NORMAL; // Velocidad_de_ascensor = 2
La palabra clave typedef le permite crear con facilidad los nuevos tipos de datos.
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
Una variable o una constante es reconocida por el compilador en base de su identificador. Un identificador tiene
significado si el compilador lo puede reconocer. El ámbito de una variable o una constante es el rango de
programa en el que su identificador tiene significado. El ámbito es determinado por el lugar en el que se declara
una variable o una constante. Intentar acceder a una variable o una constante fuera de su ámbito resulta en un
error. Una variable o una constante es invisible fuera de su ámbito. Todas las variables y constantes que
pensamos utilizar en un programa deben ser declaradas anteriormente en el código. Las variables y constantes
pueden ser globales o locales. Una variable global se declara en el código fuente, fuera de todas las funciones,
mientras que una variable local se declara dentro del cuerpo de la función o dentro de un bloque anidado en una
función.
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();
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
int a,b,c; // Declarar 3 enteros a, b, c
a = 5; // Inicializar a
b = 4; // Inicializar b
c = a + b; // c = 9
c = c%2; // c = 1. Esta operación se utiliza con frecuencia
// para comprobar la paridad. En este caso, el
// resultado es 1 lo que significa que la variable
// es un número imparo
OPERADORES DE ASIGNACIÓN
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
int a = 5; // Declarar e inicializar la variable a
a += 10; // a = a + 10 = 15
Las operaciones de incremento y decremento por 1 se denotan con "++" y "--". Estos caracteres pueden
preceder o seguir a una variable. En primer caso (++x), la variable x será incrementada por 1 antes de ser
utilizada en la expresión. De lo contrario, la variable se utilizará en la expresión antes de ser aumentada por 1.
Lo mismo se aplica a la operación de decremento.
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...’
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.
Operador Función
&& Y
|| O
! NO
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:
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.
Aparte de los operadores de asignación, dos operadores no deben estar escritos uno junto al otro.
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.
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.
if(expresión) operación;
Como hemos mencionado, la otra forma combina tanto el operador if como el else:
if(expresión) operación1 else operación2;
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
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
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
La ejecución de esta secuencia de programa es similar al bucle while, salvo que en este caso el proceso de
especificar el valor inicial (inicialización) se realice en la declaración. La expresión_ inicial especifica la
variable inicial del bucle, que más tarde se compara con la expresión_ de_condición antes de entrar al bucle.
Las operaciones dentro del bucle se ejecutan repetidamente y después de cada iteración el valor de la
expresión_inicial se incrementa de acuerdo con la regla cambiar_expresión. La iteración continúa hasta que la
expresión_de_condición llegue a ser falsa.
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
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.
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
Una matriz es una lista de elementos del mismo tipo colocados en localidades de memoria contiguas. Cada
elemento es referenciado por un índice. Para declarar una matriz, es necesario especificar el tipo de sus
elementos (denominado tipo de matriz), su nombre y el número de sus elementos encerrados entre corchetes.
Todos los elementos de una matriz tienen el mismo tipo.
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:
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;
Para acceder al contenido de la variable apuntada, debe utilizar ‘*’. El siguiente ejemplo muestra el contenido
de memoria dependiendo de la acción realizada por medio del puntero.
Los punteros son muy útiles para manejar las matrices. En este caso, un puntero se utilizará para apuntar al
primer elemento de una matriz. Debido al hecho de que es posible realizar operaciones básicas sobre los
punteros (aritmética de punteros), es fácil manejar los elementos de una matriz.
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.
Cada función debe ser declarada apropiadamente para poder interpretarla correctamente durante el proceso de
compilación. La declaración contiene los siguientes elementos:
Note que una función no necesita parámetros (función main() por ejemplo), pero debe estar entre paréntesis.
En caso contrario, el compilador malinterpretaría la función. Para hacerlo más claro, puede sustituir el
espacio en blanco encerrado entre paréntesis por la palabra clave void: main (void).
VALOR DEVUELTO
Una función puede devolver un valor (esto no es obligatorio) por medio de la palabra clave return. Al llegar a
return, la función evalúa un valor (puede ser una expresión) y lo devuelve a la línea de programa desde la que
fue llamada.
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:
En realidad, el compilador reserva memoria para el almacenamiento de variables de la función reverse sólo
durante su ejecución. Una vez completada la ejecución de reverse, la localidad de memoria para la variable i o
para la matriz r ya no está reservada. Esto significa que la dirección que contiene los valores de i o r[] está libre
para introducir datos nuevos. Concretamente, la función devuelve sólo el valor &r[0], así que sólo el primer
elemento de la matriz tab será almacenado en la memoria. Las demás localidades de memoria, tales como
&tab[1], &tab[2], etc. serán consideradas por el compilador como espacios en blanco, o sea, estarán listas para
recibir los nuevos valores.
Para escribir esta función es necesario pasar la matriz r [] como parámetro (vea la subsección Pasar los
parámetros).
La función puede contener más de una sentencia return. En este caso, al ejecutar la primera sentencia return, la
función devuelve el valor correspondiente y se detiene la ejecución de la función.
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:
void wait_1 (unsigned int a)
{
cnt ++; // Incremento de una variable global cnt
Delay_ms(a) ; // Ejecución de la función Delay_ms
} // Note que Delay_ms no devuelve nada
Para utilizar una función, el compilador debe ser consciente de su presencia en el programa. En la programación
en C, los programadores normalmente primero escriben la función main() y luego las funciones adicionales.
Para avisar al compilador de la presencia de las funciones adicionales, se requiere declarar los prototipos de
funciones en el principio de programa antes de la función main(). Un prototipo de función está compuesto por:
tipo de resultado
nombre de función
tipos de parámetros
un punto y coma (;)
Mientras una función es definida y su prototipo declarado, se puede utilizar en culquier parte de programa. Sin
embargo, como la función main es 'raiz' del programa, no puede ser llamada de ninguna parte de programa. Para
ejecutar una función, es necesario escribir su nombre y los parámetros asociados. Vea los siguientes ejemplos:
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.
Al llamar una función, se le pasan los parámetros. En C existen dos formas diferentes para pasar parámetros a
una función.
El primer método, denominado ‘paso por valor’, es el más fácil. En este caso, los parámetros se pueden
considerar como variables locales de la función. Cuando se llama una función, el valor de cada parámetro se
copia a un nuevo espacio de memoria reservado durante la ejecución de la función. Como los parámetros se
consideran como variables locales por el compilador, sus valores pueden ser modificados dentro de la función,
pero sus modificaciones no se quedan en la memoria una vez completada la ejecución de la función.
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.
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
#include Define una sustitución de macro
#undef Quita una definición de nombre de macro
#define Especifica un archivo a ser incluido
#ifdef Prueba para definición de macro
#endif Especificar el final de #if
#ifndef Prueba si una macro no está definida
#if Prueba las condiciones de compilar
#else Especifica alternativas cuando la prueba de #if falla
#elif Especifica alternativas cuando más de dos condiciones se necesitan
Definiciones de macro
Inclusiones de archivos
Control de compilación
Ahora, vamos a presentar sólo las directivas del preprocesador utilizadas con más frecuencia. Sin embargo, no
es necesario saber todas ellas para programar microcontroladores. Sólo tenga en cuenta que el preprocesador es
una herramienta muy poderosa para los programadores avanzados en C, especialmente para el control de
compilación.
Por medio de los macros es posible definir las constantes y ejecutar funciones básicas. Una sustitución de macro
es un proceso en el que un identificador del programa se sustituye por una cadena predefinida. El preprocesador
sustituye cada ocurrencia del identificador en el código fuente por una cadena. Después de la sustitución, el
código será compilado normalmente.
Esto significa que el código sustituido debe respetar la sintaxis del mikroC. La acción se realiza por medio de la
directiva '#define'.
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).
Como todos los microcontroladores, los de familia PIC tienen los registros de funciones especiales (SFR). Para
programar un PIC, es necesario acceder a estos registros (para leerlos o escribir en ellos). Al utilizar el
compilador mikroC PRO for PIC es posible de acceder a cualquier SFR del microcontrolador de cualquier parte
del código (los SFR se consideran como variables globales) sin necesidad de declararlo anteriormente. Los
registros de funciones especiales se definen en un archivo externo e incluido dentro del compilador (archivo
.def). Este archivo contiene todos los SFR del microcontrolador PIC a programar.
TRISB = 0; // todos los pines del puerto PORTB se configuran como salidas
PORTB = 0; // todos los pines del PORTB se ponen a 0
El compilador mikroC PRO for PIC le permite acceder a los bits individuales de variables de 8 bits por su
nombre o su posición en byte:
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.
A veces el proceso de escribir un programa en C requiere las partes del código escritas en ensamblador. Esto
permite ejecutar las partes complicadas del programa de una forma definida con precisión en un período de
tiempo exacto. Por ejemplo, cuando se necesita que los pulsos muy cortos (de unos microsegundos) aparezcan
periódicamente en un pin del microcontrolador. En tales casos la solución más simple sería utilizar el código
ensamblador en la parte del programa que controla la duración de pulsos.
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.
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.
Librarí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 Stdlib Library Contiene las funciones de librerías estándar
ANSI C String Library Utilizada para realizar las operaciones de cadenas y de manipulación de memoria
- librerías misceláneas:
Libraría Descripción
Button Library Utilizada para desarrollar los proyectos
Conversion Library Utilizada para la conversión de tipos de datos
Sprint Library Utilizada para formatear los datos con facilidad
PrintOut Library Utilizada para formatear los datos e imprimirlos
Time Library Utilizada para cálculos de tiempo (formato UNIX time)
Trigonometry Library Utilizada para la implementación de funciones trigonométricas fundamentales
Setjmp Library Utilizada para los saltos de programa
Libraría Descripción
ADC Library Utilizada para el funcionamiento del convertidor A/D
CAN Library Utilizada para las operaciones con el módulo CAN
Utilizada para las operaciones con el módulo CAN externo (MCP2515 o
CANSPI Library
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
Utilizada para las operaciones con el módulo LCD gráfico con resolución
Graphic Lcd Library
128x64
Utilizada para las operaciones con el módulo de comunicación serial I2C
I2C Library
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)
Manchester Code Library Utilizada para la comunicación utilizando el código Manchester
Multi Media Card Library Utilizada para las operaciones con las tarjetas multimedia MMC flash
Utilizada para las operaciones con los circuitos utilizando la comunicación
One Wire Library
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
Utilizada para las operaciones con los módulos utilizando la comunicación
RS-485 Library
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
Sound Library Utilizada para generar las señales de audio
SPI Library Utilizada para las operaciones con el módulo SPI incorporado
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
Utilizada para la comunicación SPI con el LCD gráfico
Library
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
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.
Antes que nada, usted debe instalar el compilador (con su IDE) en la PC. La instalación del mikroC PRO for
PIC es similar a la instalación de cualquier programa en Windows. Todo el procedimiento se lleva a cabo por
medio de los wizards (asistentes de instalación):
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.
Un controlador es un programa que permite al sistema operativo comunicar con un periférico. En este caso, este
dispositivo es el programador (hardware) del sistema de desarrollo.
El controlador a instalar depende del sistema operativo utilizado. Seleccione el controlador correspondiente al
SO (sistema operativo) utilizado (por medio de abrir la carpeta correspondiente) e inicie la instalación. Otra vez,
Next, OK, Next, Next... Bueno, ¡todo está instalado para iniciar a programar!
Al iniciar el IDE del compilador mikroC PRO for PIC por primera vez, aparecerá una ventana como se muestra
a continuación:
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].
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...
Una vez creado el proyecto, es posible manejar todos los archivos que contiene al utilizar la ventana Project
Manager. Basta con pulsar con el botón derecho del ratón sobre una carpeta y seleccionar la opción que necesita
para su proyecto.
PROJECT SETTINGS (CONFIGURACIÓN DE PROYECTOS)
Al compilar un proyecto, el compilador genera el archivo .hex que se cargará en el microcontrolador. Estos
archivos serán diferentes lo que depende del tipo del microcontrolador así como del propósito de la
compilación. Por esta razón es necesario ajustar algunos parámetros de proyectos utilizando la ventana Project
Settings.
Device (dispositivo):
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.
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.
Por ejemplo, si su programa utiliza un LCD no hace falta escribir nuevas funciones ya que al seleccionar la
librería Lcd, usted podrá utilizar funciones listas para ser utilizadas de la librería LCD (Lcd_Cmd, LCD_Init...)
en su programa. Si esta librería no está seleccionada en la ventana Library Manager, cada vez que intente
utilizar una función de la librería LCD, el compilador le informará de un error. Una descripción de cada librería
está disponible al pulsar con el botón derecho del ratón sobre su nombre y seleccionar la opción Help.
El proceso de editar programas se debe realizar dentro de la ventana principal del IDE denominada Code Editor.
Al escribir el programa no se olvide de los comentarios. Los comentarios son muy importantes para depurar y
mejorar el programa. Además, aunque el compilador no tenga las restricciones de formateo, siempre debe seguir
a las mismas reglas de editar (como en los ejemplos proporcionados en este libro). Como no hay limitaciones de
tamaño, no vacile en utilizar los espacios en blanco para hacer su código más legible.
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.
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.
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.
Step Into - Ejecuta una sola instrucción. Cuando la instrucción es una llamada a una rutina, el depurador
hará un salto a la rutina y se detendrá después de ejecutar la primera instrucción dentro de la rutina.
Step Over - Se ejecuta una sola instrucción. Cuando la instrucción es una llamada a una rutina, el
depurador no hará un salto a la rutina, sino que se ejecutará toda la rutina. El depurador se detiene a la
primera instrucción después de la llamada a la rutina.
Run To Cursor - El programa se ejecuta hasta la línea en la que se encuentre el cursor.
Step out - Se ejecutan las demás instrucciones dentro de la rutina. El depurador se detiene
inmediatamente al salir de la rutina.
Los puntos de ruptura hacen el proceso de depurar los programas de una manera más eficiente, puesto que
permiten ejecutar el programa a toda velocidad y detenerlo automáticamente en una línea específica (punto de
ruptura). Eso resulta muy útil, permitiéndole comprobar sólo las partes críticas del programa y no perder el
tiempo probando todo el programa línea a línea. Para añadir o quitar un punto de ruptura basta con pulsar sobre
la línea apropiada en el lado izquierdo del editor del código, o presionar [F5]. Una pequeña ventana denominada
Breakpoints muestra dónde están los puntos de ruptura. Note que las líneas designadas como puntos de ruptura
están marcadas en rojo.
La línea que se está ejecutando actualmente está marcada en azul. Es posible leer el contenido de registros y
variables seleccionados en la ventana Watch Values en cualquier momento. Para ejecutar la parte de programa
desde la línea en la que está el cursor hasta el punto de ruptura, utilice el comando Run/Pause Debugger.
VENTANA WATCH VALUES
El depurador software y hardware tienen la misma función de monitorear el estado de los registros durante la
ejecución del programa. La diferencia es que el depurador software simula ejecución de programa en una PC,
mientras que el depurador ICD (depurador hardware) utiliza un microcontrolador real. Cualquier cambio de
estado lógico de los pines se indica en el registro (puerto) apropiado. Como la ventana Watch Values permite
monitorear el estado de todos los registros, resulta fácil comprobar si un pin está a cero o a uno. La última
modificación está marcada en rojo en la ventana Watch Values. Esto le permite localizar la modificación en la
lista de variables y registros durante el proceso de la depuración.
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...).
El compilador mikroC PRO for PIC proporciona herramientas que en gran medida simplifican el proceso de
escribir el programa. Todas estas herramientas se encuentran en el menú Tools. En la siguiente sección vamos a
darle una breve descripción de todas ellas.
TERMINAL USART
El terminal USART representa una sustitución para la estándar Windows Hyper Terminal. Se puede utilizar
para controlar el funcionamiento del microcontrolador que utiliza la comunicación USART. Tales
microcontroladores están incorporados en un dispositivo destino y conectados al conector RS232 de la PC por
medio de un cable serial.
La ventana USART terminal dispone de opciones para configurar la comunicación serial y visualizar los datos
enviados/ recibidos.
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.
Un editor de siete segmentos le permite determinar con facilidad el número a poner en un puerto de salida con
el propósito de visualizar un símbolo deseado. Por supuesto, se da por entendido que los pines del puerto deben
estar conectados a los segmentos del visualizador de manera apropiada. Basta con colocar el cursor en cualquier
segmento del visualizador y pulsar sobre él. Se visualizará inmediatamente el número a introducir en el
programa.
LCD CUSTOM CHARACTER (CARACTERES LCD DEFINIDOS POR EL USUARIO)
Además de los caracteres estándar, el microcontrolador también puede visualizar los caracteres creados por el
programador. Al seleccionar la herramienta LCD custom character, se evitará un pesado trabajo de crear
funciones para enviar un código apropiado a un visualizador. Para crear un símbolo, pulse sobre los cuadros
pequeños en la ventana LCD custom character, luego seleccione la posición y la fila y pulse sobre el botón
GENERATE. El código apropiado aparece en otra ventana. No es necesita pulsar más. Copy to Clipboard
(copiar al portapapeles) - Paste (pegar)...
El generador de mapa de bits para un LCD gráfico es una herramienta insustituible en caso de que el programa
que escribe utilice el visualizador LCD (GLCD). Esta herramienta le permite visualizar un mapa de bits con
facilidad. Seleccione la opción Tools/Glcd Bitmap Editor aparecerá la ventana apropiada. Para utilizarlo,
seleccione el tipo de visualizador a utilizar y cargue un mapa de bits. El mapa de bits debe ser monocromático y
tener la resolución apropiada del visualizador (128 x 64 píxeles en este ejemplo). El procedimiento a seguir es
igual que en el ejemplo anterior: Copy to Clipboard...
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.