mikroC PRO para PIC está basado en el lenguaje C estándar empleado en la programación de computadoras, por lo tanto se encontrará que hay sólo unas pequeñas diferencias propias de la aplicación en el campo de los microcontroladores. Para aprender programación lo más recomendable es conocer los elementos básicos, los cuales se emplean en la solución de la gran mayoría de problemas.
A continuación se presentan los conceptos fundamentales más importantes con ejemplos resueltos, necesarios para emprender exitosamente el aprendizaje de programación de microcontroladores PIC, utilizando el conocido compilador mikroC PRO para PIC.
La programación en el compilador mikroC PRO se basa en PROYECTOS. Cada proyecto está conformado por un conjunto de archivos interrelacionados entre sí. Para crear un programa primero se debe crear un Proyecto (básicamente se debe seleccionar el microcontrolador, la frecuencia de operación y establecer los bits de configuración); a continuación se procede a escribir las instrucciones del programa en lenguaje C (código fuente); y por último se genera el código de máquina .HEX (código ejecutable) que será almacenado en la memoria de programa del microcontrolador.
En este artículo se explican los detalles de la programación de microcontroladores PIC en lenguaje C, es decir la sintaxis (cómo se deben escribir las instrucciones) y el uso de las instrucciones con ejemplos prácticos. Se recomienda al lector que estudie y repase estas líneas con paciencia y dedicación ya que constituyen la piedra angular sobre la cual descansa la programación de microcontroladores; el dominio de estos conceptos le permitirán a usted convertirse en un experto en la programación de microcontroladores PIC.
¡Descubre el mundo de los microcontroladores PIC y lleva tus habilidades de programación al siguiente nivel! Obtén nuestro eBook y disfruta aprendiendo paso a paso desde cero, podrás realizar proyectos prácticos, encontrar soluciones a problemas comunes y explorar una amplia cobertura de temas desde lo más básico a lo más avanzado. Incluye software y ejemplos resueltos. ¡Cómpralo ahora y libera el increíble potencial de esta poderosa tecnología hoy mismo!
Estructura básica de un programa en mikroC (sin funciones)
Todos los programas (código fuente) de microcontroladores PIC en mikroC tienen una estructura básica, a partir de la cual se desarrolla cualquier aplicación del usuario:
//Nombre_de_programa.c //Descripción del programa. //Autor: Ing. Penagos R. //Declaración de variables ... //Función principal void main( ){ //Instrucciones del programa. ... }
En el código anterior se debe entender lo siguiente:
- Las líneas que comienzan con el doble slash
//
son sólo comentarios que no son necesarios pero sí son muy importantes para documentar (informar) acerca del significado de este código. - La declaración de variables se hace donde aparecen los puntos suspensivos ... (normalmente la mayoría de programas que son de alguna utilidad práctica tienen variables, por lo tanto se debe hacer la declaración correspondiente).
- Las instrucciones del programa (las instrucciones que va a ejecutar o realizar el microcontrolador) se colocan donde van los puntos suspensivos ... (dentro de la función main)
Los 7 elementos básicos de la programación en mikroC PRO para PIC
La programación de PIC en C se puede comprender mejor si se estudian sus elementos básicos; una vez que se dominen estos elementos se podrá dar solución a la gran mayoría de problemas de programación. El propósito de la mayoría de los programas es resolver un problema. Los programas resuelven los problemas por medio de la manipulación de información o datos. Normalmente los programas se caracterizan por permitir el ingreso de información, tener uno o varios lugares de almacenamiento de dicha información, contar con las instrucciones para manipular estos datos y obtener algún resultado del programa que sea útil para el usuario. También, las instrucciones se pueden organizar de tal forma que algunas de ellas se ejecuten sólo cuando una condición específica (o conjunto de condiciones) sea verdadera, otras instrucciones se repitan un cierto número de veces y otras pueden ser agrupadas en bloques que se ejecutan en diferentes partes de un programa.
Lo anterior constituye una breve descripción de los siete elementos básicos de la programación: entrada de datos, tipos de datos, operaciones, salida, ejecución condicional, lazos y funciones. Una vez que se dominan estos elementos se puede afirmar que se conocen los fundamentos de la programación, con lo cual ya es posible desarrollar una gran cantidad de aplicaciones de diversa índole.
Instrucciones básicas
1.- Instrucción de asignación (=)
Permite asignar a una variable un valor constante, el contenido de otra variable o el resultado de una expresión matemática. La asignación va de derecha a izquierda. Por ejemplo,
suma=0; //El valor 0 se almacena en la variable suma. x0=x1; //El contenido de la variable x1 se almacena en la variable x0. dx=(b-a)/n; //El resultado de la expresión matemática se almacena en la variable dx.
2.- Instrucción de entrada de datos (variable=PORTx)
Permite el ingreso de uno o más datos a través de los pines del microcontrolador y almacenarlos en una o más variables. Por ejemplo,
variable=PORTA; //Los bits del puerto A se almacenan en la variable.
El siguiente es un caso especial utilizado en la programación de microcontroladores PIC en C:
PORTB=PORTA; //Los bits del puerto A se envían hacia los pines del puerto B.
También se puede leer el estado individual de cada bit de un puerto:
variable=RB3_bit; //Lee el estado del pin RB3 y lo guarda en la variable.
3.- Instrucción de salida de datos (PORTx=dato)
Permite el envío de datos, el contenido de una variable o el resultado de una expresión matemática hacia los pines de un puerto. Por ejemplo,
PORTA=0x00; //Todos los pines del puerto A se ponen en 0. PORTB=variable; Los bits de la variable son enviados hacia los pines del puerto B. PORTB=PORTA+65; //El valor del puerto A más 65 se envía hacia el puerto B.
Como caso especial, se pueden enviar bits individuales a cada uno de los pines de un puerto:
RB0_bit=0; //El pin RB0 se pone en 0.
4.- Instrucción de decisión (if...else)
Permite la ejecución de las instrucciones1 si la condición es verdadera, de lo contrario se ejecutan las instrucciones2. Las llaves { }
no son necesarias cuando hay una sola instrucción.
if (condición){ instrucciones1; } else{ instrucciones2; }
Ejemplo 1:
Si el contenido de la variable codigo
es igual al contenido de la variable clave
, se ejecutan las primeras cuatro instrucciones; de lo contrario se ejecutan únicamente los dos últimas instrucciones.
if (codigo==clave){ intentos=0; RA7_bit=1; Delay_1sec( ); RA7_bit=0; } else{ intentos++; Delay_ms(200); }
Ejemplo 2:
Instrucción de decisión sin else. Esta es una variante muy utilizada cuando se desea condicionar la ejecución de un grupo de instrucciones.
Las dos instrucciones se ejecutarán únicamente si la variable contador
es igual a 2, de lo contrario la ejecución continúa a partir de la línea //Aquí
.
if (contador==2){ RB6_bit=~RB6_bit; contador=0; } //Aquí.
Ejemplo 3:
Similar al caso anterior pero con una sola instrucción. Si la variable horas
es igual a 24 se reinicia esta variable con un valor de cero.
if (horas==24) horas=0;
Nota 1: Las condiciones se obtienen por medio de los operadores de relación y los operadores lógicos.
Nota 2: Operadores de relación:
> Mayor que
>= Mayor o igual que
< Menor que
<= Menor o igual que
== Igual a (nótese la diferencia con el operador de asignación =)
!= No es igual a
Nota 3: Operadores lógicos:
&& Y
|| O
5.- Instrucción de ciclo controlado por una variable (for)
Permite ejecutar un grupo de instrucciones de manera repetitiva, una determinada cantidad de veces.
for (número de veces){ instrucciones; }
Ejemplo 1:
La variable i
tiene un valor inicial de 7 (i=7
) y un valor final de 1 (i>=1
). Esta variable va disminuyendo de 1 en 1 (i--
). Por lo tanto las dos instrucciones se ejecutarán en 7 ocasiones. La primera vez cuando i=7, la segunda cuando i=6, la tercera cuando i=5 y así sucesivamente hasta la séptima vez cuando i=1. Luego la ejecución continúa a partir de la línea //Aquí
.
for (i=7; i>=1; i--){ PORTB=PORTB<<1; Delay_ms(500); } //Aquí.
Ejemplo 2:
El valor inicial de la variable es 1 y su valor final es 3. La variable i
se va incrementando de 1 en 1 (i++
). Por lo tanto la instrucción se ejecuta tres veces, lo que da como resultado un retardo de 3 segundos. Luego la ejecución continúa a partir de la línea //Aquí
.
for (i=1; i<=3; i++) Delay_1sec( ); //Aquí.
6.- Instrucción iterativa condicional (while)
Permite ejecutar un grupo de instrucciones de manera repetitiva, mientras una condición sea verdadera. Primero se revisa la condición para determinar su valor de verdad (verdadero o falso) y luego se ejecutan las instrucciones.
while (condición){ instrucciones; }
Ejemplo 1:
La ejecución del programa permanece indefinidamente en esta línea mientras el bit IOFS del registro OSCCON sea igual a cero. Como caso particular no se ejecuta ninguna instrucción (la cual debería estar antes del punto y coma).
while (OSSCON.IOFS==0) ;
Ejemplo 2:
Ejemplo de un lazo infinito. En mikroC PRO, cualquier valor numérico diferente a cero se considera VERDADERO, y un valor numérico igual a cero se considera FALSO.
Al valor numérico del puerto A se le suma el valor 65, el resultado se envía hacia los pines del puerto B. Este proceso se repite continua e indefinidamente, debido a que la condición siempre es verdadera (1).
while (1) PORTB=PORTA+65;
Ejemplo 3:
Las cuatro instrucciones encerradas por { }
se ejecutarán indefinidamente mientras el valor del bit RB0 sea igual a 0.
while (RB0_bit==0){ RB1_bit=1; Delay_ms(500); RB1_bit=0; Delay_ms(200); }
7.- Instrucción hacer-mientras (do...while)
Permite ejecutar un grupo de instrucciones de manera repetitiva, mientras una condición sea verdadera. Es similar a la instrucción while, con la diferencia de que primero se ejecutan las instrucciones y luego se revisa la condición.
do{ instrucciones; } while (condición);
Ejemplo 1:
La variable kp
tiene un valor inicial de cero. La instrucción kp=Keypad_Key_Click( );
se ejecuta y luego se revisa la condición (!kp
).Mientras kp
sea igual a cero (FALSO) la condición será VERDADERA (!kp
), debido al operador de negación !
que cambia el valor de verdad a su estado contrario. Como resultado se tendrá un lazo infinito mientras la variable kp
siga en cero. Cuando la variable kp
cambie de valor como consecuencia de la pulsación de una tecla, la condición será FALSA y la ejecución continuará en la línea //Aquí
.
kp=0; do kp=Keypad_Key_Click( ); while (!kp); //Aquí.
Ejemplo 2:
Las cuatro instrucciones dentro de { }
se ejecutarán indefinidamente mientras la variable tecla
sea diferente a 1.
do{ ingreso( ); raiz( ); pn_1( ); seg_grado( ); } while (tecla != 1);
Nota: A diferencia de la instrucción while
, en la instrucción do...while
las instrucciones se ejecutan por lo menos una vez.
8.- Instrucción de selección múltiple (switch)
Permite la ejecución de un grupo de instrucciones de varios grupos posibles, dependiendo del valor de una variable.
switch (variable){ case 1: instrucciones1; break; case 2: instrucciones2; break; case 3: instrucciones3; break; … default: instrucciones; }
Si la variable es igual a 1 se ejecutan únicamente las instrucciones1, si es igual a 2 se ejecutan únicamente las instrucciones2 y así sucesivamente. Si la variable no es igual a ninguno de los casos (case) se ejecutan las instrucciones por defecto (default).
Ejemplo 1:
Esta es una función numérica que da como resultado un número hexadecimal dependiendo del valor que tenga la variable digit
. Si digit
es igual a 0 la función devuelve (return) el valor 0x3F. Si digit
es igual a 1, la función devuelve el valor 0x06, y así sucesivamente. Este ejemplo es una variante de la instrucción switch
, donde no aparece el elemento default
.
char Bin2_7seg(char digit){ switch (digit){ case 0: return 0x3F; case 1: return 0x06; case 2: return 0x5B; case 3: return 0x4F; case 4: return 0x66; } }
Funciones
Una función es una agrupación de instrucciones para formar una nueva instrucción creada por el programador (usuario). Empleando funciones, la solución total de un determinado problema se divide en varios subproblemas, cada uno de los cuales es resuelto por medio de una función particular, aplicando de esta manera la conocida máxima Divide y vencerás.
Las funciones constituyen una de las características fundamentales de mikroC PRO, pues todo programa bien escrito hace uso de ellas. Para poder utilizar una función se deben cumplir los dos siguientes pasos:
1.-Declaración de la función.- Consiste en indicar el tipo, nombre y parámetros de la función:
tipo nombre ( parámetro1, parámetro2, ...);
2.-Definición de la función.- Consiste en indicar las instrucciones que forman parte de dicha función:
tipo nombre ( parámetro1, parámetro2, ...){ instrucciones; }
Estructura básica de un programa en mikroC PRO (con funciones)
Todos los programas (código fuente) en mikroC PRO tienen una estructura básica, a partir de la cual se desarrolla cualquier aplicación del usuario:
//Nombre_de_programa.c //Descripción del programa. //Autor: Ing. Penagos R. //***************************Declaración de funciones (prototipos)************************** ... //******************************Fin de declaración de funciones***************************** //==================================Declaración de variables================================ ... //===============================Fin de declaración de variables============================ //**********************************Función principal******************************** void main( ){ //Instrucciones del programa. ... } //********************************Fin de función principal*************************** //================================Definición de funciones============================ función1{ instrucciones1; } función2{ instrucciones2; } //============================Fin de definición de funciones==========================
Nota 1: Los tipos de funciones más empleadas son numéricas char
y nulas void
. Las primeras retornan return
o devuelven como resultado un número, mientras que las segundas simplemente ejecutan un grupo de instrucciones.
Ejemplo 1:
En este ejemplo la función es numérica tipo char
, su nombre es Bin2_7seg
y tiene un parámetro digit
de tipo char
.
La función se utiliza como si fuera una instrucción cualquiera, tomando en cuenta el tipo de función y su(s) parámetro(s). En este ejemplo se tiene PORTB=Bin2_7seg(PORTA)
. Esto significa que la variable PORTA remplaza a la variable digit
. Por lo tanto si PORTA es igual a 0, la función devuelve el valor 0x3F
que será enviado al puerto B. Si PORTA es igual a 1, la función devuelve 0x06
que será enviado al puerto B, y así sucesivamente.
//7seg1.c //Se utiliza la función Bin2_7seg que transforma un número binario a su //equivalente en 7 segmentos. char Bin2_7seg(char digit); //Prototipo de la función. void main(){ OSCCON=0x40; //Oscilador interno a 1MHz. while (OSCCON.IOFS==0);//Esperar mientras el oscilador está inestable. PORTA=0x00; //Inicialización. PORTB=0x00; ANSEL=0x00; //Pines AN<6:0> como E/S digital. TRISB=0x00; //Puerto B como salida. while (1) PORTB=Bin2_7seg(PORTA); } char Bin2_7seg(char digit){ //Definición de la función. switch (digit){ case 0: return 0x3F; //0x3F es el código 7-segmentos del 0. case 1: return 0x06; //0x06 es el código 7-segmentos del 1. case 2: return 0x5B; case 3: return 0x4F; case 4: return 0x66; case 5: return 0x6D; case 6: return 0x7D; case 7: return 0x07; case 8: return 0x7F; case 9: return 0x67; } }
Ejemplo 2: Trabajo simplificado con funciones.
Variante del ejemplo anterior, en el que se hace únicamente la definición de la función (sin declaración). Se debe hacer antes de la función principal, de lo contrario se producirán errores de compilación por tratar de usar una función desconocida. Este método de uso de funciones es muy popular ya que simplifica notablemente la escritura de los programas asi como su mantenimiento.
//7seg1.c //Se utiliza la función Bin2_7seg que transforma un número binario a su //equivalente en 7 segmentos. char Bin2_7seg(char digit){ //Definición de la función. switch (digit){ case 0: return 0x3F; //0x3F es el código 7-segmentos del 0. case 1: return 0x06; //0x06 es el código 7-segmentos del 1. case 2: return 0x5B; case 3: return 0x4F; case 4: return 0x66; case 5: return 0x6D; case 6: return 0x7D; case 7: return 0x07; case 8: return 0x7F; case 9: return 0x67; } } void main(){ OSCCON=0x40; //Oscilador interno a 1MHz. while (OSCCON.IOFS==0);//Esperar mientras el oscilador está inestable. PORTA=0x00; //Inicialización. PORTB=0x00; ANSEL=0x00; //Pines AN<6:0> como E/S digital. TRISB=0x00; //Puerto B como salida. while (1) PORTB=Bin2_7seg(PORTA); }
Detalles importantes de mikroC PRO para PIC para tener en cuenta
En la programación de PIC en C existen pequeños detalles que se deben tener muy en cuenta y que permitirán que los programas realizados cumplan las tareas para los cuales fueron diseñados. Con respecto a los comentarios, se puede decir que son importantes aunque no son necesarios. Su importancia radica en que ofrecen una mayor facilidad para entender el código y realizar su mantenimiento (esto es especialmente cierto cuando ha pasado el tiempo y necesitamos realizar alguna modificación).
- Los comentarios se inician con la doble barra diagonal
//
. - Los signos de agrupación siempre deben estar en pareja, es decir si hay tres llaves de apertura
{{{
, deben haber tres llaves de cierre correspondientes}}}
. Lo mismo con los paréntesis( )
. - Los números hexadecimales se escriben comenzando siempre con 0x, por ejemplo
0x0A
,0x16
,0xFD
, etc. - Los números binarios se escriben comenzando siempre con 0b, por ejemplo
0b001110
,0b11101111
, etc. - Los números decimales se escriben de la forma común y corriente, por ejemplo 64, 126, 12.75, etc.
- No se debe confundir el operador de asignación
=
con el operador de comparación==
igual a. - El punto y coma
;
indica el final de una instrucción, por lo tanto hay que tener mucho cuidado para colocarlo en el lugar apropiado. - Las llaves
{ }
no son necesarias en aquellos casos en los que únicamente se va a ejecutar una instrucción (ver los ejemplos a lo largo de este apartado). - Todo programa en mikroC PRO debe tener una función principal
main
, y su nombre no debe ser cambiado.
Los tipos de datos más utilizados se muestran en la siguiente tabla.
El tipo float
es para números con punto decimal, mientras que los demás tipos son para números enteros.
La EJECUCIÓN DE UNA INSTRUCCIÓN consiste en la realización de las operaciones especificadas en esa instrucción. De la ejecución se encarga la CPU (unidad central de proceso) del microcontrolador PIC.