martes, 16 de abril de 2013

Programa PIC básico vs Programa Atmel básico

Hola!, hace mucho que no escribía por acá. Me anoté en la facultad para hacer el proyecto final de ingeniería así que estuve ocupado haciendo bastantes cosas. Lo más probable es que haga algún proyecto de robótica o simulación :)

Aparte, comencé a ir al Club de Robótica FIUBA http://www.clubderobotica.com.ar/ , del cual aprovecho para agradecerles a los organizadores por haberme permitido que me uniera. La didáctica es bastante sencilla: hay distintos proyectos, ya sea para principiantes y o avanzados, los cuales están dirigidos por un coordinador. (También es posible proponer un tema propio). Una vez que nos decidimos por un proyecto, comenzamos a llevarlo adelante.

Algunos de los proyectos son autitos seguidores de líneas, brazos robóticos, robots mini sumo, robots que encuentran salidas a laberintos, cubo de leds, etc.

Como plataforma principal de trabajo utilizan micros de la marca Atmel programados mediante un programador USB.

Para probar qué tan fácil es programar los ATMEL les voy a mostrar el mismo código que generé en mis primeros tutoriales para encender un LED tanto con el PIC16F88 como con el Atmega48 (Quise comprar el Atmega88 pero no lo tenían en los locales del centro). Me salió $19,72 en sycelectronica.

Los Atmega48, 88 y 168 son micros de 8bits, de 28 pins y con similares características. Lo que los diferencia son los tamaños de memoria flash. El atmega48 tiene 4k de memoria mientras que el 88 y el 168 tienen 8 y 16k respectivamente.

Atmega48/88/168


Veamos algunas comparaciones simples entre el PIC16F88 y los atmel anteriores:

PIC16F88


Al igual que el PIC16F88 tienen un oscilador interno de hasta 8Mhz y soportan hasta 20 Mhz con un xtal externo.

Los bits de configuración en la arquitectura Avr (atmel) se les llaman FUSES y se especifican por fuera del código en C.

En cantidad de pins, el pic tiene 18 mientras que los atmel tienen 28.

El voltaje de operación en el pic va de los 2.0V a los 5.5V mientras que en el atmel va de 2.7V a 5.5V.

Ambos productos tienen soporte para grabación "in circuit", es decir, cambiar el firmware del micro estando en un circuito que hayamos diseñado.

Ambos tienen soporte para comunicación serial mediante USART.

Microchip tiene su entorno de desarrollo MPLABX basado en Netbeans (Windows/Linux) mientras que Atmel tiene a AtmelStudio basado en Visual Studio 2010 (Windows). Igualmente hay varios IDEs distintos y Open Source disponibles para Windows/Linux.

Después tienen otros pines que todavía no utilicé como el PWM y entradas analógicas.

Ejemplo que permite prender y apagar un led:

PIC16F88 (XC8):

#define _XTAL_FREQ       8000000
#include <xc.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

__CONFIG(MCLRE_ON & CP_OFF & CPD_OFF & LVP_OFF & BOREN_OFF &
         WDTE_OFF & FOSC_INTOSCIO & PWRTE_OFF);
__CONFIG(IESO_OFF & FCMEN_OFF);

#define posLED  _PORTB_RB0_POSITION // Posición en la que se encuentra el Led
#define PORTLED PORTB 
 
/**
 * Programa principal
 */
void main(void) {

    // Configura el Oscilador interno a 8Mhz
    OSCCONbits.IRCF = 0b111;
     
    // Configura un 0 en la posición que le corresponde al LED
    // y un 1 a todas las demás.
    // 0 = Salida, 1 = entrada
    TRISB = ~(1 << posLED);
    // La línea anterior es equivalente a
    // TRISB = 0b11111110;
 
    // Bucle principal
    while(true) {
 
        PORTLED |= (1 << posLED);    // Setea en estado alto (High) el LED
         
        __delay_ms(500); // Se queda esperando 500 milisegundos
         
        PORTLED &= ~(1 << posLED);    // Setea en estado bajo (Low) el LED
         
        __delay_ms(500);
 
    }
}

Atmega48, 88, 168 (avr-gcc)
#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>

#define posLED PORTD0 // Posición en la que se encuentra el Led
#define PORTLED PORTD

int main( void) {
       
        // Configura un 1 en la posición que le corresponde al LED
        // y un 0 a todas las demás.
        // 1 = Salida, 0 = entrada
        DDRD = (1 << posLED);
        // La línea anterior es equivalente a
        // DDRD = 0b00000001;
       
        // Bucle principal
        while(1) {
              
               PORTLED |= (1 << posLED);     // Setea en estado alto (High) el LED
              
               _delay_ms(500);         // delay 500 ms
              
               PORTLED &= ~(1 << posLED);    // Setea en estado bajo (Low) el LED
              
               _delay_ms(500);         // delay 500 ms
        }
}




El ejemplo con PIC está distinto que el de los posts anteriores para que se parezca más al código del Atmel.

En cuanto tenga tiempo veo si puedo comparar los códigos para utilizar el USART. Hasta la próxima!

domingo, 24 de marzo de 2013

Lectura de Inputs en un pic

En los post anteriores estuvimos usando el bit 0 del puerto B del PIC16F88 para encender y apagar un led cada 500 ms. Esto quiere decir que usamos ese bit como salida. Ahora les mostraré como utilizar un bit en modo entrada de datos digital utilizando un botón de tipo "push".

