The simplest interface directly senses the buttons and the quadrature signals from the shaft encoders. The earliest mice simply provided these signals, and the interface logic was on the motherboard. The next generation mice tracked the encoder signals and sent information to an RS232 port. The current generation of mice send their information to a PS/2 port.
The simple interface can be a good choice where a track-ball is built into the equipment, since it avoids the mouse-controller chip. Kees van Oss interfaced a simple mouse to an Atom, using TTL counter chips.
However, virtually all mice use PS/2 at the time of writing this page.
This uses the same front-end logic as the PS/2 keyboard. Thus all the tricky bit manipulation logic already exists. The output, defined by Microsoft, of the PS/2 front-end is a stream of bytes that look like this:
Data packet format |
|
||||||||||||||||||
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | ||||||||||||
1: | YV | XV | YS | XS | 1 | M | R | L | |||||||||||
2: | X7 | X6 | X5 | X4 | X3 | X2 | X1 | X0 | Movement in X direction | ||||||||||
3: | Y7 | Y6 | Y5 | Y4 | Y3 | Y2 | Y1 | Y0 | Movement in Y direction | ||||||||||
4: | Z7 | Z6 | Z5 | Z4 | Z3 | Z2 | Z1 | Z0 | Movement in Z direction (for wheel mice only) |
There seems to be a snag in that there is now simple way of
spotting the start of the packet.
There used to be such a way with RS232 mice, since they used D7
to mark the first byte.
The standard PS/2 mouse (with Logitech mouse) defaults to 160
CPI and can be switched to 40, 80, 160 or 320 CPI with software.
Microsoft mouse driver for Windows 3.x and Windows 95 defaults to
160 counts per inch.
The maximum tracking rate for PS/2 mouse is 40 report/second *
255 counts per report = 10200 counts per second.
For 100 CPI mouse this would indicate maximum tracking rate of
102 inches per second and for 400 CPI mouse only 25.2 inches per
second.
People use a mixture of coarse and fine movements. When using a pen, they use an arm to move the pen quickly to roughly the right area, and gradually refine movement until the hand moves in fine detail. The same requirements were found necessary to position a mouse cursor. Coarse motions are generally larger than fine motion, so they are made to move a greater number of pixels. This adds a further layer of complexity to cursor management.
As final nuisances, PS/2 mice sends other data than the motion data packet. They also start up as wheel-less mice, for the benefit of PCs that are not aware of wheel mice. They have to be sent a command to enable the wheel data byte to appear in the motion packet. My current VHDL implementation of PS/2 only supports data input, not output.
The VHDL implementation I found on the web activates the mouse by sending a single byte immediately after reset. Mice will default to the simple 2-button mode. Activating modes with more buttons and/or a wheel requires more complex command sequences that are wiser to implement in software.
It seems a good idea to have a software-loaded byte-transmission buffer, but one that is filled with the simple activation byte (hex F4) instead of the usual clearing at reset. This allows the mouse to be both activated automatically (in simple mode) from hardware reset, and set to other modes later from software. Note that this also changes the format of the data packets, so the packet interpreter VHDL code has to change accordingly.
Ideally the mouse and cursor would be managed entirely in hardware, so that they placed no processing load on the CPU. They would continue to operate even if the processor became very busy or stopped working altogether. Even on modern desktop machines, the cursor can appear to lag behind mouse movement. If those machines find it a problem, a 6502 machine may do as well.
Unfortunately the amount of mouse management argues for doing it in software. Some compromise may be needed.
As with the keyboard interface, there are few VHDL examples and those are not very good. The biggest problem seems to be the bidirectionality: many designs carry the disclaimer that they can only receive and not transmit. Those that do transmit have kludgy state machines that may well work but are not very adaptable - the transmit and receive processes are not well seperated so it is not easy to see how to modify either. In particular, I want to have a transmit process that can be software controlled instead of just automatically sending a mouse activation command byte.
On top of that there are things that are just plain wrong. For example, the PS/2 signals are specified as open collector. That is, they can be driven low, or tristated to be pulled up by resistors, but never be driven high. This critical detail ensure that devices can never try to drive at conflicting levels. The I2C interface is open-collector for the same reason. And like I2C, public examples often bodge this detail! This fragment is just plain wrong:
MOUSE_DATA <= 'Z' WHEN MOUSE_DATA_DIR = '0' ELSE MOUSE_DATA_BUF; MOUSE_CLK <= 'Z' WHEN MOUSE_CLK_DIR = '0' ELSE MOUSE_CLK_BUF;
A better bet might be this:
if MOUSE_DATA_DIR = '1' and MOUSE_DATA_BUF = '0' then MOUSE_DATA = '0' else MOUSE_DATA = 'Z' if MOUSE_CLK_DIR = '1' and MOUSE_CLK_BUF = '0' then MOUSE_CLK = '0' else MOUSE_CLK = 'Z'
State machines are often hard to interpret what is going on. Rather than reverse engineering one, perhaps it is better to try to understand the essential points. There are two main processes going on: transmit and receive. Simple UARTs have separate wires for their serial data, but PS/2 devices share a single data signal wire. So devices must not transmit when the receiver is busy, or receive when the transmitter is busy:
Host ^
||
vRx
bytebusy
inhibit-->
<--inhibit
busyTx
byte|___________|
^
|
VPS/2 device
The diagram above seems okay at first glance, working on a byte per byte basis. Each process is inhibited while the other is transmitting a byte, but it is still possible for one to begin between the bytes of a packet. So that suggests you need a more complex model like so:
Host ^
||
vRx
packetbusy
inhibit-->
<--inhibit
busyTx
packet^
||
vRx
byteTx
byte|___________|
^
|
VPS/2 device
The packet processes inhibit each other for the duration of the packet. This raises yet another problem: packet size may vary. In the case of PS/2 mice, incoming packets have 3 or 4 bytes. Outgoing packets to mice or keyboards can be much more complex. Therefore it is more complex a problem to commit to hardware. Since complex packet are only needed in a software supervised environment, it makes sense to have software supervise the enabling and disabling of the Tx / Rx processes. For instance the CPU could disable reception before sending a packet, then re-enable reception afterwards.
As a further refinement, one can share a PS/2 transmitter process between the keyboard and mouse because the CPU can manage with talking to one device at a time. In normal use, bytes are infrequently sent to mice or keyboards so sharing the transmitter should not affect system performance noticeably.
Host <--
<--
-->busy
data
inhibit<---------- Keyboard mpx --> --> data --> --------------> --> Mouse <--
<--
-->busy
data
inhibit<----------
The benefit is a reduction of logic gates required.
After implementing some logic for the busy / inhibit lines, I looked hard for flaws. For example, if the CPU reads that a receiver is not busy then asserts inhibit, what happens if the receiver becomes busy in this small but finite time? It may not happen very often but if it can then it eventually will. A better way would be to have a request-grant system. The CPU asserts the request, and the PS/2 receiver grants it if not busy. If it is busy, it continues to receive the current message packet then disables reception and grants access to the CPU. It locks the state if not busy and ignores input. If busy, it can go to not-busy but will stay there when it does. Thus the procedure is:
Writing the PS/2 software was tedious and dull, but having taken the time to look for flaws it did not take too long before the mouse showed signs of life. After the 'start-streaming' command was sent, mouse motion caused the PS/2 signal activity. Bytes started appearing in the packet buffer. Initially the X-byte was appearing where the Y-byte should be, and the caused was deduced to be that all bytes were one step out of sync because the hardware expected to receive a single acknowledge byte whereas in fact that had been handled by the software. This was soon fixed.
The interface system now receives streamed packets and calculates a screen co-ordinate. I first printed this on the screen, then implemented a byte counter so that the CPU can seen when data had arrived changed. This allowed the software to update the display only when the mouse had moved. Even this was improved by just counting whole packets instead.
The software sets the streaming rate to one packet per frame (i.e. 50 or 60 Hz). It doesn't make sense to update any more often than that. The hardware X and Y counters never miss a packet so even if the software misses packets the absolute co-ordinates are always correct.
The simple test program did miss packets, because it takes longer than 1/50th or 1/60th of a second to scroll the text screen in software. The new hardware-scrolling features should allow screens to be scrolled in a few microseconds. This does have implications for application programs, as these will have to either handle packets as fast as they arrive or gracefully fail to do so.
The interface is working acceptably for the moment. In the future, it should be modified to allow the position data to be latched 4-bytes at once and read one byte at a time. This ensures they are all valid for a particular instant. Also, it would be cautious to have a timeout so the receiver can re-synchronise with packet boundaries if it happens to miss bytes.
As an aside, it was very crowded on my desk having two keyboards and two mice. So I bought a KVM switch. They are rather expensive, around £20 to £30 depending on the video bandwidth.