0
\$\begingroup\$

I am completely new to microcontroller programing. Just started my journey with PIC18F24K22 and now I am on the UART interrupt stage. I have been reading the datasheet meticulously and it really took me some time to derive this code from zero. The main idea behind it is to send and receive the sent character on the screen (Implemented by UART <-> USB converter, I am also invoking PuTTy terminal for that). Some part of it works, but I still have 2 major problems.

  1. The UART receive interrupt triggers only once. After the flashing (or reset) I can only receive the first keyboard input character (depicted in terminal), after that the microcontroller does not respond to keyboard input whatsoever. If I omit all the global and peripheral interrupt enable/disable lines from the ISR routine, then I can send and receive keyboard input characters continuously. I can not understand why? But even then, the second problem persists.

  2. I can not get the transmit interrupts firing. I try to implement different interrupt priorities on purpose (to get a better understanding in the learning stage). Whatever I do, I can only get the receive interrupt to work (UART send functions works just like described in the first problem, but the transmit interrupt itself never triggers, havo no clue why....)

Been fighting with this test program for over a week now. Already tried heavy googling, it did not work... Also hooked up the logic analyzer to the circuit, the results were exactly as described above. However, I do not get any problems with framing or overrun errors, baud rate is 9600 indeed (1 bit is 104 us in duration), 1 stop bit, no parity, not inverted data, just casual UART setup. Also tried countless debugging code adjustments, but having reached this point I dont know what to do further... I really appreciate any help, this is driving me crazy....

(P.S. Please ignore extensive commenting. I am a new guy in here, just want to keep track of everything for myself)

Vilius

Configuration bits:

#ifndef XC_HEADER_TEMPLATE_H
#define XC_HEADER_TEMPLATE_H

#include <xc.h> 

// CONFIG1H
#pragma config FOSC = INTIO7       // Oscillator Selection bits (Internal oscillator block)
#pragma config PLLCFG = OFF     // 4X PLL Enable (Oscillator used directly)
#pragma config PRICLKEN = ON    // Primary clock enable bit (Primary clock enabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF       // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)

// CONFIG2L
#pragma config PWRTEN = OFF     // Power-up Timer Enable bit (Power up timer disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bits (Brown-out Reset disabled in hardware and software)
#pragma config BORV = 190       // Brown Out Reset Voltage bits (VBOR set to 1.90 V nominal)

// CONFIG2H
#pragma config WDTEN = OFF      // Watchdog Timer Enable bits (Watch dog timer is always disabled. SWDTEN has no effect.)
#pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config CCP2MX = PORTB3  // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN = OFF     // PORTB A/D Enable bit (PORTB<5:0> pins are configured as digital I/O on Reset)
#pragma config CCP3MX = PORTB5  // P3A/CCP3 Mux bit (P3A/CCP3 input/output is multiplexed with RB5)
#pragma config HFOFST = ON      // HFINTOSC Fast Start-up (HFINTOSC output and ready status are not delayed by the oscillator stable status)
#pragma config T3CMX = PORTC0   // Timer3 Clock input mux bit (T3CKI is on RC0)
#pragma config P2BMX = PORTC0  // ECCP2 B output mux bit (P2B is on RB5)
#pragma config MCLRE = EXTMCLR  // MCLR Pin Enable bit (MCLR pin enabled, RE3 input pin disabled)

// CONFIG4L
#pragma config STVREN = OFF     // Stack Full/Underflow Reset Enable bit (Stack full/underflow will not cause Reset)
#pragma config LVP = ON         // Single-Supply ICSP Enable bit (Single-Supply ICSP enabled if MCLRE is also 1)
#pragma config XINST = OFF      // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))

// CONFIG5L
#pragma config CP0 = OFF        // Code Protection Block 0 (Block 0 (000800-001FFFh) not code-protected)
#pragma config CP1 = OFF        // Code Protection Block 1 (Block 1 (002000-003FFFh) not code-protected)

// CONFIG5H
#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot block (000000-0007FFh) not code-protected)
#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM not code-protected)

// CONFIG6L
#pragma config WRT0 = OFF       // Write Protection Block 0 (Block 0 (000800-001FFFh) not write-protected)
#pragma config WRT1 = OFF       // Write Protection Block 1 (Block 1 (002000-003FFFh) not write-protected)

// CONFIG6H
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot Block (000000-0007FFh) not write-protected)
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM not write-protected)

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protection Block 0 (Block 0 (000800-001FFFh) not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF      // Table Read Protection Block 1 (Block 1 (002000-003FFFh) not protected from table reads executed in other blocks)

// CONFIG7H
#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot Block (000000-0007FFh) not protected from table reads executed in other blocks)

#endif  /* XC_HEADER_TEMPLATE_H */

The main program:

#include "config.h"
#include "stdint.h" 
#include "stdbool.h" 
#define _XTAL_FREQ 16000000 

uint8_t data ='A', dummy; //Just a random initail value for testing UART transmit command
bool RCD = false, LI = false, HI = false; //Receive indication variable
   
void UART_INIT (uint16_t GENR, unsigned SYNC, unsigned BRGH, unsigned BRG16, unsigned DTR){
    
    //SETTING UP UART PROTOCOL - SETTING EUSART REGISTER BITS, INTERRUPTS AND THEIR PRIORITIES
    
    ANSELCbits.ANSC7=0;
    //Digital input buffer enable (---VERY IMPORTANT---)
    
    TRISCbits.RC6 = 1;
    TRISCbits.RC7 = 1;
    //Configure RC7 and RC6 (UART pins) as inputs
    
    SPBRGH1 = (GENR & 0XFF00) >> 8;
    //Combining upper 8 bits of baud register with a bit mask
    SPBRG1 = GENR & 0X00FF;
    //Combining lower 8 bits of baud register with a bit mask
    
    RCSTA1bits.CREN = 1;
    //Enable (continuous) receiver
    RCSTA1bits.SPEN = 1;
    //Serial port enable, it sets RC7 and RC6 to UART mode by default, but setting it just to be sure
    RCSTA1bits.RX9 = 0;
    //8 bit mode setup
    BAUDCONbits.BRG16=BRG16;
    //Setting baud rate bus to 8 or 16 bits
    BAUDCONbits.DTRXP=DTR;
    //Setting data polarity (0 is non inverted data)
    
    TXSTA1bits.SYNC = SYNC;
    //Setting synchronous/asynchronous mode
    TXSTA1bits.BRGH = BRGH;
    //Baud rate speed select bit
    TXSTA1bits.TXEN = 1;
    //Transmit enable
    
    PIE1bits.RC1IE = 1;
    //Enable EUSART1 receive interrupt
    
    IPR1bits.RC1IP = 1;
    //Setting EUSART1 receive interrupt to high priority
    IPR1bits.TX1IP = 0;
    //Setting EUSART1 transmit interrupt to low priority   
}

void UART_RECEIVE (uint8_t *tx, bool *RECFLAG){
    if (RCSTA1bits.FERR){
    //Checking for framing error
    uint8_t err = RCREG1;
    //Reading the receive buffer to reset this error
    LATAbits.LA1^= 1;
    __delay_ms(250);
    LATAbits.LA1^= 1;}
    //error indication output port
        else if (RCSTA1bits.OERR){
        //Checking for overrun error
        RCSTA1bits.CREN = 0;
        RCSTA1bits.CREN = 1;
        //Re-enabling receiver
        LATAbits.LA2^= 1;
        __delay_ms(250);
        LATAbits.LA2^= 1;}
            else{
            //Lastly, condition if no errors occur
            *tx=RCREG1;
            //Received data from RCREG register becomes pointer to transmit variable data
            *RECFLAG = true;
            //This triggers main loop condition to send back received data
            }
}

void UART_SEND(uint8_t*tx){
    PIE1bits.TX1IE = 0;
    TXREG1=*tx;
    //TXREG1 is transmission buffer, we load the data we want to send to it
    //Loading data to TXREG initiates transmission    
    while(!(TXSTA1bits.TRMT));
    //Waiting until TSR (transmission shift register) gets empty to load a whole set of new data to TXREG without any delay on next transmission event
    //TSR is depicted only via TRMT bit (read only))
    PIE1bits.TX1IE = 1;
    
    /*Quote from the datasheet ,,To use interrupts when transmitting data, set the TXxIE 
                                bit only when there is more data to send. Clear the
                                TXxIE interrupt enable bit upon writing the last
                                character of the transmission to the TXREGx.
    */
    
    //As I understand, PIE1bits.TX1IE should be declared and set just like that, but I am not sure...
}

void __interrupt(high_priority) h_isr(void){
  INTCONbits.GIEH = 0;
  if(PIR1bits.RC1IF){
  HI=true; //Receive interrupt indicator flag
  UART_RECEIVE (&data,&RCD);
  dummy=RCREG1; //Not sure about this one, just reading for the datasheet recommendation 
  PIR1bits.RC1IF^=1;
  } 
  INTCONbits.GIEH = 1;
  INTCONbits.GIEL = 1;
}

//If Global interrupt INTCONbits.GIE bit is cleared in the routine, UART doesn`t work (UART RX and TX registers are directly dependent on GIE, not like some other interrupts)

void __interrupt(low_priority) l_isr(void){
  INTCONbits.GIEH = 0; 
  if(!(PIR1bits.TX1IF)){
  LI=true; //Transmit interrupt indicator flag
  }  
  INTCONbits.GIEH = 1;
  INTCONbits.GIEL = 1;
}

void main(void) {
    
    OSCCONbits.IRCF = 0x07; 
    //Setting internal RC oscillator at 16 MHz
    OSCCONbits.SCS = 0x03;
    //Double check the clock source is internal oscillator
    while (OSCCONbits.HFIOFS!=1);
    //Waiting until internal clock gets stable
      
    TRISAbits.RA0 = 0;
    TRISAbits.RA1 = 0;
    TRISAbits.RA2 = 0;
    //Setup of the debugging output ports
    LATAbits.LA0= 0;
    LATAbits.LA1= 0;
    LATAbits.LA2= 0;
    //Initial value are all zeros 
    
    // TRISA ARE JUST FOR DEBUGGING, COMPLETELY NOT NECESSARY FOR ANY PROGRAM
    
    UART_INIT(103,0,1,0,0);  
    // Function to set everything up for UART
    // Can find it in the datasheet of the microcontroller or calculate by formulas provided
    // This is 9600 KBit/s asynchronous 16 MHZ configuration with 1 stop bit, not inverted data, no parity bits
  
    RCONbits.IPEN = 1;   
    //Interrupt priority enable 
    INTCONbits.GIEH = 1;
    //Global interrupts enable
    INTCONbits.GIEL = 1;
    //Peripheral interrupts enable
    
    while(1){
 
        if(RCD){
        UART_SEND(&data);
        RCD=false;}//This however triggers only once...
        
        if(HI){
        LATAbits.LA0^=1;
        __delay_ms(10);
        LATAbits.LA0^=1;
        HI = false;}//Just an indication LED
        
        if(LI){
        LATAbits.LA1^= 1;
        __delay_ms(10);
        LATAbits.LA1^= 1;
        LI = false;}//Just an indication LED
        
    }
}```
\$\endgroup\$
8
  • 3
    \$\begingroup\$ extensive commenting is very good ... it is less cluttered if placed at right side just like the first code listing ... correct indentation in the if blocks would also help readability \$\endgroup\$
    – jsotola
    Feb 6 at 18:52
  • \$\begingroup\$ microchip forum has an anwered question like yours \$\endgroup\$
    – jsotola
    Feb 6 at 19:02
  • \$\begingroup\$ I checked that forum deeply. However, I did not see any exact example, there may be similar topics, but I am afraid I need some personal advice or help in this situation. \$\endgroup\$ Feb 6 at 19:08
  • \$\begingroup\$ duckduckgo.com/?q=PIC18F24K22+uart&ia=web \$\endgroup\$
    – jsotola
    Feb 6 at 19:20
  • \$\begingroup\$ Just like I mentioned, not my case exactly... Of course, the UART interrupts are wide topic, but I consider I have done all I could before posting here, now I am hoping for personal code explanation or example... But still, I want to honestly thank you. \$\endgroup\$ Feb 6 at 19:45

1 Answer 1

0
\$\begingroup\$

I answered the same exact question for the PIC18F24K22 on the Microchip forum here.

This should answer your question for an example of coding a UART loop back application for the PIC18F24K22 controller:

/*
 * File:   main.c
 * Author: dan1138
 * Target: PIC18F24K22
 * Compiler: XC8 v2.35
 * IDE: MPLABX v5.50
 * 
 *                           PIC18F24K22
 *                 +------------:_:------------+
 *  ICD_VPP RE3 -> :  1 VPP         PGD/RX2 28 : <> RB7 ICD_PGD
 *          RA0 <> :  2 AN0         PGC/TX2 27 : <> RB6 ICD_PGC
 *          RA1 <> :  3 AN1            AN13 26 : <> RB5
 *          RA2 <> :  4 AN2            AN11 25 : <> RB4
 *          RA3 <> :  5 AN3             AN9 24 : <> RB3
 *          RA4 <> :  6            INT2/AN8 23 : <> RB2
 *          RA5 <> :  7 AN4       INT1/AN10 22 : <> RB1
 *      GND VSS -> :  8           INT0/AN12 21 : <> RB0
 *          RA7 <> :  9 OSC1                20 : <- VDD 5v0
 *          RA6 <> : 10 OSC2                19 : <- VSS GND
 *          RC0 <> : 11 SOSCO      RX1/AN19 18 : <> RC7 UART_RX
 *          RC1 <> : 12 SOSCI      TX1/AN18 17 : <> RC6 UART_TX
 *          RC2 <> : 13 AN14           AN17 16 : <> RC5
 *          RC3 <> : 14 AN15           AN16 15 : <> RC4
 *                 +---------------------------+
 *                            DIP-28
 * 
 * Description:
 * 
 *  This application implements an inefficient way to send and receive
 *  data through the UART one byte at a time.
 * 
 *  This code can be improved by implementing some kind of buffer handling.
 *
 * Created on February 22, 2022, 4:13 PM
 */

#pragma config FOSC = INTIO67, PLLCFG = OFF, PRICLKEN = ON, FCMEN = OFF
#pragma config IESO = OFF, PWRTEN = ON, BOREN = OFF, BORV = 220, WDTEN = OFF
#pragma config WDTPS = 32768, CCP2MX = PORTC1, PBADEN = OFF, CCP3MX = PORTC6
#pragma config HFOFST = OFF, T3CMX = PORTC0, P2BMX = PORTC0, MCLRE = EXTMCLR
#pragma config STVREN = ON, LVP = ON, XINST = OFF
#pragma config CP0 = OFF, CP1 = OFF, CPB = OFF, CPD = OFF
#pragma config WRT0 = OFF, WRT1 = OFF, WRTC = OFF, WRTB = OFF, WRTD = OFF
#pragma config EBTR0 = OFF, EBTR1 = OFF, EBTRB = OFF      
/*
 * Define the system oscillator frequency that this code will setup
 */
#define _XTAL_FREQ (16000000UL)
#define FCY  (_XTAL_FREQ/4)
/*
 * Include target specific definitions for special function registers
 */
#include <xc.h>
/*
 * Interrupt handlers
 */
volatile uint8_t gRX_data;
volatile uint8_t gTX_data;
volatile struct {
    unsigned RX_ready : 1;
    unsigned TX_done : 1;
    unsigned RX_overrun : 1;
} gU1_flags;

void __interrupt(high_priority) InterruptHandlerHigh( void )
{
    uint8_t Result;
    
    if(PIE1bits.RC1IE) if (PIR1bits.RC1IF)
    {
        if (RCSTA1bits.OERR)
        {
            RCSTA1bits.CREN = 0;
            RCSTA1bits.CREN = 1;
            gU1_flags.RX_overrun = 1;
        }
        else if (RCSTA1bits.FERR)
        {
            RCREG1;
        }
        else
        {
            Result = RCREG1;
            if (gU1_flags.RX_ready)
            {
                gU1_flags.RX_overrun = 1;
            }
            else
            {
                gRX_data = Result;
                gU1_flags.RX_ready = 1;
            }
        }
    }
}
void __interrupt(low_priority) InterruptHandlerLow( void )
{
    if(PIE1bits.TX1IE) if (PIR1bits.TX1IF)
    {
        TXREG1 = gTX_data;
        PIE1bits.TX1IE = 0;
    }
}
/*  
 * Initialize this PIC18F24K22
 * FOSC is 16MHz derived from the internal RC oscillator at 16MHz, No PLL
 *  
 * This results in a 4MHz peripheral clock.
 */  
void PIC_Init(void)
{   
    INTCON = 0;                 /* Disable all interrupts */
    INTCON3 = 0;
    PIE1 = 0;
    PIE2 = 0;
    PIE3 = 0;
    PIE4 = 0;
    PIE5 = 0;
    
    OSCCONbits.IRCF = 0b111;    /* Internal Oscillator - HFINTOSC (16mHz) */
    OSCCONbits.SCS = 0b00;      /* System Clock Select */
    OSCTUNEbits.PLLEN = 0;      /* PLL Disable */
    
    CM1CON0bits.C1ON = 0;       /* disable everything, peripheral-wise */
    CM2CON0bits.C2ON = 0;
    VREFCON1bits.DACEN = 0;
    SLRCONbits.SLRA = 0;
    SLRCONbits.SLRB = 0;
    SLRCONbits.SLRC = 0;
    SRCON0bits.SRLEN = 0;
    SSP1CON1bits.SSPEN = 0;
    SSP2CON1bits.SSPEN = 0;
    
    ANSELA = 0x00;              /* Default all pins to digital */
    ANSELB = 0x00;              /* Default all pins to digital */
    ANSELC = 0x00;              /* Default all pins to digital */
    
    TRISA = 0x00;               /* Default all pins to outputs */
    TRISB = 0x00;               /* Default all pins to outputs */
    TRISC = 0x00;               /* Default all pins to outputs */
                        
    LATA = 0x00;                /* Set all pins to 0 */
    LATB = 0x00;                /* Set all pins to 0 */
    LATC = 0x00;                /* Set all pins to 0 */

    RCONbits.IPEN = 1;          /* use priority interrupt model */
}
/*
 * Initialize UART1
 */
#define BAUD_RATE (9600)
void UART1_Init(void) 
{ 
    PIE1bits.TX1IE = 0;     /* disable transmitter interrupts */
    PIE1bits.RC1IE = 0;     /* disable receiver interrupts */
    BAUDCON1 = 0;           /* stop UART1 */
    RCSTA1   = 0;
    TXSTA1   = 0;
    ANSELCbits.ANSC7 = 0;
    ANSELCbits.ANSC6 = 0;
    TRISCbits.TRISC7 = 1;
    TRISCbits.TRISC6 = 0;
    LATCbits.LATC6   = 1;
    
    _delay((FCY/BAUD_RATE)*20); /* wait 20 bit times with UART1 TX output at stop bit state */
    
    TXSTA1bits.SYNC = 0;    /* use asynchronous mode */
    TXSTA1bits.BRGH = 1;    /* use high speed BRG mode, see errata */
    BAUDCON1bits.BRG16 = 1; /* use 16-bit baud rate divisor */
    
    /* set the baud rate */
    SPBRGH1 = (unsigned char)(((FCY/BAUD_RATE)-1U) >> 8U);
    SPBRG1  = (unsigned char)((FCY/BAUD_RATE)-1U);
    
    TXSTA1bits.TXEN = 1;    /* enable transmitter */
    RCSTA1bits.CREN = 1;    /* enable receiver */
    RCSTA1bits.SPEN = 1;    /* enable UART1 */
    
    gU1_flags.RX_ready = 0;
    gU1_flags.RX_overrun = 0;
    gU1_flags.TX_done = 1;
    
    IPR1bits.RC1IP  = 1;    /* set receiver interrupt for high priority */
    IPR1bits.TX1IP  = 0;    /* set transmitter interrupt for low priority */
    PIE1bits.RC1IE  = 1;    /* enable receiver interrupts */
    PIE1bits.TX1IE  = 0;    /* disable transmitter interrupts */
}
/*
 * Get one character
 */
uint8_t UART1_getc(void)
{
    uint8_t Result;
    
    while (gU1_flags.RX_ready == 0){};
    Result = gRX_data;
    gU1_flags.RX_ready = 0;
    
    return Result;
}
/*
 * Send one character
 */
void UART1_putc(uint8_t Data)
{
    while(PIE1bits.TX1IE == 1){};
    gTX_data = Data;
    PIE1bits.TX1IE = 1;
}
/*
 * Send a zero terminated string
 */
void UART1_puts(char * pStr)
{
    if(pStr)
    {
        while(*pStr)
        {
            while(PIE1bits.TX1IE == 1){};
            gTX_data = *pStr++;;
            PIE1bits.TX1IE = 1;
        }
    }
}
/*
 * Main application
 */
void main(void) 
{
    /*
     * Initialize application
     */
    PIC_Init();
    UART1_Init();
    /*
     * Enable the interrupt system
     */
    INTCONbits.GIEL = 1;    /* enable low priority interrupts */
    INTCONbits.GIEH = 1;    /* enable high priority interrupts */
    
    __delay_ms(20);
    UART1_puts("\r\nPIC18F24K22 start built on " __DATE__ " at " __TIME__ "\r\n");
    /*
     * Application loop
     */
    for(;;)
    {
        UART1_putc(UART1_getc());
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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