0
\$\begingroup\$

Recently I bought a SparkFun RFID Starter Kit with ID-12LA RFID Module. Similar to the one in the following Link: https://learn.sparkfun.com/tutorials/sparkfun-rfid-starter-kit-hookup-guide. I tried it using the USB interface and connected it to Putty and it worked great. I tried Also connecting it to Arduino as presented on the website, also everything is working correctly. I also tried not using the SoftwareSerial Library of the arduino and it also worked perfectly! When I started trying it with PIC18F4550, It didn't work as it should be. First I connected its TX prin to RX on the PIC microcontroller. And connected a character based LCD to the microcontroller. I am powering The PIC, RFID reader, and LCD through a 5V linear voltage regulator 7805. Note that the reader with the USB to serial interface board, is set to ASCII mode. The following is the code I am using:

#include <p18F4550.h>
#include <delays.h>

// CONFIG1H
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator 
                                // (HS))
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable bit (Fail-
                                // Safe Clock Monitor enabled)
#pragma config IESO = ON        // Internal/External Oscillator Switchover 
                                // bit (Oscillator Switchover mode enabled)

// CONFIG2L
#pragma config PWRT = ON        // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOR = ON         // Brown-out Reset Enable bits (Brown-out 
                                // Reset enabled in hardware only (SBOREN is 
                                // disabled))
#pragma config BORV = 3         // Brown-out Reset Voltage bits (Minimum 
                                // setting 2.05V)

// CONFIG2H
#pragma config WDT = OFF        // Watchdog Timer Enable bit 
                                // (WDT disabled (control is placed on the 
                                // SWDTEN bit))

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

#define LCD_RS LATBbits.LATB0   
#define LCD_EN LATBbits.LATB1   
#define LCD_DATA LATD

#define UART_RECEIVE_FLAG PIR1bits.RCIF

void initialize_LCD();
void latch_LCD();
void print_char_LCD(unsigned char c);
void print_HEX_BYTE_LCD(unsigned char byte);
void command_LCD(unsigned char c);
void clear_LCD();

void Delay_ms(int ms);

void chk_isr(void); // check interrupt subroutine
void RC_ISR(void);  // interrupt subroutine for receive of UART
void initializeSerial();

void enInterrupts();

volatile unsigned char recv;

#pragma code My_HiPrio_Int=0x0008//high priority interrupt

void My_HiPrio_Int(void) {
    _asm
        GOTO chk_isr
    _endasm
}
#pragma code

#pragma interrupt chk_isr //used for high-priority interrupt only

void chk_isr(void) {
    if (UART_RECEIVE_FLAG == 1)// && status == connection_OK_Status)
        RC_ISR();
}

void main(void) {

    // set LCD pins as output

    TRISBbits.TRISB0 = 0;
    TRISBbits.TRISB1 = 0;
    TRISD = 0x00;

    initialize_LCD();

    initializeSerial();
    enInterrupts();

    clear_LCD();
    Delay_ms(1000);
    while (1) {

    }
    return;
}

void Delay_ms(int ms) {

    int counter = 1;
    for (counter = 1; counter <= ms; ++counter) {
        Delay100TCYx(25);
    }

}

void initializeSerial() {
    TRISCbits.TRISC6 = 0; //TX pin = output
    TRISCbits.TRISC7 = 1; //RX pin = input
    TXSTA = 0x20; //low baud rate, 8-bit
    SPBRG = 15; //9600 baud rate
    RCSTAbits.CREN = 1;
    RCSTAbits.SPEN = 1;
    TXSTAbits.TXEN = 1;
    PIE1bits.RCIE = 1; //enable rcv interrupt
    //PIE1bits.TXIE = 1; //enable TX interrupt
}

void enInterrupts() {
    INTCONbits.PEIE = 1; //enable peripherals interrupt
    INTCONbits.GIE = 1; //enable all interrupts
}

void latch_LCD() {
    LCD_EN = 1;
    Delay_ms(1);
    LCD_EN = 0;
    Delay_ms(1);
}

void initialize_LCD() {
    TRISD = 0;
    TRISBbits.RB3 = 0;
    TRISBbits.RB4 = 0;

    Delay_ms(15);

    LCD_RS = 0;
    LCD_DATA = 0x30;
    latch_LCD();
    Delay_ms(16);

    LCD_RS = 0;
    LCD_DATA = 0x30;
    latch_LCD();
    Delay_ms(16);

    LCD_RS = 0;
    LCD_DATA = 0x30;
    latch_LCD();
    Delay_ms(16);

    LCD_RS = 0;
    LCD_DATA = 0x38;
    latch_LCD();
    Delay_ms(16);

    LCD_RS = 0;
    LCD_DATA = 0x08;
    latch_LCD();
    Delay_ms(16);

    LCD_RS = 0;
    LCD_DATA = 0x01;
    latch_LCD();
    Delay_ms(16);

    LCD_RS = 0;
    LCD_DATA = 0x06;
    latch_LCD();
    Delay_ms(16);

    LCD_RS = 0;
    LCD_DATA = 0x0E;
    latch_LCD();
    Delay_ms(16);
}

void print_char_LCD(unsigned char c) {
    LCD_DATA = c;
    LCD_RS = 1;
    latch_LCD();
    Delay_ms(1);
}

void print_HEX_BYTE_LCD(unsigned char byte){
    unsigned char nibble_MS = (byte >> 4) & 0x0F;
    unsigned char nibble_LS = byte & 0x0F;

    if(nibble_MS <= 9){
        nibble_MS += '0';
    }
    else{
        nibble_MS = nibble_MS - 10 + 'A';
    }
    print_char_LCD(nibble_MS);

    if(nibble_LS <= 9){
        nibble_LS += '0';
    }
    else{
        nibble_LS = nibble_LS - 10 + 'A';
    }
    print_char_LCD(nibble_LS);

    print_char_LCD(' ');
}

void command_LCD(unsigned char c) {
    LCD_DATA = c;
    LCD_RS = 0;
    latch_LCD();
    Delay_ms(1);
}

void clear_LCD() {
    command_LCD(0x01);
}

void RC_ISR() {
    recv = RCREG;
    print_HEX_BYTE_LCD(recv);
    UART_RECEIVE_FLAG = 0;
}

The problem is that whenever I scan a card (that already came prepackaged with the kit), I will only get the first 3 bytes of the reponse printed on the screen! Also If I rescan another card or the same card nothing will show on the LCD screen until I reset the microcontroller. After resetting also the first 3 bytes only will be displayed as I scan a card. I tried a code that counts the number of serially inputted bytes and it was 3, so what I am seeing on the LCD is the only response from the reader. Is this problem related to the reader or a problem in the code? My connections, I am assuming, are perfect or else I will never get the LCD to work or the reader (i.e. connections are good since I am getting a response).

Any help is appreciated. I googled alot with no similar case!

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

Since you receive three bytes correctly, the general mechanism is working, and the problem is somewhere on a higher level.

At 9600Baud, the transfer of one byte takes about 1ms. Your code needs at least 6ms due the delays to process a received byte.
So, if your RFID device sends the data too fast, you are going to miss some bytes.

The microcontroller indicates missed bytes by raising an overflow flag. To my knowledge, it will stop reading data until your code clears this flag.

In general, an interrupt routine should be short, to make it available for the next interrupt as fast as possible. You should buffer the received bytes in an array within the interrupt routine, and let the main routine shove them into the LCD.

This is straight forward when your device sends some kind of end-of-message signal like CR LF. In this case, your interrupt routine can write the entire message to a buffer and then notify the main routine to write the data to the LCD.


EDIT:

The datasheet mentiones RCSTA.OERR as Overrun error flag. Though it is not mentioned in the text any more, the datasheet says one should check for errors on reception of a byte, and, if there is one, clear it by setting RCSTA.CREN (Continous receive enabled) to 0 (and 1 again).

As to why exactly three bytes are received correctly:

Technically, a byte is received by pushing the bits into a shift register RSR on arrival. Once all eight bits are there, the byte is copied into RCREG, where it waits to be read by your code.
If there already is a byte waiting in RCREG, RSR also waits, but of course can not receive another byte. If a new byte is received, it is abandoned, the OERR flag is set, and the UART module stops operation until the code clears the error.
This way, you have a two-byte long buffer, which contains the last two bytes before overflow occurred.

Now, in your project, the first byte is received into RSR, copied into RCREG, and directly read by the code. The code will be busy with processing the byte for ages now. The second byte is received and also copied into RCREG, since the first byte was already read.
The third byte is also received into RSR, but can't be copied, because the second byte is still waiting.. The fourth byte causes OERR and is not stored, as any further bytes.

After an age, the code has finished processing, reads the second byte and starts to process it. The RSR copies the third byte into RCREG, where it will be processed by the code one age later. But meanwhile, no further byte is received.

\$\endgroup\$
  • \$\begingroup\$ Thank you! That is an ultimate answer! If I am not mistaken, by this way we are emulating the serial buffer of the Arduino. \$\endgroup\$ – Khaldoun Kassam Feb 11 '18 at 14:19

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.