Skip to content

Instantly share code, notes, and snippets.

@samneggs
Created February 26, 2023 21:48
Show Gist options
  • Save samneggs/6bf98746d8caae4054d29c2b175306d7 to your computer and use it in GitHub Desktop.
Save samneggs/6bf98746d8caae4054d29c2b175306d7 to your computer and use it in GitHub Desktop.
Racing Bots on Pi Pico in MicroPython
# Race car bots
from machine import Pin,SPI,PWM,ADC, Timer, reset, soft_reset
import framebuf, gc
import time, array, _thread
from time import sleep, ticks_us, ticks_diff, ticks_ms
from lcd_1_8 import LCD_1inch8
from random import randint
from sys import exit
from math import sin,cos,pi, radians
from uctypes import addressof
from car_bitmap import car_bitmap
from tiles5 import tiles
from race_texture import map_texture
MAXSCREEN_X = const(160)
MAXSCREEN_Y = const(128)
map_width = 6
map_height = 6
MAP_WIDTH = const(6)
MAP_HEIGHT = const(6)
MARGIN = const(50)
NUM_CARS = const(10)
NUM_PARM = const(6)
SCALE = const(12)
X = const(0)
Y = const(1)
DEG = const(2)
SPEED = const(3)
MAP_X = const(4)
MAP_Y = const(5)
dark_grey = const(0x4711) #pico_display.create_pen(20, 40, 60)
black = const(0) #pico_display.create_pen(0, 0, 0)
dark_green = const(0x0024) #pico_display.create_pen(32, 128, 0)
lt_blue = const(0x3e44) #pico_display.create_pen(66, 133, 244)
lt_yellow = const(0x33ff) #pico_display.create_pen(255, 229, 153)
lt_green = const(0xe007) #pico_display.create_pen(0, 255, 0)
brown = const(0xe079) #pico_display.create_pen(120, 63, 4)
white = const(0xffff) #pico_display.create_pen(255, 255, 255)
sky_blue = const(0x1f66) #pico_display.create_pen(96, 192, 255)
blue = const(0x1f00)
red = const(0xf8)
lt_brown = const(0x52de)
colors = array.array('H',(black,dark_grey,red,white,black,black))
colors2 = array.array('H',(black,black,black,black,red,black,black,black,blue,black,black,black,black,black,black, sky_blue,blue,black))
colors3 = array.array('H',(white,black,red,lt_green, sky_blue,dark_green,dark_grey,sky_blue,blue,white,white,white,white,lt_green,dark_green, sky_blue,blue,black))
INTERP0_ACCUM0 = const(0x080)
INTERP0_ACCUM1 = const(0x084)
INTERP0_BASE0 = const(0x088)
INTERP0_BASE1 = const(0x08c)
INTERP0_BASE2 = const(0x090)
INTERP0_POP_FULL = const(0x09c)
INTERP0_CTRL_LANE0 = const(0x0ac)
INTERP0_CTRL_LANE1 = const(0x0b0)
INTERP1_ACCUM0 = const(0x0c0)
INTERP1_ACCUM1 = const(0x0c4)
INTERP1_BASE0 = const(0x0c8)
INTERP1_BASE1 = const(0x0cc)
INTERP1_BASE2 = const(0x0d0)
INTERP1_POP_FULL = const(0x0dc)
INTERP1_CTRL_LANE0 = const(0x0ec)
INTERP1_CTRL_LANE1 = const(0x0f0)
@micropython.viper
def interp_setup(map_width_bits:int, map_height_bits:int, uv_fractional_bits:int, tile_width_bits:int, tile_height_bits:int):
# texture width
sio=ptr32(0xd000_0000) # shift raw mask MSB LSB
sio[INTERP0_CTRL_LANE0//4] = 16 | 1<<18 | (map_width_bits-1)<<10 | 0<<5
sio[INTERP0_CTRL_LANE1//4] = 16-map_width_bits | 1<<18 | (map_width_bits+map_height_bits-1)<<10 | map_width_bits<<5
sio[INTERP1_CTRL_LANE0//4] = 16-4 | 1<<18 | 3<<10 | 0<<5 #tile
sio[INTERP1_CTRL_LANE1//4] = 16-8 | 1<<18 | 7<<10 | 4<<5
du = 65536 // 16
dv = 0
sio[INTERP0_BASE0//4] = du
sio[INTERP0_BASE1//4] = dv
sio[INTERP1_BASE0//4] = du
sio[INTERP1_BASE1//4] = dv
SCREEN_CTL = const(0)
COLORS_CTL = const(4)
TILES_CTL = const(8)
MAP_CTL = const(12)
CAR_CTL = const(16)
SIO_CTL = const(20)
@micropython.asm_thumb
def draw_track_asm(r0):
ldr(r1,[r0,CAR_CTL])
ldr(r1,[r1,MAP_X*4]) # r1 : u = car_ptr[MAP_X]
mov(r2,0) # r2 : y
label(LOOP_Y)
ldr(r3,[r0,CAR_CTL])
ldr(r3,[r3,MAP_Y*4]) # car_ptr[MAP_Y]
asr(r3,r3,SCALE) # car_ptr[MAP_Y]>>SCALE
add(r3,r3,r2) # car_ptr[MAP_Y]>>SCALE)+y
lsl(r3,r3,12) # r3 : v = ((car_ptr[MAP_Y]>>SCALE)+y)<<12
ldr(r4,[r0,SIO_CTL]) # 0xd000_0080
str(r1,[r4,0]) # sio[INTERP0_ACCUM0//4] = u
str(r1,[r4,0x40]) # sio[INTERP1_ACCUM0//4] = u
str(r3,[r4,0x4]) # sio[INTERP0_ACCUM1//4] = v
str(r3,[r4,0x44]) # sio[INTERP1_ACCUM1//4] = v
mov(r5,MAXSCREEN_X)
mul(r5,r2) # r5 : y_offset = y * MAXSCREEN_X
mov(r6,0) # r6 : x
label(LOOP_X)
ldr(r4,[r0,SIO_CTL]) # 0xd000_0080
ldr(r7,[r4,0x1c]) # t = sio[INTERP0_POP_FULL//4] # map
ldr(r3,[r0,MAP_CTL]) # map_ptr
add(r3,r3,r7) # [t]
ldrb(r3,[r3,0]) # map_ptr[t]
lsl(r3,r3,8) # map_ptr[t]<<8
ldr(r7,[r4,0x5c]) # c = sio[INTERP1_POP_FULL//4] # tile
add(r3,r3,r7) # c+(map_ptr[t]<<8)
ldr(r7,[r0,TILES_CTL]) # tiles_ptr
add(r3,r3,r7)
ldrb(r7,[r3,0]) # tiles_ptr[c+(map_ptr[t]<<8)]
add(r7,r7,r7) # double for ldrh
ldr(r3,[r0,COLORS_CTL]) # colors_ptr
add(r3,r3,r7) #
ldrh(r3,[r3,0]) # r3 : pixel = colors_ptr[tiles_ptr[c+(map_ptr[t]<<8)]]
ldr(r7,[r0,SCREEN_CTL]) #
add(r4,r5,r6) # y_offset+x
add(r4,r4,r4) # double for strh
add(r7,r7,r4) # output[y_offset+x]
strh(r3,[r7,0]) # output[y_offset+x] = pixel
add(r6,r6,1)
cmp(r6,MAXSCREEN_X)
blt(LOOP_X)
add(r2,r2,1)
cmp(r2,MAXSCREEN_Y)
blt(LOOP_Y)
label(EXIT)
@micropython.viper
def draw_track():
output = ptr16(LCD.buffer)
colors_ptr = ptr16(colors3)
tiles_ptr = ptr8(tiles)
map_ptr = ptr8(map_texture)
sio = ptr32(0xd000_0000)
car_ptr = ptr32(CAR)
u = car_ptr[MAP_X]
for y in range(1,MAXSCREEN_Y):
v = ((car_ptr[MAP_Y]>>SCALE)+y)<<12
sio[INTERP0_ACCUM0//4] = u
sio[INTERP1_ACCUM0//4] = u
sio[INTERP0_ACCUM1//4] = v
sio[INTERP1_ACCUM1//4] = v
y_offset = y * MAXSCREEN_X
for x in range(0,MAXSCREEN_X):
t = sio[INTERP0_POP_FULL//4] # map
c = sio[INTERP1_POP_FULL//4] # tile
pixel = colors_ptr[tiles_ptr[c+(map_ptr[t]<<8)]]
output[y_offset+x] = pixel
def init_isincos():
global isin,icos
isin=array.array('i',range(0,360))
icos=array.array('i',range(0,360))
for i in range(0,360):
isin[i]=int(sin(radians(i))*(1<<SCALE))
icos[i]=int(cos(radians(i))*(1<<SCALE))
def init_joystick():
global POT_X,POT_Y,POT_X_ZERO,POT_Y_ZERO
POT_X = machine.ADC(27)
POT_Y = machine.ADC(26)
POT_X_ZERO = 0
POT_Y_ZERO = 0
for i in range(1000):
POT_X_ZERO += POT_X.read_u16()
POT_Y_ZERO += POT_Y.read_u16()
POT_X_ZERO = POT_X_ZERO//1000
POT_Y_ZERO = POT_Y_ZERO//1000
pot_scale = 12
def init_car():
global CAR,car_bitmap1
CAR = array.array('i',())
for i in range(NUM_CARS):
CAR.append(randint(65,85)<<SCALE) # X
CAR.append(randint(55,65)<<SCALE) # Y
CAR.append(0) # DEG
CAR.append(+NUM_CARS-(i>>1)) # SPEED
CAR.append(i<<15) # MAP_X
CAR.append((7+ i % 4)<<16) # MAP_Y
car_bitmap1 = car_bitmap[:]
for i in range(len(map_texture)):
t=map_texture[i]
if t == 0:
map_texture[i] = 4
else:
map_texture[i] = t-1
for y in range(16):
for x in range(16):
car_bitmap[y*16+(15-x)] = car_bitmap1[x*16+y] # rotate bitmap
@micropython.viper
def autopilot():
map_data = ptr8(map_texture)
sine = ptr32(isin)
cosine = ptr32(icos)
screen = ptr16(LCD.buffer)
car_ptr = ptr32(CAR)
for i in range(NUM_CARS):
#if i == 0 : continue
index = i * NUM_PARM
rot = car_ptr[DEG+index]
speed = car_ptr[SPEED+index]
inc_right = speed
inc_left = speed
rot_right = rot + 60
rot_left = rot - 60
if rot_right > 359: rot_right -= 360
if rot_left < 0 : rot_left += 360
x = car_ptr[MAP_X+index]+(5<<16)
y = car_ptr[MAP_Y+index]+(4<<16)
step = 120_000 # 120_000
x_right = x + ((step * cosine[rot_right])>>SCALE)
y_right = y + ((step * sine[rot_right])>>SCALE)
x_left = x + ((step * cosine[rot_left])>>SCALE)
y_left = y + ((step * sine[rot_left])>>SCALE)
x_c = (x>>16) & 63
y_c = (y>>16) & 63
x_r = (x_right>>16) & 63
y_r = (y_right>>16) & 63
x_l = (x_left>>16) & 63
y_l = (y_left>>16) & 63
center = map_data[y_c*64+x_c]
right = map_data[y_r*64+x_r]
left = map_data[y_l*64+x_l]
if left == 4: rot -= inc_left
if right == 4: rot += inc_right
if rot > 359: rot -= 360
if rot < 0 : rot += 360
car_ptr[DEG+index] = rot
@micropython.viper
def rot_viper(deg:int, width:int, height:int,source:ptr16,dest:ptr16): # 0-360 degrees, pixel width and height of bitmap
sin=ptr32(isin)
cos=ptr32(icos)
offset_x = width//2
offset_y = height//2
cos_rad = cos[deg]
sin_rad = sin[deg]
for i in range(16*16):
dest[i]=0
y=height-1
while y>=0:
x=width-1
while x>=0:
i=y*height+x
color=source[i]
if color or 1:
adjusted_x = (x - offset_x)
adjusted_y = (y - offset_y)
qx = offset_x
qx+= (cos_rad * adjusted_x)>>SCALE
qx+= (sin_rad * adjusted_y)>>SCALE
if qx>=0 and qx<width:
qy = offset_y
qy+= (sin_rad * adjusted_x)>>SCALE
qy-= (cos_rad * adjusted_y)>>SCALE
if qy>=0 and qy<height:
i = qy * width + qx
dest[i]=color
x-=1
y-=1
def read_joystick():
global CAR
pot_scale = 13
x_inc = int(POT_X.read_u16() - POT_X_ZERO)>>pot_scale
y_inc = int(POT_Y.read_u16() - POT_Y_ZERO)>>pot_scale
if int(abs(x_inc))<2:
x_inc=0
if int(abs(y_inc))<2:
y_inc=0
if x_inc==0 and y_inc==0: return
CAR[DEG] -= x_inc
if CAR[DEG] > 359: CAR[DEG] -= 360
if CAR[DEG] < 0 : CAR[DEG] += 360
CAR[SPEED] -= y_inc
if CAR[SPEED] > 10 : CAR[SPEED] = 10
if CAR[SPEED] < 0 : CAR[SPEED] = 0
@micropython.viper
def calc_move():
car_ptr = ptr32(CAR)
sin_ptr = ptr32(isin)
cos_ptr = ptr32(icos)
map_data = ptr8(map_texture)
for i in range(NUM_CARS):
index = i * NUM_PARM
x = car_ptr[MAP_X+index]+(5<<16)
y = car_ptr[MAP_Y+index]+(4<<16)
x_c = (x>>16) & 63
y_c = (y>>16) & 63
center = map_data[y_c*64+x_c]
if center == 4:
speed = car_ptr[SPEED+index]
else:
speed = car_ptr[SPEED+index]>>1
#if i>0: car_ptr[SPEED+index] = int(randint(19,21))
x_inc = (cos_ptr[car_ptr[DEG+index]]*speed)>>2
y_inc = (sin_ptr[car_ptr[DEG+index]]*speed)>>2
new_x = car_ptr[MAP_X+index] + (x_inc<<1)
new_y = car_ptr[MAP_Y+index] + (y_inc<<1)
x1 = new_x
y1 = new_y
width = 1<<14
height = 1<<14
collision = 0
for j in range(NUM_CARS):
if i == j: continue # same car
index_j = j * NUM_PARM
x2 = car_ptr[MAP_X+index_j]
y2 = car_ptr[MAP_Y+index_j]
if x1 < x2 + width and x1 + width > x2 and y1 < y2 + height and y1 + height > y2:
collision = 1
break
if collision == 0 :
car_ptr[MAP_X+index] += x_inc<<1
car_ptr[MAP_Y+index] += y_inc<<1
if int(randint(0,5)) == 1:
car_ptr[DEG+index] += int(randint(-1,1))*10
else:
car_ptr[DEG+index] += int(randint(-1,1))*20
@micropython.viper
def draw_car(fps):
screen_ptr = ptr16(LCD.buffer)
color_ptr = ptr16(colors)
color3_ptr = ptr16(colors3)
car_ptr2 = ptr16(car_bitmap1)
car_pos_ptr= ptr32(CAR)
width = 16
height = 16
car1_x = car_pos_ptr[MAP_X+0*NUM_PARM]
car1_y = car_pos_ptr[MAP_Y+0*NUM_PARM]
for i in range(NUM_CARS):
index = i * NUM_PARM
rot_viper(car_pos_ptr[DEG+index],16,16,car_bitmap,car_bitmap1)
car_x = ((car_pos_ptr[MAP_X+index]-car1_x)>>12)+75
car_y = ((car_pos_ptr[MAP_Y+index]-car1_y)>>12)+60
for y in range(16):
for x in range(16):
car_pos = 16*y+x
color = car_ptr2[car_pos]
if color == 0xe027: color = color3_ptr[i] # change car color
if color:
x1 = x+car_x
y1 = y+car_y
if x1<160 and y1<128 and x1>=0 and y1>=0:
screen_ptr[160*y1+x1]= color
#LCD.text(str(car_pos_ptr[MAP_X]>>16),30,0,0xff)
#LCD.text(str(car_pos_ptr[MAP_Y]>>16),60,0,0xff)
LCD.text(str(fps),0,0,0xff)
LCD.show()
LCD.fill(0)
@micropython.viper
def draw_tiles():
screen_ptr = ptr16(LCD.buffer)
tiles_ptr = ptr8(tiles)
map_ptr = ptr8(map_texture)
color_ptr = ptr16(colors3)
car_pos_ptr= ptr32(CAR)
map_x = car_pos_ptr[MAP_X]>>SCALE
map_y = car_pos_ptr[MAP_Y]>>SCALE
for ym in range(8):
for xm in range(10):
for y in range(16):
for x in range(16):
addr = y*160+x + (ym*160*16 + xm*16)
t = (y*16+x)+map_ptr[(map_y+ym)*64+(map_x+xm)]*256
screen_ptr[addr] = color_ptr[tiles_ptr[t]]
#@micropython.viper
def main():
global EXIT
fps = 0
old_deg = CAR[DEG]
joystick_ticks = 0
calc_move_ticks = 0
debug_ticks = 0
autopilot_ticks = 0
while not EXIT:
gticks = ticks_us()
if int(ticks_diff(ticks_ms(),joystick_ticks)) > 1:
joystick_ticks = int(ticks_ms())
read_joystick()
if old_deg != CAR[DEG]:
old_deg = CAR[DEG]
#rot_viper(CAR[DEG],16,16,car_bitmap,car_bitmap1)
if int(ticks_diff(ticks_ms(),calc_move_ticks)) > 30:
calc_move_ticks = int(ticks_ms())
calc_move()
if int(ticks_diff(ticks_ms(),debug_ticks)) > 1000:
debug_ticks = int(ticks_ms())
#draw_track()
#draw_tiles()
if int(ticks_diff(ticks_ms(),autopilot_ticks)) > 30: #50
autopilot_ticks = int(ticks_ms())
autopilot()
draw_track_asm(track_ctl)
draw_car(fps)
fps = 1_000_000//int(ticks_diff(ticks_us(),gticks))
if __name__=='__main__':
EXIT = 0
interp_setup(MAP_WIDTH, MAP_HEIGHT, 16, 4, 4)
init_joystick()
init_isincos()
init_car()
FIRE = Pin(22, Pin.IN, Pin.PULL_UP)
POWER = Pin(16, machine.Pin.OUT)
BUZZ = machine.PWM(Pin(17, machine.Pin.OUT))
pwm = PWM(Pin(13))
pwm.freq(1000)
pwm.duty_u16(0x7fff)#max 65535
machine.freq(200_000_000)
LCD = LCD_1inch8()
machine.mem32[0x40008048] = 1<<11 # enable peri_ctrl clock
track_ctl = array.array('I',(addressof(LCD.buffer),addressof(colors3),addressof(tiles),
addressof(map_texture),addressof(CAR),0xd000_0080))
gc.collect()
print(gc.mem_free())
main()
draw_tiles()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment