;************************************************************************** ; FILE: picworks2.asm * ; CONTENTS: PICworks2 * ; COPYRIGHT: MadLab Ltd. 2006 * ; AUTHOR: James Hutchby * ; UPDATED: 19/06/06 * ;************************************************************************** list p=16F648A ; list p=16F628A ifdef __16F648A include "p16f648a.inc" endif ifdef __16F628A include "p16f628a.inc" endif __config _HS_OSC & _WDT_ON & _PWRTE_ON & _BOREN_OFF & _MCLRE_OFF & _LVP_OFF & _CP_OFF __idlocs h'FF10' errorlevel -205,-207,-302,-305,-306 ;************************************************************************** ; * ; Specification * ; * ;************************************************************************** ; power-up self-test - beeps twice ; supply voltage = 6V - 12V (typically 6 x AA, not 4 x NiCd/NiMH) ; H-bridge: 2 channels, 600mA maximum output current per channel, ~50mA quiescent current ; LEDs: ; connect LEDs back-to-back in pairs ; LED1 - anode to LO1, cathode to LO2 ; LED2 - anode to LO2, cathode to LO1 ; LED3 - anode to LO3, cathode to LO4 ; LED4 - anode to LO4, cathode to LO3 ; 100R in series with LO1 and LO3 (15mA or 30mA if not multiplexed) ; can drive outputs without series resitor on LO2 and LO4, use external resistor to limit current ; note: if LO1 or LO2 used then PRESET should be turned fully clockwise ; LO3 and LO4 could be reconfigured as inputs (with weak pull-ups) but not supported by software ; 2 x dc motors (bi-directional): ; MOTOR1 - connect to HI1 & HI2 ; MOTOR2 - connect to HI3 & HI4 ; 100n ceramic capacitor across motor tags to reduce noise ; note: motors driven at supply voltage rather than regulated 5V ; could drive 4 uni-directional motors connected to HI1 & GND, HI2 & GND etc. ; 2 x servo motors: ; SERVO1 - power (+ve) to HI2, ground (-ve) to GND, control to HI1 ; SERVO2 - power (+ve) to HI4, ground (-ve) to GND, control to HI3 ; note: servos driven at supply voltage rather than regulated 5V ; an additional 2 servos could be driven from HI2 and HI4 if the servos were powered externally ; 1 x bipolar stepper motor: ; STEPPER - connect coil 1 to HI1 & HI2 and coil 2 to HI4 & HI3 ; note: stepper driven at supply voltage rather than regulated 5V ; unipolar stepper can be driven by grounding or not connecting centre taps ; standard sequence: 1A -> 2A -> 1B -> 2B ; high-torque sequence: 1A+2A -> 2A+1B -> 1B+2B -> 2B+1A ; half-step sequence: 1A+2A -> 2A -> 2A+1B -> 1B -> 1B+2B -> 2B -> 2B+1A -> 1A ; physical limit to maximum stepper speed ; analogue sensors: ; LDR light sensor and PRESET rotary control return values 0 to 255 ; LDR value increases with dark, PRESET value increases clockwise ; note: LO1 and LO2 can't be connected if LDR or PRESET used ; moving-coil speaker: ; 2-channel tune player (converted MIDI file) ; outputs disabled and keyboard not polled while tune playing ; keyswitch: ; could replace PCB keyswitch with external switch ; settings entered using digit keys, terminated with ENTER key ; ENTER by itself selects without altering setting ; double beep when ENTER pressed ; F1 -> set MOTOR1 maximum speed (1 to 64) ; shift F1 -> set MOTOR2 maximum speed (1 to 64) ; F2 -> set SERVO1 minimum pulse length (1 to 250) ; shift F2 -> set SERVO2 minimum pulse length (1 to 250) ; F3 -> set SERVO1 maximum pulse length (1 to 250) ; shift F3 -> set SERVO2 maximum pulse length (1 to 250) ; F4 -> set SERVO1 tracking speed (1 to 127) ; shift F4 -> set SERVO2 tracking speed (1 to 127) ; F5 -> set STEPPER maximum speed (1 to 127) (NB not linear) ; F6,1 -> standard stepper sequence ; F6,2 -> high-torque stepper sequence (higher power) ; F6,3 -> half-step stepper sequence (lower speed) ; F7 -> set light threshold (1 to 255) then echo to keyboard LEDs ; F8,1 -> PRESET analogue sensor ; F8,2 -> motor/servo/stepper speed controlled by PRESET ; F9 -> set PRESET threshold (1 to 255) then echo to keyboard LEDs ; F11 -> echo LDR to keyboard LEDs (3 most significant bits) ; F12 -> echo PRESET to keyboard LEDs (3 most significant bits) ; any key exits echo modes ; real-time live control, real-time recording and playback ; shift R -> record sequence of events ; P or ENTER -> play recorded sequence of events ; if keyboard not detected on power-up then plays recorded sequence ; ESC -> abort ; S -> stop ; L -> loop to start of sequence ; 1 -> toggle LED1 ; 2 -> toggle LED2 ; 3 -> toggle LED3 ; 4 -> toggle LED4 ; RIGHT ARROW -> toggle MOTOR1/SERVO1/STEPPER forward ; LEFT ARROW -> toggle MOTOR1/SERVO1/STEPPER reverse ; UP ARROW -> toggle MOTOR2/SERVO2/STEPPER forward ; DOWN ARROW -> toggle MOTOR2/SERVO2/STEPPER reverse ; K,0 -> wait for keyswitch off ; K,1 -> wait for keyswitch on ; A,0 -> wait for light off ; A,1 -> wait for light on ; A,2 -> wait for light change ; V,0 -> wait for PRESET off ; V,1 -> wait for PRESET on ; V,2 -> wait for PRESET change ; B -> beep ; T -> play tune ; stores ~120 events in EEPROM (2 or 3 bytes per event) ; time between events from 0 to 16383 seconds with resolution of 1/16s ; (time between events greater than 63 seconds use 3 bytes) ; setting changes not stored in sequences ;************************************************************************** ; * ; Port assignments * ; * ;************************************************************************** PORTA_IO equ b'11100000' ; port A I/O status PORTB_IO equ b'11000000' ; port B I/O status ; low current outputs #define LO1 PORTA,3 ; LO1 #define LO2 PORTA,2 ; LO2 #define LO3 PORTA,1 ; LO3 #define LO4 PORTA,0 ; LO4 ; high current outputs #define HI1 PORTB,3 ; HI1 #define HI2 PORTB,2 ; HI2 #define HI3 PORTB,5 ; HI3 #define HI4 PORTB,4 ; HI4 #define SPEAKER PORTB ; speaker #define SPK1 0 #define SPK2 1 #define LDR PORTA,3 ; LDR #define PRESET PORTA,2 ; PRESET #define CAP PORTA,4 ; capacitor #define KEYSWITCH PORTA,5 ; keyswitch #define KBD_CLK PORTB,7 ; keyboard clock #define KBD_DATA PORTB,6 ; keyboard data #define STEPPER_PORT PORTB STEPPER_1A equ 1<<3 ; HI1 STEPPER_1B equ 1<<2 ; HI2 STEPPER_2A equ 1<<4 ; HI4 STEPPER_2B equ 1<<5 ; HI3 kbd macro c,d if c == 0 && d == 0 movlw PORTB_IO&(~(1<<7))&(~(1<<6)) bcf KBD_CLK bcf KBD_DATA endif if c == 0 && d == 1 movlw PORTB_IO&(~(1<<7)) bcf KBD_CLK endif if c == 1 && d == 0 movlw PORTB_IO&(~(1<<6)) bcf KBD_DATA endif if c == 1 && d == 1 movlw PORTB_IO endif tris_B endm ; charge the capacitor through the LDR charge_ldr macro movlw PORTA_IO|(1<<2)|(1<<4) tris_A bsf LDR endm ; charge the capacitor through the PRESET charge_preset macro movlw PORTA_IO|(1<<3)|(1<<4) tris_A bsf PRESET endm ; discharge the capacitor discharge macro bcf LDR bcf PRESET bcf CAP movlw PORTA_IO&(~(1<<4)) tris_A endm ;************************************************************************** ; * ; Constants and timings * ; * ;************************************************************************** ; processor clock frequency in Hz CLOCK equ d'20000000' ; magic number MAGIC equ d'42' ; keyboard poll delay in ms POLL equ d'40' ; number of motor pwm steps PWM_STEPS equ d'64' ; motor absolute and default maximum speed in 1/64's of supply voltage MOTOR_MAX equ d'64' MOTOR_DEF equ MOTOR_MAX/2 ; motor acceleration mask MOTOR_ACCEL equ h'3f' ; servo nominal minimum and maximum pulse lengths in us (centred at 1.5ms) ; (normally minimum = 90 degrees anti-clockwise, maximum = 90 degrees clockwise) SERVO_MIN equ d'1000' SERVO_MAX equ d'2000' ; servo maximum and default tracking speed (maximum = 1/10s full travel at nominal pulse limits) SERVO_SPEED equ d'127' SERVO_DEF equ d'2'<<3 ; servo pulse resolution in us SERVO_RES equ d'10' ; absolute lower and upper limits SERVO_FLOOR equ d'500'/SERVO_RES SERVO_CEILING equ d'2500'/SERVO_RES if SERVO_MIN/SERVO_RES < SERVO_FLOOR error "Servo minimum pulse length < lower limit (0.5ms)" endif if SERVO_MAX/SERVO_RES > SERVO_CEILING error "Servo maximum pulse length > upper limit (2.5ms)" endif ; stepper absolute and default maximum speed (minimum delay of 1ms per cycle) STEPPER_MAX equ d'127' STEPPER_DEF equ d'50' ; light and PRESET thresholds THRESHOLD1_DEF equ h'7f' THRESHOLD2_DEF equ h'7f' ; capacitor discharge period in ms DISCHARGE equ d'1' ; events cblock 0 EVENT_STOP ; stop EVENT_LOOP ; loop to start EVENT_LED1 ; toggle LED1 EVENT_LED2 ; toggle LED2 EVENT_LED3 ; toggle LED3 EVENT_LED4 ; toggle LED4 EVENT_FORWARD1 ; toggle MOTOR1/SERVO1/STEPPER forward EVENT_REVERSE1 ; toggle MOTOR1/SERVO1/STEPPER reverse EVENT_FORWARD2 ; toggle MOTOR2/SERVO2/STEPPER forward EVENT_REVERSE2 ; toggle MOTOR2/SERVO2/STEPPER reverse EVENT_KEYSWITCH_OFF ; wait for keyswitch off EVENT_KEYSWITCH_ON ; wait for keyswitch on EVENT_LDR_OFF ; wait for light off EVENT_LDR_ON ; wait for light on EVENT_LDR_CHANGE ; wait for light change EVENT_PRESET_OFF ; wait for PRESET off EVENT_PRESET_ON ; wait for PRESET on EVENT_PRESET_CHANGE ; wait for PRESET change EVENT_BEEP ; beep EVENT_TUNE ; play tune endc ;-------------------------------------------------------------------------- ; keyboard codes ;-------------------------------------------------------------------------- ; keyboard command codes KBD_BAT equ h'AA' ; Basic Assurance Test successful KBD_ERROR equ h'FC' ; keyboard error KBD_EXTENDED equ h'E0' ; extended key code (+h'80' in scan code) KBD_LEDS equ h'ED' ; set/reset LEDs (0 = scroll, 1 = num, 2 = caps) KBD_BREAK equ h'F0' ; key break code KBD_TYPEMATIC equ h'F3' ; set typematic rate and delay (h'7f' = slowest) KBD_ACK equ h'FA' ; acknowledge KBD_RESEND equ h'FE' ; resend last byte KBD_RESET equ h'FF' ; reset keyboard ; keyboard scan codes KBD_A equ h'1C' KBD_B equ h'32' KBD_C equ h'21' KBD_D equ h'23' KBD_E equ h'24' KBD_F equ h'2B' KBD_G equ h'34' KBD_H equ h'33' KBD_I equ h'43' KBD_J equ h'3B' KBD_K equ h'42' KBD_L equ h'4B' KBD_M equ h'3A' KBD_N equ h'31' KBD_O equ h'44' KBD_P equ h'4D' KBD_Q equ h'15' KBD_R equ h'2D' KBD_S equ h'1B' KBD_T equ h'2C' KBD_U equ h'3C' KBD_V equ h'2A' KBD_W equ h'1D' KBD_X equ h'22' KBD_Y equ h'35' KBD_Z equ h'1A' KBD_0 equ h'45' KBD_1 equ h'16' KBD_2 equ h'1E' KBD_3 equ h'26' KBD_4 equ h'25' KBD_5 equ h'2E' KBD_6 equ h'36' KBD_7 equ h'3D' KBD_8 equ h'3E' KBD_9 equ h'46' KBD_QUOTE equ h'0E' KBD_MINUS equ h'4E' KBD_EQUALS equ h'55' KBD_BACKSLASH equ h'5D' KBD_BACKSPACE equ h'66' KBD_SPACE equ h'29' KBD_TAB equ h'0D' KBD_CAPS_LOCK equ h'58' KBD_LEFT_SHFT equ h'12' KBD_LEFT_CTRL equ h'14' KBD_LEFT_GUI equ h'1F'+h'80' KBD_LEFT_ALT equ h'11' KBD_RIGHT_SHFT equ h'59' KBD_RIGHT_CTRL equ h'14'+h'80' KBD_RIGHT_GUI equ h'27'+h'80' KBD_RIGHT_ALT equ h'11'+h'80' KBD_APPS equ h'2F'+h'80' KBD_ENTER equ h'5A' KBD_ESC equ h'76' KBD_F1 equ h'05' KBD_F2 equ h'06' KBD_F3 equ h'04' KBD_F4 equ h'0C' KBD_F5 equ h'03' KBD_F6 equ h'0B' KBD_F7 equ h'83' KBD_F8 equ h'0A' KBD_F9 equ h'01' KBD_F10 equ h'09' KBD_F11 equ h'78' KBD_F12 equ h'07' KBD_PRNT_SCRN1 equ h'12'+h'80' ; make = E0,12,E0,7C; break = E0,F0,7C,E0,F0,12 KBD_PRNT_SCRN2 equ h'7C'+h'80' KBD_SCROLL_LOCK equ h'7E' KBD_PAUSE equ h'E1' ; make = E1,14,77,E1,F0,14,F0,77; break = none KBD_OPEN_SQUARE equ h'54' KBD_INSERT equ h'70'+h'80' KBD_HOME equ h'6C'+h'80' KBD_PAGE_UP equ h'7D'+h'80' KBD_DELETE equ h'71'+h'80' KBD_END equ h'69'+h'80' KBD_PAGE_DOWN equ h'7A'+h'80' KBD_UP_ARROW equ h'75'+h'80' KBD_LEFT_ARROW equ h'6B'+h'80' KBD_DOWN_ARROW equ h'72'+h'80' KBD_RIGHT_ARROW equ h'74'+h'80' KBD_NUM_LOCK equ h'77' KBD_KP_SLASH equ h'4A'+h'80' KBD_KP_ASTERISK equ h'7C' KBD_KP_MINUS equ h'7B' KBD_KP_PLUS equ h'79' KBD_KP_ENTER equ h'5A'+h'80' KBD_KP_DOT equ h'71' KBD_KP_0 equ h'70' KBD_KP_1 equ h'69' KBD_KP_2 equ h'72' KBD_KP_3 equ h'7A' KBD_KP_4 equ h'6B' KBD_KP_5 equ h'73' KBD_KP_6 equ h'74' KBD_KP_7 equ h'6C' KBD_KP_8 equ h'75' KBD_KP_9 equ h'7D' KBD_CLOSE_SQUARE equ h'5B' KBD_SEMICOLON equ h'4C' KBD_APOSTROPHE equ h'52' KBD_COMMA equ h'41' KBD_DOT equ h'49' KBD_SLASH equ h'4A' ; ACPI KDB_POWER equ h'37'+h'80' KBD_SLEEP equ h'3F'+h'80' KBD_WAKE equ h'5E'+h'80' ; Windows multimedia KDB_NEXT_TRACK equ h'4D'+h'80' KDB_PREV_TRACK equ h'15'+h'80' KDB_STOP equ h'3B'+h'80' KDB_PLAY_PAUSE equ h'34'+h'80' KDB_MUTE equ h'23'+h'80' KDB_VOLUME_UP equ h'32'+h'80' KDB_VOLUME_DOWN equ h'21'+h'80' KDB_MEDIA_SELECT equ h'50'+h'80' KDB_EMAIL equ h'48'+h'80' KDB_CALCULATOR equ h'2B'+h'80' KDB_MY_COMPUTER equ h'40'+h'80' KDB_WWW_SEARCH equ h'10'+h'80' KDB_WWW_HOME equ h'3A'+h'80' KDB_WWW_BACK equ h'38'+h'80' KDB_WWW_FORWARD equ h'30'+h'80' KDB_WWW_STOP equ h'28'+h'80' KDB_WWW_REFRESH equ h'20'+h'80' KDB_WWW_FAVOURITES equ h'18'+h'80' KBD_QUESTION equ KBD_SLASH ;-------------------------------------------------------------------------- ; conditional assembly flags to allow code footprint to be reduced by ; removing hardware support which is not required ;-------------------------------------------------------------------------- _LEDS equ 1 ; non-zero to include LED multiplexing _MOTORS equ 1 ; non-zero to include dc motor control _SERVOS equ 1 ; non-zero to include servo motor control _STEPPER equ 1 ; non-zero to include stepper motor control _ANALOGUE equ 1 ; non-zero to include LDR/PRESET sampling _PS2 equ 1 ; non-zero to include PS/2 keyboard support _TUNES equ 1 ; non-zero to include tune player ;************************************************************************** ; * ; File register usage * ; * ;************************************************************************** RAM set h'20' MAX set h'80' cblock RAM context:3 ; saved context ticks ; clock ticks (250us period) seconds:2, beats ; real-time clock, seconds and 1/16s lo_flags ; low current output flags hi_flags ; high current output flags temp1, temp2 ; isr work registers motor1_max ; MOTOR1 maximum speed (1 to 64) motor2_max ; MOTOR2 maximum speed (1 to 64) motor1_speed ; MOTOR1 and MOTOR2 speeds (pwm duty cycles), motor2_speed ; -64 (fast reverse) to 0 (off) to 64 (fast forward) servo1_min ; SERVO1 minimum pulse length (in 10us) servo2_min ; SERVO2 minimum pulse length (in 10us) servo1_max ; SERVO1 maximum pulse length (in 10us) servo2_max ; SERVO2 maximum pulse length (in 10us) servo1_steps ; SERVO1 number of steps servo2_steps ; SERVO2 number of steps servo1_speed ; SERVO1 tracking speed (1 to 127) servo2_speed ; SERVO2 tracking speed (1 to 127) servo1_position:2 ; SERVO1 position, 0 (left) to servo1_steps-1 (right) servo2_position:2 ; SERVO2 position, 0 (left) to servo2_steps-1 (right) servo1_offset ; SERVO1 position offset, -ve (left) to 0 (stopped) to +ve (right) servo2_offset ; SERVO2 position offset, -ve (left) to 0 (stopped) to +ve (right) stepper_max ; STEPPER maximum speed (1 to 127) stepper_speed ; STEPPER speed, -127 (fast reverse) to 0 (stopped) stepper_timer ; to +127 (fast forward) stepper_sequence ; STEPPER sequence (1 to 3) stepper_position ; STEPPER sequence position (0 to 3) pnt ; tune pointer note ; tune note timer:3 ; tune timer timer_A, timer_B ; note timers period_A, period_B ; note periods spk_flags, spk_mask ; speaker flags and mask kbd_key ; key pressed, 0 if none kbd_flags ; keyboard flags kbd_code ; keyboard code kbd_command ; keyboard command kbd_leds ; keyboard LEDs kbd_ignore ; number of scan codes to ignore flags ; various flags magic ; magic number bits ; bits count setting ; entered setting threshold1 ; light threshold (0 to 255) threshold2 ; PRESET threshold (0 to 255) sensor ; sensor reading preset_mode ; PRESET mode (1 or 2) event ; current event seconds_:2, beats_ ; event seconds and 1/16s timeout ; timeout addr ; EEPROM address rand:2 ; random number ndx ; index count, loop, repeat ; scratch counters work1, work2 ; work registers RAM_ endc if RAM_ > MAX error "File register usage overflow" endif ; flags KEYBOARD equ 0 ; set if keyboard found RESET equ 1 ; set if keyboard reset BEEP equ 2 ; set if beeping RECORD equ 3 ; set if recording in progress ; keyboard flags LEFT equ 0 ; set if left arrow key pressed RIGHT equ 1 ; set if right arrow key pressed FORWARD equ 2 ; set if up arrow key pressed BACKWARD equ 3 ; set if down arrow key pressed EXTENDED equ 4 ; set if extended key code received BREAK equ 5 ; set if key break code received SHIFT1 equ 6 ; set if left shift key pressed SHIFT2 equ 7 ; set if right shift key pressed SHIFT_MASK equ (1< d'64' call delay64 ; [64] i -= d'64' endw if i >= d'16' variable n = (i-d'16')/d'4' call delay16-n ; [8+4n+8] i -= (n*d'4')+d'16' endif while i > 0 nop ; [4] i -= d'4' endw endm ;-------------------------------------------------------------------------- ; waits for the number of ms in w reg ;-------------------------------------------------------------------------- routine wait_ms movwf count WAIT equ CLOCK/(d'1000'*d'80') wait1 movlf WAIT,loop wait2 clrwdt ; [4] delay d'64' ; [64] decfsz loop ; [4] goto wait2 ; [8] decfsz count goto wait1 return ;************************************************************************** ; PS/2 keyboard routines * ;************************************************************************** if _PS2 ;-------------------------------------------------------------------------- ; waits for a clock transition from the keyboard, returns the Z flag set ; if timeout ;-------------------------------------------------------------------------- routine wait_kbd ; timeout period in ms TIMEOUT equ (d'16'*d'256'*d'24')/(CLOCK/d'1000') movlw d'16' ; wait for clock to go high clrf timeout wait3 clrwdt ; [4] btfsc KBD_CLK ; [8] goto wait4 decfsz timeout ; [4] goto wait3 ; [8] addlw -1 bnz wait3 goto wait7 ; timeout wait4 movlw d'16' ; wait for clock to go low clrf timeout wait5 clrwdt ; [4] btfss KBD_CLK ; [8] goto wait6 decfsz timeout ; [4] goto wait5 ; [8] addlw -1 bnz wait5 goto wait7 ; timeout wait6 delay d'10'*(CLOCK/d'1000000') clrz ; signal ok wait7 return ;-------------------------------------------------------------------------- ; tests if the keyboard is ready to transmit a byte, returns the NZ flag ; set if yes ;-------------------------------------------------------------------------- routine test_kbd ; test period in ms TEST equ (d'10'*d'256'*d'24')/(CLOCK/d'1000') movlw d'10' ; wait for clock to go low clrf timeout testk1 clrwdt ; [4] btfss KBD_CLK ; [8] goto testk2 decfsz timeout ; [4] goto testk1 ; [8] addlw -1 bnz testk1 goto testk3 ; timeout testk2 delay d'10'*(CLOCK/d'1000000') clrz ; signal ready testk3 return ;-------------------------------------------------------------------------- ; transmits the byte in w reg to the keyboard, returns the Z flag set if ; timeout ;-------------------------------------------------------------------------- routine tx_kbd movwf kbd_code bcf INTCON,GIE ; interrupts off kbd 0,1 ; request to send movlw 1 call wait_ms kbd 0,0 ; start bit nop kbd 1,0 call wait_kbd bz tx6 ; branch if timeout movlf d'8',count clrf bits tx1 btfsc kbd_code,0 incf bits rrf kbd_code ; transmit data bit skpnc bsf KBD_DATA skpc bcf KBD_DATA call wait_kbd bz tx6 ; branch if timeout decfsz count goto tx1 btfss bits,0 ; parity bit (odd parity) bsf KBD_DATA btfsc bits,0 bcf KBD_DATA call wait_kbd bz tx6 ; branch if timeout kbd 1,1 ; stop bit clrf timeout ; wait for acknowledge bit tx2 clrwdt decf timeout bz tx6 ; branch if timeout btfsc KBD_DATA goto tx2 clrf timeout tx3 clrwdt decf timeout bz tx6 ; branch if timeout btfsc KBD_CLK goto tx3 clrf timeout tx4 clrwdt decf timeout bz tx6 ; branch if timeout btfss KBD_DATA goto tx4 clrf timeout tx5 clrwdt decf timeout bz tx6 ; branch if timeout btfss KBD_CLK goto tx5 clrz ; signal ok goto tx7 tx6 kbd 1,1 setz ; signal timeout tx7 bsf INTCON,GIE ; interrupts on return ;-------------------------------------------------------------------------- ; receives a byte from the keyboard into w reg (and kbd_code), returns ; the Z flag set if timeout and the C flag set if error ;-------------------------------------------------------------------------- routine get_kbd bcf INTCON,GIE ; interrupts off kbd 1,1 call test_kbd ; keyboard ready to transmit ? bz rx4 ; branch if not routine rx_kbd bcf INTCON,GIE ; interrupts off btfsc KBD_DATA ; test start bit goto rx5 ; branch if error movlf d'8',count clrf bits rx1 call wait_kbd bz rx4 ; branch if timeout clrc ; receive data bit btfsc KBD_DATA setc rrf kbd_code btfsc kbd_code,7 incf bits decfsz count goto rx1 call wait_kbd bz rx4 ; branch if timeout btfsc KBD_DATA ; test parity bit incf bits btfss bits,0 goto rx5 ; branch if error call wait_kbd bz rx4 ; branch if timeout btfss KBD_DATA ; test stop bit goto rx5 ; branch if error clrf timeout ; wait for clock to go high rx2 clrwdt btfsc KBD_CLK goto rx3 decfsz timeout goto rx2 goto rx4 ; timeout rx3 clrz ; signal ok clrc goto rx6 rx4 clrf kbd_code setz ; signal timeout clrc goto rx6 rx5 clrf kbd_code clrz ; signal error setc rx6 kbd 0,1 swapf kbd_code swapf kbd_code,w swapf kbd_code ; w <= byte bsf INTCON,GIE ; interrupts on return ;-------------------------------------------------------------------------- ; polls the keyboard ;-------------------------------------------------------------------------- routine poll_kbd clrf kbd_key call get_kbd ; get keyboard byte bz pollk8 ; branch if timeout bc pollk7 ; branch if error tstf kbd_ignore ; ignore scan codes ? bz pollk1 ; branch if not decf kbd_ignore goto pollk7 pollk1 movlw KBD_PAUSE ; pause key ? subwf kbd_code,w bnz pollk2 ; branch if not movlf d'7',kbd_ignore ; ignore the next scan codes goto pollk7 pollk2 movlw KBD_EXTENDED ; extended key code ? subwf kbd_code,w bnz pollk3 ; branch if not bsf kbd_flags,EXTENDED ; signal received goto pollk8 pollk3 movlw KBD_BREAK ; key break code ? subwf kbd_code,w bnz pollk4 ; branch if not bsf kbd_flags,BREAK ; signal received goto pollk8 pollk4 btfsc kbd_flags,EXTENDED ; extended key ? goto pollk5 ; branch if yes key_ macro scan,bit local key1 movlw (scan)&h'7f' ; key ? subwf kbd_code,w bnz key1 ; branch if not bsf kbd_flags,bit ; set flag if pressed btfsc kbd_flags,BREAK bcf kbd_flags,bit ; clear flag if released goto pollk6 key1 endm key_ KBD_LEFT_SHFT,SHIFT1 ; test shift keys key_ KBD_RIGHT_SHFT,SHIFT2 goto pollk6 pollk5 key_ KBD_LEFT_ARROW,LEFT ; test arrow keys key_ KBD_RIGHT_ARROW,RIGHT key_ KBD_UP_ARROW,FORWARD key_ KBD_DOWN_ARROW,BACKWARD pollk6 btfsc kbd_flags,BREAK ; key break ? goto pollk7 ; branch if yes movff kbd_code,kbd_key ; store key pressed btfsc kbd_flags,EXTENDED bsf kbd_key,7 ; extended scan code pollk7 bcf kbd_flags,EXTENDED bcf kbd_flags,BREAK pollk8 return ;-------------------------------------------------------------------------- ; transmits the command in w reg to the keyboard, returns the Z flag set ; if error ;-------------------------------------------------------------------------- routine tx_command movwf kbd_command txc1 movfw kbd_command ; transmit command call tx_kbd bz txc4 ; branch if timeout call get_kbd ; wait for acknowledge bz txc2 ; branch if timeout bc txc2 ; branch if error xorlw KBD_RESEND ; resend ? bz txc1 ; branch if yes xorlw KBD_RESEND ; acknowledge ? xorlw KBD_ACK bnz txc4 ; branch if not goto txc3 txc2 movlw KBD_RESEND ; transmit command call tx_kbd bz txc4 ; branch if timeout call get_kbd ; wait for acknowledge bz txc4 ; branch if timeout bc txc4 ; branch if error xorlw KBD_RESEND ; resend ? bz txc1 ; branch if yes xorlw KBD_RESEND ; acknowledge ? xorlw KBD_ACK bnz txc4 ; branch if not txc3 clrz ; signal ok return txc4 setz ; signal error return ;-------------------------------------------------------------------------- ; initialises the keyboard, returns the Z flag set if error ;-------------------------------------------------------------------------- routine initialise_keyboard bcf flags,RESET REPEAT equ d'1000'/TIMEOUT initk1 movlf REPEAT,repeat ; wait for keyboard BAT initk2 call get_kbd bz initk3 ; branch if timeout bc initk4 ; branch if error xorlw KBD_BAT bnz initk4 goto initk5 ; branch if ok initk3 decfsz repeat goto initk2 initk4 btfsc flags,RESET ; not received or error goto initk6 bsf flags,RESET movlw d'250' call wait_ms movlw d'250' call wait_ms movlw d'250' call wait_ms movlw d'250' call wait_ms movlw KBD_RESET ; reset keyboard call tx_command bz initk6 ; branch if error goto initk1 initk5 movlw KBD_TYPEMATIC ; set typematic to slowest call tx_command bz initk6 movlw h'7f' call tx_command bz initk6 ; branch if error clrz ; signal ok return initk6 setz ; signal error return ;-------------------------------------------------------------------------- ; inhibits communication from the keyboard ;-------------------------------------------------------------------------- routine inhibit_keyboard kbd 0,1 movlw 1 call wait_ms return ;-------------------------------------------------------------------------- ; releases communication from the keyboard ;-------------------------------------------------------------------------- routine release_keyboard kbd 1,1 ; delay d'75'*(CLOCK/d'1000000') return ;-------------------------------------------------------------------------- ; sets/resets the keyboard LEDs ;-------------------------------------------------------------------------- routine do_kbd_leds movlw KBD_LEDS call tx_command bz dokl1 movfw kbd_leds andlw 7 call tx_command dokl1 return ;-------------------------------------------------------------------------- ; gets a digit (0 to 9) from the keyboard, returns the digit in w reg, or ; returns -1 with the C flag set if no digit pressed ;-------------------------------------------------------------------------- routine get_digit getd1 movlw 1 ; wait for key press call wait_ms call poll_kbd tstf kbd_key bz getd1 clrf ndx getd2 movfw ndx index digit_tbl incf ndx tstw ; end of table ? bz getd3 ; branch if yes subwf kbd_key,w ; scan code found ? bnz getd2 ; branch if not decf ndx clrc rrf ndx,w ; w <= digit clrc return getd3 movlw -1 setc ; not digit return ;-------------------------------------------------------------------------- ; polls for and processes a key press ;-------------------------------------------------------------------------- routine do_key movlw 1 call wait_ms call poll_kbd ; poll keyboard tstf kbd_key bz dok6 ; branch if no key pressed clrf ndx dok1 movfw ndx index key_tbl incf ndx tstw ; end of table ? bz dok6 ; branch if yes subwf kbd_key,w ; scan code found ? bnz dok4 ; branch if not movfw ndx ; get shift status index key_tbl incf ndx tstw bnz dok2 movfw kbd_flags ; shift key must not be pressed andlw SHIFT_MASK bnz dok5 goto dok3 dok2 andlw h'80' bnz dok3 movfw kbd_flags ; shift key must be pressed andlw SHIFT_MASK bz dok5 dok3 clrf kbd_leds ; keyboard LEDs off call do_kbd_leds clrf kbd_key ; clear key movfw ndx ; execute associated routine jump key_tbl dok4 incf ndx dok5 incf ndx goto dok1 dok6 return ;-------------------------------------------------------------------------- ; tests for a key press, returns the NZ flag set if key pressed ;-------------------------------------------------------------------------- routine test_key movlw 1 call wait_ms call poll_kbd ; poll keyboard tstf kbd_key bz tstk1 ; branch if no key pressed clrf kbd_key ; clear key clrz ; signal key pressed tstk1 return endif ;************************************************************************** ; I/O routines * ;************************************************************************** ;-------------------------------------------------------------------------- ; turns all outputs off ;-------------------------------------------------------------------------- routine outputs_off if _LEDS clrf lo_flags ; LEDs off endif if _MOTORS clrf motor1_speed ; motors off clrf motor2_speed endif if _SERVOS clrc ; centre servos rrf servo1_steps,w movwf servo1_position clrf servo1_position+1 clrf servo1_offset clrc rrf servo2_steps,w movwf servo2_position clrf servo2_position+1 clrf servo2_offset endif if _STEPPER clrf stepper_speed ; stepper stopped clrf stepper_position clrf stepper_timer incf stepper_timer endif bcf LO1 bcf LO2 bcf LO3 bcf LO4 bcf HI1 bcf HI2 bcf HI3 bcf HI4 return ;-------------------------------------------------------------------------- ; LED multiplexing ;-------------------------------------------------------------------------- if _LEDS do_leds macro btfss LEDS ; multiplexing enabled ? goto dole2 ; branch if not btfsc ticks,0 goto dole1 bcf LO2 btfsc LED1 bsf LO1 btfss LED1 bcf LO1 bcf LO4 btfsc LED3 bsf LO3 btfss LED3 bcf LO3 goto dole2 dole1 bcf LO1 btfsc LED2 bsf LO2 btfss LED2 bcf LO2 bcf LO3 btfsc LED4 bsf LO4 btfss LED4 bcf LO4 dole2 endm endif ;-------------------------------------------------------------------------- ; dc motor pwm control ;-------------------------------------------------------------------------- if _MOTORS do_motors macro movfw ticks andlw h'3f' movwf temp1 clrf temp2 movfw ticks andlw MOTOR_ACCEL skpnz bsf temp2,0 btfss MOTOR1 ; MOTOR1 enabled ? goto domo2 ; branch if not btfsc motor1_speed,7 goto domo1 movfw temp2 ; accelerate tstf motor1_speed skpz addwf motor1_speed movfw motor1_max ; check speed within range subwf motor1_speed,w skpnc subwf motor1_speed bcf HI2 ; forward movfw motor1_speed subwf temp1,w skpnc bcf HI1 skpc bsf HI1 goto domo2 domo1 movfw temp2 ; accelerate tstf motor1_speed skpz subwf motor1_speed movfw motor1_max ; check speed within range negw subwf motor1_speed,w skpc subwf motor1_speed bcf HI1 ; reverse movfw motor1_speed negw subwf temp1,w skpnc bcf HI2 skpc bsf HI2 domo2 btfss MOTOR2 ; MOTOR2 enabled ? goto domo4 ; branch if not btfsc motor2_speed,7 goto domo3 movfw temp2 ; accelerate tstf motor2_speed skpz addwf motor2_speed movfw motor2_max ; check speed within range subwf motor2_speed,w skpnc subwf motor2_speed bcf HI4 ; forward movfw motor2_speed subwf temp1,w skpnc bcf HI3 skpc bsf HI3 goto domo4 domo3 movfw temp2 ; accelerate tstf motor2_speed skpz subwf motor2_speed movfw motor2_max ; check speed within range negw subwf motor2_speed,w skpc subwf motor2_speed bcf HI3 ; reverse movfw motor2_speed negw subwf temp1,w skpnc bcf HI4 skpc bsf HI4 domo4 endm endif ;-------------------------------------------------------------------------- ; servo motor control ;-------------------------------------------------------------------------- if _SERVOS do_servos macro btfsc flags,BEEP ; beeping ? goto dose6 ; branch if yes btfsc SERVO1 ; neither servo enabled ? goto dose1 btfss SERVO2 goto dose6 ; branch if yes dose1 comf ticks,w ; execute every 16ms andlw h'3f' bnz dose6 decf servo1_steps,w ; check positions within range subwf servo1_position,w skpnc subwf servo1_position decf servo2_steps,w subwf servo2_position,w skpnc subwf servo2_position movfw servo1_position ; determine control pulse lengths addwf servo1_min,w movwf temp1 movfw servo2_position addwf servo2_min,w movwf temp2 btfsc SERVO1 ; servo power bsf HI2 btfsc SERVO2 bsf HI4 btfsc SERVO1 ; output control pulses bsf HI1 btfsc SERVO2 bsf HI3 dose2 clrwdt ; [4] tstf temp1 ; [4] skpz ; [8/4] decf temp1 ; [0/4] btfss SERVO1 ; [8/4] clrz ; [0/4] skpnz ; [8/4] bcf HI1 ; [0/4] tstf temp2 ; [4] skpz ; [8/4] decf temp2 ; [0/4] btfss SERVO2 ; [8/4] clrz ; [0/4] skpnz ; [8/4] bcf HI3 ; [0/4] DELAY set (CLOCK/d'1000000')*SERVO_RES-d'80' delay DELAY movfw temp1 ; loop until both pulses finished [4] iorwf temp2,w ; [4] bnz dose2 ; [12] tstf servo1_offset ; SERVO1 stopped ? bz dose4 ; branch if yes movff servo1_offset,temp1 clrf temp2 rlf temp1,w rrf temp1 rrf temp2 rlf temp1,w rrf temp1 rrf temp2 rlf temp1,w rrf temp1 rrf temp2 movfw temp2 ; offset position addwf servo1_position+1 skpnc incf servo1_position movfw temp1 addwf servo1_position btfsc servo1_offset,7 goto dose3 movlw h'ff' skpnc movwf servo1_position decf servo1_steps,w ; check within range subwf servo1_position,w skpnc subwf servo1_position goto dose4 dose3 skpc clrf servo1_position dose4 tstf servo2_offset ; SERVO2 stopped ? bz dose6 ; branch if yes movff servo2_offset,temp1 clrf temp2 rlf temp1,w rrf temp1 rrf temp2 rlf temp1,w rrf temp1 rrf temp2 rlf temp1,w rrf temp1 rrf temp2 movfw temp2 ; offset position addwf servo2_position+1 skpnc incf servo2_position movfw temp1 addwf servo2_position btfsc servo2_offset,7 goto dose5 movlw h'ff' skpnc movwf servo2_position decf servo2_steps,w ; check within range subwf servo2_position,w skpnc subwf servo2_position goto dose6 dose5 skpc clrf servo2_position dose6 endm endif ;-------------------------------------------------------------------------- ; stepper motor control ;-------------------------------------------------------------------------- if _STEPPER do_stepper macro movfw ticks ; execute every 1ms andlw h'03' bnz dost1 btfss STEPPER ; stepper enabled ? goto dost1 ; branch if not decfsz stepper_timer goto dost1 movfw stepper_speed ; recharge timer btfsc stepper_speed,7 negw sublw d'128' movwf stepper_timer bcf HI1 bcf HI2 bcf HI3 bcf HI4 tstf stepper_speed ; stepper stopped ? bz dost1 ; branch if yes btfss stepper_speed,7 ; step sequence incf stepper_position btfsc stepper_speed,7 decf stepper_position movlw 7 andwf stepper_position decf stepper_sequence,w ; drive outputs movwf temp1 addwf temp1 rlf temp1 rlf temp1,w ; * 8 iorwf stepper_position,w index stepper_tbl iorwf STEPPER_PORT dost1 endm endif ;-------------------------------------------------------------------------- ; samples the LDR, returns the result in w reg (0 = light to 255 = dark) ;-------------------------------------------------------------------------- if _ANALOGUE routine get_ldr discharge ; discharge the capacitor movlw DISCHARGE call wait_ms clrwdt bcf INTCON,GIE ; interrupts off clrf count charge_ldr ; charge the capacitor getl1 incf count bz getl2 ; branch if timeout LDR_DELAY equ d'250' delay LDR_DELAY btfss CAP ; charged ? goto getl1 ; loop if not getl2 bsf INTCON,GIE ; interrupts on movlw PORTA_IO tris_A decf count,w return endif ;-------------------------------------------------------------------------- ; samples the PRESET, returns the result in w reg (0 = left to 255 = right) ;-------------------------------------------------------------------------- if _ANALOGUE routine get_preset discharge ; discharge the capacitor movlw DISCHARGE call wait_ms clrwdt bcf INTCON,GIE ; interrupts off clrf count charge_preset ; charge the capacitor getp1 incf count bz getp2 ; branch if timeout PRESET_DELAY equ d'42' delay PRESET_DELAY btfss CAP ; charged ? goto getp1 ; loop if not getp2 bsf INTCON,GIE ; interrupts on movlw PORTA_IO tris_A decf count,w return endif ;************************************************************************** ; executive routines * ;************************************************************************** ;-------------------------------------------------------------------------- ; runs the real-time clock ;-------------------------------------------------------------------------- do_rtc macro btfss PIR1,TMR1IF ; timer1 overflow ? goto rtc1 ; branch if not bcf PIR1,TMR1IF BEAT equ (CLOCK/4)/d'8'/d'16' ; 1/16s beat movlw low (-BEAT&h'ffff') ; recharge timer1 addwf TMR1L skpnc incf TMR1H movlw high (-BEAT&h'ffff') addwf TMR1H incf beats ; increment real-time clock clrw btfsc beats,4 movlw 1 bcf beats,4 addwf seconds+1 skpnc incf seconds+0 rtc1 endm ;-------------------------------------------------------------------------- ; resets the real-time clock ;-------------------------------------------------------------------------- routine reset_rtc bcf INTCON,GIE ; disable interrupts clrf seconds+0 ; zero clock clrf seconds+1 clrf beats movlw -1 movwf TMR1L movwf TMR1H return ;-------------------------------------------------------------------------- ; interrupt service routine (called every 250us) ;-------------------------------------------------------------------------- routine isr movwf context+0 ; save context swapf STATUS,w clrf STATUS ; bank 0 movwf context+1 movff PCLATH,context+2 clrf PCLATH ; page 0 movlw (1< 0 nop ; [4] n -= 1 endw decfsz timer+2 ; [4] goto playt3 ; [8] ; cycles per loop = 152 ; lowest C frequency = CLOCK/(255*2*152) = ~260Hz ; MIDI tick = (256*152)/CLOCK = ~2ms decf timer+1 skpnz decf timer+0 bnz playt3 playt4 get_tune ; get note movwf note andlw h'0f' ; note off ? bnz playt5 ; branch if not btfss note,7 ; disable channel bcf spk_mask,SPK1 btfsc note,7 bcf spk_mask,SPK2 goto playt8 playt5 movff PCLATH,work1 ; get note period movlf high note_tbl,PCLATH movfw note andlw h'0f' index note_tbl movwf work2 movff work1,PCLATH swapf note,w ; adjust for octave andlw h'07' bz playt7 movwf work1 playt6 clrc rrf work2 skpnc incf work2 ; round decfsz work1 goto playt6 playt7 movfw work2 btfss note,7 movwf period_A btfsc note,7 movwf period_B movlw 1 btfss note,7 movwf timer_A btfsc note,7 movwf timer_B btfss note,7 bcf spk_flags,SPK1 btfsc note,7 bsf spk_flags,SPK2 btfss note,7 ; enable channel bsf spk_mask,SPK1 btfsc note,7 bsf spk_mask,SPK2 playt8 goto playt1 playt9 bcf SPEAKER,SPK1 ; speaker off bcf SPEAKER,SPK2 return endif endif ;************************************************************************** ; EEPROM data * ;************************************************************************** org h'2100' ; high current output flags hi_flags_ de 0 ; motor maximum speeds motor1_max_ de MOTOR_DEF motor2_max_ de MOTOR_DEF ; servo minimum and maximum pulse lengths, and tracking speeds servo1_min_ de SERVO_MIN/SERVO_RES servo2_min_ de SERVO_MIN/SERVO_RES servo1_max_ de SERVO_MAX/SERVO_RES servo2_max_ de SERVO_MAX/SERVO_RES servo1_speed_ de SERVO_DEF servo2_speed_ de SERVO_DEF ; stepper maximum speed and sequence stepper_max_ de STEPPER_DEF stepper_sequence_ de 1 ; light and PRESET thresholds threshold1_ de THRESHOLD1_DEF threshold2_ de THRESHOLD2_DEF ; PRESET mode preset_mode_ de 1 ; events storage events de (0<<5)|EVENT_STOP,0 end