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 22.214.171.124 ; 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!