Back to homepage
PIC microcontrollers
Introduction
PIC microcontrollers are a well known series of microcontrollers from
Microchip.
Their good sides are low price, and a whole lot of stuff included on board (depending on model):
Flash, Eeprom, USART, timers, counters, A/D converters, PWM, etc.
Chip programmers are reasonably priced.
Test setup. Serial port wires connect to MAX232 chip. PIC 16F874A microcontroller is on the
right side of the protoboard.
Serial port was used to send read/write and test commands to PIC.
LEDs are used to indicate status of some microcontroller pins. PGM pin (36, PORTB 3)
should be grounded, in picture it is shown incorrectly connected to +5 V.
ICSP is not used :)
PIC bad sides are incredible variety of partially or fully incompatible models:
for example, PIC 16F874 and 16F874A have different ways for writing to Flash memory,
so your old code for Flash writing for 874 will not work on 874A.
Copying code between different models even in the same family (within 16F, within 18F)
will rarely work without major modifications.
Assembly language instructions take some time to adjust to.
There is only 35 of them on 16F series, and there is no Branch If Equal or similar instruction :)
Instead, intrepid programmer must use INCFSZ, DECFSZ, BTFSC, BTFSS combined with GOTOs.
Also, crazy bank switching for register access is a cause of
very much grief.
With all this, PICs are unmatched in price for a large quantity.
So if you plan on selling a huge number of devices with embedded microcontrollers,
PICs are an excellent choice.
With that out of the way, I will present some code segments to get you started (or unstuck ;/)
with/from PICs.
16F874A examples (they also work with 16F873A, 16F876A, 16F877A)
Reading and writing to EEPROM
EEPROM sizes on 16F devices range from zero to 256 bytes.
EEPROM will allow 1 million writes.
Reads and writes are performed on single byte at a time.
However, there must be a delay of 4 to 8 ms between subsequent writes,
which I discovered in the PIC 16F87xA manual after several days of despair :)
Eeprom reads don't have this delay requirement.
Below is the EEPROM read/write code that works.
Code was originally taken from Microchip AN851, but was heavily modifed to suit my needs.
Code is in subroutines for easy use in your programs.
You need to declare variables COUNTER2 and COUNTER2 in your code.
In my code it is declared as:
COUNTER2 equ H'25'
COUNTER3 equ H'26'
; Read EEPROM subroutine
ReadEE
clrf COUNTER3
movlw H'20' ; # of bytes, 32
movwf COUNTER3
movlw H'50' ; RAM address
movwf FSR
bsf STATUS,RP1 ; bank 2
bcf STATUS,RP0
movlw H'00' ; eeprom address
movwf EEADR
ReadEE1:
bsf STATUS,RP1 ; bank 3
bsf STATUS,RP0
clrf EECON1
bsf EECON1,RD ; Read the data
bsf STATUS,RP1 ; bank 2
bcf STATUS,RP0
movf EEDATA,W
movwf INDF
incf FSR,F
incf EEADR,F ; Adjust EEDATA pointer
bcf STATUS,RP1 ; bank 0
bcf STATUS,RP0
decfsz COUNTER3,F
goto ReadEE1 ; Not finished then repeat
bcf STATUS,RP1 ; bank 0
bcf STATUS,RP0
return
; Write EEPROM subroutine
WriteEE
bcf STATUS,RP1 ; bank 0
bcf STATUS,RP0
clrf COUNTER3
movlw H'20' ; # of bytes, 32
movwf COUNTER3
; test data
WriteEE0:
movlw H'50' ; RAM address
movwf FSR
movlw H'00'
movlw COUNTER2
WriteEE1:
clrw ; W=0
movf COUNTER2,W ; W=0+COUNTER2
addlw H'41' ; W=W+hex41
movwf INDF
incf FSR,F
incf COUNTER2,F
btfss COUNTER2,5 ; number of bytes, 32
goto WriteEE1
movlw H'50' ; RAM address
movwf FSR
bsf STATUS,RP1 ; bank 2
bcf STATUS,RP0
movlw H'00' ; eeprom address
movwf EEADR
WriteEE2:
bsf STATUS,RP1 ; bank 2
bcf STATUS,RP0
movf INDF,W
movwf EEDATA
incf FSR,F
call WriteWaitEEData ; Write data
bsf STATUS,RP1 ; bank 2 restore, after subr. call !
bcf STATUS,RP0
incf EEADR,F ; Adjust EEDATA pointer
bcf STATUS,RP1 ; bank 0
bcf STATUS,RP0
decfsz COUNTER3,F
goto WriteEE2 ; Not finished, then repeat
bcf STATUS,RP1 ; bank 0
bcf STATUS,RP0
return
WriteWaitEEData
bsf STATUS,RP1 ; bank 3
bsf STATUS,RP0
movlw b'00000100' ; Setup for EEData
movwf EECON1
call StartWrite ; Write and wait
bsf STATUS,RP1 ; bank 3
bsf STATUS,RP0
btfsc EECON1,WR
goto $ - 1
call Wait10ms ; between bytes write delay, recommended min. 8 ms.
bcf STATUS,RP1 ; bank 0
bcf STATUS,RP0
return
StartWrite
clrwdt
bsf STATUS,RP1 ; bank 3
bsf STATUS,RP0
movlw 0x55 ; Unlock
movwf EECON2
movlw 0xAA
movwf EECON2
bsf EECON1,WR ; Start the write
nop
nop
bcf STATUS,RP1 ; bank 0
bcf STATUS,RP0
return
Reading and writing to Flash program memory
Flash program memory is much more spacious than EEPROM, several kilobytes is typical.
However, only 100 000 write cycles are allowed.
Below is working code, which has been adapted from Microchip AN851.
First 32 RAM memory locations are pre-filled with ascending numbers.
Then those numbers are transferred to Flash memory buffers and written in blocks of
8 bytes=4 words=1 block.
Total of 4 blocks is written to Flash program memory.
One should keep in mind that only 14 bits of each word are implemented in PIC16 series,
so non-programmed Flash memory words will read as 3F FF.
When writing to upper word, only lower 6 bits will be written.
WriteFlash:
bcf STATUS,RP0 ; bank 0
bcf STATUS,RP1
clrf PORTB
bsf PORTB,5 ; debug light
bsf STATUS,RP1 ; bank 2
bcf STATUS,RP0
movlw H'07' ; Load initial Flash address MSB
movwf EEADRH
movlw H'00' ; Flash address LSB
movwf EEADR
bcf STATUS,RP0 ; bank 0
bcf STATUS,RP1
clrf COUNTER2
clrf COUNTER9
movlw H'04'
movwf COUNTER9 ; number of Flash blocks to transfer
; 1 block=4 words=8 bytes
WriteFlash0:
; fill 32 ram locations with ascending numbers
clrw ; W=0
movf COUNTER2,W ; W=0+COUNTER2
addlw H'50' ; W=W+hex50
movwf FSR ; ram h50
movwf INDF ; h50+cnt2 => ram h50+cnt2
incf FSR,F
incf COUNTER2,F
btfss COUNTER2,5 ; number of bytes, 32
goto WriteFlash0
; restore ram pointer to ram start
movlw H'50'
movwf FSR
WriteFlash1:
bsf STATUS,RP0 ; bank 3
bsf STATUS,RP1
movlw b'10000100'
movwf EECON1
bcf STATUS,RP0 ; bank 2
movlw b'11111100' ; force a boundary
andwf EEADR,F
movlw 0x04
movwf TEMP
WriteFlash2:
movf INDF,W
movwf EEDATA
incf FSR,F
movf INDF,W
movwf EEDATH
incf FSR,F
clrwdt
bsf STATUS,RP0 ; bank 3
bsf STATUS,RP1
movlw 0x55 ; unlock
movwf EECON2
movlw 0xAA
movwf EECON2
bsf EECON1,WR
nop
nop
bcf STATUS,RP0 ; bank 2
bsf STATUS,RP1
incf EEADR,F
btfsc STATUS,Z
incf EEADRH,F ; switch to new 256 byte block, if needed
decfsz TEMP,F
goto WriteFlash2
decfsz COUNTER9,F
goto WriteFlash1
bcf STATUS,RP0 ; bank 0
bcf STATUS,RP1
return
Back to homepage