# PIC18f4550 serial programming with magnetic card reader

#### hari95

Joined Jan 18, 2015
8
i am quite new to microcontroller programming but i do understand basic configuration methods and able to code.I am trying to interface pic18f4550 with a magnetic card reader that uses rs232 connection.i connected the microcontroller and the card reader using a sp233ACP line driver which connects the card reader to the microcontroller.i am trying to receive data from the card reader to microcontroller and the microntroller will do a function(e.g when card is inserted into card reader,the microcontroller processes a new function and when card is taken out it performs another function).However,i cant receive any data from the card reader when i insert the card.I suspect it is my coding that has some problems but i cant seem to figure out the cause.In the code,i made it to turn on the LED in port D once the card was inside.The link to the magnetic card reader specification is https://www.dropbox.com/s/orvgc30fkkmehf5/WBM-1300 Specification.pdf?dl=0.I have tried all my best and have referred to all sources but they all bear no fruit.Following is the code that i implemented:

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

// Include this when using Bootloader Program ================================
#pragma udata

extern void _startup (void);  // See c018i.c in your C18 compiler dir
#pragma code _RESET_INTERRUPT_VECTOR = 0x000800
void _reset (void)
{
_asm goto _startup _endasm
}
#pragma code

#pragma code _HIGH_INTERRUPT_VECTOR = 0x000808
void _high_ISR (void)
{
;
}

#pragma code _LOW_INTERRUPT_VECTOR = 0x000818
void _low_ISR (void)
{
;
}
#pragma code

#pragma code

//  Ends here ===============================================================

// Program starts here:

void main(void)
{
// =======================================
CMCON = 0x07;
// ========================================

//TRISCbits.TRISC6=0;
TRISCbits.TRISC7=1;

RCSTA=0x90;
TXSTA=0x00;
SPBRG=77;

//t=0;
//r=0;

while(1)       //repeat
{
//if(PIR1bits.TXIF==1);
{
//TXREG=data_out[t];
//t++;
}
if(PIR1bits.RCIF==1)
{
PORTD = 0b00000001; // turn on LED at _____
Delay10KTCYx (250); // for a while

PORTD = 0b00000010; // turn on LED at _____
Delay10KTCYx (250); // for a while

//data_in[r]=RCREG;
//r++;
}

Delay10KTCYx(250);   //LEDs on for a while
Delay10KTCYx(250);

}

}
Moderators note: Please use code tags for pieces of code

Last edited by a moderator:

Joined Jul 18, 2013
23,791
I use a RS232 diagnostic program such as RS232 HEX COM, you would need a PC with a RS232 serial port to see the nature of the data output when you insert a card.
Max.

#### JohnInTX

Joined Jun 26, 2012
4,564
According to the Databook, both TRISC 6 and 7 should be 1. You have not set PORT D to be an output port. I didn't review all of your IO setup other than to notice that you haven't initialized ANY of the other IO. That's not good.

