📄 mouse.asm
字号:
;*******************
;
; optic Y1
;
;*******************
measure_y1_optics:
;***** optic Y1 - pulse and release
mov A, LEFT_BUTTON | RIGHT_BUTTON | MIDDLE_BUTTON | X0 | X1 | Y0
iowr Y_OPTICS_PORT
;***** release optic into hi-z mode
mov A, 00h ; clear gpio interrupt flag
mov [gpio_int_flag], A
mov A, Y1 ; enable gpio interrupt
iowr port0_int
mov A, 80h | X0 | X1 | Y0 ; set pin in hi-z mode
iowr port0_mode0
;***** measure rise time
mov A, 00h ; clear interrupt flag
mov [int_flag], A
iord timer_lsb ; store the start timer
mov [optic_start_time], A
mov A, 00h
wait_timer_y1: ; wait for gpio interrupt to occur
cmp A, [gpio_int_flag]
jz wait_timer_y1
mov X, [int_flag] ; store the int_flag in X
;***** restore Y1 port to medium - drive 1 strength
mov A, 00h ; disable gpio interrupt
iowr port0_int
mov A, LEFT_BUTTON | RIGHT_BUTTON | MIDDLE_BUTTON | X0 | X1 | Y0 | Y1
iowr Y_OPTICS_PORT
mov A, 80h | X0 | X1 | Y0 | Y1
iowr port0_mode0
;***** check if interrupt went off during measurement
;***** this could possibly corrupt the timing measurement
;***** so we have to ignore the measured data if this happens
mov A, X
cmp A, 00h
jz y1_no_interrupt_went_off
y1_interrupt_went_off:
mov A, [y_last_state]
mov [y_current_state], A
jmp analyze_x_optics
y1_no_interrupt_went_off:
mov A, [optic_finish_time] ; get the finish time
sub A, [optic_start_time] ; get time difference
push A ; stash time difference away
;***** if time differnce is greater than max time, store this as
;***** the new max time and recalculate the average time
compare_y1_max:
cmp A, [y1_max_time]
jc compare_y1_min
cmp A, THRESHOLD_TIME ; guard band in case we hit an
jnc compare_y1_min ; interrupt during timer measurement
; set new max time
mov [y1_max_time], A
; recalculate average
add A, [y1_min_time]
jc calc_y1_xavg_with_carry
calc_y1_xavg_without_carry:
asr A ; divide value by 2
mov [y1_avg_time], A
jmp calc_y1_xavg_hysteresis
calc_y1_xavg_with_carry:
asr A
or A, 80h ; add msb back in to average
mov [y1_avg_time], A
calc_y1_xavg_hysteresis:
mov A, [y1_max_time] ; hysteresis high value
sub A, [y1_avg_time]
asr A ; 50%
asr A ; 25%
;asr A ; 12.5%
and A, HYST_MASK
add A, [y1_avg_time]
mov [y1_hyst_high], A
mov A, [y1_avg_time] ; hysteresis low value
sub A, [y1_min_time]
asr A ; 50%
asr A ; 25%
;asr A ; 12.5%
and A, HYST_MASK
mov [temp], A
mov A, [y1_avg_time]
sub A, [temp]
mov [y1_hyst_low], A
; if time differnce is less than max time, store this as
; the new max time and recalculate the average time
compare_y1_min:
pop A
push A
cmp A, [y1_min_time]
jnc compare_y1_avg
; set new min time
mov [y1_min_time], A
; recalculate average
add A, [y1_max_time]
jc calc_y1_mavg_with_carry
calc_y1_mavg_without_carry:
asr A ; divide value by 2
mov [y1_avg_time], A
jmp calc_y1_mavg_hysteresis
calc_y1_mavg_with_carry:
asr A
or A, 80h ; add msb back in to average
mov [y1_avg_time], A
calc_y1_mavg_hysteresis:
mov A, [y1_max_time] ; hysteresis high value
sub A, [y1_avg_time]
asr A ; 50%
asr A ; 25%
;asr A ; 12.5%
and A, HYST_MASK
add A, [y1_avg_time]
mov [y1_hyst_high], A
mov A, [y1_avg_time] ; hysteresis low value
sub A, [y1_min_time]
asr A ; 50%
asr A ; 25%
;asr A ; 12.5%
and A, HYST_MASK
mov [temp], A
mov A, [y1_avg_time]
sub A, [temp]
mov [y1_hyst_low], A
; compare the timer value to the average value to determine if
; the output value was a 1 or 0
compare_y1_avg:
pop A ; get last measured time
cmp A, [y1_hyst_low]
jc y1_is_zero
cmp A, [y1_hyst_high]
jnc y1_is_one
y1_invalid:
mov A, [y_last_state]
and A, 02h
or [y_current_state], A
jmp analyze_x_optics
y1_is_one:
mov A, 02h
or [y_current_state], A
y1_is_zero:
;**************************************************
;***** analyze optic values to determine quadrature
;***** movement - if any
analyze_x_optics:
mov A, [x_last_state]
asl A
asl A
or A, [x_current_state]
asl A
jacc x_jumptable ; jump to transition state
x_increment:
mov A, [x_current_state]
mov [x_last_state], A
inc [ep1_dmabuff1] ; increment mouse cursor change
mov A, EVENT_PENDING ; set event pending to signal data to
mov [event_machine], A ; send to the host
jmp analyze_y_optics
x_decrement:
mov A, [x_current_state]
mov [x_last_state], A
dec [ep1_dmabuff1] ; decrement mouse cursor change
mov A, EVENT_PENDING ; set event pending to signal data to
mov [event_machine], A ; send to the host
jmp analyze_y_optics
x_error:
analyze_y_optics:
mov A, [y_last_state]
asl A
asl A
or A, [y_current_state]
asl A
jacc y_jumptable ; jump to transition state
y_increment:
mov A, [y_current_state]
mov [y_last_state], A
inc [ep1_dmabuff2] ; increment mouse cursor change
mov A, EVENT_PENDING ; set event pending to signal data to
mov [event_machine], A ; send to the host
jmp analyze_z_optics
y_decrement:
mov A, [y_current_state]
mov [y_last_state], A
dec [ep1_dmabuff2] ; decrement mouse cursor change
mov A, EVENT_PENDING ; set event pending to signal data to
mov [event_machine], A ; send to the host
jmp analyze_z_optics
y_error:
analyze_z_optics:
; mov A, [z_last_state]
; asl A
; asl A
; or A, [z_current_state]
; asl A
; jacc z_jumptable ; jump to transition state
;
z_forward:
; mov A, 01h
; mov [ep1_dmabuff3], A ; increment mouse wheel change
; mov A, EVENT_PENDING ; set event pending to signal data to
; mov [event_machine], A ; send to the host
; jmp optic_task_done
z_backward:
; mov A, FFh
; mov [ep1_dmabuff3], A ; decrement mouse wheel change
; mov A, EVENT_PENDING ; set event pending to signal data to
; mov [event_machine], A ; send to the host
optic_task_done:
; mov A, [z_current_state]
; mov [z_last_state], A
; this task sends data out endpoint 1 if the endpoint is configured
; and not set to stall
mov A, [event_machine]
jacc event_machine_jumptable
event_pending:
; if not configured then skip data transfer
mov A, [configuration]
cmp A, 01h
jnz event_task_done
; if stalled then skip data transfer
mov A, [ep1_stall]
cmp A, FFh
jz event_task_done
; We have to determine which protocol is currently enabled
; and send the format that is supported by that protocol
mov A, [protocol]
cmp A, HID_REPORT
jnz boot_protocol_report
hid_protocol_report:
mov A, 04h ; set endpoint 1 to send 3 bytes
or A, [ep1_data_toggle]
iowr ep1_count
mov A, ACK_IN ; set to ack on endpoint 1
iowr ep1_mode
jmp event_task_done
boot_protocol_report:
mov A, 03h ; set endpoint 1 to send 3 bytes
or A, [ep1_data_toggle]
iowr ep1_count
mov A, ACK_IN ; set to ack on endpoint 1
iowr ep1_mode
event_task_done:
mov A, NO_EVENT_PENDING ; clear pending events
mov [event_machine], A
no_event_task:
jmp task_loop
;*******************************************************
;
; Interrupt handler: gpio
; Purpose: The program jumps to this routine when
; a gpio interrupt occurs.
;
;*******************************************************
gpio:
push A
iord timer_lsb ; store current timer value
mov [optic_finish_time], A
; mov A, 01h
; mov [gpio_int_flag], A ; set a flag to indicate
inc [gpio_int_flag]
; that a gpio interrupt occurred
pop A
reti
;*******************************************************
;
; Interrupt handler: Bus_reset
; Purpose: The program jumps to this routine when
; the host causes a USB reset.
;
;*******************************************************
Bus_reset:
mov A, STALL_IN_OUT ; set to STALL INs&OUTs
iowr ep0_mode
mov A, ADDRESS_ENABLE ; enable USB address 0
iowr usb_address
mov A, DISABLE ; disable endpoint1
iowr ep1_mode
mov A, 00h ; reset program stack pointer
mov psp,a
jmp Main
;*******************************************************
;
; Interrupt: 1ms_clear_control
; Purpose: This interrupt handler sets a flag to indicate
; it's time to read the buttons in the main task loop,
; watches for USB suspend, and handles the HID idle
; timer.
;
;*******************************************************
1ms_timer:
push A
; flag that interrupt went off during processing.
inc [int_flag]
;**********************************
; poll buttons for button on/off state
mov A, [button_machine]
cmp A, BUTTON_DATA_PENDING
jz 1ms_suspend_timer
; if the main task has read the buttons, then we need to re-read the
; buttons and set the pending flag
mov A, 00h
mov [current_button_state], A
left_button_read:
iord LEFT_BUTTON_PORT
and A, LEFT_BUTTON
jnz right_button_read
mov A, HID_LEFT_MOUSE_BUTTON
or [current_button_state], A
right_button_read:
iord RIGHT_BUTTON_PORT
and A, RIGHT_BUTTON
jnz middle_button_read
mov A, HID_RIGHT_MOUSE_BUTTON
or [current_button_state], A
middle_button_read:
iord MIDDLE_BUTTON_PORT
and A, MIDDLE_BUTTON
jnz button_read_done
mov A, HID_MIDDLE_MOUSE_BUTTON
or [current_button_state], A
button_read_done:
mov A, BUTTON_DATA_PENDING
mov [button_machine], A
;**********************************
; check for no bus activity/usb suspend
1ms_suspend_timer:
iord usb_status ; read bus activity bit
and A, BUS_ACTIVITY ; mask off activity bit
jnz bus_activity
inc [suspend_count] ; increment suspend counter
mov A, [suspend_count]
cmp A, 04h ; if no bus activity for 3-4ms,
jz usb_suspend ; then go into low power suspend
jmp idle_timer_handler
usb_suspend:
;******************************
; disable unused pins
; buttons are already resistively pulled up
; optics inputs are pulled down by external resistors
; must turn off LED
; NOTE - this firmware must change for each pin out
mov A, OPTIC_CONTROL
iowr OPTIC_CONTROL_PORT
; enable wakeup timer interrupt
mov A, (WAKEUP_INT | USB_RESET_INT)
iowr global_int
; suspend execution
mov A, (SUSPEND | RUN) ; set suspend bit
ei
iowr control
nop
di
; look for bus activity, if none go back into suspend
iord usb_status
and A, BUS_ACTIVITY
jz usb_suspend
; re-enable the optics in the case of the host wakeup
mov A, 00h
iowr OPTIC_CONTROL_PORT
; re-enable interrupts
mov A, (1MS_INT | USB_RESET_INT | GPIO_INT)
iowr global_int
; reset wakeup timer
mov A, 00h
mov [wakeup_timer], A
bus_activity:
mov A, 00h ; reset suspend counter
mov [suspend_count], A
iord usb_status
and A, ~(BUS_ACTIVITY | 10h) ; clear bus activity bit
iowr usb_status
;**********************************
; handle HID SET_IDLE request
idle_timer_handler:
mov A, [idle]
cmp A, 00h
jz ms_timer_done
inc [idle_prescaler]
mov A, [idle_prescaler]
cmp A, 04h
jz decrement_idle_timer
jmp ms_timer_done
decrement_idle_timer:
mov A, 00h
mov [idle_prescaler], A
dec [idle_timer]
jz reset_idle_timer
jmp ms_timer_done
reset_idle_timer:
mov A, [idle]
mov [idle_timer], A
mov A, EVENT_PENDING
mov [event_machine], A
ms_timer_done:
pop A
reti
;*******************************************************
;
; Interrupt: Endpoint0
; Purpose: Usb control endpoint handler. This interrupt
; handler formulates responses to SETUP and
; CONTROL READ, and NO-DATA CONTROL transactions.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -