Curso «Programación de PICs en C»

Hoy empiezo este mini curso de programación de microcontroladores PIC en lenguaje C desde 0. La idea es aprender paso a paso, realizar algunos proyectos simples y sumar conocimientos.

Voy a trabajar sobre el sistema operativo Ubuntu, con software libre o gratuito. Para todos aquellos que utilizan Windows no van a tener inconvenientes porque los paquetes de software son multiplataforma. Voy a utilizar como entorno de desarrollo MPLAB X en conjunto con el compilador XC8. No me voy a detener en la instalación y configuración del IDE y del compilador ya que existen muchos tutoriales sobre esto (por ejemplo).

El curso lo voy realizando sobre la marcha, más que nada basado en errores experiencias propias. Por este motivo lo verán formarse «online» y a medida que el tiempo me lo permita iré sumando contenido. Les recomiendo que lean el tutorial sobre XC8 que escribió Suky que es un buen punto de partida y el «Tutorial MPLAB C18 Desde Cero». También es de mucha utilidad tener a mano los ejemplos de Microchip para consulta: Microchip Code Examples (12F & 16F).

1.1. ESTRUCTURA DE UN PROGRAMA EN C

C es el lenguaje de programación que utilizaremos. Un programa C está compuesto por una o varias funciones, dentro de las cuales tendremos una o más instrucciones. Existe una función llamada «main» donde tendremos nuestro código principal.

Comentarios: Los comentarios son de mucha utilidad para entender lo que escribimos. Para realizar un comentario anteponemos en la línea de código «//» y el compilador la ignorará. También podemos comentar bloques de código utilizando «/*» para abrir y «*/» para cerrar.

Includes: utilizando «#include» incluimos en nuestro programa archivos de funciones o librerías que nos permitirán utilizar características especiales.

Ejemplo:

/* 
 * File:   main.c
 * Author: lucas
 * Created on 1 de abril de 2013, 22:20
 *
 * ESTO ES UN BLOQUE DE COMENTARIOS
 */

#include  // Incluimos la librería XC8

// ESTO ES UN COMENTARIO DE UNA LINEA

// Función principal
void main ()
{
    OSCCONbits.IRCF = 0b110; // Instrucción

}

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 minúsculas de mayúsculas.

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 ejemplo:

/* 
 * File:   main.c
 * Author: lucas
 * Created on 1 de abril de 2013, 22:20
 * Microcontrolador: PIC16F648A
 *
 * ¡Hola Mundo! en C (o como hacer destellar un LED)
 */

#include 
#include 
#include  // Librería 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 = 0b00000000; // Configuro puerto B como salidas
     while (1) // Bucle infinito
     {
         PORTBbits.RB0 = 0; // Apago pin RB0
         __delay_ms(500);
         PORTBbits.RB0 = 1; // Enciendo pin RB0
         __delay_ms(500);
     }
}

*** Podríamos hacer el cambio de estado del pin RB0 (toggle) utilizando la siguiente instrucción: 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 
#include 
#include  // Librería 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;
    }
}

1.4. UTILIZANDO PWM

Vamos a utilizar una señal PWM de 8 bits para controlar el brillo de un LED. Para configurar el módulo CCP1 en modo PWM solo basta con escribir 0x0C en el registro CCP1CON.

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 período 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 
#include 
#include  // Librería 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(void) {
    TRISB = 0;                  // Puerto B como salidas
    PORTB = 0;                  // Limpio el puerto B

// CONFIGURANDO PWM   
    CCP1CON = 0b00001100;       // Activamos el modo PWM
    PR2 = 250;                  // Frecuencia 250Hz
    T2CONbits.T2CKPS = 0b10;    // Prescaler del timer 2 en 1:16
    T2CONbits.TMR2ON = 1;       // Arranca el PWM       

// BUCLE INFINITO
    unsigned char i;           // Declaramos una variable
    while (1){
    for(i=0; i=50; i++)
    {
      CCPR1L = CCPR1L++;        // Seteando el ciclo de trabajo
      __delay_ms (100);     
    }
    i=0;                       // Reiniciamos la variable para comenzar el ciclo de nuevo
    }
    }

Si te gusto el curso y quieres colaborar podes hacer alguna donación económica o de material para poder seguir construyendo proyectos. Enterate cómo hacerlo en el siguiente enlace. ¡Muchas gracias!

Enlaces de interés