Breadboard Games 2022 Assembly

Note on board co-ordinates:

There are two sets of co-ordinates shown on the board. We will be using the ones that are the “right way up” i.e. the ones shown on the left side of the image. Some of the images can be a little misleading because of the angle the photos were taken. This is particularly true for the red and black wires at the top left of the image. The red wire connects to 3V3 to any hole just above the red line. The black wire connects GND to any hole just below the blue wire.

Display wiring:

White : Column 32, Row A to any hole just above the red line (keep it left of Column 30)

Purple : Column 31, Row A to GP22

Pink : Column 30, Row A to GP20

Blue : Column 29, Row A to GP21

Orange : Column 28, Row A to GP19

Yellow : Column 27, Row A to GP18

Red : Column 26, Row A to any hole just above the red line (3.3V)

Black : Column 25, Row A to any hole just below the blue line (0V)

Buttons: Place as shown.

Black wires (0V) These are used connect GND (0V) signals together.

Link wires between buttons

Column 42, Row E to Column 43, Row D

Column 42, Row F to Column 43, Row G

Button wiring. Try to get at least some of the button wires into the channel between the top and bottom halves of the breadboard.

Brown : GP26 to Column 55, Row A

Grey : GP27 to Column 50, Row A

Purple : GP17 to Column 14, Row E

Buzzer: GP9 to any hole just below the blue line

Yellow: GP16 to Column 36, Row C

White : GP15 to Column 45, Row C

Green: GP14 to Column 40, Row F

WARNING : ASK FOR ASSISTANCE WITH THIS. DISPLAY ARE FRAGILE!

Fit the display as shown in the row of holes just below the display wires. The rightmost pin of the display should line up with the small white wire.

Breaboard games 2022

Breadboard Games 2022 is approaching. This year we will have 16 students assemble the game console shown above. The console consists of a Raspberry Pi Pico linked to an ST7735, some buttons and a piezo speaker. The schematic is shown below

Two simple games are featured: A version of break out and a simple Santa Clause game. These games are written in Micropython with an emphasis on ease of understanding rather than game play. Nevertheless, the graphics module is reasonably optimized with an inline assembler module that executes very quickly and which is used by API functions as much as possible. Furthermore, the sound module makes use of the second core in the Pico which allows sound to be be played at the same time as the game.

The game sprites are edited in a bitmap editor such as Windows Paint or KolourPaint etc. The sprites are created as 24 bit bitmap (bmp) files. A python script converts these bitmaps to python arrays on the host computer. These arrays are coded as 16 bit RGB (5-6-5) colour values which are compatible with the ST7735. These arrays are then included in a sprite module in the game.

Programs were edited using Thonny which is a nice cross-platform editor that works with a number of operating systems. It can be installed without administrator permissions on Windows and Linux systems.

Code is over here on github.

Speeding up some micropython with a touch of inline assembly on the Raspberry Pi Pico

I have been working on a graphics library for the ST7735 and the Raspberry Ri Rico. The first version was written in pure micropython and worked well enough but was quite slow – especially when writing out blocks of colour (fillRectangle). This was the original code for fillRectangle:

def fillRectangle(self,x1,y1,w,h,colour):
        self.openAperture(x1,y1,x1+w-1,y1+h-1)        
        pixelcount=h*w
        self.command(0x2c)
        self.a0.value(1)        
        msg=bytearray()
        while(pixelcount >0):
            pixelcount = pixelcount-1          
            msg.append(colour >> 8)
            msg.append(colour & 0xff)
        self.spi.write(msg)

Not only was this slow, it also required that a buffer be created that held the filled rectangle in RAM. This was slow and memory intensive.

The new version looks like this:

def fillRectangle(self,x1,y1,w,h,colour):
        self.openAperture(x1,y1,x1+w-1,y1+h-1)        
        pixelcount=h*w
        self.command(0x2c)
        self.a0.value(1)
        self.fill_block(colour,pixelcount) 

It makes use of an inline assembler function the source code of which is as follows:

@micropython.asm_thumb
    def fill_block(r0,r1,r2):
        # pointer to self passed in r0
        # r1 contains the 16 bit data to be written
        # r2 countains count
        # Going to use SPI0.
        # Base address = 0x4003c000
        # SSPCR0 Register OFFSET 0
        # SSPCR1 Register OFFSET 4
        # SSPDR Register OFFSET 8
        # SSPSR Register OFFSET c
        push({r1,r2,r3,r4,r7})
        # Convoluted load of a 32 value into r7
        mov(r7,0x40)
        lsl(r7,r7,8)
        add(r7,0x03)
        lsl(r7,r7,8)
        add(r7,0xc0)
        lsl(r7,r7,8)
        add(r7,0x00)
        mov(r4,2)        
        label(fill_block_loop_start)
        cmp(r2,0)
        beq(fill_block_exit)        
        mov(r3,r1) # read next byte
        lsr(r3,r3,8)
        strb(r3,[r7,8]) # write to SPI
        label(fill_block_spi_wait1)        
        ldr(r3,[r7,0xc]) # read next byte
        and_(r3,r4)
        beq(fill_block_spi_wait1)
        
        mov(r3,r1) # read next byte        
        strb(r3,[r7,8]) # write to SPI        
        sub(r2,r2,1) # decrement count                
        label(fill_block_spi_wait2)        
        ldr(r3,[r7,0xc]) # read next byte
        and_(r3,r4)
        beq(fill_block_spi_wait2)
        b(fill_block_loop_start)
        
        label(fill_block_exit)
        pop ({r1,r2,r3,r4,r7})

This writes the colour value directly to the SPI port the required number of times. It needs to pause when the SPI FIFO fills up (hence he need for the labels fill_block_spi_wait1/2).

The performance improvement is about a factor of 20!

Code is available over on gihub and is likely to change lots in the next couple of weeks while I prepare for a STEM event.