Wiring a PS/2 mouse.

Go To Last Post
12 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello. I'm trying to connect a mouse to my atmega8 and I searched the forum, and I found these links:
http://www.computer-engineering....
http://www.computer-engineering....
Also, I've found a project by zbaird, but it's in ASM and I don't know ASM :( ( https://www.avrfreaks.net/index.p... ).
What I was wondering if someone has already made something like this and has code to share in C/C++ so I can see how he implemented the transmission and reception of the data.

Cheers,
reSpawn.

Nicu reSpawn.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Host to device communication:
1. Bring the Clock line low for at least 100 microseconds.
2. Bring the Data line low.
3. Release the Clock line.
4. Wait for the device to bring the Clock line low.
5. Set/reset the Data line to send the first data bit
6. Wait for the device to bring Clock high.
7. Wait for the device to bring Clock low.
8. Repeat steps 5-7 for the other seven data bits and the parity bit
9. Release the Data line.
10. Wait for the device to bring Data low.
11. Wait for the device to bring Clock low.
12. Wait for the device to release Data and Clock

I have one question, how do I "bring the data line low" ? by setting that port's bit output to 0 or to 1 ?

Nicu reSpawn.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Usually low is 0 and high is 1.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yes, but If leave it to 0V I don't influence in any way the CLK right? this is why I'm confused

Nicu reSpawn.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You will be using the clock line for two way communications, so when you (the host) are the active party you set its DDR bit to output and write a 0 (usually low) or 1 (usually high) as required.

When you "release the clock line" you need to set its DDR bit to an input. Then you read that input to get the mouse's response.

Same with the data line. Is this the source of your confusion?

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

can you take a look at this code I've wrote?

