list n=96 ;********************************************************************** ;2345678901234567890123456789012345678901234567890123456789012345678901234567890 ; ; Filename: ajbrrem.asm ; Date: 26 April 2004 ; File Version: 00.10 (set in ID locs) ; __IDLOCS 0x0010 ; ; Author: Steve Russell (pic.projects@ntlworld.com) ; Company: Grapevyne ; ; Copyright: (C) Steve Russell 2003 ; This code is available at no charge to individuals for ; personal, non-profit, use, and may be used as is or ; modifed as desired. Further distribution of this code ; or a derivative work is allowed as long as this ; copyright notice is not removed or modified. Changes ; to the original code should be clearly identified. ; Any commercial use of this code, or a derivative of it, ; may only be made with the written agreement of the ; copyright holder. ; ; This file is provided "as is". No claims of suitability for any ; particular purpose are made. No liability for any damages (of any sort) ; resulting from its use are accepted, no matter how they may arise. ; ;********************************************************************** ; ; Files required: PIC12C508A.INC ; ; The final design uses an LC part, but this is fully compatible with ; the C part. ; ;********************************************************************** ; ; DESCRIPTION ; ; This program implements a remote control for the Archos series of MP3 ; jukebox recorders and players, using a PIC12LC508A to provide the ; necessary logic. Six buttons allow the user to increase or decrease the ; playback volume, Play, Stop, and skip to the next or the previous song. ; The remote runs on one 3V CR2032 battery, which should have a life of ; more than a year under normal usage. If a PIC12C508A is used, then a ; supply voltage of 4.5V should be provided. ; ; ACKNOWLEDGEMENTS ; ; Information required to drive the remote port of Archos devices was ; gleaned from Tjerk Schuringa, creator of the original REMOCLONE ; remote (implemented with a 16LF84), and the authors of Rockbox, the ; the amazing open source alternative to the Archos' proprietary firmware, ; available from http://rockbox.haxx.se. ; page ; USER OPERATION ; ; Normal mode ; ; * The user presses the button for the required function and, when a ; valid debounced button press is determined, the appropriate code is ; sent to the Archos. Only one key can be pressed at a time (with one ; exception - see Calibration Mode below), all other combinations are ; treated as invalid. Holding a volume adjustment key down sends a ; stream of repeat commands to the Archos. ; ; Calibration Mode ; ; * If Volup and Voldn are pressed simultaneously for about 2 seconds, ; the device is put into calibration mode. This allows you to tweak ; the serial timing to account for the relatively wide tolerance of the ; PIC's internal clock. In limited testing, the tolerance of the clock ; when loaded with the factory OSCCAL value is close enough for the ; serial data to be recognised by the Archos Jukebox. It is possible, ; however, that adjustment of the default baud rate may be necessary. ; ; * While in calibration mode, a test code is sent to the Archos every ; 0.5s or so. 16 codes are sent in total - 4 VolDn followed by 4 VolUp, ; repeated twice - with the intent that this sequence is least likely ; to change the user's setting. Each press of the Next button increases ; the baud rate by decreasing the serial bit time by about 1us from the ; nominal period of 104us. Pressing the Prev button decreases the baud ; rate (increases the bit time) in the same way. Total adjustment is ; +/- 16 steps away from nominal. Making an adjustment when less than ; 16 test codes remain to be sent adds annother 16 to the number to be ; sent. ; ; * For optimal performance over an extended temperature range, use the ; calibration feature to determine the range of values over which the ; Archos recognises commands from the remote at normal room ; temperatures, and then set to the middle of this range. If you get ; lost during this procedure, pressing Play (while in Calibrate mode) ; will return the timing to nominal. ; ; * Calibration mode is exited when the last test code has been sent, or ; can be terminated early by pressing the Stop button. ; ; HARDWARE DESIGN ; ; Two outputs of the PIC12LC508A (GP4,5) drive a 2x3 matrix of switches. ; The switch states are read in using inputs GP0, 1, 3, which are ; equipped with weak internal pullups, so no external resistors are ; required. These inputs also support "wake on pin change", so the remote ; responds essentially immediately to switch changes. The final output ; (GP2) drives the serial data to the Archos Jukebox. The Archos expects ; an open-drain driver for serial data, this is implemented using the ; PIC's ability to tri-state the output pin. ; ; All timings are calculated assuming a nominal 4MHz clock, so each ; instruction cycle takes 1us. The configuration fuses are set to use the ; internal oscillator, but more accurate timing may be required for serial ; communications. To achieve this, a calibration mode allows the user to ; adjust the serial data rate. ; page ; SOFTWARE DESIGN ; ; The PIC spends most of its time in the sleep state. With no buttons ; (or an invalid combination) pressed, the WDT is set to its longest ; period (about 2.3 seconds), so waking the PIC up after this time. After ; waking, unless a button has been pressed, the PIC rapidly returns to the ; sleep state. There is a small window of opportunity for a button to be ; pressed after wakeup, and thus going unnoticed. This window is less than ; 17us (or about 300us if changes during WDT reset are not reflected in ; the status bits) in 2.3s. Worst case, this is less than 0.02%, hence can ; be ignored. ; ; If a button is pressed while the PIC is asleep, it wakes up, debounces ; the switch and responds to valid presses by transmitting the appropriate ; control code to the Archos and then returning to the sleep state. In ; this case, the WDT is set to 144us, so that, while valid keys are held ; pressed, the PIC wakes up more frequently. After a delay of about 0.75s, ; and if a volume adjustment button is pressed, the code for the ; appropriate button is transmitted each time the PIC wakes up. ; ; A specific "invalid" keypress (Volup+Voldn) puts the device in calibrate ; mode, which allows the serial transfer rate to be trimmed to compensate ; for the tolerance of the internal oscillator. ; ; The bit time is modified by simply incrementing or decrementing a ; variable (bitadjust) to add or subtract 1 instruction cycle (c. 1us) ; to or from the nominal bit time of 104us. A little trickery is required ; in bitdelay to allow adjustment by only one cycle. Adjustment is ; limited to a range that encompasses the specified tolerance of the ; internal oscillator. ; page ;********************************************************************** ; ; CHANGE RECORD ; ; TO DO: ; ; v00.10 26Apr04 Comment out OSCCAL value ; v00.09 24Jan03 Provide a way to reset baud rate to nominal (press ; Play in calibrate mode). ; Set delay after button press in calibrate mode to ; short so disruption to flow of test characters is ; shorter. ; v00.08 17Jan03 Prevent button causing test char when change baud rate ; vFF.07 16Jan03 Test build with BEEP, using GP0 as piezo driver. ; Different symptoms to v00.05, but still no good. ; v00.07 16Jan03 Change SETTST to 4 (was 3) to increase calib time ; v00.06 16Jan03 Removed Beep code as PIC oscillated when driving ; the piezo transducer directly. ; v00.05 15Jan03 Changed to using a jump table in calib mode. ; Prevent calib mode timeout when holding calib mode ; buttons down. ; Changed repeat delay from 500ms to 750ms. ; Audible beep for enter/exit calib mode, hit baud rate ; limit (controlled by BEEPON define) ; v00.04 20Dec02 Tidied up button handling ; Changed handling of commands to simplify adding more ; Allow S_REPEAT to be set for individual buttons ; Implement calibrate mode ; v00.03 12Dec02 Forgot to set/clear S_REPEAT ; Set repeat timer to 144ms (think 72ms will be too fast) ; v00.02 10Dec02 Added bit timing code ; Resolved problem due to v.weak pullup on GP3 ; Added delay before repeat ; Added debounce ; v00.01 08Dec02 Basic functionality test ; ;********************************************************************** page ;********************************************************************** ; ASSEMBLER DIRECTIVES ;********************************************************************** list p=12c508a, r=dec #include __CONFIG _CP_OFF & _WDT_ON & _MCLRE_OFF & _IntRC_OSC ; IDLOCS set above (see File version) ;********************************************************************** ; MACROS ;********************************************************************** ;********************************************************************** ; CONSTANTS ;********************************************************************** GPIOLO equ 0x00 ; GPIO value to allow o/ps to be 0/Hi-Z BTNMASK equ 0x0B ; Mask to isolate buttons STATMASK equ 0x77 ; Mask for aligned btnstat bits GPHIZ equ 0x3F ; --111111 GP0,1,3 I/P, GP2,4,5 O/P (Hi-Z) GP5LO equ 0x1F ; --011111 TRIS value for driving GP5 lo GP4LO equ 0x2F ; --101111 TRIS value for driving GP4 lo GP45LO equ 0x0F ; --001111 TRIS value for driving GP4/5 lo DATALO equ 0x3B ; --111011 TRIS value for driving data lo DATAHI equ 0x3f ; --111111 TRIS value for driving data hi NUMBITS equ 10 ; 1 start bit, 8 data bits, 1 stop bit BITTIME equ 80 ; Number of extra clocks needed in bitdelay ; ... to set 104 clock bit width RANGE equ 0x10 ; Allow +/- 16 clocks for bit timing ULIMIT equ BITTIME+RANGE LLIMIT equ BITTIME-RANGE RPTTIME equ 144 ; Nominal repeat time (WDT) in ms RPTDLY equ (750/RPTTIME)+1 ; Approx 0.75s delay before repeat start CALDLY equ (2000/RPTTIME)+1 ; Approx 2s delay before enter cal mode TSTMASK equ 0x03 ; Used to test if time to change test code SETTST equ 4 ; 1<3, 1->2, 2->1, 3->0) addwf PCL,f ; Use the number to trim the cycle count nop nop nop bit1 nop ; This loop is 4 clocks in length, use decfsz loopcnt,f ; ...calculated value to generate bit timing. goto bit1 nop ; Last time thru loop has to be 4 clocks too retlw 0 page ;********************************************************************** ; Transmit code ; ; This subroutine sends the code in xmitchar to the Archos at ; 9600 baud, N-8-1, LSB first. The GP2 output is used, with the GPIO ; port value set to 0, and the TRIS bit used to drive 0/Hi-Z onto the ; dataline, emulating an open-drain driver. ; ; INPUT: The code in xmitchar. ; ; OUTPUT: Serial code is sent out via GP2 ; xmitchar is destroyed ; ;********************************************************************** xmit movlw NUMBITS ; Set bitcount to number of bits to transmit movwf bitcount movlw DATALO ; Send start bit ;********* ; This loop controls the baud rate for the serial transmission. The entire ; loop should last 104.17us (104 instruction clocks at 4MHz). bitdelay is ; initialised to provide the extra delay needed to achieve this. Calibrate ; mode allows the user to adjust the bit timing by modifying bitadjust. ;********* nextbit tris GPIO ; This instruction changes the O/P bit state call bitdelay ; Send one bit time bsf STATUS,C ; Set the carry bit (this will set the STOP bit) rrf xmitchar,f ; Put next bit in carry movlw DATAHI ; Assume it's HI btfss STATUS,C ; Test carry bit movlw DATALO ; ...set data LO if necessary decfsz bitcount,f ; Have we sent all data? goto nextbit ; NO: Send the next bit ; End of timing loop retlw 0 page ;********************************************************************** ; INIT ; ; Start of program proper. Initialise ports, variables, etc. ; TRIS is set by any reset, so all pins are Hi-Z ; GPIO retains its settings for all except POR ; ;********************************************************************** init movlw SETOPTWL ; Set OPTION register, assume long WDT needed option ; ... so don't worry about WDT while debouncing ; These two instructions could go in the POR code, since GPIO survives resets ; due to WDT or pin changes. However, should bit 2 be set to 1 somehow (cosmic ; rays?), it could damage the Archos, so we'll leave it here..... movlw GPIOLO ; Set O/Ps low to enable "open drain" operation movwf GPIO btfss STATUS,NOT_PD ; Test to see if this is a POR goto notpor ; NO: Must be a WDT timeout or input change ;********** ; YES: Power-on reset unique code clrf remstat ; Clear the status register (normal, no repeat) movlw STATMASK ; Get mask for both banks of switches movwf buttons ; Set no buttons pressed movlw BITTIME ; Get the adjustment to set 104 clocks bitwidth movwf bitadjust ; Set it goto ipchng ; Go to button handling code ;********** ; WDT or input change wakeup notpor btfsc STATUS,NOT_TO ; Test to see if this is a WDT timeout goto ipchng ; NO: Must be an input change ;********** ; YES: WDT time-out. btfsc remstat,S_REPEAT ; Test if we're repeating a button press goto repeat ; YES: Short (repeat) WDT timeout ;********** ; NO: Go back to sleep (OPTION is set to SETOPTWL at reset ; ; Also used as an entry point for going to sleep after button decooding ; - OPTION reg determines time for next WDT wakeup ; - SETOPTWL=2.3s, SETOPTWS=144ms goodnight movlw GP45LO ; We want switches to be active while asleep tris GPIO ; ...so set outputs active low movf GPIO,w ; Read port before sleeping (as per Microchip) sleep ; ... and goodbye! ;********** ; We arrive here if WDT wakes us up and a key has been held pressed repeat decfsz rptdelay,f ; Decrement the initial delay counter goto shortsleep ; Delay not complete, go back to sleep incf rptdelay,f ; Bump the delay counter for next repeat ;********** ; At this point, a pin change has occurred or we are repeating a button ipchng movlw 1 ; Set debounce timer=1, so we don't debounce movwf dbnctmr ; ... if we're repeating a code page ;****************************************************************************** ; Now we check the button status, and repeat this loop until there has been ; no change in button state for 10ms. ; ; NOTE: The timing of this loop is important, since it is used as the basis ; for debounce timing. Be careful when modifying the path taken when ; the buttons haven't changed - DEBCNT will need to be modified. ;****************************************************************************** dbncloop call readbtns ; [35] Read the button status movf btnstat,w ; [36] Get current button state subwf buttons,f ; [37] Compare with last reading movwf buttons ; [38] Save new value, we only need Z flag btfsc STATUS,Z ; [39] Have they changed? goto nobtnchg ; [41] NO: Need to see if debounced movlw DEBCNT ; YES: Start debounce timer again movwf dbnctmr movlw RPTDLY ; Set repeat delay movwf rptdelay goto dbncloop ; ...and go back to read buttons again ; Buttons didn't change this time around, decrement timer nobtnchg decfsz dbnctmr,f ; [42] Decrement debounce timer, test if done goto dbncloop ; [44] NO: Go back and read buttons again ;*********** ; We have a new debounced button reading, so we need to decide what to do. call getcommand ; Convert the buttons to a command code ; Now we have a 4-bit code that corresponds to button presses btfss remstat,S_CALIB ; Are we in calibrate mode goto fntable ; NO: Execute the command requested by the user goto caltable ; YES: We're now in calibration mode ;********** ; Calibration mode routines ; ; These are the routines called from caltable when in calibration mode ; ; Reset baud rate to nominal value rst2nom movlw BITTIME ; Get the adjustment to set 104 clocks bitwidth movwf bitadjust ; Set it goto xtndsend ; Increase baud rate - decrease bitadjust incbaud movlw LLIMIT ; Get the lower limit for bitadjust subwf bitadjust,w ; Compare with current value btfss STATUS,Z ; Test if at lower limit decf bitadjust,f ; NO: Decrement bitadjust to increase baud rate goto xtndsend ; ... and send the next test code (extend seq) ; Decrease baud rate - increase bitadjust decbaud movlw ULIMIT ; Get the upper limit for bitadjust subwf bitadjust,w ; Compare with current value btfss STATUS,Z ; Test if at upper limit incf bitadjust,f ; NO: Increment bitadjust to decrease baud rate ; ... and send the next test code (extend seq) ; This entry point extends the sequence of transmitted test characters. ; Used when user adjusts the baud rate or holds the calib mode buttons xtndsend bsf testctr,SETTST ; Set hi bit of test counter (extend sequence) sendtest btfsc STATUS,GPWUF ; Did we wake because of a button press? goto shortsleep ; YES: don't send next char yet movf testcode,w ; Get the test code (Voldn or Volup) movwf xmitchar ; Set the code to transmit call xmit ; Send it decfsz testctr,f ; Have we sent the last test code? goto $+2 ; NO: Need to see if time to change test code goto longsleep ; YES: Exit calibrate mode ; Now we check if we need to switch between sending VOLDN or VOLUP commands movlw TSTMASK ; Load mask for lower two bits andwf testctr,w ; See if we're at a multiple of 4 movlw 0 ; Assume not (doesn't affect Z flag) btfsc STATUS,Z ; Perform the test movlw C_VOLUP^C_VOLDN ; YES: Get mask to flip between volup/voldn xorwf testcode,f ; Flip the bits calsleep movlw SETOPTWM ; Set medium repeat for test chars goto setsleep ; Now go back to sleep for next test char/button page ;********** ; Normal mode routines ; ; These are the routines called from fntable when in normal mode ;********* ; Send just one code to the Archos, even if buttons are held down ; OPTION register and status set for a long sleep, no repeat ;********* send_one movlw SETOPTWL ; Set OPTION register for long WDT option bcf remstat,S_REPEAT ; Clear the repeat flag goto sendchar ;********* ; Set things up to repeat codes if buttons are held down. ; OPTION register and status set for a short sleep, repeat. ;********* send_rep movlw SETOPTWS ; YES: We're done, set short delay for WDT option ; ... to allow button repeat bsf remstat,S_REPEAT ; Set the repeat flag sendchar ; Transmit the code call getcode ; Returns code to xmit to Archos movwf xmitchar ; Save it call xmit ; Code has been sent, go to sleep and wait for repeat or next button change. ; We don't have to go through shortsleep or longsleep as already set at send_one ; or send_rep. goto goodnight ;******** ; The user has pressed the buttons to enter calibrate mode ;******** usercalib btfsc remstat,S_CALDLY ; Are we already waiting to enter cal mode? goto calwait ; YES: Jump to delay code bsf remstat,S_CALDLY ; NO: Set calibrate delay flag movlw C_VOLDN ; Store the code for Voldn movwf testcode ; ....as the initial test code. movlw CALDLY ; Set delay time to enter calibrate mode movwf caldelay movlw 1<