Está en la página 1de 15

FACULTAD DE INGENIERÍA ELECTRÓNICA Y

ELÉCTRICA
“Lenguaje de Programación”
Alumna :

Alarcón Palomino, Mary Jhanira 16190175

Profesor : Yuri Mijail Arrieta Laya

Turno : Lunes

Horario : 2:00 – 5:00 pm

Fecha : 2016
LENGUAJE C PARA MICROCONTROLADORES
Introducción
Un programa codificado en lenguaje C resulta muy útil en la aplicación de
Controladores, dado que su compilación es bastante eficiente y óptima acercándose
a la codificación de lenguaje de máquina. Lo descriptivo de la sintaxis permite
elaborar de mejor forma los algoritmos olvidándose de los molestos push y pop
usados en el lenguaje de máquina cuando se usan saltos a subrutinas.
C es un lenguaje bastante conciso y en ocasiones desconcertante. Considerado
ampliamente como un lenguaje de alto nivel, posee muchas características
importantes, tales como: programación estructurada, un método definido para
llamada a funciones y para paso de parámetros, potentes estructuras de control,
etc.
El lenguaje C dispone de todas las ventajas de un lenguaje de programación de alto
nivel (anteriormente descritas) y le permite realizar algunas operaciones tanto sobre
los bytes como sobre los bits (operaciones lógicas, desplazamiento etc.). Las
características de C pueden ser muy útiles al programar los microcontroladores.
Además, C está estandarizado (el estándar ANSI), es muy portable, así que el
mismo código se puede utilizar muchas veces en diferentes proyectos. Lo que lo
hace accesible para cualquiera que conozca este lenguaje sin reparar en el
propósito de uso del microcontrolador. C es un lenguaje compilado, lo que significa
que los archivos fuentes que contienen el código C se traducen a lenguaje máquina
por el compilador. Todas estas características hicieron al C uno de los lenguajes de
programación más populares.

Posibles modelos de memoria


Así como el programador de PC tiene que elegir entre los modelos de memoria tiny,
small, medium,compact, large y huge para definir la segmentación de la RAM, el
programador de 8051 tiene que decidir el modelo de memoria a utilizar, el cual
determina el lugar de residencia del programa y de los datos.
C51 soporta actualmente las siguientes configuraciones de memoria:
 ROM: el tamaño máximo del programa que permite generar C51 es de 64K,
sin embargo es posible aumentarlo hasta 1MB mediante el modelo BANKED
descrito más abajo. Todos los elementos que se deseen almacenar en la
EPROM/ROM, tales como constantes, tablas etc., deben declararse como
code.
 RAM: Se admiten tres modelos de memoria, SMALL, COMPACT y LARGE
SMALL: todas las variables y segmentos de paso de parámetros se guardan en
RAM interna.
COMPACT: las variables se almacenan en una región de RAM externa de
acceso paginado y de tamaño máximo 256 bytes. El acceso a las mismas se
hace indirectamente por medio de instrucciones "MOVX A,@R0". Los registros
internos del 8051 se usan para variables locales y parámetros de función.
LARGE: las variables se almacenan en memoria externa, y se accede a ellas
con instrucciones
"MOVX A,@DPTR", lo que permite disponer de un espacio máximo de 64Kbytes
para las variables. Los registros internos del 8051 se usan para variables locales y
parámetros de función.
Modelo BANKED: Con este modelo el código puede ocupar hasta 1MB utilizando
algunas patillas de un puerto del 8051, o un latch mapeado en memoria. Estos hilos
se utilizan para paginar la memoria por encima de la dirección 0xFFFF. Dentro de
cada bloque de 64KB debe existir un bloque común (COMMON) para hacer posibles
las llamadas a funciones que se encuentren en bancos distintos.
Además de la elección del modelo de RAM, es posible utilizar un modelo
globalmente y forzar que ciertas variables y objetos residan en otros espacios de
memoria.
Cada tipo de memoria tiene sus pros y sus contras. Aquí se ofrecen algunas
recomendaciones para hacer el mejor uso de los mismos.
DATA: 128 bytes; área utilizada por el modelo SMALL
 El mejor para: Datos a los que se accede con frecuencia, variables usadas
por rutinas de interrupción, variables de rutinas re-entrantes.
 El peor para: Arrays y estructuras de tamaño de medio a grande.
