RGB Matrix with Bouncing Balls and PS2 Mouse Control.

By brad, July 11, 2013

Firstly, A video of the display in action:

I bought a 32×32 RGB Matrix display from China. Well more specifically it was from a helpful guy named James who works at www.wanzhouled.net

I have searched the internet for the cheapest prices on Matrix displays and James is by far the cheapest. A 32×32 RGB Matrix was only $22 (with postage being approx $10) Send an email to them via their site to get a price list.

And on to the project…

I had previously seen on youtube a simple bouncing balls animation and thought it looked really cool.

I decided to try my own version with my new display – on top of that, I wanted to finally get around to connecting up a PS2 mouse to a microcontroller. This proved to be somewhat difficult but I got there in the end.

 

The PICnDuino was used at the heart of this project (this is a dual microcontroller development platform that I made back in 2012, you can see more info on that HERE). More specifically, I am using the PIC18F25K20 microcontroller contained on the PICnDuino. Any Microcontroller should work fine as long as you have enough PORT pins.

Here is the PICnDuino Connected to the 32×32 RGB Matrix:
PICnDuinoConnectedToMatrix

Here is how to connect the PICnDuino to the 32×32 RGB Matrix and PS2 Mouse. (It shouldn’t be to difficult to use this diagram for connecting to other microcontrollers).
PICnDuinoToRGBMatrixAndPS2mouse

I included the resistors on the mouse lines because one of them (RC6) is also used to communicate with the FTDI chip on the PICnDuino board. I put two in for good measure.

Here is a picture of the display in action showing the balls and a HELLO message:

32x32MatrixHello

Here is how I connected the mouse to the PICnDuino:
PS2MouseConnection

How the screen works:

It is quite interesting how these matrix displays actually do their thing I.E. draw pixels on the screen. I will do my best to explain and will use my Swordfish Basic code to help out:

First of all, I have given nicknames to all the port pins:

// Port Setup
Dim RedData0 As PORTB.0
Dim RedData1 As PORTB.1
Dim GreenData0 As PORTB.2
Dim GreenData1 As PORTB.3
Dim BlueData0 As PORTB.4
Dim BlueData1 As PORTB.5
Dim Latch As PORTB.6 
Dim OutputEnable As PORTB.7
Dim RowA As PORTC.0
Dim RowB As PORTC.1 
Dim RowC As PORTC.2
Dim RowD As PORTC.3
Dim CLK As PORTC.4

 

Then I have a routine that deals with sending all the data to the 32×32 Pixel Display:

Sub DrawGraphics()
    For Y = 0 To 15
        TempData0 = OutputDataRed(Y)
        TempData1 = OutputDataRed(Y + 16)       
        TempData2 = OutputDataGreen(Y)
        TempData3 = OutputDataGreen(Y + 16) 
        TempData4 = OutputDataBlue(Y)
        TempData5 = OutputDataBlue(Y + 16)
        For X = 0 To 31
            RedData0 = TempData0.bits(31 - X)
            RedData1 = TempData1.bits(31 - X) 
            GreenData0 = TempData2.bits(31 - X)
            GreenData1 = TempData3.bits(31 - X) 
            BlueData0 = TempData4.bits(31 - X)
            BlueData1 = TempData5.bits(31 - X)
            CLK = 1
            CLK = 0
        Next
        RowA = Y.bits(0)
        RowB = Y.bits(1)
        RowC = Y.bits(2) 
        RowD = Y.bits(3)
        Latch = 1
        Latch = 0  
        OutputEnable = 0
        DelayUS(150)
        OutputEnable = 1
    Next
End Sub

 

The screen has 32 rows, each containing 32 pixels. To draw a complete image, you need to send in 32 bits of data (serially) to each of the 6 Color lines, these being:

  • Red 0
  • Red 1
  • Green 0
  • Green 1
  • Blue 0
  • Blue 1

You then need to decide which rows these 32bits will show up on. That’s where the ROW A, B, C and D lines come into play. Since there are four row select lines, you are able to have 16 different combinations to select the rows. So here’s all the combinations and the resulting Rows your Red, Green and Blue data will end up:

DCBA
0000 - (R0, G0 AND B0 WILL BE ON ROW 0) (R1, G1 AND B1 WILL BE ON ROW 16)
0001 - (R0, G0 AND B0 WILL BE ON ROW 1) (R1, G1 AND B1 WILL BE ON ROW 17)
0010 - (R0, G0 AND B0 WILL BE ON ROW 2) (R1, G1 AND B1 WILL BE ON ROW 18)
0011 - (RO, G0 AND B0 WILL BE ON ROW 3) (R1, G1 AND B1 WILL BE ON ROW 19)
0100 - (R0, G0 AND B0 WILL BE ON ROW 4) (R1, G1 AND B1 WILL BE ON ROW 20)
0101 - (R0, G0 AND B0 WILL BE ON ROW 5) (R1, G1 AND B1 WILL BE ON ROW 21)
0110 - (R0, G0 AND B0 WILL BE ON ROW 6) (R1, G1 AND B1 WILL BE ON ROW 22)
0111 - (R0, G0 AND B0 WILL BE ON ROW 7) (R1, G1 AND B1 WILL BE ON ROW 23)
1000 - (R0, G0 AND B0 WILL BE ON ROW 8) (R1, G1 AND B1 WILL BE ON ROW 24)
1001 - (R0, G0 AND B0 WILL BE ON ROW 9) (R1, G1 AND B1 WILL BE ON ROW 25)
1010 - (R0, G0 AND B0 WILL BE ON ROW 10) (R1, G1 AND B1 WILL BE ON ROW 26)
1011 - (R0, G0 AND B0 WILL BE ON ROW 11) (R1, G1 AND B1 WILL BE ON ROW 27)
1100 - (R0, G0 AND B0 WILL BE ON ROW 12) (R1, G1 AND B1 WILL BE ON ROW 28)
1101 - (R0, G0 AND B0 WILL BE ON ROW 13) (R1, G1 AND B1 WILL BE ON ROW 29)
1110 - (R0, G0 AND B0 WILL BE ON ROW 14) (R1, G1 AND B1 WILL BE ON ROW 30)
1111 - (R0, G0 AND B0 WILL BE ON ROW 15) (R1, G1 AND B1 WILL BE ON ROW 31)

Hopefully you can see that when we draw the screen, we are drawing two rows at the same time, and these two rows are 16 rows away from each other. The first rows to be drawn will be the top row and middle(ish) row. Then we keep moving down by one row but always keeping a 16 row separation between the two drawn rows.

So back to my code, first up I am loading some data that was stored in an array, into a temp location so I can use it in my routine to draw pixels on the screen.

Note – Each TempData location holds 32bits (which is perfect for a 32 pixel wide display)

For Y = 0 To 15
        TempData0 = OutputDataRed(Y)
        TempData1 = OutputDataRed(Y + 16)       
        TempData2 = OutputDataGreen(Y)
        TempData3 = OutputDataGreen(Y + 16) 
        TempData4 = OutputDataBlue(Y)
        TempData5 = OutputDataBlue(Y + 16)

Notice how I am using a for loop? Also notice that the for loop will run this code 16 times (Starting from 0 and then incrementing by 1 each time until we reach 15). This lines up with the number of rows we will cycle through on the screen. Remember we draw two rows at a time since the screen is made of 32 rows, we only need to run through the code 16 times.

Can you also see that I am loading into TempData1, the OutputDataRed component that is 16 steps away from what got loaded into TempData0? And then the same again for green then blue.

This is because we are drawing two rows at a time and the second row we’re drawing is always 16 rows apart from the first row.

Once this data is loaded into the easy to access TempData locations, we need to serially send the six lots of 32 bits into the display:

For X = 0 To 31
        RedData0 = TempData0.bits(31 - X)
        RedData1 = TempData1.bits(31 - X) 
        GreenData0 = TempData2.bits(31 - X)
        GreenData1 = TempData3.bits(31 - X) 
        BlueData0 = TempData4.bits(31 - X)
        BlueData1 = TempData5.bits(31 - X)
        CLK = 1
        CLK = 0
Next

To do this, we need another for loop. This time the for loop will run the code 32 times. Each time it will send through one bit from the TempData variables. Basically I tell the port pins which bits to be present on them, then I send a clock pulse which is connected to all the shift registers within the 32×32 matrix. I do this 32 times and then all the 32 bits of Red, Green and Blue for the first row will be loaded in aswell as the 32 bits of Red, Green and Blue for the second row.

Note – the reason I have written in

(31 - X)

in the code is because if I just had

(X)

the image would be drawn backwards. (Essentially I need to send the last bit first, and the first bit last)

Now that we have sent in all six lots of 32 bits (2 lines for Red, 2 lines for Green and 2 lines for Blue) We then need to tell the matrix which rows to show this data up on. That’s where the ROW A,B,C and D lines come in (remember, there are 16 combinations of 1’s and 0’s on four lines)

So here is the code that tells the data which lines to show up on:

        RowA = Y.bits(0)
        RowB = Y.bits(1)
        RowC = Y.bits(2) 
        RowD = Y.bits(3)
        Latch = 1
        Latch = 0  
        OutputEnable = 0
        DelayUS(150)
        OutputEnable = 1
    Next
End Sub

First it will put a four bit number on the Row A, B, C and D lines. It get’s this info from the Y variable. Let’s assume this is the first time that we have run this code (I.E. Y = 0) this means that the four bit binary representation of 0 is 0000. So all Row lines will have a zero on them.

Looking at the Row table above, you can see that with 0000 on the row lines, it will place the newly loaded RowData0 on the very top row and then RowData1, 16 rows down from that. Let’s say that we had been running through this loop a number of times now and Y was 10. that means that the binary number would be 1010. These digits would then be sent to Row A, B, C and D – this would then send the loaded RowData0 to line 10 and then RowData1 16 rows away at line 26.

So we need to latch it in to do this we have this piece of code:

        Latch = 1
        Latch = 0

Now were are nearly there! The data is now in place. All that’s left to do is enable the tri-state buffers connected to all these LED’s – to do this you just need this line of code:

        OutputEnable = 0

The LED’s will now turn on with their data. We hold it there for just a short while and then turn them off again, with this code:

        DelayUS(150)
        OutputEnable = 1

Then we need to loop back and do it again 15 more times!

    Next
End Sub

 

That’s how to draw to the display, so in order to display what you want, you just need to fill up your Red, Green and Blue arrays with a whole bunch of 1’s and 0’s. This is how I declare them:

Dim OutputDataRed(32) As LongWord 
Dim OutputDataGreen(32) As LongWord
Dim OutputDataBlue(32) As LongWord

 

I then make sure all of the data contained in this array is cleared when I first start. Here’s how we do that:

Sub ClearScreen()
    For X = 0 To 31
        OutputDataRed(X) = 0
        OutputDataGreen(X) = 0
        OutputDataBlue(X) = 0
    Next
End Sub

 

If you wanted to simply show a single red dot in the top right of the screen, all you would need to do is this:

OutputDataRed(0) = %00000000000000000000000000000001

 

By the way, OutputDataRed(0) is the top row of the screen and OutputDataRed(31) is the bottom row. (Same goes for green and blue). So the code above has now given us a single red pixel on the screen.

If you wanted a blue box that outlines the screen you would do this:

OutputDataBlue(00) = %11111111111111111111111111111111
OutputDataBlue(01) = %10000000000000000000000000000001
OutputDataBlue(02) = %10000000000000000000000000000001
OutputDataBlue(03) = %10000000000000000000000000000001
OutputDataBlue(04) = %10000000000000000000000000000001
OutputDataBlue(05) = %10000000000000000000000000000001
OutputDataBlue(06) = %10000000000000000000000000000001
OutputDataBlue(07) = %10000000000000000000000000000001
OutputDataBlue(08) = %10000000000000000000000000000001
OutputDataBlue(09) = %10000000000000000000000000000001
OutputDataBlue(10) = %10000000000000000000000000000001
OutputDataBlue(11) = %10000000000000000000000000000001
OutputDataBlue(12) = %10000000000000000000000000000001
OutputDataBlue(13) = %10000000000000000000000000000001
OutputDataBlue(14) = %10000000000000000000000000000001
OutputDataBlue(15) = %10000000000000000000000000000001
OutputDataBlue(16) = %10000000000000000000000000000001
OutputDataBlue(17) = %10000000000000000000000000000001
OutputDataBlue(18) = %10000000000000000000000000000001
OutputDataBlue(19) = %10000000000000000000000000000001
OutputDataBlue(20) = %10000000000000000000000000000001
OutputDataBlue(21) = %10000000000000000000000000000001
OutputDataBlue(22) = %10000000000000000000000000000001
OutputDataBlue(23) = %10000000000000000000000000000001
OutputDataBlue(24) = %10000000000000000000000000000001
OutputDataBlue(25) = %10000000000000000000000000000001
OutputDataBlue(26) = %10000000000000000000000000000001
OutputDataBlue(27) = %10000000000000000000000000000001
OutputDataBlue(28) = %10000000000000000000000000000001
OutputDataBlue(29) = %10000000000000000000000000000001
OutputDataBlue(30) = %10000000000000000000000000000001
OutputDataBlue(31) = %11111111111111111111111111111111

Note – I purposely wrote the code out the long way above just to help you visualize.

If you wanted a yellow face in the top left corner, you would need to write the same data for green and red:

OutputDataRed(00) =   %0111111000000000000000000000000
OutputDataRed(01) =   %1000000100000000000000000000000
OutputDataRed(02) =   %1010010100000000000000000000000
OutputDataRed(03) =   %1000000100000000000000000000000
OutputDataRed(04) =   %1010010100000000000000000000000
OutputDataRed(05) =   %1001100100000000000000000000000
OutputDataRed(06) =   %1000000100000000000000000000000
OutputDataRed(07) =   %0111111000000000000000000000000

OutputDataGreen(00) = %0111111000000000000000000000000
OutputDataGreen(01) = %1000000100000000000000000000000
OutputDataGreen(02) = %1010010100000000000000000000000
OutputDataGreen(03) = %1000000100000000000000000000000
OutputDataGreen(04) = %1010010100000000000000000000000
OutputDataGreen(05) = %1001100100000000000000000000000
OutputDataGreen(06) = %1000000100000000000000000000000
OutputDataGreen(07) = %0111111000000000000000000000000

Hopefully these examples give you an idea of how it all works.

 

And now onto how the PS2 Mouse works.

 

The PS2 mouse was quite hard to get to work. I found all sorts of web sites on the topic although they all seemed to make it all to confusing. Eventually I figured it out with the help of some Arduino code I found HERE. Thankyou to the person who wrote that!

So how does it work?


That’s a good question. Have you ever tried to connect a PS2 mouse to a computer when the computer is already turned on. It doesn’t actually work – the reason is because the computer first needs to send some commands to the mouse to essentially tell the mouse to start transmitting data. Without these commands, the mouse won’t do anything.

I found some good info HERE that details a bit about the commands that you can send to the PS2 mouse.

Here is how I got my microcontroller to get the mouse to send data:

  1. Tell the mouse to reset itself by sending the command ‘FF’
  2. Tell the mouse to operate in read mode by sending the command ‘F0’
  3. Tell the mouse that we want to change the sample rate by sending the command ‘F3’
  4. Set sample rate to decimal 200 by sending the hex equivalent value of ‘C8’
  5. Tell the mouse that we want to change the resolution by sending the command ‘E8’
  6. Set the resolution to the lowest setting (1 count per mm) by sending the hex value ’00’

If you don’t mind the default sample rate and resolution, you can get rid of steps 3, 4, 5 and 6.

 

You may say – ‘well that sounds simple enough!’ well unfortunately it wasn’t. I needed to work out how to talk to the mouse and how to listen to the mouse.

The PS2 mouse has a bi-directional serial data bus and a bi-directional clock. The microcontroller needs to keep changing it’s port pins between being inputs and outputs (depending on if we are sending data to the mouse or receiving from the mouse.)

First up, here are the PORT and TRIS nicknames for the mouse (we need the TRIS because sometimes we need to make the PORT pins inputs and sometimes outputs.)

Dim MouseClockTris As TRISC.5
Dim MouseDataTris As TRISC.6
Dim MouseClock As PORTC.5
Dim MouseData As PORTC.6

 

Here’s the code I used to send a command to the mouse:

Sub InitMouse2(InstructionToSend As Word)
    MouseClock = 1          // make sure we start with a logic 1
    MouseData = 1           // make sure we start with a logic 1
    MouseClockTris = 0      // allow the microcontroller to control the clock line
    MouseDataTris = 0       // allow the microcontroller to control the data line
    DelayUS(100)            // need to hold it for 100uS
    MouseClock = 0          // then set the clock line low to tell the mouse we want to talk to it
    DelayUS(100)            // hold it for 100uS
    MouseData = 0           // and then we set the data line low (part of the communication process)
    DelayUS(5)              // hold it for 5uS
    MouseClock = 1          // set the clock line back to a 1 (we will then make it an input and wait for the mouse to make it low again
    MouseClockTris = 1      // set the clock line to an input to allow the mouse to take control
    DelayUS(5)              // we need a slight delay here just to give the mouse some time to take control of the clock pin
    While MouseClock <> 0   // wait for the mouse to make it a 0
        // do nothing
    Wend
    // now the mouse has caused the clock to go low, we can start sending data.
    For X = 0 To 9  // we send 10 bits, 1 byte of data + odd parity bit + logic 1 stop bit
        MouseData = instructiontosend.bits(X)
        While MouseClock <> 1
            // wait for clock to go high
        Wend
        While MouseClock <> 0
            // and now wait for it to go low again...
        Wend
    Next
    While MouseClock <> 1
        // wait until the mouse makes the clock line high
    Wend
End Sub

 

Firstly, the microcontroller needs to set the clock and data lines high and these two port pins as outputs. We then hold the two lines high for 100uS:

MouseClock = 1          
MouseData = 1          
MouseClockTris = 0
MouseDataTris = 0
DelayUS(100)

Then pull the MouseClock line low and hold it for 100uS, this indicates to the mouse that we want to talk to it:

MouseClock = 0
DelayUS(100)

 

Then pull the MouseData line low and hold it for 5uS (this is still part of the mouses communication protocol):

MouseData = 0
DelayUS(5)

 

The mouse should now understand that we want to talk to it. The last thing we do before giving control to the mouse is set the MouseClock back to a logic 1. (the mouse will make it low when it is ready to acknowledge that it now has control of the communication process):

MouseClock = 1

 

Then we set the microcontroller MouseClock pin to an input to allow the mouse to send us a reply. We then pause for 5uS to give the mouse time to get all set up:

MouseClockTris = 1
DelayUS(5)

 

And now we wait in a constant loop until the mouse pulls the MouseClock line low. Basically this code keeps checking the MouseClock pin until the mouse makes it a logic 0. I.E. if it does not equal 0, it will stay in the loop. When it does equal zero, it will move on:

While MouseClock <> 0

Wend

 

Now that the mouse has acknowledged that we want to send it data, we can actually start to send that data. We need to send it an eight bit command, followed by an ODD parity bit and then finally a stop bit which is always a logic 1. So if we wanted to send the reset command, we would have called the InitMouse2 routine like this:

InitMouse2(%1111111111)

 

This will pass the binary number 1111111111 into the sub routine InitMouse2. The sub routine InitMouse2 will then refer to this binary number as InstructionToSend and is declared as a word (which can hold 16bits). Just for clarification, here is the heading for the subroutine InitMouse2:

Sub InitMouse2(InstructionToSend As Word)

 

If we break down all those logic 1’s. We are sending the command ‘FF’ which in binary is 11111111. Then we need to add to the left of that an ODD parity bit. Since there is an even number of 1’s, we need to make the parity bit a 1 to make it an odd number of 1’s. Then finally we always need to add a stop bit of 1 to the left of the parity bit. This now gives us ten binary digits: 1111111111 and this is now contained within the variable InstructionToSend:

    For X = 0 To 9  
        MouseData = instructiontosend.bits(X)
        While MouseClock <> 1
            // wait for clock to go high
        Wend
        While MouseClock <> 0
            // and now wait for it to go low again...
        Wend
    Next

 

The above for loop, sends out each of these ten bits one after the other along the serial data bus (MouseData). We send the least significant bit first. We need to stay in the loop ten times (because there are ten bits to send I.E. bits 0 to 9)

    For X = 0 To 9  
        MouseData = instructiontosend.bits(X)

 

Now that the first bit is present on the MouseData line, we need to wait for the mouse clock to synchronise the data. Once the mouse causes the clock line to go high and then low again, we can go to the next bit I.E. bit 1, then again we will wait for the mouse clock to go high and then low and then loop around again for bit 2 etc…

        While MouseClock <> 1

        Wend
        While MouseClock <> 0

        Wend
    Next

 

Once all bits have been sent, we will exit the for loop.  Then we wait for the mouse to make the clock line low again:

    While  MouseClock <> 1

    Wend

 

And that is how we send a command to the mouse!

Here is the code to send all the commands to the mouse that I used in conjunction with the 32×32 RGB LED Matrix:

sub InitMouse()
    InitMouse2(%1111111111)   // reset mouse by sending $FF (then an odd parity bit of 1 and then stop bit of 1)
    InitMouse2(%1111110000)   // tell the mouse to operate in read mode by sending $F0 (odd parity bit of 1 and stop bit of 1)  
    InitMouse2(%1111110011)   // tell mouse we want to set sample rate by sending $F3 (odd parity bit of 1 and stop bit of 1) 
    InitMouse2(%1011001000)   // then set sample rate to decimal 200 by sending $c8 (odd parity bit of 1 and stop bit of 1)
    InitMouse2(%1111101000)   // tell mouse we want to set resolution by sending $E8 (odd parity bit of 1 and stop bit of 1) 
    InitMouse2(%1100000000)   // then set resolution to the lowest (1 count per 1mm) (odd parity bit of 1 and stop bit of 1)
end sub

 

Remember you only need the first two commands, to get the mouse to run in receive mode:

sub InitMouse()
    InitMouse2(%1111111111)   // reset mouse by sending $FF (then an odd parity bit of 1 and then stop bit of 1)
    InitMouse2(%1111110000)   // tell the mouse to operate in read mode by sending $F0 (odd parity bit of 1 and stop bit of 1)  
end sub

 