Cuando un pin de nuestro PIC (ej.: RB4) se utiliza como entrada, lo que debemos hacer es conectarlo a +5 Volts mediante 2 resistencias de 1k (R1) y 10k (R2) en ese orden. Entre estas 2 resistencias colocamos el botón (S1) conectado al negativo de nuestra fuente.

Imagen 1 - Diagrama de conexión de una entrada

Imagen 2 - Switch (S1) contectado entre las resistencias y los 0Volts


El efecto que provoca es que el Input siempre esté en estado alto (HIGH o 1). Cuando se presiona el botón (S1) los 5V se dirigen a GND lo que provoca que el input esté en estado bajo (LOW o 0).

Ejemplo 1:

Para nuestro primer ejemplo vamos a hacer lo siguiente:

Si el botón está pulsado (RB4 = 0), prender el led,
Sino, mantenerlo apagado.

Utilizaremos el bit 4 del puerto B tal como aparece en la figura anterior.

#define LED     PORTBbits.RB0 // El Led se encuentra conectado en RB0
#define posLED  0             // Posición en la que se encuentra el Led

#define BUTTON  PORTBbits.RB4 // El botón se encuentra conectado en RB4
#define posBUTTON   4         // Posición en la que se encuentra el botón

void main(void)
{
    // Configura el oscilador
    ConfigureOscillator();

    // Configura un 0 en la posición que le corresponde al LED
    // y un 1 a todas las demás.
    // 0 = Salida, 1 = entrada
    TRISB = ~(1 << posLED) | (1 << posBUTTON);
    // La línea anterior es equivalente a
    // TRISB = 0b11111110;

    // Inicializo el led apagado
    LED = 0;

    // Bucle principal
    while(true) {
        // Enciende el led sólo si está presionado el botón
        LED = BUTTON ? 0 : 1;
    }
}

En el bucle principal hacemos que el valor del LED sea igual al 0 si BUTTON (RB4) es HIGH, sino 0.

Código completo del ejemplo 1

Ejemplo un poco más complicado:


En este ejemplo vamos a probar lo siguiente: Cuando se presione el botón, si el LED estaba apagado, encenderlo, si el LED estaba encendido, apagarlo.

Para esto podríamos reemplazar el bucle principal del ejemplo anterior por lo siguiente:

// Bucle principal
while(true) {
     while(BUTTON == 1) {
     }
     LED = 1;

     while(BUTTON == 0) {
     }
     LED = 0;
}

Si probamos compilarlo y pasarlo a nuestro PIC, este código funciona correctamente, sin embargo lo hará de manera errática. A veces nos hará caso y a veces parecerá que el LED se apaga y se prende muy rápidamente.

Este comportamiento se debe al "rebote" eléctrico que hace el botón al presionarse. Para explicarlo de forma simple, el código dentro del bucle principal se ejecuta muy rápidamente, a razón de millones de instrucciones por segundo mientras que el movimiento del dedo empujando el botón es muy lento lo que provoca que este toque la superficie y rebote hasta estabilizarse pasando de alto a bajo en cuestión de microsegundos.

Para evitar esto debemos manejar este rebote mediante un contador. En este ejemplo haremos que un botón esté presionado sólo si se mantiene apretado durante 10ms.

uint8_t contadorDeRebotes;      // Cuenta los rebotes de un input

// Bucle principal
for(;;) {
     // Espera hasta que el botón esté presionado unos 10 ms como mínimo
     for(contadorDeRebotes = 0 ; contadorDeRebotes < 10 ; contadorDeRebotes++) {
          __delay_ms(1);
          if(BUTTON == 1)
               contadorDeRebotes = 0;
     }

     LED ^= 1;   // Hace un XOR entre el valor actual y 1
                 // Con esto obtiene el valor contrario.

     // Espera hasta que el botón esté levantado unos 10 ms como mínimo
     for(contadorDeRebotes = 0 ; contadorDeRebotes < 10 ; contadorDeRebotes++) {
          __delay_ms(1);
          if(BUTTON == 0)
               contadorDeRebotes = 0;
     }
}

Ahora sí, nuestro código funciona como esperamos.

Una mejora que le podemos hacer al código es utilizar lo que se llama "shadow register". Esto es una variable auxiliar que mantiene en todo momento el estado de un registro. Lo que permite es que en lugar de leer el estado que tiene un bit de un puerto (que en ciertos casos puede leerse de forma incorrecta si está pasando de un estado a otro), leamos un valor previamente guardado cuyo valor no cambiará a menos que se lo especifiquemos.

Con un shadow register el ejemplo quedaría así:

uint8_t contadorDeRebotes;      // Cuenta los rebotes de un input
uint8_t shadowPortB = 0;        // Shadow copy del puerto B

// Bucle principal
for(;;) {
     // Espera hasta que el botón esté presionado unos 10 ms como mínimo
     for(contadorDeRebotes = 0 ; contadorDeRebotes < 10 ; contadorDeRebotes++) {
          __delay_ms(1);
          if(BUTTON == 1)
               contadorDeRebotes = 0;
     }

     shadowPortB ^= 1 << posLED; // Hace un XOR entre el valor actual y 1
                                 // sobre el shadow register
     PORTB = shadowPortB;        // Copia el valor del registro al puerto B

     // Espera hasta que el botón esté levantado unos 10 ms como mínimo
     for(contadorDeRebotes = 0 ; contadorDeRebotes < 10 ; contadorDeRebotes++) {
          __delay_ms(1);
          if(BUTTON == 0)
               contadorDeRebotes = 0;
     }
}

