1
\$\begingroup\$

I'm using PIC24FJ64GB002.
I want to use it for UART by RS232C with my PC.

The problem is that, if I make "IEC0bits.U1TXIE = 1;", the program doesn't work at all.
I mean even LED does not become bright.
However, even if "IEC0bits.U1TXIE = 0", "IEC0bits.U1RXIE = 1" or "IEC0bits.U1RXIE = 0;", the program works except for the matter of the baud rate.

I want to know what is wrong and how to make it right.
But, I guess it can be due to the hardware problem.

And, excuse me for my English as I'm not a native speaker.

My environment;
OS: 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++);
    }
}
\$\endgroup\$

4 Answers 4

2
\$\begingroup\$

I'm not familiar with PIC programming so I cannot give you specifics, but I don't see an interrupt service routine (ISR) for the UART in your code. In general, when a UART transmit interrupt is enabled, an interrupt will be generated if the UART can accept a new transmit byte. It will continue interrupting until it receives enough bytes to fill its FIFO or the interrupt is disabled/masked.

The way this is normally done is that the interrupt is left disabled/masked until the main-line wants to send data. The main-line puts the data in a ring-buffer and enables the interrupt.

The interrupt happens, running the ISR. This takes the next available byte from the ring buffer, feeds it to the UART and then returns. If the ISR is called and finds the ring buffer enpty, it disables/masks its own interrupt and returns.

Obviously, this is a very high-level description and there is a lot of detail to worry about, such as synchronizing access to the ring buffer between the mail-line and the ISR.instead of

\$\endgroup\$
1
\$\begingroup\$

I didn't look thru all that code, but did you remember to clear the interrupt condition in the interrupt handler? That is a common mistake that makes everything not work as soon as the first interrupt comes along.

\$\endgroup\$
1
\$\begingroup\$

While processors vary in their approach to handling interrupts, there are two general patterns:

  • Any time the processor is about to execute an instruction and any condition exists which should cause the processor to take an interrupt, it will change one or more interrupt-control flags in such fashion as to disable further response to that interrupt and call the interrupt-service routine. When the interrupt-service routine is done, the interrupt will be re-enabled by some means and execution returned to whatever code was running before. This is what the PIC does.

  • Any time the processor is about to execute an instruction and a condition which has not already been acknowledged arises which should cause the processor to take an interrupt, acknowledge the condition, maybe disable the interrupt-enable bit associated with that condition, and call the ISR. When the ISR completes, re-enable the condition if it had been disabled.

I greatly favor the first approach, since it works well even when one interrupt routine has to deal with multiple interrupt causes. If while the ISR is handling one condition, another arises which also needs to be handled, things will get processed most efficiently if the ISR handles that second condition before it returns, but things will still behave correctly if the ISR exits without handling the second condition, since the processor will re-execute the ISR either immediately or almost immediately after it exits; the ISR will then check all interrupt conditions, notice the new one which has arisen, and handle it. The only "problem" with this approach is that if an interrupt is enabled for a condition which the ISR doesn't resolve, the processor will continuously re-execute the interrupt-service routine to the exclusion or near-exclusion of everything else, frequently causing the system to seem to "lock up".

Some processors (but not the PIC) try to avoid the above-mentioned problem by not having an ISR re-execute if it fails to resolve the interrupting condition. I dislike this approach. It's generally not too bad if an interrupt has only a single associated cause, but it complicates things if there are multiple causes for an interrupt which are not distinguished by the controller itself. Suppose, for example, that a UART has a single interrupt output which combines a transmit-ready and receive-empty conditions, and an ISR starts by checking receive ready and processing incoming characters; once those are done it checks whether the UART is ready to send data and, if so, either feeds it data or disables the transmit interrupt. It's possible that between the time the ISR processes received data and the time it resolves the "transmit-ready" condition, another byte of data might arrive and re-trigger the receive-ready condition. If that happens, the interrupt controller will never see that the "UART interrupt" condition has ever been resolved. Consequently, any time the interrupt-service routine handles the transmit interrupt, it must re-poll the receive interrupt. If a received character has arrived while handling the transmit-ready interrupt, that received character must be processed before the ISR exits or else the serial port interrupt will "die" [the interrupt controller won't register any interrupts unless the UART's interrupt-request goes inactive and then inactive, but the UART's interrupt-request line won't go inactive unless the UART is serviced].

Because the PIC uses the first approach, you don't have to worry about the problems associated with the second. A fair number of other parts use the second approach, however (since the hardware's more complicated, I can only guess that the hardware designers mistakenly think it makes things easier for the software implementers) so if you ever migrate to other parts you should bear that in mind.

\$\endgroup\$
5
  • \$\begingroup\$ Actually neither of the two scenarios you list is how the PIC interrupts work. A PIC interrupt does not disable the particular interrupt. The interrupt mechanism never alters the interrupt flag bit nor the specific enable. On the processor the OP asked about, it raises the interrupt level so that only higher priority causes can interrupt. \$\endgroup\$ Sep 30, 2013 at 18:00
  • \$\begingroup\$ @OlinLathrop: On PIC18Fxx, there are two interrupts: low-priority and high-priority. Taking the low-priority interrupt clears the low-priority interrupt enable. Taking the high-priority interrupt clears the global interrupt enable. The PIC's interrupt logic doesn't monkey with individual peripheral-interrupt enables, but clearing one of those two interrupt-enable flags in response to an interrupt will disable all further response to that interrupt type until that flag is re-enabled. \$\endgroup\$
    – supercat
    Sep 30, 2013 at 18:15
  • \$\begingroup\$ Right, but it never clears the enable for the specific interrupt condition as you said, and we're talking about a PIC 24 anyway, which doesn't work like that. On the 16 bit PICs, taking a interrupt bumps the priority level to one plus that interrupt's priority, disabling it and any lower priority interrupts. \$\endgroup\$ Sep 30, 2013 at 18:46
  • \$\begingroup\$ @OlinLathrop: Is my first bullet point more to your liking now? \$\endgroup\$
    – supercat
    Sep 30, 2013 at 19:05
  • \$\begingroup\$ @Yes, that's better. \$\endgroup\$ Oct 1, 2013 at 13:17
0
\$\begingroup\$

By setting IEC0bits.U1TXIE to 1, you are enabling transmit interrupts.

Your code needs an interrupt handler in this case:

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

It's possible that the ISR is getting fired as soon as the interrupt is enabled, preventing your code from reaching the point where your LEDs are toggled - it's jumping to an undefined location (causing an address trap, or just running off into empty flash) since there's no ISR defined in the code you showed.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.