Now that we have told the mouse to operate in receive mode, we then request data from it whenever we want to by sending the command ‘EB’. Once the mouse recognises this command, it will send us four bytes of data. The first byte is an acknowledgment byte which should always be ‘FA’. It’s the next three bytes that we want to store:

  1. The second byte contains the x overflow flag, y overflow flag, y sign bit, x sign bit, always 1, middle button status, right button status and left button status. The ones we would be most concerned with here is probably just bit 0 and bit 1 which are our left and right mouse buttons.
  2. The third byte gives us the x direction of the mouse in 2’s compliment form If the mouse is still, this byte will be 00000000. If we move it left or right, we will get a number proportional to how fast we are moving it. I.E. the faster you move it, the higher the number.
  3. The fourth byte gives us the y direction of the mouse in 2’s compliment form. It works the same as the third byte.

 

Here is the code to receive and store these three bytes of data:

Sub SaveMouseData()
    InitMouse2(%1111101011)   // request data from mouse
    ReceiveMouseData          // this first one should always give back %11111010 ($FA) because this is the mouses first response which means acknowledge
    ReceiveMouseData          // this second one is like this (Y overflow, X overflow, Y sign bit, X sign bit, always 1, middle button, right button, left button)              
        MouseByte(0) = MouseDataIn
    ReceiveMouseData          // this third one is the x direction of the mouse (2s compliment form) 
        MouseByte(1) = MouseDataIn
    ReceiveMouseData          // this fourth on is the y direction of the mouse (2s compliment form)
        MouseByte(2) = MouseDataIn
End Sub

 

We first send the command ‘EB’ to the mouse to tell it to send us data:

InitMouse2(%1111101011)   // request data from mouse

 

Then we have another sub routine that handles receiving data from the mouse. I.E. as soon as we have sent the command to the mouse to send us data, we need to straight away get ready to receive the reply:

ReceiveMouseData

 

The first time we call this, we don’t need to save it because the mouse always responds with an acknowledgement reply (which will always be ‘FA’ if everything went to plan)

We then call receive data again and this time we want to save the data:

    ReceiveMouseData  
    MouseByte(0) = MouseDataIn

 

We then want to receive the next byte and save it:

   ReceiveMouseData  
    MouseByte(1) = MouseDataIn

 

And finally, the last byte:

    ReceiveMouseData  
    MouseByte(2) = MouseDataIn

 

Here is the code that handles receiving data from the mouse:

Sub ReceiveMouseData() 
    MouseClock = 1
    MouseData = 1
    MouseClockTris = 0
    MouseDataTris = 0
    DelayUS(5)
    MouseClockTris = 1  // set it back as an input and then wait for the mouse to take control
    DelayUS(5)
    While MouseClock <> 0
        // keep waiting until the mouse sends the clock line low
    Wend
    DelayUS(5)
    While MouseClock <> 1
        // wait for the mouse to send the clock high
    Wend
    MouseDataTris = 1   // to receive data, we need to make the data line an input
    // now that the clock has gone high, we need to start reading the incoming data
    For X = 0 To 7
        While MouseClock <> 0   // wait for it to go to a 0
        Wend
        MouseDataIn.bits(X) = MouseData
        While MouseClock <> 1   // wait for it to go to a 1
        Wend
    Next
    // grab the parity bit (and ignore it)
    While MouseClock <> 0   // wait for it to go to a 0
    Wend                 
    While MouseClock <> 1   // wait for it to go to a 1
    Wend 
    // grab the stop bit (and ignore it)
    While MouseClock <> 0   // wait for it to go to a 0
    Wend                 
    While MouseClock <> 1   // wait for it to go to a 1
    Wend
    MouseClockTris = 0
    MouseClock = 0
End Sub

It is quite similar to the previous code except that this time we allow the mouse to send us data on the MouseData line, we then receive the data bit by bit and then save it to a global variable MouseDataIn.

 

And that’s how I got the screen and mouse working!

Here is the complete Swordfish Basic code for my Bouncing Balls with mouse painting program (download also provided at the bottom of this page). Please note that this program also uses the RandGen2.bas file which you can get HERE

Code Designed to run straight on the PICnDuino or PIC18F25K20 with a 16Mhz external Oscillator

Device = 18F25K20    //Automatically brings in device file 18F25K22.bas
Clock = 64           //64MHz (top speed)
Config FOSC = HSPLL   //tells PIC to use external high speed (crystal) oscillator, medium power

Include "utils.bas"      // we are including an extra file here which allows us to use shortcuts    
Include "RandGen2.bas"

Structure Ball
    Color As Byte
    X As Byte
    Y As Byte
    XDelay As Byte
    XDelay2 As Byte
    YDelay As Byte
    YDelay2 As Byte
    GoingUp As Boolean
    GoingLeft As Boolean
    MaxJumpHeight As Byte
    CurrentJumpHeight As Byte
End Structure

// Variables
Dim Multiball(20) As Ball

Dim X As Byte
Dim Y As Byte
Dim MouseDelay As Byte
Dim TempData0 As LongWord
Dim TempData1 As LongWord
Dim TempData2 As LongWord
Dim TempData3 As LongWord
Dim TempData4 As LongWord
Dim TempData5 As LongWord

Dim OutputDataRed(32) As LongWord 
Dim OutputDataGreen(32) As LongWord
Dim OutputDataBlue(32) As LongWord
Dim MouseDataRed(32) As LongWord 
Dim MouseDataGreen(32) As LongWord
Dim MouseDataBlue(32) As LongWord
Dim MouseDataBarrier(32) As LongWord

Dim MouseByte(3) As Byte
Dim MouseDataIn As Byte
Dim PaintColor As Byte

// Port Setup
Dim RedData0 As PORTB.0
Dim RedData1 As PORTB.1
Dim GreenData0 As PORTB.2
Dim GreenData1 As PORTB.3
Dim BlueData0 As PORTB.4
Dim BlueData1 As PORTB.5
Dim Latch As PORTB.6 
Dim OutputEnable As PORTB.7
Dim RowA As PORTC.0
Dim RowB As PORTC.1 
Dim RowC As PORTC.2
Dim RowD As PORTC.3
Dim CLK As PORTC.4