Código completo del ejemplo 2

Bueno, espero que se hayan entendido estos ejemplos básicos. El código con los ejemplos está en mi repositorio de GitHub :

Fuentes:
http://www.gooligum.com.au/tutorials/midrange/PIC_Mid_C_1.pdf

Enviar datos a la PC mediante USART

En este post voy a utilizar el puerto USART de mi PIC16F88 para enviar datos a la pc cada 500 ms. Utilizando el proyecto que prende o apaga un led ahora además voy a enviar a la pc el valor de una variable que se incrementa cada cierta cantidad de tiempo.

Proyectos relacionados:



Lo primero que tenemos que hacer es configurar el puerto USART de nuestro pic para que envíe datos a una determinada velocidad. La misma va a depender de la velocidad del clock que estemos usando.

En mi caso voy a utilizar un cristal de 20Mhz. En la documentación de nuestro pic veremos que combinaciones podemos utilizar:

Primero tenemos que elegir el valor del parámetro BRGH ( "0" Baja velocidad  -  "1" Alta velocidad)

Tabla 1 - Datasheet PIC16F88

Dependiendo del valor que elijamos en BRGH iremos a la tabla que corresponda. Según la recomendación del manual, es preferible elegir el valor "1" ya que en la mayoría de los casos da un menor error en los cálculos:

Tabla 2 - Datasheet PIC16F88

Ahora lo que tenemos que hacer es para un determinado valor de KBauds seleccionar el valor SPBRG que le corresponda.

Por ejemplo, para 20Mhz y 19200 Baud el valor de SPBRG es 64 con un error de +0.16 para BRGH=1, (Si hubiese elegido BRGH=0 el error sería de +1.72)

La configuración en C es la siguiente:

// Habilita la conexión serial
// Habilitar uart

TXSTAbits.TX9 = 0;  //  Transmisión de 8-bit
TXSTAbits.SYNC = 0; // Modo asincrónico
TXSTAbits.BRGH = 1; // BRGH en alta velocidad

RCSTAbits.RX9 = 0;  // Recepción de 8-bit
RCSTAbits.SPEN = 1; // Puerto serial habilitado
// (configura RB2/SDO/RX/DT y RB5/SS/TX/CK pins como puerto serial)

SPBRG = 64;       // 19200 baud

TXSTAbits.TXEN = 1; // Habilita la transmisión de datos
RCSTAbits.CREN = 1; // Habilita la recepción de datos
RCIE = 1;           // Habilita las interrupciones por recibo de datos

__delay_ms(80); // Delay que permite que se estabilice la configuración y las interrupciones antes de comenzar a trabajar



Aparte de esto debemos agregar el siguiente código para que se limpie la interrupción de recepción de datos:

/*
* Vector de interrupciones
*/
static void interrupt isr(void) {
    // Limpia la interrupción de recepción de data
    if(RCIF && RCIE) {
        RCIF = 0;
    }
}

Si no hacemos esto, lo que sucede es que no recibiremos nada en la consola de la PC.

El siguiente código es necesario para poder utilizar las funciones como printf, getch, putch, etc para escribir y leer variables tal cómo se haría en una aplicación de consola.

/*
* Rutina necesaria para que funcione correctamente el printf.
* Escribe un caracter en el puerto serial.
*/
void putch(unsigned char data) {
  /* output one byte */
  while(!TXIF)     /* set when register is empty */
    continue;
  TXREG = data;
}

/**
* Obtiene un caracter desde el puerto serial.
* @return
*/
unsigned char getch() {
     /* retrieve one byte */
     while(!RCIF)     /* set when register is not empty */
          continue;
     return RCREG;
}

/**
* Obtiene un caracter desde el puerto serial y lo retransmite.
* @return
*/
unsigned char getche(void) {
     unsigned char c;
     putch(c = getch());
     return c;
}

Ahora lo que nos resta es llenar el cuerpo de la aplicación:

int contador = 0;

// Bucle principal
while(true) {

     LED = 1;    // Setea en estado alto (High) el LED

     __delay_ms(500); // Se queda esperando 500 milisegundos

     printf("contador: %d\r\n", contador++);
     LED = 0;    // Setea en estado bajo (Low) el LED

     __delay_ms(500);

     printf("contador: %d\r\n", contador++);

}

Lo que hace este código es bastante fácil de entender. 

1) Crea una variable de tipo integer llamada "contador"

2) Enciende el LED cómo se mostró en los anteriores posts y espera 500ms para apagarlo.

3) El método printf recibe una cadena de texto + parámetros. 
  • la expresión "%d" indica que debe recibir un parámetro de tipo entero (número) para formatearlo como cadena de caracteres. 
  • El "\r\n" hace un Enter en la consola. 
  • contador++ incrementa la variable

Una vez que compilamos el proyecto y lo pasamos con el AN1310, seleccionamos la opción "Run application firmware" y veremos que aparecerá la información en pantalla:


Imagen 1 - An1310 recibiendo datos cada 500ms
En mi caso estoy enviando un contador bastante simple pero haciéndo algunos cambios en el código podemos informar el valor de alguna entrada. Por ejemplo, podríamos informar que se presionó un botón o que recibimos información de algún sensor.

Hoy no hay video porque estoy afónico!

El código completo lo encontrarán en mi repositorio en GitHub: PIC16F88_06_USART.

Saludos!

Fuente:
Datasheet PIC16F88

martes, 26 de febrero de 2013

Bootloader AN1310 - Video

Hola!,

En esta entrada voy a subir mi primer video tutorial que hice para que vean lo fácil que es cargar los programas al PIC teniendo un bootloader.

Este post extiende al anterior: Bootloader - AN1310

Saludos!

PD: Pido disculpas si es que no se entiende pero estuve luchando varias horas con el editor de videos para poder mezclar la captura de pantalla con lo que filmaba desde el celular :)


domingo, 24 de febrero de 2013

Bootloader - AN1310

En esta entrada voy a hablar del Bootloader que utilizo para programar mi PIC16F88.

Un Bootloader es una porción de código estática dentro del pic que se ejecuta antes del código que nosotros programaremos. Cuando se activa permite comunicarse con la PC para realizar actualizaciones del programa que debe ejecutar el pic. Esto permite que el pic se programe a sí mismo sin necesidad de un grabador de pics.

Este código generalmente se encuentra en la parte más alta de la memoria de programa y no debe ser modificado por el programa que nosotros hagamos

¿Cuál es la ventaja de utilizar un bootloader? La ventaja más significativa es que podemos construir un circuito con un código base, soldar todos sus componentes y a medida que desarrollamos el software o descubrimos bugs, mediante el bootloader le actualizamos su firmware.

Como desventaja, hay menos espacio en el chip para nuestro código.

En la siguiente imagen podemos ver de forma simple cómo se ve un pic normal y uno con bootloader:




AN1310


Hay varios Bootloader disponibles para el pic16f88 y hasta podríamos hacernos el propio pero en lugar de eso vamos a utilizar el que nos provee Microchip, el AN1310.

El AN1310 es un Bootloader bastante pesado ya que ocupa unas 1000 palabras de programa (el PIC16F88 tiene 4096 palabras). Está escrito en assembler y debemos compilarlo para cada tipo de pic en particular ya que cambian las posiciones de memoria.

Para descargarlo debemos ir a la siguiente dirección:

Este es el esquema que debe tener el circuito que hagamos para conectar nuestro pic:


Para el PIC 16F88 en lugar +3.3V utilizaremos los +5V que venimos usando en los posts anteriores.

Para explicarlo brevemente, se utilizan las líneas RXD y TXD del puerto serial para cargar los datos al pic mediante la interfaz USART mientras que se utiliza la línea de control RTS para provocar un "Reset" del pic.

Este reset por software es muy útil para que luego de cargar un programa se pueda probar o frenar el mismo. 

Interfaz del programa:


Al conectar nuestro pic mediante el circuito presentado anteriormente al puerto serial de la pc y hacer click en el botón "bootloader Mode" (el cuadrado rojo), y si todo está conectado ok, veremos lo siguiente:


Cargaremos el mismo programa que vengo haciendo en los primeros posts con algunos cambios que mostraré más adelante y haciendo click en write device pasará el programa a nuestro pics pocos segundos:


Luego de cargar el programa hacemos click en el botón "Run application firmware" (el verde) y podremos ver nuestro programa funcionado.

En el manual de este bootloader podremos encontrar muchas más opciones.

Configuración que debe tener nuestro programa para que funcione correctamente:


Para que un programa hecho en MPLABX cargue correctamente utilizando el bootloader debemos modificar las opciones del linker de XC8 para que no sobreescriba los sectores de memoria reservados:


Para el PIC 16F88 las opciones son: --ROM=default,-e40-fff --RAM=default,-7E-7F

Lo que estamos indicándole es: Para la memoria rom usá el espacio default excepto las posiciones entre E40 y FFF. Para la memoria RAM usá el espacio default excepto las posiciones entre 7E y 7F.

Acabo de subir a GitHub el proyecto PIC16F88_03_TestBootLoader con el programa del led intermitente más la configuración anterior y el proyecto PIC16F88_04_Bootloader_20Mhz con la configuración necesaria para compilar el bootloader. Hay que tener en cuenta que el bootloader se compila en assembler así que tienen que tener habilitado el compilador MPASMX que se encuentra en la carpeta de instalación del MPLABX.

Repositorio:

Saludos,


Fuentes:
TinyPIC - http://www.etc.ugal.ro/cchiculita/software/picbootloader.htm
AN1310 - http://ww1.microchip.com/downloads/en/AppNotes/01310a.pdf


martes, 19 de febrero de 2013

Mi repositorio en GitHub

Hola, esta entrada va a ser bastante corta.

Acabo de crear el repositorio "programandopics" en GitHub para poder compartir el código que voy haciendo para así tener todos los ejemplos de pics juntos.

La dirección es:

https://github.com/jgquiroga/programandopics

Ya están subidos los ejemplos para encender un led de forma intermitente.

Saludos!

sábado, 22 de diciembre de 2012

USART - Comunicación Pic - PC

Comunicación Serial con el PIC + Fuente de 5 Volts.


En este post les voy a mostrar el circuito que hice para poder conectar mi pic mediante un cable serial a la PC. Esto nos permitirá realizar programas que controlen las acciones del PIC o que reciban información del PIC. En mi caso, primero voy a utilizarla para grabar programas mediante un Bootloader y así evitar tener que mover el pic a mi grabadora JDM. Luego lo voy a utilizar para mover servomotores.

