; *******************************************************************
; PWM generator with up/down key input
; *******************************************************************
;
; 2008 Copyright by Frank Buss
; *******************************************************************

			LIST	P=16F628
;			LIST	P=12C508

#include "P16F628.INC"
;#include "P12C508.INC"


			org 0

;
; defines
;

; destinations
W			EQU	D'0'
F			EQU	D'1'

; port bits
PWM_OUT		EQU D'0'
UP_KEY		EQU D'1'
DOWN_KEY	EQU D'2'
GPIO		EQU D'5'  ; PORT A

; key statemachine defines
IDLE_STATE				EQU 0
PRESSED_HOLDOFF_STATE	EQU 1
PRESSED_STATE			EQU 2
RELEASED_HOLDOFF_STATE	EQU 3

; delay times
; loop frequency: about 244 Hz (1 MHz / 256 / 16)

; holdoff after key press and key release (about 65ms)
HOLDOFF_TIME 		EQU D'32'

; milliseconds until automatic repeat (about 262ms)
REPEAT_DELAY_TIME 	EQU D'128'

; milliseconds delay between automatic repeat steps (about 98ms)
REPEAT_RATE_TIME 	EQU D'48'


;
; register aliases
;

; current state of key up
UP_STATE	EQU H'20'

; current state of key down
DOWN_STATE	EQU H'21'

; last sampled state of key up / down
KEY_BIT		EQU H'22'

; delay counter for keyboard state changes
KEY_DELAY	EQU H'23'

; 1, if the current tested key was pressed or repeated
KEY_SIGNAL	EQU H'24'

; current PWM index in PWM time lookup table
PWM_INDEX	EQU H'25'

; current PWM value from lookup table
PWM_WIDTH	EQU H'26'


; prescaler: 16
			movlw	D'0'		; set levels low
			movwf	PORTA		;  for port a
			movlw	0x07 		; turn comparators off and
			movwf	CMCON 		;  enable pins for I/O functions
			clrwdt
			bsf		STATUS, RP0	; select bank 1
			movlw	B'11111110'	; set bit 0 port a as output
			movwf	TRISA - H'80'
			movlw 2
;			option
			movwf	OPTION_REG - H'80'
			bcf		STATUS, RP0	; select bank 0

; init program logic
			movlw IDLE_STATE
			movwf UP_STATE
			movwf DOWN_STATE
			clrf KEY_DELAY
			movlw H'10'
			movwf PWM_INDEX

; wait until timer is 0
loop_start: movf TMR0, W	; load timer to W register, z flag is set, if 0
			btfss STATUS, Z	; skip, if zero
			goto loop_start

; set PWM output to high, if PWM_WIDTH > 0
			movf PWM_WIDTH, W
			btfss STATUS, Z
			bsf GPIO, PWM_OUT

; wait until timer reached the selected PWM value
pwm_wait:	movf TMR0, W	; load timer to W register
			xorwf PWM_WIDTH, W
			btfss STATUS, Z	; skip, if zero
			goto pwm_wait

; set PWM output to low
			bcf GPIO, PWM_OUT

; keyboard test for up key
			movlw 0
			btfss GPIO, UP_KEY	; up key is pressed, if input is low
			movlw 1
			movwf KEY_BIT
			movlw UP_STATE
			movwf FSR
			clrf KEY_SIGNAL
			call key_test
; if up key signalled, increment PWM index
			btfsc KEY_SIGNAL, 0
			incf PWM_INDEX, F

; keyboard test for down key
			movlw 0
			btfss GPIO, DOWN_KEY	; down key is pressed, if input is low
			movlw 1
			movwf KEY_BIT
			movlw DOWN_STATE
			movwf FSR
			clrf KEY_SIGNAL
			call key_test
; if down key signalled, decrement PWM index
			btfsc KEY_SIGNAL, 0
			decf PWM_INDEX, F

; test for valid PWM index
			movf PWM_INDEX, W
			xorlw H'FF'
			btfsc STATUS, Z
			incf PWM_INDEX, F
			movf PWM_INDEX, W
			xorlw H'20'
			btfsc STATUS, Z
			decf PWM_INDEX, F

