Del libro
Cómo programar en
lenguaje C los microcontroladores PIC16F88, 16F628A y
16F877A. 2da edición.
Regístrate aquí
para descargar una muestra gratis de este libro
En esta sección:
CONCEPTOS BÁSICOS DE LENGUAJE C PARA PIC
A continuación se presentan los conceptos
fundamentales más importantes, necesarios para
emprender exitosamente el aprendizaje de
programación de microcontroladores PIC.
Estructura básica de un
programa en lenguaje C (sin funciones)
Todos los programas (código fuente) en lenguaje C
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.
...
}
Los
siete elementos básicos de la
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 de programación
de microcontroladores PIC con C
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:
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 lenguaje C, 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: Programación en lenguaje C para PIC
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 del lenguaje C,
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 lenguaje C (con
funciones).-
Todos los programas (código fuente) en lenguaje C
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:
Este ejemplo se puede encontrar en el capítulo
II, Puertos Digitales, del libro. La función es
numérica (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:
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.
//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 del lenguaje C para tener en
cuenta
- 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 lenguaje C 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 tabla 1.5.
Tabla 1.5 Tipos de
datos más comunes en lenguaje C
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.