https://www.adafruit.com/product/420
Python file: •https://gist.github.com/ScienceElectronicsFun/aa84ac095d66a1f6e114c0cbc9aa4a58
8051 assembly file: •https://gist.github.com/ScienceElectronicsFun/fc68753311d5008b7ae7401c67d91a68
Eagle files:
Schematic
Project File
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Version 0.2 | |
#Different color bricks now supported. | |
import random | |
import serial | |
import time | |
u = serial.Serial("COM13", baudrate=9600, timeout=10) | |
class block(): | |
def __init__(self): | |
self.generate_block() | |
self.location = [8, 1] | |
def generate_block(self): | |
''' | |
Generate block of random type and color. | |
Returns block matrix and color. | |
''' | |
self.block_type = random.randint(0,5) | |
c = random.randint(1,3) | |
self.color = c | |
if self.block_type == 0: | |
self.block = [[c,c,c], | |
[c,c,c], | |
[c,c,c]] | |
self.rotated = self.block | |
elif self.block_type == 1: | |
self.block = [[c,0,0], | |
[c,c,0], | |
[c,c,c]] | |
self.rotated = [[c,c,c], | |
[0,c,c], | |
[0,0,c]] | |
elif self.block_type == 2: | |
self.block = [[c], | |
[c], | |
[c]] | |
self.rotated = [[c,c,c]] | |
elif self.block_type == 3: | |
self.block = [[c], | |
[c], | |
[c], | |
[c]] | |
self.rotated = [[c,c,c,c]] | |
elif self.block_type == 4: | |
self.block = [[c,c], | |
[c,c]] | |
self.rotated = self.block | |
elif self.block_type == 5: | |
self.block = [[c,c,0], | |
[0,c,c]] | |
self.rotated = [[0,c], | |
[c,c], | |
[c,0]] | |
def rotate(self): | |
temp = self.block | |
self.block = self.rotated | |
self.rotated = temp | |
def check_collision(self, m): | |
''' | |
Checks to see if the forward face of the block touches an element | |
of given matrix object 'm'. | |
''' | |
collision = False | |
x = self.location[0] | |
y = self.location[1] | |
for i, row in enumerate(self.block): | |
#print(row) | |
furthest = 0 | |
#In the block, find the furthest to the right it extends to. | |
for j, col in enumerate(row): | |
if self.block[i][j] > 0: | |
furthest = j | |
#Furthest is j | |
#Now, does the matrix have a block in the next position? | |
if m.data[i+x][furthest+y+1] > 0: | |
collision = True | |
break | |
return collision | |
def generate_merge(self, m): | |
''' | |
Adds the block to the given matrix object 'm' for display purposes. | |
''' | |
x = self.location[0] | |
y = self.location[1] | |
c = self.color | |
for i, row in enumerate(self.block): | |
for j, col in enumerate(row): | |
if self.block[i][j] > 0: | |
m.data[x+i][y+j] = c | |
def subtract(self, m): | |
''' | |
Removes the block from the given matrix object 'm'. | |
This is done prior to moving the block to a new location. | |
''' | |
x = self.location[0] | |
y = self.location[1] | |
for i, row in enumerate(self.block): | |
for j, col in enumerate(row): | |
if self.block[i][j] > 0: | |
m.data[x+i][y+j] = 0 | |
class matrix(): | |
''' | |
The matrix class is a 16 (row) x 32 (column) matrix where 0 = off and 1 = on. | |
The translate function converts to a stream of 256 bytes that can be sent to the '8051 driver | |
to display the pattern on the RGB LED matrix. | |
''' | |
def __init__(self): | |
r = [0 for i in range(0, 32)] | |
#self.data initializes to an empty matrix. | |
self.data = [list(r) for i in range(0, 16)] | |
self.stream = None | |
self.bytes = None | |
self.max = 25 | |
self.color_map = {1:(2, 1), | |
2:(8, 4), | |
3:(64, 32)} | |
def check_for_full_cols(self): | |
full_col_list = [] | |
for i in range(0, self.max): | |
if all([x[i] for x in self.data]): | |
full_col_list.append(i) | |
for col in full_col_list: | |
for k in range(0, 16): | |
for j in range(1,i+1): | |
self.data[k][j] = self.data[k][j-1] | |
def translate(self): | |
#Translates pattern to byte stream to drive the RGB LED matrix. | |
self.stream = [0 for i in range(0,256)] | |
#Top half | |
for k in range(0, 8): | |
for i, member in enumerate(self.data[k]): | |
if member > 0: | |
self.stream[223-i-k*32] += self.color_map[member][0] | |
#Bottom half | |
for k in range(8, 16): | |
for i, member in enumerate(self.data[k]): | |
if member > 0: | |
self.stream[223-i-((k-8)*32)] += self.color_map[member][1] | |
self.bytes = '' | |
#Convert to hex | |
for byte in self.stream: | |
self.bytes += chr(byte) | |
def dump_matrix(self): | |
''' | |
Prints matrix object for debugging. | |
''' | |
print() | |
for row in self.data: | |
print(''.join([str(x) for x in row])) | |
print() | |
def readfile(filename): | |
''' | |
Reads a csv file matrix object. | |
Returns grid which can be loaded into m.data. | |
''' | |
file_r = open(filename, 'r') | |
data = file_r.readlines() | |
grid = [] | |
for line in data: | |
newline = [int(x.strip()) for x in line.split(',')] | |
grid.append(newline) | |
return grid | |
def start_game(): | |
m = matrix() | |
# This sets bottom brick layer | |
t = 25 | |
for i in range(0,16): | |
m.data[i][t] = 1 | |
m.translate() | |
game_running = True | |
points = 0 | |
while game_running: | |
m.check_for_full_cols() | |
#Start new round | |
round_running = True | |
b = block() | |
c = b.check_collision(m) | |
if c: | |
game_running = False | |
round_running = False | |
while round_running: | |
#Display the new block | |
b.generate_merge(m) | |
m.translate() | |
u.write('\x10'.encode()) | |
u.write(m.bytes.encode()) | |
time.sleep(0.01) | |
c = b.check_collision(m) | |
if c: | |
round_running = False | |
if round_running: | |
b.subtract(m) | |
#Move the block to the right | |
b.location[1] = b.location[1] + 1 | |
#If command pending, move | |
u.write('\x11'.encode()) | |
current = u.read() | |
if current == bytes(chr(2).encode()): | |
b.location[0] += 1 | |
if current == bytes(chr(4).encode()): | |
b.location[0] -= 1 | |
if current == bytes(chr(8).encode()): | |
b.rotate() | |
if current == bytes(chr(16).encode()): | |
b.location[0] -= 2 | |
start_game() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; Used Tom C. Hayes codes as templates (bit flip.a51, serial message receive.a51, int_inc_dec.a51) | |
; from Learning the Art of Electronics | |
$NOSYMBOLS ; keeps listing short.. | |
$INCLUDE (C:\MICRO\8051\RAISON\INC\c8051f410.inc) | |
$INCLUDE (C:\MICRO\8051\RAISON\INC\VECTORS320.INC) | |
CLOCK EQU P0.0 | |
LATCH EQU P0.1 | |
TIMER96K EQU 96h | |
ORG 0 ; tells assembler the address at which to place this code | |
SJMP STARTUP ; here code begins--with just a jump to start of | |
; real program. ALL our programs will start thus | |
ORG 80h ; ...and here the program starts | |
STARTUP: | |
CLR 0 | |
CLR 1 | |
CLR 2 | |
CLR 3 | |
ACALL USUAL_SETUP | |
ACALL TIMER_SETUP | |
ACALL SERIAL_SETUP | |
ACALL INTERRUPT_SETUP | |
SETB LATCH ;LATCH IS ACTIVE LOW | |
CLR CLOCK | |
RESET: | |
MOV R3, #00h ; This holds the current byte | |
MOV P2, #00h ; ADDRESS | |
MOV R2, #00h ; ADDRESS COUNTER | |
MOV P1, #00h ; port 1 = GGXXBBRR | |
MOV R1, #00h; increment for serial | |
MOV DPTR, #0000h ; initialize the data pointer | |
;SCRATCH REGISTERS USED | |
;R0 CLOCK PULSES | |
;R1 INCREMENT FOR SERIAL DATA INPUT | |
;R2 ADDRESS COUNTER (LINE ON DISPLAY) | |
;R3 BYTE INDEX FOR DISPALY | |
MAIN_LOOP: | |
JNB RI0, REFRESH_DISPLAY ; await receipt of a byte from UART | |
CLR RI0 ; ...clear flag when it's asserted | |
MOV A, SBUF0 | |
CJNE A, #10h, CHECK_NEXT | |
SJMP RESET | |
CHECK_NEXT: | |
CJNE A, #11h, STORE_IT ; PROGRAM REQUEST KEY STATUS | |
MOV R7, #00h | |
JNB 0, CHECK_UP_DIR | |
MOV R7, #02h | |
CLR 0 ; CLEAR THE FLAG | |
SJMP SEND_COMMAND | |
;MOV R7, #41h | |
CHECK_UP_DIR: | |
JNB 1, CHECK_ROTATE | |
MOV R7, #04h | |
CLR 1 | |
SJMP SEND_COMMAND | |
CHECK_ROTATE: | |
JNB 2, CHECK_DROP | |
MOV R7, #08h | |
CLR 2 | |
SJMP SEND_COMMAND | |
CHECK_DROP: | |
JNB 3, SEND_COMMAND | |
MOV R7, #10h | |
CLR 3 | |
SEND_COMMAND: | |
ACALL SENDIT | |
SJMP RESET | |
;SJMP REFRESH_DISPLAY | |
STORE_IT: | |
MOVX @R1, A ; get received data | |
INC R1 | |
REFRESH_DISPLAY: | |
CLR LATCH | |
MOV R0, #20h ; R0 holds clock pulses, EACH ADDRESS 4 bytes of clocks (32) | |
PULSES: | |
;CLOCK IN PULSES TO R1,2/G1,2/B1,2 | |
ACALL TICKTOCKBYTE | |
;FOUR BYTES OF PULSES CLOCKED IN TO CURRENT ADDRESS | |
DJNZ R0, PULSES | |
;LATCH DATA FROM ADDRESS | |
SETB LATCH | |
CLR LATCH | |
;INCREMENT ADDRESS | |
INC P2 | |
INC R2 | |
;ALL ADDRESSES COMPLETE? | |
CJNE R2, #08, REFRESH_DISPLAY ; NO KEEP GOING | |
;YES, SET ADDRESS BACK TO 0 | |
MOV P2, #00h | |
MOV R2, #00h | |
;ANOTHER ROUND | |
SJMP MAIN_LOOP | |
USUAL_SETUP: | |
ANL PCA0MD, #NOT(040h) ; Clear Watchdog Enable bit | |
MOV OSCICN, #087h | |
MOV XBR1, #40h ; Enable Crossbar | |
MOV P0MDOUT, #0DFh | |
MOV P1MDOUT, #0FFh | |
MOV P2MDOUT, #0FFh | |
MOV XBR0, #01h | |
RET | |
TIMER_SETUP: | |
;mov TCON, #040h | |
MOV TMOD, #20h ; Timer 1: Mode 2 (8-bit reload) (Dallas sample pgm, p. 299) | |
MOV TH1, #96h ; Reload for 9600, if SMOD = 0 (table, p. 296, again) | |
;MOV TL1, #TIMER96K ; This is for first-pass only | |
MOV TL1, #00h | |
ANL (PCON), #07Fh ; clear SMOD bit (this is default, but let's be thorough): | |
; prevents baud-rate doubling at Serial Port 0 (p. 21) | |
RET | |
SERIAL_SETUP: | |
mov SCON0, #10h ; set receive enable, 8-bit rather than "9-bit" or what the Dallas pgm calls 10-bit | |
SETB TR1 ; Start Timer | |
ret | |
SENDIT: | |
MOV SBUF0, R7 ; Put character in buffer (send it)--UART 1 | |
LINGER: JNB TI0, LINGER ; wait here till told it's been sent | |
CLR TI0 | |
RET | |
TICKTOCK: | |
SETB CLOCK | |
CLR CLOCK | |
RET | |
TICKTOCKBYTE: | |
ACALL GETCODE | |
MOV P1, A | |
SETB CLOCK | |
CLR CLOCK | |
INC R3 | |
DONE: | |
RET | |
; SUBROUTINE GETCODE | |
; LOAD R3 WITH INDEX OF CODE YOU WANT (0 INDEXED) | |
; BYTE RETURNS IN ACCUMULATOR | |
GETCODE: | |
MOV DPL, R3 | |
MOVX A, @DPTR | |
RET | |
INTERRUPT_SETUP: | |
SETB IT0 | |
;SETB IT1 | |
MOV IT01CF, #02h ; SETS INT0* on P0.2 | |
SETB EX0 | |
;SETB EX1 | |
SETB EA | |
RET | |
;BUTTON PUSHED MOVE BLOCK LEFT OR RIGHT | |
ORG INT0VECTOR | |
SJMP ISR0 | |
ISR0: | |
JNB P0.3, BUTTON_A_PRESS | |
JNB P0.6, BUTTON_B_PRESS | |
JNB P0.7, BUTTON_C_PRESS | |
;MUST BE BUTTON_D | |
SETB 3 | |
RETI | |
BUTTON_A_PRESS: | |
SETB 0 | |
RETI | |
BUTTON_B_PRESS: | |
SETB 1 | |
RETI | |
BUTTON_C_PRESS: | |
SETB 2 | |
RETI | |
END | |