PDA

View Full Version : Proximity Reader (Weigand) aka RFID



GoldStar
- 23rd May 2009, 05:41
Well after a good day of getting used to the basic I/O of some of the PICs I have I decided to tackle a RFID card reader project. The use of an oscilloscope was definitely a huge help in the process. It's taken me about 4 days to get the program to it's current state, really the only thing left to do is replace the "TODO" comments with actual code, but I'll leave that up to whoever actually uses the code. Note: LCD code was taken from pic16.com examples so I can't take credit for that; the original was written in C and I had to port it over to PBP

Here's the hardware:
QL200 Pic Development Board (ebay)
Mini Weigand 26 Card Reader (ebay)
Trine electric strike (16-24 volt model but works fine at 12VDC) (ebay)
PIC 16F8778A (came with the board)
LCD screen (not serial interfaced / connected to portD[0:7])
IRF520 -- interface between PIC output and strike, the strike needs at least 500mA to activate!
RA4 has a pullup resistor



'''''''''''''''''''''''''''''''
''' Set up I/O
'''''''''''''''''''''''''''''''
ADCON1 = %00000111
TRISA = %00000000
TRISD = %00000000
TRISC = %11111111
'''''''''''''''''''''''''''''''
''' Tell PBP we are running
''' at 20 MHz
'''''''''''''''''''''''''''''''
DEFINE osc 20
''''''''''''''''''''''''''
' Initialize EEPROM '
''''''''''''''''''''''''''
' 33971 33972 33973 other are 65535 i.e FF or uninitialized
DATA @0, 179, 132, 180, 132, 181, 132, 255, 255, 255, 255
DATA @10, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
DATA @20, 03 'Number of users stored in EEPROM
'''''''''''''''''''''''''''''''
''' LCD Screen I/O
'''''''''''''''''''''''''''''''
rs var PortA.1 'Register Select (Data/Command Register)
rw var PortA.2 'Register Read/Write
e var PortA.3 'LCD Enable
'DB0 - DB7 located at PortD
'''''''''''''''''''''''''''''''
''' Temporary variables
'''''''''''''''''''''''''''''''
x var byte
i var byte
'''''''''''''''''''''''''''''''
''' Most important variables
'''''''''''''''''''''''''''''''
CardRead var bit
GoodCard var bit
PgrmMode var bit
BitCount var byte
CardWordH var word 'Need two words for 26 bit card
CardWordL var word
Users var word[10] 'Word array of users containing only user codes
NumUsers var byte
SiteCode con 209
StrikeTime con 5000 'time in ms to unlock door
'''''''''''''''''''''''''''''''
''' Weigand reader I/O
'''''''''''''''''''''''''''''''
Data0 var PORTC.0
Data1 var PORTC.1
Strike var PORTA.5
Buzzer var PORTA.0
GreenLED var PORTA.4
PgrmSw var PORTC.2
'''''''''''''''''''''''''''''''
''' Initialize variables
''' Card Users and
''' I/O ports For first use
'''''''''''''''''''''''''''''''
InitUsers:
Read 20, NumUsers
if NumUsers = $FF then NumUsers = 0 'Just in case EEPROM has nothing.
For i = 0 to NumUsers*2 'Bytes are written reversed For easy reading ;-)
Read i, Users.lowbyte(i)
Next i
BitCount = 0
CardRead = 0
CardWordL = 0
CardWordH = 0
LOW strike
HIGH buzzer
HIGH GreenLed
'''''''''''''''''''''''''''''''
''' Set up LCD and splash screen
'''''''''''''''''''''''''''''''
GoSub LCD_init
GoSub write_splash
'''''''''''''''''''''''''''''''
''' Here is the main program
'''''''''''''''''''''''''''''''
loop:
If PgrmSW = 0 then
If CardRead = 1 Then
PORTD = %00000001 'clr screen
GoSub lcd_cmd
For i = 15 to 0 step -1 'This loop writes the Site Code to Line 1
x = 0
x = CardWordH.0(i)
x = x | %00110000 'Make x a character
GoSub lcd_data
Next i
PORTD = %11000000 'set the 2nd line display address
GoSub lcd_cmd
Pause 100
For i = 15 to 0 step -1 'This loop writes the User Code to Line 2
x = 0
x = CardWordL.0(i)
x = x | %00110000 'Make x a character
GoSub lcd_data
Next i
CardRead = 0
GoodCard = 0
For i = 0 to NumUsers - 1
If CardWordL = Users[i] and CardWordH = SiteCode Then
HIGH Strike 'Activate electric strike
LOW GreenLED 'Green LED activated on gnd
Pause StrikeTime
PORTD = %00000001 'clr screen
GoSub lcd_cmd
For i = 0 to 15
LOOKUP i, ["CARD ACCEPTED "], x
GoSub lcd_data
Next i
LOW Strike
HIGH GreenLED
GoodCard = 1
EndIf
Next i
If GoodCard = 0 then 'Invalid Card
For i = 0 to 2 'Beep 3 times
LOW Buzzer
Pause 100
HIGH Buzzer
Pause 100
Next i
EndIf
CardWordH = 0
CardWordL = 0
EndIf
GoSub ReadCard
Else
'Init Program Mode
While PgrmSw = 1: Wend
PgrmMode = 1
PORTD = %00000001 'clr screen
GoSub lcd_cmd
For i = 0 to 15
LOOKUP i, ["Program Mode "], x
GoSub lcd_data
Next i
While PgrmMode = 1
If PgrmSW = 1 then
While PgrmSw = 1: Wend
PgrmMode = 0
EndIf
'Main Program Mode Menu Here
Wend
'Exit Program Mode / UPDATE EEPROM
PORTD = %00000001 'clr screen after exiting program mode
GoSub lcd_cmd
EndIf
Goto loop

