El programa UART se atasca cuando se habilita la interrupción con PIC24F

Estoy usando PIC24FJ64GB002.
Quiero usarlo para UART por RS232C con mi PC.

El problema es que, si hago "IEC0bits.U1TXIE = 1;", el programa no funciona en absoluto.
Quiero decir, incluso el LED no se vuelve brillante.
Sin embargo, incluso si "IEC0bits.U1TXIE = 0", "IEC0bits.U1RXIE = 1" o "IEC0bits.U1RXIE = 0;", el programa funciona excepto por el tema de la velocidad en baudios.

Quiero saber qué está mal y cómo corregirlo.
Pero, supongo que puede ser debido al problema de hardware.

Y discúlpeme por mi inglés ya que no soy un hablante nativo.

mi entorno ;
SO: Windows 7,
IDE: MPLABX,
Compilador: XC16

/* 
 * File:   main_pic24fSerial.c
 * Author: Raven Iqqe
 *
 * Created on 2013/09/26, 18:56
 */

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include <uart.h>

#define OUTSTR  "Hello!\0"
#define ALPHA   'A'
#define GRNLED  LATBbits.LATB15
#define YLWLED  LATBbits.LATB14
#define REDLED  LATBbits.LATB13

// CONFIG4
#pragma config DSWDTPS = DSWDTPSF       // DSWDT Postscale Select (1:2,147,483,648 (25.7 days))
#pragma config DSWDTOSC = LPRC          // Deep Sleep Watchdog Timer Oscillator Select (DSWDT uses Low Power RC Oscillator (LPRC))
#pragma config RTCOSC = SOSC            // RTCC Reference Oscillator  Select (RTCC uses Secondary Oscillator (SOSC))
#pragma config DSBOREN = ON             // Deep Sleep BOR Enable bit (BOR enabled in Deep Sleep)
#pragma config DSWDTEN = ON             // Deep Sleep Watchdog Timer (DSWDT enabled)

// CONFIG3
#pragma config WPFP = WPFP63            // Write Protection Flash Page Segment Boundary (Highest Page (same as page 42))
#pragma config SOSCSEL = SOSC           // Secondary Oscillator Pin Mode Select (SOSC pins in Default (high drive-strength) Oscillator Mode)
#pragma config WUTSEL = LEG             // Voltage Regulator Wake-up Time Select (Default regulator start-up time used)
#pragma config WPDIS = WPDIS            // Segment Write Protection Disable (Segmented code protection disabled)
#pragma config WPCFG = WPCFGDIS         // Write Protect Configuration Page Select (Last page and Flash Configuration words are unprotected)
#pragma config WPEND = WPENDMEM         // Segment Write Protection End Page Select (Write Protect from WPFP to the last page of memory)

// CONFIG2
#pragma config POSCMOD = XT             // Primary Oscillator Select (Primary Oscillator Enabled, XT)
#pragma config I2C1SEL = PRI            // I2C1 Pin Select bit (Use default SCL1/SDA1 pins for I2C1 )
#pragma config IOL1WAY = ON             // IOLOCK One-Way Set Enable (Once set, the IOLOCK bit cannot be cleared)
#pragma config OSCIOFNC = OFF           // OSCO Pin Configuration (OSCO pin functions as clock output (CLKO))
#pragma config FCKSM = CSECME           // Clock Switching and Fail-Safe Clock Monitor (Sw Enabled, Mon Enabled)
#pragma config FNOSC = FRCDIV           // Initial Oscillator Select (Fast RC Oscillator with Postscaler (FRCDIV))
#pragma config PLL96MHZ = ON            // 96MHz PLL Startup Select (96 MHz PLL Startup is enabled automatically on start-up)
#pragma config PLLDIV = NODIV           // USB 96 MHz PLL Prescaler Select (Oscillator input divided by 12 (48 MHz input))
#pragma config IESO = ON                // Internal External Switchover (IESO mode (Two-Speed Start-up) enabled)

// CONFIG1
#pragma config WDTPS = PS32768          // Watchdog Timer Postscaler (1:32,768)
#pragma config FWPSA = PR128            // WDT Prescaler (Prescaler ratio of 1:128)
#pragma config WINDIS = OFF             // Windowed WDT (Standard Watchdog Timer enabled,(Windowed-mode is disabled))
#pragma config FWDTEN = ON              // Watchdog Timer (Watchdog Timer is enabled)
#pragma config ICS = PGx1               // Emulator Pin Placement Select bits (Emulator functions are shared with PGEC1/PGED1)
#pragma config GWRP = OFF               // General Segment Write Protect (Writes to program memory are allowed)
#pragma config GCP = OFF                // General Segment Code Protect (Code protection is disabled)
#pragma config JTAGEN = ON              // JTAG Port Enable (JTAG port is enabled)

// Local Variables Declaration
char str[32] = OUTSTR;

// Prototype Declaration
char outputstr(char *);
void delay(void);

// Main Function
int main(void) {///*
    // Oscillator Setting
    OSCCONbits.COSC = 0b001;    // Current oscillator is FRC with PLL.
    OSCCONbits.NOSC = 0b010;    // New oscillator is 8MHz.

    OSCCONbits.CLKLOCK = 0;
    OSCCONbits.IOLOCK = 0;
    OSCCONbits.LOCK = 0;
    OSCCONbits.CF = 0;
    OSCCONbits.POSCEN = 0;
    OSCCONbits.SOSCEN = 0;
    OSCCONbits.OSWEN = 1;

    // Clock Division Setting
    CLKDIVbits.ROI = 0;         // No division of clock
    CLKDIVbits.DOZE = 0b000;
    CLKDIVbits.DOZEN = 0;
    CLKDIVbits.RCDIV = 0b000;
    CLKDIVbits.CPDIV = 0b00;
    CLKDIVbits.PLLEN = 0;

    // Port Setting
    TRISBbits.TRISB10 = 1;
    TRISBbits.TRISB11 = 1;
    TRISBbits.TRISB13 = 0;  // Red LED
    TRISBbits.TRISB14 = 0;  // Yellow LED
    TRISBbits.TRISB15 = 0;  // Green LED
    LATB = 0;

    // UART Pin Setting
    RPINR18bits.U1RXR = 11;
    RPOR5bits.RP10R = 3;

    // AD Converter Setting
    AD1PCFG = 0xFFFF;       // Disable all A/D convertion.

    // Interrupt Setting
    SRbits.IPL = 0b111;     // CPU Interrupt Priority
    INTCON1bits.NSTDIS = 0; // Interrupt nesting is enabled.

    // UART Setting
    U1MODEbits.UARTEN = 1;
    U1MODEbits.UEN = 0b11;
    U1MODEbits.USIDL = 0;
    U1MODEbits.ABAUD = 0;
    U1MODEbits.BRGH = 0;
    U1MODEbits.PDSEL = 0b00;
    U1MODEbits.STSEL = 0;

    U1STAbits.UTXISEL0 = 0;
    U1STAbits.UTXISEL1 = 0;
    U1STAbits.UTXEN = 1;
    U1STAbits.UTXINV = 0;
    U1STAbits.URXISEL = 0b00;

    U1BRG = 207;

    IPC3bits.U1TXIP = 0b001;
    IPC2bits.U1RXIP = 0b001;
    IFS0bits.U1TXIF = 0;
    IFS0bits.U1RXIF = 0;
    IEC0bits.U1TXIE = 1;        // Here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    IEC0bits.U1RXIE = 1;


    // Main Routine
    while(1){
        if(OSCCONbits.CF == 1){
            REDLED = 1;
        }

        GRNLED = 1;
        outputstr(str);

        delay();
    }

    return (EXIT_SUCCESS);
}

// Normal Functions ************************************************************

// String Output
char outputstr(char *string){
    while(*string){                 // Output string until \0 (null).
        while (!IFS0bits.U1TXIF);   // Wait for completion of output
        U1TXREG = *string++;        // Output string and increment string's address.
    }
    YLWLED = 1;

    return(1);
}

void delay(void){
    int i, n;
    for(n=0; n<32; n++){
        for(i=0; i<10000; i++);
    }
}

Respuestas (4)

No estoy familiarizado con la programación de PIC, por lo que no puedo darle detalles, pero no veo una rutina de servicio de interrupción (ISR) para el UART en su código. En general, cuando se habilita una interrupción de transmisión UART, se generará una interrupción si el UART puede aceptar un nuevo byte de transmisión. Continuará interrumpiendo hasta que reciba suficientes bytes para llenar su FIFO o la interrupción esté deshabilitada/enmascarada .

La forma en que esto se hace normalmente es que la interrupción se deja deshabilitada/enmascarada hasta que la línea principal quiera enviar datos. La línea principal coloca los datos en un búfer de anillo y habilita la interrupción.

Ocurre la interrupción, ejecutando el ISR. Esto toma el siguiente byte disponible del búfer de anillo, lo alimenta al UART y luego regresa. Si se llama al ISR y encuentra que el búfer de anillo está vacío, deshabilita/enmascara su propia interrupción y regresa.

Obviamente, esta es una descripción de muy alto nivel y hay muchos detalles de los que preocuparse, como sincronizar el acceso al búfer circular entre la línea de correo y el ISR. En lugar de

No revisé todo ese código, pero ¿recordó borrar la condición de interrupción en el controlador de interrupciones? Ese es un error común que hace que todo no funcione tan pronto como llega la primera interrupción.

Si bien los procesadores varían en su enfoque para manejar las interrupciones, existen dos patrones generales:

  • Cada vez que el procesador esté a punto de ejecutar una instrucción y exista cualquier condición que haga que el procesador reciba una interrupción, cambiará uno o más indicadores de control de interrupción de tal manera que inhabilite una respuesta posterior a esa interrupción y llamará a la interrupción. rutina de servicio. Cuando finaliza la rutina de servicio de interrupción, la interrupción se volverá a habilitar de alguna manera y la ejecución volverá al código que se estaba ejecutando antes. Esto es lo que hace el PIC.

  • Cada vez que el procesador está a punto de ejecutar una instrucción y surge una condición que aún no ha sido reconocida que debería causar que el procesador tome una interrupción, reconozca la condición, tal vez deshabilite el bit de habilitación de interrupción asociado con esa condición y llame al ISR. . Cuando se complete la ISR, vuelva a habilitar la condición si se había deshabilitado.

Estoy muy a favor del primer enfoque, ya que funciona bien incluso cuando una rutina de interrupción tiene que lidiar con múltiples causas de interrupción. Si mientras el ISR está manejando una condición, surge otra que también necesita ser manejada, las cosas se procesarán de manera más eficiente si el ISR maneja esa segunda condición antes de que regrese, pero las cosas seguirán comportándose correctamente si el ISR sale sin manejar la segunda condición. , ya que el procesador volverá a ejecutar la ISR inmediatamente o casi inmediatamente después de salir; el ISR luego verificará todas las condiciones de interrupción, notará la nueva que ha surgido y la manejará. El único "problema" con este enfoque es que si se habilita una interrupción para una condición que el ISR no resuelve,

Algunos procesadores (pero no el PIC) intentan evitar el problema mencionado anteriormente al no volver a ejecutar un ISR si no resuelve la condición de interrupción. No me gusta este enfoque. Por lo general, no es tan malo si una interrupción tiene una sola causa asociada, pero complica las cosas si hay múltiples causas para una interrupción que no son distinguidas por el propio controlador. Supongamos, por ejemplo, que un UART tiene una sola salida de interrupción que combina condiciones de transmisión lista y recepción vacía, y un ISR comienza verificando la recepción lista y procesando los caracteres entrantes; una vez hecho esto, verifica si el UART está listo para enviar datos y, de ser así, lo alimenta con datos o desactiva la interrupción de transmisión. Es posible que entre el momento en que el ISR procesa los datos recibidos y el momento en que resuelve el " podría llegar otro byte de datos y volver a activar la condición de listo para recibir. Si eso sucede, el controlador de interrupciones nunca verá que la condición de "interrupción de UART" se haya resuelto alguna vez. En consecuencia, cada vez que la rutina de servicio de interrupción maneja la interrupción de transmisión, debe volver a sondear la interrupción de recepción. Si ha llegado un carácter recibido mientras se maneja la interrupción de transmisión lista, ese carácter recibido debe procesarse antes de que salga el ISR o, de lo contrario, la interrupción del puerto serie "morirá" [el controlador de interrupciones no registrará ninguna interrupción a menos que la solicitud de interrupción de UART se vuelve inactivo y luego se vuelve inactivo, pero la línea de solicitud de interrupción del UART no se vuelve inactiva a menos que el UART reciba servicio]. podría llegar otro byte de datos y volver a activar la condición de listo para recibir. Si eso sucede, el controlador de interrupciones nunca verá que la condición de "interrupción de UART" se haya resuelto alguna vez. En consecuencia, cada vez que la rutina de servicio de interrupción maneja la interrupción de transmisión, debe volver a sondear la interrupción de recepción. Si ha llegado un carácter recibido mientras se maneja la interrupción de transmisión lista, ese carácter recibido debe procesarse antes de que salga el ISR o, de lo contrario, la interrupción del puerto serie "morirá" [el controlador de interrupciones no registrará ninguna interrupción a menos que la solicitud de interrupción de UART se vuelve inactivo y luego se vuelve inactivo, pero la línea de solicitud de interrupción del UART no se vuelve inactiva a menos que el UART reciba servicio].

Debido a que el PIC usa el primer enfoque, no tiene que preocuparse por los problemas asociados con el segundo. Sin embargo, una buena cantidad de otras partes usan el segundo enfoque (dado que el hardware es más complicado, solo puedo suponer que los diseñadores de hardware piensan erróneamente que facilita las cosas para los implementadores de software), por lo que si alguna vez migra a otras partes, debe soportar eso. en mente.

En realidad, ninguno de los dos escenarios que enumera es cómo funcionan las interrupciones de PIC. Una interrupción PIC no deshabilita la interrupción en particular. El mecanismo de interrupción nunca altera el bit indicador de interrupción ni la habilitación específica. En el procesador sobre el que preguntó el OP, eleva el nivel de interrupción para que solo las causas de mayor prioridad puedan interrumpir.
@OlinLathrop: En PIC18Fxx, hay dos interrupciones: de baja prioridad y de alta prioridad. Tomar la interrupción de baja prioridad borra la habilitación de la interrupción de baja prioridad. Tomar la interrupción de alta prioridad borra la habilitación de interrupción global. La lógica de interrupción del PIC no juega con las habilitaciones de interrupción de periféricos individuales, pero borrar uno de esos dos indicadores de habilitación de interrupción en respuesta a una interrupción deshabilitará todas las respuestas posteriores a ese tipo de interrupción hasta que se vuelva a habilitar ese indicador.
Correcto, pero nunca borra la habilitación para la condición de interrupción específica como dijiste, y estamos hablando de un PIC 24 de todos modos, que no funciona así. En los PIC de 16 bits, tomar una interrupción aumenta el nivel de prioridad a uno más la prioridad de esa interrupción, deshabilitándola y cualquier interrupción de menor prioridad.
@OlinLathrop: ¿Mi primera viñeta es más de su agrado ahora?
@Sí eso es mejor.

Al configurar IEC0bits.U1TXIE en 1, está habilitando las interrupciones de transmisión.

Su código necesita un controlador de interrupción en este caso:

 void __attribute__((__interrupt__)) _U1TXInterrupt(void)
 {
    IFS0bits.U1TXIF = 0;  // clear the interrupt
    // your code goes here
 }

Es posible que el ISR se dispare tan pronto como se habilite la interrupción, lo que impide que su código alcance el punto en el que se alternan los LED: salta a una ubicación indefinida (causando una trampa de dirección, o simplemente quedando en un flash vacío) ya que no hay ISR definido en el código que mostraste.