IDATA; No dependiente del modelo utilizado
 El mejor para: Acceso rápido a arrays y estructuras de tamaño medio (unos
32 bytes cada uno, sin sobrepasar los 64 bytes). Ya que el acceso a este tipo
de datos suele realizarse indirectamente, mediante punteros, éste tipo de
memoria es el mejor para los mismos. También es un buen lugar para la pila,
a la que siempre se accede indirectamente.
 El peor para: Grandes arrays de datos.
CODE: 64K bytes
 El mejor para: Constantes y grandes tablas, además de para el código del
programa, ¡No faltaba más!
 El peor para: ¡Variables!
PDATA: 256 bytes; área utilizada por el modelo COMPACT
 El mejor para: Accesos a datos que requieran una velocidad intermedia, así
como a matrices y estructuras de tamaño moderado.
 El peor para: Arrays y estructuras cuyo tamaño supere los 256 bytes. Datos
a los que se accede con mucha frecuencia, etc.
XDATA; área utilizada por el modelo LARGE
 El mejor para: Arrays y estructuras cuyo tamaño supere los 256 bytes.
Variables a las que se accede con poca frecuencia.
 El peor para: Datos a los que se accede con mucha frecuencia, etc.

Elementos básicos
Comentario, este permite la documentación del código y se usa de acuerdo a la
siguiente sintaxis
/* Este es un comentario */ o
// Este es un comentario
Inicio y fin de bloque, permite agrupar un número de instrucciones las que pueden
ser ejecutadas con cierta prioridad. Se usa { para iniciar bloque y } para finalizar
bloque.
{ // inicio de bloque
// instrucciones
} // final de bloque
Identificador, es el nombre que se le da a una variable o función por lo general
asociado al tipo de dato que ha de contener la variable o al tipo de procedimiento
que ha de realizar la función.
Tipo, es una palabra reservada definida que indica el tipo de variable que se ha de
definir y su alcance numérico, esto de acuerdo a:
Tipo Código Tipo de compilador C
char Entero de 8 bit Estándar
int Entero de 16 bit Estándar
long Entero de 32 bit Estándar
float Real de codificado en 32 bit Estándar
double Real codificado en 64 bit Estándar
uns16 Entero sin signo 16 bit CC5x (*)
uns32 Entero sin signo 32 bit CC5x (*)
Así, la definición de las variables se hace usando el tipo y un identificador que da el
nombre de la variable finalizado por un ; (punto y coma). También puede definir
varias variables con un solo tipo. En esta caso son separadas por , (coma) pero
siempre se finaliza con ; (punto y coma).
char i; // define variable tipo char de 8 bits
char j,i;
float x,r;
long a,b,c,d;
int i,j; // define dos enteros

Estructura básica de un programa


La estructura de un programa básico en lenguaje c se indica en el esquema de la
Fig. 1, el cual muestra un bloque principal llamado main() y bloques de funciones.
Puede ser, dependiendo de la aplicación, que solo se requiera del bloque principal.

El siguiente código fuente contempla la declaración de las variables y el módulo


principal.
// Ejemplo de programa sin funciones
// Aquí se definen las variables globales
void main()
{
// aquí se definen las variables locales
// aquí van las instrucciones
}

Una variación permite incorporar la declaración de las variables y el módulo principal


y la zona donde se recomienda se escriban las funciones.
// Ejemplo de programa con funciones
// Aquí se definen las variables globales
// Aquí se Escriben las funciones
void main()
{
// Aquí se definen las variables locales
// Aquí van las instrucciones y llamados a funciones
}

Instrucciones básicas
Estos son los elementos más básicos para la generación de un programa, se
considera: asignación, operaciones matemáticas, operaciones lógicas.
 Asignaciones
Consiste en asignarle un valor a una determinada variable. En el ejemplo se
declaran dos variables de 8 bits y luego se le asignan los valores respectivos.
char i, j; // Declaración de variables
void main()
{
i=10;
j=20;
// Aquí van otras instrucciones
}

Los valores asignados a las variables tipo char o int pueden tener distintos formatos
ya sea decimal, octal, hexadecimal o binario. Esto es muy útil dado que en el más
bajo nivel siempre se trabaja en binario.
Formatos de asignación
La asignación puede ser hecha en decimal, hexadecimal, binario o carácter (si es
que se conoce).

Indica el formato binario para el compilador CC5X.