SPBRG=77 for what clock rate? I get 47.358080MHz? (BRGH=0, 8 Bit BRG) -> SPBRG = Fosc/[64(9600 + 1) I don't think SPBRG is correct. You should consider BRGH=1 and/or using BRG16 for more accuracy. Be sure to completely init the USART even if you just set the registers to their power on values.

Your main problem is that you sample RCIF once then delay a long time. Its probable that the code is executing those long delays when the first character followed by a next character is received. This will cause an overflow error OERR=1 and stop the receive process until the receiver is disabled then re-configured.

Personally, I always use an interrupt driven buffered RX and TX. For a burst thing like a mag card you could get away with a linear buffer and poll RXIF like to store characters but that's a mess. If you have any delays in the polling, you are going to run into trouble.

Review your setup and/or post your schematic, FULL configuration etc and we can go from there.

Good luck.

#### spinnaker

Joined Oct 29, 2009
7,835
Use code tags when you post. Remove all of those commented put lines. The way you posted your code makes everying hard to follow.

Why are you checking the interrupt flag in your main loop? That should happen in the ISR.

Are you certain your hardware is working as expected? What have you done to troubleshoot the issue on your own?

#### JohnInTX

Joined Jun 26, 2012
4,564
Why are you checking the interrupt flag in your main loop? That should happen in the ISR.
He's not using interrupts so polling RCIF is the only way to know a character has been received. Still a bad idea, though.

#### spinnaker

Joined Oct 29, 2009
7,835
He's not using interrupts so polling RCIF is the only way to know a character has been received. Still a bad idea, though.

Good catch. That code is laid out and commented so poorly it is tedious to read. My guess the OP will not be back. I hesitate to answer members with only 1 post but if no one answers then they will only have 1 post.

#### hari95

Joined Jan 18, 2015
8
I use a RS232 diagnostic program such as RS232 HEX COM, you would need a PC with a RS232 serial port to see the nature of the data output when you insert a card.
Max.
Ok will try that but what application would you recommend to use?

#### hari95

Joined Jan 18, 2015
8
According to the Databook, both TRISC 6 and 7 should be 1. You have not set PORT D to be an output port. I didn't review all of your IO setup other than to notice that you haven't initialized ANY of the other IO. That's not good.

SPBRG=77 for what clock rate? I get 47.358080MHz? (BRGH=0, 8 Bit BRG) -> SPBRG = Fosc/[64(9600 + 1) I don't think SPBRG is correct. You should consider BRGH=1 and/or using BRG16 for more accuracy. Be sure to completely init the USART even if you just set the registers to their power on values.

Your main problem is that you sample RCIF once then delay a long time. Its probable that the code is executing those long delays when the first character followed by a next character is received. This will cause an overflow error OERR=1 and stop the receive process until the receiver is disabled then re-configured.

Personally, I always use an interrupt driven buffered RX and TX. For a burst thing like a mag card you could get away with a linear buffer and poll RXIF like to store characters but that's a mess. If you have any delays in the polling, you are going to run into trouble.

Review your setup and/or post your schematic, FULL configuration etc and we can go from there.

Good luck.
I figured that trisc7 is to read data while trisc6 is to send data so i set TRISC7=1 and TRISC6=0 so that i could receive data serially.I didnt know that i had to initialize the other i/o ports as i am only using PORT C for the serial communication but i will implement it now!The SPBRG=77 is to set baud rate of 9600 with 48MHz Fosc.Since the card reader's specification specifies to use 9600 baud rate and 48MHz would be appropriate.I am not quite familiar with interrupts but i am willing to learn.What would you suggest that i do.

#### hari95

Joined Jan 18, 2015
8
Good catch. That code is laid out and commented so poorly it is tedious to read. My guess the OP will not be back. I hesitate to answer members with only 1 post but if no one answers then they will only have 1 post.
Terribly sorry for the delay.I will try to simplify and edit the codes immediately.Thanks

#### spinnaker

Joined Oct 29, 2009
7,835
I figured that trisc7 is to read data while trisc6 is to send data so i set TRISC7=1 and TRISC6=0 so that i could receive data serially.I didnt know that i had to initialize the other i/o ports as i am only using PORT C for the serial communication but i will implement it now!The SPBRG=77 is to set baud rate of 9600 with 48MHz Fosc.Since the card reader's specification specifies to use 9600 baud rate and 48MHz would be appropriate.I am not quite familiar with interrupts but i am willing to learn.What would you suggest that i do.

I always like this site for lessons

http://www.mikroe.com/chapters/view/15/chapter-2-programming-microcontrollers/

There is a section on interrupts. The actual code you will use might be different depending on your compiler but you will learn the basics,

Joined Jul 18, 2013
23,791
Ok will try that but what application would you recommend to use?
To debug a PIC RS232 port or test a communication to peripheral I usually use the Hex Com program here http://www.rs232pro.com/
Max.

#### hari95

Joined Jan 18, 2015
8
I always like this site for lessons

http://www.mikroe.com/chapters/view/15/chapter-2-programming-microcontrollers/

There is a section on interrupts. The actual code you will use might be different depending on your compiler but you will learn the basics,

I tried implementing a USART Receive Interrupt according to the Microchip software appendix and followed the steps required to set up an interrupt.I am able to control the LED lights but in the wrong way..I had to plug out the connection between the board and the card reader and push it back.Then when i insert the card in,The lighting stops and repeats the step again.Instead of that,i need the led to light up once the card is in without plugging out the connection(i know i am near there but i cant figure out a way to this).Below is the revised code i implemented

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

unsigned char insert;

// Include this when using Bootloader Program ================================
#pragma udata

extern void _startup (void);  // See c018i.c in your C18 compiler dir
#pragma code _RESET_INTERRUPT_VECTOR = 0x000800
void _reset (void)
{
_asm goto _startup _endasm
}
#pragma code

#pragma code _HIGH_INTERRUPT_VECTOR = 0x000808
void _high_ISR (void)
{

;
}

#pragma code _LOW_INTERRUPT_VECTOR = 0x000818
void _low_ISR (void)
{
;
}
#pragma code

#pragma code

//  Ends here ===============================================================

// Program starts here:

void main(void)
{
// =======================================
CMCON = 0x07;
// ========================================

TRISB=0b11110000;   //RB3 to RB0 are connected DIG3 to DIG0 active high
TRISD=0x00;
//TRISCbits.TRISC6=1;
TRISCbits.TRISC7=1;

RCSTA=0x90;
TXSTA=0x00;
SPBRG=77;

TRISD=0b00000000;   //RD7 to RD0 are connected to segment LEDs active high
INTCONbits.GIEH=1;  //enable global interrupt
INTCONbits.PEIE=1;  //enable peripheral interrupt

while(1)       //repeat
{

{

PORTD = 0b00000001; // turn on LED at _____
Delay10KTCYx (250); // for a while

PORTD = 0b00000010; // turn on LED at _____
Delay10KTCYx (250); // for a while

}

}

}
Moderators note: Please use code tags for pieces of code

Last edited by a moderator:

#### JohnInTX

Joined Jun 26, 2012
4,564
1) are you sure that the internal Fosc is 48MHz. The 18F4550 is a USB PIC and has a different clocking scheme than non USB PICs and requires some setup.
2) I get 78.1 for BRGH at Fosc = 48MHz.
3) There are no interrupts enabled or serviced in your code - so you'll still have problems with the delay.

