Test Parallax Mouse vs. PS/2 Spec
I was experimenting with the Test Parallax Mouse object, and wanted to have it return the mouse x/y values for any given moment, instead of incrementing/decrementing the value for each. I thought this could be done by simply changing the add operators to variable assignment operators:
The PS/2 mouse spec indicates an x/y movement range from -255 to 255 utilizing a byte for each and a third byte containing a sign and overflow bit for each. But I am seeing returns values well outside that range however. Does anyone know why this is? Is Test Mouse not using the PS/2 spec, and if not, how are the data packets from the mouse being interpreted?
PUB MouseDisplay | mousex, mousey, mousez, mousebtns
mouse.start(24, 25) ' Start mouse
debug.start(31, 30, 0, 57600) ' Start serial connection
waitcnt(clkfreq + cnt) ' Wait 1 s before starting
' Display static text.
debug.str(String(CLS, "Mouse", CR, "x = ", CR, "y = ", CR, "scroll = ", CR, "CRL", HOME))
repeat
{
mousex += mouse.delta_x ' Get mouse data (original)
mousey += mouse.delta_y
mousez += mouse.delta_z
mousebtns := mouse.buttons
}
mousexread := mouse.delta_x ' Get mouse data (altered)
mousex := mouse.delta_x
mousey := mouse.delta_y
mousez += mouse.delta_z
mousebtns := mouse.buttons
debug.str(string(CRSRXY, 4, 1)) ' Display mouse data
debug.dec(mousex)
debug.str(string(CLREOL, CRSRXY, 4, 2))
debug.dec(mousey)
debug.str(string(CLREOL, CRSRXY, 9, 3))
debug.dec(mousez)
debug.str(string(CLREOL, CR, CR))
debug.bin(mouse.buttons, 5)
waitcnt(clkfreq/20 + cnt)
The PS/2 mouse spec indicates an x/y movement range from -255 to 255 utilizing a byte for each and a third byte containing a sign and overflow bit for each. But I am seeing returns values well outside that range however. Does anyone know why this is? Is Test Mouse not using the PS/2 spec, and if not, how are the data packets from the mouse being interpreted?
Comments
I adapted the code (adding a couple of local variables for the mousereads) to capture the max delta values returned to test it for sure; they do exceed 255:
mousexread := mouse.delta_x ' Get mouse data IF mousexread > mousex mousex:=mousexread mouseyread := mouse.delta_y IF mouseyread > mousey mousey:=mouseyread mousez += mouse.delta_z mousebtns := mouse.buttons
Any thoughts?
There is also a bound_limits method which appears to set the max/min x,y, and z.
http://www.parallax.com/sites/default/files/downloads/28060-PS2-Adapter-v1.0.pdf
'' Test Parallax Mouse.spin '' Displays Parallax mouse X, Y, and Scroll button coordinates along with the scroll wheel, '' right and left button states in the Parallax Serial Terminal. '' See 28060-PS2Adapter.pdf for schematics and instructions. CON _clkmode = xtal1 + pll16x ' System clock settings _xinfreq = 5_000_000 HOME = 1 ' Parallax Serial Terminal constants CRSRXY = 2 CR = 13 CLREOL = 11 CLS = 16 OBJ mouse : "Mouse" ' Declare Mouse object debug : "FullDuplexSerial" ' Declare FullDuplexSerial object PUB MouseDisplay | mousex, mousey, mousez, mousebtns mouse.start(24, 25) ' Start mouse debug.start(31, 30, 0, 57600) ' Start serial connection waitcnt(clkfreq + cnt) ' Wait 1 s before starting ' Display static text. debug.str(String(CLS, "Mouse", CR, "x = ", CR, "y = ", CR, "scroll = ", CR, "CRL", HOME)) repeat mousex += mouse.delta_x ' Get mouse data mousey += mouse.delta_y mousez += mouse.delta_z mousebtns := mouse.buttons debug.str(string(CRSRXY, 4, 1)) ' Display mouse data debug.dec(mousex) debug.str(string(CLREOL, CRSRXY, 4, 2)) debug.dec(mousey) debug.str(string(CLREOL, CRSRXY, 9, 3)) debug.dec(mousez) debug.str(string(CLREOL, CR, CR)) debug.bin(mouse.buttons, 5) waitcnt(clkfreq/20 + cnt)
So the delta_x and delta_y methods are giving the change in the values between calls of those methods then; and not the x and y movement bytes the mouse is reporting?
I'm not proficient in assembly, but it looks like the x and y received movement bytes are being immediately added to their previous values. How could I retrieve just the raw bytes received from the mouse?
That would be the absolute values suggested by Duane Degn in post #4.
You could roll up your sleeves and study mouse.spin. I guarantee it will help your assembly proficiency.
If you told us more about what you're trying to do, we might be able to help you better.
I agree with your assessment of the PASM code.
As Mike G. suggests, this could be a good time to try a bit of PASM yourself.
To catch the raw values and write them to the hub. I added a few lines to the PASM indicating where I think you'd want to write data to the hub.
Besides the delta x and y values, I also included a "readCount" variable so the parent object could know when new information was read and how many reads had occurred.
test _buttons,#$10 wc 'adjust _x muxc data,signext add _x,data wrlong data, deltaXPtr '' added by DWD call #receive 'receive third byte test _buttons,#$20 wc 'adjust _y muxc data,signext add _y,data wrlong data, deltaYPtr '' added by DWD add readCount, #1 '' added by DWD and _buttons,#1 'trim buttons wrlong readCount, readCountPtr '' added by DWD cmp _present,#2 wc 'if not scrollwheel mouse, update parameters
There's a big problem doing it this way though. Your Spin loop is going to miss a bunch of these delta values.
If you were to monitor the readCount value, this would be jumping in by large amounts. There would be many delta values you'd miss.
Here's what the output looks like:
Mouse x = 50 y = 116 scroll = 0 CRL 00000 delta x = 4 delta y = 1 readCount = 1424
When I tried this modified version I noticed the readCount amount only changed when the mouse was moved. Apparently the code only writes to the hub when there's new data to write. When the mouse data does change, it changes too fast for Spin to keep up with. I think this is one reason the raw delta information isn't normally written to the hub. The information changes too fast to be useful to code running in Spin.
add _y,data
and likewise for _x and just use the existing delta method calls to get the values. Does that seem reasonable?
It depends on what you are trying to do.
The code attached to post #9 lets you access the raw delta amounts but these amounts change so fast they don't do you much good in Spin.
This is exactly what the demo application does.
The PS2 movement data packet returns the relative offset in position from the previous packet. It does NOT return an absolute X/Y values alluded to in your original post. The mouse has no idea if it is on your forehead or mouse pad.
Is there any more verbose annotation or just a written explanation available for how the Mouse object works? It's pretty slow going for me trying to decode what's going on. So far, this is what I think is happening:
DAT '*************************************** '* Assembly language PS/2 mouse driver * '*************************************** org ' ' ' Entry ' entry mov p,par 'load input parameters: 'par value here is the par_x address passed from Spin in the Start method add p,#5*4 '_dpin/_cpin 'Increase p by 20 (i.e to equal the address five 4-byte longs ahead of par_x) rdlong _dpin,p 'Read main memory at the address of p (i.e. par_dpin) and store in _dpin add p,#4 'Increase p by 4 (i.e. the address one 4-byte long ahead of par_dpin) rdlong _cpin,p 'Read main memory at the address of p (i.e. par_cpin) and store in _cpin 'set pin masks mov dmask,#1 'Set dmask to 000000_00000000_00000000_00000001 shl dmask,_dpin 'shift dmask left by _dpin number of bits mov cmask,#1 '(do the same as above for cmask) shl cmask,_cpin 'modify port registers within code ***PURPOSE?*** test _dpin,#$20 wc 'bitwise AND _dpin and 20, set C flag if result contains an odd number of high bits. muxc _d1,dlsb 'set each bit in _d1 corresponding to dlsb's high bits to the C state muxc _d2,dlsb 'set each bit in _d2 corresponding to dlsb's high bits to the C state muxc _d3,#1 'set least significant bit in _d3 to the C state muxc _d4,#1 'set least significant bit in _d4 to the C state test _cpin,#$20 wc '(Do as above for cpin registers) muxc _c1,dlsb muxc _c2,dlsb muxc _c3,#1 'reset output parameters: movd :par,#_x 'set the destination register with the address of :par to the value of _x mov p,#5 '_x/_y/_z/_buttons/_present ''set p to 5 :par mov 0,#0 'Set register 0 to 0 add :par,dlsb 'Add the address of dlsb to :par djnz p,#:par 'Decrement p and jump to the register number :par if p is not zero ' ' ' Reset mouse ' reset mov dira,#0 'reset directions 'Set all pins to inputs mov dirb,#0 '(same as above, though DIRB is n/a, being reserved for future use) mov stat,#1 'set reset flag 'Set stat to 1 ' ' ' Update parameters ' 'update output parameters: update movd :par,#_x 'set the destination register with the address of :par to the value of _x mov p,par '_x/_y/_z/_buttons/_present ''set p to "par_x" address passed from Spin in Start mov q,#5 'set q to 5 :par wrlong 0,p 'Write "0" to main memory at address of p add :par,dlsb 'Add the address of dlsb to :par add p,#4 'set p to 4 djnz q,#:par 'Decrement q and jump to the register numbered value of :par test stat,#1 wc 'if reset flag, transmit reset command 'AND stat and 1, set C flag if result has odd number of high bits if_c mov data,#$FF 'if C flag is set, set data to $FF if_c call #transmit 'if C flag is set jump to transmit, execute and return here ' ' ' Get data packet ' mov stat,#0 'reset state call #receive 'receive first byte cmp data,#$AA wz 'powerup/reset? if_z jmp #init mov _buttons,data 'data packet, save buttons call #receive 'receive second byte test _buttons,#$10 wc 'adjust _x muxc data,signext add _x,data call #receive 'receive third byte test _buttons,#$20 wc 'adjust _y muxc data,signext add _y,data and _buttons,#1 'trim buttons cmp _present,#2 wc 'if not scrollwheel mouse, update parameters if_c jmp #update call #receive 'scrollwheel mouse, receive fourth byte cmp _present,#3 wz 'if 5-button mouse, handle two extra buttons if_z test data,#$10 wc if_z_and_c or _buttons,#000 if_z test data,#$20 wc if_z_and_c or _buttons,#000 shl data,#28 'adjust _z sar data,#28 sub _z,data jmp #update 'update parameters ' ' ' Initialize mouse ' init call #receive '$AA received, receive id movs crate,#100 'try to enable 3-button scrollwheel type call #checktype movs crate,#200 'try to enable 5-button scrollwheel type call #checktype shr data,#1 'if neither, 3-button type add data,#1 mov _present,#3 '*********replaced "data" with "#3" movs srate,#200 'set 200 samples per second call #setrate mov data,#$F4 'enable data reporting call #transmit jmp #update ' ' ' Check mouse type ' checktype movs srate,#200 'perform "knock" sequence to enable call #setrate '..scrollwheel and extra buttons crate movs srate,#200/100 call #setrate movs srate,#80 call #setrate mov data,#$F2 'read type call #transmit call #receive checktype_ret ret ' ' ' Set sample rate ' setrate mov data,#$F3 call #transmit srate mov data,#0 call #transmit setrate_ret ret ' ' ' Transmit byte to mouse ' transmit _c1 or dira,cmask 'pull clock low movs napshr,#13 'hold clock for ~128us (must be >100us) call #nap _d1 or dira,dmask 'pull data low movs napshr,#18 'hold data for ~4us call #nap _c2 xor dira,cmask 'release clock test data,#$0FF wc 'append parity and stop bits to byte muxnc data,#$100 or data,dlsb mov p,#10 'ready 10 bits transmit_bit call #wait_c0 'wait until clock low shr data,#1 wc 'output data bit _d2 muxnc dira,dmask mov wcond,c1 'wait until clock high call #wait djnz p,#transmit_bit 'another bit? mov wcond,c0d0 'wait until clock and data low call #wait mov wcond,c1d1 'wait until clock and data high call #wait call #receive_ack 'receive ack byte with timed wait cmp data,#$FA wz 'if ack error, reset mouse if_nz jmp #reset transmit_ret ret ' ' ' Receive byte from mouse ' receive test _cpin,#$20 wc 'wait indefinitely for initial clock low waitpne cmask,cmask receive_ack mov p,#11 'ready 11 bits receive_bit call #wait_c0 'wait until clock low movs napshr,#16 'pause ~16us call #nap _d3 test dmask,ina wc 'input data bit rcr data,#1 mov wcond,c1 'wait until clock high call #wait djnz p,#receive_bit 'another bit? shr data,#22 'align byte test data,#$1FF wc 'if parity error, reset mouse if_nc jmp #reset and data,#$FF 'isolate byte receive_ack_ret receive_ret ret ' ' ' Wait for clock/data to be in required state(s) ' wait_c0 mov wcond,c0 '(wait until clock low) wait mov q,tenms 'set timeout to 10ms wloop movs napshr,#18 'nap ~4us call #nap _c3 test cmask,ina wc 'check required state(s) _d4 test dmask,ina wz 'loop until got state(s) or timeout wcond if_never djnz q,#wloop '(replaced with c0/c1/c0d0/c1d1) tjz q,#reset 'if timeout, reset mouse wait_ret wait_c0_ret ret c0 if_c djnz q,#wloop '(if_never replacements) c1 if_nc djnz q,#wloop c0d0 if_c_or_nz djnz q,#wloop c1d1 if_nc_or_z djnz q,#wloop ' ' ' Nap ' nap rdlong t,#0 'get clkfreq napshr shr t,#18/16/13 'shr scales time min t,#3 'ensure waitcnt won't snag add t,cnt 'add cnt to time waitcnt t,#0 'wait until time elapses (nap) nap_ret ret ' ' ' Initialized data ' dlsb long 1 << 9 tenms long 10_000 / 4 signext long $FFFFFF00 ' ' ' Uninitialized data ' dmask res 1 cmask res 1 stat res 1 data res 1 p res 1 q res 1 t res 1 _x res 1 'write-only _y res 1 'write-only _z res 1 'write-only _buttons res 1 'write-only _present res 1 'write-only _dpin res 1 'read-only _cpin res 1 'read-only {{ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ TERMS OF USE: MIT License │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ │files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ │modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ │is furnished to do so, subject to the following conditions: │ │ │ │The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│ │ │ │THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ │WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ │COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ │ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }}
I kind of see how "entry" is setting up a port to coincide with the dpin and cpin values passed from Start, but from there are steps where I can decode what the PASM is doing, I just can't quite grasp why. Could anyone please shed a little light for me overall on what is going on in this driver? Thanks.