Note que el número codificado hexadecimal comienza con 0x, es decir 31H será
0x31 y el binario comienza con 0b, es decir, 00110001 será 0b00110001 (este
segundo formato corresponde al entregado por el compilador CC5X). El caso de las
variables tipo char se tiene el siguiente ejemplo, se inicializa una variable con el
valor 1 ASCII, sin embargo esto se puede realizar de varias formas.
char c;
void main()
{
c=0x31; // en hexa
c=49; // en decimal
c=’1’; // en caracter
}

int d;
long x;
void main()
{
d=0xA031; // en hexa
x=49000L; // en long
}

 Operaciones Matemáticas
Se tienen la suma, resta multiplicación y división, división entera más las
operaciones de incremento y decremento.
 Operaciones Lógicas
Se realizan con variables enteras y afectan directamente al bit del dato, se tienen
los operadores lógicos y los desplazamientos a la Izquierda y a la derecha.

Sentencias de control
Permiten definir las funciones a ejecutar dependiendo de ciertas condiciones que
se deben evaluar. Estas condiciones pueden ser tipo lógicas, tipo mayor, menor o
igual o ambas. Por lo general se agrupan entre paréntesis para definir la prioridad
de la evaluación de la condición.
 Sentencia if-else
Puede usarse el if solo o el if-else. Si la condición es verdadera entonces se ejecuta
una instrucción o un grupo de instrucciones en caso contrario se ejecuta la que
sigue.
 Sentencia while
Esta evalúa la condición al inicio, si ésta es verdadera, entonces se ejecuta el bloque
que viene a continuación en caso contrario lo salta. El formato es el siguiente:
while(condición) instrucción; while(condición) {
instrucción_1;
...
instrucción_n;
}
En el siguiente código fuente se muestra que el ciclo while se realiza ya que i
siempre sea mayor que 0. Sin embargo en algún momento i será menor que 0, dado
que k se va decrementando y se hace igual a i dentro del ciclo.
Para salir de un ciclo donde no existe posibilidad de que la condición sea alterada,
se puede usar la sentencia break.
 Sentencia switch - case
Esta instrucción se usa cuando se deben seleccionar entre varias opciones tipo
numéricas. Es básicamente un selector.
 Sentencia for
Permite el uso de los clásicos ciclos for

Funciones
Las funciones permiten agrupar código y optimizar el tamaño de éste. Se tienen
distintos tipos de funciones:
¨ Las que solo ejecutan tareas y no devuelven valor.
¨ Las que ejecutan y devuelven un valor.
¨ Las que reciben parámetros y no devuelven.
¨ Las que reciben parámetros y devuelven información.

Punteros
Los punteros son variables que almacenan la dirección de una variable, el contenido
de dicha variable es accesado en forma indirecta. La variable tipo puntero se define
en forma similar a una variable que almacena datos, pero se agrega un *.
char *p; //p es un puntero a un char
char i, *p; // i es un char y p es un puntero a char
int j, *p; // j es una var entera y p es un puntero a entero

¿Qué quiere decir puntero a char o puntero a int?.


Simple, cuando un puntero a char se incrementa, la dirección almacenada en la
variable se incrementa en una posición de memoria en cambio en un puntero a
int se incrementa de 2 posiciones. En el caso de que fuera a una variable tipo long,
la dirección se incrementaría en 4 posiciones de memoria.

Arreglos y matrices
Los arreglos son vectores unidimensionales (llamados string o cadenas) o
bidimensionales (Matrices). Su formato es el siguiente:
tipo ident_arreglo[num_elementos];
tipo ident_arreglo[num_fila][num_col];
A continuación se muestran ejemplos de arreglos unidimensionales y
bidimensionales
char cadena[10];// define una var string llamada cadena de 10 char
int datos[15]; // define una var string llamada datos de 15 int
char cad[2][3]; // define una matriz cad de 2 filas y 3 col

Los arreglos pueden contener números o caracteres, pueden ser usados para
manejo de cadenas de caracteres o string. Es conveniente siempre inicializar con
un valor los arreglos. El primer elemento del arreglo corresponderá al almacenado
en la posición 0 y el n-ésimo elemento corresponderá al almacenado en la posición
n-1 del arreglo. Cuando se definen los arreglos, estos ocupan zonas completas de
memoria, con direcciones consecutivas.

Arquitectura del Microcontrolador