/*
CLK, DATA
CLK <-> (INT0) PD2
DATA <-> PB0
*/
#define CLK			2
#define CLK_PORT	PORTD
#define DATA		0
#define DATA_PORT	PORTB
void send(uint8_t packet)
{
	uint8_t parity = 0;              
	uint8_t mask = 0;
	parity = (packet & 0x01) ^
            ((packet & 0x02) >> 1) ^ 
            ((packet & 0x04) >> 2) ^ 
            ((packet & 0x08) >> 3) ^ 
            ((packet & 0x10) >> 4) ^ 
            ((packet & 0x20) >> 5) ^ 
            ((packet & 0x40) >> 6) ^ 
            ((packet & 0x80) >> 7);
	bit_set( CLK_PORT, BIT(CLK) );  	// 1. Bring the Clock line low for at least 100 microseconds.
	delay_us(120);
	bit_set( DATA_PORT, BIT(DATA) );  // 2. Bring the Data line low.
	bit_unset( CLK_PORT, BIT(CLK) ); 	// 3. Release the Clock line.

	while(bit_is_set( CLK_PORT, BIT(CLK) );  // 4. Wait for the device to bring the Clock line low.
	for(i = 0; i < 8; i++)
	{
		DATA_PORT.DATA = !(packet & mask); // 5. Set/reset the Data line to send the first data bit
		while(bit_is_clear( CLK_PORT, BIT(CLK) )); // 6. Wait for the device to bring Clock high.
		while(bit_is_set( CLK_PORT, BIT(CLK) ));	// 7. Wait for the device to bring Clock low. 
		mask = mask << 1;
	}

	// Send the parity bit
	DATA_PORT.DATA = parity; // 5. Set/reset the Data line to send the first data bit
	while(bit_is_clear( CLK_PORT, BIT(CLK) );// 6. Wait for the device to bring Clock high.
	while(bit_is_set( CLK_PORT, BIT(CLK) ); // 7. Wait for the device to bring Clock low. 
	bit_unset( DATA_PORT, BIT(DATA) );  // 9. Release the Data line. 

	while(bit_is_set( DATA_PORT, BIT(DATA) ); // 10. Wait for the device to bring Data low.
	while(bit_is_set( CLK_PORT, BIT)(CLK) ); // 11. Wait for the device to bring Clock low.

especially look at the for loop, at this part PORTD.1 = !(packet & mask); I don't really understand how I have to send the bit. If it's a 1 do I have to send it as a 0 ?:o

Nicu reSpawn.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With just a very quick look I don't see any data direction register (DDR) manipulation. Maybe I'm blind, but if not, do some reading on how you set pins to be inputs and/or outputs. Check out the device's datasheet for information.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah, yes!

/*
CLK, DATA
CLK <-> (INT0) PD2
DATA <-> PB0
*/
#define CLK			2
#define CLK_PORT	PORTD
#define CLK_DDR		DDRD
#define DATA		0
#define DATA_PORT	PORTB
#define DATA_DDR	DDRB
void send(uint8_t packet)
{
	uint8_t parity = 0;              
	uint8_t mask = 0;
	parity = (packet & 0x01) ^
            ((packet & 0x02) >> 1) ^ 
            ((packet & 0x04) >> 2) ^ 
            ((packet & 0x08) >> 3) ^ 
            ((packet & 0x10) >> 4) ^ 
            ((packet & 0x20) >> 5) ^ 
            ((packet & 0x40) >> 6) ^ 
            ((packet & 0x80) >> 7);
	bit_set( CLK_DDR, BIT(CLK) ); // set CLK as output
	bit_set( CLK_PORT, BIT(CLK) );  	// 1. Bring the Clock line low for at least 100 microseconds.
	delay_us(120);
	bit_set( DATA_DDR, BIT(DATA) ); // set DATA as output
	bit_set( DATA_PORT, BIT(DATA) );  // 2. Bring the Data line low.
	bit_clear( CLK_PORT, BIT(CLK) ); 	// 3. Release the Clock line.
	bit_clear( CLK_DDR, BIT(CLK) ); // set CLK as input

	while(bit_is_set( CLK_PORT, BIT(CLK) );  // 4. Wait for the device to bring the Clock line low.
	for(i = 0; i < 8; i++)
	{
		if(!(packet & mask)) // 5. Set/reset the Data line to send the first data bit
			bit_set( DATA_PORT, BIT(DATA) );
		else
			bit_clear( DATA_PORT, BIT(DATA) );
		while(bit_is_clear( CLK_PORT, BIT(CLK) )); // 6. Wait for the device to bring Clock high.
		while(bit_is_set( CLK_PORT, BIT(CLK) ));	// 7. Wait for the device to bring Clock low. 
		mask = mask << 1;
	}

	// Send the parity bit
	if(parity) // 5. Set/reset the Data line to send the first data bit
		bit_set( DATA_PORT, BIT(DATA) );
	else
		bit_clear( DATA_PORT, BIT(DATA) );
	while(bit_is_clear( CLK_PORT, BIT(CLK) );// 6. Wait for the device to bring Clock high.
	while(bit_is_set( CLK_PORT, BIT(CLK) ); // 7. Wait for the device to bring Clock low. 
	bit_clear( DATA_PORT, BIT(DATA) );  // 9. Release the Data line. 
	
	bit_clear( DATA_DDR, BIT(DATA) ); // set DATA as input
	while(bit_is_set( DATA_PORT, BIT(DATA) ); // 10. Wait for the device to bring Data low.
	while(bit_is_set( CLK_PORT, BIT)(CLK) ); // 11. Wait for the device to bring Clock low.
}

How does it look now? does it look right ? :?

Nicu reSpawn.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

the code is not good, it hangs my atmega and I'm tired of changing something then flashing it :|. I gave up :( I will try again tomorrow.

Nicu reSpawn.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Very seldom do you need to talk to the device. With a keyboard you may want to turn the lights on and off, but there's nothing to control on a mouse. Concentrate on receiving the data first, it's much less complicated. The ports stay as inputs, always. Set a pin change interrupt on the clock line and sample data on the falling edge.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, actually there are some options I can control at a mouse, but I'll leave that for now.

volatile uint8_t parity = 0;
volatile uint8_t bit_data = 0;
volatile uint8_t bit_count = 0;
SIGNAL( INT0_vect )
{
	while(bit_is_set( CLK_PORT, BIT(CLK) ) );	//wait for clock low
	
	if(bit_count == 0)
		bit_count++; // 1 start bit. This is always 0.
	else if(bit_count >= 1 && bit_count <= 9) // 8 data bits, least significant bit first.
	{
		if( bit_is_set(DATA_PORT, BIT(DATA)) )
			bit_set( bit_data, BIT(bit_count++));
		else
			bit_set( bit_data, BIT(bit_count++));
	}
	else if(bit_count == 10)
	{
		if( bit_is_set(DATA_PORT, BIT(DATA)) )
			parity = 1;
		else
			parity = 0;
		bit_count++;
	}
	else if(bit_count == 11)
	{
		bit_count = 0; //1 stop bit. This is always 1.
		mouseData.append( bit_data );
	}
}

void enable_int0_interrupt()
{
	// ISC11 ISC10 Description
	// 1 0 The falling edge of INT1 generates an interrupt request.
	bit_set( MCUCR, BIT(ISC01) );
	bit_clear( MCUCR, BIT(ISC00) );
	GIMSK  |= (1<<INT0);
	sei();
}

void disable_int0_interrupt()
{
	bit_clear(GIMSK, BIT(INT0) );		//Disable interrupts
}

int main()
{
    DDRB = 0b11101110; //PB4 = MISO 
    DDRC = 0b11111110; //
    DDRD = 0b11111100; //PORTD (RX on PD0)

	bit_clear( DATA_DDR, BIT(DATA) );
	bit_clear( DATA_PORT, BIT(DATA) );
	bit_clear( CLK_DDR, BIT(CLK) );
	bit_clear( CLK_PORT, BIT(CLK) );
	

	mouseData.Init( mouseDataPtr, 0x10 );

	wdt_enable( WDTO_500MS );
	UART::Init( 0, 9600 );

	enable_int0_interrupt();

	adcInit();
	adcSetPrescaler(ADC_PRESCALE_DIV128);

	//set OC1A and OC1B for pwm output :)
	bit_set(PORTB, BIT(1));
	bit_set(PORTB, BIT(2));
	
	uint8_t c = 0;

	for( ;; ) {
		wdt_reset();
		if( !mouseData.isEmpty() )
		{
			uint16_t temp2 = mouseData.getFromFront();
				printf("got %i\n", temp2);
		}

		if(adcConvert10bit(0) > 500)
			bit_clear(PORTC, BIT(1));
		else
			bit_set(PORTC, BIT(1));
		
		if( (c = UART::getByte()) )
		{
			(void)printf( "code = %c\n", c);
			if(c == '1')
			{
				(void)printf( "ADCH = %i\n", adcConvert10bit(0) );
			}

			else if(c == '5')
			{
				disable_int0_interrupt();
				send(0xFF);
				enable_int0_interrupt();
			}
			else if(c == '6')
			{
			}
			UART::sync();
		}
	}
	
	return 0;
}

It doesn't work. I even tried to print messages when the I receive a SIGNAL and I get it only when I plug my mouse in the breadboard. :(
Shouldn't I receive an SIGNAL for every bit received ? That's 11 bits per packet.

Quote:
As I mentioned in the previous section, the keyboard and mouse use a serial protocol with 11-bit frames. These bits are:

* 1 start bit. This is always 0.
* 8 data bits, least significant bit first.
* 1 parity bit (odd parity).
* 1 stop bit. This is always 1.

The keyboard/mouse writes a bit on the Data line when Clock is high, and it is read by the host when Clock is low. Figures 2 and 3 illustrate this.

Nicu reSpawn.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can also try the library in the project section. I haven't yet tried it myself (I am about to), but I have looked at the code, and it's readable and complete (all mouse functions are implemented.)
https://www.avrfreaks.net/index.p...

There was a discussion on this a couple of months ago, and it seems that a minor fix might be needed at one point. Check out
https://www.avrfreaks.net/index.p...

I hope this helps.
Cheers,
Zoltan