title "Hardware shakeout program and software library prototype" subtitle "Hardware shakeout program and software library prototype" ; Building initial routine library while testing hardware. radix dec processor 16f628a include "p16f628a.inc" __CONFIG 0x3f62 ; HS oscillator errorlevel -302 constant XTAL= 18432000 ; multiple of 300 # BRGH= 1 constant H115200= 9 ; async bit rate constants constant H19200= 59 constant H9600= 119 constant H4800= 239 # BRGH= 0 constant L19200= 14 constant L9600= 29 constant L4800= 59 constant L2400= 119 constant L1200= 239 constant __DLY10uS= 11 ; 10uS loop constant constant __DLY100uS= 125 ; 100uS loop constant ; Model 01a IO constants. ; NOTE: These are for the unrevised board!!! ; Serial Data output, in common with all on-board peripherals. constant SD= 0 ; PORTA, RA0, SR data, ; Shift register outputs PORTA, PORTB, PORTC. constant SRA= 1 ; PORTA, RA1, SR A clock, constant SRB= 2 ; PORTA, RA2, SR B clock, constant SRC= 5 ; portB, PB5, SRC clock NOT PORTA! ; MAX515 DAC bits. constant DAC= 3 ; PORTA, RA3, DAC select, constant SCLK= 4 ; PORTA, RA4, DAC CLK, ; Onboard UART pins, and 01a-defined support pins. constant RXD= 1 ; portB, PB1, hardware UART RX, constant TXD= 2 ; portB, PB2, hardware UART TX, constant CTS= 7 ; portB, PB7, conventional, CTS, constant RTS= 6 ; portB, PB6, conventional, RTS, ; We reserve the 16 bytes at the top of the register section for ; the library routines, because they don't need a banksel. constant LIBMEM= h'70' ; start of library routine reserved constant LIBLEN= h'80' - LIBMEM ; it's length ; constant mS= h'70' ; constant mSlo= h'70' ; constant mShi= h'71' ; ISR two byte millisec timer, ; constant __ISRF= h'72' ; ISR flags ; constant switches= h'73' ; readsw() single switches, bit field ; constant swval= h'74' ; readsw() rotary switch, value ; constant __J= h'75' ; various ; ; constant __DC1= h'7c' ; dlynnnxS () counter, ; constant __DC2= h'7d' ; dlynnnxS () counter, ; constant __ISRW= h'7e' ; ISR save W, ; constant __ISRS= h'7f' ; ISR save STATUS, ; ------------------------------------------------------------------------- cblock LIBMEM mS:2, mSlo, mShi ; ISR two byte millisec timer, __ISRF:1 ; ISR flags, see below. switches:1 ; readsw() single switches, bit field swval:1 ; readsw() rotary switch, value __DC1:1 ; dlynnnxS () counter, __DC2:1 ; dlynnnxS () counter, __J:1 ; various __ISRW:1 ; ISR save W, __ISRS:1 ; ISR save STATUS, endc ; Constants for readsw(). constant SWPORT= SRC ; port that scans switches, PORTx constant SWPOLE= 6 ; switch pole, PBx constant SWMASK= 0 ; mask for rotary switch bits ; ISR code constants. See "PIC BRG calculator.sxc" for timer counts. constant __ISRPS= b'10000100' ; TMR0 prescale 32, Fosc/4 constant __ISRINC= 1 ; __ISRF bit: mS timer has changed, constant __ISR1MS= 144 ; __ISRF bit: TMR0 count for 1mS int, ; ; End of Model 01a constants. ; ============================================================ cblock h'20' pattern:1 lastSW:1 XDAC:2, XDAChi, XDAClo ; note Big Endian order YDAC:2, YDAChi, YDAClo endc ; ------------------------------------------------------------------------- org 0 goto __MAIN org 4 ; ISR not always used, but goto __ISR ; this overhead is minimal. ; ------------------------------------------------------------------------- ; Main entry point; clear the memory we reserve for the ; library routines, turn off some hardware. __MAIN: movlw LIBMEM & 255 ; clear lib routine memory movwf FSR movlw LIBLEN __m1 clrf INDF incf FSR, f addlw 255 btfss STATUS, Z goto __m1 banksel CMCON ; disable the analog movlw 7 ; comparator, which movwf CMCON ; frees up PBx bits. ; ------------------------------------------------------------------------- call SRinit ; ready SR outputs, call DACinit ; init DAC as necessary, movlw 32 ; 2400 baud call serinit clrf pattern clrf XDAChi clrf YDAChi clrf XDAClo clrf YDAClo bcf STATUS, RP1 bsf STATUS, RP0 movlw 0 movwf TRISB bcf STATUS, RP0 foo2: bsf PORTB, 7 bcf PORTB, 7 incf pattern, f movf pattern, w call incDAC movlw XDAC call outDAC ; output to DACs, movf pattern, w call outA ; output to PORTA, movf pattern, w call serout movlw 1 call dly10mS goto foo2 movlw 1 call readsw ; scan switches, movf switches, w ; if switches different xorwf lastSW, w ; than last time, btfsc STATUS, Z goto foo2 movf switches, w movwf lastSW movlw "S" call serout movlw "W" call serout movlw "=" call serout movf swval, w addlw "0" ; output switch #, call serout movlw 13 call serout movlw 10 call serout goto foo2 ; Generate a pretty pattern for LEDs. makepat: incf pattern, f movlw 1 call dly10mS return ; Increment the DACs. incDAC: incf XDAClo, f btfsc STATUS, Z incf XDAChi, f incf YDAClo, f btfsc STATUS, Z incf YDAChi, f return ; ------------------------------------------------------------------ ; ISR CODE: ; ; Timer value is from the PIC BRG spreadsheet. ; Timer0 module data is in the PIC datasheet, PDF page 47. ; ; Interrupt service routine code. By default the interrupt system ; is disabled. Calling ISRinit() starts the 1mS software timer, ; using Timer0 (TMR0). When TMR0 reaches terminal count, this ; code sets the __ISRINC flag, then increments the 16-bit timer 'mS' ; (mSlo, mShi). The normal thread code can use this flag to check ; that the two 8-bit halves of the mS timer are in phase (eg. the ; case where an interrupt occurs between accesses of the two halves). ISRinit: banksel INTCON clrf INTCON ; disable all interrupts, banksel OPTION_REG movlw __ISRPS ; set TMR0 prescaler, movwf OPTION_REG call __ISRTMR0 ; start TMR0 int, bsf INTCON, GIE ; global interrupt enable. return ;------------------------------------------------------------ ; ; This is the entry point for interrupts. Carefully save W and ; STATUS, then determine the source and dispatch to the right ; handler. __ISR: movwf __ISRW ; enter ISR, swapf STATUS, w ; enter ISR, movwf __ISRS ; Test each of the sources and dispatch. btfsc INTCON, T0IF ; if Timer0, call __ISRTMR0 ; call it, ; Return from interrupt, registers saved properly. __ISRRET: swapf __ISRS, w movwf STATUS swapf __ISRW, f swapf __ISRW, w bcf INTCON, T0IF retfie ; ;------------------------------------------------------------ ; Handle a Timer0 interrupt; increment the counter, set the ; 'incremented' flag, and re-set the timer to interrupt ; again. Is also used to init Timer0 in the first place. __ISRTMR0: incf mSlo, f ; increment the timer, btfsc STATUS, Z ; propagate carry to MS, incf mShi, F bsf __ISRF, __ISRINC ; timer changed, movlw __ISR1MS ; count for 1mS overflow, movwf TMR0 ; set down count, bcf INTCON, T0IF ; clear interrupt. return ; Delay W 10 uS intervals. This was hand calibrated on a ; 20 MHz HS oscillator. It measured precisely 2.5mS at ; W=250; 1.0mS at W=100; 101uS at W=10, and 11.8uS at ; W=1. dly10uS movwf __DC1 __dly1 movlw __DLY10uS __dly2 addlw 255 btfss STATUS, Z goto __dly2 nop nop nop decfsz __DC1, f goto __dly1 return ; Delay W 100 uS intervals. This was hand calibrated on a ; 20 MHz HS oscillator. It measured precisely 25mS at ; W=250; 10mS at W=100; 1.01mS at W=10; 100uS at W=1. dly100uS movwf __DC1 __dly3 movlw __DLY100uS __dly4 addlw 255 btfss STATUS, Z goto __dly4 decfsz __DC1, f goto __dly3 return ; Delay W 10mS intervals. dly10mS movwf __DC2 __dly5 movlw 100 call dly100uS decfsz __DC2, f goto __dly5 return ; Initialize the UART for asyncrhonous in and out ; and set the bit rate. This sets PB2 (TD) as output, ; PB1 (RXD) as input and sets the bit rate to the factor ; W. See PIC16F628a data sheet PDF page 73-75. serinit banksel TRISB ; bank bsf TRISB, RXD ; 1 RB1 is input, RD bcf TRISB, TXD ; 1 RB2 is output, TD bcf TXSTA, BRGH ; 1 set LOW B.R. range, movwf SPBRG ; 1 set bit rate divisor, bsf TXSTA, TXEN ; 1 Tx enable banksel RCSTA bsf RCSTA, SPEN ; 0 Rx enable, bsf RCSTA, CREN ; 0 continuous Rx, return ; Output W to the UART. serout banksel PIR1 __so1 btfss PIR1, TXIF ; Tx full goto __so1 ; wait... movwf TXREG ; output to transmitter. return ; Output W to PORTA's shift register. outA: movwf __J ; the word to output, movlw 8 oa1 bcf PORTA, SD ; set SR data input to 0 btfsc __J, 7 ; or 1 to match MSB, bsf PORTA, SD bsf PORTA, SRA ; pulse the clock, bcf PORTA, SRA rlf __J, f ; shift data left, addlw 255 btfss STATUS, Z goto oa1 return outB: movwf __J ; the word to output, movlw 8 ob1 bcf PORTA, SD ; set SR data input to 0 btfsc __J, 7 ; or 1 to match MSB, bsf PORTA, SD bsf PORTA, SRB ; pulse the clock, rlf __J, f ; shift data left, bcf PORTA, SRB addlw 255 btfss STATUS, Z goto ob1 return outC: movwf __J ; the word to output, movlw 8 oc1 bcf PORTA, SD ; set SR data input to 0 btfsc __J, 7 ; or 1 to match MSB, bsf PORTA, SD bsf PORTB, SRC ; pulse the clock, rlf __J, f ; shift data left, bcf PORTB, SRC addlw 255 btfss STATUS, Z goto oc1 return ; Ready the IO pins necessary to write to the SRs. ; SD, SRA, SRB and SRC all become outputs. SRinit: banksel TRISA bcf TRISA, SD bcf TRISA, SRA bcf TRISA, SRB bcf TRISB, SRC ; note not PORTA! banksel PORTA return ; ; Read up to 8 switches tied to SR C outputs (switch contacts) ; with the pole tied to pin7, and store the result in 'switches'. ; Switches should be debounced in hardware, there is a 1mS delay to ; allow the debounce RC to charge/discharge. This polls the switches ; once per call, assumes this routine is called repeatedly in loops. ; The results of the first call are garbage, since this routine ; relies on the previous call to have initialized the SR. Modifies J. ; ; __J is our loop counter and bit-position tester; we shift it ; left until it's 0. readsw movlw 1 ; seed loop ctr/switch value J movwf __J clrf switches ; assume none closed, bsf PORTB, SD ; clock a 1 into LSB 1st time, _raw1 bsf PORTB, SWPORT ; clock bit over, bcf PORTB, SWPORT bcf PORTB, SD ; shift in 0's ; This makes the set loop too slow, and we miss rotary encoder ; state changes! Now if it were interrupt driven... ; call _P1msec ; brief delay for C on sw line, btfsc PORTB, SWPOLE ; if switch is ON (pulls to gnd) goto _raw2 movf __J, w ; get test bit, iorwf switches, f ; set "switch" ON, _raw2 bcf STATUS, 0 ; clear carry before shift in rlf __J, f ; move 'em over, btfss STATUS, 0 ; until bit moves to carry, goto _raw1 ; repeat; ; ; Now turn the rotary switch bits into an index, eg. position number. ; For a 3-pos switch, values run 1, 2, 4; convert this to 0, 1, 2 ; by finding the first bit set. For failsafe, we shift in 1's from the left ; to ensure termination. ; movf switches, w ; get switches read, andlw SWMASK ; mask off our bits, movwf __J ; save for test, clrf swval ; set initial value, _raw3 btfsc __J, 0 ; if switch on, return ; clear WDT, return val. bsf __J, 7 ; (failsafe) rrf __J, f ; shift to next bit, incf swval, f ; next value, goto _raw3 ; keep looking. ; Initialize the DAC system. Since this should be done ; soon after __MAIN, the DAC X and Y values will be zero. DACinit: banksel TRISA bcf TRISA, SCLK ; SCLK is an output, bcf TRISA, DAC ; bcf TRISA, SD ; etc. banksel PORTA bcf PORTA, SCLK ; SCLK must be low when /CS transits bsf PORTA, DAC ; deassert /CS return ; Driver for Maxim MAX515 10-bit DACs, two daisy chained. ; ; Output the four bytes pointed to by W to the DACs. The bytes ; are assumed to be X msb, X lsb, Y msb, Y lsb. Refer to the MAX515 ; datasheet; each chip, wired in series, wants 16 bits total; four ; 0-fill bits, 10 data bits, two dummy LSBs, two trailing bits to ; round to 16. The dummy LSBs for X and the leading 0's for Y are ; output in one call. ; ; Modifies J, I. ; outDAC movwf FSR ; set pointer, bcf PORTA, DAC ; assert DAC /CS, bcf STATUS, IRP ; 9th address bit movf INDF, w ; X DAC hi, ; movf XDAChi, w ;FOO andlw b'00000011' ; we need at least 4 0's before MS bits, movwf __J ; X MSBs to J, upper bits 0's, rlf __J, f rlf __J, f ; now four 0's precede X MSBs, movlw 6 ; 4 fill + 2 data, from MS end, call _outJ ; output those, incf FSR, f movf INDF, w ; X DAC low, ; movf XDAClo, w ; FOO movwf __J movlw 8 call _outJ ; output 8 LSBs, ; The X DAC still needs two dummy LSBs; we'll output them ; when we issue Y MSBs. incf FSR, f ; point to Y hi, movf INDF, w ; Y DAC hi ; movf YDAChi, w ; FOO andlw b'00000011' ; enforce format, movwf __J ; Y MSBs to J, upper bits 0's, movlw 8 call _outJ ; output 2 dummy LSBs, 4 fill, 2 data, incf FSR, f movf INDF, w ; Y DAC low, ; movf YDAClo, w ; FOO movwf __J movlw 8 call _outJ ; output 8 Y LSBs, clrf __J movlw 2 ; now final 2 dummy LSBs. call _outJ bcf PORTA, SD ; just cleaner on the scope... bsf PORTA, DAC ; deassert /CS return ; ; Output (reg. W) bits of (J) to the DAC, starting ; with the MSB. ; Modifies I, J. ; _outJ bcf PORTA, SD ; data=0... btfsc __J, 7 ; if MSB true, bsf PORTA, SD ; ...data=1. bsf PORTA, SCLK bcf PORTA, SCLK ; clock it rlf __J, f ; (move MSB to carry to test) addlw 255 ; decrement W, btfss STATUS, Z goto _outJ return end