Electronic – Why UART Interrupts are not working properly in dsPIC

cinterruptsmicrochippicuart

Hello I am working with the UART1 module in a dsPIC33FJ128GP802 but it is not working as expected using XC16 compiler. Here is my initialization code and interrupt routine, the debug() and printNumericDebug() macros just send data to UART2 module for debugging purposes:

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

So when I send my first byte through UART1 it works correctly going to interrupt routine and entering this function in the switch case:

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

Here everything work just right, but when I enter to vLogicAnalizer() problems start to appear, here is the code of 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
}

I get the 'L' character as expected coming from TX of UART1, then, I clear the UART1 buffer by reading all the available data and I get one or two data containing 0 value. AFTER this interrupt is immediately fired even if I am sending no data and the data beign read is 0.

I tried removing sleepWait() and just waiting for an incoming data with mReadUART1() but also I immediately get 0 value.

Why is this happening when I enter this function. If you need more source code I can upload the complete files for you. Thank you very much for your help!


EDIT: another thing I noticed is that sometimes interrupt is fired but no data is received (U1STA.URXDA bit is 0), why?

Best Answer

Ok after a few days I finally could solve it and it is a very stupid problem. Basically when I am entering to vLogicAnalizer() I set my TRISB:

TRISB = 0xFF00;

This was affecting my UART Tx and Rx pins so to solve this I just need to do:

TRISB |= 0xFF00;