jueves, 30 de mayo de 2019

Práctica 5.b Interrupciones internas (Internal interrupts pic32)


Práctica 5.b Interrupciones internas



Las interrupciones internas son generadas por módulos contenidos en la pastilla del microcontrolador, estos módulos pueden ser el UART, los temporizadores(Timer), el PWM, etc. En esta práctica se verá el uso de la interrupción generada por “Timer 1”.



En la práctica 3 se usó el “Timer 1” para generar un retardo. Como se recordará el “Timer” se puede ver como un contador. El conteo lo inicia en 0 y guarda en el registro TMRx la cuenta que lleva. El número hasta el que va a contar(periodo) lo pone el usuario en el registro PRx, y cuando TMRx es igual a PRx se levanta una bandera y/o se genera una interrupción. En la práctica 3, el CPU estuvo vigilando la bandera, sin realizar otro trabajo. Esta manera de usar el “Timer” tiene varias deficiencias, la más importante es que el CPU está dedicado a vigilar el estado de la bandera, otra deficiencia es que se complica obtener periodos precisos, ya que cada vez que se inicia un periodo se lleva a cabo un cambio de contexto en el caso de que la configuración y arranque del “Timer” se encuentren en una función, además de los Tcy’s necesarios para hacer las configuraciones y comparaciones al vigilar el estado de la bandera del “Timer”.





Pasos para configurar la interrupción del “Timer 1”



  1. Elegir modo, ya sea mono ó multi-vector, si es multi-vector se pone a uno el bit MVEC(12) del registro INTCON
  2. Se establecen las propiedades de la interrupción. En la figura 5.1, se puede ver que la interrupción generada por el “Timer 1” Tiene el vector 4, su bandera se encuentra en el bit 4 de IFS0, el bit de habilitación se encuentra en el bit 4 de IEC0 y su prioridad se fija en los bits 4, 3 y 2 del registro IPC1 y la sub-prioridad en los bits 1 y 0 del mismo registro IPC1.
  3. Se define la función que se ejecutará cuando se active la interrupción (DS51686E-p133). Esta función ya se encuentra predefinida y esta definición es la misma para todas las interrupciones, tanto internas como externas. En esta práctica queda definida de la siguiente manera:                 void __ISR(4, IPL7AUTO) invierte_led(void)

Se usa el mismo equipo y material de la práctica 5a

El circuito usado es el del sistema mínimo, no se requiere el circuito que se agrega en la figura 5.1, y por lo tanto tampoco se requieren esos componentes(“push-button” y la resistencia de 1k)

El programa

/*

* x32_05b.c

* Autor: Jesus Acosta

* 12 Abril 2019

* Ejemplo de interrupci'on timer 1

* Usa oscilador FRCPLL

* Micro = PIC32MX220F032B

* No requiere el uso de plib.h

*/



#include <p32xxxx.h>

#include <sys/attribs.h>          //Para las macros de Interrupciones



#pragma config ICESEL = ICS_PGx2 //PGEC = pin 22

                                                                  //PGED = pin 21



#pragma config JTAGEN = OFF      // Deshabilita JTAG

#pragma config FSOSCEN = OFF     // Deshabilita oscilador secundario

#pragma config FWDTEN = OFF      // Deshabilita watchdog timer



#pragma config FNOSC=FRCPLL      // Usa oscilador FRC con PLL [8MHz]]

//#pragma config FPLLIDIV=DIV_4  // Pero como PLL FPLLIDIV no lo divide

                                                                  // Entonces entran 4MHZ

#pragma config FPLLMUL=MUL_16    // Multiplica por 16 [Ahora 64MHz]

#pragma config FPLLODIV=DIV_8    // Divide entre 8 [Ahora 8MHz]

#pragma config FPBDIV = DIV_1    // Divide entre 1 el reloj a los

                                                                  // periféricos

#pragma config DEBUG = OFF       // Se pone a ON/OFF para depuraci'on

                                                                  // usando ICD3



void retardo1ms(void );                     //Prototipo función

void __ISR(4, IPL7AUTO) invierte_led(void); //Prototipo funci'on

int main(void);                             //Prototipo funcion main


//************** Programa principal **********************

