Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Aquí se presenta un tutorial del compilador XC8 que pretende proporcionar los conocimientos básicos para comenzar a desarrollar con
el mismo, para ello daremos una introducción de cómo es un microcontrolador PIC y su funcionamiento, los lenguajes de programación
y la ventaja de usar C, como crear un proyecto, la estructura de un programa en C y lo necesario para ir creando ejemplos. Espero sea de
utilidad
Licencia
Indice
Introducción al microcontrolador
El lenguaje de programación
Comenzando a desarrollar en C
Introducción al microcontrolador
Un microcontrolador es un dispositivo electrónico capaz de ejecutar una secuencia de comandos previamente programados. Estos
comandos son proyectados por el usuario utilizando algún lenguaje de programación y luego grabados en la memoria del
microcontrolador.
Los microcontroladores PIC de gama baja poseen arquitectura Harvard, esto quiere decir que utilizan dos memorias distintas, una para
almacenar las instrucciones y otra para manejar los datos. Entonces está compuesto principalmente por un procesador (CPU), memoria
RAM, memoria ROM y buses de comunicaciones (cada tipo de memoria tiene un bus de datos, uno de direcciones y uno de control).
Adicionalmente también dispone de puertos de entrada y salida, y diversos periféricos que nos facilitan el desarrollo tales como
osciladores, temporizadores/contadores, módulos de comunicación serial y paralela, comparadores analógicos, conversores analógicos
profundamente.
La memoria ROM es del tipo no volátil, esto significa que los datos almacenados no se pierden aunque no esté energizada. Se destina
principalmente a contener la lista de instrucciones que conforman la aplicación, por ello suele llamarse memoria de programa y en
microcontroladores de la actualidad ronda desde los 512 bytes a 128 kbytes, correspondientes a los de gama baja de Microchip. Son del
tipo Flash, de bajo consumo, que se pueden escribir y leer, y de gran densidad de almacenamiento. Además tienen la ventaja que
permiten ser reprogramadas en circuito, sin necesidad de extraer el circuito integrado de la tarjeta.
La memoria RAM, memoria volátil, es la destinada a guardar las variables y datos temporales que serán utilizados por el procesador
para realizar cálculos u otro tipo de operaciones lógicas. El espacio de direcciones de memoria RAM se divide en dos sectores: registros
de propósito general (GPR), espacio destinado para crear variables por el usuario y variables propias del compilador; y registros de
funciones especiales (SFR), espacio que contiene bits de configuración y control de los periféricos del microcontrolador. En la
actualidad se pueden encontrar microcontroladores con memoria RAM de unos 32 bytes hasta 4 kbytes.
Un registro es una pequeña porción de la memoria y su tamaño se mide generalmente en bits (8-bits, 16-bits, 32-bits). Está
representado por un numero que denominamos dirección de memoria, pero dentro del lenguaje de programación existe la posibilidad
El CPU o unidad central de procesamiento es el encargado de direccionar la memoria ROM, decodificar la instrucción y ejecutar la
operación que implica. El primer paso es leer la instrucción de la memoria, la posición es controlada por un contador de programa (PC)
que almacena un número que identifica la posición actual a ejecutar. La instrucción que el CPU lee desde la memoria es usada para
determinar qué operación debe hacer el CPU, en este paso hay una decodificación, en donde la instrucción es dividida en partes que
tienen significado para otras unidades del CPU. Después de los pasos de lectura y decodificación, es llevado a cabo el paso de
la ejecución de la instrucción. Durante este paso, varias unidades del CPU son conectadas de tal manera que ellas pueden realizar la
operación deseada. Si, por ejemplo, una operación de adición fue solicitada, una unidad aritmético lógica (ALU) será conectada a un
conjunto de entradas (números a ser sumados) y un conjunto de salidas (suma). Luego el paso final, simplemente la obtención del
resultado, escribiéndolo en un registro interno del CPU de acceso rápido, modificando un registro de la memoria RAM o modificando el
CISC (Computadores de juego de instrucciones complejo): Disponen de un conjunto amplio de instrucciones donde muchas de ellas
son complejas, permitiendo realizar operaciones sofisticadas y potentes, pero que ocupan varios ciclos.
RISC (Computadores de juego de instrucciones reducido): En este caso el repertorio de instrucciones mínimo y muy sencillo, y
generalmente ocupa entre 1 o 2 ciclos. La ventaja de éstos es que la sencillez y rapidez de las instrucciones permiten optimizar el
SISC (Computadores de juego de instrucciones específicos): Se utiliza para microcontroladores destinados a aplicaciones concretas,
donde el set de instrucciones es reducido y específico para las tareas a desarrollar, adaptándose a las necesidades de la aplicación
prevista.
Los microcontroladores de gama baja de Microchip se basan en la arquitectura RISC, con 35 instrucciones para los 16F y 75
instrucciones para los 18F, en este último caso parecen muchas pero son simples y que ocupan pocos ciclos.
El PIC18F4550
Ahora tenemos un conocimiento general de que compone un microcontrolador y como es su funcionamiento, así que pasaremos a uno
puntual. En este tutorial nos basaremos en el microcontrolador PIC18F4550 de la familia PIC18 de Microchip que tiene las siguientes
características básicas:
Pila de 32 niveles.
20 fuentes de interrupción.
4 temporizadores.
2 comparadores analógicos.
Como hemos comentado anteriormente, en la memoria de programa existe una lista de instrucciones que el microcontrolador ejecuta
de forma secuencial. Estas instrucciones son números binarios de ancho establecido por la arquitectura (12, 14, 16 bits, etc.) y que, como
es más fácil de trabajar, se representan de forma hexadecimal. Entonces a esta lista de instrucciones se la suele denominar código HEX.
Para generar este código HEX existen varios lenguajes de programación, tenemos de bajo nivel denominado Assembler y de alto nivel
entre los cuales podemos encontrar C, C++, Basic, Pascal, lenguaje de tipo visual, etc.
El lenguaje Assembler no es más que una traducción de las instrucciones en hexadecimal que “entiende” el microcontrolador en
abreviaturas más sencillas de recordar y manejar por un programador. Requiere tener mucho conocimiento de la arquitectura y para
proyectos grandes es extremadamente compleja su utilización, además de requerir mucho más tiempo de trabajo.
Los lenguajes de programación de alto nivel fueron creados con el propósito de superar estas desventajas, otorgando un mayor
entendimiento de cómo es el flujo del programa, generando sentencias simples de entender para una persona, ya no es necesario
conocer en profundidad el conjunto de instrucciones de la arquitectura y además proporcionan portabilidad. O sea, un programa
(funciones, librerías) escrito en lenguaje de alto nivel en determinado microcontrolador es mucho más sencillo portarlo a otro
microcontrolador, ya sea de distintas prestaciones dentro de la misma marca o un microcontrolador de otra empresa. (Principalmente
C)
Proporciona facilidades para realizar programas modulares y/o utilizar código o librerías existentes.
A diferencia de un programa realizado en Assembler, es menos eficiente ocupando más espacio y siendo más lento.
XC8 es un nuevo compilador que ha presentado Microchip para las familias de microcontroladores de 8-bits (PIC10, PIC12, PIC16,
PIC18) que reúne de forma integral a los antiguos compiladores Hitech PICC, Hitech PICC18 y MPLAB C18. Es un compilador
multiplataforma, compatible con Windows, Linux y Mac. Hay diferentes opciones de descarga según la necesidad, Pro, Estándar y Free
XC8 puede utilizarse por medio de línea de comandos o integrarse a un entorno de desarrollo, como MPLAB IDE o MPLAB X. Se
encarga de realizar la compilación de los archivos fuente del proyecto y de realizar el enlace de éstos con librerías pre compiladas para la
El compilador “lee” lo escrito por el programador e intenta interpretarlo según las reglas del lenguaje y la sintaxis de C. Si no encuentra
errores, produce un archivo intermedio llamado objeto (con la extensión obj). En caso de tener un archivo fuente escrito en asm, este es
Luego el paso siguiente para la generación del HEX es el enlazado (link en ingles). Este toma los archivos objetos del proceso de
compilación y ensamblado, toma archivos objetos de librerías pre compiladas según la necesidad del proyecto y genera el HEX que
puede ser grabado en el microcontrolador. Adicionalmente a éste se generan otros archivos que permiten realizar depuraciones
los realiza de forma automática. Para ello crea archivos makefile que contienen los comandos y argumentos a ejecutar para generar el
HEX. O sea, nosotros presionaremos Build All y si todo está correcto obtendremos los archivos objeto, el archivo cof y el archivo HEX
Para comenzar a desarrollar con XC8 y MPLAB X es necesario que ambos estén instalados en la PC. Los podemos bajar directamente
desde www.microchip.com, en este caso usaremos MPLAB X v1.2 y XC8 v1.1. El siguiente vídeo muestra los pasos para crear un
Una vez creado el proyecto ya podemos comenzar a desarrollar nuestro programa en C. El archivo principal, o sea el que contiene la
función principal (main) generalmente tiene la siguiente estructura:
/* Archivo: main.c
Autor: Suky
Fecha: 20/08/12
*/ Cabecera, información sobre el archivo pero sin importancia para el proceso de compilación.
Inclusión de archivos cabecera con información necesaria para las funciones implementadas en el
#include <xc.h> proyecto. Para ello se hace uso de directivas de preprocesador.
unsigned char Variable1, Declaración de variables globales, pueden ser utilizadas por cualquiera de las función que se encuentran
Variable2; en el archivo fuente.
void vFuncion1(void);
void vFuncion2(unsigned
char Data); Declaración del prototipo de las funciones implementadas en el archivo fuente.
void main(void){
….
vFuncion1();
….
while(1){
….
vFuncion2(45);
….
void vFuncion1(void){
}
void vFuncion2(unsigned
char Data){
}
Los comentarios
Es una buena práctica comentar el código fuente que vamos desarrollando, ya sea para uno mismo que después de un tiempo retoma el
proyecto o para otra persona que lo desea modificar o ampliar. Tenemos dos maneras de realizar los comentarios, cortos (comienzan
Retorna: Nada
*/
El preprocesador es el primer programa que se ejecuta en el proceso de compilación y trata directivas especiales
como#include, #define, #if, etc. Algunas de las ventajas de utilizar directivas de preprocesador son las siguientes:
Los programas son más fáciles de leer.
#include
Esta directiva de preprocesador le indica al compilador que debe incluir un archivo fuente antes de continuar. Este archivo fuente (.h)
tendrá las declaraciones de las funciones que implementa dicha librería y/o macros útiles en el proyecto. En el ejemplo mostrado
incluimos xc.h, si buscamos que contiene encontraremos que incluye otros archivos fuente dependiendo de que microcontrolador
estemos usando y entre esos archivos fuente encontraremos macros que nos ayudarán a crear nuestro código. Como por ejemplo:
#define Nop() asm(" nop")
Además encontraremos la declaración de los puertos y pines del microcontrolador, registros y bits de configuración de los periféricos,
etc., por lo tanto no tendremos que saber cual es su dirección o número de bit para poder trabajar con ellos.
El archivo a incluir se puede indicar mediante comillas (“”) o entre ángulos (<>). Con la primer opción el compilador busca en la misma
carpeta donde se encuentra el archivo que realiza el llamado o la dirección que indique el mismo y entre ángulos busca en las carpetas
#include <Timers.h>
#include “MiLibreria.h”
#include “../Include/MiLibreria.h”
En este último caso al colocar doble punto (..) el compilador sale de la carpeta donde se encuentra el archivo que hace el llamado, busca
#define
Esta directiva permite crear un identificador para una secuencia de caracteres de la siguiente forma:
El preprocesador buscará los identificadores creados y los reemplazará por la secuencia de caracteres para su posterior compilación.
Esto es muy útil para crear código que sea más entendible a la lectura. Veamos ejemplos:
#define FALSO 0
#define VERDADERO 1
#define PI 3.141592
#define PIN_ENTRADA 1
#define PIN_SALIDA 0
#define ENCENDIDO 1
#define APAGADO 0
Tengamos en cuenta que podemos crear macros que se asemejen a funciones, la ventaja que otorga es que nos ahorramos esos ciclos de
llamada a dicha función pero ocupamos más código, dado que el preprocesador solo reemplaza texto por texto.
arquitecturas, para ser utilizada por varios compiladores o simplemente para seleccionar el nivel de uso de cierto proyecto.
Por ejemplo podemos crear una definición para realizar o no debugger por puerto serial, y en código si está habilitado compilar dichas
#define USE_DEBUG
Ifdef USE_DEBUG
printf(“Debug habilitado”);
#endif
#include <pic.h>
#endif
#if defined(__PICC18__)
#include <pic18.h>
#endif
#ifdef __18CXX
#include <pic18.h>
#endif
Puede que en un principio en sus programas no implemente tantas opciones y solo se enfoque en una arquitectura, pero tener
conocimiento ayudará a entender archivos fuentes que encontremos incluidos en el compilador (librerías) y después con mucha más
practica comenzar a implementarlos
Su memoria de programa de de 32768 bytes, pero las instrucciones ocupan 2 bytes (excepto call, goto, movff, lsfr que ocupan 4), por lo
que se pueden almacenar hasta 16384 instrucciones como máximo. Las direcciones especiales son0×00 que corresponde al reset del
prioridad. Las prioridades de interrupciones pueden no habilitarse y comportarse como un PIC16F, o habilitarse y cada fuente de
interrupción (excepto la interrupción externa por RB0, solo es de alta prioridad) tiene un bit que la configura como de alta prioridad o
baja prioridad.
4 de ellos son utilizados por el módulo USB en caso de utilizarlo. Además se tiene 160 bytes dedicados a los registros de funciones
pines donde, excepto los de alimentación, los pines tienen varias funciones según como se lo configure. Se puede encontrar con los
package TQFP, QFN o DIP. Este último, el más utilizado para el aprendizaje y los hobbystas, tiene el siguiente pinout:
Hay mucho más por agregar respecto a este microcontrolador, pero no es la intensión de este tutorial, para profundizar más es
Teniendo una idea general de cómo es la estructura de un archivo fuente de C pasaremos a realizar un primer ejemplo como para
llevarlo al hardware y ver su funcionamiento. Para trabajar nuestro microcontrolador necesita una fuente de reloj, que provea una señal
a frecuencia constante que permita ir leyendo y ejecutando las sentencias secuencialmente. Generalmente los microcontroladores
modernos ofrecen la posibilidad de recibir esta señal de reloj desde varias fuentes, cristales externos, osciladores externos u osciladores
internos, pero además permiten elevar la frecuencia de trabajo internamente (PLL). Esto permite mayor flexibilidad y además al
El microcontrolador PIC18F4550 que se utiliza en este tutorial incluye un PLL que permite multiplicar la frecuencia de un cristal u
A tener en cuenta, los microcontroladores de gama baja de Microchip necesita 4 ciclos de reloj para ejecutar una instrucción simple,
entonces tenemos que Fcy=Fosc/4. Un PIC18F4550 que puede trabajar hasta 48 MHz ejecuta como máximo 12 millones de
Para seleccionar qué tipo de fuente de reloj se utilizará y la configuración del PLL se utilizan los bits de configuración del
microcontrolador que solo pueden ser modificados al ser programados. Además de estas opciones los bits de configuración permiten
establecer otras características como por ejemplo, habilitar o no el reset, proteger contra lectura ciertos sectores de programa, habilitar
el perro guardian, etc. Para más detalles hay que estudiarse el datasheet del microcontrolador específico, dado que en PICs pueden
Para configurar el reloj y PLL del PIC18F4550 veamos el siguiente esquema extraído del datasheet:
Vamos a fijar como frecuencia de trabajo los 48 MHz máximos que soporta el microcontrolador, para ello vamos a utilizar como fuente
un cristal externo de 12 MHz. Para lograr los 48MHz es necesario habilitar el PLL, esto lo hacemos mediante los bits FOSC3:FOSC0.
Para un cristal de 12 MHz se debe colocar 111x = HS oscillator, PLL enabled (HSPLL) y revisando la documentación que provee XC8
(doc/pic18_chipinfo.html) se debe colocar FOSC=HSPLL_HS. Si observamos la figura anterior, al habilitar el PLL la frecuencia de
entrada del mismo debe ser 4 MHz, y para ello se provee de un divisor de frecuencia controlado por PLLDIV, en este caso los 12 MHz los
dividimos por 3, por lo tanto debemos hacer PLLDIV=3. La frecuencia de salida del PLL es de 96 MHz, los cuales sirven de fuente de
clock para el módulo USB y también para el microcontrolador. Pero para el microcontrolador los 96 MHz los podemos dividir en 2, 3, 4
o 6! O sea, podemos obtener gran variedad de frecuencias. Como nos establecimos como meta obtener 48 MHz debemos
configuración que otorgan otras opciones de trabajo. Tenemos IESO, que permite cambiar la fuente de clock cuando el
microcontrolador está en funcionamiento y FCMEN que en caso de fallar la fuente de reloj principal realiza el cambio a la fuente de
reloj interna INTRC. Dentro del datasheet hay secciones donde podemos encontrar más detalles para el uso de estos modos, aquí solo
Como comentamos anteriormente existen otros fusibles de configuración que dependen del microcontrolador utilizado, aquí vamos a
establecerlos de la siguiente manera, los detalles los podemos encontrar en la sección Special Feactures of the
CPU/Configuration Bits.
Hay varios más, pero si no se configuran quedan como vienen por defecto. En MPLAB X podemos ver la
Como primer ejemplo vamos hacer titilar un led, este ejemplo es el “Hola Mundo” en los microcontroladores. Para ello vamos a
necesitar una función que cree una demora y para ello XC8 dispone de __delay_us() y __delay_ms(), para integrarlas debemos llamar
a xc.h pero además previamente definir el valor de _XTAL_FREQ. A tener en cuenta, tanto __delay_ms como __delay_us utilizan la
función _delay(), y esta solo soporta hasta 197120 ciclos, o sea que para 48 MHz la máxima demora posible es 16.42 ms.
Para el control del led debemos establecer el pin a utilizar como salida y luego escribir en el pin el valor que queramos establecer (0 o 1
lógico), para ello tenemos los registros TRIS y los registros LAT. En caso de necesitar leer el valor que tiene un pin se utilizan los
registros PORT. Esto es una mejora introducida a partir de la familia PIC18, debido a que en familias anteriores para hacer operaciones
sucesivas en puertos se necesitaba agregar un nop entre ellas. Si por ejemplo queremos controlar nuestro led mediante el
pin B0 debemos trabajar con TRISB y LATB, XC8 para facilitarnos el desarrollo tiene definido el bit 0 de TRISB como TRISB0 o
como también es compatible con C18 se puede acceder mediante TRISBbits.TRISB0. Los mismo para el bit
0 de LATB, LATB0 o LATBbits.LATB0. Esto lo podemos ver en el archivo cabecera pic18F4450.h que se encuentra
en xc8\v1.00\include.
/*
* File: main.c
* Author: Suky
*/
#include <xc.h>
void main(void) {
TRISB0=0; // Lo establecemos como salida
while(TRUE){
__delay_ms(10);
__delay_ms(10);
Nota: Tener en cuenta que la demora generada es solo de 10ms, en simulación se podrá visualizar el titilar del led pero en una
/*
* File: main.c
* Author: Suky
*/
#include <xc.h>
#define OUTPUT 0
#define INPUT 1
#define ON 1
#define OFF 0
void main(void) {
PIN_DIR_LED=OUTPUT;
while(TRUE){
PIN_LED=OFF;
__delay_ms(10);
PIN_LED=ON;
__delay_ms(10);
}
Se dan cuenta que si quiero cambiar de pin para controlar el Led se hace mucho más sencillo? Y eso que solo es un ejemplo muy
sencillo!
En este ejemplo solo implementamos la función principal (void main(void)) que debe estar si o si en nuestro proyecto, pues el
compilador tiene una función que es llamada al ocurrir un reset (vector 0×00) la cual inicializa el puntero de la pila (Stack en ingles),
Esta función puede contener el código para inicializar el hardware de la forma que nosotros queramos, o llamar a funciones adicionales
que hagan esta tarea y luego generalmente tiene el bucle infinito. Este bucle debe estar si o si, sino al terminar de ejecutar las
instrucciones el microcontrolador se resetea. Dentro del bucle tendremos lo que queremos que el microcontrolador ejecute
periódicamente hasta ser reseteado. En C un bucle infinito se puede hacer de varias formas, aquí usamos la sentencia while:
while(Condicion){Sentencias}
Esta sentencia o estructura de control de C permite ejecutar las sentencias que contiene de forma cíclica mientras la condición sea
verdadera. Tener en cuenta que primero se pregunta si la condición es cierta y luego ejecuta las sentencias. Como la condición en
En este caso las sentencias se ejecutan primero y luego se pregunta la condición, por lo que las sentencias se ejecutan mínimo una vez.
Operadores en C
Operador de asignación: Permite asignar un valor al operando izquierdo en base al operando derecho. El valor
puede truncarse dependiendo del tipo de variable del operando izquierdo. Veamos unos ejemplos.
unsigned char K; // Variable que puede tomar valores enteros entre 0 y 255.
float J; // Flotante de 24-bits
K=150; // k adquiere el valor 150.
K= 796; // k adquiere el valor 28. 796 necesita 16-bits para ser almacenado, pero como la variable
es de 8-bits, los 8-bits altos se pierden.
J=10; // J adquiere el valor 10.0
J=95000.0; // J adquiere el valor 95000.0
J=95001.0; // J adquiere el valor 95002.0. Esto es debido a la resolución que se logra con 24-bits,
si cambiamos a 32-bits podremos almacenar 95001.0.
unsigned char K;
unsigned int A=796;
K=A; // K adquiere el valor 28.
k==0, k==‟0‟,
== Retorna true si los operandos son iguales k==a
Operadores aritméticos: Básicamente, permiten realizar cálculos aritméticos como suma, resta, división, etc..
Operadores lógicos: Retornan un valor lógico, true o false, en base a las denominadas tablas de verdad. Los
operadores son:
! NOT !(k<5)
Tablas de verdad:
AND
A B Resultado
OR
A B Resultado
NOT
A Resultado
False True
True False
Operadores bitwise: Estos operadores permiten actuar a nivel bit. Tenemos los siguientes operadores:
A=0xAA ->
0b10101010
B=0xF0 ->
0b11110000
AND, compara dos bits; si los dos son 1 el resultado es 1, en
& otro caso el resultado será 0. C=A&B=0xA0 ->
0b10100000
A=0x0A ->
0b00001010
B=0xF0 ->
0b11110000
C=A|B=0xFA ->
OR, compara dos bits, si cualquiera de los dos bits es 1 el 0b11111010
| resultado es 1, en otro caso será 0.
C=A^B=0×55 ->
XOR, compara dos bits, si cualquiera de los dos bits es 1 pero 0b01010101
^ no ambos el resultado es 1, en otro caso será 0.
A=0xAA ->
0b10101010
B=~A=0×55 ->
0b01010101
~ Complemento, retorna el complemento a 1 del operando
El operador shift >> tiene diferente comportamiento dependiendo el tipo de dato al que se le va a aplicar. Más
precisamente si la variable fue creada con signo o sin signo.
Variables en C
Una variable no es más que la asignación de un nombre a una dirección de memoria, la cual dependiendo del
tipo puede ocupar uno o más bytes. La sintaxis es la siguiente:
void vMiFuncion(void){
unsigned char VariableLocal;
VariableGlobal=10;
VariableLocal=55;
}
void main(void){
VariableGlobal=60;
}
Adicionalmente al tipo se pueden implementar modificadores que darán información adicional del modo en
que serán utilizadas las variables. XC8 admite los modificadores estándar de ANSI C y modificadores
especiales que son útiles en sistemas embebidos en microcontroladores de 8-bits.
Const: Indicara que la variable es solo de lectura y que no se modificará. Al definirse de esta manera la variable
se localiza en la memoria de programa. Puede indicarse la dirección absoluta de donde ubicarla de la siguiente
manera:
const char MiConstante @ 0×100=50;
Volatile: Indicará al compilador que no se asegura que se mantendrá su valor en accesos sucesivos. Este tipo de
modificador debe utilizarse en variables que son modificadas en interrupciones y que son testeadas en el
programa principal. Esto es para evitar que el compilador al detectar que la variable no es modificada en dos
puntos dados del código haga un cacheo del valor de la variable. Ejemplo:
volatile bit MiBandera=0;
MiBandera=1;
}
void main(void){
while(1){
if(MiBandera==1){
MiBandera=0;
}
}
}
Pesistent: Le indica al compilador que dicha variable no debe ser inicializada a 0 en el inicio. Por default si a
una variable no se le asigna un valor al declararla en el inicio es establecida en 0, al colocar persisten esto se
evita y mantiene su valor al ocurrir un reset.
persisten char VariablePesistente;
Near: Le indica al compilador que la variable debe estar en el banco de acceso (PIC18), con lo cual puede ser
accedida sin importar el banco de memoria que este seleccionado. Esto reduce el código y los tiempos de
ejecución ya que no es necesario el cambio de banco.
near char VariableAcceso;
En la siguiente imagen podemos ver la diferencia entre el modo de acceso a una variable near y otra ubicada en
otros bancos:
unsigned char k, a;
for(k=0;k<10;k++){
a=k;
}
En primer lugar k se establece en 0, luego se testea la condición, si es válida ejecuta el bloque de código.
Luego se aplica el incremento establecido, se testea la condición y si es válida ejecuta el bloque de código
nuevamente. Esto se repite hasta que la condición no es válida. De esta manera k toma los siguientes valores:
0,1,2,3,4,5,6,7,8,9,10.
En cambio a toma los siguientes valores:
0,1,2,3,4,5,6,7,8,9.
Otro ejemplo:
for(k=5;k<=17;k+=3){
a=k;
}
k=5,8,11,14,17.
a=5,8,11,14.
En nuestro primer ejemplo teníamos el inconveniente de que la demora máxima que podemos generar a
48MHz es de 16.42 ms. Ahora podemos mediante el uso de variables y estructuras for crear una demora mayor,
por ejemplo ejecutando 30 veces una demora de 10ms. Tenemos entonces:
Ahora sí, si lo ejecutamos en hardware real podremos ver el titular del led.
/*
* File: main.c
* Author: Suky
*/
#define _XTAL_FREQ 48000000
#include
void main(void) {
Unsigned char k;
PIN_DIR_LED=OUTPUT;
while(TRUE){
// _delay(x) 197120 cycles max
PIN_LED=OFF;
for(k=0;k<30;k++){__delay_ms(10);}
PIN_LED=ON;
for(k=0;k<30;k++){__delay_ms(10);}
}
}
Estructura if-else: En este caso se agrega la instrucción else. Traduciendo nuevamente sería, si la
condición es verdadera ejecuta esto (Sentencias 1) y sino esto otro (Sentencias 2)
if(<condición>){
// Sentencias 1
}else{
// Sentencias 2
}
Ejemplos:
Cuando se utilizan estructuras de control y operadores lógicos hay que tener en cuenta la precedencia.
Las expresiones vinculadas por && o || son evaluadas de izquierda a derecha, y la evaluación se
detiene tan pronto como se conoce el resultado verdadero o falso. Ejemplo:
if((k<10) && (++i==100)){
Primero se evalúa si k es menor a 10, si esta condición es falsa sin importar lo que sucede con lo
demás (por el &&) salta a la siguiente línea. Si en el diseño del algoritmo se necesita que i se
incremente de igual manera el resultado será incorrecto.
Arreglos de variables:
Los arreglos son una colección de variables del mismo tipo identificadas por un mismo nombre. Son
posiciones de memoria contiguas y cada elemento del arreglo puede ser direccionado por medio de un
índice que lo identifica. Los arreglos pueden ser de una o más dimensiones. La sentencia sería la
siguiente:
Cuando declaramos el arreglo podemos cargar los valores iniciales de la siguiente manera:
Nota: Esto solo se puede hacer al declararse, en otro caso debe accederse al elemento mediante un
índice.
Cadenas.
Para manejar string (cadena de caracteres ascii) en C se deben crear arreglos de char con suficiente
espacio como para manejar los string a utilizar más un byte adicional para almacenar el carácter nulo
(0, „\0‟), el cual indica final de string. Entonces si sabemos que los string a utilizar dentro del
algoritmo van a tener como máximo 10 caracteres, el arreglo a crear debe ser de 11 posiciones:
Esto equivale a:
Como C no maneja string, no podemos utilizar los operandos como para enteros o flotantes, se deben
utilizar funciones especificas para trabajar con ellos. C en su biblioteca estándar incluye la
librería string.h la cual dispone de funciones para comparar strings, para concatenar strings, para
copiar strings, para buscar caracteres dentro del string, etc. Para más información leer el help de la
misma.
Un ejemplo que vamos a realizar para ejercitar lo mostrado hasta el momento será visualizar
mediante un display de 7 segmentos la cuenta de pulsaciones que realicemos. El hardware a utilizar
sería el siguiente:
Código:
/*
* File: main.c
* Author: Suky
*/
#define _XTAL_FREQ 48000000
#include <xc.h>
void main(void) {
unsigned char k,j;
PIN_DIR_SW=INPUT;
PORT_DIR_DISPLAY=0x00;
// Comienza en 0
PORT_DISPLAY=Display7SegAC[0]; // El display es de Anodo Comun, como el arreglo..
// ..es para Catodo Comun se realiza el complemento
xD
k=0;
while(TRUE){
if(PIN_SW==0){
__delay_ms(10); // Preguntamos, esperamos y volvemos a preguntar para evitar
el efecto rebote..
if(PIN_SW==0){ // .. de esta manera nos aseguramos que es una pulsación
valida.
if(++k==10){ // Primero se incrementa k, luego se compara..
k=0;
}
PORT_DISPLAY=Display7SegAC[k]; // Cargamos nuevo valor
while(PIN_SW==0); // Esperamos a que se suelte
}
}
}
}
Diagrama de flujo: