¿Por qué las interrupciones UART no funcionan correctamente en dsPIC?

3

Hola, estoy trabajando con el módulo UART1 en un dsPIC33FJ128GP802 pero no funciona como se espera con el compilador XC16. Aquí está mi código de inicialización y la rutina de interrupción, las macros debug() y printNumericDebug() simplemente envían datos al módulo UART2 para propósitos de depuración:

Main.c

/* 
 * File:   Main.c
 * Author: Andres Torti
 *
 * Created on 24 de mayo de 2012, 21:15
 */

/* Definiciones */
#include "definitions.h"
#include "mylibs.h"
/* Includes generales */
#include <libpic30.h>     // Libreria para delays
#include <stdio.h>
#include <stdlib.h>
#include <uart.h>
/* Cabeceras de funciones */
#include "LogicAnalizer.h"
#include "Frecuencimetro.h"

#if DEBUG_ISIS == TRUE
    #include    <p33FJ32MC204.h>
    // FBS
    #pragma config BWRP = WRPROTECT_OFF     // Boot Segment Write Protect (Boot Segment may be written)
    #pragma config BSS = NO_FLASH           // Boot Segment Program Flash Code Protection (No Boot program Flash segment)
    // FGS
    #pragma config GWRP = OFF               // General Code Segment Write Protect (User program memory is not write-protected)
    #pragma config GSS = OFF                // General Segment Code Protection (User program memory is not code-protected)
    // FOSCSEL
    #pragma config FNOSC = PRIPLL           // Oscillator Mode (Primary Oscillator (XT, HS, EC) w/ PLL)
    #pragma config IESO = OFF               // Internal External Switch Over Mode (Start-up device with user-selected oscillator source)
    // FOSC
    #pragma config POSCMD = HS              // Primary Oscillator Source (HS Oscillator Mode)
    #pragma config OSCIOFNC = OFF           // OSC2 Pin Function (OSC2 pin has clock out function)
    #pragma config IOL1WAY = OFF            // Peripheral Pin Select Configuration (Allow Multiple Re-configurations)
    #pragma config FCKSM = CSDCMD           // Clock Switching and Monitor (Both Clock Switching and Fail-Safe Clock Monitor are disabled)
    // FWDT
    #pragma config WDTPOST = PS256          // Watchdog Timer Postscaler (1:256)
    #pragma config WDTPRE = PR128           // WDT Prescaler (1:128)
    #pragma config WINDIS = OFF             // Watchdog Timer Window (Watchdog Timer in Non-Window mode)
    #pragma config FWDTEN = OFF             // Watchdog Timer Enable (Watchdog timer enabled/disabled by user software)
    // FPOR
    #pragma config FPWRT = PWR2             // POR Timer Value (2ms)
    #pragma config ALTI2C = OFF             // Alternate I2C  pins (I2C mapped to SDA1/SCL1 pins)
    // FICD
    #pragma config ICS = PGD1               // Comm Channel Select (Communicate on PGC1/EMUC1 and PGD1/EMUD1)
    #pragma config JTAGEN = OFF             // JTAG Port Enable (JTAG is Disabled)