int main(void){

    TRISACLR = 0x0010; //Se pone a cero bit 4 de TRISA (Output)

    LATASET = 0x0010; //Se pone a uno bit LATA4



    //Configura la interrupci'on del timer 1

    __builtin_disable_interrupts(); //Deshabilita todas las interrupciones

    IEC0bits.T1IE = 0; // Inhabilita interrupci'on timer 1

    INTCONbits.MVEC = 1; // Multivector habilitado

    IPC1bits.T1IP = 7; // Prioridad, 7 la mas alta

    IPC1bits.T1IS = 3; // Sub-prioridad 3, la mas alta

    IFS0bits.T1IF = 0; // Se baja bandera

    IEC0bits.T1IE = 1; // Habilita interrupci'on T1

    __builtin_enable_interrupts(); //Habilita todas las interrupciones



    retardo1ms(); //Se configura y arranca el “timer 1” solo una vez


    //Ciclo infinito haciendo nada

    while(1){

       asm("nop"); //Hace nada

    }

}



void retardo1ms(void ){

    /* Se asegura el reset del timer 1 */

    T1CON = 0;

    TMR1 = 0;

    PR1 = 7999; //Se pone el periodo

    IFS0bits.T1IF = 0; //Baja bandera

    IEC0bits.T1IE = 1; //Habilita interrupci'on del timer 1

    T1CON = 0x8000;    //Configura T1CON=1000 0000 0000 0000

                                          //con prescaler 1:1

    return;

}



//Funci'on que se ejecuta con la interupci'on de T1

void __ISR(4, IPL7AUTO) invierte_led(void){

    LATAINV = 0x0010; //Invierte estado bit LATA4

    IFS0bits.T1IF = 0; //Baja bandera

}





Descripción del programa:

Esta práctica consiste en un programa donde el CPU por única vez configura y arranca al “Timer 1”, después se queda ejecutando un ciclo infinito en cuyo interior solo se encuentra la instrucción “nop” que es un periodo Tcy inactivo del CPU. Cuando PR1 y TMR1 son iguales el “Timer 1” genera una interrupción y reinicia la cuenta poniendo a cero TMR1. La interrupción fuerza a que el CPU abandone el ciclo infinito que estaba ejecutando y realice las acciones que están programadas en la función asociada al “Timer 1”, esta función cambia el estado del puerto RA4. Si tenía un uno, cambia a cero, y si tenía un cero cambia a uno. Después el CPU regresa a continuar ejecutando el ciclo infinito.





Bibliografía:

Capítulo 7 del manual de referencia del pic32 (DS60001168K)

Capítulo 12 del manual de referencia del pic32 (DS60001168K)





Proyecto:



Realizar un contador de revoluciones por minuto (RPM) y desplegar el resultado en un LCD.

Práctica 5.a Interrupciones externas (External interrupts pic32)


Práctica 5.a Interrupciones externas

El objetivo de esta práctica es que el estudiante experimente con el uso de las interrupciones. El CPU, en todo momento está ejecutando un programa, y las interrupciones son un mecanismo para que se detenga y atienda a algún dispositivo. Por ejemplo en la PC existen interrupciones (IRQ), que atienden a dispositivos como el teclado con la IRQ=1, el disco duros con IRQ=5, etc. En el caso de los microcontroladores, las interrupciones se pueden encontrar en el UART, temporizadores, PWM, etc. También puede disponer de interrupciones externas generadas cuando cambia el nivel de voltaje que recibe una patita asociada a una interrupción.

El controlador de interrupciones es responsable de procesar las solicitudes de interrupción (IRQ) y presentarlas en el orden apropiado al CPU. El controlador de interrupción está diseñado para recibir hasta 96 IRQ desde periféricos en el chip capaces de generar Interrupciones, y cinco entradas externas. Todas las IRQ se muestrean en el flanco ascendente de SYSCLK y se pone a uno la bandera correspondiente en los registros IFSx. Solo las interrupciones externas se pueden configurar para que se activen en el flanco ascendente o en en el descendente de la señal de la interrupción. Una IRQ pendiente se indica cuando el bit que le corresponde en un registro IFSx es igual a “1”. El IRQ pendiente no causará más procesamiento si el bit correspondiente en el registro de habilitación de interrupción (IECx) está en cero (cleared). En otras palabras, los bits IECx actúan para habilitar las interrupciones. Todas las IRQ se codifican en un de vector con una longitud igual a 64. Dado que hay más IRQ que los números de vectores disponibles, algunos IRQ comparten números de vectores comunes. A cada entrada del vector se le asigna un número de prioridad de interrupción y número de conjunto registros sombra (SRS=Shadow Registers Set).

Registros Sombra(SRS)
Cuando el CPU atiende una interrupción, abandona la tarea que estaba realizando y ejecuta la rutina asociada a la interrupción. Cuando termina de ejecutar la rutina de la interrupción regresa a continuar la ejecución anterior. Para que el CPU pueda regresar a continuar con la ejecución de la tarea que estaba realizando antes de ser interrumpido, se guarda información como variables locales y registros del CPU, este proceso de guardar y restaurar el estado de ejecución se le conoce como cambio de contexto y puede consumir bastantes ciclos de CPU. En el caso de los PIC32, se tienen los SRS para reducir la cantidad de ciclos de CPU necesarios para realizar los cambios de contexto. Los SRS son registros adicionales a los que posee el CPU y que no requieren ser salvados. Por lo tanto cuando el CPU abandona la tarea que estaba realizando no se requiere que se salven los registros del CPU, pues al ejecutar el código asociado a la interrupción se usa otro juego de registros, los registros sombra, cuando regresa a la tarea anterior solo continua usando los registros originales del CPU y los SRS usados ya no son necesarios. 
 
Nivel de prioridad
El nivel de prioridad está determinado por la configuración del registro IPCx del vector asociado. En el modo Multi-Vector, el usuario puede seleccionar un nivel de prioridad para recibir un conjunto de registro sombra dedicado. En el modo de un solo vector, todas las interrupciones pueden recibir un conjunto de sombras dedicado. El controlador de interrupción selecciona la IRQ de mayor prioridad entre todas las IRQs pendientes y presenta el número de vector asociado, el nivel de prioridad y el número de conjunto de registros sombras al CPU. La sub-prioridad es para que el controlador de interrupciones pueda elegir que interrupción presentar al CPU en caso de que se generaran interrupciones con el mismo nivel de prioridad.

El modulo de interrupciones dispone de los siguientes registros SFR(Special Function Registers):

INTCON    Registro de control de interrupciones
INTSTAT   Registro de estatus
TPTMR      Registro de proximidad temporal del timer (ver DS61108E-p24)
IFSx            Registro de la bandera de estatus
IECx           Registro del control de habilitación
IPCx           Registro del control de prioridad


Modo mono-vector y multi-vector

Después de un “reset” el controlador de interrupciones está configurado para funcionar como mono-vector. En este modo todas las fuentes de interrupción tienen un número único de vector y para identificar la fuente se usa la ecuación: “Single Vector Address = EBase + 0x200”, para mas detalles ver capítulo 8.6.2 del documento DS61108E. Cuando se usa el modo multi-vector cada fuente de interrupción posee su propio número de vector, este modo aunque consume mas RAM, es mucho más rápido y fácil de usar. En esta práctica es el que se usará.

Interrupciones externas

En esta familia de microcontroladores se pueden tener hasta 5 interrupciones externas, sin embargo cada microcontrolador en particular dispone de un número igual o menor debido a la cantidad de patitas disponibles. En el caso del PIC32MX220F032B solo se dispone de una interrupción externa, la INT0.



Figura 5.1 Segmento de la Tabla 7.1 de la hoja de datos de la familia PIC32 (DS61168D-p88)

