Capítulo 9: PWM

Configuramos el módulo CCP1 (PIN_C2) para que genere una señal cuadrada (PWM). En PIN_C2 tenemos conectado un LED. El ciclo de trabajo (duty cycle) de la señal PWM variará de 0% (apagada) a 100% (totalmente encendida), pasando por 4 estados intermedios, de forma que veremos como va cambiando la intensidad luminosa del LED de manera progresiva.

Código fuente: PWM.c

T_PWM = (PR2+1)*prescaler*4*Tosc

Duty cycle (CCPRxx): valor mínimo=0 (0%), valor máximo=PR2 (100%)

El PIC18F2550 tiene dos puertos que pueden generar automáticamente señales PWM: CCP1 (PIN_C2) y CCP2 (PIN_C1).

En nuestro ejemplo, el período de la señal PWM sería:

T_PWM = (255+1) * 16 * 4 *  (1/48MHz) = 341us (2.93KHz)

Duty cycle: CCPR1L = (0 – 255)

Registros para configurar el PWM (para generar el PWM, el PIC18F2550 trabaja con el timer2):

CPxCONbits.CCPxM: configura el módulo CCPx en modo PWM, o en modo Comparación o en modo Mejorado de Captura.
PR2: Similar al valor máximo del duty cycle, para una resolución de 8 bits.
T2CONbits.TMR2ON: Habilita el timer2.
T2CONbits.T2CKPS: Prescaler del timer2. El postscaler del timer2 no se usa en el PWM.
CCPRxL: El valor del duty cycle puede tener una resolución de 8 bits o de 10 bits. Los 8 bits más significativos se almacenan en el resgistro CCPRxL. Los dos bits menos significativos estarían en CPxCONbits.DCxB. En la mayoría de los casos, una resolución de 8 bits (0-255) sería suficiente, por lo que tan solo habría que dar un valor a CCPRxL para cambiar el duty cycle.

ccpxcon

CCPxCON: CCP control register

timer2

T2CON: Timer2 control register

Capítulo 8: Timer 0

Se configura el timer_0 del PIC18F2550 para que salte la interrupción por desbordamiento cada 500ms. Y cada 500ms se invierte el valor del PIN_C6, para que parpadee el LED que tiene conectado.

Código fuente: timer0.c

tiempo = (cont_máx – cont_mín)*Prescaler*4*Tosc

cont_máx = 255+1 (8 bits) ó 65535+1 (16 bits).

cont_mín = TMR0 en el momento del desbordamiento. Si en la subrutina de interrupción no se le da ningún valor, empieza contando desde cero.

En nuestro ejemplo: tiempo = (65535+1 – 18661) * 128 * 4 * (1/48MHz) = 500ms

Registros para configurar la interrupción:

INTCONbits.GIE: Habilita las interrupciones.
INTCONbits.PEIE: Habilita las interrupciones periféricas (todas son periféricas, menos las externas, port B).
INTCONbits.TMR0IE: Habilita la interrupción del timer0.
INTCONbits.TMR0IF: Flag o bandera del timer0, que se pone a uno cuando salta la interrupción por desbordamiento del timer0.

Registros para configurar el timer0:

T0CONbits.TMR0ON: Habilita el timer0.
T0CONbits.T08BIT: Contador de 8 bits (desborda a 255+1) o de 16 bits (desborda a 65535+1), la interrupción salta al reiniciar.
T0CONbits.T0CS: Cuenta los pulsos del reloj interno (48MHz) o de una fuente externa.
T0CONbits.T0SE: Cuenta los flancos de subida o de bajada.
T0CONbits.PSA: Se le asigna un valor al prescaler.
T0CONbits.T0PS: Prescaler.

t0con

T0CON: Timer0 control register

Capítulo 7: Interrupciones externas

Un pulsador en pull-up conectado al PIN_B7 interrumpe el bucle infinito while(1) para encender un LED (PIN_C6) cuando lo pulsamos y apagarlo cuando lo soltamos.

Código fuente: interrupcion_externa.c

En el puerto B tenemos varios tipos de interrupciones externas:

Interrupciones por flanco: INT0 (PIN_B0), INT1 (PIN_B1) y INT2 (PIN_B2). Cada una se puede configurar de manera independiente, es decir, cada una tiene su propio «flag», que puede saltar en el flanco de subida o en el de bajada, según se quiera.

Interrupciones por nivel: la interrupción saltaría ante cualquier cambio de nivel en cualquiera de los 4 puertos: RB4-RB7. Por eso es importante comprobar dentro de la subrutina de interrupción qué puerto es el que ha cambiado.

Registros a tener en cuenta para configurar interrupciones por nivel:

INTCONbits.GIE: Habilita las interrupciones.
INTCONbits.RBIE: Habilita interrupciones por nivel, RB4-RB7.
INTCONbits.RBIF: Indicador de bandera (flag), se pone a uno cuando salta la interrupción de nivel.

Apuntes detallados de cómo configurar interrupciones externas, tanto las de flanco como las de nivel:

interrupt_port_B.pdf

INTCON: Interrupt control register

intcon2

INTCON2: Interrupt control register 2

intcon3

INTCON3: Interrupt control register 3

Capítulo 6: Entrada analógica

En el PIN_A0 (AN0) tenemos conectado un potenciómetro, de manera que el PIC18F2550 leerá valores analógicos entre 0 y 5V. El programa enciende un LED cuando sobrepasa los 2.5V y lo apaga cuando está por debajo.

Código fuente: entrada_analogica.c

Configurar un puerto como analógico:

TRIS…: Configura el PIN de salida.
ADCON1bits.VCFG: Configura tensiones de referencia. Pueden usarse los 0 y 5V de alimentación, como valores mínimo y máximo, o tensiones Vref- y Vref+, conectadas a PIN_A2 y PIN_A3, respectivamente.
ADCON1bits.PCFG: Configura los puertos que queremos que sean analógicos, y los que serán digitales.
ADCON2bits.ADCS: Configura el tiempo de conversión analógico-digital, según frecuencia de trabajo.
ADCON2bits.ACQT: Configura tiempo de adquisición, para el que hay que tener en cuenta el de conversión.
ADCON2bits.ADFM: El resultado se puede almacenar en 8 bits (resolución 256, dato justificado a la izquierda) o en 10 bits (resolución 1024, dato justificado a la derecha).

Conversión analógico-digital:

ADCON0bits.ADON: Habilita el módulo convertidor analógico-digital.
ADCON0bits.CHS: El PIC18F2550 sólo tiene un módulo convertidor, que es compartido por todos los puertos analógicos. Por lo que antes de lanzar  la conversión, hay que decir de qué puerto se va a leer el valor analógico.
ADCON0bits.GO_DONE: Lanza la conversión analógico-digital del puerto seleccionado anteriormente. Hasta que no se pone a CERO, no se ha finalizado la conversión, y no se debe leer el dato.
ADRESH y ADRESL: Registros donde se carga el resultado. Para justificación a la izquierda (resolución 256), sólo se leería ADRESH.

Apuntes detallados con las tablas de valores para cada uno de los registros, según datasheet:

analog ports.pdf

adcon0

ADCON0: A/D control register 0

adcon1

ADCON1: A/D control register 1

adcon2

ADCON2: A/D control register 2

Capítulo 5: Pulsador

Un pulsador en modo pull-up (PIN_C0) y un LED (PIN_C6) conectados al PIC18F2550. Al pulsar BUTTON (PIN_C0 vale 0) se enciende el LED, al soltar (PIN_C0 vale 1) se apaga el LED.

Hemos incluido un if-else y cómo leer un bit del exterior. Además, hemos usado #define.

Código fuente: Pulsador.c

lat-vs-port

TRISCbits.TRISC0=1;            // PIN_C0 periférico de entrada, BUTTON
if (PORTCbits.RC0==0)…   // si PIN_C0 vale 0, entonces…

Explicación detallada del funcionamiento de los biestables y puertas lógicas al usar: TRIS, LAT, PORT.

TRIS vs LAT vs PORT.pdf

RESUMEN y RECOMENDACIÓN:

TRIS: configura puertos para conectar periféricos de entrada (1) o salida (0).

//TRISx=…   //TRISxxbits.TRISxx=…

LAT: escribe un dato en un puerto de salida.

//LATx=…   //LATxxbits.LATxx=…

PORT: lee un dato de un puerto de entrada.

//PORTx    //PORTxxbits.Rxx

Capítulo 4: Coche fantástico

Parecido al Blinking_LED, pero accediendo al puerto B completo. Se ha programado un juego de luces con 7 LEDs (B0-B6), similar al del «Coche Fantástico».

Código fuente: coche_fantastico.c

TRISB=0b00000000;   // Todo el puerto B de salida (TRISB=0x00;)
LATB = 0b00001111;   // B0-B3 nivel alto, B4-B7 nivel bajo

Capítulo 3: Compilar, cargar y ejecutar Blinking_LED

Usando la placa MKBot-Motherboard v.3, en MPLAB-X v.3.2, para el PIC18F2550, con un cristal de 20MHz, y una frecuencia de trabajo de 48MHz. Parpadea un LED en PIN_C6 cada 500ms.

Código fuente: Blinking_LED.c

TRISCbits.TRISC6=0;  // PIN_C6 periférico de salida, LED
LATCbits.LATC6 = 1;  // PIN_C6 nivel alto, enciende LED
LATCbits.LATC6 = 0;  // PIN_C6 nivel bajo, apaga LED

Capítulo 2: Configurar fuses

Cómo configurar los fuses en MPLAB-X v.3.2, para el PIC18F2550, con un cristal de 20MHz, en especial los fuses del oscilador.

oscilador

#include <xc.h>
#pragma config PLLDIV = 5                       // PLL Prescaler: divide por 5 (Oscilador entrada: 20MHz)
#pragma config CPUDIV = OSC1_PLL2    // System Clock Postscaler: Divide entre 2 los 96MHz del PLL, CPU = 48MHz
#pragma config USBDIV = 2                      // USB Clock Selection bit: Divide entre 2 los 96MHz del PLL, USB = 48MHz
#pragma config FOSC = HSPLL_HS        // Oscillator Selection: HS oscillator, PLL habilitado (HSPLL))
#define _XTAL_FREQ 48000000           // Frecuencia de trabajo 48MHz (gracias al PLL)