#else
    #include    <p33FJ128GP802.h>
    // FBS
    #pragma config BWRP = WRPROTECT_OFF     // Boot Segment Write Protect (Boot Segment may be written)
    #pragma config BSS = NO_FLASH           // Boot Segment Program Flash Code Protection (No Boot program Flash segment)
    #pragma config RBS = NO_RAM             // Boot Segment RAM Protection (No Boot RAM)
    // FSS
    #pragma config SWRP = WRPROTECT_OFF     // Secure Segment Program Write Protect (Secure segment may be written)
    #pragma config SSS = NO_FLASH           // Secure Segment Program Flash Code Protection (No Secure Segment)
    #pragma config RSS = NO_RAM             // Secure Segment Data RAM Protection (No Secure RAM)
    // FGS
    #pragma config GWRP = OFF               // General Code Segment Write Protect (User program memory is not write-protected)
    #pragma config GSS = OFF                // General Segment Code Protection (User program memory is not code-protected)
    // FOSCSEL
    #pragma config FNOSC = PRIPLL           // Oscillator Mode (Primary Oscillator (XT, HS, EC) w/ PLL)
    #pragma config IESO = OFF               // Internal External Switch Over Mode (Start-up device with user-selected oscillator source)
    // FOSC
    #pragma config POSCMD = HS              // Primary Oscillator Source (HS Oscillator Mode)
    #pragma config OSCIOFNC = OFF           // OSC2 Pin Function (OSC2 pin has clock out function)
    #pragma config IOL1WAY = OFF            // Peripheral Pin Select Configuration (Allow Multiple Re-configurations)
    #pragma config FCKSM = CSDCMD           // Clock Switching and Monitor (Both Clock Switching and Fail-Safe Clock Monitor are disabled)
    // FWDT
    #pragma config WDTPOST = PS256          // Watchdog Timer Postscaler (1:256)
    #pragma config WDTPRE = PR128           // WDT Prescaler (1:128)
    #pragma config WINDIS = OFF             // Watchdog Timer Window (Watchdog Timer in Non-Window mode)
    #pragma config FWDTEN = OFF             // Watchdog Timer Enable (Watchdog timer enabled/disabled by user software)
    // FPOR
    #pragma config FPWRT = PWR2             // POR Timer Value (2ms)
    #pragma config ALTI2C = OFF             // Alternate I2C  pins (I2C mapped to SDA1/SCL1 pins)
    // FICD
    #pragma config ICS = PGD1               // Comm Channel Select (Communicate on PGC1/EMUC1 and PGD1/EMUD1)
    #pragma config JTAGEN = OFF             // JTAG Port Enable (JTAG is Disabled)
#endif

/* RAM contigua para buffer */
__attribute__((far,aligned)) unsigned char Buffer[BUFFER_SIZE];
volatile unsigned int mode = NO_MODE;       // Modo para el USART

void debugUARTInit (void){
    #if DEBUG_ISIS == FALSE
    // Configuración UART 2
    U2MODEbits.USIDL = 1;       // Continúa operando en modo IDLE
    U2MODEbits.IREN = 0;        // IrDa deshabilitado
    U2MODEbits.RTSMD = 1;       // Pin RTS no se utiliza
    U2MODEbits.UEN = 0b00;      // Pines RTS y CTS no se usan, solo TX y RX
    U2MODEbits.WAKE = 1;        // UART habilitado en sleep
    U2MODEbits.LPBACK = 0;      // No usa modo Loop-back
    U2MODEbits.ABAUD = 0;       // Auto-Baud apagado
    U2MODEbits.BRGH = SPEED_MODE;
    U2BRG = BRGVal;
    U2MODEbits.PDSEL = 0b00;    // 8 bits no parity
    U2MODEbits.STSEL = 0;       // 1 bit de stop

    U2STAbits.UTXINV = 0;       // Estado IDLE a 1
    U2STAbits.UTXISEL0 = 0;     // Deshabilitada interrupción por envío
    U2STAbits.UTXISEL1 = 0;
    U2STAbits.UTXEN = 1;        // Pin TX habilitado
    U2STAbits.URXISEL = 0b00;   // Con solo un caracter la interrupcion por recepcion es lanzada
    U2STAbits.ADDEN = 0;        // Address mode apagado
    U2STAbits.URXISEL = 0b00;

    U2MODEbits.UARTEN = 1;      // Módulo UART habilitado
    #endif
}

/**
 * Interrupcion de recepcion de dato en el UART 1 que es el que se comunica
 * con el modulo bluetooth
 */
void __attribute__((interrupt, no_auto_psv)) _U1RXInterrupt(void){
    IFS0bits.U1RXIF = 0;

    debug("\r\nInterrupt");
    if(DataRdyUART1() && mode == NO_MODE){         // Compruebo por las dudas de que en verdad halla un dato
        mode = ReadUART1();     // Leo el dato desde el UART
        printNumericDebug("\r\nIData: ", mode);
    }

    if(U1STAbits.OERR) U1STAbits.OERR = 0;
    //U1MODEbits.WAKE = 1;        // UART habilitado en sleep
}

int main (void) {

    // Configuración del oscilador para usar un cristal de 4MHz y lograr 40MIPS
    PLLFBDbits.PLLDIV = 78;     // M=80
    CLKDIVbits.PLLPOST = 0;         // N1=2
    CLKDIVbits.PLLPRE  = 0;         // N2=2
    OSCTUN             = 0;         // Tune FRC oscillator, if FRC is used
    RCONbits.SWDTEN = 0;            // WDT desactivado
    while(!OSCCONbits.LOCK);            // Espero que se estabilize el PLL

    __IOUNLOCK
    #if DEBUG_ISIS == TRUE
        RPINR18bits.U1RXR = 0b11001;        // RX en RP25
        RPOR12bits.RP24R = 0b00011;         // TX en RP24
    #else
        RPINR18bits.U1RXR = 0b110;          // RX en RP6
        RPOR3bits.RP7R = 0b00011;           // TX en RP7

        RPOR0bits.RP0R = 0b00101;           // TX2 en RP0
        RPINR19bits.U2RXR = 1;              // RX2 en RP1
    #endif
    __IOLOCK

    TRISAbits.TRISA1 = 0;       // Pin de dirección del buffer
    PORTAbits.RA1 = 0;          // Puerto B del buffer como entrada y A como salida (A <- B)
    PORTB = 0xFF00;             // Parte del buffer como entrada para evitar problemas

    TRISBbits.TRISB2 = 0;
    LATBbits.LATB2 = 0;
    toggle(LATBbits.LATB2);
    __delay_ms(500);
    toggle(LATBbits.LATB2);
    __delay_ms(500);
    toggle(LATBbits.LATB2);
    __delay_ms(500);
    toggle(LATBbits.LATB2);
    __delay_ms(500);
    toggle(LATBbits.LATB2);

    // Configuración UART 1
    U1MODEbits.USIDL = 1;       // Continúa operando en modo IDLE
    U1MODEbits.IREN = 0;        // IrDa deshabilitado
    U1MODEbits.RTSMD = 1;       // Pin RTS no se utiliza
    U1MODEbits.UEN = 0b00;      // Pines RTS y CTS no se usan, solo TX y RX
    U1MODEbits.WAKE = 1;        // UART habilitado en sleep
    U1MODEbits.LPBACK = 0;      // No usa modo Loop-back
    U1MODEbits.ABAUD = 0;       // Auto-Baud apagado
    U1MODEbits.BRGH = SPEED_MODE;
    U1BRG = BRGVal;
    U1MODEbits.PDSEL = 0b00;    // 8 bits no parity
    U1MODEbits.STSEL = 0;       // 1 bit de stop

    U1STAbits.UTXINV = 0;       // Estado IDLE a 1
    U1STAbits.UTXISEL0 = 0;     // Deshabilitada interrupción por envío
    U1STAbits.UTXISEL1 = 0;
    U1STAbits.UTXEN = 1;        // Pin TX habilitado
    U1STAbits.URXISEL = 0b00;   // Con solo un caracter la interrupcion por recepcion es lanzada
    U1STAbits.ADDEN = 0;        // Address mode apagado

    IFS0bits.U1RXIF = 0;        // Borro el flag de interrupción
    U1STAbits.OERR = 0;         // Borro el flag de error
    IEC0bits.U1RXIE = 1;        // Activo interrupción del UART
    U1MODEbits.UARTEN = 1;      // Módulo UART habilitado

    debugUARTInit();
    __delay_ms(50);             // Pequeño delay de arranque para estabilizacion

    __C30_UART = 1;
    printf(" ");                // Debo enviar algo por printf() para que el UART
                                // comienze a funcionar, de otro modo el UART jamas inicia
    __C30_UART = 2;
    printf(" ");

    debug("Debugger Iniciado UART 2");

    while(TRUE){
        Idle();                 // El PIC se mantiene en modo Idle esperando un dato del UART
        printNumericDebug("\r\nDespertado de Idle modo: ", mode);
        switch(mode){
            case FRECUENCIMETER:
                //vFrecuencimetro();
                mode = NO_MODE;
                break;

            case LC_METER:
                //vLC_Meter();
                mode = NO_MODE;
                break;

            case VOLTMETER:
                mode = NO_MODE;
                break;

            case LOGIC_ANALIZER:
                vLogicAnalizer();
                mode = NO_MODE;
                break;
        }
    }
    return (EXIT_SUCCESS);
}

Definitions.h

#define FCY                 40000000UL      // MIPS para calculos de delay
#define BaudRate            9600

#define START_BYTE          'S'
#define BUFFER_SIZE         16000
#define ACK                 0x06

#define TRUE                1
#define FALSE               0

#define FRECUENCIMETER      0
#define LC_METER            1
#define LOGIC_ANALIZER      'L'
#define VOLTMETER           3
#define NO_MODE             4

#define INDUCTANCE_MODE     4
#define CAPACITOR_MODE      5

#define FlancoSubida        0
#define FlancoBajada        1

#define DEBUG_ISIS          FALSE

Mylibs.h

#ifndef MYLIBS_H
#define MYLIBS_H

#include "definitions.h"
#include "uart.h"
#include "stdio.h"
#include "stdlib.h"

void float2str(int *buffer, float f);
void printNumber(unsigned int number);

/* Definiciones generales */
// No permite cambios en Peripheral Pin Select
#define __IOLOCK    __builtin_write_OSCCONL(OSCCON | 0x40);
// Permite cambios en Peripheral Pin Select
#define __IOUNLOCK  __builtin_write_OSCCONL(OSCCON & 0xDF);
#define toggle(bit)     bit ^= 0x01;

#if DEBUG_ISIS == FALSE
    // Macros para DEBUG por el UART2
    #define debug(data)                   putsUART2((unsigned int *)data); while(BusyUART2());
    #define printNumericDebug(data, x)    debug(data); printNumber(x); while(BusyUART2());
    #define printCharDebug(data, x)       debug(data); putcUART2((char)x); while(BusyUART2());
#else
    #define debug(data)
    #define printNumericDebug(data, x)
    #define printCharDebug(data, x)
#endif

/**
 * Macro para el calculo de baudios automatico
 * Hay que colocar el define BAUDIOS en la funcioón OpenUSART
 * donde pide el valor de "spbrg" y SPEED_MODE donde pide modo
 * high o low speed:
 * SPEED_MODE hay que colcoarlo en donde se pide el modo de velocidad y BAUDIOS donde
 * se pide el "spbrg"
 */
#if (((FCY/BaudRate)/16)-1) < 0xFFFF
    #define SPEED_MODE 0
    #define BRGVal (((FCY/BaudRate)/16)-1)
#elif (((FCY/BaudRate)/4)-1) < 0xFFFF
    #define SPEED_MODE 1
    #define BRGVal (((FCY/BaudRate)/4)-1)
#else
    #error No se pudo establecer un baudrate correcto
#endif
/***********************************************************************************/

#endif

Entonces, cuando envío mi primer byte a través de UART1, funciona correctamente, voy a interrumpir la rutina e ingresar esta función en el caso del conmutador:

case LOGIC_ANALIZER:
       vLogicAnalizer();
       mode = NO_MODE;
       break;

Aquí todo funciona correctamente, pero cuando entro a vLogicAnalizer() comienzan a aparecer los problemas, aquí está el código de LogicAnalizer.c :

LogicAnalizer.c

    /* Includes */
#include    "mylibs.h"
#include    "definitions.h"
#include    "RunLengthAlgorithm.h"
#include    <uart.h>
#include    <libpic30.h>              
#include    <stdlib.h>

#if DEBUG_ISIS == FALSE
    #include    <p33FJ128GP802.h>
#else
    #include    <p33FJ32MC204.h>
#endif

/* Definiciones */
#define     F40MHz      'A'
#define     F20MHz      'S'
#define     F10MHz      'D'
#define     F4MHz       'F'
#define     F400KHz     'G'
#define     F2KHz       'H'
#define     F10Hz       'J'

#define     noTrigger       'N'
#define     simpleTrigger   'S'

#define bitTest(data, n) (data & (1 << n))
#define enableUARTInt() U1MODEbits.WAKE = 1; IFS0bits.U1RXIF = 0; IEC0bits.U1RXIE = 1;
#define disableUARTInt() IEC0bits.U1RXIE = 0;

#define sleepWait() enableUARTInt(); Idle(); disableUARTInt();
#define writeUART1(data) WriteUART1(data); while(BusyUART1());

/**
 * Definimos la variable tipo extern que fue definida en Main.c
 * Archivo 1:
 *      int VariableGlobal;     // Definición
 *      void UnaFunción (void); // Declaración externa implícita
 *
 *      int main() {
 *          VariableGlobal = 1;
 *          UnaFunción();
 *          return 0;
 *      }
 *
 * Archivo 2:
 *      extern int VariableGlobal;  // Declaración externa
 *
 *      void UnaFunción (void) {
 *          ++ VariableGlobal;
 *      }
 *
 * En este ejemplo la variable VariableGlobal es definida en el archivo 1.
 * Para utilizar la misma variable en el archivo 2, tiene que ser declarada
 * usando el keyword extern. Independientemente de la cantidad de archivos,
 * una variable global solo se define una vez, sin embargo, tiene que ser declarada
 * usando extern en cualquier archivo aparte de aquel que contiene la definición.
 * Técnicamente, UnaFunción es también externa, pero en C y en C++ todas las funciones
 * son consideradas externas por defecto y normalmente no necesitan ser declaradas.
 *
 * Fuente: http://es.wikipedia.org/wiki/Variable_externa
 */
__attribute__((far,aligned)) extern unsigned char Buffer[BUFFER_SIZE];

/**
 * Espero un byte desde el USART 1 y lo devuelvo
 * @return byte leido desde el USART
 */
unsigned int mReadUART1 (void){
    while(!DataRdyUART1());
    return ReadUART1();
}

void vLogicAnalizer(void){

    unsigned int keepGoing = 0;             // Determina si debe seguir o no el muestreo
    unsigned int triggerType;               // Tipo de trigger
    unsigned int samplingFrequency;         // Frecuencia de muestreo
    unsigned int channelMask;               // Máscara para filtrar los canales (un 1 en el canal que se desea el trigger, 0 de otro modo)

    TRISB = 0xFF00;                         // Parte alta del puerto B como entrada
    TRISAbits.TRISA1 = 0;                   // Pin de dirección del buffer
    PORTAbits.RA1 = 0;                      // Puerto B del buffer como entrada y A como salida (A <- B)
    CNPU1 = CNPU2 = 0;                      // Deshabilito pull-ups

    disableUARTInt();
    writeUART1(LOGIC_ANALIZER);             // Envío el modo

    debug("\r\nAnalizador Logico");
    while(DataRdyUART1()){
        printNumericDebug("\r\nDatos disponibles en el UART: ", ReadUART1());
    }

    // Leo el estado para saber si debo continuar o detenerme
    sleepWait();
    keepGoing = mReadUART1();
    printNumericDebug("\r\nKeepGoing recibido: ", keepGoing);

    while(keepGoing != 0){

        if(keepGoing != 0){
            debug("\r\nKeep Going!");
            samplingFrequency = mReadUART1();   // Obtengo la frecuencia de muestreo
            triggerType = mReadUART1();         // Obtengo el tipo de trigger
            channelMask = mReadUART1();         // Obtengo la máscara

            printCharDebug("\r\nSampling Frequency: ", samplingFrequency);
            printNumericDebug("\r\nTrigger Type: ", triggerType);
            printNumericDebug("\r\nChannel Mask: ", channelMask);

            CNEN1 = CNEN2 = 0;
            if(channelMask == 0) triggerType = noTrigger;

            // De acuerdo al bit seteado en el Mask detecto o no el cambio de estado
            // en el pin correspondiente
            if(bitTest(channelMask, 7)) CNEN1bits.CN11IE = 1;
            if(bitTest(channelMask, 6)) CNEN1bits.CN12IE = 1;
            if(bitTest(channelMask, 5)) CNEN1bits.CN13IE = 1;
            if(bitTest(channelMask, 4)) CNEN1bits.CN14IE = 1;
            if(bitTest(channelMask, 3)) CNEN1bits.CN15IE = 1;
            if(bitTest(channelMask, 2)) CNEN2bits.CN16IE = 1;
            if(bitTest(channelMask, 1)) CNEN2bits.CN21IE = 1;
            if(bitTest(channelMask, 0)) CNEN2bits.CN22IE = 1;

            // Habilito interrupciones por cambio de estado en caso de usarlas
            if(triggerType == simpleTrigger){
                IEC1bits.CNIE = 1;
                IFS1bits.CNIF = 0;
            }
            else IEC1bits.CNIE = 0;
        }
        else{
            debug("\r\nAnalizador Logico BREAK");
            break;
        }

        switch(samplingFrequency){
            case F40MHz:
                debug("\r\nSampling 40MHz");
                if(triggerType == noTrigger) vSample40MHz((unsigned char *)&PORTB+1, Buffer, BUFFER_SIZE);
                else if(triggerType == simpleTrigger) vSample40MHzTriggerSimple((unsigned char *)&PORTB+1, Buffer, BUFFER_SIZE, channelMask);
                break;

            case F20MHz:
                debug("\r\nSampling 20MHz");
                if(triggerType == noTrigger) vSample20MHz((unsigned char *)&PORTB+1, Buffer, BUFFER_SIZE);
                else if(triggerType == simpleTrigger) vSample20MHzTriggerSimple((unsigned char *)&PORTB+1, Buffer, BUFFER_SIZE, channelMask);
                break;

            case F10MHz:
                debug("\r\nSampling 10MHz");
                if(triggerType == noTrigger) vSample10MHz((unsigned char *)&PORTB+1, Buffer, BUFFER_SIZE);
                else if(triggerType == simpleTrigger) vSample10MHzTriggerSimple((unsigned char *)&PORTB+1, Buffer, BUFFER_SIZE, channelMask);
                break;

            case F4MHz:
                debug("\r\nSampling 4MHz");
                if(triggerType == noTrigger) vSample4MHz((unsigned char *)&PORTB+1, Buffer, BUFFER_SIZE);
                else if(triggerType == simpleTrigger) vSample4MHzTriggerSimple((unsigned char *)&PORTB+1, Buffer, BUFFER_SIZE, channelMask);
                break;

            case F400KHz:
                debug("\r\nSampling 400KHz");
                if(triggerType == noTrigger) vSample400KHz((unsigned char *)&PORTB+1, Buffer, BUFFER_SIZE);
                else if(triggerType == simpleTrigger) vSample400KHzTriggerSimple((unsigned char *)&PORTB+1, Buffer, BUFFER_SIZE, channelMask);
                break;

            case F2KHz:
                debug("\r\nSampling 2KHz");
                //vSample2KHz((char *)&PORTB+1, &Buffer, BUFFER_SIZE);
                break;

            case F10Hz:
                debug("\r\nSampling 10Hz");
                //vSample10Hz((char *)&PORTB+1, &Buffer, BUFFER_SIZE);
                break;

            default:
                debug("\r\nSampling Default");
                break;
        }

        writeUART1(START_BYTE);             // Envío byte de Start
        writeUART1(LOGIC_ANALIZER);         // Envío proveniencia del dato

        // Envío el buffer comprimido
        RLEncodeSendBuffer(Buffer, BUFFER_SIZE);
        // Dos 0xFF indican la terminación
        writeUART1(0xFF);
        writeUART1(0xFF);
    }
    debug("\r\nSale de Analizador Logico");
    enableUARTInt();        // Habilito nuevamente las interrupciones UART
}

Obtengo el carácter 'L' como se esperaba de TX de UART1, luego borro el búfer de UART1 leyendo todos los datos disponibles y obtengo uno o dos datos que contienen 0 valores. DESPUÉS, esta interrupción se activa inmediatamente, incluso si no envío datos y la lectura de los datos es 0.

Intenté eliminar sleepWait() y solo esperé los datos entrantes con mReadUART1() , pero también obtuve un valor de 0 de forma inmediata.

¿Por qué sucede esto cuando entro en esta función? Si necesita más código fuente, puedo cargar los archivos completos para usted. Muchas gracias por tu ayuda!

EDITAR: otra cosa que noté es que a veces se dispara una interrupción pero no se reciben datos (el bit U1STA.URXDA es 0), ¿por qué?

    
pregunta Andres

1 respuesta

4

Ok, después de unos días, finalmente pude resolverlo y es un problema muy estúpido. Básicamente, cuando estoy ingresando a vLogicAnalizer() , configuro mi TRISB:

TRISB = 0xFF00;

Esto estaba afectando a mis pines UART Tx y Rx, así que para resolver esto solo necesito hacerlo:

TRISB |= 0xFF00;
    
respondido por el Andres

Lea otras preguntas en las etiquetas