So here I am, frittering away a Sunday afternoon, all because I was lured into Julien Illett’s Youtube Walk down PIC memory lane. The development board which is useful to play along with these dabblings is an EBAY special which comprises nothing more than a PIC12F675 and a few discrete components.
It looks like this…
So having connected it up to a PicKit and reverse engineered the simple circuit see previous post it seemed only right to turn it on and see what happens… After a false start due to not having the jumpers in place the board did indeed show some life by flashing LED1 and LED2 alternately (with POWer lit too).
So… let’s do some reverse engineering PIC12 style….
org 0x0000
start:
;
; Datasheet 9.2.5.1
; Calibrating the Internal Oscillator
; OSCCAL register is 0x10 in Bank 1
bsf STATUS, RP0 ;Bank 1
call OSCINFO ;Get the cal value
movwf OSCCAL ;Calibrate
bcf STATUS, RP0 ;Bank 0
GOTO main
org 0x3d8
main: CLRF STATUS
GOTO init
;
; Some kind of delay function?
;
L7: MOVWF 0x20
CLRF 0x22
L6: CLRF 0x21
L5: MOVF 0x20,W
SUBWF 0x21,W
BTFSC STATUS,C
GOTO L4
INCF 0x21,F
GOTO L5
L4: MOVLW 0xC8
INCF 0x22,F
SUBWF 0x22,W
BTFSC STATUS,C
RETURN
GOTO L6
init: MOVLW 0xFF ; All GPIO High (although currently Inputs)
MOVWF GPIO
MOVLW 0x07 ; Turn Comparator Mode to OFF
MOVWF CMCON
BSF STATUS,RP0 ; Bank 1
CLRF ANSEL ; Use pins as Digital
MOVLW 0x3C ; GPIO0 and GPIO1 Outputs
MOVWF TRISIO
MOVLW 0xFF ; Weak Pull-ups Enabled
MOVWF WPU
MOVLW 0x3C ; GPIO0 and GPIO1 Outputs. Again???
MOVWF TRISIO
loop: MOVLW 0xF4 ; 244?
BCF STATUS,RP0 ; Use Bank 0
BCF GPIO,0 ; Clear GPIO0 (ie LED1 ON)
BSF GPIO,1 ; Set GPIO1 (ie LED2 OFF)
CALL L7
MOVLW 0xF4
BSF GPIO,0 ; Set GPIO0 (ie LED OFF)
BCF GPIO,1 ; Clear GPIO1 (i.e LED ON)
CALL L7
GOTO loop
org 0x3ff
OSCINFO:
RETLW 0x2C
There are some interesting things in here…
Firstly, as the PIC starts at PC=0 it loads the OSCCAL value by doing a call to 0x3ff. The very last instruction is itself the OSCCAL value wrapped up in a RETLW instruction. (i.e. Return Literal). This value needs to be loaded explictly in PIC12F675 unlike for PIC12C5xx series which uses a really dirty trick to start the PC the last memory address, pick up the value and then wrap PC to 0 to start the main program. How cool is that for a hack!
Secondly, there appears to be a delay routine. This appears to be a collection of loops designed to fritter away real-time by going around in circles. Personally I don’t like such loops, it would be much better to use timers and interrupts to defer work and to get on with something more interesting in the meantime but here there isn’t anything else to do so it is a good solution. In practical terms you would probably just use an existing library routine for delays but hand crafted ones are common, in fact there is even an online generator to help produce such things PIC Delay Generator. Here is looks like you put a literal in W and CALL L7 for delay.
Thirdly, the PIC’s registers are set-up to allow I/O to LED1 and LED2 on GPIO0 and GPIO1 respectively. There appears to be a small bug? TRISO set up twice?
Finally, the main loop consists of LED1 ON (by going output low, causing the LED which is already connected to VDD via a resistor to glow) & LED2 OFF, a delay, then LED1 OFF & LED2 ON and a delay and then repeat forever.
The delay argument is 244 which is a funny number, and if the delay routine was 1ms then perhaps that was intended as 244ms!?!
Measurement of the flashing frequency seems to yield a period of around 698ms i.e. 350ms (ish) ON and OFF.
Incidentally the config bits programmed in when it arrived were 0x01B4, i.e. Internal Oscillator with No WDT and GPIO3, GPIO4 and GPIO5 useable for I/O. So that should give a clock frequency of 4MHz.
One instruction cycle consists of four oscillator periods; for an oscillator frequency of 4 MHz, this gives a normal instruction execution time of 1 µs.
It might be interesting to evaluate the instruction timing of that loop…
…maybe next time.
But before I go I converted the disassembly into MPASM for use in MPLABX…
#include "p12f675.inc"
; CONFIG
; __config 0x1B4
__CONFIG _FOSC_INTRCIO & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _BOREN_OFF & _CP_OFF & _CPD_OFF
RES_VECT CODE 0x0000 ; processor reset vector
banksel OSCCAL
CALL 0x03FF
MOVWF OSCCAL
CLRF STATUS
GOTO START
MAIN_PROG CODE 0x3D8 ; let linker place main program
START
CLRF STATUS
GOTO INIT
DELAY
MOVWF 0x20
CLRF 0x22
L1
CLRF 0x21
L2
MOVF 0x20, W
SUBWF 0x21, W
BTFSC STATUS, 0x0
GOTO L3
INCF 0x21, F
GOTO L2
L3
MOVLW 0xC8
INCF 0x22, F
SUBWF 0x22, W
BTFSC STATUS, 0x0
RETURN
GOTO L1
INIT
MOVLW 0xFF
MOVWF GPIO
MOVLW 0x7
MOVWF CMCON
banksel ANSEL
CLRF ANSEL
MOVLW 0x3C
MOVWF TRISIO
MOVLW 0xFF
MOVWF WPU
MOVLW 0x3C
MOVWF TRISIO
LOOP
MOVLW 0xF4
banksel GPIO
BCF GPIO, 0x0
BSF GPIO, 0x1
CALL DELAY
MOVLW 0xF4
BSF GPIO, 0x0
BCF GPIO, 0x1
CALL DELAY
GOTO LOOP
END
This produces identical code except for some extra instructions added by the linked.
These harmless RETLW 0x00 instructions (as described here http://www.microchip.com/forums/m10435.aspx) can be removed by a bit of linker script..
...
CODEPAGE NAME=.cinit START=0x4000 END=0x4fff PROTECTED
...
SECTION NAME=.cinit ROM=.cinit // Useless Initialization stuff
...
and now we have identical HEX to the original. Hooray!