Pasos para configurar las interrupciones

  1. Elegir modo, ya sea mono ó multi-vector, si es multi-vector se pone a uno el bit MVEC(12) del registro INTCON
  2. Se establecen las propiedades de la interrupción. En la figura 5.1, se puede ver que la interrupción cero (INT0) Tiene el vector 3, su bandera se encuentra en el bit 3 de IFS0, el bit de habilitación se encuentra en el bit 3 de IEC0 y su prioridad se fija en los bits 28, 27 y 26 del registro IPC0 y la sub-prioridad en los bits 25 y 24 del mismo registro IPC0.
  3. Se define la función que se ejecutará cuando se active la interrupción (DS51686E-p133). Esta función ya se encuentra predefinida y esta definición es la misma para todas las interrupciones, tanto internas como externas. Esta función es: void __ISR(3, IPL7AUTO) invierte_led(void){
void      No regresa parámetros
__ISR            Nombre función (Interrupt Service Routine)
3                      Número de vector de INT0
IPL7 IPL7  Interrup Priority Level 7,
AUTO      Se elige de forma automática el uso de los SRS, si el microcontrolador dispone de ellos
apaga_led(void) Nombre de función definida por el usuario, no recibe parámetros

Para establecer la fuente de interrupción se puede usar una macro ya definida,  que para el caso de INT0 es_EXTERNAL_0_VECTOR”. En este manual se prefiere usar el número del vector de interrupción.

Equipo
  • Computadora personal
  • Fuente de poder de laboratorio
  • Osciloscopio digital
  • Programador ICD3, PICkit3 o equivalente
Material:
Cantidad Descripción
1 PIC32MX220F032B
1 Resistencia de 10kΩ
2 Resistencia de 1 kΩ
2 Diodo Emisor de luz (Led)
1 Tablilla para prototipo (Protoboard)


Figura 5.1 Este “push button” está conectado a la patita 16.

Para construir el circuito para la práctica, se agrega el circuito de la figura 5.1 al circuito del sistema mínimo.

El programa

/*
* x32_05a.c
* Autor: Jesus Acosta
* 11 Abril 2019
* Ejemplo de interrupci'on externa
* Usa oscilador FRCPLL
* Micro = PIC32MX220F032B
* No requiere el uso de plib.h
*/

#include <p32xxxx.h>
#include <sys/attribs.h>         //Para las macros de Interrupciones

#pragma config ICESEL = ICS_PGx2 //PGEC = pin 22
                                                                  //PGED = pin 21

#pragma config JTAGEN = OFF      // Deshabilita JTAG
#pragma config FSOSCEN = OFF     // Deshabilita oscilador secundario
#pragma config FWDTEN = OFF      // Deshabilita watchdog timer

#pragma config FNOSC=FRCPLL      // Usa oscilador FRC con PLL [8MHz]]
//#pragma config FPLLIDIV=DIV_4  // Pero con PLL FPLLIDIV no lo divide
                                                                  // Entonces entran 4MHZ
#pragma config FPLLMUL=MUL_16    // Multiplica por 16 [Ahora 64MHz]
#pragma config FPLLODIV=DIV_8    // Divide entre 8 [Ahora 8MHz]
#pragma config FPBDIV = DIV_1    // Divide entre 1 el reloj a los perif'ericos
#pragma config DEBUG = OFF       // Se pone a On para depuraci'on usando ICD3

void __ISR(3, IPL7AUTO) invierte_led(void); //Prototipo funci'on
int main(void);                             //Prototipo funcion main

//************** Programa principal **********************
int main(void){
    TRISACLR = 0x0010; //Se pone a cero bit 4 de TRISA (Output)
    LATASET = 0x0010; //Se pone a uno bit LATA4

    __builtin_disable_interrupts(); //Deshabilita todas las interrupciones
    IEC0bits.INT0IE = 0; // Inhabilita interrupci'on externa INT0
    INTCONbits.MVEC = 1; // Multivector habilitado
    INTCONbits.INT0EP = 0; // Se activa en flanco negativo
    IPC0bits.INT0IP = 7; // Prioridad, 7 la mas alta
    IPC0bits.INT0IS = 3; // Sub-prioridad
    IFS0bits.INT0IF = 0; // Se baja bandera
    IEC0bits.INT0IE = 1; // Habilita interrupci'on INT0
    __builtin_enable_interrupts(); //Habilita todas las interrupciones
    //Ciclo infinito haciendo nada
    while(1){
       asm("nop"); //Un periodo Tcy Haciendo nada
    }
}

//Funci'on que se ejecuta cuando se activa INT0
//void __ISR(_EXTERNAL_0_VECTOR, IPL7AUTO) invierte_led(void){
void __ISR(3, IPL7AUTO) invierte_led(void){
    LATAINV = 0x0010;    //Invierte estado bit 4 (0000 0000 0001 0000)
    IFS0bits.INT0IF = 0; //Se baja bandera de INT0
}
Descripción del programa:

Esta práctica consiste en un programa donde el CPU está ejecutando un ciclo infinito en cuyo interior solo se encuentra la instrucción “nop” que es un periodo Tcy inactivo del CPU. Cuando se presiona el “push-button” que se encuentra conectado a la patita 16, se dispara la INT0 y esto fuerza a que el CPU abandone el ciclo infinito y ejecute la rutina(función) asociada a la INT0. Esta función solo cambia el estado del bit RA4. Si tenía un uno, cambia a cero, y si tenía un cero cambia a uno. Después regresa al ciclo infinito