Componentes necesarios:


Para la fuente de +5V (ver en detalle en un post previo):


  • 7805 - 1 circuito integrado 7805
  • C1, C4 - 2 capacitores de 470uF
  • C2, C3 - 2 capacitores de 0.1uF
  • R1 - 1 resistencia de 220 ohm (depende del LED)
  • LED - 1 LED Rojo
  • BAT1 - 1 batería de 9Volts
  • 9V - 1 conector macho de 2 patas.
  • PORT1 - 1 conector macho a placa de 2 o 4 patas (Este conector tendrá la salida a 5Volts)


Para la comunicación serial


  • MAX232 - 1 circuito integrado Max232
  • C5, C6, C7, C8, C9 - 5 capacitores de 1uF
  • PortPIC, PortPC - 2 conectores macho a placa de 4 pines



Este es el diagrama del esquemático de la fuente de 5Volts:


Fuente de 5 Volts


Este el es diagrama para la comunicación serial:

MAX32 - USART


Para explicar de forma básica que es lo que hace este circuito podemos pensar en 2 cables que se unen entre la PC y el PIC. Estos 2 cables tienen la función de enviar (TX) y recibir (RX) datos de forma serial, es decir, un bit atrás del otro:

Comunicación simplificada entre PC y PIC


El PIC16F88 cuenta con la función USART (Universal Synchronous-Asynchronous Receiver-Transmitter) que permite utilizar 2 de sus patas para esta comunicación. Para simbolizar un bit en 1, la salida debe estar a +5V, y para simbolizar un 0, en bajo voltaje, cercano a los 0V (TTL).

Por el lado de la PC utilizaremos el puerto Serial que envía señales en base al protocolo RS-232 simbolizando un 1 con un voltaje entre +3 y +15V y un 0 con un voltaje de entre -15 y -3V, es decir, un 1 es voltaje positivo y un 0 es voltaje negativo.

Como podemos observar, el tipo de comunicación de los dispositivos anteriores no es compatible, es por esto que debemos utilizar un componente en el medio para que transforme estas señales. Este componente es el MAX232 el cual nos provee de 2 pares de conversores de señal. Para la comunicación con el pic sólo es necesario 1 par de estos conversores.

Si nos fijamos bien, del par de Rx/Tx que nos sobra del MAX232 una línea Tx la nombré RTS ya que para próximos circuitos me servirá de señal de reset.

En un próximo post probaremos este circuito en funcionamiento.

Les dejo unos planos de cómo quedaría en una placa de PCB (los diagramas los hice con DipTrace):

Diagrama PCB hecho con Diptrace

Diagrama PCB hecho con Diptrace mostrando los componentes
Fotos del circuito armado:

Circuito listo para alimentar un pic y comunicarse con la pc
Nota: Luego de haber puesto los componentes en el circuito me di cuenta que era muy poco el espacio entre los capacitores de la fuente de alimentación así que para mi siguiente circuito los alejaré más.

Vista del circuito pasado a un PCB



Saludos!

martes, 2 de octubre de 2012

Distintas configuraciones del Oscilador con el PIC

Vamos a ver las distintas configuraciones del oscilador que puede tener el PIC16F88

En el Post anterior (Primer proyecto en XC8) utilicé el oscilador interno del PIC16F88 a 8Mhz con los siguientes fragmentos de código:

/*
 * File:   system.h
 */
#define _XTAL_FREQ       8000000
 
/**
 * File:        configuration_bits.c
 */
 
__CONFIG(MCLRE_ON & CP_OFF & CPD_OFF & LVP_OFF & BOREN_OFF &
         WDTE_OFF & FOSC_INTOSCIO & PWRTE_OFF);
__CONFIG(IESO_OFF & FCMEN_OFF);

/**
 * File:        system.c
 */
void ConfigureOscillator(void) {
    // Configura el Oscilador interno a 8Mhz
    OSCCONbits.IRCF = 0b111;
}


Con los mismos bits de configuración del código anterior, para cambiar la frecuencia del oscilador interno lo que debemos hacer es cambiar el valor de OSCCONbits.IRCF (Internal RC Oscillator Frequency) por uno de los siguientes:

0b000 = 31.25 kHz
0b001 = 125 kHz
0b010 = 250 kHz
0b011 = 500 kHz
0b100 = 1 MHz
0b101 = 2 MHz
0b110 = 4 MHz
0b111 = 8 MHz

El "0b" que aparece adelante de cada valor le indica al compilador que lo que le estamos pasando es un número binario. También podríamos pasarle el valor en hexadecimal pero queda más claro de esta manera. IRCF ocupa 3 bits del registro OSCCON (bits 6,5 y 4).

Por ejemplo, si al programa del Post anterior le queremos cambiar la frecuencia del oscilador interno a 4 Mhz, debemos setear en IRCF el valor 0b110 y actualizar el macro _XTAL_FREQ para que la rutina de delay funcione correctamente:

/*
 * File:   system.h
 */
#define _XTAL_FREQ       4000000     // Necesario para la rutina de Delay


/**
 * File:        system.c
 */
void ConfigureOscillator(void) {
    // Configura el Oscilador interno a 4Mhz
    OSCCONbits.IRCF = 0b110;
}

Una de las ventajas que trae el seteo del oscilador interno es que podemos disponer de 2 pines más para utilizarlos como Entrada/Salida, pero trae como desventaja que no es del todo preciso.

Utilización de un Cristal externo

Si utilizamos un cristal externo, vamos a tener mucha más precisión que si utilizásemos el oscilador interno. Aparte de esto, podemos llegar a frecuencias mayores lo que permite que nuestro pic realice muchas más acciones en la misma cantidad de tiempo.

Por ejemplo, si queremos utilizar un cristal externo de 20 MHz debemos hacer los siguientes cambios en el código:

/*
 * File:   system.h
 */
#define _XTAL_FREQ       20000000 // Necesario para la rutina de Delay
 
/**
 * File:        configuration_bits.c
 */
 
__CONFIG(MCLRE_ON & CP_OFF & CPD_OFF & LVP_OFF & BOREN_OFF &
         WDTE_OFF & FOSC_HS & PWRTE_OFF);
__CONFIG(IESO_OFF & FCMEN_OFF);

/**
 * File:        system.c
 */
void ConfigureOscillator(void) {
    // No se setea ninguna configuración
}


Ahora el circuito nos quedará de la siguiente manera:

Circuito agregando el cristal externo de 20 Mhz y 2 capacitores de 0,15pF:
Imagen 1 - Cristal externo


El valor de los capacitores va a depender del cristal como podemos leerlo en la ficha técnica del PIC16F88:

Imagen 2


Se pueden agregar capacitores de más tamaño para aumentar la estabilidad del clock con la contra de que se retrasa unos milisegundos más el inicio. En mi caso utilicé 2 capacitores de 22pF.

Hay que tener en cuenta que tanto el cristal como los capacitores deben estar lo más cerca posible del PIC para evitar interferencias.

Cómo queda el circuito en mi protoboard:

Imagen 3

Imagen 4

Hasta la próxima!
Saludos.

miércoles, 26 de septiembre de 2012

Primer proyecto en XC8 (Parte 3) - Probando el código

Continúa de Primer proyecto en XC8 (Parte 2).

Ya vimos como hacer funcionar nuestro primer proyecto, ahora vamos a verlo en funcionamiento.

Algo que olvidé de mencionar en la entrada anterior fue que el resultado de la compilación es un archivo con extensión ".hex". Este archivo es el que debemos utilizar tanto para grabar nuestro pic como para hacer simulaciones con él. Este archivo lo encontramos dentro de la carpeta en la que guardamos nuestro proyecto, en la subcarpeta : "dist\default\production".

Por ej. en mi caso se encuentra en:
C:\pics\MPLABX\PIC16F88_01_Led_Intermitente.X\dist\default\production\

y el archivo generado es:
PIC16F88_01_Led_Intermitente.X.production.hex

MPLABX nos permite debuggear el programa y así ver que valores tienen los registros en un momento en particular. De esta forma nos damos cuenta que el programa está haciendo lo que le dijimos que haga. Sin embargo, no se lleva bien con las rutinas de delay ya que un retardo de 500 milisegundos puede durar unos 15 segundos o más.

Para ver en tiempo real cómo actuaría mi programa utilicé un simulador más sencillo, el PICSimlabhttp://sourceforge.net/projects/picsim/


En lugar de utilizar un PIC16F88 este programa simula un PIC16F628 que es bastante similar y tiene la misma disposición de pines que es lo que realmente nos importa.

El programa se ve así:

Figura 1


Lo que debemos hacer es seleccionar en File/Load Hex y seleccionar el archivo ".hex" que compilamos anteriormente. Luego seteamos el clock en 8 Mhz y podremos ver nuestro programa corriendo. Este simulador tiene conectado todo el Puerto B de nuestro pic a 8 leds con sus respectivas resistencias. Si creamos correctamente nuestro programa, deberíamos ver que el LED en RB0 se enciende y se apaga cada 1 segundo:

Figura 2

Ahora que sabemos como se comporta nuestro programa en el simulador, podemos grabarlo en nuestro PIC y probar su funcionamiento:

Primero conecto mi grabador de PICs JDM con el PIC 16F88 al puerto serie.

Figura 3
Luego abro el programa grabador (En mi caso el PICPgm):

Figura 4


Este programa detectó automáticamente que tengo conectado mi PIC al puerto COM1 de la PC.
Luego selecciono el archivo ".hex" compilado anteriormente y reviso que la configuración sea la correcta:

Figura 5


Vemos que la configuración está Ok. Utilizará el oscilador interno y utilizará el pin 4 como RESET.

Hacemos click en el botón "Program PIC" y comenzará a pasar nuestro programa al PIC:

Figura 6
Este proceso tarda apenas unos segundos.

Figura 7

Aprovecharemos el mismo protoboard del post Fuente de alimentación de 5 Volts y colocaremos los componentes de la siguiente manera:

Para hacer el esquema anterior utilicé Eagle que tiene una versión freeware con algunas limitaciones.

Por último, les muestro cómo me quedó el circuito en el protoboard:

Figura 9

FIgura 10

Esta fue un post bastante extenso pero era necesario contar paso a paso cómo es que estoy trabajando con los pics. Para lo próximo que explique solamente voy a postear el código, el esquema del circuito y alguna foto del circuito armado.

Gracias por visitarme, Saludos!

viernes, 21 de septiembre de 2012

Primer proyecto en XC8 (Parte 2) - Código para el Led intermitente

Continúa de Primer proyecto en XC8 (Parte 1) - Creación de proyecto

Agregando código a nuestro proyecto

En la parte 1 quedamos con la siguiente pantalla que muestra nuestro proyecto vacío recién creado:

Figura 1
A este proyecto le agregaremos la siguiente estructura de archivos de base:

Header Files:

  • system.h : Macros y funciones para configurar el sistema.
  • user.h: Macros y funciones específicas de la aplicación.
Source Files:
  • configuration_bits.c: Programación de los bits de configuración del sistema.
  • system.c: Funciones que configuran el sistema (Por ejemplo el Oscilador)
  • main.c: Programa principal
  • user.c: Algoritmos de la aplicación.


Para agregar un archivo ".h" hacemos click derecho en Header Files y seleccionamos "New/ C Header File...". En la siguiente pantalla escribimos el nombre del archivo y luego finalizar:

Ej.: system.h:

Figura 2
Figura 3

Para agregar un archivo ".c" hacemos click derecho en Source Files y seleccionamos "New/ C Source File...". En la siguiente pantalla escribimos el nombre del archivo y luego finalizar:

Figura 4

Figura 5
En system.h escribimos lo siguiente:

/* 
 * File:   system.h
 * Author: Julián Quiroga
 */
/*******************************************************/
/* System Level #define Macros                         */
/*******************************************************/
/**
 * Indica que el pic funcionará a 8MHz.
 * Este macro es necesario para las funciones de delay.
 */
#define _XTAL_FREQ       8000000

/*******************************************************/
/* System Function Prototypes                          */
/*******************************************************/
/**
 * Función que debe configurar el Oscilador
 */
void ConfigureOscillator(void);

En user.h por ahora lo dejaremos vacío:

/* 
 * File:   user.h
 * Author: Julián Quiroga
 */
/*******************************************************/
/* System Level #define Macros                         */
/*******************************************************/

/*******************************************************/
/* System Function Prototypes                          */
/*******************************************************/

En "configuration_bits.c" agregaremos lo que hablé en esta entrada "Configuración básica del PIC en XC8":
/**
 * File:        configuration_bits.c
 * PIC:         PIC16F88
 * Author:      Julián Quiroga
 *
 * Descripción: Configura el pic para que utilice
 *              el oscilador interno y el pin 4
 *              como Reset
 */
#include <xc.h>

__CONFIG(MCLRE_ON & CP_OFF & CPD_OFF & LVP_OFF & BOREN_OFF &
         WDTE_OFF & FOSC_INTOSCIO & PWRTE_OFF);
__CONFIG(IESO_OFF & FCMEN_OFF);

En "system.c" escribiremos lo siguiente:

/**
 * File:        system.c
 * PIC:         PIC16F88
 * Author:      Julián Quiroga
 *
 * Descripción: Funciones base del sistema
 */
#include <xc.h>
#include <stdint.h>        /* For uint8_t definition */
#include <stdbool.h>       /* For true/false definition */

#include "system.h"

/**
 * Configurar todo lo necesario para que funcione el oscilador interno 
 * a 8Mhz.
 */
void ConfigureOscillator(void) {
    // Configura el Oscilador interno a 8Mhz
    OSCCONbits.IRCF = 0b111;
}

Ahora sólo nos queda crear main.c:

/* 
 * File:        main.c
 * PIC:         PIC16F88
 * Author:      Julián Quiroga
 *
 * Descripción: Programa que enciende y apaga un LED de forma
 *              intermitente en RB0.
 */
#include <xc.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

#include "system.h"
#include "user.h"


#define LED     PORTBbits.RB0 // El Led se encuentra conectado en RB0
#define posLED  0             // Posición en la que se encuentra el Led

/**
 * Programa principal
 */
void main(void) {

    // Configura el oscilador
    ConfigureOscillator();
    
    // Configura un 0 en la posición que le corresponde al LED
    // y un 1 a todas las demás.
    // 0 = Salida, 1 = entrada
    TRISB = ~(1 << posLED);
    // La línea anterior es equivalente a 
    // TRISB = 0b11111110;

    // Bucle principal
    while(true) {

        LED = 1;    // Setea en estado alto (High) el LED
        
        __delay_ms(500); // Se queda esperando 500 milisegundos
        
        LED = 0;    // Setea en estado bajo (Low) el LED
        
        __delay_ms(500);

    }
}

El código anterior define 2 macros: LED y posLED. Estos macros permiten que podamos cambiar de lugar en led haciendo mínimos cambios en el código.

LED indica el lugar en el que se seteará un 1.
posLed indica la posición del LED en el puerto B.

La instrucción: "1 << posLED" hace que el "1" de la izquierda se mueva tantos lugares a la izquierda como los que indica posLED. En este caso, como es "0", el resultado es un "1" que no se movió de lugar.

Luego, la instrucción "~" hace que todo lo que esté dentro del paréntesis quede negado, es decir, en bits, con el valor opuesto. Entonces en lugar de quedar todos ceros y un uno, ahora quedan todos unos y un cero: "0b11111110".

Ahora sólo nos queda compilar nuestro proyecto y ver que no tenga errores. Para hacer eso, hacemos click en el ícono que muestra un martillo, llamado "Build Main Proyect".


Si no hubo errores, el resultado de la compilación nos dirá algo similar a esto:

BUILD SUCCESSFUL (total time: 5s)