Para las aplicaciones se utilizará un Microcontrolador basado en la familia 16F87Xa
de microchip. Este microcontrolador contieneregistros, módulos ADC, comunicación
serial, temporizadores y manejo de interrupciones. Además posee un memoria de
programa de entre 4 y 8Kbytes, su reloj puede ser de entre 4 y 20MHz. (ver data
µC16F87xA).
Un esquema simplificado es el de la Fig

Esquema simple del µC 16F87XA.


Es bastante sencillo de hacer funcionar, solo basta un par de capacitores, el cristal
más un resistor, de acuerdo a la Fig. 5. En dicho circuito, el terminal 28
correspondiente al bit más significativo del puerto B, se usará como monitor del
funcionamiento. La tarea básica a implementar será poner en 1 y en 0
alternadamente el bit mencionado de tal forma de visualizarlo mediante un
instrumento.
• TIMER0 TIMER1 TIMER2 10bit-ADC
• EEPROM CCP1,2 USART Synchronous
• Serial Port
• FLASH

Memoria De Programas
• RAM
• File
Registers
• PORTA
• PORTB
• PORTC
• CPU
• PCL
• REF COMPARADOR
Voltaje

Consideraciones Especiales
Respecto de las variables enteras
Se debe considerar que las variables enteras se pueden acceder al bit o al nibble
en el caso de ser de 8 bit.
Las de 16 bit se pueden acceder tanto al bit, de 4, de 8 bit o completas. Esto implica
tanto su lectura como su asignación de algún valor.
char i;
uns16 dato; //Entero sin signo de 16 bits
void main()
{
i=10; // 0x00001010 0x0a
dato=2048; // 0b0000100000000000=0x0800
//modificando un bit

i.0=1; // se modifica el bit de menos peso luego i=0b00001011=11


dato.low8=0xff; //solo se acceden los 8 bit de menos peso asi dato=0x8ff;
}

Respecto de las cadenas de caracteres


El compilador CC5X inicializa las cadenas o string de acuerdo al siguiente formato:
const char *ps = "Hello world!";
const char a2[] = "ab";
Cuando se han de pasar cadenas o string a funciones puede usarse la siguiente
sintaxis:
void funcion(const char *str)
{
// codigo
}
void main()
{
char tab[20]; // string en RAM
const char ctab[] = “A string”;

funcion(“Hello”); // Pasando el string explicito


funcion(&tab[i]); // Usando una direccion de la cadena
funcion(ctab); // Pasando la direccion del primer elemento
}
Manejo de Puertos de E/S
El microcontrolador posee al menos 3 puertos que pueden ser usados como
entradas-salidas digitales o como entradas análogas (si corresponde). Los Puertos
son básicamente registros de 8 bits basados en Flip-Flops Tipo D, que pueden ser
usados como entrada o salida. Los nombres de los puertos corresponden a los
especificados en la data del microcontrolador, así se tiene el PORTB de 8 bits, el
PORTC de 8 bit y el PORTA de 5 bits. Dichos bits se configuran como entrada o
salida a través de un registro llamado TRIS. Para el caso del Puerto A será TRISA,
para el puerto B, se usa el TRISB, etc. Los puertos señalados poseen más de una
función. Estos puertos del microcontrolador se usan como si fueran variables
enteras de 8 bit y pueden accesarse nivel de bit o en forma de bytes.
Programa básico de Test
El programa permite verificar el funcionamiento del µC a través de la generación de
un pulso en un terminal del dispositivo. Considerando el bit 7 de puerto B, se tiene.
void main()
{
TRISB.7=0;
PORTB.7=1;
while (1)
{
if(PORTB.7==1) PORTB.7=0;
else PORTB.7=1;
}
}

El terminal deberá ser inspeccionado a través de una sonda lógica. Si no se dispone


de ella, entonces puede usarse un retardo, el cual en conjunto con un LED permitirá
visualizar los cambios de estado.
void ret(uns16 r)
{
while(r>0) { r--;}
}
void main()
{
TRISB.7=0;
PORTB.7=1;
while (1)
{
if(PORTB.7==1) PORTB.7=0;
else PORTB.7=1;
ret(5000);
}
}
Programación Conversor Análogo-Digital
 La conversión Análogo- Digital