Dim MouseClockTris As TRISC.5
Dim MouseDataTris As TRISC.6
Dim MouseClock As PORTC.5
Dim MouseData As PORTC.6
Dim MouseCursorX As Byte
Dim MouseCursorY As Byte

// Sub Routines
Sub BounceTheBall()
    Dim TempBall As Ball
    For X = 0 To Bound(Multiball)
        TempBall = Multiball(X)
        If TempBall.XDelay <> 0 Then
            TempBall.XDelay = TempBall.XDelay - 1
        Else
            TempBall.XDelay = TempBall.XDelay2
            If TempBall.GoingLeft = false Then
                TempBall.X = TempBall.X + 1
                If MouseDataBarrier(tempball.X + 1).bits(tempball.Y) = 1 Then // check to see if there is a wall to the right of us
                    TempBall.GoingLeft = true
                EndIf
                If tempball.X > 31 Then  // if we have been moving right and we go out of the right hand side of the screen, then make it appear at the left side.
                    tempball.X = 0
                EndIf
            Else
                TempBall.X = TempBall.X - 1
                If MouseDataBarrier(tempball.X - 1).bits(tempball.Y) = 1 Then  // check to see if there is a wall to the left of us
                    TempBall.GoingLeft = false
                EndIf
                If tempball.X > 128 Then  // if we have been moving left and we go out of the left hand side of the screen, then make it appear at the right side. (going left will eventually go below 0 to 255, so I just check if its greater than 128 but I could have probably said if it equals 255 etc..
                    tempball.X = 31
                EndIf
            EndIf
        EndIf

        If TempBall.YDelay <> 0 Then
            TempBall.YDelay = TempBall.YDelay - 1
        Else
            If TempBall.GoingUp = false Then                          // if the ball is going down
                If TempBall.YDelay2 > 0 Then                           // only take one away if we are still greater than 0 (0 is the fastest we can get because its the lowest delay)
                    TempBall.YDelay2 = TempBall.YDelay2 - 1
                EndIf
                TempBall.YDelay = TempBall.YDelay2    
                TempBall.Y = TempBall.Y + 1
            Else                                                    // if the ball is not going down, then it must be going up - so we need to slow the ball down                 
                TempBall.YDelay2 = TempBall.YDelay2 + 1
                TempBall.YDelay = TempBall.YDelay2
                TempBall.Y = TempBall.Y - 1
                TempBall.CurrentJumpHeight = TempBall.CurrentJumpHeight + 1
                If MouseDataBarrier(tempball.X).bits(tempball.Y - 1) = 1 Then
                    TempBall.GoingUp = false 
                EndIf
            EndIf
            If MouseDataBarrier(tempball.X).bits(tempball.Y + 1) = 1 And tempball.GoingUp = false Then // Check the pixel just below us to see if we can bounce off of it
                TempBall.YDelay = 0
                TempBall.YDelay2 = 0
                TempBall.GoingUp = true
                TempBall.CurrentJumpHeight = 0
            End If
            If tempball.Y > 31 Then // if its gone below the bottom of the screen, then send it back to the top!
                tempball.Y = 0
            EndIf

            If TempBall.GoingUp = true And TempBall.CurrentJumpHeight >= TempBall.MaxJumpHeight Then
                TempBall.GoingUp = false
            EndIf
        EndIf
        Multiball(X) = TempBall
    Next   
End Sub

Sub InitMouse2(InstructionToSend As Word)
    MouseClock = 1          // make sure we start with a logic 1
    MouseData = 1           // make sure we start with a logic 1
    MouseClockTris = 0      // allow the microcontroller to control the clock line
    MouseDataTris = 0       // allow the microcontroller to control the data line
    DelayUS(100)            // need to hold it for 300uS
    MouseClock = 0          // then set the clock line low to tell the mouse we want to talk to it
    DelayUS(100)            // hold it for 300uS
    MouseData = 0           // and then we set the data line low (part of the communication process)
    DelayUS(5)              // hold it for 10uS
    MouseClock = 1          // set the clock line back to a 1 (we will then make it an input and wait for the mouse to make it low again
    MouseClockTris = 1      // set the clock line to an input to allow the mouse to take control
    DelayUS(5)              // we need a slight delay here just to give the mouse some time to take control of the clock pin and to allow the mouse to be the one who makes the clock line 0. (without a delay, the microcontroller will think that the mouse has taken control and it will move onto the next piece of code - before the mouse is ready for the data)
    While MouseClock <> 0   // wait for the mouse to make it a 0
         //do nothing
    Wend
    // now the mouse has caused the clock to go low, we can start sending data.
    For X = 0 To 9  // we send 10 bits, 1 byte of data + odd parity bit + logic 1 stop bit
        MouseData = instructiontosend.bits(X)
        While MouseClock <> 1
            // wait for clock to go high
        Wend
        While MouseClock <> 0
            // and now wait for it to go low again...
        Wend
    Next
    While MouseClock <> 1
        // wait for the mouse to make this line high again
    Wend
End Sub

