Curso PIC32. Osciladores, Timer,...
Descripción de los diferentes relojes y osciladores de los PIC32
MICROCONTROLADORES
Biblioman
6/6/201010 min leer
Una de las primeras cosas que hay que tener claro cuando trabajamos con Microcontroladores es la configuración de los diferentes osciladores que tiene nuestro micro, si esa configuración no es correcta el PIC no funcionará o lo hará a un ritmo diferente al deseado.
Los PIC32 presentan bastantes novedades en los relojes y en los osciladores disponibles para su uso, lo primero que hay que tener en cuenta es que en esta arquitectura existen dos buses diferentes para la comunicación de instrucciones y datos entre los diferentes componentes del PIC.
El principal (Matrix), utilizado por el núcleo del Microcontrolador y por unos pocos periféricos privilegiados como el Acceso Directo a Memoria (DMA) y el controlador del servicio de interrupciones (entre otros). que demandan una frecuencia elevada en el bus de comunicaciones. El resto de componentes se comunica a través del bus de periféricos, que trabaja a una frecuencia menor que el bus principal, ambos buses se enlazan a través de un puente llamado "peripheral Bridge" similar al puente Norte y puente Sur que disponen las placas base de los ordenadores personales.
Cada uno de estos buses funciona con una señal de reloj diferente, los relojes disponibles en un PIC32 son:
El reloj del bus principal (SYSCLK)
EL reloj del bus de periféricos (PBCLK)
El reloj USB (USBCLK), utilizado expresamente por los periféricos USB.
Cada una de estas señales de reloj pueden ser producidas por diferentes fuentes de oscilación:
Oscilador externo primario (POSC) conectado entre los pines OSCI y OSCO del PIC. EL starter kit tiene conectado un cristal de 8 Mhz.
Oscilador externo secundario (SOSC) conectado en los pines SOSCI y SOSCO. EL starter kit viene preparado para conectarle un cristal externo de 32.768 Hz que puede ser utilizado como oscilador principal ó como fuente para el Timer1 o los módulos RTCC. Es un oscilador de precisión que se puede utilizar en aplicaciones donde se necesite un cronometraje de tiempo preciso.
Oscilador RC interno (FRC), proporciona una frecuencia de reloj de 8 MHz y no requiere ningún componente externo.
Oscilador RC interno de baja potencia (LPRC), proporciona una frecuencia de reloj base de 32 KHz, sin necesidad de ningún componente externo también pero con una precisión baja.
Cada una de las fuentes de reloj tiene sus propias opciones de configuración, como multiplicador y divisor de entrada y salida del PLL, que nos permite obtener la frecuencia de reloj que queramos en cada uno de los buses.
Nota: existe la posibilidad de utilizar una señal de reloj externa (EC) que a través de un circuito externo permite reemplazar por completo el oscilador y proporcionar al microcontrolador una entrada de onda cuadrada de cualquier frecuencia deseada.
El esquema de bloques de los diferentes osciladores de que disponen los PIC32 lo tenéis en la siguiente figura:
En este ejemplo vamos a utilizar el timer2, que es uno de los cinco temporizadores de 16 bits de que dispone el PIC32, hay que decir que aunque los temporizadores son de 16 bits, el Timer2 y Timer3 se pueden agrupar para formar un temporizador de 32 bits, lo mismo ocurre para el Timer4 y Timer5, eso da un margen de tiempos muy amplio.
Los registros principales para controlar el timer2 son:
TMR2--> donde se guarda el valor del contador de 16 bits.
T2CON --> Registro de configuración del TIMER2
PR2 --> valor a cargar para producir el reseteo del Timer
Como siempre podemos hacer el ejemplo de dos formas diferentes, utilizando identificadores para los diferentes registros del PIC y trabajar directamente sobre ellos o utilizar la librería de periféricos (plib.h). En este caso utilizaremos la segunda opción.
Lo que queremos conseguir con el ejemplo es el parpadeo de un Led cada 1 ms (lo del parpadeo es un decir, porque a esa frecuencia no lo notaremos a simple vista), pero con la ayuda de un osciloscopio podremos comprobar la precisión de la señal obtenida con el timer, para obtener dicha señal utilizaremos la función de interrupción por desbordamiento del Timer2.
El código del ejemplo será el siguiente:
//////////////////////////////////////////////////////////////
// Ejemplo: Timer2 con interrupción por desbordamiento TMR2//
// Microcontrolador: PIC 32 //
// IDE: MPLAB //
// Compilador: C32 //
// Autor: Biblioman //
/////////////////////////////////////////////////////////////
#include <plib.h>
//Bits de configuración
#pragma config FNOSC = PRIPLL//Oscilador Principal con PPL
#pragma config POSCMOD = XT //Modo reloj principal XT (8Mz)
#pragma config FPBDIV = DIV_8//divisor Bus Periféricos
#pragma config FWDTEN = OFF//Wachdog deshabilitado
#pragma config FPLLODIV = DIV_1//Pos-Divisor PPL 1/1
#pragma config FPLLIDIV = DIV_2//Pre-Divisor PPL 1/2
#pragma config FPLLMUL = MUL_20// 20xPPL
//Demás Opciones por defecto
int main(void)
{
//Función de optimización, configuración de cache, etc.
SYSTEMConfigPerformance(80000000L);
// Sobrescribo el PBDIV a 1:8 para este ejemplo, la sentencia anterior varía el PBDIV
mOSCSetPBDIV(OSC_PB_DIV_8);
//Configuración Timer 2
OpenTimer2(T2_ON | T2_PS_1_256, 0x00000027);
//Configuración de Interrupción por desbordamiento TMR2, Prioridad 2
ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_2);
//Habilitar múltiples vectores de interrupción
INTEnableSystemMultiVectoredInt();
//Configuración PORTD.RD0 = salida
mPORTDSetPinsDigitalOut(BIT_0);
while(1);
}
//Función de tratamiento de la interrupción por desbordamiento del timer 2
void __ISR(_TIMER_2_VECTOR, ipl2) Timer2Handler(void)
{
//Borro el flag de interrupción
mT2ClearIntFlag();
//Toggle LED RD0
mPORTDToggleBits(BIT_0);
}
Comentario del programa
Lo primero que hay que configurar son los fusibles o bits de configuración, para ello tenemos dos opciones configurarlos, a través del IDE de MPLAB: menú -->Configurar--> Configuración Bits.... y Una vez establecidos los valores se guardarán en el archivo del proyecto (.MCW), volcándose a la memoria Flash del PIC en la programación del mismo o podemos configurarlos como en este ejemplo en el propio código a través de la directiva #pragma,
Los PIC32 disponen de un amplio número de fusibles con diferentes parámetros la mayoría de ellos, pero se pueden configurar los básicos e imprescindibles y dejar por defecto los otros, está configuración básica nos servirá de plantilla en otra ocasión para futuros ejemplos.
Empecemos a describir cada uno de los fusibles:
#pragma config FNOSC = PRIPLL//Con FNOSC seleccionamos el oscilador a utilizar, seleccionaremos el Oscilador Principal con PPL (Multiplicador), recordar que en el Starter Kit está alimentado por un cristal de 8MHz
#pragma config POSCMOD = XT //Modo reloj principal, como la frecuencia del reloj es menor de 10 MHz seleccionamos XT.
#pragma config FPBDIV = DIV_8 //divisor Bus Periféricos, el Bus de Periféricos será un submultiplo de la frecuencia del Bus principal, en este caso dividimos por 8 la frecuencia principal.
#pragma config FWDTEN = OFF//Wachdog deshabilitado
//Configuración del Multiplicador
#pragma config FPLLODIV = DIV_1 //Pos-Divisor PPL 1/1
#pragma config FPLLIDIV = DIV_2 //Pre-Divisor PPL 1/2
#pragma config FPLLMUL = MUL_20 // 20xPPL
Podemos decir que la frecuencia del Bus principal vendrá dada por la siguiente fórmula:
Y según la configuración de nuestro ejemplo, obtendremos:
La fórmula para calcular la frecuencia del Bus de periféricos, es bien sencilla y será proporcional a la frecuencia que tengamos en el bus principal, en nuestro ejemplo valdrá lo siguiente:
¿Podremos decir con esto, que tenemos ya configurado nuestro PIC para que trabaje a 80 MHz y optimizado para que realice el mayor número de instrucciones por segundo (124,8 DMIPS) ?. Pues por lo visto No.
Esta arquitectura de microcontroladores incorpora entre otras novedades un módulo de memoria cache, este tipo de memoria es muy rápida pero también costosa por lo que por cuestiones de precio no se puede poner la que se quiera, a muchos de vosotros por lo menos les sonará el nombre ya que las computadoras personales las incorporan desde hace tiempo. El concepto es el mismo, cuando el microprocesador solicita una instrucción o un dato de la memoria Flash, primero comprueba si está en
la memoria cache, si está lo coge de ahí, si no lo va a buscar a la memoria Flash y guarda una copia en la cache para la próxima vez que el micro necesite ejecutar la misma instrucción, con esto se reducen los tiempos de espera en que el micro tiene que esperar en recuperar una instrucción o un dato de la memoria Flash.
La memoria cache esta deshabilitada por defecto en los PIC32 y lo mismo que en un ordenador personal hay que entrar en el setup de la Bios, y activarla para sacarle el máximo rendimiento al ordenador, en el PIC 32 tenemos que hacer lo mismo, salvo que aquí no tenemos ninguna BIOS, para hacerlo fácil Microchip ha incorporado una función de biblioteca del tipo todo en uno, que tendremos que llamar al inicio de nuestro programa:
SYSTEMConfigPerformance(80000000L);
Esta función recibirá como parámetro la frecuencia de trabajo de nuestro Bus Principal y no solamente activará la memoria cache sino que configurará el resto de parámetros necesarios para que nuestro PIC trabaje al máximo rendimiento.
La siguiente sentencia en el código llama a la función:
mOSCSetPBDIV(OSC_PB_DIV_8);
La llamada a esta función lo que hace es sobrescribir el divisor del bus de periféricos al valor que habíamos establecido previamente en los bits de configuración, esto es necesario ya que la función de optimización anterior modifica por su cuenta el divisor del bus de periféricos, supongo que para obtener la máxima frecuencia también en este bus, pero que si lo dejamos así, los cálculos que realicemos para el timer con los parámetros que hemos elegido nosotros no serán correctos.
Una vez configurados los fusibles y optimizado los recursos de nuestro PIC para obtener su máximo rendimiento, nos queda hacer los cálculos necesarios sobre el Timer2 para obtener una interrupción cada milisegundo.
La fórmula para calcular los valores de los Timers del PIC32 en su modo simple (16 bits) es la siguiente:
en donde:
Time --> Tiempo en segundos para el desbordamiento del timer.
PBCLK --> Frecuencia del bus de periféricos.
Prescaler --> Puede tomar los valores (1:1, 1:2, 1:4, 1:8, 1:16, 1:32, 1:64, 1:256)
PR2 --> Valor del registro PR2, es el valor a calcular en función de los otros parámetros.
Si utilizamos un preescaler de 256 el valor a cargar en el PR2 para que se produzca un desbordamiento cada 1ms será:
La biblioteca plib.h nos proporciona una función para configurar fácilmente el timer2:
OpenTimer2(T2_ON | T2_PS_1_256, 0x00000027);
Esta función recibe una serie de parámetros en los que:
T2_ON --> activa el TIMER2
T2_PS_1_256 --> establece el prescaler a 1:256
0x00000027 --> carga el valor calculado previamente en el registro PR2
Hay que tener en cuenta que el Timer2 en su modo de trabajo simple es un contador de 16 bits por lo que el valor del contador estará comprendido entre 0 y 65535, cualquier valor que intentemos cargar en el PR2 superior a este será truncado y el resultado obtenido será inesperado. Para obtener temporizaciones mayores a las permitidas por este rango, siempre podemos utilizar el Timer2 junto al Timer3 trabajando en modo de 32 bits.
Las siguientes instrucciones habilitan el modo de múltiples vectores de interrupción y la interrupción por desbordamiento del TMR2 con un nivel de prioridad de 2.
ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_2);
//Habilitar multiples vectores de interrupción
INTEnableSystemMultiVectoredInt();
Los PIC32 disponen de un controlador para gestionar las interrupciones muy flexible, se puede programar para que trabaje con un solo vector de interrupción como lo hacen los pic de inferior categoría o para aumentar el rendimiento, utilizar un sistema con múltiples vectores de interrupción, la
familia PIC32MX dispone, debido al núcleo (MIPS), de un máximo 64 vectores de interrupción, sin embargo se dispone de hasta 96 fuentes diferentes de interrupción, por lo que los diseñadores han establecido que determinadas fuentes de interrupción pertenecientes a un mismo periférico compartan
el mismo vector de interrupción. Si queremos habilitar la opción de Múltiples vectores de interrupción y utilizamos para ello la librería plib de Microchip, simplemente tenemos que incluir la llamada a la función INTEnableSystemMultiVectoredInt(). En el modo Multi-vector, a cada vector de interrupción se le puede asigna una prioridad determinada que va del 1 (menor prioridad) al 7 (máxima prioridad) un valor 0 indica que el vector de interrupción está deshabilitado (valor por defecto). Las interrupciones de mayor prioridad prevalecen a las de menor prioridad, además cada vector admite hasta cuatro niveles
de sub-prioridad diferentes, de esta manera se puede programar diferentes vectores con la misma prioridad pero con una sub-prioridad diferente y prevalecerá la que tenga una sub-prioridad mayor.
La siguiente instrucción configura el pin RD0 del puerto D como salida:
mPORTDSetPinsDigitalOut(BIT_0);
Dentro ya de la función de interrupción hacemos solo dos cosas:
Borrar el flag de interrupción, bit T2IF del registro IFS0 por medio de la llamada a la función
mT2ClearIntFlag();
y hacer el toggle del led conectado en el pin RD0
mPORTDToggleBits(BIT_0);
Una vez compilado el ejemplo y ejecutado en la starter kit podemos comprobar si son correctos los cálculos realizados, viendo en el osciloscopio el periodo de la señal obtenida en el pin RD0.
BIBLIOGRAFIA
Documentación de referencia PIC32 de Microchip
MARCAS REGISTRADAS
El nombre y el logotipo de Microchip son marcas registradas de Microchip Technology Inc. en EE.UU. y otros países.
Muchas de las imágenes utilizadas en este artículo están sacadas de los Data Sheet y manuales de referencia que proporciona Microchip, su utilización es con fines educativos y sin ánimo de lucro, y con la intención de no infringir ninguno derecho de autor.
Los comentarios y conclusiones hechos en este artículo son personales y no garantizan la veracidad de los mismo, para una información más amplia y precisa consultar la bibliografía citada.
Un saludo