This page describes the programming of the roaster.
The hardware is elsewhere, with separate pages for
electronic aspects and
mechanical and electrical aspects.
The physical electronics are discussed on the electronic aspects page.
The program was developed using the gnupic utilities project gputils to program in assembly. This and lots of other open-source stuff for pic development an be easily found for free at www.gnupic.org.
To physically program the device, I use a Microchip PICKit2 programmer (a genuine one, though I understand the chinese copies are just as good), driven by the Microchip official but unsupported command-line utility pk2cmd.
There's an opensource simulator gpsim, but it depends upon a gtk toolkit that's a bit of a pig to get compiled (at least, it was for me) so I've done a page of notes about compiling gtkextra and gpsim on opensuse 11.1. Actually, although it's a very impressive piece of work, I prefer to just burn the program to the hardware and see if it does what I want - I don't often use a simulator.
If you're still stuck with Microsoft, Microchip give away an assembler-based IDE (MPLAB) for windows platforms.
The program memory in the pic is in four pages, and jumps from one page to another are a multi-instruction process, so I try and minimise them. Consequently, the program code is laid out within the pages. Effectively, I have a number of major modes, and the whole of a mode resides within one page.
This is initialisations and utility functions (things like lcd control routines, routines to read and write the i2c bus.
This is 'idle mode', where it sits when doing nothing else.
There is also a 'settings mode' which has such utility functions as erasing all program memory on the eeproms and setting the system clock time.
This is the main number-crunching that controls everything while roasting
The display when running has elapsed time and current temperature on the top row. Below that is the current program step label, and the current target temperature. At far right (across both lines) is a bar-chart of current heat input, here just below 50% because the actual temperature is just above the target.
All this and more is also sent out the serial port (USB) every second,
which is what enables the graphs elsewhere on these pages to be generated.
This is the routines used to define and manipulate the defined roast profiles.
Does anyone want code? This is a recentish development version. The comments should describe current stage of development.
This is 4249 instructions, out of a theoretical capacity of 8192.
;; Ian's astounding digital coffee roaster system ;; $Id: roast.asm,v 1.71 2009/05/04 08:12:15 ian Exp ian $ #define banner "Roaster v0.22" ;; this must be 16 characters ;; "123456789ABCDEF^" ;; compiler confguration LIST n=0 ;no page breaks in .lst file ERRORLEVEL -302 ;suppress register not in bank 0 message ERRORLEVEL -306 ;suppress crossing page boundary message ;; Hardware requirements: ;; PIC 16F877A, 16 Mhz oscillator ;; see below for pin usage ;; Version History ;; 0.01-0.12 progressive development of initialisation routines ;; with associated test routines at intermediate stages: ;; lcd initialise, utility functions & banner ;; arithmetic functions, debounce, analogue input ;; i2c RTC functions, timer0 config, program eeprom ;; usart routines, test dynamic user characters ;; 0.13 add main block at program page 1 with button actions ;; 0.14 add program routines in program page 2 ;; 0.15 add settings mode and set time ;; 0.16 add erase all memory to settings mode ;; 0.17 program mode completed ;; 0.18 test mode inserted in place of running mode ;; if any program is run, enters test mode, in which the current ;; temperatures on ambient and both thermocouple inputs are displayed ;; 0.19 running mode partly completed, but pprog mode needs moving to ppage3 ;; 0.20 prog mode moved to ppage3 and runprog moved to ppage2 ;; this is first fully functional version ;; though does not have drum, fan, eject or cooling control ;; 0.21 add drum and element fan control ;; 0.22 add output of running info to serial port ;; Todo ;; TODO01 save last started program in RTC ram and default to this on startup ;; TODO02 make the display temperature average of two thermocouples ;; and multiple readings ; ;; TODO03 output info about program step to USART ;; prog memory usage ;; ppage0 initialise and library of utility routines ;; ppage1 idle mode, settings mode ;; ppage2 program running ;; ppage3 programming mode (define profiles) ;; Pin Usage ;; pin name pin no Use in roaster ;; OSC1/CLKI 13 clock ;; OSC2/CLKO 14 clock ;; MCLR/VPP 1 reset button on back panel ;; ;; Port A ;; RA0/AN0 2 cold junction temperature sensor ;; RA1/AN1 3 in-beans probe thermocouple ;; RA2/AN2 4 air inlet thermocouple ;; RA3/AN3 5 voltage reference ;; RA4/T0CKI 6 timer clock input at 256 Hz ;; RA5/AN4 7 front panel light ;; ;; Port B ;; RB0/INT 33 LCD E line ;; RB1 34 LCD R/W line ;; RB2 35 LCD RS line ;; RB3/PGM 36 rotary encoder direction - 1=clockwise, 0=anticlock ;; RB4 (ioc) 37 rotary encoder change - flips as encoder changes ;; RB5 (ioc) 38 front panel button near knob - buttonK ;; RB6 (ioc) 39 front panel button to right - buttonR ;; RB7 (ioc) 40 front panel button to left - buttonL ;; ;; Port C ;; RC0 15 ** reserved for future use - cooling? ** ;; RC1 16 Door open – mosfet small heatsink (has a LED in parallel) ;; RC2 17 mosfet w large heatsink (has a LED in parallel) - cooling ;; RC3/SCK 18 i2c clock ;; RC4/SDI 23 i2c data ;; RC5 24 piezo sounder ;; RC6/TX 25 USART TX ;; RC7/RX 26 USART RX ;; ;; Port D ;; RDx 19-22 & 27-30 LCD data lines bit x ;; ;; Port E ;; RE0 8 drive to SSR (has a LED in parallel) ;; RE1 9 top element heater fan control ;; RE2 10 rotisserie control ;; ;; others ;; VSS 12 & 31 ;; VDD 11 & 32 ;; operating instructions ;; ;; in standby mode: ;; knob changes program number ;; K -press-> run from currently displayed step ;; -hold-> go to program mode ;; L -press-> go to program 1 ;; -hold-> go to manual mode ;; R -press-> does nothing ;; -hold-> go to settings mode ;; ;; program mode ;; in general, L and R buttons either step between parameters on screen, ;; in which case the knob adjusts value, ;; or L and R select indicated options (in which case knob not functional) ;; K button advances to next screen ;; ;; manual mode [not currently coded] ;; ;; settings mode ;; answer questions to change major settings: ;; change syustem time & date ;; erase all program memory ;; in general, keys operate as for program mode ;; note that can only make one settings mode change, then are returned ;; to idle mode (re-enter settings mode to change another item) ;; program definition as stored on eeprom ;; ;; byte 0 not used ;; ;; bytes 1-63 are jump table for programs ;; specify a program step from which to start running ;; ;; bytes 64 onwards store program steps ;; first byte of each step is at 32*step number ;; (meaning valid step numbers are 2-255) ;; each step bytes: ;; 0 flags 0 = step is blank ;; 1 not in use ;; 2,3 duration of step in seconds (2=low, 3=high) ;; 4,5 target temperature at start in 10ths degree (4=low) ;; 6,7 target temperature at end in 10ths degree (6=low) ;; 8 equip flag bit 0 - rotisserie on ;; 1 - top element fan on ;; 4 - door open (ie eject) ;; 9 cooling fans setting (might be bitwise) ;; 10-13 not in use ;; 14 proportional band in degrees ;; 15 max heat proportion ;; 16 min heat proportion ;; 17-21 not in use ;; 22 number of previous step in program (0 if this is first) ;; 23 number of next step in program (0 if this is final) ;; 24-31 8 characters, name of this step ;; ############################# ;; set up the parameters ;; ############################# LIST P=PIC16F877A include__CONFIG _HS_OSC & _PWRTE_ON & _WDT_OFF & _CP_OFF & _BODEN_OFF & _LVP_OFF ;; ############################# ;; definitions of port use ;; ############################# ;; lcd control stuff lcd_data equ PORTD ; port with lcd data on (uses 8 bit wide) lcd_ctrl equ PORTB ; port with all lcd control lines on lcd_e equ 0 ; pin number for Enable lcd_rw equ 1 ; pin number for Read/Write lcd_rs equ 2 ; pin number for Regsister Select ;; ############################# ;; definitions of ram working locations ;; ############################# ;; context saves in locations reached from all 4 banks w_temp equ 0x070 status_temp equ 0x071 pclath_temp equ 0x072 ;; general temp space high byte and low byte lowb equ 0x20 highb equ 0x21 temp1 equ 0x22 temp2 equ 0x23 temp3 equ 0x24 temp4 equ 0x25 ;; bytes for bcd packed to nybbles bcdtempl equ 0x30 bcdtemph equ 0x31 bcdut equ 0x32 bcdht equ 0x33 bcdtt equ 0x34 ;; specific temporary stuff interrupt_temp equ 0x35 ; temporary value reserved for interrupt routine use delay_temp1 equ 0x36 ; used in delay routines delay_temp2 equ 0x37 ; used in delay routine lcd_temp equ 0x38 ; used for sending text from program memory to lcd deb_temp equ 0x39 ; used in debounce routine portbbuff equ 0x3A ;; values used regarding prog flow prognum equ 0x40 progstep equ 0x41 prevstep equ 0x42 newstep equ 0x43 nextstep equ 0x44 pmflags equ 0x45 ;; values used in definition and calculation of the running step el_secs equ 0x50 ; elapsed seconds el_minsl equ 0x51 ; minutes low byte el_minsh equ 0x52 ; minutes high byte or hours (depends on context) elt_l equ 0x53 ; elapsed this step elt_h equ 0x54 maxheat equ 0x55 ;max allowed heat for this step minheat equ 0x56 ;min limit of heater power (defined in program) heatpower equ 0x57 ; desired power setting heaton equ 0x58 ; counter in heater loop heatoff equ 0x59 ; counter in heater loop w_a equ 0x5A ;24 bit arithmentic register lowest byte w_b equ 0x5B w_c equ 0x5C w_d equ 0x5D w_e equ 0x5E ;; temperatures t0_l equ 0x60 ;t0 is the cold juction sensor t0_h equ 0x61 t1_l equ 0x62 ;t1 is first thermocouple t1_h equ 0x63 t2_l equ 0x64 ;t2 is second thermocouple t2_h equ 0x65 td_l equ 0x66 ;td is the temperature for display td_h equ 0x67 tt_l equ 0x68 ;tt is the target temperature at this time tt_h equ 0x69 ;; buffer for screen lcdbuf equ 0xA0 ; unspecified number of bytes used ; but no good reason to exceed 32 ;; i2c and usart stuff is generally in register bank 2 i2caddr equ 0x110 ; address of i2c device (7bit) + R/W bit i2chigh equ 0x111 ; high byte, normally address within device i2clow equ 0x112 ; low byte, normally address within device i2ccount equ 0x113 ; count of bytes to read i2ctemp equ 0x114 ; temporary byte usartaddr equ 0x118 ; address of next byte to send out usartcount equ 0x119 ; number of bytes (including next) to send i2cbuf equ 0x120 ; start of read / write buffer ram ;; ############################# ;; define macros for register banks and program banks ;; ############################# rbank0 MACRO BCF STATUS,RP0 ; clear bank select bits BCF STATUS,RP1 ENDM rbank1 MACRO BSF STATUS,RP0 BCF STATUS,RP1 ENDM rbank2 MACRO BCF STATUS,RP0 BSF STATUS,RP1 ENDM rbank3 MACRO BSF STATUS,RP0 BSF STATUS,RP1 ENDM rbank0to2 MACRO bsf STATUS,RP1 ENDM rbank2to0 MACRO bcf STATUS,RP1 ENDM ppage0 MACRO BCF PCLATH,3 BCF PCLATH,4 ENDM ppage1 MACRO BSF PCLATH,3 BCF PCLATH,4 ENDM ppage2 MACRO BCF PCLATH,3 BSF PCLATH,4 ENDM ppage3 MACRO BSF PCLATH,3 BSF PCLATH,4 ENDM ;; ############################# ;; some data label definitions ;; ############################ ;; these must be at 0x0700 and onwards - see lcdtext routine org 0x0700 ; encode some constant data strings near end of prog bank 0 banner_label da banner ; this is defined at top of file for ease of updating version code init_label da "initialising ..." ; used when initialising standby_label da "Run program " ; used in standby mode arrows ;; this is five spaces, arrow, four spaces, arrow, five spaces da " ",a' '*0x80+1," ",2*0x80+a' '," " yesnolabel da " yes",a' '*0x80+1," ",2*0x80+a' ',"no " onofflabel da " on",a' '*0x80+1," ",2*0x80+a' ',"off " dowlabels Sunlabel da "Sun " Monlabel da "Mon " Tuelabel da "Tue " Wedlabel da "Wed " Thulabel da "Thu " Frilabel da "Fri " Satlabel da "Sat " progmodeselect da "Edit program " progmode01a da "Edit step New " progmode01b da " ",a' '*0x80+1," ",d'2'*0x80+a' ',"step" progmode02 da "Add to curr prg?" progmode03 da "Set step duratn." progmode04 da "Set temperatures" progmode09 da "Proportionl band" progmode10 da "Power max & min%" progmode11 da "Set step name: " progmode12 da "Write to memory?" progmode13 da "More editing? " ;; These are six pairs of characters long ;; because they have yes/no or on/off after them on top row: progmode05 da "Drum rotate:" progmode06 da "Element fan:" progmode07 da "Open door: " progmode08 da "Cooling fan:" setmode01 da "Set clock? " setmode90 da "Erase all progs?" setmode91 da "Cancel erase? " setmode92 da "Wait, erasing..." ;; ############################# ;; start code ;; ############################# org 0x000 goto start ; ie on power up, start! org 0x004 ; i think this is the reset vector interrupt_vec ; do context save as per microchip data sheet movwf w_temp swapf STATUS,w clrf STATUS ; which forces bank0 movwf status_temp movf PCLATH,w movwf pclath_temp clrf PCLATH ; which forces ppage0 ;; check interrupts we are handling ;; and clear any for which we have not got the enable bit set ;; eg: btfss INTCON,RBIE ;; bcf INTCON,RBIF ;; now check for each interrupt we care about btfsc PIR1,TXIF ;is it tehg flag saying ready for next byte? goto usart_transmit ;if so, send it ;; could drop through list above to here & return ;; if so, it seems that something unexpected triggered interrupt interrupt_done ; recover context, as per microchip datasheet ;; note this resest rbank to whatever it was movf pclath_temp,w movwf PCLATH swapf status_temp,w movwf STATUS swapf w_temp,f swapf w_temp,w retfie ; back into the program & enable gie ;; ############################ ;; action interrupts ;; ############################ ;; action usart transmit interrupt ;; ############################### ;; process to use interrupt driven transmit: ;; check if transmissionn is current by testing if usartcount >0 ;; 1: load a buffer in register bank 2 or 3 with ascii code ;; 2: set register usartaddr with address of first byte ;; 3: set register usartcount with number of bytes to send ;; 4: execute 'bsf PIE1,TXIE', should start transmission running usart_transmit rbank2 ;where the serial port registers are movf usartaddr,w ; get address of next byte to send movwf FSR ; put it in indirect addressing pointer bsf STATUS,IRP ; point to banks 2&3 rbank0 movf INDF,w ; get the byte movwf TXREG ; put byte in transmit buffer, which will send it on its way rbank2 incf usartaddr,f ; point to next byte decf usartcount,f ; reduce count of bytes still to go rbank1 btfsc STATUS,Z ; check if we have sent all bytes bcf PIE1,TXIE ; if so, switch off the flag so we don't get more interrupts goto interrupt_done ; note that loading new data resets the flag automatically ;; ############################# ;; utility functions used elsewhere in code ;; ############################# ;; waitpress waits until a button has been pressed or the knob turned ;; if button, it then waits for button release ;; it returns with a value in portbbuff, where bits 7-3 reflect button & knob state ;; and bit 0 reflects if held for more than 1 second ;; note that this resets T0 to do the timing, ;; and really doesn't want interrupts running when called ;; otherwise all hell will break loose waitpress rbank0 clrf portbbuff ;; record start state of knob btfsc PORTB,4 ; bit 4 is the knob toggle channel bsf portbbuff,0 ;set the lsbit to match knob start state bcf INTCON,RBIF ;wipe any residual flag btfss INTCON,RBIF ;look at port b interrupt flag goto $-1 ;wait here until interrupt flag is set clrf TMR0 ; clear timer 0 bcf INTCON,T0IF ;clear overflow flag ;; if was knob, do not need the debounce and so on ;; (knob signal has separate processor doing debounce and so on) swapf PORTB,w ;get port b with swapped nybbles xorwf portbbuff,f ;xor current state with previous state btfss portbbuff,0 ;see if knob has chnaged goto waitisbutton ;if not, assume is buttons ;; if we get here, we have decided it was a knob change clrf portbbuff bsf portbbuff,4 ;set the knob indicator bit btfsc PORTB,3 ;check direction bsf portbbuff,3 ; set direction if required return waitisbutton call debounce ;wait for switches to be stable bcf INTCON,RBIF ;clear port b change flag movf PORTB,w ;get port b status andlw b'11100000' ;mask out all but buttons movwf portbbuff ;set that aside waitrelease btfsc INTCON,T0IF ;see if timer has overflowed bsf PORTA,5 ;switch on light movf PORTB,w ;get button state andlw b'11100000' ;mask for just buttons btfss STATUS,Z ;see if buttons all off goto waitrelease ;if not carry on waiting call debounce ;wait for buttons to stabilise after release bcf INTCON,RBIF ;mop up any residual bounce effects bcf PORTA,5 ;switch off light (whether set or not) btfsc INTCON,T0IF bsf portbbuff,0 ;set lsbit in portbbuff if > 1 sec return ;; delaywms delays by value in W register milliseconds ;; it assumes a 16MHz crystal / oscillator ;; requires ram locations delay_temp1 and delay_temp2 ;; note that it destroys the value in W delaywms movwf delay_temp1 ; 1 cycle delay_outer_loop movlw d'235' ; 1 cycles movwf delay_temp2 ; 1 cycles delay_inner_loop nop ; 1 cycle nop ; +1 = 2 nop ; +1 = 3 nop ; +1 = 4 nop ; +1 = 5 nop ; +1 = 6 nop ; +1 = 7 nop ; +1 = 8 nop ; +1 = 9 nop ; +1 = 10 nop ; +1 = 11 nop ; +1 = 12 nop ; +1 = 13 nop ; +1 = 14 nop ; +1 = 15 DECFSZ delay_temp2, F ; +1 = 16 GOTO delay_inner_loop ; +2 = 17 ; so inner loop takes 17*235 = 3995 cycles decfsz delay_temp1, F ; 1 cycle goto delay_outer_loop ; 2 cycles ; so outer takes 2 setup + 3995 inner + 3 outer = 4000 cycles delay_end return ; 1 cycle plus it took one extra to get out of outer loop ; so total delay = 4000 x W + 3 cycles ;; debounce monitors switches on the top three bits of port b ;; when called, it waits until they have remained unchanged ;; for eight sequential samples 1mS appart ;; note, button state in upper nybble, read counter in lower nybble debounce movf PORTB,w ; get the port b state andlw b'11100000' ; mask out the lower nybble movwf deb_temp ; set that aside deb_loop movlw 0x1 call delaywms ; wait one millisecond movf PORTB,w ; get portb state xorwf deb_temp,w ; so w now has what's different from first time we looked andlw b'11100000' ; limit our investigation to just the three switch bits btfss STATUS,Z ; check if the answer was zero (ie, no change) goto debounce ; if not, ie change occurred, start from scratch incf deb_temp,f ; otherwise we increment temp (ie, we count up in the low nybble) btfss deb_temp,3 ; if bit three is set, have been through the loop 8 times goto deb_loop ; if not yet, go round the loop return ; otherwise, consider the port b state to be stable ;; ################################# ;; lcd control routines ;; ################################# ;; lcd busy waits until the lcd is not busy lcdbusy rbank1 ; register bank 1 movlw 0xff ; all input movwf lcd_data ; ie, I am relying on tris being in same place but bank 1 rbank0 bcf lcd_ctrl,lcd_rs ; set busy flag mode bsf lcd_ctrl,lcd_rw ; set read mode bsf lcd_ctrl,lcd_e ; latch command state nop nop btfss lcd_data,7 ; if busy flag in bit 7 is set, lcd is busy so don't ... goto lcdnotbusy ; break out nop ; at this point a bit more delaying won't hurt bcf lcd_ctrl,lcd_e ; pull the enable line down goto lcdbusy ; loop round for another check lcdnotbusy bcf lcd_ctrl,lcd_e ; pull the enable line down rbank1 ; move to register bank 1 clrf lcd_data ; lcd data port back to output rbank0 ; back to register bank 0 return ; return to whatever called us ;; sends a command byte on w to the lcd as a command lcdsendcmd rbank0 movwf lcd_data call lcdbusy ; wait until it's ready for us bcf lcd_ctrl,lcd_rw ; rw low so writing bcf lcd_ctrl,lcd_rs ; rs low means command bsf lcd_ctrl,lcd_e ; latch command state nop nop bcf lcd_ctrl,lcd_e ; latch data nop return ;; sends a character byte on w to the lcd lcdsendchar rbank0 movwf lcd_data call lcdbusy ; wait until it's ready for us bcf lcd_ctrl,lcd_rw ; rw low so writing bsf lcd_ctrl,lcd_rs ; rs high means character not command bsf lcd_ctrl,lcd_e ; latch command state nop nop bcf lcd_ctrl,lcd_e ; latch data nop return ;; call with number of characters reqd on w ;; will send bytes from lcdbuf onwards to lcd lcdsendbuf rbank0 movwf lcd_temp movlw low(lcdbuf) movwf FSR bcf STATUS,IRP lcdsendbufloop movf INDF,w movwf lcd_data call lcdbusy ; wait until it's ready for us bcf lcd_ctrl,lcd_rw ; rw low so writing bsf lcd_ctrl,lcd_rs ; rs high means character not command bsf lcd_ctrl,lcd_e ; latch command state nop nop bcf lcd_ctrl,lcd_e ; latch data nop incf FSR,f ;point to next byte decfsz lcd_temp,f goto lcdsendbufloop ;; now finished return ;; clear screen and move curosr to start of first line ;; can also move to start of first line without clear screen lcdclear movlw 0x01 call lcdsendcmd lcdline1 movlw 0x02 call lcdsendcmd return ;; move cursor to start of second line lcdline2 movlw 0xc0 call lcdsendcmd return ;; an address should be in W, set cursor to that positin ;; 00-0F is first line, 40-4F is second line lcdaddress iorlw b'10000000' ;set first bit call lcdsendcmd return ;; send a block of text from program space to the LCD ;; text is packed 2-bytes to word, as by 'da' ;; located at 0x07xx where xx is indicated by fsr ;; w contains number of program words to read (ie, bytes/2) lcdtext rbank0 ; select register bank 0 movwf lcd_temp ; save how many reads to do rbank2 ; eeaddress control is in bank 2 movlw 0x07 ; high byte address movwf EEADRH ; high byte of eeprom address register movf FSR,w ; get low byte from fsr movwf EEADR ; set low byte of address lcdtextloop rbank3 ; register bank 3 bsf EECON1,EEPGD ; looking at program memory bsf EECON1,RD ; doing a read nop ; wait nop ; wait some more rbank2 ; back to bank 2 movf EEDATH,w ; get high byte of data rbank0 ; bank 0 is where my general storage space is movwf highb ; store it rbank2 ; bank 2 movf EEDATA,w ; get the low byte rbank0 ; bank 0 movwf lowb ; set it to ram too bcf STATUS,C ; clear the carry flag rlf lowb,f ; roll low byte up rlf highb,f ; roll high byte up, getting bit from low byte and so has ascii bcf STATUS,C ; clear carry so we roll in a 0 rrf lowb,f ; roll low byte back down so now we have ascii movf highb,w ; get the high ascii call lcdsendchar ; put it on display movf lowb,w ; get the low ascii call lcdsendchar ; put that on the display too rbank2 ; bank 2 incf EEADR,F ; so we get the next pair of characters rbank0 ; back to bank 0 decfsz lcd_temp,f ; decrement our counter and loop 'till we've done required reads goto lcdtextloop ; loop around return ; when we've finished ;; ####################### ;; numbers and arithmetic routines ;; ###################### ;; binary to bcd ;; unsigned 16 bit binary in FSR:FSR+1 converted to bcd in three bytes ;; byte in FSR is low byte ;; uses double dabble (see wikipedia) ;; trashes the temp1 register (so that can't be one of the input bytes) b2bcd rbank0 movf 0,w ; get the low byte movwf bcdtempl ; set it in our working space incf FSR,f movf 0,w ; get the high byte movwf bcdtemph ; set that into our working space movlw d'16' ; number of binary digits, so number of loops movwf temp1 ; now put it where we will count clrf bcdut ; initialise all bcd digits to zero clrf bcdht clrf bcdtt bcdloop bcdunits movf bcdut,w ; get two digits into w andlw 0x0f ; so now we have units in w sublw d'4' ; subtract w from 4 and put answer in w btfsc STATUS,C ; if carry is clear answer was -ve goto bcdtens ; otherwise consider next digit movlw d'3' ; if answer was -ve, add three addwf bcdut,f ; add it and put it back in f bcdtens swapf bcdut,w ; swapped nybbles into w andlw 0x0f ; now have just tesn digit in low nybble of w sublw d'4' ; subtract w from 4 and put answer in w btfsc STATUS,C ; if carry is clear answer was -ve goto bcdhundreds ; otherwise consider next digit movlw 0x30 ; if answer was -ve, add three to the bcd byte addwf bcdut,f ; add it and put it back in f bcdhundreds movf bcdht,w ; get two digits into w andlw 0x0f ; so now we have hundreds in w sublw d'4' ; subtract w from 4 and put answer in w btfsc STATUS,C ; if carry is clear answer was -ve goto bcdthousands ; otherwise consider next digit movlw d'3' ; if answer was -ve, add three addwf bcdht,f ; add it and put it back in f bcdthousands swapf bcdht,w ; swapped nybbles into w andlw 0x0f ; now have thousands digit in low nybble of w sublw d'4' ; subtract w from 4 and put answer in w btfsc STATUS,C ; if carry is clear answer was -ve goto bcdtenthous ; otherwise consider next digit movlw 0x30 ; if answer was -ve, add three to the bcd byte addwf bcdht,f ; add it and put it back in f bcdtenthous movf bcdtt,w ; get two digits into w andlw 0x0f ; so now we have ten-thousands in w sublw d'4' ; subtract w from 4 and put answer in w btfsc STATUS,C ; if carry is clear answer was -ve goto bcdshifts ; otherwise consider next digit movlw d'3' ; if answer was -ve, add three addwf bcdtt,f ; add it and put it back in f ;; note that there is no 6th digit becuase can't get there with 16 bit binary bcdshifts bcf STATUS,C ; clear carry flag to start rlf bcdtempl,f ; do series of roll left across the working space rlf bcdtemph,f rlf bcdut,f rlf bcdht,f rlf bcdtt,f ;; thats the end of the loop decfsz temp1,f ; after decrement, we have number of times more too loop goto bcdloop ; if that's nonzero, do the loop return ; otherwise we're finished ;; binary to hours minutes seconds ;; unsigned 16 bit binary in FSR:FSR+1 converted to bcd h:m:s in three bytes ;; byte in FSR is low byte ;; uses double dabble (see wikipedia) modified for base 6 in tens units ;; does this by saying if have 30 minutes, when double, we want 1 hr ;; so any tens of minutes >2 we add 0x50 before doubling ;; thus, 0x030 -add-> 0x080 -double-> 0x100 ;; trashes the temp register ;; seconds placed in bcdut, minutes in bcdht, hrs in bcdtt b2hms movf 0,w ; get the low byte movwf bcdtempl ; set it in our working space incf FSR,f movf 0,w ; get the high byte movwf bcdtemph ; set that into our working space movlw d'16' ; number of binary digits, so number of loops movwf temp1 ; now put it where we will count clrf bcdut ; initialise all bcd digits to zero clrf bcdht clrf bcdtt hmsloop hmsunits movf bcdut,w ; get two digits into w andlw 0x0f ; so now we have units of seconds in w sublw d'4' ; subtract w from 4 and put answer in w btfsc STATUS,C ; if carry is clear answer was -ve goto hmstens ; otherwise consider next digit movlw d'3' ; if answer was -ve, add three addwf bcdut,f ; add it and put it back in f hmstens swapf bcdut,w ; swapped nybbles into w andlw 0x0f ; now have just tens of seconds in low nybble of w sublw d'2' ; subtract w from 2 and put answer in w btfsc STATUS,C ; if carry is clear answer was -ve goto hmshundreds ; otherwise consider next digit movlw 0x50 ; if answer was -ve, add five to the tens addwf bcdut,f ; add it and put it back in f hmshundreds movf bcdht,w ; get two digits into w andlw 0x0f ; so now we have units of minutes in w sublw d'4' ; subtract w from 4 and put answer in w btfsc STATUS,C ; if carry is clear answer was -ve goto hmsthousands ; otherwise consider next digit movlw d'3' ; if answer was -ve, add three addwf bcdht,f ; add it and put it back in f hmsthousands swapf bcdht,w ; swapped nybbles into w andlw 0x0f ; now have tens of minutes in low nybble of w sublw d'2' ; subtract w from 4 and put answer in w btfsc STATUS,C ; if carry is clear answer was -ve goto hmstenthous ; otherwise consider next digit movlw 0x50 ; if answer was -ve, add three to the bcd byte addwf bcdht,f ; add it and put it back in f hmstenthous movf bcdtt,w ; get two digits into w andlw 0x0f ; so now we have units of hours sublw d'4' ; subtract w from 4 and put answer in w btfsc STATUS,C ; if carry is clear answer was -ve goto hmsshifts ; otherwise consider next digit movlw d'3' ; if answer was -ve, add three addwf bcdtt,f ; add it and put it back in f ;; note that there is no 6th digit becuase can't get there with 16 bit binary ;; - max is 18 hrs plus minutes so tens of hours never exceeds 1 hmsshifts bcf STATUS,C ; clear carry flag to start rlf bcdtempl,f ; do series of roll left across the working space rlf bcdtemph,f rlf bcdut,f rlf bcdht,f rlf bcdtt,f ;; thats the end of the loop decfsz temp1,f ; after decrement, we have number of times more too loop goto hmsloop ; if that's nonzero, do the loop return ; otherwise we're finished ;############################# ; i2c_rtc routine reads or writes the real-time-clock chip ; use general registers in bank 2: ; i2caddr only lsbit consulted to decide if read or write ; buffers reads & writes through GPRs 0x120 - 0x127 ; also note that this routine disables interrupts, so might need to re-enable gie ; ; to use: ; set i2caddr with r/w bit (0=write) ; if writing, load 0x0120 and onwards with bytes to transmit ; call routine i2c_rtc ; if reading (ie bit 0 of i2caddr was set), 0x0120 onwards contains read bytes ; re-enable gie if appropriate ;############################# i2c_rtc rbank0 ; register bank 0 movf INTCON,w ; get current interrupts flag clrf INTCON ; switch off all interrupts rbank2 movwf i2ctemp ; stash the safe intcon somewhere ;; we need to load the i2c registers with the data particular to the rtc chip movf i2caddr,w ; get address and r/w bit andlw b'00000001' ; mask out all but the r/w bit iorlw b'11010000' ; put in the address of the rtc chip movwf i2caddr ;put that back as the address to use movlw d'8' ;clock has 8 bytes of data movwf i2ccount ;into the byte counter rbank0 movlw low(i2cbuf) ; address of read/write multi-byte buffer bsf STATUS,IRP ; set this so fsr points at banks 2&3 movwf FSR ; into fsr register ;; generate start rbank1 bsf SSPCON2,SEN ; set start enable bit call i2c_idle ; load device target rbank0 ; bank 0 selected now movlw b'11010000' ; 1101000 address plus last bit=0 (ie write) movwf SSPBUF ; device address put into transmit buffer as a write call i2c_idle ; wait 'till transmit complete ; now, if chip was busy, it will not have acknowledged ;; actually, I don't think the RTC chip ever is busy, ;; but I had the code, so stick it in rbank1 ; so register bank 1 btfss SSPCON2,6 ; this is what the slave sent back goto i2c_rtc_address ; carry on with what we wanted bsf SSPCON2,PEN ; otherwise generate stop condition call i2c_idle ; wait for that to happen nop nop ; no good reason for this nop goto i2c_rtc ; try again i2c_rtc_address rbank0 clrf SSPBUF ;sspbuf set to zero call i2c_idle ;wait for that to go out ;; from here, the process is the same for rtc or for eeprom ;; (the difference being only in the addressing) ;; this is why we loaded the device address and byte count, ;; so that we can jump into the eeprom routines goto i2ceeprom_data ;############################# ; I2C eeprom read and write routines ; use general registers in bank 2: ; i2caddr i2c control byte (7 bit address of chip and R/W) ; i2chigh high byte of instruction to chip ; i2clow low byte of instruction to chip ; i2ccount number of bytes to be read / written ; buffers reads & writes through GPRs 0x0120 - whatever ; note there is no error checking, do not read or write more than 80 bytes ; otherwise you start overwriting other stuff ; also note that this routine disables GIE, so you might need to re-enable gie ; ; to use: ; set i2caddr with target address and r/w bit (0=write) ; set i2chigh and i2clow with address bytes ; set i2ccount with number of bytes to read or write ; if writing, load 0x0120 and onwards with bytes to transmit ; call routine i2ceeprom ; if reading (ie bit 0 of i2caddr was set), 0x0120 onwards contains read bytes ; re-enable gie if appropriate ;############################# i2ceeprom rbank0 ; register bank 0 movf INTCON,w ; get current interrupts flag rbank2 movwf i2ctemp ; stash that somewhere rbank0 clrf INTCON ; switch off all interrupts movlw low(i2cbuf) ; address of read/write multi-byte buffer bsf STATUS,IRP ; set this so fsr points at banks 2&3 movwf FSR ; into fsr register ; generate start rbank1 bsf SSPCON2,SEN ; set start enable bit call i2c_idle ; load device target rbank2 movf i2caddr,w ; get the device address / control byte rbank0 ; bank 0 selected now andlw b'11111110' ; force bit 7 low (ie, for a write) movwf SSPBUF ; device address put into transmit buffer as a write call i2c_idle ; wait 'till transmit complete ; now, if memory chip was busy, it will not have acknowledged rbank1 ; so register bank 1 btfss SSPCON2,6 ; this is what the slave sent back goto i2ceeprom_address ; carry on with what we wanted bsf SSPCON2,PEN ; otherwise generate stop condition call i2c_idle ; mwait for that to happen nop nop ;no good reason nop goto i2ceeprom ; try again - not ethat if a problem on the bus, we'll hang here i2ceeprom_address ; now load data - high byte of memory location rbank2 movf i2chigh,w rbank0 ; back to bank 0 movwf SSPBUF ; high byte into buffer call i2c_idle ; wait 'till byte sent ; now load 2nd byte data - low byte of memeory location rbank2 movf i2clow,w rbank0 movwf SSPBUF ; byte into buffer call i2c_idle ; wait till transmit is over i2ceeprom_data ; now we need to decide if we're reading or writing, and how much rbank2 btfss i2caddr,0 ; if this is high we are reading goto i2ceeprom_write ;otherwise jump forward to write routine i2ceeprom_read ; generate a repeated start to cancel the write rbank1 bsf SSPCON2,RSEN ; generate a restart call i2c_idle ; wait 'till complete ; load device target rbank2 movf i2caddr,w ; get the device address / control byte rbank0 movwf SSPBUF ; device address put into transmit buffer call i2c_idle ; wait 'till transmit complete ; now program toi receive i2ceeprom_rbyte rbank1 bsf SSPCON2,RCEN ; enable for receive call i2c_idle ; wait 'till complete (note, this switches to bank 0) movf SSPBUF,w ; get the data byte movwf INDF ; write into ram incf FSR,f ; move forward to the next byte ; now, if we want another byte, must acknowledge rbank2 decfsz i2ccount,f ; bytes to receive goto i2ceeprom_ack ; acknowledge & loop goto i2ceeprom_readdone ; generate a stop i2ceeprom_ack rbank1 bcf SSPCON2, ACKDT ; set ack bit state to 0 bsf SSPCON2, ACKEN ; generate a n acknowledge call i2c_idle ; wait 'till done goto i2ceeprom_rbyte ; loop around i2ceeprom_write rbank0 movf INDF,w ; get the byte movwf SSPBUF ; data byte into buffer call i2c_idle ; wait for transmit to finish incf FSR,f ; point to next byte for next write rbank2 decfsz i2ccount,f ; number of bytes still to read goto i2ceeprom_write ; loop around if there's more goto i2ceeprom_writedone ; otherwise generate a stop i2ceeprom_readdone ; generate no acknowledge then stop rbank1 bsf SSPCON2, ACKDT ; set ack bit state to 1 (ie, no acknowledge) bsf SSPCON2, ACKEN ; generate a not acknowledge call i2c_idle ; wait 'till done i2ceeprom_writedone rbank1 bsf SSPCON2,PEN ; generate stop condition call i2c_idle rbank2 movf i2ctemp,w ; get saved interrupts setup rbank0 movwf INTCON ; re-enable interrupts return ; note, will return with bank 0 selected ; wait until i2c module is idle i2c_idle rbank0 ; bank 0 bcf PIR1,SSPIF ; clear any old interrupt flag btfss PIR1,SSPIF ; check for flag going high goto $-1 ; by looping return ; return, since must now be idle ;; ##################### ;; powerbar routine ;; ##################### powerbar ;; this builds a vertical bar chart in lcd user defined characters 3 & 4 ;; 3 is upper character, 4 is lower rbank0 ;; we want 6 bits (64 states) so rolldown twice times rrf heatpower,w ;power rolled down once movwf temp1 rrf temp1,f ;power rolled right twice, but we didn't clean top bits movlw b'00111111' andwf temp1,w ;top bits clean sublw d'63' ;63-n gives number of white segments at top of chart ;; note that if power=0, will force to 64 white segments furtehr down movwf temp1 movlw b'01011000' ; 01=cgram, 011=character3, 000=first byte call lcdsendcmd movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'11110' call lcdsendchar movlw b'01011000' ; 01=cgram, 011=character3, 000=first byte call lcdsendcmd movf temp1,f ;look at value btfsc STATUS,Z ;is it zero? goto bar_done ;if so, solid bar is waht we want barchartbyte decfsz temp1,f goto bar_test_two goto bar_one_done bar_test_two decfsz temp1,f goto bar_test_three goto bar_two_done bar_test_three decfsz temp1,f goto bar_test_four goto bar_three_done bar_test_four decfsz temp1,f goto bar_four_loop bar_four_done movlw b'00000' call lcdsendchar goto bar_done bar_three_done movlw b'01000' call lcdsendchar goto bar_done bar_two_done movlw b'01100' call lcdsendchar goto bar_done bar_one_done movlw b'11100' call lcdsendchar goto bar_done bar_four_loop movlw b'00000' call lcdsendchar goto barchartbyte bar_done ;; just check if power was actually zero movf heatpower,f ;look at power setting btfss STATUS,Z ;was it zero? return ;if not, return movlw b'01100111' ;last byte of character 4 call lcdsendcmd movlw 0 call lcdsendchar return ;; #################### ;; main program operation starts here ;; #################### start ; we should get here very soon on power up initports rbank0 ; switch to register bank 0 clrf PORTA ; all ports cleared at initialisation clrf PORTB clrf PORTC clrf PORTD clrf PORTE clrf INTCON ; disable all interrupts rbank1 ; select register bank 1 for tris values ;; port a is almost all inputs (will be analogue & T0 clock in) movlw b'00011111' ; two unused, front panel light, rest inputs (analogue & T0CKI) movwf TRISA ; port a control register ;; port b is LCD control lines (outputs) and user controls bsf OPTION_REG, NOT_RBPU ; disable PORTB pull-ups movlw B'11111000' ; 3 buttons & 2 knob inputs, 3 LCD control outputs movwf TRISB ; set it into the control byte ;; port c is assorted mainly outputs ;; usart requires bits <7:6> to be set ;; <5>=out (sounder), <4:3> set for I2C, <2:0> are outputs (mosfets & front LED) movlw b'11011000' movwf TRISC ;; port d is entirely lcd display data, all set to outputs clrf TRISD ; TRISD all 0 so port d all ouputs ;; port e all outputs clrf TRISE ; set pins ; note that TRISE also controls parallel slave port config ; but they all default to 0 anyway, so this is OK ; adc setup - all off at this stage, but make port E digital ; power-up state is adcon0 all 0, so converter module off ;; note that we use porta,5 as a digital output, ;; but trisa will over-ride ADC settings, so that's OK movlw b'10000011' ; right justified, 4 analogue, one references, movwf ADCON1 ; initially disable all interrupts clrf PIE1 ; diable peripheral interrupts clrf PIE2 ; disable the rest of the peripheral interrupts clrf INTCON ; disable global interrupts bsf INTCON,PEIE ; peripherals interrupt enable, ; so can just set peripheral enables as required rbank0 ; return to register bank 0 ;; flash front panel light before lcd is available first_post bsf PORTA,5 ; heartbeat light on movlw d'70' call delaywms ; short pause bcf PORTA,5 ; light off movlw d'250' call delaywms ; longer pause 250ms initlcd ;; lcd is in 8 bit mode ;; initialisation is a bit cryptic, but is taken from data sheet ;; note, explicit power-on delay not needed because it's over 250 mS since power-on ;; the first three steps are explicit, because the busy line is not working ;; after that, can use the utility functions defined above clrf lcd_data ; all data starts zero bcf lcd_ctrl,lcd_e ; set control lines low bcf lcd_ctrl,lcd_rw ; set control lines low bcf lcd_ctrl,lcd_rs ; set control lines low initlcd_step1 movlw b'00110000' ; bsf lcd_ctrl,lcd_e ; latch control state nop nop nop bcf lcd_ctrl,lcd_e ; latch the data movlw d'5' call delaywms ; required is at least 4.1mS initlcd_step2 movlw b'00110000' ; bsf lcd_ctrl,lcd_e ; latch control state nop nop nop bcf lcd_ctrl,lcd_e ; latch the data movlw d'1' call delaywms ; required is at least 0.1mS initlcd_step3 movlw b'00110000' ; bsf lcd_ctrl,lcd_e ; latch control state nop nop nop bcf lcd_ctrl,lcd_e ; ltach the data movlw d'1' call delaywms ; not sure if is required, but it seems a good idea initlcd_step4 movlw b'00111000' ; 001, 8 bit, 2 line, 5x7, x, x call lcdsendcmd ; send to the lcd movlw b'00001000' ; 00001, display off, underline off, blink off call lcdsendcmd ; send to the lcd movlw b'00000001' ; clear display call lcdsendcmd ; send to the lcd movlw b'00000110' ; 000001, increment cursor, don't shift display call lcdsendcmd ; send to the lcd movlw b'00001100' ; 00001, display on, underline off, blink off call lcdsendcmd ; send to the lcd ;; should now be ready to do something ;; put up banner movlw low(banner_label) movwf FSR movlw 0x08 call lcdtext call lcdline2 movlw low(init_label) movwf FSR movlw 0x08 call lcdtext movlw b'01000000' ; should be command to first character in cgram call lcdsendcmd ; move to ram movlw b'00100' ;character 0 = vertical bar call lcdsendchar movlw b'00100' call lcdsendchar movlw b'00100' call lcdsendchar movlw b'00100' call lcdsendchar movlw b'00100' call lcdsendchar movlw b'00100' call lcdsendchar movlw b'00100' call lcdsendchar movlw b'00100' call lcdsendchar movlw b'00000' ; this is downwards arrow, left tail call lcdsendchar movlw b'00000' call lcdsendchar movlw b'11000' call lcdsendchar movlw b'00100' call lcdsendchar movlw b'00100' call lcdsendchar movlw b'10101' call lcdsendchar movlw b'01110' call lcdsendchar movlw b'00100' call lcdsendchar movlw b'00000' ; this is downwards arrow, right tail call lcdsendchar movlw b'00000' call lcdsendchar movlw b'00011' call lcdsendchar movlw b'00100' call lcdsendchar movlw b'00100' call lcdsendchar movlw b'10101' call lcdsendchar movlw b'01110' call lcdsendchar movlw b'00100' call lcdsendchar ;; characters 3 & 4 are power bar chart, but set inoitial to chequer movlw b'01010' call lcdsendchar movlw b'10101' call lcdsendchar movlw b'01010' call lcdsendchar movlw b'10101' call lcdsendchar movlw b'01010' call lcdsendchar movlw b'10101' call lcdsendchar movlw b'01010' call lcdsendchar movlw b'10101' call lcdsendchar movlw b'01010' call lcdsendchar movlw b'10101' call lcdsendchar movlw b'01010' call lcdsendchar movlw b'10101' call lcdsendchar movlw b'01010' call lcdsendchar movlw b'10101' call lcdsendchar movlw b'01010' call lcdsendchar movlw b'10101' call lcdsendchar ;; set up mssp for i2c comms to access external eeprom & rtc chip initi2c movlw b'00101000' ; x, x, i2c enabled, x, 12c master with manual clock movwf SSPCON ; set to register rbank1 ; select bank 1 movlw d'39' ; 16MHz clock, 39=100kHz bus (9=400kHz but RTC not work) movwf SSPADD ; set clock multiplier movlw b'10000000' ; slew rate off, i2c spec levels, rest are results movwf SSPSTAT ; set rate and levels rbank0 ;; this is probably not needed, but I somehow feel ;; that letting the bus settle is a good idea movlw d'5' call delaywms ;; now initialise the clock chip rbank2 bsf i2caddr,0 ;set lsbit so say reading call i2c_rtc ;get existing clock data ;; note: ;; to hard-code a default time, set it into i2cbuf here ;; adjust read time data to ensure chip runs as required rbank2 bcf i2cbuf,7 ;clear the bit clock halt bit ;; check if already in 24 hour mode btfss i2cbuf+2,6 ;see if currently in 24 hour mode goto rtccontrolbyte ;if so jump forwards btfss i2cbuf+2,5 ;test if it is am goto rtcset24 ; if so, just change to 24 hour mode movlw b'00011111' ; mask for all but the hours andwf i2cbuf+2,w ; get that into w btfss i2cbuf+2,3 ;test if hours were 8 or 9 goto rtcadd12 ; if not, simply ad 12 in bcd andlw 1 ;mask all but lsb addlw 0x20 ;add 20 in bcd movwf i2cbuf+2 ;put that in register goto rtcset24 ;then change to 24 hour mode rtcadd12 addlw 0x12 ;add 12 hours movwf i2cbuf+2 ;write that back into clock register rtcset24 bcf i2cbuf+2,6 ;clear the 12/24 hour bit to set 24 hr mode rtccontrolbyte movlw b'10010011' movwf i2cbuf+7 ;set oscillator output at 32768Hz ;; write modified values back to chip bcf i2caddr,0 ;clear lsbit so that we write call i2c_rtc ;write that settings into the clock chip ;; set up usart for broadcast of running commentary ;; note that does not read anything back from usart ;; so receive enable bit is not set initusart rbank1 movlw b'00100100' ;x, 8-bit, transmit enabled, asynch, x, high speed, x,x movwf TXSTA ; configure transmit movlw d'16' ; for baud rate 57.6k movwf SPBRG ; set baud rate rbank0 movlw b'10000000' ; enabled, 8-bit, x, receive off, no address, x,x,x movwf RCSTA ; configure general & receive ; should now be configured ;; as for i2c bus this is probably not needed, but do it anyway movlw d'5' call delaywms ;; now greet the world ;; note that this is not interrupt driven - blocks until complete ; send out two CR-LF pairs to wake up whatever's on the serial port movlw d'13' ; carriage return movwf TXREG ; out serial port nop btfss PIR1,TXIF ; wait 'till it's gone goto $-1 movlw d'10' ; line feed movwf TXREG ; out serial port nop btfss PIR1,TXIF ; wait 'till it's gone goto $-1 movlw d'13' ; carriage return movwf TXREG ; out serial port nop btfss PIR1,TXIF ; wait 'till it's gone goto $-1 movlw d'10' ; line feed movwf TXREG ; out serial port nop btfss PIR1,TXIF ; wait 'till it's gone goto $-1 ;; now talk to the world - just announce 'Roaster' movlw a'R' movwf TXREG nop btfss PIR1,TXIF goto $-1 movlw a'o' movwf TXREG nop btfss PIR1,TXIF goto $-1 movlw a'a' movwf TXREG nop btfss PIR1,TXIF goto $-1 movlw a's' movwf TXREG nop btfss PIR1,TXIF goto $-1 movlw a't' movwf TXREG nop btfss PIR1,TXIF goto $-1 movlw a'e' movwf TXREG nop btfss PIR1,TXIF goto $-1 movlw a'r' movwf TXREG nop btfss PIR1,TXIF goto $-1 movlw d'13' ; carriage retuirn movwf TXREG ; out serial port nop btfss PIR1,TXIF ; wait 'till it's gone goto $-1 movlw d'10' ; line feed movwf TXREG ; out serial port nop btfss PIR1,TXIF ; wait 'till it's gone goto $-1 ;; initialise timer 0 ;; clock chip should be providing 256Hz signal ;; (actually, clock chip provides 32768Hz and separate divider drops it to 256Hz) initt0 rbank0 clrf TMR0 ; start with zeroed timer rbank1 bsf OPTION_REG,PSA ; prescaler set to wdt, not timer bcf OPTION_REG,T0SE ; increment on low-to-high bsf OPTION_REG,T0CS ; use t0cki pin to trigger rbank0 second_post movlw d'250' call delaywms ; longer pause 250ms bsf PORTA,5 ; heartbeat light on movlw d'70' call delaywms ; short pause bcf PORTA,5 ; light off movlw d'70' call delaywms ; short pause bsf PORTA,5 ; light on movlw d'70' call delaywms ; short pause bcf PORTA,5 movlw d'70' call delaywms ; short pause ;; at this point everything is initialised ;; so jump to main program block ppage1 goto idlemodestart ;; ####################### ;; program page 1 ;; ####################### org 0x0800 idlemodestart rbank0 ;this already set, but just to be sure... ;; switch off buttons and timer 0 interrupt bcf INTCON,RBIE bcf INTCON,T0IE ;; switch off all heat and all fans clrf PORTE ; SSR, top element fan, rotisserie off bcf PORTC,1 ; door eject off bcf PORTC,2 ; cooling fans off bcf PORTA,5 ;front panel light off movlw 1 ;start at step 1 TODO01 movwf prognum standbyloop rbank0 ;; find what step selected prognum matches movf prognum,w rbank2 movwf i2clow clrf i2chigh movlw 1 movwf i2ccount movlw b'10100001' ; memory, chip 0, read movwf i2caddr ; control byte set ppage0 call i2ceeprom ; get the byte from the eeprom ;; get starting step for current program from lookup rbank2 movf low(i2cbuf),w ;offset to get to i2cbuf rbank0 movwf progstep ;put it into program step register ;; now set up to read that step swapf progstep,w ; swap nybbles of step into w andlw b'00001111' ; and mask out upper nybble, so w now has high nybble of progstep rbank2 ; select register bank 2 movwf i2chigh ; and put that in high byte rbank0 ; back to bank 0 swapf progstep,w ; swap nybbles of step into w andlw b'11110000' ; and mask out lower nybble, so w now has (low nybble of progstep)*16 rbank2 ; select register bank 2 movwf i2clow ; and put that in low byte ;i2chigh:i2clow now contain step*16 bcf STATUS,C ; clear carry rlf i2clow,f ; low*2 rlf i2chigh,f ; high*2 so bytes have proper address movlw b'10100001' ; memory, chip 0, read movwf i2caddr ; control byte set movlw d'32' movwf i2ccount call i2ceeprom ; do the read rbank0 ;; top line of display is just the 'run from' label call lcdclear bcf STATUS,IRP movlw low(standby_label) movwf FSR movlw 8 call lcdtext ;; second line of display call lcdline2 ;; display program number clrf highb movf prognum,w movwf lowb movlw lowb movwf FSR bcf STATUS,IRP call b2bcd ;convert counter to bcd ;; put that on screen swapf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for tens digit call lcdsendchar ; send it movf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for units digit call lcdsendchar ; send it movlw a'-' call lcdsendchar ;; display program step clrf highb movf progstep,w movwf lowb ;; convert to bcd movlw lowb movwf FSR bcf STATUS,IRP call b2bcd ;convert counter to bcd ;; put that on display movf bcdht,w andlw 0x0f addlw a'0' ; so w now has ascii code for hundreds digit call lcdsendchar ; send it swapf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for tens digit call lcdsendchar ; send it movf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for units digit call lcdsendchar ; send it movlw a':' call lcdsendchar movlw a' ' call lcdsendchar ;; display step name movlw low(i2cbuf+d'24') movwf FSR bsf STATUS,IRP movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar bcf STATUS,IRP standbywait ppage0 ;we need this because sometimes do a goto to standbywait ;; now we need to wait for a button to be pressed call waitpress ;; so now portbbuff has button status and if bit0 is set, button was pressed > 1 sec ;; decide what we should do about it ppage1 btfsc portbbuff,0 goto standbylongpress standbyshortpress btfsc portbbuff,4 ;indicates knob changed goto standbyknob btfsc portbbuff,5 ;button by knob runs the current program goto runmode btfsc portbbuff,6 ;right button does nothing goto standbywait btfsc portbbuff,7 ;left button jumps to prog1 goto standbystep1 goto standbywait standbylongpress btfsc portbbuff,5 ;button by knob engages program mode goto standbygotoprogmode btfsc portbbuff,6 ;right button goes to settings mode goto settingsmode btfsc portbbuff,7 ;left button goes to manualmode goto standbygotomanual goto standbywait runmode ppage2 goto runprog standbyknob btfss portbbuff,3 goto standbyminus standbyplus incf prognum,f ;add one to program number btfss prognum,6 ;check if that gets us to 64 goto standbyloop standbystep1 ;long press on left button also gets here movlw 1 ;if we got to 64 loop to 1 movwf prognum goto standbyloop standbyminus decfsz prognum,f ;subtract one and check if got to zero goto standbyloop movlw d'63' ;if we got to zero, loop round to 63 movwf prognum goto standbyloop standbygotoprogmode ppage3 ;program mode is in program page 2 goto progmodestart ; go there standbygotomanual ;currently does nothing goto standbyloop settingsmode ;; this does such things as set the time and erase all memory set01 ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(setmode01) ; asks about setting clock movwf FSR movlw 8 call lcdtext set01row2 ppage0 call lcdline2 movlw low(yesnolabel) ; movwf FSR movlw 8 call lcdtext set01wait ;; now check button-presses ppage0 call waitpress ; also waits for knob change ;; now process key press ppage1 btfsc portbbuff,6 ;right button adances to next question goto set02 btfsc portbbuff,7 ;left button is 'yes' - set clock goto set01gettime ; goto set01wait ; if something else, simply ignore keypress set01gettime ;; first get the current time ppage0 rbank2 bsf i2caddr,0 ;set ls bit so we read time call i2c_rtc rbank0 clrf pmflags set01showtime ;; put that on screen ppage0 call lcdclear movlw b'00001100' ; 00001, display on, underline off, blink off call lcdsendcmd ; send to the lcd movlw a' ' call lcdsendchar movlw a' ' call lcdsendchar ;; day name rbank2 bcf STATUS,C rlf low(i2cbuf+3),w ;so w has day of week * 2 addlw low(dowlabels-2) ;these are in order, so now pointing to right one movwf FSR movlw 2 ;two pairs of characters call lcdtext ;onto screen ;; date rbank2 swapf low(i2cbuf+4),w andlw 0x0f addlw a'0' call lcdsendchar rbank2 movf low(i2cbuf+4),w andlw 0x0f addlw a'0' call lcdsendchar movlw a'/' call lcdsendchar rbank2 swapf low(i2cbuf+5),w andlw 0x0f addlw a'0' call lcdsendchar rbank2 movf low(i2cbuf+5),w andlw 0x0f addlw a'0' call lcdsendchar movlw a'/' call lcdsendchar rbank2 swapf low(i2cbuf+6),w andlw 0x0f addlw a'0' call lcdsendchar rbank2 movf low(i2cbuf+6),w andlw 0x0f addlw a'0' call lcdsendchar ;; second line call lcdline2 movlw a' ' call lcdsendchar movlw a' ' call lcdsendchar movlw a' ' call lcdsendchar movlw a' ' call lcdsendchar ;; time rbank2 swapf low(i2cbuf+2),w andlw 0x0f addlw a'0' call lcdsendchar rbank2 movf low(i2cbuf+2),w andlw 0x0f addlw a'0' call lcdsendchar movlw a':' call lcdsendchar rbank2 swapf low(i2cbuf+1),w andlw 0x0f addlw a'0' call lcdsendchar rbank2 movf low(i2cbuf+1),w andlw 0x0f addlw a'0' call lcdsendchar movlw a':' call lcdsendchar rbank2 swapf low(i2cbuf),w andlw 0x0f addlw a'0' call lcdsendchar rbank2 movf low(i2cbuf),w andlw 0x0f addlw a'0' call lcdsendchar ;; now need to show cursor movlw b'00001110' ; 00001, display on, underline on, blink off call lcdsendcmd ; send to the lcd ;; computed lookup table coming up ;; if we are too close to a rollover of pcl put in an org ;; allow space for 20 instructions to end of lookup table if (low($)>d'235') ; org ((high($)+1)*0x100) endif ppage1 movlw high(set01table) movwf PCLATH movf pmflags,w ; so w has flags value addwf PCL,f ; add that into prog counter - gives computed goto set01table goto set01seconds goto set01minutes goto set01hours set01seconds movlw 0x4B ppage0 call lcdaddress ;; now wait for something to happen call waitpress ppage1 btfsc portbbuff,4 ;indicates knob changed goto set01secknob btfsc portbbuff,5 ;button by knob proceeds to deal with this step goto set01write btfsc portbbuff,6 ;right button increments the step to edit goto set01secR btfsc portbbuff,7 ;left button decrements the step goto set01secL ; goto set01seconds ;shouldn't get here, but just in case, simply ignore keypress set01secknob btfss portbbuff,3 goto set01seckminus set01seckplus rbank2 incf low(i2cbuf+0),f ;increment in place movf low(i2cbuf+0),w ;get the incremented value andlw 0x0f ;mask out upper nybble sublw d'9' ;so now has 9 - units digit, should be +ve or zero btfsc STATUS,C goto set01showtime ;C set, so +ve or zero, so OK movf low(i2cbuf+0),w ;get incremented count addlw 6 ;this converts 0x0A to 0x10 (etc) movwf low(i2cbuf+0) ;put that in sublw 0x60 ; have we got to 60? btfss STATUS,Z goto set01showtime clrf low(i2cbuf+0) goto set01minkplus set01seckminus rbank2 movf low(i2cbuf+0),f ;look at byte btfsc STATUS,Z goto set01seckmzero decf low(i2cbuf+0),f movf low(i2cbuf+0),w andlw 0x0f sublw d'9' btfsc STATUS,C goto set01showtime movf low(i2cbuf+0),w addlw d'-6' movwf low(i2cbuf+0) goto set01showtime set01seckmzero movlw 0x59 movwf low(i2cbuf+0) goto set01minkminus set01secR rbank0 movlw 2 movwf pmflags goto set01showtime set01secL rbank0 movlw 1 movwf pmflags goto set01showtime set01minutes movlw 0x48 ppage0 call lcdaddress ;; now wait for something to happen call waitpress ppage1 btfsc portbbuff,4 ;indicates knob changed goto set01minknob btfsc portbbuff,5 ;button by knob proceeds to deal with this step goto set01write btfsc portbbuff,6 ;right button increments the step to edit goto set01minR btfsc portbbuff,7 ;left button decrements the step goto set01minL ; goto set01minutes ;shouldn't get here, but just in case, simply ignore keypress set01minknob btfss portbbuff,3 goto set01minkminus set01minkplus rbank2 incf low(i2cbuf+1),f ;increment in place movf low(i2cbuf+1),w ;get the incremented value andlw 0x0f ;mask out upper nybble sublw d'9' ;so now has 9 - units digit, should be +ve or zero btfsc STATUS,C goto set01showtime ;C set, so +ve or zero, so OK movf low(i2cbuf+1),w ;get incremented count addlw 6 ;this converts 0x0A to 0x10 (etc) movwf low(i2cbuf+1) ;put that in sublw 0x60 ; have we got to 60? btfss STATUS,Z goto set01showtime clrf low(i2cbuf+1) goto set01hourkplus set01minkminus rbank2 movf low(i2cbuf+1),f ;look at byte btfsc STATUS,Z goto set01minkmzero decf low(i2cbuf+1),f movf low(i2cbuf+1),w andlw 0x0f sublw d'9' btfsc STATUS,C goto set01showtime movf low(i2cbuf+1),w addlw d'-6' movwf low(i2cbuf+1) goto set01showtime set01minkmzero movlw 0x59 movwf low(i2cbuf+1) goto set01hourkminus set01minR rbank0 movlw 0 movwf pmflags goto set01showtime set01minL rbank0 movlw 2 movwf pmflags goto set01showtime set01hours movlw 0x45 ppage0 call lcdaddress ;; now wait for something to happen call waitpress ppage1 btfsc portbbuff,4 ;indicates knob changed goto set01hourknob btfsc portbbuff,5 ;button by knob proceeds to deal with this step goto set01write btfsc portbbuff,6 ;right button increments the step to edit goto set01hourR btfsc portbbuff,7 ;left button decrements the step goto set01hourL ; goto set01hours ;shouldn't get here, but just in case, simply ignore keypress set01hourknob btfss portbbuff,3 goto set01hourkminus set01hourkplus rbank2 incf low(i2cbuf+2),f ;increment in place movf low(i2cbuf+2),w ;get the incremented value andlw 0x0f ;mask out upper nybble sublw d'9' ;so now has 9 - units digit, should be +ve or zero btfsc STATUS,C goto set01check24 ;C set, so +ve or zero, so OK movf low(i2cbuf+2),w ;get incremented count addlw 6 ;this converts 0x0A to 0x10 (etc) movwf low(i2cbuf+2) ;put that in set01check24 movf low(i2cbuf+2),w ; we need to do this in case we jumped to label sublw 0x24 ; have we got to 24? btfss STATUS,Z goto set01showtime movlw 0x23 movwf low(i2cbuf+2) movlw 0x59 movwf low(i2cbuf+1) movwf low(i2cbuf+0) goto set01showtime set01hourkminus rbank2 movf low(i2cbuf+2),f ;look at byte btfsc STATUS,Z goto set01hourkmzero decf low(i2cbuf+2),f movf low(i2cbuf+2),w andlw 0x0f sublw d'9' btfsc STATUS,C goto set01showtime movf low(i2cbuf+2),w addlw d'-6' movwf low(i2cbuf+2) goto set01showtime set01hourkmzero clrf low(i2cbuf+2) clrf low(i2cbuf+1) clrf low(i2cbuf+0) goto set01showtime set01hourR rbank0 movlw 1 movwf pmflags goto set01showtime set01hourL rbank0 movlw 0 movwf pmflags goto set01showtime set01write ppage0 rbank2 bcf i2caddr,0 ; clear ls bit so we write time call i2c_rtc ;do the write ppage1 goto idlemodestart ;note that if we set time, we jump back to idel mode set02 set90 ;; this should be last setp in settings mode ;; is erasing all memory ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(setmode90) ; asks whether to erase programs movwf FSR movlw 8 call lcdtext set90row2 ppage0 call lcdline2 movlw low(yesnolabel) ; movwf FSR movlw 8 call lcdtext set90wait ;; now check button-presses ppage0 call waitpress ; also waits for knob change ;; now process key press ppage1 btfsc portbbuff,6 ;right button adances to next question goto set99 btfsc portbbuff,7 ;left button is 'yes' - advane to check question goto set91 ; goto set90wait ; if something else, simply ignore keypress set91 ;; confirmation prior to erasing all memory ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(setmode91) ; asks whether to erase programs movwf FSR movlw 8 call lcdtext set91row2 ppage0 call lcdline2 movlw low(yesnolabel) ; movwf FSR movlw 8 call lcdtext set91wait ;; now check button-presses ppage0 call waitpress ; also waits for knob change ;; now process key press ppage1 btfsc portbbuff,6 ; right button odes erase goto set92 btfsc portbbuff,7 ; left button is 'yes' - cancel so bail out goto set99 ; goto set91wait ; if something else, simply ignore keypress set92 ;; it has been confirmed to erase all program memory ppage0 call lcdclear bcf STATUS,IRP movlw low(setmode92) ; is the 'wait, erasing' message movwf FSR movlw 8 call lcdtext ;; erase 32 bytes of i2cbuffer rbank2 clrf i2cbuf clrf i2cbuf+0x1 clrf i2cbuf+0x2 clrf i2cbuf+0x3 clrf i2cbuf+0x4 clrf i2cbuf+0x5 clrf i2cbuf+0x6 clrf i2cbuf+0x7 clrf i2cbuf+0x8 clrf i2cbuf+0x9 clrf i2cbuf+0xA clrf i2cbuf+0xB clrf i2cbuf+0xC clrf i2cbuf+0xD clrf i2cbuf+0xE clrf i2cbuf+0xF clrf i2cbuf+0x10 clrf i2cbuf+0x11 clrf i2cbuf+0x12 clrf i2cbuf+0x13 clrf i2cbuf+0x14 clrf i2cbuf+0x15 clrf i2cbuf+0x16 clrf i2cbuf+0x17 clrf i2cbuf+0x18 clrf i2cbuf+0x19 clrf i2cbuf+0x1A clrf i2cbuf+0x1B clrf i2cbuf+0x1C clrf i2cbuf+0x1D clrf i2cbuf+0x1E clrf i2cbuf+0x1F ;; initialise paramters movlw b'10100000' ; memory, chip 0, write movwf i2caddr ; control byte set clrf i2clow clrf i2chigh rbank0 clrf temp1 movlw 0xff movwf temp2 set92dowrite rbank2 movlw b'10100000' ; memory, chip 0, write movwf i2caddr ; control byte set movlw d'32' movwf i2ccount ppage0 call i2ceeprom rbank0 ppage1 incfsz temp1,f goto set92donext goto idlemodestart ;have erased everything so exit settings mode set92donext rbank2 movlw d'32' addwf i2clow,f btfsc STATUS,Z incf i2chigh,f goto set92dowrite set99 setend ;; if we get here simply return to idle mode ppage1 goto idlemodestart ;; ####################### ;; program page 2 ;; ####################### org 0x1000 ;; this is routine that sets a program running runprog rbank0 clrf el_secs ;clear elapsed time counters clrf el_minsl clrf el_minsh ;; report that have read step rbank0to2 movlw a'R' movwf i2cbuf+.40 movlw a'u' movwf i2cbuf+.41 movlw a'n' movwf i2cbuf+.42 movlw a' ' movwf i2cbuf+.43 movlw a'p' movwf i2cbuf+.44 movlw a'r' movwf i2cbuf+.45 movlw a'o' movwf i2cbuf+.46 movlw a'g' movwf i2cbuf+.47 movlw d'13' movwf i2cbuf+.48 movlw d'10' movwf i2cbuf+.49 movlw .10 movwf usartcount movlw low(i2cbuf+.40) movwf usartaddr bsf INTCON,PEIE ;enable peripheral interrupts bsf INTCON,GIE ;enable interrupts globally rbank1 bsf PIE1,TXIE ;enable serial port, should now transmit rbank0 clrf elt_h ;clear elapsed this step clrf elt_l bcf INTCON,T0IF ;clear timer overflow flag clrf TMR0 ;clear timer clock ;; idle mode has already read the step into i2cbuf ;; so jump to the pre-calculation step goto precalc nextprogstep ;; read the next step from the i2c eeprom rbank2 movf i2cbuf+d'22',w ;this is number of next step rbank0 movwf progstep ;set it into the register as current step btfss STATUS,Z goto readnextstep ;; if nextstep=0, then program is over ppage1 goto idlemodestart ;return to idle mode as if after power-on init readnextstep ;; now set up to read that step swapf progstep,w ; swap nybbles of step into w andlw b'00001111' ; and mask out upper nybble, so w now has high nybble of progstep rbank2 ; select register bank 2 movwf i2chigh ; and put that in high byte rbank0 ; back to bank 0 swapf progstep,w ; swap nybbles of step into w andlw b'11110000' ; and mask out lower nybble, so w now has (low nybble of progstep)*16 rbank2 ; select register bank 2 movwf i2clow ; and put that in low byte ;i2chigh:i2clow now contain step*16 bcf STATUS,C ; clear carry rlf i2clow,f ; low*2 rlf i2chigh,f ; high*2 so bytes have proper address movlw b'10100001' ; memory, chip 0, read movwf i2caddr ; control byte set movlw d'32' movwf i2ccount ppage0 call i2ceeprom ; do the read ppage2 rbank0 ;; zero the elapsed-this-step count clrf elt_h clrf elt_l ;; report that have read step rbank0to2 movlw a'S' movwf i2cbuf+.40 movlw a't' movwf i2cbuf+.41 movlw a'e' movwf i2cbuf+.42 movlw a'p' movwf i2cbuf+.43 movlw a' ' movwf i2cbuf+.44 movlw a'r' movwf i2cbuf+.45 movlw a'e' movwf i2cbuf+.46 movlw a'a' movwf i2cbuf+.47 movlw a'd' movwf i2cbuf+.48 movlw d'13' movwf i2cbuf+.49 movlw d'10' movwf i2cbuf+.50 ;; TODO03 output at least program step number, name to usart ;; probably also start and end temps and duration movlw .11 movwf usartcount movlw low(i2cbuf+.40) movwf usartaddr bsf INTCON,PEIE ;enable peripheral interrupts bsf INTCON,GIE ;enable interrupts globally rbank1 bsf PIE1,TXIE ;enable serial port, should now transmit precalc ;; calculates values that are unchanging for duration of ;; the current program step ;; power limits are saved as 0-100, need to be converted to 0-256 ;; but limited to 255 (ie, max single-byte value) ;; put these into i2cbuf+.34 for max and i2cbuf+.35 for min ;; actually calculate 2 9/16 x value, rbank2 pcalcmax bcf STATUS,C rlf i2cbuf+.15,w movwf i2cbuf+.34 ;limit x 2 in register bcf STATUS,C rrf i2cbuf+.15,w addwf i2cbuf+.34,f ;limit x 2.5 in register bcf STATUS,C rrf i2cbuf+.15,w ;limit x 0.5 in W movwf i2cbuf+.32 ;limit x 1/2 in temporary space bcf STATUS,C rrf i2cbuf+.32,f ;limit x 1/4 bcf STATUS,C rrf i2cbuf+.32,f ;limit x 1/8 bcf STATUS,C rrf i2cbuf+.32,w ;limit x 1/16 in w addwf i2cbuf+.34,f ;added into register btfss STATUS,C goto pcalcmin movlw 0xff movwf i2cbuf+.34 pcalcmin bcf STATUS,C rlf i2cbuf+.16,w movwf i2cbuf+.35 ;limit x 2 in register bcf STATUS,C rrf i2cbuf+.16,w addwf i2cbuf+.35,f ;limit x 2.5 in register bcf STATUS,C rrf i2cbuf+.16,w ;limit x 0.5 in W movwf i2cbuf+.32 ;limit x 1/2 in temporary space bcf STATUS,C rrf i2cbuf+.32,f ;limit x 1/4 bcf STATUS,C rrf i2cbuf+.32,f ;limit x 1/8 bcf STATUS,C rrf i2cbuf+.32,w ;limit x 1/16 addwf i2cbuf+.35,f ;added into register btfss STATUS,C goto runloop movlw 0xff movwf i2cbuf+.35 ;; main running loop, ie roaster is running runloop rbank0 bsf PORTA,5 bsf PORTA,5 ;front panel light on while we work ;; some information sent out usart is calculated as we go along ;; so pre-load the usart buffer with constants / defaults ;; note that spaces in first 20 characters done later so do not overwrite ;; 'read step' message, which could still be buffering out at this time rbank2 movlw a' ' movwf i2cbuf+.40+.23 movwf i2cbuf+.40+.25 movwf i2cbuf+.40+.28 movwf i2cbuf+.40+.30 movlw a'-' movwf i2cbuf+.40+.24 ;this is flag to say if power limit applied movlw a'_' movwf i2cbuf+.40+.26 ;this is flag to say if drum is running movwf i2cbuf+.40+.27 ;this is flag to say if fan is running movwf i2cbuf+.40+.29 ;this is flag to say if door is opening movwf i2cbuf+.40+.31 ;this is flag to say if cooling is running movlw a'?' movwf i2cbuf+.40+.20 ;this is power display, not yet set movwf i2cbuf+.40+.21 movwf i2cbuf+.40+.22 get_temps rbank0 ; read the on-board temperature (cold junction) movlw b'10000001' ; switch on adc channel 0, Fosc/32 movwf ADCON0 ; need a 20 uS wait, 80 instructions movlw d'27' movwf temp1 decfsz temp1,f ; 1 cycle goto $-1 ; +2 = 3cycles*27=81 cycles ; set conversion running bsf ADCON0,2 ; set conversion running btfsc ADCON0,2 ; poll the bit goto $-1 ; loop 'till it's clear movf ADRESH,w ; get high byte movwf t0_h ; save it in temperature register rbank1 movf ADRESL,w ; get low byte rbank0 movwf t0_l ; save it in temperature register clrf t1_h ; clear the registers holding temps 1 and 2 clrf t1_l clrf t2_h clrf t2_l movlw d'4' movwf temp2 ; use to count number of times we sample ADCs ; the two thermocouple inputs are read 4 times and summed ; this is equivalent to using average of 4 reads as value ; note we want to multiply the read value by four anyway, so this is convenient t1_t2_read ; wait 3.2uS before next acquisition - 13 instruction cycles movlw d'5' movwf temp1 decfsz temp1,f ; 1 cycle goto $-1 ; +2 = 3cycles*5=15 cycles ; obtain temperature sensor 1 value movlw b'10001001' ; switch on adc channel 1, Fosc/32 movwf ADCON0 ; need a 20 uS wait, 80 instructions movlw d'27' movwf temp1 decfsz temp1,f ; 1 cycle goto $-1 ; +2 = 3cycles*27=81 cycles ; set conversion running bsf ADCON0,2 ; set conversion running btfsc ADCON0,2 ; poll the bit goto $-1 ; loop 'till it's clear bcf STATUS,C ; clear carry so we don't bring anything in accidently rbank1 movf ADRESL,W ; get low byte of ADC result rbank0 addwf t1_l,f ; add low sample to total btfsc STATUS,C ; check carry incf t1_h,f ; increment high byte if set movf ADRESH,W ; get high byte of ADC result addwf t1_h,f ; add to total ; wait 3.2uS before next acquisition - 13 instruction cycles movlw d'5' movwf temp1 decfsz temp1,f ; 1 cycle goto $-1 ; +2 = 3cycles*5=15 cycles ; obtain temperature sensor 1 value movlw b'10010001' ; switch on adc channel 2, Fosc/32 movwf ADCON0 ; need a 20 uS wait, 80 instructions movlw d'27' movwf temp1 decfsz temp1,f ; 1 cycle goto $-1 ; +2 = 3cycles*27=81 cycles ; set conversion running bsf ADCON0,2 ; set conversion running btfsc ADCON0,2 ; poll the bit goto $-1 ; loop 'till it's clear bcf STATUS,C ; clear carry so we don't bring anything in accidently rbank1 movf ADRESL,w ; get low byte of ADC result rbank0 addwf t2_l,f ; add low sample to total btfsc STATUS,C ; check carry incf t2_h,f ; increment high byte if set movf ADRESH,W ; get high byte of ADC result addwf t2_h,f ; add to total decfsz temp2,f ; count down to zero goto t1_t2_read ; loop so we do four reads ; add t0 to t1 and t2 to give direct reading of t1 & t2 movf t0_l,w addwf t1_l,f btfsc STATUS,C incf t1_h,f movf t0_h,w addwf t1_h,f movf t0_l,w addwf t2_l,f btfsc STATUS,C incf t2_h,f movf t0_h,w addwf t2_h,f ;; now t0 should have controller ambient temperature ;; and t1, t2 should have thermocouple temperatures ;; t0 t1 and t2 all in tenths of degree C ;; let the measured temperature (for calcs & display) = t2 ;; TODO02 could have a better definintion of measured temperature movf t2_l,w movwf td_l movf t2_h,w movwf td_h ;; do calculations calculate_tt rbank2 movf i2cbuf+4,w xorlw 0xff rbank0 movwf w_a ;inverse of step start temperature low byte into w_a rbank2 movf i2cbuf+5,w xorlw 0xff rbank0 movwf w_b ;inverse of step start t high byte into w_b movlw 1 addwf w_a,f ;add 1 to give 2s complemet btfsc STATUS,Z incf w_b,f ;; w_a:b now has 0 - step start temperature rbank2 movf i2cbuf+6,w ;low byte of end temperature rbank0 addwf w_a,f btfsc STATUS,C ;was carry set? incf w_b,f ;if so increment high byte too rbank2 movf i2cbuf+7,w ;high byte of end temperature rbank0 addwf w_b,f ;; w_a:b now has ((step end t) - (step start t)) ;; note that this may be negative movf w_a,w ;copy (end-start) into target temperature register movwf tt_l movf w_b,w movwf tt_h ;; now do 16 bit multiply ;; zero where answer will build clrf w_a ;this is lsB clrf w_b clrf w_c clrf w_d ; clrf w_e ; msB (clearing this is redundant - it gets overwritten below) ;; nice little trick from from Bob Fehrenbach & Scott Dattalo c/o piclist bsf w_b,7 ;when this drops out from rrf routine is at end ;; use temp1 as negative flag clrf temp1 btfss tt_h,7 ;is tt_a:b negative? goto tt_positive ;if not, jump forward ;; tt is negative, invert it incf temp1,f ;set flag to remember to invert answer movlw 0xff xorwf tt_h,f xorwf tt_l,f incf tt_l,f btfsc STATUS,Z incf tt_h,f ;tt_h:l is now +ve bcf STATUS,C tt_positive rrf tt_h,f rrf tt_l,f btfss STATUS,C goto tt_nextbit movf elt_l,w addwf w_c,f movf elt_h,w btfsc STATUS,C incfsz elt_h,w addwf w_d,f tt_nextbit bcf STATUS,C rrf w_d,f rrf w_c,f rrf w_b,f rrf w_a,f btfss STATUS,C goto tt_positive ;; now w_d:c:b:a contains (end-start)*t, always +ve movf w_d,w movwf w_e movf w_c,w movwf w_d movf w_b,w movwf w_c movf w_a,w movwf w_b clrf w_a ;; w now multiplied by 256 ;; divide routine taken from piclist by Peter Hemsley and modified to be 40 bits by 16 tt_divide movlw d'40' movwf temp2 ;temp2 is bitcount (temp1 is still negative flag) clrf lowb clrf highb ;lowb and highb are the remainder ;; use tt to contain Divisor, being step duration rbank2 movf i2cbuf+2,w rbank0 movwf tt_l rbank2 movf i2cbuf+3,w rbank0 movwf tt_h tt_divloop clrc rlf w_a,f rlf w_b,f rlf w_c,f rlf w_d,f rlf w_e,f ;msb rolled out of dividend rlf lowb,f ;and into partial remainder rlf highb,f skpnc ;check overflow goto tt_subd movfw tt_h ; Compare partial remainder and divisor subwf highb,w skpz goto tt_testgt ; Not equal so test if remdrH is greater movfw tt_l ; High bytes are equal, compare low bytes subwf lowb,w tt_testgt skpc ; Carry set if remdr >= divis goto tt_remrlt tt_subd movfw tt_l ; Subtract divisor from partial remainder subwf lowb,f skpc ; Test for borrow decf highb,f ; Subtract borrow movfw tt_h subwf highb,f bsf w_a,0 ; Set quotient bit to 1 ; Quotient replaces dividend which is lost tt_remrlt decfsz temp2,f ;bit count is in tenp2 goto tt_divloop ;; w should now contain 256 x the modulus of required change from start temperature ;; alternatively, view w_e:d:c:b as difference and w_a as fractional part btfss temp1,0 ;check if it should be negative goto tt_addstart ;; we need to change w to -ve, ie multiply current by -1 movlw 0xff xorwf w_e,f xorwf w_d,f xorwf w_c,f xorwf w_b,f xorwf w_a,f incf w_a,f btfsc STATUS,Z incf w_b,f btfsc STATUS,Z incf w_c,f btfsc STATUS,Z incf w_d,f btfsc STATUS,Z incf w_e,f tt_addstart ;; get start temperature out of i2cbuffer rbank2 movf i2cbuf+4,w ;start temperature low byte rbank0 addwf w_b,f ;add it in to w, remembering that w_a is fractional part btfsc STATUS,C incf w_c,f btfsc STATUS,Z incf w_d,f btfsc STATUS,Z incf w_e,f ;; high byte rbank2 movf i2cbuf+5,w ;start temperature high byte rbank0 addwf w_c,f btfsc STATUS,C incf w_d,f btfsc STATUS,Z incf w_e,f ;; w should now contain 256 x target temperature ;; alternatively, view w_e:d:c:b as temperature and w_a as fractional part ;; now define the target temperature for display purposes ;; (calculation continues with value in w) movf w_c,w movwf tt_h movf w_b,w movwf tt_l btfss w_a,7 ;check if fractional part >= 0.5 goto calcerror ;if not jump forwards incf tt_l,f ;otherwise round displayed temperature up btfsc STATUS,Z incf tt_h,f ;; can also drop w_e and w_d from subsequent calculation ;; since target temperature cannot exceed 400, so value is 0-4000 calcerror ;; calculate difference from desired ;; measured temperature is in td_h:l ;; multiply w by -1 movlw 0xff xorwf w_c,f xorwf w_b,f xorwf w_a,f incf w_a,f btfsc STATUS,Z incf w_b,f btfsc STATUS,Z incf w_c,f ;; w_c:a now has -1 x target temperature movf td_l,w ;get low byte of measured addwf w_b,f ;add it in to w, remembering that w_a is fractional part btfsc STATUS,C incf w_c,f movf td_h,w ;get high byte of measured addwf w_c,f ;add it in to w ;; w now has measured-desired temperature (fractional part in w_a) ;; it may be negative clrf temp1 ;will hold sign flag in this again btfss w_c,7 ;check negative indication goto err_positive ;; error is negative incf temp1,f ;set negative flag movlw 0xff xorwf w_c,f xorwf w_b,f xorwf w_a,f incf w_a,f btfsc STATUS,Z incf w_b,f btfsc STATUS,Z incf w_c,f ;w now been inverted, so is +ve err_positive ;but temp1 indicates if should be negative err_divide movlw d'24' movwf temp2 ;this will count bits clrf lowb clrf highb ;lowb and highb are the remainder ;; use w_e and w_d as divisor, which is bandwidth ;; however, bandwidth in memory is in units of degree C not tenths of degree rbank2 movf i2cbuf+d'14',w rbank0 movwf temp3 ;temp1 is -ve flag, temp2 iscounting bits, temp 3 is bandwidth movwf w_d clrf w_e ;w_e:d has bandwidth*1, d is lsbyte bcf STATUS,C rlf w_d,f rlf w_e,f ;bandwidth x 2 rlf w_d,f rlf w_e,f ;bandwidth x 4 movf temp3,w addwf w_d,f btfsc STATUS,C incf w_e,f ;bandwidth x 5 bcf STATUS,C rlf w_d,f rlf w_e,f ;bandwidth x 10 err_divloop clrc rlf w_a,f rlf w_b,f rlf w_c,f ;msb rolled out of dividend rlf lowb,f ;and into partial remainder rlf highb,f skpnc ;check overflow goto err_subd movfw w_e ; Compare partial remainder and divisor subwf highb,w skpz goto err_testgt ; Not equal so test if remdrH is greater movfw w_d ; High bytes are equal, compare low bytes subwf lowb,w err_testgt skpc ; Carry set if remdr >= divis goto err_remrlt err_subd movfw w_d ; Subtract divisor from partial remainder subwf lowb,f skpc ; Test for borrow decf highb,f ; Subtract borrow movfw w_e subwf highb,f bsf w_a,0 ; Set quotient bit to 1 ; Quotient replaces dividend which is lost err_remrlt decfsz temp2,f ;bit count is in temp2 goto err_divloop ;; w_c:b:a should now have modulus of error as proportion of bandwidth ;; w_a is fractional part err_fixsign ;; if (measured-desired)is +ve then temp1 == 0 ;; in this case power should be 0.5-(error as prop of bandwidth) ;; so reverse sign of proportion btfsc temp1,0 ;check if it should be negative goto add_half ;; we need to change w to -ve, ie multiply current by -1 movlw 0xff xorwf w_c,f xorwf w_b,f xorwf w_a,f incf w_a,f btfsc STATUS,Z incf w_b,f btfsc STATUS,Z incf w_c,f add_half ;; now add 0.5 movlw d'128' addwf w_a,f btfsc STATUS,C incf w_b,f btfsc STATUS,Z incf w_c,f ;; w now has the power proportion required ;; if w is -ve, power should be zero btfss w_c,7 goto pow_not_neg clrf heatpower goto pow_done pow_not_neg movf w_c,f ;look at w_c btfss STATUS,Z goto pow_full movf w_b,f btfss STATUS,Z goto pow_full pow_fractional ;; we are within the proportional band movf w_a,w movwf heatpower goto pow_done pow_full movlw d'255' movwf heatpower pow_done ;; power is now defined as 0 (off) to 255 (which is either full or 255/256ths) ;; now need to limit power as program requires ;; power limits scaled to 0-255 in precalc maxpowlimit rbank2 movf i2cbuf+.34,w ;w now has maximum power limit rbank0 subwf heatpower,w ;w now has heatpower-limit btfss STATUS,C ;is answer +ve? goto minpowlimit ;if not, check min limit, rbank2 movlw a'u' movwf i2cbuf+.40+.24 movf i2cbuf+.34,w rbank0 movwf heatpower goto powlimitdone minpowlimit rbank2 movf i2cbuf+.35,w ;w now has minimum power limit rbank0 subwf heatpower,w ;w now has heatpower-limit btfsc STATUS,C ;is answer -ve? goto powlimitdone ;if not, limit is done rbank2 movlw a'l' movwf i2cbuf+.40+.24 movf i2cbuf+.35,w rbank0 movwf heatpower powlimitdone ;; now build the bar for display ;; this has lots of jumping to and fro, so it's more easily done in ppage0 ppage0 call powerbar ppage2 runningdisplay ;; display time and current temperature on first line ppage0 call lcdclear movlw el_minsl movwf FSR bcf STATUS,IRP call b2bcd ;; if we are over 9999 minutes, do a different display ppage2 movf bcdtt,f ;look at ten thousands digit btfss STATUS,Z ;will generally be zero goto timer_outrange ; but if not, display '++++' ppage0 swapf bcdht,w andlw 0x0f addlw a'0' rbank0to2 ;one operatiuon quicker than rbank2 macro movwf i2cbuf+.40 call lcdsendchar movf bcdht,w andlw 0x0f addlw a'0' rbank0to2 ;one operatiuon quicker than rbank2 macro movwf i2cbuf+.41 call lcdsendchar swapf bcdut,w andlw 0x0f addlw a'0' rbank0to2 ;one operatiuon quicker than rbank2 macro movwf i2cbuf+.42 call lcdsendchar movf bcdut,w andlw 0x0f addlw a'0' rbank0to2 ;one operatiuon quicker than rbank2 macro movwf i2cbuf+.43 call lcdsendchar ppage2 goto timersecs timer_outrange ppage0 movlw a'+' call lcdsendchar movlw a'+' call lcdsendchar movlw a'+' call lcdsendchar movlw a'+' call lcdsendchar rbank0to2 movlw a'+' movwf i2cbuf+.40 movwf i2cbuf+.41 movwf i2cbuf+.42 movwf i2cbuf+.43 timersecs ppage0 movlw a':' rbank0to2 ;one operatiuon quicker than rbank2 macro movwf i2cbuf+.44 call lcdsendchar ppage3 ; w2bcd is in program mode movf el_secs,w call w2bcd ppage0 movwf temp1 swapf temp1,w andlw 0x0f addlw a'0' rbank0to2 ;one operatiuon quicker than rbank2 macro movwf i2cbuf+.45 call lcdsendchar movf temp1,w andlw 0x0f addlw a'0' rbank0to2 ;one operatiuon quicker than rbank2 macro movwf i2cbuf+.46 call lcdsendchar movlw a' ' rbank0to2 movwf i2cbuf+.40+.7 call lcdsendchar movlw a' ' call lcdsendchar ;; print current display temperature movlw td_l movwf FSR bcf STATUS,IRP call b2bcd swapf bcdht,w andlw 0x0f addlw a'0' rbank0to2 movwf i2cbuf+.40+.14 call lcdsendchar movf bcdht,w andlw 0x0f addlw a'0' rbank0to2 movwf i2cbuf+.40+.15 call lcdsendchar swapf bcdut,w andlw 0x0f addlw a'0' rbank0to2 movwf i2cbuf+.40+.16 call lcdsendchar movlw a'.' rbank0to2 movwf i2cbuf+.40+.17 call lcdsendchar movf bcdut,w andlw 0x0f addlw a'0' rbank0to2 movwf i2cbuf+.40+.18 call lcdsendchar movlw a' ' rbank0to2 movwf i2cbuf+.40+.19 call lcdsendchar movlw 3 ;top half of power bar chart call lcdsendchar ;; second line has step numbers or name and target temperature call lcdline2 ppage2 btfss el_secs,1 ; 2 seconds of each goto rd_stepname ;otherwise show name rd_stepnumbs ppage0 movlw a' ' call lcdsendchar ;; display program number clrf highb movf prognum,w movwf lowb movlw lowb movwf FSR bcf STATUS,IRP call b2bcd ;convert counter to bcd ;; put that on screen swapf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for tens digit call lcdsendchar ; send it movf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for units digit call lcdsendchar ; send it movlw a'-' call lcdsendchar ;; display program step clrf highb movf progstep,w movwf lowb ;; convert to bcd movlw lowb movwf FSR bcf STATUS,IRP call b2bcd ;convert counter to bcd ;; put that on display movf bcdht,w andlw 0x0f addlw a'0' ; so w now has ascii code for hundreds digit call lcdsendchar ; send it swapf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for tens digit call lcdsendchar ; send it movf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for units digit call lcdsendchar ; send it movlw a' ' call lcdsendchar ppage2 goto rd_target rd_stepname ppage0 ;; display step name movlw low(i2cbuf+d'24') movwf FSR bsf STATUS,IRP movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar bcf STATUS,IRP rd_target ppage0 ;; print current display temperature movlw a' ' call lcdsendchar movlw tt_l movwf FSR bcf STATUS,IRP call b2bcd swapf bcdht,w andlw 0x0f addlw a'0' rbank0to2 movwf i2cbuf+.40+.8 call lcdsendchar movf bcdht,w andlw 0x0f addlw a'0' rbank0to2 movwf i2cbuf+.40+.9 call lcdsendchar swapf bcdut,w andlw 0x0f addlw a'0' rbank0to2 movwf i2cbuf+.40+.10 call lcdsendchar movlw a'.' rbank0to2 movwf i2cbuf+.40+.11 call lcdsendchar movf bcdut,w andlw 0x0f addlw a'0' rbank0to2 movwf i2cbuf+.40+.12 call lcdsendchar movlw a' ' rbank0to2 movwf i2cbuf+.40+.13 call lcdsendchar movlw 4 ;bottom half of power bar chart call lcdsendchar ;; front panel light rbank0 bcf PORTA,5 ;front panel light off when procssing is complete ppage2 ;; decide whether the drum, fan etc need to be running rbank2 movf i2cbuf+.8,w ;this has the bits for on/off eqpt. rbank0 movwf temp1 ;so we can interrogate it checkdrum btfss temp1,0 ;this is rotisserie bit goto drumoff drumon movlw a'd' rbank0to2 movwf i2cbuf+.40+.26 rbank2to0 bsf PORTE,2 goto checkfan drumoff bcf PORTE,2 checkfan btfss temp1,1 ;this is top element fan bit goto fanoffmaybe fanon movlw a'f' rbank0to2 movwf i2cbuf+.40+.27 rbank2to0 bsf PORTE,1 goto checkdoor fanoffmaybe ;; we force the fan on for at least 1 second if any heater input movf heatpower,f btfsc STATUS,Z goto fanoff ;; power on, so fan goes on for this second movlw a'F' ;upper case F indicates forced fan rbank0to2 movwf i2cbuf+.40+.27 rbank2to0 bsf PORTE,1 goto checkdoor fanoff bcf PORTE,1 checkdoor btfss temp1,4 ;this is door control bit goto dooroff ;; if got here, door may be open ;; however, we only activate control for 1 second, ;; do this if we are in first second of step, ie if elapsed this = 0 rbank2 movlw a'e' movwf i2cbuf+.40+.29 rbank0 movf elt_h,f btfss STATUS,Z goto dooroff ; because high byte is non-zero movf elt_l,f btfss STATUS,Z goto dooroff ; because low byte is non-zero ;; door control bit set, and in first second of step, soactivate bsf PORTC,1 goto doordone dooroff bcf PORTC,1 ;door control off doordone ;; put all information out serial port ;; time, current temp and required temp has been buffered as LCD display generated ;; others buffered as calc'd or as actioned ;; note this is interrupt driven, so does not block actioning power rbank2 movlw d'13' movwf i2cbuf+.40+.32 movlw d'10' movwf i2cbuf+.40+.33 movlw .34 movwf usartcount movlw low(i2cbuf+.40) movwf usartaddr rbank1 bsf INTCON,PEIE ;enable peripheral interrupts bsf INTCON,GIE ;enable interrupts globally bsf PIE1,TXIE ;enable serial port, should now transmit ;; now action heat power checkfullpower rbank0 movf heatpower,w sublw 0xff ;check if it is full btfss STATUS,Z goto checkzeropower ;; is set to full power bsf PORTE,0 ;SSR on goto waitfortick checkzeropower movf heatpower,w btfsc STATUS,Z goto ssroff ;; switch on output bsf PORTE,0 ;SSR on checkclockcount movf heatpower,w ;get required power subwf TMR0,w ;w now has (timer0 - power) btfss STATUS,C ;if carry set, result=0 or +ve, ie timer0 >= power goto checkclockcount ssroff ;; switch off output bcf PORTE,0 waitfortick btfss INTCON,T0IF goto $-1 ;wait here until the timer 0 overflow flag is set bcf INTCON,T0IF ;clear the flag increment_time ;; increment elapsed-this-step time incf elt_l,f btfsc STATUS,Z incf elt_h,f ; increment count of hours, minutes and seconds incf el_secs,f ; increment seconds movf el_secs,w ; get current count of seconds sublw d'60' ; w now contains 60-seconds btfss STATUS,Z ; if zero set, we need to do something goto checkifstepend ; otherwise bail out clrf el_secs ; set seconds to 0 incf el_minsl,f ; and increment minutes btfsc STATUS,Z incf el_minsh,f ; increment high byte of minutes checkifstepend rbank2 movf i2cbuf+.3,w ;get high byte of step duration rbank0 subwf elt_h,w ;so w now has (elt_h-duration_h) btfss STATUS,C goto runloop ;result was -ve ie cannot have expired btfss STATUS,Z ;check that it is zero goto nextprogstep ;if non-zero, have somehow overshot, so next step ;; at this point, high bytes match rbank2 movf i2cbuf+.2,w rbank0 subwf elt_l,w ;so w now has (elt_l-duration_l) btfss STATUS,C goto runloop ;result was -ve, ie duration > elapsed ;; step has elapsed goto nextprogstep carryon goto runloop ;; ####################### ;; program page 3 ;; contains programming mode ;; ####################### org 0x1800 ;; routines used exclusively in programming mode findnewstep ;; find first unused program step movlw 2 ; 2 is the minimum possible program step number rbank0 movwf newstep ;; now set up to read that step PMtrynextstep ppage0 swapf newstep,w ; swap nybbles of step into w andlw b'00001111' ; and mask out upper nybble, so w now has high nybble of progstep rbank2 ; select register bank 2 movwf i2chigh ; and put that in high byte rbank0 ; back to bank 0 swapf newstep,w ; swap nybbles of step into w andlw b'11110000' ; and mask out lower nybble, so w now has (low nybble of progstep)*16 rbank2 ; select register bank 2 movwf i2clow ; and put that in low byte ;i2chigh:i2clow now contain step*16 bcf STATUS,C ; clear carry rlf i2clow,f ; low*2 rlf i2chigh,f ; high*2 so bytes have proper address movlw b'10100001' ; memory, chip 0, read movwf i2caddr ; control byte set movlw d'1' ;we only need to read first byte to determine if is available movwf i2ccount call i2ceeprom ; do the read rbank2 ppage3 movf i2cbuf,f ;look at first byte btfsc STATUS,Z ;is it zero? return ;if so, we've found step number rbank0 incf newstep,f goto PMtrynextstep ;; w to bcd ;; unsigned 8 bit binary in W converted to bcd in W (hundreds dropped) ;; trashes the temp & temp3 register w2bcd rbank0 movwf bcdtempl ; set it in our working space movlw d'8' ; number of binary digits, so number of loops movwf temp1 ; now put it where we will count clrf temp3 ; initialise bcd digits to zero w2bcdloop w2bcdunits movf temp3,w ; get two digits into w andlw 0x0f ; so now we have units in w sublw d'4' ; subtract w from 4 and put answer in w btfsc STATUS,C ; if carry is clear answer was -ve goto w2bcdtens ; otherwise consider next digit movlw d'3' ; if answer was -ve, add three addwf temp3,f ; add it and put it back in f w2bcdtens swapf temp3,w ; swapped nybbles into w andlw 0x0f ; now have just tesn digit in low nybble of w sublw d'4' ; subtract w from 4 and put answer in w btfsc STATUS,C ; if carry is clear answer was -ve goto w2bcdshifts ; otherwise consider next digit movlw 0x30 ; if answer was -ve, add three to the bcd byte addwf temp3,f ; add it and put it back in f w2bcdshifts bcf STATUS,C ; clear carry flag to start rlf bcdtempl,f ; do series of roll left across the working space rlf temp3,f ;; thats the end of the loop decfsz temp1,f ; after decrement, we have number of times more too loop goto w2bcdloop ; if that's nonzero, do the loop movf temp3,w ; get the result back in w return ; otherwise we're finished ;; ############################ ;; start of program mode proper ;; ############################ progmodestart rbank0 ;this already set, but just to be sure... ;; switch off buttons and timer 0 interrupt bcf INTCON,RBIE bcf INTCON,T0IE ;; if the current step is not set to zero ;; we move around within current program movf progstep,f btfss STATUS,Z goto PMselectstep ;; otherwise, prognum is defined but progstep is zero ;; so we start a new program at this prognum call findnewstep goto PMwriteprognum PMselectstep ;; the program number is defined by calling routine ;; now we choose which step to deal with ppage0 ;; prognum and progstep should be defined ;; now set up to read that step swapf progstep,w ; swap nybbles of step into w andlw b'00001111' ; and mask out upper nybble, so w now has high nybble of progstep rbank2 ; select register bank 2 movwf i2chigh ; and put that in high byte rbank0 ; back to bank 0 swapf progstep,w ; swap nybbles of step into w andlw b'11110000' ; and mask out lower nybble, so w now has (low nybble of progstep)*16 rbank2 ; select register bank 2 movwf i2clow ; and put that in low byte ;i2chigh:i2clow now contain step*16 bcf STATUS,C ; clear carry rlf i2clow,f ; low*2 rlf i2chigh,f ; high*2 so bytes have proper address movlw b'10100001' ; memory, chip 0, read movwf i2caddr ; control byte set movlw d'32' movwf i2ccount call i2ceeprom ; do the read rbank0 ;; put display on screen ppage0 call lcdclear bcf STATUS,IRP movlw low(progmodeselect) movwf FSR movlw 6 call lcdtext movlw a' ' call lcdsendchar ;; display program number clrf highb movf prognum,w movwf lowb movlw lowb movwf FSR bcf STATUS,IRP call b2bcd ;convert counter to bcd ;; put that on screen swapf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for tens digit call lcdsendchar ; send it movf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for units digit call lcdsendchar ; send it ;; second line of display call lcdline2 movlw a' ' call lcdsendchar ;; display program step clrf highb movf progstep,w movwf lowb ;; convert to bcd movlw lowb movwf FSR bcf STATUS,IRP call b2bcd ;convert counter to bcd ;; put that on display movf bcdht,w andlw 0x0f addlw a'0' ; so w now has ascii code for hundreds digit call lcdsendchar ; send it swapf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for tens digit call lcdsendchar ; send it movf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for units digit call lcdsendchar ; send it movlw a' ' call lcdsendchar movlw a':' call lcdsendchar movlw a' ' call lcdsendchar ;; display step name movlw low(i2cbuf+d'24') movwf FSR bsf STATUS,IRP movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar bcf STATUS,IRP PMselectstepwait ;; wait for key press call waitpress ;; action key press ppage3 btfsc portbbuff,4 goto PMselectstepknob btfsc portbbuff,5 ;button by knob proceeds to deal with this step goto PM01 btfsc portbbuff,6 ;right button increments the step to edit goto PMnextstep ; btfsc portbbuff,7 ;left button decrements the step goto PMprevstep ; goto PMselectstepwait ;shouldn't get here, but just in case, try again PMselectstepknob btfss portbbuff,3 ;check knob direction goto PMprevstep ;if not previous step, withh overflow to next PMnextstep rbank2 movf i2cbuf+d'22',w ; rbank0 btfss STATUS,Z ;if next would be zero, just stay where we are movwf progstep goto PMselectstep ;show that step PMprevstep rbank2 movf i2cbuf+d'23',w ; rbank0 btfss STATUS,Z ;if previous would be zero, just stay where we are movwf progstep goto PMselectstep ;show that step PM01 ;; asks if editing existing step ;; or creating a new one ;; routines generally in prog page 0 ppage0 ;; top line of display is defined label call lcdclear bcf STATUS,IRP movlw low(progmode01a) movwf FSR movlw 8 call lcdtext ;; put the arrows on line 2 call lcdline2 movlw low(progmode01b) movwf FSR movlw 8 call lcdtext ;; put current progstep on line2 call lcdline2 movlw a' ' call lcdsendchar clrf highb movf progstep,w movwf lowb ;; convert to bcd movlw lowb movwf FSR bcf STATUS,IRP call b2bcd ;convert counter to bcd ;; put that on display movf bcdht,w andlw 0x0f addlw a'0' ; so w now has ascii code for hundreds digit call lcdsendchar ; send it swapf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for tens digit call lcdsendchar ; send it movf bcdut,w andlw 0x0f addlw a'0' ; so w now has ascii code for units digit call lcdsendchar ; send it ;; now wait for a key to be pressed call waitpress ppage3 btfsc portbbuff,5 ;button by knob returns to idlemode goto PM01gotoidle btfsc portbbuff,6 ;right button creates a new step goto PM02 ;we need to create that btfsc portbbuff,7 ;left button edits current step goto PM03 ;so jump to editing stage goto PM01 ;shouldn't get here, but just in case, try again PM01gotoidle ppage1 goto idlemodestart PM02 ;; we will need a new step location and write it into newstep register call findnewstep ; note that this only reads 1 byte out of each step ;; we need to ask if this is a new program ;; or inserted in current program ;; top line of display is defined label, second line is yes/no PM02display ppage0 call lcdclear bcf STATUS,IRP movlw low(progmode02) ; is "add to current program?" movwf FSR movlw 8 call lcdtext call lcdline2 movlw low(yesnolabel) movwf FSR movlw 8 call lcdtext ;; wait for key press call waitpress ;; action key press ppage3 btfsc portbbuff,6 ;right button 'no' means start a new program goto PMnewprog ; btfsc portbbuff,7 ;left button 'yes' means insert into current program goto PMinsertstep ; goto PM02display ; button K does nothing, so could get here, just try again ;; creating a new program at current step number ;; we need to find a new prog number PMnewprog ppage0 ;; we already know what the new step will be numbered ;; need to decide about program number movf prognum,f ;look at program number btfsc STATUS,Z ;see if it is zero goto PMwriteprognum ;if so, define this program ;; we need to find a new program to write to ;; read whole table into i2cbuffer and work through rbank2 clrf i2clow clrf i2chigh ;start at byte zero movlw d'64' movwf i2ccount ;read 64 bytes movlw b'10100001' ; memory, chip 0, read movwf i2caddr ; control byte set ppage0 call i2ceeprom ; get the byte from the eeprom ppage3 bsf STATUS,IRP movlw low(i2cbuf) movwf FSR rbank0 clrf prognum PMtrythisprog incf prognum,f incf FSR,f ; movf INDF,w ;look at possible candidate btfss STATUS,Z ;is it zero? goto PMtrythisprog ;if not try the next ;; now prognum is set to one that's free PMwriteprognum ;; set current program to refer to new step rbank0 ppage0 call lcdclear movlw a'w' call lcdsendchar movlw a' ' call lcdsendchar swapf prognum,w andlw 0x0f addlw a'0' call lcdsendchar movf prognum,w andlw 0x0f addlw a'0' call lcdsendchar movlw a' ' call lcdsendchar swapf newstep,w andlw 0x0f addlw a'0' call lcdsendchar movf newstep,w andlw 0x0f addlw a'0' call lcdsendchar call waitpress movf prognum,w rbank2 movwf i2clow clrf i2chigh ;byte address = prognum rbank0 movf newstep,w ;get new step number rbank2 movwf i2cbuf ;put it into the buffer movlw 1 movwf i2ccount ;just one byte to write movlw b'10100000' ; memory, chip 0, write movwf i2caddr ; control byte set ppage0 call i2ceeprom ; write that byte into the eeprom rbank0 clrf nextstep clrf prevstep ppage3 goto PMinitialisestep ;; inserting a new step between current and next step ;; need to find a blank step, modify current and next ;; then set up the new step with the same values as current PMinsertstep ppage0 ;; already know what number our new step will be ;; note that although we've been reading into i2cbuf, we only over-wrote ;; the first byte when we got to here, so bytes 22 and 23 should be OK rbank2 movf i2cbuf+d'22',w rbank0 movwf nextstep rbank2 movf i2cbuf+d'23',w rbank0 movwf prevstep ;; now we need to rewrite previous and next steps ;; to refer to the new step ;; start with previous, which is already loaded movf newstep,w rbank2 movwf i2cbuf+d'22' ;set next step in prog to be the new step movlw 1 movwf i2cbuf ;reset first byte flag (shouldn't be needed) ;; now set up to read that step swapf progstep,w ; swap nybbles of step into w andlw b'00001111' ; and mask out upper nybble, so w now has high nybble of progstep rbank2 ; select register bank 2 movwf i2chigh ; and put that in high byte rbank0 ; back to bank 0 swapf progstep,w ; swap nybbles of step into w andlw b'11110000' ; and mask out lower nybble, so w now has (low nybble of progstep)*16 rbank2 ; select register bank 2 movwf i2clow ; and put that in low byte ;i2chigh:i2clow now contain step*16 bcf STATUS,C ; clear carry rlf i2clow,f ; low*2 rlf i2chigh,f ; high*2 so bytes have proper address movlw b'10100000' ; memory, chip 0, write movwf i2caddr ; control byte set movlw d'32' movwf i2ccount call i2ceeprom ; write the whole step ;; now load what was the next step rbank0 swapf nextstep,w ; swap nybbles of step into w andlw b'00001111' ; and mask out upper nybble, so w now has high nybble of progstep rbank2 ; select register bank 2 movwf i2chigh ; and put that in high byte rbank0 ; back to bank 0 swapf nextstep,w ; swap nybbles of step into w andlw b'11110000' ; and mask out lower nybble, so w now has (low nybble of progstep)*16 rbank2 ; select register bank 2 movwf i2clow ; and put that in low byte ;i2chigh:i2clow now contain step*16 bcf STATUS,C ; clear carry rlf i2clow,f ; low*2 rlf i2chigh,f ; high*2 so bytes have proper address movlw b'10100001' ; memory, chip 0, read movwf i2caddr ; control byte set movlw d'32' movwf i2ccount call i2ceeprom ; do the read ;; update the program step movf newstep,w rbank2 movwf i2cbuf+d'23' ;set previous step in prog to be the new step movlw 1 movwf i2cbuf ;reset first byte flag (shouldn't be needed) ;; i2clow and i2chigh should still be set movlw b'10100000' ; memory, chip 0, write movwf i2caddr ; control byte set movlw d'32' movwf i2ccount call i2ceeprom ; do the write PMinitialisestep ;; initialise our new step rbank2 movlw 1 movwf i2cbuf ;byte 0 lsbit says this step is in use clrf i2cbuf+1 ;byte 1 not in use movwf i2cbuf+2 ; low byte duration = 1 clrf i2cbuf+3 ; high byte = 0 so step duration = 1 second movlw d'250' movwf i2cbuf+4 ;bytes 4&5 = 250 (25 C start temperature) clrf i2cbuf+5 movwf i2cbuf+6 ;bytes 6&7 = 250 (25C end temperature) clrf i2cbuf+7 movlw b'00000011' movwf i2cbuf+8 ;byte 8 - x,x,x, shut, x,x, fan on, rot on clrf i2cbuf+9 ;cooling fans off movlw d'20' movwf i2cbuf+d'14' ;byte 14 = prop band = 20C movlw d'100' movwf i2cbuf+d'15' ;byte 15 - no maximum heat limit (ie 100% set) clrf i2cbuf+d'16' ;byte 16 - no minimum heat limit (ie 0% set) rbank0 movf nextstep,w rbank2 movwf i2cbuf+d'22' ; set next step value rbank0 movf progstep,w ; previous to the new step will be the current step rbank2 movwf i2cbuf+d'23' ; set previous step value movlw a' ' movwf i2cbuf+d'24' ;label is all blanks movwf i2cbuf+d'25' movwf i2cbuf+d'26' movwf i2cbuf+d'27' movwf i2cbuf+d'28' movwf i2cbuf+d'29' movwf i2cbuf+d'30' movwf i2cbuf+d'31' rbank0 movf newstep,w ;get the proposed new step movwf progstep ;make it the current step PM03 ;; this is start of editing the current step ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(progmode03) ; is "set step duration" movwf FSR movlw 8 call lcdtext ;; start by setting hours clrf pmflags PM03row2 rbank1 movlw a' ' movwf lcdbuf movwf lcdbuf+1 movwf lcdbuf+3 movwf lcdbuf+5 movwf lcdbuf+8 movwf lcdbuf+0x0A movwf lcdbuf+0x0D movwf lcdbuf+0x0E movwf lcdbuf+0x0F movlw a':' movwf lcdbuf+4 movwf lcdbuf+9 ;; decide whether setting h, m or s and highlight accordingly ppage3 rbank0 movf pmflags,w btfsc STATUS,Z goto PM03sethrs addlw 0xff ;which will roll to 0 if pmflags was 1 btfsc STATUS,Z goto PM03setmins addlw 0xff ;which will roll to 0 if pmflags was 2 btfsc STATUS,Z goto PM03setsecs goto PM03 ;if not a recognised value, reset and try again PM03sethrs rbank1 movlw a'>' movwf lcdbuf+1 movlw a'<' movwf lcdbuf+3 goto PM03showval PM03setmins rbank1 movlw a'>' movwf lcdbuf+5 movlw a'<' movwf lcdbuf+8 goto PM03showval PM03setsecs rbank1 movlw a'>' movwf lcdbuf+0x0A movlw a'<' movwf lcdbuf+0x0D goto PM03showval PM03showval ;; convert step duration to hms rbank0 movlw low(i2cbuf)+2 movwf FSR bsf STATUS,IRP ppage0 call b2hms ;; fill in time digits rbank0 movf bcdtt,w andlw 0x0f ;mask all but low digit addlw a'0' rbank1 movwf lcdbuf+2 rbank0 swapf bcdht,w andlw 0x0f ;mask all but low digit addlw a'0' rbank1 movwf lcdbuf+6 rbank0 movf bcdht,w andlw 0x0f ;mask all but low digit addlw a'0' rbank1 movwf lcdbuf+7 rbank0 swapf bcdut,w andlw 0x0f ;mask all but low digit addlw a'0' rbank1 movwf lcdbuf+0x0b rbank0 movf bcdut,w andlw 0x0f ;mask all but low digit addlw a'0' rbank1 movwf lcdbuf+0x0c call lcdline2 movlw d'16' call lcdsendbuf ;; now check button-presses call waitpress ; also waits for knob change ;; now process key press ppage3 btfsc portbbuff,4 ;indicates knob changed goto PM03knob btfsc portbbuff,5 ;button by knob proceeds to deal with this step goto PM04 btfsc portbbuff,6 ;right button increments the step to edit goto PM03incflags ; btfsc portbbuff,7 ;left button decrements the step goto PM03decflags ; goto PM03row2 ;shouldn't get here, but just in case, simply ignore keypress PM03knob ;; need to check if set to h, m or s rbank0 ;register bank 0 movlw low(i2cbuf)+2 movwf FSR bsf STATUS,IRP ;but INDF set up to address bank 2 movf pmflags,w btfsc STATUS,Z goto PM03khrs addlw 0xff ;which will roll to 0 if pmflags was 1 btfsc STATUS,Z goto PM03kmins addlw 0xff ;which will roll to 0 if pmflags was 2 btfsc STATUS,Z goto PM03ksecs goto PM03 ;if not a recognised value, reset and try again PM03khrs movlw high(d'3600') movwf temp2 movlw low(d'3600') movwf temp1 btfss portbbuff,3 ;check direction goto PM03kminus goto PM03kplus PM03kmins clrf temp2 movlw d'60' movwf temp1 btfss portbbuff,3 ;check direction goto PM03kminus goto PM03kplus PM03ksecs clrf temp2 movlw 1 movwf temp1 btfss portbbuff,3 ;check direction goto PM03kminus goto PM03kplus PM03kplus PM03kpluslow movf temp1,w ;this is low byte of addition addwf INDF,f ;add it in btfss STATUS,C goto PM03kplushigh incf FSR,f incf INDF,f decf FSR,f ;put pointer back to low byte PM03kplushigh incf FSR,f movf temp2,w addwf INDF,f ;; check we have not exceeded 9:59:59 = 35999 movlw high(d'35999') subwf INDF,w btfss STATUS,C ;if carry clear, result -ve, ie high byte < 140 goto PM03row2 ;so go round again ;; high byte >= 140 btfss STATUS,Z ;if zero, then high byte was equal to limit goto PM03limitmax ;if not, high byte > 140 so apply max limit ;; high byte == 140 decf FSR,f movlw low(d'35999') subwf INDF,w btfss STATUS,C ;if carry clear, result=-ve ie low byte < 159 goto PM03row2 ;so go round again PM03limitmax rbank2 movlw low(d'35999') movwf i2cbuf+2 movlw high(d'35999') movwf i2cbuf+3 goto PM03row2 PM03kminus PM03kminuslow movf temp1,w ;low byte to be subtracted subwf INDF,f btfsc STATUS,C ;checkif went negative goto PM03kminushigh ;if C was set, result still +ve (or zero) incf FSR,f movf INDF,w ;get high byte into W btfsc STATUS,Z ;if high byte already zero... goto PM03limitmin addlw 0xff ;equivalent to subtracting 1 movwf INDF ; put answer back into byte decf FSR,f ;point back at low byte PM03kminushigh incf FSR,f movf temp2,w subwf INDF,f btfss STATUS,C ;if c is clear, has gone negative goto PM03limitmin ;so limit ;; now check if zero (minimum step duration=1) rbank2 movf low(i2cbuf+3),f ;look at high byte btfss STATUS,Z ;see if it is zero goto PM03row2 ;if not, loop around movf low(i2cbuf+2),f ;look at low byte btfss STATUS,Z ; see if it is zero goto PM03row2 ;if not loop around ;; otherwise, run through to set minimum: PM03limitmin rbank2 movlw 1 movwf low(i2cbuf+2) clrf low(i2cbuf+3) goto PM03row2 PM03incflags incf pmflags,f movf pmflags,w sublw d'4' btfsc STATUS,Z clrf pmflags goto PM03row2 PM03decflags decf pmflags,f movf pmflags,w sublw d'255' btfss STATUS,Z goto PM03row2 movlw 3 movwf pmflags goto PM03row2 ;; set temperatures PM04 ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(progmode04) ; is "set temperatures" movwf FSR movlw 8 call lcdtext ;; start by setting start temperature clrf pmflags PM04row2 ;; load up lcd buffer with display rbank1 movlw a' ' movwf lcdbuf movwf lcdbuf+6 movwf lcdbuf+7 movwf lcdbuf+8 movwf lcdbuf+9 movwf lcdbuf+0x0F movlw a'.' movwf lcdbuf+4 movwf lcdbuf+0x0D ;; decide what digits we are setting ppage3 rbank0 btfsc pmflags,0 ;since we just have two alternatives, we can look at lsbit goto PM04setend PM04setstart rbank1 movlw a'>' movwf lcdbuf movlw a'<' movwf lcdbuf+6 goto PM04showval PM04setend rbank1 movlw a'>' movwf lcdbuf+9 movlw a'<' movwf lcdbuf+0x0f PM04showval ;; start temperature rbank0 movlw low(i2cbuf)+4 movwf FSR bsf STATUS,IRP ppage0 call b2bcd rbank0 swapf bcdht,w andlw 0x0f ;mask all but low digit addlw a'0' rbank1 movwf lcdbuf+1 rbank0 movf bcdht,w andlw 0x0f ;mask all but low digit addlw a'0' rbank1 movwf lcdbuf+2 rbank0 swapf bcdut,w andlw 0x0f ;mask all but low digit addlw a'0' rbank1 movwf lcdbuf+3 rbank0 movf bcdut,w andlw 0x0f ;mask all but low digit addlw a'0' rbank1 movwf lcdbuf+5 ;; now end temperature rbank0 movlw low(i2cbuf)+6 movwf FSR bsf STATUS,IRP call b2bcd rbank0 swapf bcdht,w andlw 0x0f ;mask all but low digit addlw a'0' rbank1 movwf lcdbuf+0x0a rbank0 movf bcdht,w andlw 0x0f ;mask all but low digit addlw a'0' rbank1 movwf lcdbuf+0x0b rbank0 swapf bcdut,w andlw 0x0f ;mask all but low digit addlw a'0' rbank1 movwf lcdbuf+0x0c rbank0 movf bcdut,w andlw 0x0f ;mask all but low digit addlw a'0' rbank1 movwf lcdbuf+0x0e call lcdline2 movlw d'16' call lcdsendbuf ;; now check button-presses call waitpress ; also waits for knob change ;; now process key press ppage3 btfsc portbbuff,4 ;indicates knob changed goto PM04knob btfsc portbbuff,5 ;button by knob proceeds to deal with this step goto PM05 btfsc portbbuff,6 ;right button increments the step to edit goto PM04incflags ; btfsc portbbuff,7 ;left button decrements the step goto PM04decflags ; goto PM04row2 ;shouldn't get here, but just in case, simply ignore keypress PM04knob ;; start or end temnperature? btfsc pmflags,0 goto PM04ke PM04ks ;; action knob when set to start temperature ;; point indf at start temperature movlw low(i2cbuf+4) movwf FSR bsf STATUS,IRP goto PM04kdirection PM04ke ;; point indf at end temperature movlw low(i2cbuf+6) movwf FSR bsf STATUS,IRP PM04kdirection btfss portbbuff,3 goto PM04kminus PM04kplus movlw 5 addwf INDF,f ;increment btfss STATUS,C ;check if overflow goto PM04kpluscheck incf FSR,f incf INDF,f decf FSR,f PM04kpluscheck ;; check we have not exceeded 350C, which is 3500 incf FSR,f ;so looking at high byte movlw high(d'3500') subwf INDF,w btfss STATUS,C ;if carry clear, result -ve, ie high byte < limit goto PM04row2 ;so go round again ;; high byte >= limit, btfss STATUS,Z ;if zero, then high byte was equal to limit goto PM04limitmax ;if not, high byte > 140 so apply max limit ;; high byte == limit decf FSR,f movlw low(d'3500') subwf INDF,w btfss STATUS,C ;if carry clear, result=-ve ie low byte < 159 goto PM04row2 ;so go round again incf FSR,f PM04limitmax movlw high(d'3500') movwf INDF decf FSR,f movlw low(d'3500') movwf INDF goto PM04row2 PM04kminus movlw 0xfb addwf INDF,f ;adding 255 equals decrement by one btfsc STATUS,C ;check if not overflow goto PM04row2 ;if not decrement high byte, cannot be at limit incf FSR,f decf INDF,f decf FSR,f PM04kminuslimit incf FSR,f btfss INDF,7 ;if msbit is set, have wrapped goto PM04row2 clrf INDF decf FSR,f clrf INDF goto PM04row2 PM04incflags incf pmflags,f goto PM04row2 PM04decflags decf pmflags,f goto PM04row2 PM05 ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(progmode05) ; is "drum rotate" movwf FSR movlw 6 call lcdtext movlw a' ' call lcdsendchar movlw a'O' call lcdsendchar rbank2 ppage3 btfss low(i2cbuf+8),0 goto PM05labeloff PM05labelon ppage0 movlw a'N' call lcdsendchar movlw a' ' call lcdsendchar ppage3 goto PM05row2 PM05labeloff ppage0 movlw a'F' call lcdsendchar movlw a'F' call lcdsendchar PM05row2 ppage0 call lcdline2 movlw low(onofflabel) ; movwf FSR movlw 8 call lcdtext ;; now check button-presses call waitpress ; also waits for knob change ;; now process key press ppage3 btfsc portbbuff,5 ;button by knob proceeds to deal with this step goto PM06 btfsc portbbuff,6 ;right button increments the step to edit goto PM05setoff btfsc portbbuff,7 ;left button decrements the step goto PM05seton ; goto PM05 ;shouldn't get here, but just in case, simply ignore keypress PM05seton rbank2 bsf low(i2cbuf+8),0 goto PM06 PM05setoff rbank2 bcf low(i2cbuf+8),0 goto PM06 PM06 ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(progmode06) ; asks about element fan movwf FSR movlw 6 call lcdtext movlw a' ' call lcdsendchar movlw a'O' call lcdsendchar rbank2 ppage3 btfss low(i2cbuf+8),1 goto PM06labeloff PM06labelon ppage0 movlw a'N' call lcdsendchar movlw a' ' call lcdsendchar ppage3 goto PM06row2 PM06labeloff ppage0 movlw a'F' call lcdsendchar movlw a'F' call lcdsendchar PM06row2 ppage0 call lcdline2 movlw low(onofflabel) ; movwf FSR movlw 8 call lcdtext ;; now check button-presses call waitpress ; also waits for knob change ;; now process key press ppage3 btfsc portbbuff,5 ;button by knob proceeds to deal with this step goto PM07 btfsc portbbuff,6 ;right button increments the step to edit goto PM06setoff btfsc portbbuff,7 ;left button decrements the step goto PM06seton ; goto PM06 ;shouldn't get here, but just in case, simply ignore keypress PM06seton rbank2 bsf low(i2cbuf+8),1 goto PM07 PM06setoff rbank2 bcf low(i2cbuf+8),1 goto PM07 PM07 ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(progmode07) ; asks about door movwf FSR movlw 6 call lcdtext movlw a' ' call lcdsendchar rbank2 ppage3 btfss low(i2cbuf+8),4 goto PM07labeloff PM07labelon ppage0 movlw a'Y' call lcdsendchar movlw a'E' call lcdsendchar movlw a'S' call lcdsendchar ppage3 goto PM07row2 PM07labeloff ppage0 movlw a'N' call lcdsendchar movlw a'O' call lcdsendchar PM07row2 ppage0 call lcdline2 movlw low(yesnolabel) ; movwf FSR movlw 8 call lcdtext ;; now check button-presses call waitpress ; also waits for knob change ;; now process key press ppage3 btfsc portbbuff,5 ;button by knob proceeds to deal with this step goto PM08 btfsc portbbuff,6 ;right button increments the step to edit goto PM07setoff btfsc portbbuff,7 ;left button decrements the step goto PM07seton ; goto PM07 ;shouldn't get here, but just in case, simply ignore keypress PM07seton rbank2 bsf low(i2cbuf+8),4 goto PM08 PM07setoff rbank2 bcf low(i2cbuf+8),4 goto PM08 PM08 ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(progmode08) ; asks about cooling fans movwf FSR movlw 6 call lcdtext movlw a' ' call lcdsendchar movlw a'O' call lcdsendchar rbank2 ppage3 btfss low(i2cbuf+9),0 goto PM08labeloff PM08labelon ppage0 movlw a'N' call lcdsendchar ppage3 goto PM08row2 PM08labeloff ppage0 movlw a'F' call lcdsendchar movlw a'F' call lcdsendchar PM08row2 ppage0 call lcdline2 movlw low(onofflabel) ; movwf FSR movlw 8 call lcdtext ;; now check button-presses call waitpress ; also waits for knob change ;; now process key press ppage3 btfsc portbbuff,5 ;button by knob proceeds to deal with this step goto PM09 btfsc portbbuff,6 ;right button increments the step to edit goto PM08setoff btfsc portbbuff,7 ;left button decrements the step goto PM08seton ; goto PM08 ;shouldn't get here, but just in case, simply ignore keypress PM08seton rbank2 bsf low(i2cbuf+9),0 goto PM09 PM08setoff rbank2 bcf low(i2cbuf+9),0 goto PM09 PM09 ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(progmode09) ; proportional band label movwf FSR movlw 8 call lcdtext PM09row2 ppage0 ;this is needed whern we goto PM09row2 rbank2 movf low(i2cbuf+d'14'),w rbank0 movwf lowb clrf highb bcf STATUS,IRP movlw lowb movwf FSR call b2bcd movlw 0x49 call lcdaddress movlw a'>' call lcdsendchar movf bcdht,w andlw 0x0f ;mask all but low digit addlw a'0' call lcdsendchar swapf bcdut,w andlw 0x0f ;mask all but low digit addlw a'0' call lcdsendchar movf bcdut,w andlw 0x0f ;mask all but low digit addlw a'0' call lcdsendchar movlw a'<' call lcdsendchar ;; now wait for action call waitpress ppage3 btfsc portbbuff,4 ;indicates knob changed goto PM09knob btfsc portbbuff,5 ;button by knob proceeds to deal with this step goto PM10 goto PM09row2 ;could get here, becasue L & R don't do anything PM09knob btfss portbbuff,3 goto PM09kminus PM09kplus rbank2 incf low(i2cbuf+d'14'),w addlw d'55' ;carry will be clear if increment went > 200 btfsc STATUS,C movlw d'255' addlw d'-55' movwf low(i2cbuf+d'14') goto PM09row2 PM09kminus rbank2 decf low(i2cbuf+d'14'),w addlw d'-1' btfss STATUS,C movlw 0 addlw 1 movwf low(i2cbuf+d'14') goto PM09row2 PM10 ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(progmode10) ; asks abouut power limits movwf FSR movlw 8 call lcdtext ;; start by setting start temperature clrf pmflags PM10row2 ppage0 call lcdline2 movlw 'm' call lcdsendchar movlw 'i' call lcdsendchar movlw 'n' call lcdsendchar movlw ' ' btfss pmflags,0 movlw '>' call lcdsendchar rbank2 movf low(i2cbuf+d'16'),w ppage3 call w2bcd rbank2 movwf low(i2cbuf+d'33') ;we know this is spare - it's beyond our buffer ;; now decide if we are at 100%, which needs special treatment ;; because we only have two digits movf low(i2cbuf+d'33'),f ;look at the value btfss STATUS,Z goto PM10mindigits ;if it's not 00 then show digits movf low(i2cbuf+d'16'),f ;look at actual value btfsc STATUS,Z ; goto PM10mindigits ;if it is actually zero, show as digits ;; since bcd digits are 00 but value is not 0, must be 100% ppage0 movlw a'1' call lcdsendchar movlw a'0' call lcdsendchar movlw a'0' call lcdsendchar ppage3 goto PM10max PM10mindigits ppage0 swapf low(i2cbuf+d'33'),w andlw 0x0f addlw a'0' call lcdsendchar rbank2 movf low(i2cbuf+d'33'),w andlw 0x0f addlw a'0' call lcdsendchar movlw ' ' btfss pmflags,0 movlw '<' call lcdsendchar PM10max ppage0 movlw ' ' call lcdsendchar movlw ' ' call lcdsendchar movlw 'm' call lcdsendchar movlw 'a' call lcdsendchar movlw 'x' call lcdsendchar movlw ' ' btfsc pmflags,0 movlw '>' call lcdsendchar rbank2 movf low(i2cbuf+d'15'),w ppage3 call w2bcd rbank2 movwf low(i2cbuf+d'33') ;we know this is spare - it's beyond our buffer ;; now decide if we are at 100%, which needs special treatment ;; because we only have two digits movf low(i2cbuf+d'33'),f ;look at the value btfss STATUS,Z goto PM10maxdigits ;if it's not 00 then show digits movf low(i2cbuf+d'15'),f ;look at actual value btfsc STATUS,Z ; goto PM10maxdigits ;if it is actually zero, show as digits ;; since bcd digits are 00 but value is not 0, must be 100% ppage0 movlw a'1' call lcdsendchar movlw a'0' call lcdsendchar movlw a'0' call lcdsendchar ppage3 goto PM10wait PM10maxdigits ppage0 rbank2 swapf low(i2cbuf+d'33'),w andlw 0x0f addlw a'0' call lcdsendchar rbank2 movf low(i2cbuf+d'33'),w andlw 0x0f addlw a'0' call lcdsendchar movlw ' ' btfsc pmflags,0 movlw '<' call lcdsendchar PM10wait ppage0 ;; now check button-presses call waitpress ; also waits for knob change ;; now process key press ppage3 btfsc portbbuff,4 ;indicates knob changed goto PM10knob btfsc portbbuff,5 ;button by knob proceeds to deal with this step goto PM11 btfsc portbbuff,6 ;right button increments the step to edit goto PM10incflags ; btfsc portbbuff,7 ;left button decrements the step goto PM10decflags ; goto PM10row2 ;shouldn't get here, but just in case, simply ignore keypress PM10knob ;; setting min or max? btfsc pmflags,0 goto PM10kmax PM10kmin ;; point indf at min limit movlw low(i2cbuf+d'16') movwf FSR bsf STATUS,IRP goto PM10kdirection PM10kmax ;; point indf at max limit movlw low(i2cbuf+d'15') movwf FSR bsf STATUS,IRP PM10kdirection btfss portbbuff,3 goto PM10kminus PM10kplus rbank2 incf INDF,w addlw d'155' ;carry will be clear if increment went > 100 btfsc STATUS,C movlw d'255' addlw d'-155' movwf INDF ; record that in register ;; now check that we don't exceed the defined max ;; this doesn't limit self, becasue we won't be bigger than self ;; but it does limit min not to exceed max subwf low(i2cbuf+d'15'),w ; so w is now max - current, should not be -ve btfsc STATUS,C ;if c=0, result was -ve goto PM10row2 ;otherwise loop round movf low(i2cbuf+d'15'),w ; get our limiting value movwf INDF ;set that as curent value goto PM10row2 ;loop round PM10kminus rbank2 decf INDF,w addlw d'1' btfsc STATUS,C movlw d'1' addlw d'255' movwf INDF ;; INDF should not go less than min - see discussion in PM10kplus movf low(i2cbuf+d'16'),w ;min limit subwf INDF,w ; so now current-min, should not be -ve btfsc STATUS,C ;if c=0, result was -ve goto PM10row2 ; done - loop round movf low(i2cbuf+d'16'),w ; get our limiting value movwf INDF ;set that as curent value goto PM10row2 ;done -loop round PM10incflags incf pmflags,f goto PM10row2 PM10decflags decf pmflags,f goto PM10row2 PM11 ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(progmode11) ; asks for step name movwf FSR movlw 8 call lcdtext ;; start by byte 0 clrf pmflags PM11row2 ppage0 movlw b'00001100' ; 00001, display on, underline off, blink off call lcdsendcmd ; send to the lcd call lcdline2 rbank0 movf pmflags,w andlw 7 ;ie, pmflags will be 0-7 movwf pmflags movlw a' ' call lcdsendchar movlw a' ' call lcdsendchar movlw a' ' call lcdsendchar movlw a'>' call lcdsendchar ;; display step name movlw low(i2cbuf+d'24') movwf FSR bsf STATUS,IRP movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar incf FSR,f movf INDF,w call lcdsendchar movlw a'<' call lcdsendchar ;; now switch on cursor movlw b'00001110' ; 00001, display on, underline on, blink off call lcdsendcmd ; send to the lcd movf pmflags,w ;so w has which character to edit addlw 0x44 ;this is address of first character of label call lcdaddress movf pmflags,w addlw low(i2cbuf+d'24') movwf FSR ;; now check button-presses call waitpress ; also waits for knob change ;; now process key press ppage3 btfsc portbbuff,4 ;indicates knob changed goto PM11knob btfsc portbbuff,5 ;button by knob proceeds to deal with this step goto PM12 btfsc portbbuff,6 ;right button increments the step to edit goto PM11plus btfsc portbbuff,7 ;left button decrements the step goto PM11minus ; goto PM11row2 ;shouldn't get here, but just in case, simply ignore keypress PM11knob btfss portbbuff,3 goto PM11kminus PM11kplus incf INDF,w andlw 0x7f btfsc STATUS,Z movlw 0x20 movwf INDF goto PM11row2 PM11kminus decf INDF,w addlw d'-31' btfsc STATUS,Z movlw (0x7f-d'31') addlw d'31' movwf INDF goto PM11row2 PM11plus incf pmflags,f goto PM11row2 PM11minus decf pmflags,f goto PM11row2 PM12 ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(progmode12) ; asks whether to write to memory movwf FSR movlw 8 call lcdtext call lcdline2 movlw low(yesnolabel) ; movwf FSR movlw 8 call lcdtext ;; now check button-presses call waitpress ; also waits for knob change ;; now process key press ppage3 btfsc portbbuff,6 ;saying no loops round to edit again goto PM03 btfsc portbbuff,7 ;saying yes does the write goto PM12write ; goto PM12 ;would get here by knob or K button, ignore PM12write ;; now we actually send stuff to the i2ceeprom ppage0 rbank0 swapf progstep,w ; swap nybbles of step into w andlw b'00001111' ; and mask out upper nybble, so w now has high nybble of progstep rbank2 ; select register bank 2 movwf i2chigh ; and put that in high byte rbank0 ; back to bank 0 swapf progstep,w ; swap nybbles of step into w andlw b'11110000' ; and mask out lower nybble, so w now has (low nybble of progstep)*16 rbank2 ; select register bank 2 movwf i2clow ; and put that in low byte ;i2chigh:i2clow now contain step*16 bcf STATUS,C ; clear carry rlf i2clow,f ; low*2 rlf i2chigh,f ; high*2 so bytes have proper address movlw b'10100000' ; memory, chip 0, write movwf i2caddr ; control byte set movlw d'32' movwf i2ccount call i2ceeprom ; do the write rbank0 PM13 ppage0 rbank0 call lcdclear bcf STATUS,IRP movlw low(progmode13) ; asks if there is more editing to do movwf FSR movlw 8 call lcdtext call lcdline2 movlw low(yesnolabel) ; movwf FSR movlw 8 call lcdtext ;; now check button-presses call waitpress ; also waits for knob change ;; now process key press ppage3 btfsc portbbuff,6 ;saying no returns to standby mode goto progtostandby btfsc portbbuff,7 ;saying yes does the write ;; completed step edit or definition ;; prognum and progstep must be defined to get here ;; so we can just go to the select step process goto PMselectstep ; goto PM13 ;would get here by knob or K button, ignore progtostandby ;; return from program mode to standby4 ppage1 ;idle mode is in prog page 1 goto standbyloop ; return to prog mode with current program selected end ; that's all folks!
and this is as far as it has got ... tune in again for another thrilling
installment some time.
back to the main oven roaster page
back to the top roaster page
back to Ian's contents page
back to www.astounding.org.uk top contents
page
To comment on anything (please do) email ian@astounding.org.uk