Created
June 4, 2022 21:40
-
-
Save samneggs/61488861a53efc19c4400e954908fb1b to your computer and use it in GitHub Desktop.
RGB 64x32 Matrix Clock
This file contains hidden or 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
from machine import Pin, Timer | |
from time import sleep_ms, sleep_us, ticks_diff, ticks_us, sleep, localtime | |
from usys import exit | |
import framebuf, array | |
from uctypes import addressof | |
from micropython import const | |
import _thread | |
from random import randint | |
import gc | |
R1 = Pin(2, Pin.OUT) | |
G1 = Pin(3, Pin.OUT) | |
B1 = Pin(4, Pin.OUT) | |
R2 = Pin(5, Pin.OUT) | |
G2 = Pin(8, Pin.OUT) | |
B2 = Pin(9, Pin.OUT) | |
CLK = Pin(11, Pin.OUT) | |
STB = Pin(12, Pin.OUT) | |
OE = Pin(13, Pin.OUT) | |
LINE_A = Pin(10,Pin.OUT) | |
LINE_B = Pin(16,Pin.OUT) | |
LINE_C = Pin(18,Pin.OUT) | |
LINE_D = Pin(20,Pin.OUT) | |
LINE_E = Pin(22,Pin.OUT) | |
LED = Pin(25,Pin.OUT) | |
IR = Pin(28, Pin.IN) | |
EMPTY = const(0) | |
WATER_COLOR = const(0x001f) | |
SAND_COLOR = const(0xff00) | |
FIXED_COLOR = const(0xffff) | |
RANDOM_ADR = const(0x4006001c) | |
BLUE = const(0x001f) | |
BLACK = const(0) | |
WHITE = const(0xffff) | |
GREEN = const(0x03e0)#e00A) | |
BROWN = const(0xe091) | |
RED = const(0b11111_000000_00000) | |
YELLOW=const(0xff00) | |
MAGENTA=const(0x1FF8) | |
ORANGE = const(0xfc06) | |
MAXSCREEN_X = const(64) | |
MAXSCREEN_Y = const(32) | |
MaxLed = 64 | |
c12 = [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] | |
c13 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0] | |
color_l2 = array.array('H', []) | |
matrix_buffer=bytearray(64*32*2) | |
matrix = framebuf.FrameBuffer(matrix_buffer, 64, 32, framebuf.RGB565) | |
text_buffer = bytearray(64*32*2) | |
big_text = framebuf.FrameBuffer(text_buffer, 64, 32, framebuf.RGB565) | |
control_asm=array.array('I',(addressof(matrix_buffer),0xD0000000,0x40014000,0,99,0,32,0,0b00000_111111_11111,200,0)) | |
T = array.array('h',(2022, 5, 26, 17, 44, 6, 3, 146)) | |
sandctl_asm=array.array('I',(addressof(matrix_buffer),0,64,SAND_COLOR,WATER_COLOR,FIXED_COLOR,RANDOM_ADR,0,0,0)) | |
MATRIX_CTL = const(0) #0 | |
SIO_START_CTL = const(4) #1 | |
GPIO_START_CTL = const(8) #2 | |
LINE_CTL = const(12) #3 | |
DEBUG_CTL = const(16) #4 | |
COUNT_CTL = const(20) #5 | |
SHADES_CTL = const(24) #6 | |
EXIT_CTL = const(28) #7 | |
TEST_CTL = const(32) #8 | |
BRIGHT_CTL = const(36) #9 | |
@micropython.asm_thumb | |
def rgb_asm(r0): | |
label(NEW_SCREEN) | |
ldr(r1,[r0,EXIT_CTL]) | |
cmp(r1,0) | |
beq(DONT_EXIT) | |
b(EXIT) # exit from upy | |
label(DONT_EXIT) | |
ldr(r4,[r0,LINE_CTL]) | |
ldr(r1,[r0,SIO_START_CTL]) # r1=SIO_START_CTL | |
mov(r6,1) | |
lsl(r2,r6,13) # pin 13 (OE) | |
str(r2,[r1,0x14]) # GPIO_SET Register | |
mov(r7,r4) | |
label(CHECK_A) | |
ror(r7,r6) | |
bpl(CLEAR_A) | |
lsl(r2,r6,10) # pin 10 (LINE_A) | |
str(r2,[r1,0x14]) # GPIO_SET Register | |
b(CHECK_B) | |
label(CLEAR_A) | |
lsl(r2,r6,10) # pin 10 (LINE_A) | |
str(r2,[r1,0x18]) # GPIO_CLR Register | |
label(CHECK_B) | |
ror(r7,r6) | |
bpl(CLEAR_B) | |
lsl(r2,r6,16) # pin 16 (LINE_B) | |
str(r2,[r1,0x14]) # GPIO_SET Register | |
b(CHECK_C) | |
label(CLEAR_B) | |
lsl(r2,r6,16) # pin 16 (LINE_B) | |
str(r2,[r1,0x18]) # GPIO_CLR Register | |
label(CHECK_C) | |
ror(r7,r6) | |
bpl(CLEAR_C) | |
lsl(r2,r6,18) # pin 18 (LINE_C) | |
str(r2,[r1,0x14]) # GPIO_SET Register | |
b(CHECK_D) | |
label(CLEAR_C) | |
lsl(r2,r6,18) # pin 18 (LINE_C) | |
str(r2,[r1,0x18]) # GPIO_CLR Register | |
label(CHECK_D) | |
ror(r7,r6) | |
bpl(CLEAR_D) | |
lsl(r2,r6,20) # pin 20 (LINE_D) | |
str(r2,[r1,0x14]) # GPIO_SET Register | |
b(CHECK_E) | |
label(CLEAR_D) | |
lsl(r2,r6,20) # pin 20 (LINE_D) | |
str(r2,[r1,0x18]) # GPIO_CLR Register | |
label(CHECK_E) | |
ror(r7,r6) | |
bpl(CLEAR_E) | |
lsl(r2,r6,22) # pin 22 (LINE_E) | |
str(r2,[r1,0x14]) # GPIO_SET Register | |
b(DONE_LINES) | |
label(CLEAR_E) | |
lsl(r2,r6,22) # pin 22 (LINE_E) | |
str(r2,[r1,0x18]) # GPIO_CLR Register | |
label(DONE_LINES) | |
mov(r5,1) | |
lsl(r5,r5,12) # pin 12 (STB) | |
str(r5,[r1,0x14]) # GPIO_OE_CLR Register | |
mov(r5,1) | |
lsl(r5,r5,12) # pin 12 (STB) | |
str(r5,[r1,0x18]) # GPIO_OUT_SET Register | |
ldr(r4,[r0,LINE_CTL]) | |
add(r4,1) | |
cmp(r4,16) | |
blt(STR_LINE_CTL) | |
mov(r4,0) | |
ldr(r1,[r0,SHADES_CTL]) | |
add(r1,1) | |
cmp(r1,16) | |
blt(NO_RESET) | |
mov(r1,0) | |
label(NO_RESET) | |
str(r1,[r0,SHADES_CTL]) # 0-16 bright level | |
label(STR_LINE_CTL) | |
str(r4,[r0,LINE_CTL]) # 0-63 for line | |
ldr(r1,[r0,SIO_START_CTL]) # r1=SIO_START_CTL | |
mov(r2,0) # r2 = 0-64 (x) | |
label(LOOP) | |
ldr(r3,[r0,LINE_CTL]) # r3=line (y) | |
lsl(r3,r3,6) # =line*64 | |
add(r3,r3,r2) # =(line*64)+r2 | |
add(r3,r3,r3) # double ldrh | |
ldr(r4,[r0,MATRIX_CTL]) # r4=matrix address | |
add(r3,r3,r4) # r3=pixel address | |
ldrh(r4,[r3,0]) # r4=pixel data | |
bl(SETPIXEL1) | |
mov(r7,1) | |
lsl(r7,r7,11) # 2048 = 64*16*2 | |
#str(r7,[r0,DEBUG_CTL]) | |
add(r7,r3,r7) # RGB2 part of matrix | |
ldrh(r4,[r7,0]) # r7=RGB2 matrix pixel | |
bl(SETPIXEL2) | |
bl(DO_CLK) | |
bl(OFF_DELAY) | |
label(NEXT) | |
add(r2,1) | |
cmp(r2,64)#64 | |
blt(LOOP) | |
mov(r5,1) | |
lsl(r5,r5,13) # pin 13 (OE) | |
str(r5,[r1,0x18]) # GPIO_CLR Register | |
bl(ON_DELAY) | |
b(NEW_SCREEN) | |
# -------------------------SUBS--------------------------------- | |
label(DO_CLK) | |
mov(r5,1) | |
lsl(r6,r5,11) # pin 11 (CLK) | |
str(r6,[r1,0x14]) # GPIO_OUT_SET Register | |
str(r6,[r1,0x18]) # GPIO_OE_CLR Register | |
bx(lr) | |
label(ON_DELAY) | |
#mov(r7,200)#32 | |
ldr(r7,[r0,BRIGHT_CTL]) | |
label(LOOP2) | |
sub(r7,1) | |
cmp(r7,0) | |
bgt(LOOP2) | |
bx(lr) | |
label(OFF_DELAY) | |
mov(r7,100)#32 | |
label(LOOP3) | |
sub(r7,1) | |
cmp(r7,0) | |
bgt(LOOP3) | |
bx(lr) | |
label(SETPIXEL1) | |
ldr(r7,[r0,SHADES_CTL]) | |
lsr(r6,r4,11)#11 | |
cmp(r6,r7) | |
ble(CLEAR_RED) | |
mov(r5,1<<2) # pin 2 (R1) | |
str(r5,[r1,0x14]) # GPIO_OUT_SET Register | |
b(CHECK_GREEN) | |
label(CLEAR_RED) | |
mov(r5,1<<2) # pin 2 (R1) | |
str(r5,[r1,0x18]) # GPIO_OE_CLR Register | |
label(CHECK_GREEN) | |
lsl(r6,r4,21) # clear out red | |
lsr(r6,r6,27)#26? # clear out blue | |
cmp(r6,r7) | |
ble(CLEAR_GREEN) | |
mov(r5,1<<3) # pin 3 (G1) | |
str(r5,[r1,0x14]) # GPIO_OUT_SET Register | |
b(CHECK_BLUE) | |
label(CLEAR_GREEN) | |
mov(r5,1<<3) # pin 3 (G1) | |
str(r5,[r1,0x18]) # GPIO_OE_CLR Register | |
label(CHECK_BLUE) | |
lsl(r6,r4,27) # clear red/green | |
lsr(r6,r6,27) | |
cmp(r6,r7) | |
ble(CLEAR_BLUE) | |
mov(r5,1<<4) # pin 4 (B1) | |
str(r5,[r1,0x14]) # GPIO_OUT_SET Register | |
b(PIXEL_DONE) | |
label(CLEAR_BLUE) | |
mov(r5,1<<4) # pin 4 (B1) | |
str(r5,[r1,0x18]) # GPIO_OE_CLR Register | |
label(PIXEL_DONE) | |
bx(lr) | |
label(SETPIXEL2) | |
ldr(r7,[r0,SHADES_CTL]) | |
lsr(r6,r4,11)#11 | |
cmp(r6,r7) | |
ble(CLEAR_RED2) | |
mov(r5,1<<5) # pin 5 (R1) | |
str(r5,[r1,0x14]) # GPIO_OUT_SET Register | |
b(CHECK_GREEN2) | |
label(CLEAR_RED2) | |
mov(r5,1<<5) | |
str(r5,[r1,0x18]) # GPIO_OE_CLR Register | |
label(CHECK_GREEN2) | |
lsl(r6,r4,21) # clear out red | |
lsr(r6,r6,27)#26? # clear out blue | |
cmp(r6,r7) | |
ble(CLEAR_GREEN2) | |
mov(r5,1) | |
lsl(r5,r5,8) # pin 8 (B2) | |
str(r5,[r1,0x14]) # GPIO_OUT_SET Register | |
b(CHECK_BLUE2) | |
label(CLEAR_GREEN2) | |
mov(r5,1) | |
lsl(r5,r5,8) # pin 8 (B2) | |
str(r5,[r1,0x18]) # GPIO_OE_CLR Register | |
label(CHECK_BLUE2) | |
lsl(r6,r4,27) # clear red/green | |
lsr(r6,r6,27) | |
cmp(r6,r7) | |
ble(CLEAR_BLUE2) | |
mov(r5,1) | |
lsl(r5,r5,9) # pin 9 (G2) | |
str(r5,[r1,0x14]) # GPIO_OUT_SET Register | |
b(PIXEL_DONE2) | |
label(CLEAR_BLUE2) | |
mov(r5,1) | |
lsl(r5,r5,9) # pin 9 (g2) | |
str(r5,[r1,0x18]) # GPIO_OE_CLR Register | |
label(PIXEL_DONE2) | |
bx(lr) | |
label(EXIT) | |
ldr(r0,[r0,DEBUG_CTL]) | |
def matrix_reset(): | |
MaxLed=64 | |
OE.value(1) | |
STB.value(0) | |
CLK.value(0) | |
for l in range(0, MaxLed): | |
y = l % 16 | |
R1.value(0)# = False | |
G1.value(0)# = False | |
B1.value(0)# = False | |
R2.value(0)# = False | |
G2.value(0)# = False | |
B2.value(0)# = False | |
if c12[y] == 1: | |
R1.value(1)# = True | |
G1.value(1)# = True | |
B1.value(1)# = True | |
R2.value(1)# = True | |
G2.value(1)# = True | |
B2.value(1)# = True | |
if l > (MaxLed - 12): | |
STB.value(1)# = True | |
else: | |
STB.value(0)# = False | |
CLK.value(1)# = True | |
for delay in range(1000): | |
pass | |
#time.sleep(0.001) #(0.000002) | |
CLK.value(0)# = False | |
STB.value(0)# = False | |
CLK.value(0)# = False | |
for l in range(0, MaxLed): | |
y = l % 16 | |
R1.value(0)# = False | |
G1.value(0)# = False | |
B1.value(0)# = False | |
R2.value(0)# = False | |
G2.value(0)# = False | |
B2.value(0)# = False | |
if c13[y] == 1: | |
R1.value(1)# = True | |
G1.value(1)# = True | |
B1.value(1)# = True | |
R2.value(1)# = True | |
G2.value(1)# = True | |
B2.value(1)# = True | |
if l > (MaxLed - 13): | |
STB.value(1)# = True | |
else: | |
STB.value(0)# = False | |
CLK.value(1)# = True | |
for delay in range(1000): | |
pass | |
#time.sleep(0.000002) | |
CLK.value(0)# = False | |
STB.value(0)# = False | |
CLK.value(0)# = False | |
@micropython.viper | |
def water_rgb16(): # 16 bit water/sand sim | |
screen_addr=ptr16(matrix_buffer) | |
offset = 0 | |
odd = 1 | |
for y in range(31,0,-1): | |
for x in range(61,0,-1): | |
source = screen_addr[(y-1)*MAXSCREEN_X+x] | |
if source == 0: | |
continue | |
dest = screen_addr[y*MAXSCREEN_X+x] | |
dest_m = screen_addr[y*MAXSCREEN_X+x-1] | |
dest_p = screen_addr[y*MAXSCREEN_X+x+1] | |
source_m = screen_addr[((y-1)*MAXSCREEN_X)+x-1] | |
source_p = screen_addr[((y-1)*MAXSCREEN_X)+x+1] | |
source_sand = source == SAND_COLOR | |
dest_water = dest == WATER_COLOR | |
destp_water = dest_p == WATER_COLOR | |
destm_water = dest_m == WATER_COLOR | |
rand_water = int(randint(0,3)) | |
if source < FIXED_COLOR: | |
if y==31: # clear out at bottom | |
source=EMPTY | |
elif dest == EMPTY or (source_sand and dest_water) : # check down | |
dest = source | |
source = EMPTY | |
elif dest_p == EMPTY or (source_sand and destp_water) : # check down/right | |
dest_p = source | |
source = EMPTY | |
elif dest_m == EMPTY or (source_sand and destm_water): # check down/left | |
dest_m = source | |
source = EMPTY | |
elif rand_water == 0: | |
if source == WATER_COLOR and source_p == EMPTY: # water right | |
source_p = source | |
source = EMPTY | |
elif source == WATER_COLOR and source_m == EMPTY: # water left | |
source_m = source | |
source = EMPTY | |
else: | |
if source == WATER_COLOR and source_m == EMPTY: # water left | |
source_m = source | |
source = EMPTY | |
elif source == WATER_COLOR and source_p == EMPTY: # water right | |
source_p = source | |
source = EMPTY | |
screen_addr[(y-1)*MAXSCREEN_X+x] = source | |
screen_addr[y*MAXSCREEN_X+x] = dest | |
screen_addr[y*MAXSCREEN_X+x-1] = dest_m | |
screen_addr[y*MAXSCREEN_X+x+1] = dest_p | |
screen_addr[(y-1)*MAXSCREEN_X+x-1] = source_m | |
screen_addr[(y-1)*MAXSCREEN_X+x+1] = source_p | |
def core1(): | |
global DONE,ALIVE | |
rgb_asm(control_asm) | |
OE.value(1) | |
DONE=True | |
def platform(start,erase): | |
if erase: | |
color1=WHITE | |
color2=BLACK | |
else: | |
color1=BLACK | |
color2=WHITE | |
matrix.line(start,25,start+6,31,color1) # \ | |
matrix.line(start,26,start+5,31,color1) # \ | |
matrix.line(start+20,25,start+15,31,color1) # / | |
matrix.line(start+20,26,start+14,31,color1) # / | |
matrix.line(start,25,start+20,25,color2) # ---- | |
def init_pile(t,pos): | |
if t==0: | |
return | |
c1=24 % t | |
c2=24 % (t-c1) | |
c3=(t-(c1+c2)) | |
if c1>0: | |
matrix.line(pos, 1,pos ,c1,SAND_COLOR) | |
if c2>0: | |
matrix.line(pos+1,1,pos+1,c2,SAND_COLOR) | |
if c3>0: | |
matrix.line(pos+2,1,pos+2,c3,SAND_COLOR) | |
def init_time(): | |
global T | |
T=localtime() | |
h=T[3] | |
if h>12: | |
h-=12 | |
init_pile(h,12) | |
m=T[4] | |
init_pile(m,32) | |
s=T[5] | |
init_pile(s,52) | |
matrix.text(':',19,5,0xffff) | |
matrix.text(':',39,5,0xffff) | |
def num2str(n,hour): | |
if n<10: | |
if hour==True: | |
return ' '+str(n) | |
else: | |
return '0'+str(n) | |
else: | |
return str(n) | |
def get_time(x): | |
global T,FIRST | |
T=localtime() | |
hr=T[3] | |
if hr>12: | |
hr-=12 | |
matrix.pixel(52,0,SAND_COLOR) # seconds | |
matrix.fill_rect(44,5,16,7,BLUE) | |
matrix.text(num2str(T[5],False),44,5,WHITE) | |
if T[5]==0 or FIRST: | |
matrix.fill_rect(25,5,16,7,BLACK) | |
matrix.text(num2str(T[4],False),25,5,WHITE) | |
if T[4]==0 or FIRST: | |
matrix.fill_rect(5,5,16,7,BLACK) | |
matrix.text(num2str(hr,True),5,5,WHITE) | |
if T[5]==59: | |
platform(42,True) | |
if T[4]<59: | |
matrix.pixel(31,0,SAND_COLOR) # minutes | |
if T[4]==59: | |
platform(21,True) | |
matrix.pixel(13,0,SAND_COLOR) # hours | |
if T[3]==23: | |
platform(0,True) | |
else: | |
platform(42,False) | |
platform(21,False) | |
platform(0,False) | |
FIRST=False | |
def fade(input_color1, input_color2): | |
color1=input_color1 | |
red1 =color1>>11& 0b11111 # extract red #13 | |
green1=color1>>6 & 0b11111 # extract green | |
blue1 =color1 & 0b11111 # extract blue | |
color2 = input_color2 | |
red2 =color2>>11& 0b11111 # extract red | |
green2=color2>>6 & 0b11111 # extract green | |
blue2 =color2 & 0b11111 # extract blue | |
inc_red =(red2- red1)/31# 31 # find increment step | |
inc_green=(green2-green1)/31#31 | |
inc_blue =(blue2- blue1)/31#31 | |
for i in range(0,32):#32 | |
red3 =red1 +int(i*inc_red) # build colors by steps | |
green3=green1+int(i*inc_green) | |
blue3 =blue1 +int(i*inc_blue) | |
color3=red3<<11 | green3<<6 | blue3 # combine RGB | |
color_l2.append(color3) # byte swap to LCD RGB565 | |
DONE=False | |
FIRST=True | |
ALIVE=0 | |
fade(BLACK,GREEN) | |
fade(GREEN,BLUE) | |
fade(BLUE,RED) | |
fade(RED,GREEN) | |
matrix_reset() | |
matrix.fill(0) | |
get_time(1) | |
control_asm[9]=255 #100 LED brightness | |
init_time() | |
_thread.start_new_thread(core1, ()) | |
tim = Timer(period=1000, mode=Timer.PERIODIC, callback=get_time) | |
while not DONE: | |
for pause in range(10000): #10000 fire, 15000 rain | |
pass | |
water_rgb16() | |
matrix.pixel(randint(2,5),0,BLUE) | |
ALIVE+=1 | |
if ALIVE>100: | |
ALIVE=0 | |
tim.deinit() | |
OE.value(1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment