UART-Programm bleibt hängen, wenn Interrupt mit PIC24F aktiviert wird

Ich verwende PIC24FJ64GB002.
Ich möchte es für UART über RS232C mit meinem PC verwenden.

Das Problem ist, dass, wenn ich "IEC0bits.U1TXIE = 1;" mache, das Programm überhaupt nicht funktioniert.
Ich meine, selbst LED wird nicht hell.
Aber auch wenn "IEC0bits.U1TXIE = 0", "IEC0bits.U1RXIE = 1" oder "IEC0bits.U1RXIE = 0;" ist, funktioniert das Programm bis auf die Baudrate.

Ich möchte wissen, was falsch ist und wie man es richtig macht.
Ich vermute aber, dass es am Hardwareproblem liegen kann.

Und entschuldigen Sie mich für mein Englisch, da ich kein Muttersprachler bin.

Meine Umgebung ;
Betriebssystem: Windows 7,
IDE: MPLABX,
Compiler: 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++);
    }
}

Antworten (4)

Ich bin mit der PIC-Programmierung nicht vertraut, daher kann ich Ihnen keine Einzelheiten nennen, aber ich sehe in Ihrem Code keine Interrupt-Service-Routine (ISR) für den UART. Wenn ein UART-Übertragungsinterrupt aktiviert ist, wird im Allgemeinen ein Interrupt erzeugt, wenn der UART ein neues Übertragungsbyte akzeptieren kann. Er unterbricht weiter , bis er genügend Bytes empfängt, um seinen FIFO zu füllen, oder der Interrupt deaktiviert/maskiert wird.

Dies geschieht normalerweise so, dass der Interrupt deaktiviert/maskiert bleibt, bis die Hauptleitung Daten senden möchte. Die Hauptleitung legt die Daten in einen Ringpuffer und gibt den Interrupt frei.

Der Interrupt passiert, indem die ISR ausgeführt wird. Dies nimmt das nächste verfügbare Byte aus dem Ringpuffer, führt es dem UART zu und kehrt dann zurück. Wenn die ISR aufgerufen wird und den Ringpuffer leer findet, deaktiviert/maskiert sie ihre eigene Unterbrechung und kehrt zurück.

Offensichtlich ist dies eine sehr allgemeine Beschreibung und es gibt viele Details, über die man sich Gedanken machen muss, wie zum Beispiel die Synchronisierung des Zugriffs auf den Ringpuffer zwischen der Mail-Leitung und dem ISR.statt

Ich habe den ganzen Code nicht durchgesehen, aber haben Sie daran gedacht, die Interrupt-Bedingung im Interrupt-Handler zu löschen? Das ist ein häufiger Fehler, der dazu führt, dass nicht alles funktioniert, sobald der erste Interrupt kommt.

Während Prozessoren in ihrer Herangehensweise an die Behandlung von Interrupts unterschiedlich sind, gibt es zwei allgemeine Muster:

  • Jedes Mal, wenn der Prozessor im Begriff ist, einen Befehl auszuführen, und eine Bedingung existiert, die den Prozessor dazu veranlassen sollte, einen Interrupt zu nehmen, ändert er ein oder mehrere Interrupt-Steuerungs-Flags so, dass eine weitere Reaktion auf diesen Interrupt deaktiviert und der Interrupt aufgerufen wird. Service-Routine. Wenn die Interrupt-Service-Routine fertig ist, wird der Interrupt auf irgendeine Weise wieder aktiviert und die Ausführung kehrt zu dem Code zurück, der zuvor ausgeführt wurde. Das macht der PIC.

  • Jedes Mal, wenn der Prozessor im Begriff ist, einen Befehl auszuführen, und eine Bedingung auftritt, die noch nicht bestätigt wurde, die den Prozessor veranlassen sollte, einen Interrupt zu akzeptieren, die Bedingung zu bestätigen, möglicherweise das dieser Bedingung zugeordnete Interrupt-Freigabebit zu deaktivieren und die ISR aufzurufen . Wenn die ISR abgeschlossen ist, aktivieren Sie die Bedingung erneut, falls sie deaktiviert war.

Ich bevorzuge den ersten Ansatz sehr, da er auch dann gut funktioniert, wenn eine Interrupt-Routine mit mehreren Interrupt-Ursachen umgehen muss. Wenn während der ISR eine Bedingung behandelt, eine andere auftritt, die ebenfalls behandelt werden muss, werden die Dinge am effizientesten verarbeitet, wenn die ISR diese zweite Bedingung behandelt, bevor sie zurückkehrt, aber die Dinge verhalten sich immer noch korrekt, wenn die ISR beendet wird, ohne die zweite Bedingung zu behandeln , da der Prozessor die ISR entweder unmittelbar oder fast unmittelbar nach dem Beenden erneut ausführt; die ISR prüft dann alle Interrupt-Bedingungen, bemerkt die neue, die aufgetreten ist, und handhabt sie. Das einzige "Problem" bei diesem Ansatz besteht darin, dass, wenn ein Interrupt für eine Bedingung aktiviert wird, die die ISR nicht löst,

Einige Prozessoren (aber nicht der PIC) versuchen, das oben erwähnte Problem zu vermeiden, indem sie eine ISR nicht erneut ausführen lassen, wenn sie den Unterbrechungszustand nicht lösen kann. Ich mag diesen Ansatz nicht. Es ist im Allgemeinen nicht schlimm, wenn ein Interrupt nur eine einzige zugeordnete Ursache hat, aber es kompliziert die Dinge, wenn es mehrere Ursachen für einen Interrupt gibt, die nicht vom Controller selbst unterschieden werden. Nehmen wir zum Beispiel an, dass ein UART einen einzigen Interrupt-Ausgang hat, der einen Sendebereitschafts- und Empfangs-leer-Zustand kombiniert, und ein ISR beginnt mit der Überprüfung der Empfangsbereitschaft und der Verarbeitung eingehender Zeichen; Sobald dies erledigt ist, prüft es, ob der UART bereit ist, Daten zu senden, und wenn ja, füttert es ihn entweder mit Daten oder deaktiviert den Sende-Interrupt. Es ist möglich, dass zwischen der Verarbeitung der empfangenen Daten durch den ISR und der Auflösung des " ein weiteres Datenbyte könnte ankommen und die Empfangsbereitschaft erneut auslösen. Wenn das passiert, wird der Interrupt-Controller niemals sehen, dass die "UART-Interrupt"-Bedingung jemals gelöst wurde. Folglich muss die Interrupt-Service-Routine jedes Mal, wenn sie den Sende-Interrupt handhabt, den Empfangs-Interrupt neu abfragen. Wenn ein empfangenes Zeichen eingetroffen ist, während der sendebereite Interrupt verarbeitet wurde, muss dieses empfangene Zeichen verarbeitet werden, bevor die ISR beendet wird, oder der Interrupt der seriellen Schnittstelle "sterbt" [der Interrupt-Controller registriert keine Interrupts, es sei denn, die Interrupt-Anforderung des UART wird inaktiv und dann inaktiv, aber die Unterbrechungsanforderungsleitung des UART wird nicht inaktiv, es sei denn, der UART wird bedient]. ein weiteres Datenbyte könnte ankommen und die Empfangsbereitschaft erneut auslösen. In diesem Fall wird der Interrupt-Controller niemals sehen, dass die Bedingung "UART-Interrupt" jemals behoben wurde. Folglich muss die Interrupt-Service-Routine jedes Mal, wenn sie den Sende-Interrupt handhabt, den Empfangs-Interrupt neu abfragen. Wenn ein empfangenes Zeichen eingetroffen ist, während der sendebereite Interrupt verarbeitet wurde, muss dieses empfangene Zeichen verarbeitet werden, bevor die ISR beendet wird, oder der Interrupt der seriellen Schnittstelle "sterbt" [der Interrupt-Controller registriert keine Interrupts, es sei denn, die Interrupt-Anforderung des UART wird inaktiv und dann inaktiv, aber die Unterbrechungsanforderungsleitung des UART wird nicht inaktiv, es sei denn, der UART wird bedient].

Da der PIC den ersten Ansatz verwendet, müssen Sie sich keine Gedanken über die mit dem zweiten verbundenen Probleme machen. Eine ganze Reihe anderer Teile verwenden jedoch den zweiten Ansatz (da die Hardware komplizierter ist, kann ich nur vermuten, dass die Hardwaredesigner fälschlicherweise denken, dass dies die Dinge für die Softwareimplementierer einfacher macht). Wenn Sie also jemals zu anderen Teilen migrieren, sollten Sie das ertragen im Kopf.

Tatsächlich funktionieren die PIC-Interrupts in keinem der beiden von Ihnen aufgelisteten Szenarien. Ein PIC-Interrupt deaktiviert nicht den bestimmten Interrupt. Der Interrupt-Mechanismus ändert niemals das Interrupt-Flag-Bit oder die spezifische Freigabe. Auf dem Prozessor, nach dem das OP gefragt hat, hebt es den Interrupt-Level an, so dass nur Ursachen mit höherer Priorität unterbrechen können.
@OlinLathrop: Auf PIC18Fxx gibt es zwei Interrupts: mit niedriger Priorität und mit hoher Priorität. Das Nehmen des Interrupts mit niedriger Priorität löscht die Interruptfreigabe mit niedriger Priorität. Das Nehmen des Interrupts hoher Priorität löscht die globale Interrupt-Freigabe. Die Interrupt-Logik des PIC spielt nicht mit einzelnen peripheren Interrupt-Freigaben, aber das Löschen eines dieser beiden Interrupt-Freigabe-Flags als Reaktion auf einen Interrupt deaktiviert alle weiteren Reaktionen auf diesen Interrupt-Typ, bis dieses Flag wieder aktiviert wird.
Richtig, aber es löscht nie die Freigabe für die spezifische Interrupt-Bedingung, wie Sie sagten, und wir sprechen sowieso von einem PIC 24, der so nicht funktioniert. Auf den 16-Bit-PICs erhöht das Nehmen eines Interrupts die Prioritätsstufe auf eins plus die Priorität dieses Interrupts, wodurch es und alle Interrupts mit niedrigerer Priorität deaktiviert werden.
@OlinLathrop: Gefällt dir mein erster Aufzählungspunkt jetzt mehr?
@Ja, das ist besser.

Indem Sie IEC0bits.U1TXIE auf 1 setzen, aktivieren Sie Sendeunterbrechungen.

Ihr Code benötigt in diesem Fall einen Interrupt-Handler:

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

Es ist möglich, dass die ISR ausgelöst wird, sobald der Interrupt aktiviert ist, wodurch verhindert wird, dass Ihr Code den Punkt erreicht, an dem Ihre LEDs umgeschaltet werden - er springt seitdem an eine undefinierte Stelle (verursacht eine Adressfalle oder läuft einfach in einen leeren Blitz). In dem von Ihnen angezeigten Code ist keine ISR definiert.