100200300400500600
  
 
 

Back to basics: PIC is not PC

Microprocessors are good, but don't forget about microcontrollers. Microprocessors are smart but they are flawed - they are too generic, too complex, running too complex code and hence unreliable. Microcontrollers are perfect implementation of KISS. So let's look back to low level - asm, mics, ICs, MCUs, etc.

Over my childhood I was addicted to electronics. Not the music, and not the gadgets. But to soldering, semiconductors, circuits... Then I met PC - and I jumped from hardware to software. Recently I bought Raspberry Pi - and remembered the roots.

The next step was arduino clone - that is self assembled arduino on top of ATmega328P. But that's too simple. I remember though before I jumped to soft - I was going to learn PIC. Finally the time has come. Arduino also proved itself handy - as a PIC programmer.

One note to the above excellent project of Rhys - if you deal with low TTL/CMOS logic it's hard (and dangerous) to be dealing with relatively high voltage of 13.5V. Looking into specs of the lowest grade PIC I bought (PICF629) it's explicitly stated that 13.5V is MAX Vpp. while MIN is Vdd+3.5V. That means - it's sufficient to have additional 3.5-4.5V source - eg. 3xAA - which shouldn't be a surprise for someone dealing with ics. Then just step-up Vpp input from Vdd instead of Gnd to the BJT emitter.

With this - we have PIC burning kit, next install gputils and gpsim - which will be your PIC IDE. And we're ready to flow. Next stupid listing at the bottom is kinda Hellow World1 for PIC - shift register control to move led light across the bar. That's right, PIC doesn't have nifty Arduino Wiring libs so here you code, not play.

The PIC of my choice is as I mentioned maybe the lowest model - at least it was the cheapest in the shop nearby - smth. about 1euro. And for the reason - it does not have any peripherals except timer and comparator. Not even USART. So everything is to be coded manually.

Anyway, once you have asm file you compile it, debug/simulate it, and burn it. First though you may want to ensure programmer is working and dump original config/calibration in case you'd need to erase it

$ ardpicprog -p /dev/ttyUSB0 -o dump.hex --skip-ones
/dev/ttyACM0
Ardpicprog version 0.1.2, Copyright (c) 2012 Southern Storm Pty Ltd.
Ardpicprog comes with ABSOLUTELY NO WARRANTY; for details
type `ardpicprog --warranty'.  This is free software,
and you are welcome to redistribute it under certain conditions;
type `ardpicprog --copying' for details.

Initializing programmer ...
Device pic12f629, program memory: 1024 words, data memory: 128 bytes.
Reading program memory,
reading data memory,
reading id words and fuses,
done.

$ gpasm picShiftOut.asm
$ gpsim picShiftOut.cod
$ ardpicprog -p /dev/ttyUSB0 -i picShiftOut.hex --burn -d pic12f629
/dev/ttyACM0
Ardpicprog version 0.1.2, Copyright (c) 2012 Southern Storm Pty Ltd.
Ardpicprog comes with ABSOLUTELY NO WARRANTY; for details
type `ardpicprog --warranty'.  This is free software,
and you are welcome to redistribute it under certain conditions;
type `ardpicprog --copying' for details.

Initializing programmer ...
Device pic12f629, program memory: 1024 words, data memory: 128 bytes.
Burning program memory, 61 locations,
burning data memory, 0 locations,
burning id words and fuses,Write to device failed

Oops... no idea why it happens but code is there, and it works. You may verify by making dump again and disassembling it with gpdasm.

ArdpicProgramming

This is how it runs. Here on the picture PIC is behind bigger green LED. It's disconnected from Atmega - Vdd red wire goes straight to rail instead of Atmega leg controlling Vdd. Also behind this red wire is BJT controlling Vpp - programming voltage of +5v of USB and 4.5V of 3xAA. Rightmost is 74HC164 SIPO shift register without latch, wired to led bar. Disregard LM35 thermistor I left here for another ADC probe of ATmega.

And finally the code. It's heavily commented to reflect my understanding of the PIC guts and why I coded it that way and not other. I've used this blank stub to begin with.

    list    p=p12F629, n=0, b=4, r=dec
    processor p12f629
    radix   dec   
    
    include "p12f629.inc"
    
    errorlevel      -302    ; Don't complain about BANK 1 Registers 

    __CONFIG    _MCLRE_OFF & _CP_OFF & _WDT_ON & _INTRC_OSC_NOCLKOUT
    ;__IDLOCS       b'00111110010000' ;ardprog still fails on fuses

    
            ; globals
                    ; bits on GPIO
pin7        equ     0       ;GP0  
pin6        equ     1       ;GP1
pin5        equ     2       ;GP2 and T0CkI
pin4        equ     3       ;GP3 input only
pin3        equ     4       ;GP4 
pin2        equ     5       ;GP5 

OCLK        equ GP2
ODAT        equ GP1
CBLOCK 0x20
    WCNT, WPRM, MCNT
ENDC

Start   org     0x00;program starts at location 000
        nop
        nop         ;NOPs to get past reset vector address
        nop
        nop
        nop

SetUp   bsf     STATUS, RP0 ;Bank 1
        movlw   0x38        ;This is taken from 0x3FF for this chip
        movwf   OSCCAL      ;Push INTOSC calibration data
        clrwdt              ;Reset watchdog timer >>> WdSleep
        movlw   b'10001110' ;Turn off T0CKI, prescale for WDT to 1:64
        movwf   OPTION_REG  ; that should result to ~1sec sleep time
        movlw   b'00001000' ;Set GP0,1,2,4,5, as output
        movwf   TRISIO      ;enable outputs
        ;movlw  b'00000001' ;Enable TMR1 INT >>> TmSleep
        bcf     STATUS, RP0 ;bank 0
        movlw   b'00000111' ;Set up W to turn off Comparator ports
        movwf   CMCON       ;must be placed in bank 0
        ;movlw  b'00001100' ;Timer1: no PS, 
        clrf    GPIO        ;Clear GPIO of junk
        clrwdt              ;And watchdog timer
        goto    MLReset 
        
    
ShftOut movlw   0x8         ;ShiftOut(WPRM) for 74HC164
        movwf   WCNT        ;Timing in 4MHz RC INTOSC (1MHz Tcy)
        ;bcf    STATUS, RP0 ;we dont really switch to bank1 anywhere around
        rrf     WPRM,F      ;Pre-fetch bit. Just to save time later
loop    btfss   STATUS,C    ;1us : If set skip to nop followed by HIGH
        goto    $+4         ;2us : Jump to data LOW
        nop                 ;3us : align data timings
        bsf     GPIO,ODAT   ;4us : Data is sensed on raised edge
        goto    $+4         ;5us :_/--\__/--\__/-- clocks levels
        bcf     GPIO,ODAT   ;4us :_____/-----\____  data levels
        nop                 ;5us : 0     1     0    data read
        nop                 ;6us : MIN Setup time Tsu is ~ 1/2 of period
        rrf     WPRM,F      ;7us : is time between DAT change and CLK rise
        bsf     GPIO,OCLK   ;8us : Hold time is just 5ns so clocks could be
        bcf     GPIO,OCLK   ;9us : /\____/\____/\__
        decfsz  WCNT,F      ;10us: Check loop condition
        goto    loop        ;11us: and repeat iteration
        return              ;Just in case - we have WRPM restored here
    ;We can sleep in power-down mode with simpe setup through
    ;Watchdog timer (18ms ~ 2s) For lower resolution and wider scale better
    ;use Timer1 with LPOSC/PS (32kHz-4kHz) ~ 30us-16s Timer1 is also more
    ; precise, WDT depends on external conditions like Vdd & Tenv
WdSleep clrwdt              ; WD time set by prescaler at setup hence fairly
        sleep               ; simple code: Clear WD, fall to sleep and return
        return              ; INTs are off, no need to check reason for waking
    ;The beauty of the timer with LP/ASYNC is that it runs independently of
    ;our actions. That is - once we started it, we can spend whatever time
    ;doing other preparations - it will still overflow and INT after Xus.
    ;So we can set, sleep, wake, and if it runs at eg. 4khz - full overflow
    ;will INT at exactly 1/4000*65536=16.384s
    ;You'll need some extra circuitry and 2 pins though - 
    ; 32kHz crystal, 2caps, pins2,3. I have only 20MHz crystals at the mmt.
TmSleep nop
        return
    
Main    movf    MCNT,W      ;Load current
        call    getArr      ; byte from arr to
        movwf   WPRM        ; shift parameter
        call    ShftOut     ;Serialize WPRM
        call    WdSleep     ;Wait
        decfsz  MCNT,F      ;Loop: dec cnt
        goto    Main        ;Loop: Iterate
MLReset movlw   ARRS        ;Loop: Reset
        movwf   MCNT        ; counter
        goto    Main        ;Loop: Start
ARRS    equ 0x8
getArr  addwf   PCL,F
        retlw   00000000b
        retlw   00000001b
        retlw   00000010b
        retlw   00000100b
        retlw   00001000b
        retlw   00010000b
        retlw   00100000b
        retlw   01000000b
        retlw   10000000b
endArr
    if ((getArr & 0xff00 ) != (endArr & 0xff00))
        error "Array may not cross page boundary"
    endif ; Idea found at http://members.shaw.ca/swstuff/lightbar.html
    end
    ; vim: ts=4 noet sts=4 ai:
Sat Nov 2 21:51:36 2013 Upd.: Sun Nov 3 16:18:53 2013
Tags:
 
 
© ruff 2011