ESTRUCTURA DE UN PROGRAMA EN C
C es el lenguaje de programacin que utilizaremos. Un programa C est compuesto por una
o varias funciones, dentro de las cuales tendremos una o ms instrucciones. Existe una
funcin llamada main donde tendremos nuestro cdigo principal.
Comentarios: Los comentarios son de mucha utilidad para entender lo que escribimos. Para
realizar un comentario anteponemos en la lnea de cdigo // y el compilador la ignorar.
Tambin podemos comentar bloques de cdigo utilizando /* para abrir y */ para
cerrar.
Includes: utilizando #include incluimos en nuestro programa archivos de funciones o
libreras que nos permitirn utilizar caractersticas especiales.
Ejemplo:
/*
* File:
* Author:
* Created
*
* ESTO ES
*/
main.c
lucas
on 1 de abril de 2013, 22:20
UN BLOQUE DE COMENTARIOS
IMPORTANTE:
* Todas las instrucciones deben terminar si o si en ;.
* Los bloques de instrucciones empiezan con { y terminan con }.
* C es case sensitive, es decir que distingue minsculas de maysculas.
1.2. HOLA MUNDO! EN C (O COMO HACER DESTELLAR UN LED)
Hacer destellar un LED es muy sencillo. Tanto como crear un bucle infinito, escribir en un
pin, generar una demora y volver a escribir. El ejemplito:
/*
* File:
main.c
* Author: lucas
*** Podramos hacer el cambio de estado del pin RB0 (toggle) utilizando la siguiente
instruccin: PORTBbits.RB0 ^= 1;
1.3. LEER UN PULSADOR
Utilizando una estructura condicional if podemos leer un pulsador conectado en RB0
para encender o apagar un LED en el pin RB4.
/*
* File:
main.c
* Author: lucas
* Created on 1 de abril de 2013, 22:20
* Microcontrolador: PIC16F648A
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <xc.h> // Librera XC8
#define _XTAL_FREQ 4000000 // Indicamos a que frecuencia de reloj esta
funcionando el micro
// PIC16F648A Configuration Bit Settings
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTOSC
oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on
RA7/OSC1/CLKIN)
#pragma config WDTE = OFF
// Watchdog Timer Enable bit (WDT
disabled)
#pragma config PWRTE = OFF
// Power-up Timer Enable bit (PWRT
disabled)
#pragma config MCLRE = OFF
// RA5/MCLR/VPP Pin Function Select bit
(RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = ON
// Brown-out Detect Enable bit (BOD
enabled)
#pragma config LVP = OFF
// Low-Voltage Programming Enable bit
(RB4/PGM pin has digital I/O function, HV on MCLR must be used for
programming)
#pragma config CPD = OFF
// Data EE Memory Code Protection bit
(Data memory code protection off)
#pragma config CP = OFF
// Flash Program Memory Code Protection
bit (Code protection off)
// FUNCION PRINCIPAL
void main() {
TRISB = 0b00000001; // Configuro puerto B
PORTB = 0b00000000;
if (PORTBbits.RB0 == 1) {
PORTBbits.RB4 = 1;
} else {
PORTBbits.RB4 = 0;
}
}
Frecuencia PWM: el periodo de la onda PWM lo determina el tiempo que dura el conteo
del Timer2 desde 0 hasta el valor cargado en el registro PR2. Y como la frecuencia es la
inversa del perodo podemos deducir:
Donde:
* PR2 es el valor del registro PR2 (entre 0 y 255)
* FOSC es la frecuencia del cristal utilizado
* Prescaler es el prescaler del Timer2 (1, 4 16). Se configura en el registro T2CON.
Ciclo de trabajo (duty cicle): es la cantidad de tiempo que en un periodo la salida PWM
permanece en estado alto. El valor es determinado por el contenido del registro CCPR1L.
Podemos deducir el tiempo utilizando la formula:
Donde:
* CCPR1L es el valor del registro CCPR1L (entre 0 y 255)
* FOSC es la frecuencia del cristal
* Prescaler es el prescaler del Timer2 (1, 4 16)
Ejemplo dimerizado de un LED conectado a RB3
/*
* File:
main.c
* Author: lucas
* Created on 1 de abril de 2013, 22:20
* Microcontrolador: PIC16F648A
*
* Utilizando PWM para dimerizar un LED
*/
#include <stdio.h>
#include <stdlib.h>
#include <xc.h> // Librera XC8
#define _XTAL_FREQ 4000000 // Indicamos a que frecuencia de reloj esta
funcionando el micro
// PIC16F648A Configuration Bit Settings
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTOSC
oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on
RA7/OSC1/CLKIN)
#pragma config WDTE = OFF
// Watchdog Timer Enable bit (WDT
disabled)
// CONFIGURANDO PWM
CCP1CON = 0b00001100;
PR2 = 250;
T2CONbits.T2CKPS = 0b10;
T2CONbits.TMR2ON = 1;
//
//
//
//
// BUCLE INFINITO
unsigned char i;
while (1){
for(i=0; i=50; i++)
{
CCPR1L = CCPR1L++;
__delay_ms (100);
}
i=0;
el ciclo de nuevo
}
}
instrucciones
}
donde:
tipo_de_datos: es el tipo de dato que devolver esa funcin.
nombre_de_la_funcion: es el identificador que le damos a nuestra funcin.
tipo_y_nombre_de_argumentos: son los parmetros que recibe la funcin.
No todas las funciones reciben argumentos. En caso que posean se tratan simplemente de
variables locales que reciben un valor. Este valor se lo enviamos al hacer la llamada a la
funcin.
Un sencillo ejemplo:
// FUNCION "EJEMPLO". NO RECIBE ARGUMENTOS
void ejemplo (void){
PORTBbits.RB1 = 1;
// High pin RB1
__delay_ms (1700);
// Demora
PORTBbits.RB1 = 0;
// Low pin RB1
}
// FUNCION PRINCIPAL
void main(void) {
TRISB = 0b00000001;
PORTB = 0;
while (1){
if (PORTBbits.RB0 == 1){
ejemplo();
}
}
// Limpio el puerto B
// Bucle infinito
// Chequeo estado de RB0
// Llamado a funcin "ejemplo"
* Comportamiento del tipo aritmtico por defecto (si no especificamos si el tipo es con o
sin signo cual es el comportamiento?)
En XC8 tenemos los siguientes tipos para almacenar datos numricos enteros (sin
decimales), a saber:
En la tabla pueden ver que puse el nombre estandarizado de cada tipo. Ya que es ms fcil
de visualizar un int16 que un signed int o int32 que signed long (ya veremos como utilizar
los nombres estandarizados en nuestro cdigo).
Hay que tener en cuenta que los tipos de datos: bit y short long no son tipos estndar de C.
Y el que tipo long long es C99 Standard. El tipo bit tiene ciertas limitaciones que ya
veremos.
Tipos con signo o sin signo. Si el signo no se especifica junto al tipo de dato, el tipo de dato
por defecto ser signado con excepcin del tipo char que por defecto es unsigned o sin
signo. El tipo bit siempre se lo toma no signado.
Los rangos disponibles nos permitirn elegir eficientemente que tipo debemos utilizar al
disear nuestro cdigo. Por ejemplo para almacenar los das dentro de un mes (de 1 a 31)
con un tipo char (int8) nos alcanzar. Pero si queremos almacenar los das consecutivos
dentro de un ao (de 1 a 365) ya no nos alcanzar debiendo utilizar un signed int (int16).
Para almacenar el kilometraje de un auto ya no nos alcanzar con un signed int (int16) y
deberemos elegir un tipo mayor como ser signed short long (int24). Y si queremos
almacenar los habitantes de un pas nos quedamos cortos con un short long (int24)
debiendo utilizar para ello un unsigned long long (int32) pudiendo almacenar hasta
4.294.967.295 habitantes.
En XC8 tambin contamos con tipos para almacenar datos numricos reales o flotantes, a
saber: 24-bit y 32-bit floating-point types. Por ahora slo debemos saber que existen
adems float y double. Ms adelante ampliaremos.
1.7. USANDO UNA INTERRUPCION POR TIMER 0
El PIC16F648A cuenta con un temporizador programable que efecta una interrupcin al
desbordar su cuenta. Esto es muy til para gran cantidad de artilugios. Para trabajar con el
Timer 0 debemos hacer uso de algunos registros y bits de configuraciones. A saber:
* Registro TMR0: almacena el valor del contador en tiempo real. Se puede alterar su
contenido.
* Registro INTCON: configuracin de interrupciones. Los siguientes bits estn asociados al
Timer0:
T0IF: bandera que se pone a 1 al producirse un desbordamiento del timer.
T0IE: habilita la interrupcin por desbordamiento del timer.
GIE: habilita las interrupciones globalmente.
* Registro OPTION: contiene varios bits de control asociados a nuestro timer:
PS0, PS1 y PS2: bits de configuracin del prescaler (divisor de pulsos).
Veamoslo en un ejemplo. Cada 65ms desbordar el timer alterando el estado del pin RB0
donde colocamos un LED para hacerlo destellar.
/*
* File:
main.c
* Author: lucas
* Created on 1 de abril de 2013, 22:20
* Microcontrolador: PIC16F648A
*
* Uso de interrupcin por Timer 0
*/
#include <stdio.h>
#include <stdlib.h>
#include <xc.h> // Librera XC8
#define _XTAL_FREQ 4000000 // Indicamos a que frecuencia de reloj esta
funcionando el micro
#define LED PORTBbits.RB0
// PIC16F648A Configuration Bit Settings
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTOSC
oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on
RA7/OSC1/CLKIN)
#pragma config WDTE = OFF
// Watchdog Timer Enable bit (WDT
disabled)
#pragma config PWRTE = OFF
// Power-up Timer Enable bit (PWRT
disabled)
#pragma config MCLRE = OFF
// RA5/MCLR/VPP Pin Function Select bit
(RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = ON
// Brown-out Detect Enable bit (BOD
enabled)
#pragma config LVP = OFF
// Low-Voltage Programming Enable bit
(RB4/PGM pin has digital I/O function, HV on MCLR must be used for
programming)
#pragma config CPD = OFF
// Data EE Memory Code Protection bit
(Data memory code protection off)
#pragma config CP = OFF
// Flash Program Memory Code Protection
bit (Code protection off)
unsigned char cuenta = 0;
// FUNCION DE INTERRUPCION
void interrupt ISR(void) {
cuenta++;
INTCONbits.TMR0IF = 0;
}
// FUNCION PRINCIPAL
void main () {
TRISB = 0;
PORTB = 0;
// Incremento variable
// Limpiamos bandera de desborde
INTCONbits.GIE = 1;
OPTION_REGbits.PS0 = 1;
OPTION_REGbits.PS1 = 1;
OPTION_REGbits.PS2 = 1;
OPTION_REGbits.PSA = 0;
OPTION_REGbits.T0CS = 0;
// Bucle infinito
while (1){
if (cuenta == 10){
LED ^= 1;
cuenta = 0;
}
}
}
/*Includes globales*/
#include <pic18.h>
#include <xc.h>
#include <delays.h> /* Para utilizar demoras en nuestro cdigo debemos
incluir la librera delays.h.*/
#include <plib/usart.h> //Libreria para el manejo del modulo USART
/*Includes locales*/
#include "configuracion_de_fuses.c"
#include "configuracion_hard.c"
/*declaracin de variables globales*/
char CaracterRx;
char MensajeTx[] = "COMUNICACION SATISFACTORIA";
/*declaracin de funciones*/
void MyMsDelay(int ms);
//void interrupt Interrupcion();
/////////////////////////////////////////////////////////////////////////
//////
//
Programa Principal
//
/////////////////////////////////////////////////////////////////////////
//////
void main(void)
{
// cambiamos la frecuencia del oscilador interno del valor por defecto de
32khz
// a 8mhz mediante los bits del registro OSCCON
OSCCONbits.IRCF2 = 1;
OSCCONbits.IRCF1 = 1;
OSCCONbits.IRCF0 = 1;
ADCON0 = 0X00,ADCON1 = 0X0F,CMCON = 0X07; //puerto A con todos los
pines digitales
TRISA = 0X00; // puertos A B y C como salida. Recordar Tip: el 0 es
una o de ouput y el 1 una I de input!!!
TRISB = 0X00;
TRISC = 0X00;
LATA = 0X00; // ponemos los puertos en cero
LATB = 0X00;
LATC = 0X00;
PINLED0 = 0; // Seteamos el pin como salida para el LED
PINLED1 = 0; // Seteamos el pin como salida para el LED
// configuramos el puerto serie
OpenUSART(USART_TX_INT_OFF &
//Activar la interrupcion por la
USART_RX_INT_ON &
recepcion de dato del buffer de Rx del USART
USART_ASYNCH_MODE &
//Modo asincrono (fullduplex)
USART_EIGHT_BIT &
//8 bits de datos
USART_CONT_RX &
//Recepcin continua
USART_BRGH_HIGH, 51); //9600 Baudios
// (frecuencia a la que trabaja el cpu/dbaudrate)/16)-1
// (8.000.000/9600)/16)-1=51
INTCONbits.PEIE = 1; //Activamos interrupcion de perifericos
INTCONbits.GIE = 1; //Activamos las interrupciones globales
while(BusyUSART());
putsUSART(MensajeTx);
while(1){
//Bucle infinito
while(BusyUSART());
putsUSART("\n\rUsted digito la tecla: ");
while(BusyUSART());
WriteUSART(CaracterRx);
PIR1bits.RCIF = 0; //Desactivamos la bandera de recepcin en el
buffer de entrada del USART
}
void MyMsDelay(int ms)
{
while(ms--)
{
__delay_ms(1);
}
}
/*
CLCULOS DE LOS RETARDOS
Para utilizar demoras en nuestro cdigo debemos incluir la librera
delays.h. En ella tenemos 4 funciones:
Delay10TCYx(i)
instrucciones
*
Delay100TCYx(i)
instrucciones
*
Delay1KTCYx(i)
instrucciones
*
Delay10KTCYx(i)
de instrucciones *
->
10.Tcy.i
i
->
100.Tcy.i
i
-> 1000.Tcy.i
i
-> 10000.Tcy.i
i
1 TCY est formado por 4 ciclos de reloj. 4 ciclos de reloj tarda el pic
en procesar una instruccin. Entonces si queremos saber cuanto demora 1
TCY debemos multiplicar 4 veces lo que demora 1 ciclo del reloj del CPU.
1 TCY se calcula como
1
4 * -------Fosc
donde fosc = frecuencia que llega a la CPU del pic (NO LA FRECUENCIA DEL
CRISTAL!!!)
Clculo de delay para generar 1 segundo con frecuencia de trabajo de CPU
a 8mhz
-------------------------------------------------------------------------------Las funciones de retardos usan como parametro los TCY. Entonces tenemos
que calcular cuantos TCY hay que utilizar para obtener 1 segundo. Dijimos
que:
1
1 TCY se calcula como 4 * -------Fosc
Fosc= 8Mhz = 8*10^6 hz = 8.000.000hz
por lo tanto 1 TCY =
1
1
4 * ---------- = ---------- = 0,0000005sec
8*10^6 Hz
2*10^6Hz
1
1seg * ----------0,0000005 seg
= 2.000.000 TCYs
Ahora que sabemos que necesitamos 2.000.000 TCYs para demorar un segundo
debemos elegir una de las
funciones que nos brinda la librera delays.h debiendo tener presente que
slo reciben como parmetro
valores de i de 1 a 255. Si se toma 0 equivale a 256
Para este caso debemos usar la funcin Delay10KTCYx(i) ya que:
2.000.000 TCYs
----------------- = nos da un valor de 200i
10.000 TCY
Y con dicho valor 200 logramos el retardo de 1 segundo!
Delay10KTCYx(200); //demora de 1 seg
*/