Sub ReceiveMouseData() 
    MouseClock = 1
    MouseData = 1
    MouseClockTris = 0
    MouseDataTris = 0
    DelayUS(5)
    MouseClockTris = 1  //set it back as an input and then wait for the mouse to take control
    DelayUS(5)
    While MouseClock <> 0
        // keep waiting until the mouse sends the clock line low
    Wend
    DelayUS(5)
    While MouseClock <> 1
        // wait for the mouse to send the clock high
    Wend
    MouseDataTris = 1   // to receive data, we need to make the data line an input
    // now that the clock has gone high, we need to start reading the incoming data
    For X = 0 To 7
        While MouseClock <> 0   // wait for it to go to a 0
        Wend
        MouseDataIn.bits(X) = MouseData
        While MouseClock <> 1   // wait for it to go to a 1
        Wend
    Next
    // grab the parity bit (and ignore it)
    While MouseClock <> 0   // wait for it to go to a 0
    Wend                 
    While MouseClock <> 1   // wait for it to go to a 1
    Wend 
    // grab the stop bit (and ignore it)
    While MouseClock <> 0   // wait for it to go to a 0
    Wend                 
    While MouseClock <> 1   // wait for it to go to a 1
    Wend
    MouseClockTris = 0
    MouseClock = 0
End Sub

Sub SaveMouseData()
    InitMouse2(%1111101011)   // request data from mouse
    ReceiveMouseData    // this first one should always give back %11111010 ($FA) because this is the mouses first response which means acknowledge
    ReceiveMouseData   // this second one is like this (Y overflow, X overflow, Y sign bit, X sign bit, always 1, middle button, right button, left button)              
        MouseByte(0) = MouseDataIn
    ReceiveMouseData    // this third one is the x direction of the mouse (2s compliment form) 
        MouseByte(1) = MouseDataIn
    ReceiveMouseData    // this fourth on is the y direction of the mouse (2s compliment form)
        MouseByte(2) = MouseDataIn
End Sub

// All we need to do is store 32 long words in our output data registers, and this routine will take care of displaying them on the screen!
Sub DrawGraphics()
    For Y = 0 To 15
        TempData0 = OutputDataRed(Y)
        TempData1 = OutputDataRed(Y + 16)       
        TempData2 = OutputDataGreen(Y)
        TempData3 = OutputDataGreen(Y + 16) 
        TempData4 = OutputDataBlue(Y)
        TempData5 = OutputDataBlue(Y + 16)
        For X = 0 To 31
            RedData0 = TempData0.bits(31 - X)
            RedData1 = TempData1.bits(31 - X) 
            GreenData0 = TempData2.bits(31 - X)
            GreenData1 = TempData3.bits(31 - X) 
            BlueData0 = TempData4.bits(31 - X)
            BlueData1 = TempData5.bits(31 - X)
            CLK = 1
            CLK = 0
        Next
        RowA = Y.bits(0)
        RowB = Y.bits(1)
        RowC = Y.bits(2) 
        RowD = Y.bits(3)
        Latch = 1
        Latch = 0  
        OutputEnable = 0
        DelayUS(150)
        OutputEnable = 1
    Next
End Sub

Sub ClearScreen()
    For X = 0 To 31
        OutputDataRed(X) = 0
        OutputDataGreen(X) = 0
        OutputDataBlue(X) = 0
    Next
End Sub

Sub MoveMouseCursor()
        MouseCursorX = MouseCursorX + MouseByte(1)
        If MouseCursorX > 31 And MouseCursorX < 128 Then    // if the cursor goes past the screen max (31 pixels) we want to hold it at 31 pixels (we need to check that its also less than 128 because if we go past screen min (pixel 0) the mousecursor will go to 255. 
            MouseCursorX = 31
        ElseIf MouseCursorX > 128 Then
            MouseCursorX = 0
        EndIf 
        MouseCursorY = MouseCursorY - MouseByte(2)
        If MouseCursorY > 31 And MouseCursorY < 128 Then 
            MouseCursorY = 31
        ElseIf MouseCursorY > 128 Then
            MouseCursorY = 0
        EndIf 
        If MouseByte(0).bits(0) = 1 Then    // draw if only left button pressed
            MouseDataBarrier(MouseCursorX).bits(MouseCursorY) = 1   // any color, we want to be a barrier\
            MouseDataRed(MouseCursorX).bits(MouseCursorY) = 1
        EndIf
        If MouseByte(0).bits(1) = 1 Then
            MouseDataBarrier(MouseCursorX).bits(MouseCursorY) = 0   // any color, we want to be a barrier\
            MouseDataRed(MouseCursorX).bits(MouseCursorY) = 0
        EndIf
        If MouseByte(0).bits(0) = 1 And MouseByte(0).bits(1) = 1 Then  // clear the screen if both buttons pressed
            For X = 0 To 31
                MouseDataRed(X) = 0
                MouseDataGreen(X) = 0
                MouseDataBlue(X) = 0
                MouseDataBarrier(X) = 0
            Next
        EndIf
End Sub

Sub SaveGraphicData()
    ClearScreen 
    OutputDataRed(MouseCursorX).bits(MouseCursorY) = 1
    OutputDataGreen(MouseCursorX).bits(MouseCursorY) = 1
    OutputDataBlue(MouseCursorX).bits(MouseCursorY) = 1
    For X = 0 To 31
        OutputDataRed(X) = OutputDataRed(X) Or MouseDataRed(X) 
        OutputDataGreen(X) = OutputDataGreen(X) Or MouseDataGreen(X)
        OutputDataBlue(X) = OutputDataBlue(X) Or MouseDataBlue(X)

        For Y = 0 To Bound(Multiball)
            If Multiball(Y).X = X Then
                OutputDataRed(X).bits(Multiball(Y).Y) = Multiball(Y).Color.bits(0)
                OutputDataGreen(X).bits(Multiball(Y).Y) = Multiball(Y).Color.bits(1)
                OutputDataBlue(X).bits(Multiball(Y).Y) = Multiball(Y).Color.bits(2)
            EndIf
        Next
    Next
End Sub

Sub InitBalls()
For X = 0 To Bound(Multiball)
    Multiball(X).X = rand()
    Multiball(X).Y = rand() / 1
    Multiball(X).YDelay = (rand() / 4 + 1)
    Multiball(X).YDelay2 = Multiball(X).YDelay
    Multiball(X).MaxJumpHeight = 5
    Multiball(X).GoingLeft = false
    Multiball(X).GoingUp = false
    Multiball(X).XDelay = (rand() / 4 + 1)  
    Multiball(X).XDelay2 = Multiball(X).XDelay
    Multiball(X).Color = X
    If Multiball(X).Color = 0 Then
        Multiball(X).Color = 7
    EndIf
Next
End Sub

Sub ClearMouseData()            
For X = 0 To 31
    MouseDataRed(X) = 0
    MouseDataGreen(X) = 0
    MouseDataBlue(X) = 0
    MouseDataBarrier(X) = 0
Next
End Sub

Sub InitMouse()
InitMouse2(%1111111111)   // reset mouse by sending $FF (then an odd parity bit of 1 and then stop bit of 1)
InitMouse2(%1111110000)   // tell the mouse to operate in read mode by sending $F0 (odd parity bit of 1 and stop bit of 1)  
InitMouse2(%1111110011)   // tell mouse we want to set sample rate by sending $F3 (odd parity bit of 1 and stop bit of 1) 
InitMouse2(%1011001000)   // then set sample rate to decimal 200 by sending $c8 (odd parity bit of 1 and stop bit of 1)
InitMouse2(%1111101000)   // tell mouse we want to set resolution by sending $E8 (odd parity bit of 1 and stop bit of 1) 
InitMouse2(%1100000000)   // then set resolution to the lowest (1 count per 1mm) (odd parity bit of 1 and stop bit of 1)
End Sub

// Start Of Program...
SetAllDigital
TRISB = %00000000
TRISA = %00000000                   
TRISC = %00000000
SetRndMax(31)
InitBalls
ClearScreen
ClearMouseData

DelayMS(250)    
InitMouse

MouseCursorX = 15
MouseCursorY = 12

// Main Loop
While True()
    BounceTheBall
    SaveMouseData
    MoveMouseCursor
    SaveGraphicData
    DrawGraphics
Wend

I hope you found this page interesting and informative!

 

God Bless.

Downloads

32x32LEDMatrixBouncyBallsWithPS2Mouse

 

  • Pingback: Fun with LED matrix and mouse()

  • Pingback: rndm(mod) » Fun with LED matrix and mouse()

  • Pingback: Fun with LED matrix and mouse | Blog of MPRosa()

  • Pingback: Fun with LED matrix and mouse | Make, Electronics projects, electronic Circuits, DIY projects, Microcontroller Projects - makeelectronic.com()

  • bogdanm

    Hello,

    Very nice project! Can you please provide the link to the actual module that you’re using? I’m not sure if I’ve located the correct one on their page.

    Thanks,
    Bogda

    • admin

      Hi Bogda, I didn’t get it directly from their site but just got in touch with them by email. The one that I got was the P5 32×32 RGB Matrix. It is 160mm x 160mm and has an LED spacing of 5mm.

      send an email to: salesb#wanzhouled,net (replace the # with @ and the , with .)

      address it to James, and say that you got the email from Brad @ bradsprojects.com and that you would like to buy a P5 32×32 RGB LED Matrix including shipping to where ever you live. I asked for the cheapest shipping which was $10 but you can get express DHL / TNT shipping but it costs extra.

      Hope that helps!

      • bogdanm

        Thanks a lot, that definitely helps!

        Best,
        Bogdan

        (and yes, I’ve spelt my own name wrong the first time 🙂 )

        • admin

          I think we’ve all spelt our name wrong at some stage in our lives…

          Regards,

          Admi

  • Jakamoto

    Hi,

    first off all great project and work with the display.

    I have a project in mind that would make use of this kind of displays.
    Do you have any idea or expirience on chaining these together and displaying content? (unanimated pictures would be enough)
    I was thinking on chaining 4 Pieces of 16X16 P20 displays together for an dynamic PixelArt-WallArt. This way I’d get a decent sized one for my living room.

    Thanks in advance,

    Jakamoto

    • admin

      Hi Jakamoto, are you looking perhaps at something like this:
      http://ledpixelart.com/

      • Jakamoto

        Hi and thanks for your fast response!
        You’re right, thats basically what I’m trying to achieve.
        I know the Ledpixelart, but I find the size quite small (the image area is only 19.7×19.7 cm) and I’d like to put a “waffle grid” for seperating the LED-Pixels (no light bleed-through) and have a diffusor plate in front.
        The size I’m aiming for is minimum around 40×40 cm, with the 4 16×16 P20 Displays it would turn out just nice.
        The only thing I’m missing is the kowledge if chaining and driving it with a µC as you did would be possible.

        Kind Regards,

        Jakamoto

        • admin

          You sure can chain them together using just one microcontroller however there are limits to this due to the speed that the screens need to refresh at to avoid flicker VS the speed that you can get all the bits sent to the screens ready for displaying the data.

          Another thing to think about is the number of colors you would like the screen to be able to display. I have been able to achieve 512 colors from a 16×32 RGB matrix but then when I tried this with a 32×32 pixel RGB matrix, there was too much flicker (due to the microcontroller not being able to send out the data, then display the data quick enough) The microcontroller was running at 64MHz.

          But if all you want is pixel art at 32×32 pixels and maybe 16 or possibly 32 colors, then you could certainly achieve that with a single microcontroller.

  • Ben

    Awesome project and just got my matrix board from James (they were great and made things easy).

    But the zip file for the code download appears to be broken. It downloads as a zip file, but is just a message that the page/file can’t be found. Can you double check this and update the link?

  • admin

    Hi Ben,

    Thanks for letting me know. It seems my download monitor plugin was corrupting all downloads on my site. I have removed it and all should be working now 🙂

    Let me know if you have any hassles.