'For Program Mode:
'For i = 0 to 1 'Bytes are written reversed For easy reading later ;-)
'write j, CardWordL.lowbyte(i)
'j = j + 1
'Next i

'''''''''''''''''''''''''''''''
''' Card Reading subroutine
'''''''''''''''''''''''''''''''
ReadCard:
if Data0 = 1 and Data1 = 1 then Return
ReadLoop:
If Data0 = 0 Then
If BitCount > 9 Then ' Bits 0 - 9 go to CardWordH, 10-25 go to CardWordL
CardWordL = CardWordL << 1
CardWordL.0 = 0
Else
CardWordH = CardWordH << 1
CardWordH.0 = 0
EndIf
BitCount = BitCount + 1
While Data0 = 0: Wend
EndIf
If Data1 = 0 Then
If BitCount > 9 Then
CardWordL = CardWordL << 1
CardWordL.0 = 1
Else
CardWordH = CardWordH << 1
CardWordH.0 = 1
EndIf
BitCount = BitCount + 1
While Data1 = 0 : Wend
EndIf
If BitCount < 26 Then
'TODO: Make sure that we don't end up in an infinite loop here; utilize the i counter
i = i + 1
Goto ReadLoop
Else
BitCount = 0
'TODO: Instead of dropping off parity, check parity and if error clear CardRead and CardWordH/L
'Even Parity: LSB should be 0
'Odd Parity: LSB should be 1
CardWordL = CardWordL >> 1 'Drop odd parity
CardWordL.15 = CardWordH.0 'CardWordL contains full user code
CardWordH.9 = 0 'Drop even parity
CardWordH = CardWordH >> 1 'We copied this bit to CardWordL already so drop it
CardRead = 1
EndIf
Return
'''''''''''''''''''''''''''''''
''' LCD subroutines below
'''''''''''''''''''''''''''''''
LCD_init:
PORTD = %00000001 'clr screen
GoSub lcd_cmd
portd = %00111000 '8 bits 2 lines 5*7 mode.
GoSub lcd_cmd
portd = %00001101 'display on, cursor off, blink on.
GoSub lcd_cmd
portd = %00000110 'no shift, cursor move right.
GoSub lcd_cmd
portd = %10000000 'set ddram address?
GoSub lcd_cmd
return

write_splash:
PORTD = %00000001 'clr screen
GoSub lcd_cmd
For i = 0 to 15
LOOKUP i, ["RFID CONTROLLER "], x
GoSub lcd_data
Next i
PORTD = %11000000 'set the 2nd line display address
GoSub lcd_cmd
For i = 0 to 15
LOOKUP i, ["(C) TONY FULLER "], x
GoSub lcd_data
Next i
Pause 1000
return

lcd_data:
PORTD = x 'Set up PORTD[0:7] with correct outputs from temp variable
rs = 1 'Data
rw = 0 'Write mode
e = 0 'make enable port low
Pause 10
e = 1 'now create edge
return

lcd_cmd:
rs = 0 'Command
rw = 0 'Write mode
e = 0 'make enable port low
Pause 10
e = 1 'now create edge
return

'''''''''''''''''''''''''''''''
''' That's all folks
'''''''''''''''''''''''''''''''
end


One note about the EEPROM data is that the site code is not stored, it's in a separate constant. The stored data is actually backwards: The first entry is 33971 in hex that is 84B3 so in the EEPROM data it is stored as B3 84. It becomes easier to read that way in the for loop.