Академический Документы
Профессиональный Документы
Культура Документы
Para entender con más facilidad las razones del éxito tan grande de los
microcontroladores, vamos a prestar atención al siguiente ejemplo. Hace unos
10 años, diseñar un dispositivo electrónico de control de un ascensor de un
edificio de varios pisos era muy difícil, incluso para un equipo de expertos. ¿Ha
pensado alguna vez en qué requisitos debe cumplir un simple ascensor?
¿Cómo lidiar con la situación cuando dos o más personas llaman al ascensor al
mismo tiempo? ¿Cuál llamada tiene la prioridad? ¿Cómo solucionar las
cuestiones de seguridad, de pérdida de electricidad, de fallos, de uso indebido?
Lo que sucede después de resolver estos problemas básicos es un proceso
meticuloso de diseñar los dispositivos adecuados utilizando un gran número de
los chips especializados. Este proceso puede tardar semanas o meses,
dependiendo de la complejidad del dispositivo. Cuando haya terminado el
proceso, llega la hora de diseñar una placa de circuito impreso y de montar el
dispositivo.¡Un dispositivo enorme! Es otro trabajo difícil y tardado. Por último,
cuando todo está terminado y probado adecuadamente, pasamos al momento
crucial y es cuando uno se concentra, respira profundamente y enciende la
fuente de alimentación.
¡La matemática es una gran ciencia! Todo es tan lógico y simple... El universo
de los números se puede describir con sólo diez dígitos. No obstante,
¿realmente tiene que ser así? ¿Necesitamos exactamente esos 10 dígitos? Por
supuesto que no, es sólo cuestión del hábito. Acuérdese de las lecciones de la
escuela. Por ejemplo, ¿qué significa el número 764? Cuatro unidades, seis
decenas y siete centenas. ¡Muy simple! ¿Se podría expresar de una forma más
desarrollada? Por supuesto que sí: 4 + 60 + 700. ¿Aún más desarrollado? Sí:
4*1 + 6*10 + 7*100. ¿Podría este número parecer un poco más “científico”? La
respuesta es sí otra vez: 4*100 + 6*101 + 7*102. ¿Qué significa esto
realmente? ¿Por qué utilizamos exactamente estos números 100, 101 y 102 ?
¿Por qué es siempre el número 10? Es porque utilizamos 10 dígitos diferentes
(0, 1, 2...8, 9). En otras palabras, es porque utilizamos el sistema de
numeración en base 10, es decir el sistema de numeración decimal.
SISTEMA DE NUMERACIÓN BINARIO
Una solución mucho más fácil es una lógica binaria donde 0 indica la ausencia
de voltaje, mientras que 1 indica la presencia de voltaje. Simplemente, es fácil
de escribir 0 o 1 en vez de “no hay voltaje” o “ hay voltaje”. Mediante el cero
lógico (0) y uno lógico (1) la electrónica se enfrenta perfectamente y realiza con
facilidad todas las operaciones aritméticas. Evidentemente, se trata de
electrónica que en realidad aplica aritmética en la que todos los números son
representados con sólo dos dígitos y donde sólo es importante saber si hay
voltaje o no. Por supuesto, estamos hablando de electrónica digital.
CÓDIGO BCD
Cabe destacar que es necesario utilizar sólo dos dígitos binarios para
representar a todos los números decimales de 0 a 3. Por consiguiente, para
representar los números de 0 a 7 es necesario utilizar tres dígitos binarios, para
representar los números de 0 a 15 - cuatro dígitos etc. Dicho de manera
sencilla, el mayor número binario que se puede representar utilizando n dígitos
se obtiene al elevar la base 2 a la potencia n. Luego, al resultado se le resta 1.
Por ejemplo, si n=4:
24 - 1 = 16 - 1 = 15
Por consiguiente, al utilizar 4 dígitos binarios, es posible representar los
números decimales de 0 a 15, que son 16 valores diferentes en total.
NÚMEROS NEGATIVOS
BIT
No se confunda si se encuentra con un bit que tiene el valor 4, 16 o 64. Son los
valores representados en el sistema decimal. Simplemente, nos hemos
acostumbrado tanto a utilizar los números decimales que estas expresiones
llegaron a ser comunes. Sería correcto decir por ejemplo, “el valor del sexto bit
en cualquier número binario equivale al número decimal 64”. Pero todos somos
humanos y los viejos hábitos mueren difícilmente. Además, ¿cómo le suena
“número uno-uno-cero-uno-cero...”?
BYTE
COMPUERTA Y (AND)
Una compuerta lógica “Y” dispone de dos o más entradas y de una salida. En
este caso la compuerta utilizada dispone de sólo dos entradas. Un uno lógico
(1) aparecerá en su salida sólo en caso de que ambas entradas (A Y B) sean
llevadas a alto (1). La tabla a la derecha es la tabla de verdad que muestra la
relación entre las entradas y salidas de la compuerta. El principio de
funcionamiento es el mismo cuando la compuerta disponga de más de dos
entradas: la salida proporciona un uno lógico (1) sólo si todas las entradas son
llevadas a alto (1).
COMPUERTA O (OR)
De manera similar, la compuerta O también dispone de dos o más entradas y
de una salida. Si la compuerta dispone de sólo dos entradas, es aplicable lo
siguiente: la salida proporciona un uno lógico (1) si una u otra entrada (A o B)
es llevada a alto (1). En caso de que la compuerta O disponga de más de dos
entradas, es aplicable lo siguiente: La salida proporciona un uno lógico (1) si
por lo menos una entrada es llevada a alto (1). Si todas las entradas están a
cero lógico (0), la salida estará a cero lógico (0) también.
COMPUERTA NO (NOT)
La compuerta lógica NO dispone de una sola entrada y una sola salida, por lo
que funciona muy simplemente. Cuando un cero lógico (0) aparezca en su
entrada, la salida proporciona un uno lógico (1) y viceversa. Esto significa que
esta compuerta invierte las señales por sí mismas y por eso es denominada
inversor.
En el programa la operación lógica NO se realiza sobre un byte. El resultado es
un byte con los bits invertidos. Si los bits de un byte se consideran número, el
valor invertido es un complemento a ese número. El complemento de un
número es el valor que se añade al número hasta llegar al mayor número
binario de 8 dígitos. En otras palabras, la suma de un dígito de 8 números y de
su complemento es siempre 255.
REGISTROS
REGISTROS SFR
OTP ROM (One Time Programmable ROM) - ROM programable una sola
vez
Memoria Flash
INTERRUPCIÓN
Como indica su nombre, esto es una unidad que controla todos los procesos
dentro del microcontrolador. Consiste en varias unidades más pequeñas, de las
que las más importantes son:
BUS
El bus está formado por 8, 16 o más cables. Hay dos tipos de buses: el bus de
direcciones y el bus de datos. El bus de direcciones consiste en tantas líneas
como sean necesarias para direccionar la memoria. Se utiliza para transmitir la
dirección de la CPU a la memoria. El bus de datos es tan ancho como los
datos, en este caso es de 8 bits o líneas de ancho. Se utiliza para conectar
todos los circuitos dentro del microcontrolador.
COMUNICACIÓN EN SERIE
La conexión paralela entre el microcontrolador y los periféricos a través de los
puertos de entrada/salida es una solución perfecta para las distancias cortas -
hasta varios metros. No obstante, en otros casos cuando es necesario
establecer comunicación entre dos dispositivos a largas distancias no es
posible utilizar la conexión paralela. En vez de eso, se utiliza la conexión en
serie.
Este tipo de conexión es asíncrona, lo que significa que no se utiliza una línea
especial para transmitir la señal de reloj. En algunas aplicaciones este rasgo es
crucial (por ejemplo, en mandar datos a distancia por RF o por luz infrarroja).
Puesto que se utiliza sólo una línea de comunicación, tanto el receptor como el
transmisor reciben y envían los datos a velocidad misma que ha sido
predefinida para mantener la sincronización necesaria. Esto es una manera
simple de transmitir datos puesto que básicamente representa una conversión
de datos de 8 bits de paralelo a serial. La velocidad de transmisión no es alta,
es hasta 1 Mbit/sec.
OSCILADOR
Los pulsos uniformes generados por el oscilador permiten el funcionamiento
armónico y síncrono de todos los circuitos del microcontrolador. El oscilador se
configura normalmente de tal manera que utilice un cristal de cuarzo o
resonador cerámico para estabilización de frecuencia. Además, puede
funcionar como un circuito autónomo (como oscilador RC). Es importante decir
que las instrucciones del programa no se ejecutan a la velocidad impuesta por
el mismo oscilador sino varias veces más despacio. Eso ocurre porque cada
instrucción se ejecuta en varios ciclos del oscilador. En algunos
microcontroladores se necesita el mismo número de ciclos para ejecutar todas
las instrucciones, mientras que en otros el tiempo de ejecución no es el mismo
para todas las instrucciones. Por consiguiente, si el sistema utiliza el cristal de
cuarzo con una frecuencia de 20 MHZ, el tiempo de ejecución de una
instrucción de programa no es 50 nS, sino 200, 400 o 800 nS dependiendo del
tipo del microcontrolador.
CIRCUITO DE ALIMENTACIÓN
Hay que mencionar dos cosas dignas de atención con relación al circuito de la
fuente de alimentación de microcontroladores:
CONTADORES
Si el perro guardián está habilitado, cada vez que cuenta hasta el máximo valor
en el que ocurre el desbordamiento del registro se genera una señal de reinicio
del microcontrolador y la ejecución de programa inicia en la primera instrucción.
El punto es evitar que eso ocurra al utilizar el comando adecuado.
CONVERTIDOR A/D
Las señales del mundo real son muy diferentes de las que “entiende” el
microcontrolador (ceros y unos), así que deben ser convertidas para que el
microcontrolador pueda entenderlas. Un convertidor analógico-digital es un
circuito electrónico encargado de convertir las señales continuas en números
digitales discretos. En otras palabras, este circuito convierte un número real en
un número binario y se lo envía a la CPU para ser procesado. Este módulo se
utiliza para medir el voltaje en el pin de entrada.
Arquitectura de von-Neumann
ARQUITECTURA DE HARVARD
256 - 18 –
PIC18FXXX 4 - 128 32 - 48 4 - 16 10 or 12 0-3
3936 80
1024 - 28 -
PIC18FXXJXX 8 - 128 40 - 48 10 - 16 10 2
3936 100
768 - 28 –
PIC18FXXKXX 8 - 64 64 10 - 13 10 2
3936 44
Todos los microcontroladores PIC utilizan una arquitectura Harvard, lo que
quiere decir que su memoria de programa está conectada a la CPU por más de
8 líneas. Hay microcontroladores de 12, 14 y 16 bits, dependiendo de la
anchura del bus. La tabla anterior muestra las características principales de
estas tres categorías.
JUEGO DE INSTRUCCIONES
LENGUAJE ENSAMBLADOR
Lenguaje C
Este libro describe una aplicación muy concreta del lenguaje de programación
C utilizado en el compilador mikroC PRO for PIC. En este caso, el compilador
se utiliza para la programación de los microcontroladores PIC.
FASES DE COMPILACIÓN
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.
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.
T AM A Ñ O
TIPO DE
DESCRIPCIÓN (NÚMERO DE R AN G O D E V AL O R E S
D AT O
BITS)
char Texto (caracteres) 8 de 0 a 255
int Valores enteros 16 de -32768 a 32767
de ±1.17549435082·10-38 a
float Valores en punto flotante 32
±6.80564774407·1038
Valores en punto flotante de de ±1.17549435082·10-38 a
double 32
doble precisión ±6.80564774407·1038
*Debido a las limitaciones impuestas por el hardware del microcontrolador, es
imposible alcanzar una mayor precisión de datos que la del tipo float. Por eso,
el tipo double en mikroC equivale al tipo float.
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
59 // entero
'p' // carácter ASCII 'p'
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.
En mikroC, los identificadores pueden ser tan largos como quiera. Sin
embargo, hay varias restricciones:
M I K R O C - P AL A B R AS C L AV E
absolute data if return typedef
asm default inline rx typeid
at delete int sfr typename
auto do io short union
bit double long signed unsigned
bool else mutable sizeof using
break enum namespace static virtual
case explicit operator struct void
catch extern org switch volatile
char false pascal template while
class float private this
code for protected throw
const friend public true
continue goto register try
Ejemplos de los identificadores válidos e inválidos:
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:
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:
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
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:
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
OPERADORES ARITMÉTICOS
O P E R AD O R O P E R AC I Ó N
+ Adición
- Resta
* Multiplicación
/ División
% Resto de la división
OPERADORES DE ASIGNACIÓN
O P E R AD O R EJEMPLO DESCRIPCIÓN
++a
++ Variable "a" es incrementada por 1
a++
--b
-- Variable "a" es decrementada por 1
b--
int a, b, c;
a = b = 5;
c = 1 + a++; // c = 6
b = ++c + a // b = 7 + 6 = 13
OPERADORES RELACIONALES
O P E R AD O R DESCRIPCIÓN EJEMPLO C O N D I C I Ó N D E V E R AC I D AD
> mayor que b>a si b es mayor que a
>= mayor o igual que a >= 5 si a es mayor o igual que 5
< menor que a<b si a es menor que b
<= menor o igual que a <= b si a es menor o igual que b
== igual que a == 6 si a es igual que 6
!= desigual que a != b si a es desigual que b
int prop;
int var = 5;
prop = var < 10; // Expresión es evaluada como verdadera, prop = 1
OPERADORES LÓGICOS
O P E R AD O R FUNCIÓN
&& Y
|| O
! NO
OPERADORES DE MANEJO DE BITS
O P E R AD O R DESCRIPCIÓN EJEMPLO R E S U L T AD O
~ Complemento a uno a = ~b b=5 a = -5
<< Desplazamiento a la izquierda a = b << 2 b = 11110011 a = 11
>> Desplazamiento a la derecha a = b >> 2 b = 11110011 a = 00
a = 11100011
& Y lógico para manejo de bits c=a&b c = 11
b = 11001100
| O lógico para manejo de bits c=a|b a = 11100011 c = 11
b = 11001100
a = 11100011
^ EXOR lógico para manejo de bits c=a^b c = 00
b = 11001100
Note que el resultado de la operación de desplazamiento a la derecha depende
del signo de la variable. En caso de que el operando se aplique a una variable
sin signo o positiva, se introducirán los ceros en el espacio vacío creado por
desplazamiento. Si se aplica a un entero con signo negativo, se introducirá un 1
para mantener el signo correcto de la variable.
¿CÓMO UTILIZAR LOS OPERADORES?
Aparte de los operadores de asignación, dos operadores no deben estar
escritos uno junto al otro.
int a, b, res;
a = 10;
b = 100;
res = a*(a + b); // resultado = 1100
res = a*a + b; // resultado = 200
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.
if(expresión) operación;
if(expresión)
operación1
else
operación2
if(expresión) {
... //
... // operación1
...} //
else
operación2
Operador Switch
break;
case constante2:
break;
...
default:
Bucle While
while(expresión){
comandos
...
}
Bucle For
Bucle Do-while
do
operación
while (cambiar_condición);
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
SENTENCIA CONTINUE
SENTENCIA GOTO
...
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.
// 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
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
int Tabla [3][4]; // Tabla se define de modo que tenga 3 filas y 4 columnas
3 42 1
7 7 19
PUNTEROS
tipo_de_variable *puntero;
puntero = &variable;
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.
struct nombre_de_estructura {
tipo1_de_miembro1 miembro1;
tipo2_de_miembro2 miembro2;
tipo3_de_miembro3 miembro3;
..
};
struct generador {
int voltaje;
char corriente;
};
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.
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
..
Esto significa que f es una función que recibe un número real x como
parámetro y devuelve 2*x-y.
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.
Para escribir esta función es necesario pasar la matriz r [] como parámetro (vea
la subsección Pasar los parámetros).
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:
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.
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.
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);
}
D I R E C T I V AS 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'.
Tanque_1 = (((Diámetro/2)*(Diámetro/2)*PI)*altura;
INCLUSIÓN DE ARCHIVOS
Como ya hemos visto, hay varias divergencias entre los lenguajes mikroC y
ANSI C. En este capítulo vamos a presentar las características específicas del
mikroC con el propósito de facilitar la programación de los microcontroladores
PIC.
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:
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.
asm
{
instrucciones en ensamblador
...
}
FUNCIÓN DE INTERRUPCIÓN
void interrupt() {
cnt++ ; // Al producirse una interrupción
// la cnt se incrementa en 1
PIR1.TMR1IF = 0; // Poner a 0 el bit TMR1IF
}
L I B R AR Í A DESCRIPCIÓN
ANSI C Ctype
Utilizada principalmente para probar o para convertir los datos
Library
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 Utilizada para realizar las operaciones de cadenas y de manipula
Library memoria
- librerías misceláneas:
L I B R AR Í 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 fundamen
Setjmp Library Utilizada para los saltos de programa
- librerías para el hardware:
L I B R AR Í 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 (MC
CANSPI Library
MCP2510)
Utilizada para las operaciones con las tarjetas de memoria C
Compact Flash Library
Flash
EEPROM Library Utilizada para las operaciones con la memoria EEPROM incorpo
EthernetPIC18FxxJ60 Library Utilizada para las operaciones con el módulo Ethernet incorporad
Flash Memory Library Utilizada para las operaciones con la memoria Flash incorporada
Utilizada para las operaciones con el módulo LCD gráfico con re
Graphic Lcd Library
128x64
I2C Library Utilizada para las operaciones con el módulo de comunicación s
incorporado
Keypad Library Utilizada para las operaciones con el teclado (botones de presión
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 f
Utilizada para las operaciones con los circuitos utiliza
One Wire Library
comunicación serial One Wire
Port Expander Library Utilizada para las operaciones con el extensor de puertos MCP23
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 utiliza
RS-485 Library
comunicación serial RS485
Software I2C Library Utilizada para simular la comunicación I2C con software
Software SPI Library Utilizada para simular la comunicación SPI con software
Software UART Library Utilizada para simular la comunicación UART con software
Sound Library Utilizada para generar las señales de audio
SPI Library Utilizada para las operaciones con el módulo SPI incorporado
Utilizada para la comunicación SPI con el módulo ETH
SPI Ethernet Library
(ENC28J60)
SPI Graphic Lcd Library Utilizada para la comunicación SPI de 4 bits con el LCD gráfico
Utilizada para la comunicación SPI de 4 bits con el LCD (d
SPI LCD Library
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
2.11 PROGRAMAR LOS PIC UTILIZANDO MIKROC PRO FOR PIC
Aparte de todas las características comunes de cualquier IDE, mikroC PRO for
PIC contiene las informaciones de arquitectura de los microcontroladores PIC
(registros, módulos de memoria, funcionamiento de circuitos particulares etc.)
para compilar y generar un archivo legible por un microcontrolador PIC.
Además, incluye las herramientas específicas para programar los
microcontroladores PIC.
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.
Al iniciar el IDE del compilador mikroC PRO for PIC por primera vez, aparecerá
una ventana como se muestra a continuación:
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.
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.
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.
DEPURAR EL PROGRAMA
PROGRAMAR EL MICROCONTROLADOR
TERMINAL USART
EDITOR EEPROM
Capitulo 4: Ejemplos
Alimentación;
Señal de reinicio; y
Señal de reloj.
Como se muestra en la figura anterior, se trata de circuitos simples, pero no
tiene que ser siempre así. Si el dispositivo destino se utiliza para controlar las
máquinas caras o para mantener funciones vitales, todo se vuelve mucho más
complicado.
ALIMENTACIÓN
SEÑAL DE REINICIO
SEÑAL DE RELOJ
¿Por qué son estos modos importantes? Como es casi imposible construir un
oscilador estable que funcione a un amplio rango de frecuencias, el
microcontrolador tiene que “saber” a qué cristal está conectado, para poder
ajustar el funcionamiento de sus componentes internos. Ésta es la razón por la
que todos los programas utilizados para escribir un programa en el chip
contienen una opción para seleccionar el modo de oscilador. Vea la figura de la
izquierda.
Cristal de cuarzo
Al utilizar el cristal de cuarzo para estabilizar la frecuencia, un oscilador
incorporado funciona a una frecuencia determinada, y no es afectada por los
cambios de temperatura y de voltaje de alimentación. Esta frecuencia se
etiqueta normalmente en el encapsulado del cristal. Aparte del cristal, los
condensadores C1 y C2 deben estar conectados como se muestra en el
siguiente esquema. Su capacitancia no es de gran importancia. Por eso, los
valores proporcionados en la siguiente tabla se deben tomar como
recomendación y no como regla estricta.
Resonador cerámico
Oscilador RC
Si la frecuencia de operación no es de importancia, entonces no es necesario
utilizar los componentes caros y adicionales para la estabilización. En vez de
eso, basta con utilizar una simple red RC, mostrada en la siguiente figura.
Como aquí es utilizada sólo la entrada del oscilador local, la señal de reloj con
la frecuencia Fosc/4 aparecerá en el pin OSC2. Ésta es la frecuencia de
operación del microcontrolador, o sea la velocidad de ejecución de
instrucciones.
Oscilador externo
Esta parte trata los componentes adicionales utilizados con más frecuencia en
la práctica, tales como resistencias, transistores, diodos LED, visualizadores
LED, visualizadores LCD y los circuitos de comunicación RS-232.
Los interruptores y los botones de presión son los dispositivos simples para
proporcionar la forma más simple de detectar la aparición de voltaje en un pin
de entrada del microcontrolador. No obstante, no es tan simple como parece...
Es por un rebote de contacto. El rebote de contacto es un problema común en
los interruptores mecánicos.
RELÉ
Un relé es un interruptor eléctrico que se abre y se cierra bajo el control de otro
circuito electrónico. Por eso está conectado a los pines de salida del
microcontrolador y utilizado para encender/apagar los dispositivos de alto
consumo tales como: motores, transformadores, calefactores, bombillas etc.
Estos dispositivos se colocan casi siempre lejos de los componentes sensibles
de la placa. Hay varios tipos de relés, pero todos funcionan de la misma
manera. Al fluir la corriente por la bobina, el relé funciona por medio de un
electromagneto, abriendo y cerrando uno o más conjunto de contactos. Similar
a los optoacopladores no hay conexión galvánica (contacto eléctrico) entre los
circuitos de entrada y salida. Los relés requieren con frecuencia tanto un voltaje
más alto y una corriente más alta para empezar a funcionar. También hay relés
miniatura que se pueden poner en marcha por una corriente baja obtenida
directamente de un pin del microcontrolador.
DIODOS LED
Probablemente sepa todo lo que necesita saber sobre los diodos LED, pero
también debe pensar en los jóvenes... A ver, ¿cómo destruir un LED?
Bueno...muy fácil.
Como cualquier otro diodo, los LEDs tienen dos puntas - un ánodo y un cátodo.
Conecte un diodo apropiadamente a la fuente de alimentación y va a emitir luz
sin ningún problema. Ponga al diodo al revés y conéctelo a la misma fuente de
alimentación (aunque sea por un momento). No emitirá luz - ¡nunca más!
Quemar lentamente
De manera similar, todo lo que tiene que hacer es elegir una resistencia para
limitar la corriente mostrada a continuación. Dependiendo de voltaje de
alimentación, los efectos pueden ser espectaculares.
VISUALIZADOR LED
Aquí le presentamos unas cosas importantes a las que debe prestar atención al
comprar un visualizador LED:
Antes que nada, un número de varios dígitos debe ser dividido en unidades,
centenas etc. en una subrutina específica. Luego, cada de estos dígitos se
debe almacenar en los bytes particulares. Los dígitos se hacen reconocibles al
realizar "enmascaramiento". En otras palabras, el formato binario de cada
dígito se sustituye por una combinación diferente de los bits por medio de una
subrutina simple. Por ejemplo, el dígito 8 (0000 1000) se sustituye por el
número binario 0111 1111 para activar todos los LEDs que visualizan el
número 8. El único diodo que queda inactivo aquí está reservado para el punto
decimal.
DÍGITOS A
S E G M E N T O S D E L V I S U A L I Z AD O R
V I S U AL I Z AR
dp a b c d e f g
0 0 1 1 1 1 1 1 0
1 0 0 1 1 0 0 0 0
2 0 1 1 0 1 1 0 1
3 0 1 1 1 1 0 0 1
4 0 0 1 1 0 0 1 1
5 0 1 0 1 1 0 1 1
6 0 1 0 1 1 1 1 1
7 0 1 1 1 0 0 0 0
8 0 1 1 1 1 1 1 1
9 0 1 1 1 1 0 1 1
Además de los dígitos de 0 a 9, hay algunas letras -A, C, E, J, F, U, H, L, b, c,
d, o, r, t - que se pueden visualizar al enmascarar.
En caso de que se utilicen los visualizadores de ánodo común, todos los unos
contenidos en la tabla anterior se deben sustituir por ceros y viceversa.
Además, los transistores PNP se deben utilizar como controladores.
OPTOACOPLADORES
VISUALIZADOR LCD
Este componente está específicamente fabricado para ser utilizado con los
microcontroladores, lo que significa que no se puede activar por los circuitos
integrados estándar. Se utiliza para visualizar los diferentes mensajes en un
visualizador de cristal líquido miniatura. El modelo descrito aquí es el más
utilizado en la práctica por su bajo precio y grandes capacidades. Está basado
en el microcontrolador HD44780 (Hitachi) integrado y puede visualizar
mensajes en dos líneas con 16 caracteres cada una. Puede visualizar todas las
letras de alfabeto, letras de alfabeto griego, signos de puntuación, símbolos
matemáticos etc. También es posible visualizar símbolos creados por el
usuario. Entre otras características útiles es el desplazamiento automático de
mensajes (a la izquierda y a la derecha), aparición del cursor, retroiluminación
LED etc.
Pines del visualizador LCD
E S T AD O
FUNCIÓN NÚMERO NOMBRE DESCRIPCIÓN
LÓGICO
Tierra 1 Vss - 0V
Alimentación 2 Vdd - +5V
Contraste 3 Vee - 0 - Vdd
D0 – D7 considerado
0 comandos
4 RS
1 D0 – D7 considerado
datos
Escribir los datos
0 microcontrolador al
Control de 5 R/W
1 Leer los datos (del
funcionamiento
microcontrolador)
Acceso al visualizado
0
deshabilitado
1
6 E Funcionamiento
Transición de 1 a
Datos/comandos se
0
transmitiendo al LCD
7 D0 0/1 Bit 0 LSB
8 D1 0/1 Bit 1
9 D2 0/1 Bit 2
Datos / comandos
10 D3 0/1 Bit 3
11 D4 0/1 Bit 4
12 D5 0/1 Bit 5
13 D6 0/1 Bit 6
14 D7 0/1 Bit 7 MSB
Pantalla LCD
Una pantalla LCD puede visualizar dos líneas con 16 caracteres cada una.
Cada carácter consiste en 5x8 o 5x11 píxeles. Este libro cubre un visualizador
de 5x8 píxeles que es utilizado con más frecuencia.
Memoria LCD
Luego, todos los caracteres enviados por las líneas D0-D7 se van a visualizar
en el formato de mensaje al que nos hemos acostumbrado - de la izquierda a la
derecha. En este caso, la visualización empieza por el primer campo de la
primera línea ya que la dirección inicial es 00hex. Si se envía más de 16
caracteres, todos se memorizarán, pero sólo los primeros 16 serán visibles.
Para visualizar los demás, se debe utilizar el comando shift. Virtualmente,
parece como si el visualizador LCD fuera una ventana, desplazándose de la
izquierda a la derecha sobre las localidades de memoria con diferentes
caracteres. En realidad, así es cómo se creó el efecto de desplazar los
mensajes sobre la pantalla.
Si se habilita ver el cursor, aparecerá en la localidad actualmente direccionada.
En otras palabras, si un carácter aparece en la posición del cursor, se va a
mover automáticamente a la siguiente localidad direccionada.
Esto es un tipo de memoria RAM así que los datos se pueden escribir en ella y
leer de ella, pero su contenido se pierde irrecuperablemente al apagar la fuente
de alimentación.
Memoria CGROM
Memoria CGRAM
Los registros de memoria son de 8 bits de anchura, pero sólo se utilizan 5 bits
más bajos. Un uno lógico (1) en cada registro representa un punto oscurecido,
mientras que 8 localidades agrupados representan un carácter. Esto se
muestra en la siguiente figura:
Los símbolos están normalmente definidos al principio del programa por una
simple escritura de ceros y unos de la memoria CGRAM así que crean las
formas deseadas. Para visualizarlos basta con especificar su dirección. Preste
atención a la primera columna en el mapa de caracteres CGROM. No contiene
direcciones de la memoria RAM, sino los símbolos de los que se está hablando
aquí. En este ejemplo ‘visualizar 0’ significa visualizar ‘sonrisa’, ‘visualizar 1’
significa - visualizar ‘ancla’ etc.
Todos los datos transmitidos a un visualizador LCD por las salidas D0-D7 serán
interpretados como un comando o un dato, lo que depende del estado lógico en
el pin RS:
TIEMPO
C O M AN D O RS RW D7 D6 D5 D4 D3 D2 D1 D0
EJECU
Borrar el
0 0 0 0 0 0 0 0 0 1 1.64mS
visualizador
Poner el cursor al
0 0 0 0 0 0 0 0 1 x 1.64mS
inicio
Modo de entrada 0 0 0 0 0 0 0 1 I/D S 40uS
Activar/desactivar el
0 0 0 0 0 0 1 D U B 40uS
visualizador
Desplazar el
0 0 0 0 0 1 D/C R/L x x 40uS
cursor/visualizador
Modo de
0 0 0 0 1 DL N F x x 40uS
funcionamiento
Establecer la
0 0 0 1 Dirección CGRAM 40uS
dirección CGRAM
Establecer la
0 0 1 Dirección CGRAM 40uS
dirección DDRAM
Leer la bandera
"BUSY"(ocupado) 0 1 BF Dirección CGRAM -
(BF)
Escribir en la
CGRAM o en la 1 0 D7 D6 D5 D4 D3 D2 D1 D0 40uS
DDRAM
Leer la CGRAM o la
1 1 D7 D6 D5 D4 D3 D2 D1 D0 40uS
DDRAM
Pocas veces se leen los datos del LCD (por lo general se transmiten del
microcontrolador al LCD) así que, con frecuencia, es posible guardar un pin de
E/S de sobra. Es simple, basta con conectar el pin L/E a Tierra. Este “ahorro”
del pin tiene su precio. Los mensajes se visualizarán normalmente, pero no
será posible leer la bandera de ocupado ya que tampoco es posible leer los
datos del visualizador. Afortunadamente, hay una solución simple. Después de
enviar un carácter o un comando es importante dar al LCD suficiente tiempo
para hacer su tarea. Debido al hecho de que la ejecución de un comando
puede durar aproximadamente 1.64mS, el LCD tarda como máximo 2mS en
realizar su tarea.
/* En mikroC for PIC, basta con escribir sólo una función para realizar todo el
proceso
de la inicialización del LCD. Antes de llamar esta función es necesario
declarar los
bits LCD_D4-LCD_D7, LCD_RS y LCD_EN. */
...
Lcd_Init(); // Inicializar el LCD
...
EJEMPLOS PRÁCTICOS
4.3 EJEMPLO 1
Escribir cabecera, configurar pines de E/S, utilizar la función Delay y el
operador Switch
/* Cabecera *********************************************/
void main() {
ANSEL = 0; // Todos los pines de E/S se configuran como
digitales
ANSELH = 0;
PORTB = 0; // Todos los pines del puerto PORTB se ponen a 0
TRISB = 0; // Pines del puerto PORTB se configuran como
salidas
do {
PORTB = ~PORTB; // Invertir el estado lógico del puerto PORTB
Delay_ms(100); // Tiempo de retardo de 100mS
k++; // Incrementar k en 1
}
while(k<20); // Quedarse en bucle hasta que k<20
do {
PORTB = ~PORTB; // Invertir el estado lógico del puerto PORTB
Delay_ms(10); // Tiempo de retardo de 10 mS
k++; // Incrementar k en 1
}
while(k<20); // Quedarse en el bucle hasta que k<20
}
4.5 EJEMPLO 3
Timer0 como un contador, declarar variables nuevas, constantes de
enumeración, utilizar relés...
void main() {
char TEST = 5; // Constante TEST = 5
enum salidas {RELÉ = 3}; // Constante RELAY = 3
do {
if (TMR0 == TEST) // ¿Coincide el número en el temporizador con la
// constante TEST?
(PORTD.RELAY = 1); // Números coinciden. Poner el bit RD3 a uno
(salida RELÉ)
}
while (1); // Quedarse en el bucle infinito
}
Si varios pines del puerto PORTD están conectados a los relés, la expresión
anterior se puede escribir de la siguiente manera también:
A todas las constantes, precedidas por las constantes con valores asignados
(RELÉ=3 y MOTOR=6), se les asignan automáticamente los valores de las
constantes precedentes, incrementados en 1. En este ejemplo, a las
constantes CALENTADOR y SURTIDOR se les asignan los valores 4 y 7, es
decir (CALENTADOR=4 y SURTIDOR=7), respectivamente.
4.6 EJEMPLO 4
Utilizar los temporizadores Timer0, Timer1 y Timer2. Utilizar
interrupciones, declarar nuevas funciones...
/*Cabecera******************************************************/
void interrupt() {
cnt++; // Con una interrupción la cnt se incrementa en 1
TMR0 = 96; // El valor inicial se devuelve en el temporizador TMR0
INTCON = 0x20; // Bit T0IE se pone a 1, el bit T0IF se pone a 0
}
void main(){
OPTION_REG = 0x84; // Pre-escalador se le asigna al temporizador TMR0
ANSEL = 0; // Todos los pines de E/S se configuran como digitales
ANSELH = 0;
TRISB = 0; // Todos los pines de puerto PORTB se configuran
// como salidas
PORTB = 0x0; // Reiniciar el puerto PORTB
TMR0 = 96; // Temporizador T0 cuenta de 96 a 255
INTCON = 0xA0; // Habilitada interrupción TMR0
cnt = 0; // A la variable cnt se le asigna un 0
do { // Bucle infinito
if (cnt == 400) { // Incrementar el puerto PORTB después 400
interrupciones
PORTB = PORTB++; // Incrementar número en el puerto PORTB en 1
cnt = 0; // Reiniciar la variable cnt
}
} while(1);
/*Cabecera******************************************************/
void interrupt() {
cnt++ ; // Con una interrupción la cnt se incrementa en 1
PIR1.TMR1IF = 0; // Reiniciar el bit TMR1IF
TMR1H = 0x80; // El valor inicial se devuelve en los registros
TMR1L = 0x00; // del temporizador TMR1H y TMR1L
}
void main() {
ANSEL = 0; // Todos los pines de E/S se configuran como digitales
ANSELH = 0;
PORTB = 0xF0; // Valor inicial de los bits del puerto PORTB
TRISB = 0; // Pines del puerto PORTB se configuran como salidas
T1CON = 1; // Configurar el temporizador TMR1
PIR1.TMR1IF = 0; // Reiniciar el bit TMR1IF
TMR1H = 0x80; // Ajustar el valor inicial del temporizador TMR1
TMR1L = 0x00;
PIE1.TMR1IE = 1; // Habilitar la interrupción al producirse un
desbordamiento
cnt = 0; // Reiniciar la variable cnt
INTCON = 0xC0; // Interrupción habilitada (bits GIE y PEIE)
do { // Bucle infinito
if (cnt == 76) { // Cambiar el estado del puerto PORTB después de 76
interrupciones
PORTB = ~PORTB; // Número en el puerto PORTB está invertido
cnt = 0; // Reiniciar la variable cnt
}
} while (1);
}
/*Cabecera******************************************************/
void Reemplazar() {
PORTB = ~PORTB; // Definir nueva función ‘Reemplazar’
} // Función invierte el estado del puerto
void interrupt() {
if (PIR1.TMR2IF) { // Si el bit TMR2IF = 1,
cnt++ ; // Incrementar variable la cnt en 1
PIR1.TMR2IF = 0; // Reiniciar el bit y
TMR2 = 0; // Reiniciar el registro TMR2
}
}
// main
void main() {
cnt = 0; // Reiniciar la variable cnt
ANSEL = 0; // Todos los pines de E/S se configuran como digitales
ANSELH = 0;
PORTB = 0b10101010; // Estado lógico en los pines del puerto PORTB
TRISB = 0; // Todos los pines del puerto PORTB se configuran como
salidas
T2CON = 0xFF; // Configurar el temporizador T2
TMR2 = 0; // Valor inicial del registro del temporizador TMR2
PIE1.TMR2IE = 1; // Interrupción habilitada
INTCON = 0xC0; // Bits GIE y PEIE se ponen a 1
4.7 EJEMPLO 5
Utilizar el temporizador perro - guardián
void main() {
OPTION_REG = 0x0E; // Pre-escalador se le asigna al temporizador WDT
(1:64)
asm CLRWDT; // Comando en ensamblador para reiniciar el
temporizador WDT
PORTB = 0x0F; // Valor inicial del registro PORTB
TRISB = 0; // Todos los pines del puerto PORTB se configuran como
salidas
Delay_ms(300); // Tiempo de retardo de 30mS
PORTB = 0xF0; // Valor del puerto PORTB diferente del inicial
Este ejemplo muestra el uso del módulo CCP1 en modo PWM. Para hacer las
cosas más interesantes, la duración de los pulsos en la salida P1A (PORTC,2)
se puede cambiar por medio de los botones de presión simbólicamente
denominados ‘OSCURO’ y ‘CLARO’. La duración ajustada se visualiza como
una combinación binaria en el puerto PORTB. El funcionamiento de este
módulo está bajo el control de las funciones pertenecientes a la librería
especializada PWM. Aquí se utilizan las tres de ellas:
void initMain() {
ANSEL = 0; // Todos los pines de E/S se configuran como digitales
ANSELH = 0;
PORTA = 255; // Estado inicial del puerto PORTA
TRISA = 255; // Todos los pines del puerto PORTA se configuran como
entradas
PORTB = 0; // Estado inicial del puerto PORTB
TRISB = 0; // Todos los pines del puerto PORTB se configuran como
salidas
PORTC = 0; // Estado inicial del puerto PORTC
TRISC = 0; // Todos los pines del puerto PORTC se configuran
// como salidas
PWM1_Init(5000); // Inicialización del módulo PWM (5KHz)
}
void main() {
initMain();
ciclo_de_trabajo_actual = 16; // Valor inicial de la variable
ciclo_de_trabajo_actual
ciclo_de trabajo_anterior = 0; // Reiniciar la variable ciclo_de
trabajo_anterior
PWM1_Start(); // Iniciar el módulo PWM1
if (old_duty != ciclo_de_trabajo_actual) { // Si
ciclo_de_trabajo_actual y
// ciclo_de trabajo_anterior no son iguales
PWM1_Set_Duty(ciclo_de_trabajo_actual); // ajustar un nuevo
valor a PWM,
ciclo_de trabajo_anterior = ciclo_de_trabajo_actual; // Guardar el nuevo
valor
PORTB = ciclo_de trabajo_anterior; // y visualizarlo en el
puerto PORTB
}
Delay_ms(200); // Tiempo de retardo de 200mS
}
}
PWM
Button
4.9 EXAMPLE 7
Utilizar el convertidor A/D
/*Cabecera******************************************************/
void main() {
ANSEL = 0x0C; // Pines AN2 y AN3 se configuran como analógicos
TRISA = 0xFF; // Todos los pines del puerto PORTA se configuran
// como entradas
ANSELH = 0; // Los demás pines se configuran como digitales
TRISB = 0x3F; // Pines del puerto PORTB, RB7 y RB6 se configuran
// como salidas
TRISD = 0; // Todos los pines del PORTD se configuran como
salidas
ADCON1.F4 = 1 ; // Voltaje de referencia es llevado al pin RA3.
do {
temp_res = ADC_Read(2); // Resultado de la conversión A/D es copiado a
temp_res
PORTD = temp_res; // 8 bits menos significativos se mueven al puerto
PORTD
PORTB = temp_res >> 2; // 2 bits más significativos se mueven a los bits
RB6 y RB7
} while(1); // Bucle infinito
}
ADC
4.10 EJEMPLO 8
Utilizar memoria EEPROM
void main() {{
ANSEL = 0; // Todos los pines de E/S se configuran como digitales
ANSELH = 0;
PORTB = 0; // Valor inicial del puerto PORTB
TRISB = 0; // Todos los pines del puerto PORTB se configuran
// como salidas
PORTD = 0; // Valor inicial del puerto PORTB
TRISD = 0; // Todos los pines del puerto PORTD se configuran
// como salidas
TRISA = 0xFF; // Todos los pines del puerto PORTA se configuran
// como entradas
PORTD = EEPROM_Read(5); // Leer la memoria EEPROM en la dirección
5
do {
PORTB = PORTB++; // Incrementar el puerto PORTB en 1
Delay_ms(100); // Tiempo de retardo de 100mS
if (PORTA.F2)
EEPROM_Write(5,PORTB); // Si se pulsa el botón MEMO, guardar el
puerto PORTB
do {
while (PORTA.F2); // Quedarse en este bucle hasta que el botón esté
pulsado
}
}
while(1); // Bucle infinito
}
EEPROM
4.11 EJEMPLO 9
Contador de dos dígitos LED, multiplexión
/*Cabecera******************************************************/
void interrupt() {
if (digit_no == 0) {
PORTA = 0; // Apagar ambos visualizadores
PORTD = digit1; // Colocar máscara para visualizar unidades en el
// puerto PORTD
PORTA = 1; // Encender el visualizador para las unidades (LSD)
digit_no = 1;
}else{
PORTA = 0; // Apagar ambos visualizadores
PORTD = digit10; // Colocar máscara para visualizar decenas en el
// puerto PORTD
PORTA = 2; // Encender el visualizador para las decenas (MSD)
digit_no = 0;
}
void main() {
OPTION_REG = 0x80; // Ajustar el temporizador TMR0
TMR0 = 0;
INTCON = 0xA0; // Deshabilitar las interrupciones PEIE,INTE,RBIE,T0IE
PORTA = 0; // Apagar ambos visualizadores
TRISA = 0; // Todos los pines del puerto PORTA se configuran
// como salidas
PORTD = 0; // Apagar todos los segmentos del visualizador
TRISD = 0; // Todos los pines del puerto PORTD se configuran
// como salidas
do {
for (i = 0; i<=99; i++) { // Contar de 0 a 99
digit = i % 10u;
digit1 = mask(digit); // Preparar la máscara para visualizar unidades
digit = (char)(i / 10u) % 10u;
digit10 = mask(digit); // Preparar la máscara para visualizar decenas
Delay_ms(1000);
}
} while (1); // Bucle infinito
}
mask.c
/*Cabecera******************************************************/
mikroElektronika
LCD example
Dos segundos más tarde, el mensaje en la segunda línea cambia, y se
visualiza el voltaje presente en la entrada del convertidor A/D (el pin RA2). Por
ejemplo:
mikroElektronika
voltage:3.141V
En un dispositivo real se puede visualizar temperatura actual o algún otro valor
medido en vez de voltaje.
Para que este ejemplo funcione apropiadamente, es necesario marcar las
siguientes librerías en la ventana Library Manager antes de compilar el
programa:
ADC
LCD
/*Cabecera*****************************************************/
// Declarar variables
unsigned char ch;
unsigned int adc_rd;
char *text;
long tlong;
void main() {
INTCON = 0; // Todas las interrupciones deshabilitadas
ANSEL = 0x04; // Pin RA2 se configura como una entrada
analógica
TRISA = 0x04;
ANSELH = 0; // Los demás pines se configuran como digitales
while (1) {
adc_rd = ADC_Read(2); // Conversión A/D. Pin RA2 es una entrada.
Lcd_Out(2,1,text); // Escribir el resultado en la segunda línea
tlong = (long)adc_rd * 5000; // Convertir el resultado en milivoltios
tlong = tlong / 1023; // 0..1023 -> 0-5000mV
ch = tlong / 1000; // Extraer voltios (miles de milivoltios)
// del resultado
Lcd_Chr(2,9,48+ch); // Escribir resultado en formato ASCII
Lcd_Chr_CP('.');
ch = (tlong / 100) % 10; // Extraer centenas de milivoltios
Lcd_Chr_CP(48+ch); // Escribir resultado en formato ASCII
ch = (tlong / 10) % 10; // Extraer decenas de milivoltios
Lcd_Chr_CP(48+ch); // Escribir resultado en formato ASCII
ch = tlong % 10; // Extraer unidades de milivoltios
Lcd_Chr_CP(48+ch); // Escribir resultado en formato ASCII
Lcd_Chr_CP('V');
Delay_ms(1);
}
}
4.13 EJEMPLO 11
Comunicación serial RS-232
/*Cabecera******************************************************/
unsigned short i;
void main() {
UART1_Init(19200); // Inicializar el módulo USART
// (8 bits, tasa de baudios 19200, no hay bit
// de paridad...)
while (1) {
if (UART1_Data_Ready()) { // si se ha recibido un dato
i = UART1_Read(); // leerlo
UART1_Write(i); // enviarlo atrás
}
}
}
UART
4.14 EJEMPLO 12
Medición de temperatura por medio del sensor DS1820. Uso del protocolo
‘1-wire’...
/*Cabecera******************************************************/
// extraer temp_whole
temp_whole = temp2write >> RES_SHIFT ;
void main() {
ANSEL = 0; // Configurar los pines AN como digitales
ANSELH = 0;
C1ON_bit = 0; // Deshabilitar los comparadores
C2ON_bit = 0;
One_Wire
LCD
4.15 EXAMPLE 13
Generación de sonido, librería de sonido...
/*Cabecera******************************************************/
void Tone1() {
Sound_Play(659, 250); // Frecuencia = 659Hz, duración = 250ms
}
void Tone2() {
Sound_Play(698, 250); // Frecuencia = 698Hz, duración = 250ms
}
void Tone3() {
Sound_Play(784, 250); // Frecuencia = 784Hz, duración = 250ms
}
void main() {
ANSEL = 0; // Todos los pines de E/S son digitales
ANSELH = 0;
TRISB = 0xF0; // Pines RB7-RB4 se configuran como entradas
while (1) {
if (Button(&PORTB,7,1,1)) // RB7 genera Tono1
Tone1();
while (PORTB & 0x80) ; // Esperar que se suelte el botón
if (Button(&PORTB,6,1,1)) // RB6 genera Tono2
Tone2();
while (PORTB & 0x40) ; // Esperar que se suelte el botón
if (Button(&PORTB,5,1,1)) // RB5 genera melodía 2
Melody2();
while (PORTB & 0x20) ; // Esperar que se suelte el botón
if (Button(&PORTB,4,1,1)) // RB4 genera melodía 1
Melody1();
while (PORTB & 0x10) ; // Esperar que se suelte el botón
}
}
Para que este ejemplo funcione apropiadamente, es necesario marcar las
siguientes librerías en la ventana Library Manager antes de compilar el
programa:
Button
Sound
4.16 EJEMPLO 14
Utilizar el visualizador LCD gráfico
/*Cabecera******************************************************/
//Declaraciones-----------------------------------------------------------------
const code char truck_bmp[1024]; // Declarar la constante definida en
truck_bmp.c
// para utilizarla en este archivo
//--------------------------------------------------------final-de-declaraciones
void main() {
unsigned short ii;
char *someText;
delay2S();
delay2S();
Glcd_Fill(0x00); // Borrar el GLCD
#ifdef COMPLETE_EXAMPLE
Glcd_Set_Font(Character8x7, 8, 7, 32); // Seleccionar la fuente, ver
// __Lib_GLCDFonts.c en la carpeta Uses
#endif
truck_bmp.c:
/*Cabecera*****************************************************/
Un panel táctil está compuesto por dos láminas rígidas, formando una
estructura de ‘sándwich’ que tiene capas resistivas en sus caras internas. La
resistencia de estas capas no excede normalmente de 1Kohm. Los lados
opuestos de las láminas disponen de los contactos para acceder a un cable
plano.
En este ejemplo se utilizan las funciones que pertenecen a las librerías Glcd y
ADC.
Teniendo en cuenta que la superficie del panel táctil es ligeramente mayor que
la del LCD gráfico, en caso de requerir una mayor precisión en la determinación
de las coordenadas, es necesario incluir el software de calibración del panel
táctil.
/* Cabecera ***************************************************/
// Leer la coordenada X
unsigned int GetX() {
//reading X
PORTC.F0 = 1; // DRIVEA = 1 (electrodo izquierdo (LEFT) conectado,
electrodo
// derecho (RIGHT) conectado, electrodo superior
(TOP)desconectado)
PORTC.F1 = 0; // DRIVEB = 0 (electrodo inferior (BOTTOM)
desconectado)
Delay_ms(5);
return ADC_Read(0); // leer el valor de X de RA0(BOTTOM)
}
// Leer la coordenada Y
unsigned int GetY() {
//Leer la Y
PORTC.F0 = 0; // DRIVEA = 0 (electrodo izquierdo (LEFT)
desconectado, electrodo
// derecho (RIGHT) desconectado, electrodo superior (TOP)
conectado)
PORTC.F1 = 1; // DRIVEB = 1 (electrodo inferior (BOTTOM) conectado)
Delay_ms(5);
return ADC_Read(1); // leer el valor de Y de RA1 (del eléctrodo izquierdo
LEFT)
}
void main() {
PORTA = 0x00;
TRISA = 0x03; // RA0 y RA1 son entradas analógicas
ANSEL = 0x03;
ANSELH = 0; // Configurar otros pines AN como digitales de E/S
PORTC = 0 ; // Todos los pines del puerto PORTC están a 0 (incluyendo
los
// pines RC6 y RC7)
while (1) {
// leer X-Y y convertirlo en la resolución de 128x64 píxeles
x_coord = GetX();
y_coord = GetY();
x_coord128 = (x_coord * 128) / 1024;
y_coord64 = 64 -((y_coord *64) / 1024);
PROGRAMAR EL MICROCONTROLADOR
Esta sección describe en breve el uso del programa (compilador) mikroC PRO
for PIC y del software de programación (programador)PICflash. Todo es muy
simple...
Usted ya tiene instalado el mikroC PRO for PIC, ¿verdad? Al iniciarlo, abra un
proyecto nuevo y un documento nuevo con extensión .c dentro del mismo.
Escriba su programa...
OK. The program has been written and tested with the simulator. It did not
report any errors during the process of compiling into the hex code? It seems
that everything is under control...
De acuerdo. El programa ha sido escrito y probado con el simulador. ¿No ha
informado de ningún error durante el proceso de compilación en el código hex?
Parece que todo funciona perfecto...
Una versión de calidad alta tiene los pines conectados a los visualizadores
LED, visualizadores LCD, sensores de temperatura u otros componentes por
los que puede estar compuesto un dispositivo destino. Si es necesario, todos
estos periféricos pueden estar conectados al microcontrolador por medio de los
puentes. Esto permite probar el programa entero en la práctica aún durante el
proceso de desarrollo, porque el microcontrolador no “sabe o no le interesa” si
su entrada está activada por un botón de presión o un sensor incorporado en
un dispositivo real.