; decrement delay counter, if not zero
			movf KEY_DELAY, W
			btfss STATUS, Z
			decf KEY_DELAY, F

; get lookup value
			call pwm_lookup
			movwf PWM_WIDTH

			goto loop_start


; input:
; PWM_INDEX
; output in W:
; PWM width value
pwm_lookup:	movlw pwm_table
			addwf PWM_INDEX, W
			movwf PCL

; input:
; KEY_BIT: last sampled key bit for the current key under test (1 if pressed)
; FSR: index to key state register (DOWN_STATE or UP_STATE)
; output:
; possibly new value in DOWN_STATE or UP_STATE
; 1 in KEY_SIGNAL, if key pressed signaled
key_test:	movf INDF, W
			xorlw IDLE_STATE
			btfss STATUS, Z	; skip, if not zero
			goto test2

; IDLE_STATE
; if key was pressed, change to PRESSED_HOLDOFF_STATE,
; set the KEY_DELAY counter and signal key pressed
			btfss KEY_BIT, 0
			retlw 0		; not pressed, return
			movlw HOLDOFF_TIME
			movwf KEY_DELAY
			movlw 1
			movwf KEY_SIGNAL
			movlw PRESSED_HOLDOFF_STATE
			movwf INDF
			retlw 0

test2:		movf INDF, W
			xorlw PRESSED_HOLDOFF_STATE
			btfss STATUS, Z	; skip, if not zero
			goto test3

; PRESSED_HOLDOFF_STATE
; wait HOLDOFF_TIME and change state to PRESSED_STATE
			movf KEY_DELAY, W
			btfss STATUS, Z
			retlw 0
			movlw REPEAT_DELAY_TIME
			movwf KEY_DELAY
			movlw PRESSED_STATE
			movwf INDF
			retlw 0

test3:		movf INDF, W
			xorlw PRESSED_STATE
			btfss STATUS, Z	; skip, if not zero
			goto test4

; PRESSED_STATE
; wait REPEAT_TIME
; if key is still pressed and trigger key signal
; if key is released, change state immediately to
; RELEASED_HOLDOFF_STATE
			movf KEY_DELAY, W
			btfss STATUS, Z
			goto test_release
			movlw REPEAT_RATE_TIME
			movwf KEY_DELAY
			movlw 1
			movwf KEY_SIGNAL
			retlw 0
test_release:
			btfsc KEY_BIT, 0
			retlw 0
			movlw HOLDOFF_TIME
			movwf KEY_DELAY
			movlw RELEASED_HOLDOFF_STATE
			movwf INDF
			retlw 0

test4:		movf INDF, W
			xorlw RELEASED_HOLDOFF_STATE
			btfss STATUS, Z	; skip, if not zero
			goto test5

; RELEASED_HOLDOFF_STATE
; wait HOLDOFF_TIME and change state to IDLE_STATE
			movf KEY_DELAY, W
			btfss STATUS, Z
			retlw 0
			movlw IDLE_STATE
			movwf INDF
			retlw 0

; unknown state, reset to idle
test5:
			movlw IDLE_STATE
			movwf INDF
			retlw 0

pwm_table:
			retlw D'0'
			retlw D'7'
			retlw D'14'
			retlw D'21'
			retlw D'28'
			retlw D'35'
			retlw D'42'
			retlw D'49'
			retlw D'56'
			retlw D'63'
			retlw D'70'
			retlw D'77'
			retlw D'84'
			retlw D'91'
			retlw D'98'
			retlw D'105'
			retlw D'112'
			retlw D'119'
			retlw D'126'
			retlw D'133'
			retlw D'140'
			retlw D'147'
			retlw D'154'
			retlw D'161'
			retlw D'168'
			retlw D'175'
			retlw D'182'
			retlw D'189'
			retlw D'196'
			retlw D'203'
			retlw D'210'
			retlw D'217'

			end