Ahora que ya tenemos nuestro primer proyecto compilado correctamente tenemos 2 opciones para probarlo, ejecutarlo en modo simulador o grabarlo en el PIC y probarlo en nuestro circuito.

En la parte 3 (Primer proyecto en XC8 (Parte 3) - Probando el código) de este tutorial les enseñaré como hacer esto.

Saludos!

Primer proyecto en XC8 (Parte 1) - Creación de proyecto


La mayoría de los ejemplos que voy a hacer están en el siguiente tutorial: http://www.gooligum.com.au/tut_baseline_C.html

Si seguimos los pasos que les indiqué aquí "Como configurar MPLABX para compilar con XC8 y MPASM", ya estamos listos para crear nuestro primer proyecto.

Lo que vamos a hacer será encender y apagar un led de forma intermitente. Para esto, vamos a necesitar de lo siguiente:

1 Fuente de alimentación de 5 Volts
1 Led de cualquier color
1 resistencia acorde al led anterior. (Cálculo de la resistencia para un LED)
1 protoboard + cables tipo alambre para poder conectar y desconectar todo fácilmente.
1 Pic: PIC16F88 (el código se puede adaptar para cualquier otro).
1 Grabador de pics: en mi caso, un simple grabador de pics JDM por puerto serial.

Creación de un proyecto en MPLABX


Lo primero que tenemos que hacer es crear un proyecto en MPLABX. Para esto, se debe seleccionar la opción "New Proyect":

Figura 1

Se nos abre un diálogo que nos permite seleccionar el proyecto que queremos crear.

Figura 2

Tenemos varias opciones:
a) Creamos nuestro proyecto desde 0.
b) Importamos el proyecto desde una versión anterior de MPLAB.
c) Utilizamos un Template.


Si seleccionamos un Template, por ejemplo "PIC16 Template" en la rama "Samples/Microchip Embedded" tendremos creado un proyecyo inicial con una configuración estándar.

En mi caso, prefiero crear el proyecto desde 0, o a lo sumo, copiar la estructura de un proyecto anterior.

Para crear un proyecto desde 0 se debe seleccionar la opción Standalone Proyect. En la siguiente pantalla debemos seleccionar el pic a programar, en mi caso es el PIC16F88.

Figura 3

Hacemos click en "Next". Ahora se nos pide que elijamos la herramienta que utilizaremos para hacer debug de nuestro programa. Como yo no dispongo de ninguna de las que aparecen en la lista, selecciono "Simulator" y luego Next:

Figura 4

Ahora debemos indicarle a MPLABX que compilador utilizar.

Figura 5
Como podemos observar, aparecen los 2 compiladores que agregamos en el post anterior. Vamos a seleccionar XC8 y luego Next.

Por último, debemos indicarle en qué lugar se guardará el proyecto y con qué nombre:
Figura 6

En mi caso, decidí nombrar los proyectos de la siguiente forma:
"PIC16F88_01_Led_Intermitente": [Modelo del Pic]_[Nro de proyecto]_[Descripción]

Luego de finalizar la creación del proyecto, la izquierda de la pantalla nos quedaría así:

Figura 7

Lo que muestra la imagen anterior es nuestro proyecto con todos los lugares en los que se agregará código. Los más importantes, para nosotros, son:

Header Files: Se agregan los archivos ".h" que contendrán la definición de macros, configuraciones y la cabecera de las funciones que crearemos.

Source Files: Se agregan los archivos ".c" que contendrán la funciones que se compilarán. La única función que no puede faltar es la llamada "main" que es el punto de entrada de toda aplicación ( a menos que se esté creando una librería).

Continúa en la siguiente entrada: "Primer proyecto en XC8 (Parte 2) - Código".

miércoles, 19 de septiembre de 2012

Cómo configurar MPLABX para compilar con XC8 y MPASM


Una de las cosas que me olvidé de mencionar en los posts anteriores era cómo configurar MPLABX para que nos permita compilar nuestros proyectos.

MPLAB X

Los pasos a seguir serían los siguientes:


1) Instalar MPLABX (http://www.microchip.com/pagehandler/en-us/family/mplabx/#downloads) siguiendo las instrucciones del instalador.

2) Instalar XC8 (http://www.microchip.com/pagehandler/en-us/family/mplabx/#downloads) también siguiendo las instrucciones del instalador.

Haciendo la instalación en este orden, el compilador ya se agrega a MPLABX. Si se instaló al revés no hay problema, el compilador se debe agregar manualmente.

Pasos para agregar XC8 a la lista de compiladores de MPLABX de forma manual:


Abrir MPLABX y hacer click en Tool/Options:


Figura 1
Hacer click en la la pestaña Embedded:

Figura 2


Ahora tenemos 2 opciones:

  1. Hacemos click en Scan for build Tools y seleccionamos el compilador si es que aparece.
  2. Hacemos click en Add y escribimos primero el directorio y luego el ejecutable principal.

Luego de esto hacemos click en Ok y ya estará todo configurado.

Con estos pasos es posible agregar cualquier otro compilador que se quiera probar.

Por ejemplo, además de XC8, también agregué MPASM para poder escribir código instrucción por instrucción en Assembler. (Más adelante lo voy a usar para compilar un bootloader).

Para que este post quede bastante acotado, voy a explicar como hacer nuestro primer proyecto XC8 en la siguiente entrada.

Hasta pronto.
Saludos!

Related Posts Plugin for WordPress, Blogger...