El proceso de conversión análogo digital consiste en transformar una señal de
voltaje analógico en un código o número digital, también llamado cuenta, este
proceso tiene varias etapas. Inicialmente se debe contar con la señal eléctrica que
se ha de convertir, posteriormente viene el proceso de muestreo, que transforma la
señal continua s(t) en una señal discreta s(k), después el mecanismo llamado ADC
(Analog to Digital Converter) realiza la transformación del voltaje analógico
asignándole al voltaje de entrada un código binario. Este código puede ser de 8bit,
10bit, 12bit o 16bit. La salida del número binario se puede visualizar en los
terminales de salida digital del ADC, de acuerdo al tipo de conversor, éste poseerá
por ejemplo 8 terminales de salida si es de 8bits.

Internamente el ADC posee distintos mecanismos de conversión, los más comunes


son los llamados de aproximación sucesiva, que pueden tener salida paralela o
salida serial. El proceso de conversión requiere un tiempo, el que está dado por el
tc (tiempo de conversión) del sistema en conjunto con otros elementos que aportan
retardos.
De esta forma el tiempo de muestreo ts (Sample Time) que establece cada cuanto
tiempo es posible efectuar la digitalización, dependerá de la frecuencia de la señal
a digitalizar, el tiempo que se demora el mecanismo en realizar la conversión.

Resumen de USING y REGISTERBANK


Expresado en pseudo-código
if(interrupt routine = USING 1){
las funciones llamadas desde aquí deben usar #pragma
REGISTERBANK(1)
}
Nota: las funciones llamadas por la interrupción, sólo pueden llamadas desde
funciones que utilicen el banco de registros 1.
 Reglas generales a seguir
Las siguientes reglas permiten que el compilador haga el mejor uso de los recursos
del procesador.
En general, la aproximación a C desde el punto de vista del programador en
ensamblador no es dañina en absoluto.
 Regla 1
Utilizar siempre que sea posible variables de 8 bit. El 8051 es una máquina de 8 bits
que procesa los char con mayor eficacia que los int.
 Regla 2
Utilizar siempre que sea posible variables unsigned. El 8051 no tiene instrucciones
para las operaciones aritméticas con signo, por lo cual las operaciones con signo
siempre añaden másinstrucciones del 8051.
 Regla 3
Procurar eliminar las divisiones salvo que sean entre números de 8 bits. El 8051
tiene una sola instrucción para dividir dos números de 8 bits. Dividir números de 32
bits entre números de 16 bits puede resultar muy lento, salvo que se utilice un
80C537.
 Regla 4
Evitar el uso de estructuras de bit que producen código lento e ineficaz. En su lugar
declarar bits individualmente, utilizando la clase de almacenamiento "bit".
 Regla 5
El estándar ANSI dice que el producto de dos cantidades de 8 bits (char) es también
un char. En consecuencia, cualquier unsigned char que al ser multiplicado pueda
producir un resultado superior a 255 debe declararse como unsigned int.
Pero tal como señala la regla 15.1 no debe utilizarse un int, cuando pueda servir un
char. La solución es convertir temporalmente (cast) el char a int. En el siguiente
ejemplo el producto potencialmente puede tener 16 bits, pero el resultado es
siempre de 8 bits. El cast o forzado de tipo "(unsigned int)" asegura que el C51
realice una multiplicación de 16 bits.
{
unsigned char z ;
unsigned char x ;
unsigned char y ;
z = ((unsigned int) y * (unsigned int) x) >> 8 ;
}

Aquí se multiplican dos números de 8 bits, y el resultado se divide entre 256. El


resultado intermedio tiene 16 bits debido a que los números x e y, han sido cargados
por la rutina de multiplicación de la librería como ints.
 Regla 6
Los cálculos con operandos enteros que, debido a un cuidadoso escalado, siempre
producen resultados de 8 bits siempre funcionarán. En:
unsigned int x, y ;
unsigned char z ;
z = x*y/256 ;

C51 igualará z al byte de dirección más alta (menos significativo) del resultado
entero. Este resultado es independiente de la máquina utilizada, ya que viene
impuesto por el estándar ANSI. En este caso C51 accede al byte menos significativo
directamente, ahorrando código, ya que la divisiónno se realiza.

Números en coma flotante


En las operaciones con números en coma flotante, un operando siempre se mete
en la pila aritmética de RAM interna. En el modelo SMALL se usa la pila del 8051,
pero en los otros modelos se crea un segmento fijo en la primera dirección
disponible por encima del área del banco de registros. En las aplicaciones en las
que sea necesario ahorrar espacio en RAM interna, no debería utilizarse
matemática de coma flotante. La coma fija es una alternativa más real.

También podría gustarte