First, I would get the clocking correct and verify by flashing an LED at 1sec intervals.
Then I would use a terminal at 9600 baud to receive ONE character, check it and light an LED to show that you got it correctly.

If you get that far, at least you know you have the clocking and baudrate set correctly.

Good luck.

#### hari95

Joined Jan 18, 2015
8
1) are you sure that the internal Fosc is 48MHz. The 18F4550 is a USB PIC and has a different clocking scheme than non USB PICs and requires some setup.
2) I get 78.1 for BRGH at Fosc = 48MHz.
3) There are no interrupts enabled or serviced in your code - so you'll still have problems with the delay.

First, I would get the clocking correct and verify by flashing an LED at 1sec intervals.
Then I would use a terminal at 9600 baud to receive ONE character, check it and light an LED to show that you got it correctly.

If you get that far, at least you know you have the clocking and baudrate set correctly.

Good luck.
The specifications recommend me to use 77 for BRGH for 9600 baud rate with 48Mhz Fosc.Isnt the interrupt enabled by using RCIF??I have even enabled the PIE register which is stated in the microchip appendix for interrupts,or do i need to add in more im not quite sure.I am able to receive a byte in the hyper terminal baud rate 9600 and once i insert card a byte is received.

#### JohnInTX

Joined Jun 26, 2012
4,564
The specifications recommend me to use 77 for BRGH for 9600 baud rate with 48Mhz Fosc
You are correct and I was off by 1. Maybe I need to change the batteries in the Lil Professor (or review the datasheet more carefully!) Sorry for the confusion.
Isnt the interrupt enabled by using RCIF??I have even enabled the PIE register which is stated in the microchip appendix for interrupts,or do i need to add in more im not quite sure.
No. You have to do several things. First, you must provide an interrupt service routine and locate it at the interrupt vector location. This routine should look something like this:
Code:
unsigned char character_in; // some place to put the char

#pragma interrupt _ISRHIGH
void _ISRHIGH(void){
// should never get here
}

#pragma interruptlow _ISR
void _ISR(void)  // Low prio - interrupt service routine
{
if (RCIE)  // if interrupt is enabled AND
if(RCIF){ // a character has been received
// here you should eventually check the status flags OERR, FERR etc.  On OERR, you have to reset the serial port to resume reception.
character_in = RCREG; // read the character from RCREG - this clears RCIF.
//process character_in - how ever you want to do it - for tests maybe copy it to TXREG to echo or LATD to see the pattern on the pins.
// alternately, you can echo the character back to Hyperterminal by:
TXREG = character_in;  // suitable for tests only, need to inspect flags etc.
}
}
1) fully configure the USART for receive and transmit - baud rate, number of bits etc.
2) read RCREG to clear RCIF then
3) IPEN = 1 to ensure priority interrupts.
4) IPR1=IPR2= 0 to make all interrupts low priority (for now)
5) RCIE = 1 to enable the receive IRQ
6) PEIE = 1 to enable low priority interrupts
7) GIE = 1 to enable global (hi priority) interupts.

When a character is received, RCIF will interrupt the processor and the ISR will read it into character_in. For now, you can copy it to a port for examination. Each char received will update the port.

I haven't messed with C18 for awhile so you'll have to consult the manual for the syntax to make an interrupt service routines. Note that if you use C18 library routines, you'll probably have to know what they expect as far as interrupt setup. In that case, you will need the 2 interrupt routines that you show.

EDIT: your code refers to a 'Bootloader Program'. If you are using something that has to live with a bootloader, you'll have to make the interrupt vectors and declarations what the bootloader requires. Setting up the interrupts will be similar but maybe not exactly the same.

Good luck.

Last edited:

#### hari95

Joined Jan 18, 2015
8
You are correct and I was off by 1. Maybe I need to change the batteries in the Lil Professor (or review the datasheet more carefully!) Sorry for the confusion.
No. You have to do several things. First, you must provide an interrupt service routine and locate it at the interrupt vector location. This routine should look something like this:
Code:
unsigned char character_in; // some place to put the char

#pragma interrupt _ISRHIGH
void _ISRHIGH(void){
// should never get here
}

#pragma interruptlow _ISR
void _ISR(void)  // Low prio - interrupt service routine
{
if (RCIE)  // if interrupt is enabled AND
if(RCIF){ // a character has been received
// here you should eventually check the status flags OERR, FERR etc.  On OERR, you have to reset the serial port to resume reception.
character_in = RCREG; // read the character from RCREG - this clears RCIF.
//process character_in - how ever you want to do it - for tests maybe copy it to TXREG to echo or LATD to see the pattern on the pins.
// alternately, you can echo the character back to Hyperterminal by:
TXREG = character_in;  // suitable for tests only, need to inspect flags etc.
}
}
1) fully configure the USART for receive and transmit - baud rate, number of bits etc.
2) read RCREG to clear RCIF then
3) IPEN = 1 to ensure priority interrupts.
4) IPR1=IPR2= 0 to make all interrupts low priority (for now)
5) RCIE = 1 to enable the receive IRQ
6) PEIE = 1 to enable low priority interrupts
7) GIE = 1 to enable global (hi priority) interupts.

When a character is received, RCIF will interrupt the processor and the ISR will read it into character_in. For now, you can copy it to a port for examination. Each char received will update the port.

I haven't messed with C18 for awhile so you'll have to consult the manual for the syntax to make an interrupt service routines. Note that if you use C18 library routines, you'll probably have to know what they expect as far as interrupt setup. In that case, you will need the 2 interrupt routines that you show.

EDIT: your code refers to a 'Bootloader Program'. If you are using something that has to live with a bootloader, you'll have to make the interrupt vectors and declarations what the bootloader requires. Setting up the interrupts will be similar but maybe not exactly the same.

Good luck.
I have tried the following code after implementing it.But still there is no improvement.I have done all that u had listed but still there is no change i have to manually remove the idc cable for the signal to show.the code below is to once the card is inserted,the led lights will light up simulanteously to show that the signak has been received by the microcontroller from the card reader.I feel that i might be doing something wrong as i am able to receive the signal but in the wrong way.Below is the code:

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

unsigned char data_in(void);
//unsigned char i;

unsigned char character_in; // some place to put the char

void _ISR(void); // interrupt service routine - LOW priority
void MOTOR_RD7_RD3();

//void ISR_PortC7_high();
// Include this when using Bootloader Program ================================
#pragma udata

extern void _startup (void); // See c018i.c in your C18 compiler dir
#pragma code _RESET_INTERRUPT_VECTOR = 0x000800
void _reset (void)
{
_asm goto _startup _endasm
}
#pragma code

#pragma code _HIGH_INTERRUPT_VECTOR = 0x000808
void _high_ISR (void)
{
// _asm
// goto ISR_PortC7_high
// _endasm
;
}

#pragma code _LOW_INTERRUPT_VECTOR = 0x000818
void _low_ISR (void)
{
_asm
goto _ISR
_endasm
}
#pragma code

#pragma code

// Ends here ===============================================================

// Program starts here:

void MOTOR_RD7_RD3(void)

{

PORTD = 0b00000001; // turn on LED at _____
Delay10KTCYx (250); // for a while

PORTD = 0b00000100; // turn on LED at _____
Delay10KTCYx (250); // for a while

}
void _ISR(void) // interrupt service routine - LOW priority
{
// if interrupt is enabled ANDb
if(PIR1bits.RCIF==1){ // a character has been received
RCSTAbits.OERR=0;// here you should eventually check the status flags OERR, FERR etc. On OERR, you have to reset the serial port to resume reception.
RCSTAbits.FERR=0;
MOTOR_RD7_RD3();
character_in = RCREG; // read the character from RCREG - this clears RCIF.
//process character_in - how ever you want to do it - for tests maybe copy it to TXREG to echo or LATD to see the pattern on the pins.
}

}

void main(void)
{
// =======================================
CMCON = 0x07;
// ========================================
TRISB=0b11110000; //RB3 to RB0 are connected DIG3 to DIG0 active high
TRISD=0b00000000; //RB5 is connected to a switch,RD7 to RD0 are connected to segment LEDs active high
//TRISCbits.TRISC6=1;
TRISCbits.TRISC7=1;

RCSTA=0x90;
TXSTA=0x00;
SPBRG=77;

//t=0;
//r=0;

RCONbits.IPEN=1;

PIE1bits.RCIE=1;

INTCONbits.PEIE=1;

INTCONbits.GIEH=1;

while(1) //repeat

{

_ISR();

}

PORTB = 0b00000001; //enable DIG3
//display 3

Delay10KTCYx(250); //LEDs on for a while
Delay10KTCYx(250);

}

#### JohnInTX

Joined Jun 26, 2012
4,564
I think you have a fundamental misunderstanding of what an interrupt is and how it works.
Code:
while(1) //repeat
{
_ISR();
}
You do not call the ISR - the interrupt feature of the processor does that. The program runs normally until RCIF is set by receiving the character. The processor AUTOMATICALLY calls the ISR routine to process the interrupt then resumes normal processing.

Questions:
Do you understand interrupts? If not we can explore them in more detail or try to do this without them. Which do you prefer?

Reading the card reader datasheet, it looks like in its default mode, it will send the card's data (many characters of data) from up to 3 tracks when the card is inserted - is that correct? If that is true, then you will either have to
1)use an interrupt-driven scheme to read the data stream or
2)poll RCIE in a tight loop for the first character to be received then poll RCIE repeatedly for the rest of the data and figure out when to stop reading. During waiting for the first character and reading the stream, you won't be able to do much of anything else (goodbye delays).

It looks like you are flailing around here (MOTOR?? that's new) Can you provide a schematic and flow chart of exactly what you want this to do? Without it, we really can't help much.

Finally - before posting your source, figure out how to use CODE tags and do it. Its a beating to try to decipher what's going on without them.

I don't mean to sound grumpy but we are not getting much traction here.

#### hari95

Joined Jan 18, 2015
8
I think you have a fundamental misunderstanding of what an interrupt is and how it works.
Code:
while(1) //repeat
{
_ISR();
}
You do not call the ISR - the interrupt feature of the processor does that. The program runs normally until RCIF is set by receiving the character. The processor AUTOMATICALLY calls the ISR routine to process the interrupt then resumes normal processing.

Questions:
Do you understand interrupts? If not we can explore them in more detail or try to do this without them. Which do you prefer?

Reading the card reader datasheet, it looks like in its default mode, it will send the card's data (many characters of data) from up to 3 tracks when the card is inserted - is that correct? If that is true, then you will either have to
1)use an interrupt-driven scheme to read the data stream or
2)poll RCIE in a tight loop for the first character to be received then poll RCIE repeatedly for the rest of the data and figure out when to stop reading. During waiting for the first character and reading the stream, you won't be able to do much of anything else (goodbye delays).

It looks like you are flailing around here (MOTOR?? that's new) Can you provide a schematic and flow chart of exactly what you want this to do? Without it, we really can't help much.

Finally - before posting your source, figure out how to use CODE tags and do it. Its a beating to try to decipher what's going on without them.

I don't mean to sound grumpy but we are not getting much traction here.
I am honestly not very familiar with interrupts other than the fact that it interrupts the microcontroller from a function to do another function.Anyways,taking your advice,i edited the code and took the ISR from the while loop and it works!I finally do not have to take the cable out for it to work! I would like to take this opportunity to thank all of you for the great help.

(P.S,I am actually currently doing a school project that requires us to come out with a product that can help improve technology.So i am assigned to a pic18f4550 microcontroller and i decided to do a eatery table and chair whereby once the magnetic card is inserted,the chairs(with the help of the motor)will pull out from the table electrically and the user is able to sit down and have his meal.After the user gets up from the chair and withdraws the card,the chairs will push back in to the table.Meanwhile,the number of available tables will be indicated on a 7-segment display similar to a car parking system.I had completed all of the segments except for this magnetic card reader.)

#### JohnInTX

Joined Jun 26, 2012
4,564
OK. Great!
I like your project - it has merit.
Keep us informed of your progress.

Good Luck!