## 100Msps ADC import machine machine.freq(200_000_000 ) print( "done" ) ### 1. DMA import time import uctypes class DMA: DMA_BASE = 0x50000000 DMA_EN = 0x01 << 0 HIGH_PRIO = 0x01 << 1 INCR_READ = 0x01 << 4 INCR_WRITE= 0x01 << 5 DREQ_PIO0_RX0 = 0x04 << 15 DREQ_SPI1_TX = 0x12 << 15 DREQ_PERMANENT= 0x3F << 15 IRQ_QUIET = 0x01 << 21 BUSY = 0x01 << 24 def __init__( self, channelNumber ): offset = channelNumber * 0x40 self.CHx_READ_ADDR = DMA.DMA_BASE + 0x00 + offset self.CHx_WRITE_ADDR = DMA.DMA_BASE + 0x04 + offset self.CHx_TRANS_COUNT = DMA.DMA_BASE + 0x08 + offset self.CHx_CTRL_TRIG = DMA.DMA_BASE + 0x0C + offset def config( self, src_addr, dst_addr, count, src_inc, dst_inc, trig_dreq ): machine.mem32[ self.CHx_CTRL_TRIG ] = 0 machine.mem32[ self.CHx_READ_ADDR ] = src_addr machine.mem32[ self.CHx_WRITE_ADDR ] = dst_addr machine.mem32[ self.CHx_TRANS_COUNT ] = count trig_val = 0 if( src_inc ): trig_val |= DMA.INCR_READ if( dst_inc ): trig_val |= DMA.INCR_WRITE trig_val |= trig_dreq machine.mem32[ self.CHx_CTRL_TRIG ] = trig_val def enable( self ): machine.mem32[ self.CHx_CTRL_TRIG ] |= DMA.DMA_EN def disable( self ): machine.mem32[ self.CHx_CTRL_TRIG ] = 0 def is_busy( self ): if( machine.mem32[ self.CHx_CTRL_TRIG ] & DMA.BUSY ): return True else: return False def test_dma(): dma = DMA(0) src_buf = b"Hello World!"*1000 dst_buf = bytearray( 12*1000 ) dma.config( src_addr = uctypes.addressof( src_buf ), dst_addr = uctypes.addressof( dst_buf ), count = len( src_buf ), src_inc = True, dst_inc = True, trig_dreq = DMA.DREQ_PERMANENT ) t0 = time.ticks_us() dma.enable() while( dma.is_busy() ): pass dma.disable() t1 = time.ticks_us() print( "dst", dst_buf[0:12], "..." ) print( "Transfer speed [B/s]:", len( src_buf )/((t1-t0)*1e-6) ) print( "@CPU freq:", machine.freq() ) test_dma() print( "done" ) ### 2. ST7789 import time import machine import uctypes class St7789: HRES = 320 VRES = 240 CASET = 0x2A RASET = 0x2B RAMWR = 0x2C def __init__( self, baudrate, cs, sck, mosi, miso, dc ): self.buf1 = bytearray( 1 ) self.buf2 = bytearray( 2 ) self.buf4 = bytearray( 4 ) self.baudrate = baudrate self.cs = cs self.sck = sck self.mosi= mosi self.miso= miso self.dc = dc self.cs.value( 1 ) self.dma = DMA( 0 ) self.spi_init() self.config() def spi_init( self ): self.spi = machine.SPI( 1, baudrate=self.baudrate, polarity=0, phase=0, sck=self.sck, mosi=self.mosi, miso=self.miso ) def write_register( self, reg, buf ): # reg for command, buf for data self.buf1[0] = reg self.cs.value( 0 ) self.dc.value( 0 ) self.spi.write( self.buf1 ) self.dc.value( 1 ) self.spi.write( buf ) self.cs.value( 1 ) # Note: if is_blocking is False, user should call to wait_dma explicitly def write_register_dma( self, reg, buf, is_blocking=True ): SPI1_BASE = 0x40040000 SSPDR = 0x008. #address of 16bit SPI1 TX FIFO self.dma.config( src_addr = uctypes.addressof( buf ), dst_addr = SPI1_BASE + SSPDR, count = len( buf ), src_inc = True, dst_inc = False, trig_dreq= DMA.DREQ_SPI1_TX ) self.buf1[0] = reg self.cs.value( 0 ) self.dc.value( 0 ) self.spi.write( self.buf1 ) self.dc.value( 1 ) self.dma.enable() if( is_blocking ): self.wait_dma() def wait_dma( self ): while( self.dma.is_busy() ): pass self.dma.disable() # Note: wait to send last byte. It should take < 1uS @ 10MHz time.sleep_us( 1 ) self.cs.value( 1 ) def config( self ): self.write_register( 0x11, b"" ) time.sleep_ms( 100 ) self.write_register( 0x36, b"\x60" ) self.write_register( 0x3A, b"\x55" ) self.write_register( 0xB2, b"\x0C\x0C\x00\x33\x33" ) self.write_register( 0xB7, b"\x35" ) self.write_register( 0xBB, b"\x28" ) self.write_register( 0xC0, b"\x3C" ) self.write_register( 0xC2, b"\x01" ) self.write_register( 0xC3, b"\x0B" ) self.write_register( 0xC4, b"\x20" ) self.write_register( 0xC6, b"\x0F" ) self.write_register( 0xD0, b"\xA4\xA1" ) self.write_register( 0xE0, b"\xD0\x01\x08\x0F\x11\x2A\x36\x55\x44\x3A\x0B\x06\x11\x20" ) self.write_register( 0xE1, b"\xD0\x02\x07\x0A\x0B\x18\x34\x43\x4A\x2B\x1B\x1C\x22\x1F" ) self.write_register( 0x55, b"\xB0" ) self.write_register( 0x29, b"" ) time.sleep_ms( 100 ) def set_window( self, x, y, w, h ): x0 = x y0 = y x1 = x0 + w - 1 y1 = y0 + h - 1 self.buf4[0] = x0 >> 8 self.buf4[1] = x0 & 0xff self.buf4[2] = x1 >> 8 self.buf4[3] = x1 & 0xff self.write_register( St7789.CASET, self.buf4 ) self.buf4[0] = y0 >> 8 self.buf4[1] = y0 & 0xff self.buf4[2] = y1 >> 8 self.buf4[3] = y1 & 0xff self.write_register( St7789.RASET, self.buf4 ) def draw_bitmap_dma( self, x, y, w, h, buf, is_blocking=True ): self.set_window( x, y, w, h ) self.write_register_dma( St7789.RAMWR, buf, is_blocking ) def clear( self, color ): self.buf2[0] = color >> 8 self.buf2[1] = color & 0xff self.set_window( 0, 0, St7789.HRES, St7789.VRES ) self.write_register( St7789.RAMWR, b"" ) self.cs.value( 0 ) self.dc.value( 1 ) for i in range( St7789.HRES ): for j in range( St7789.VRES ): self.spi.write( self.buf2 ) self.cs.value( 1 ) def build_square_buf( w, h ): top = b"\xFF\xFF"*w body=(b"\xFF\xFF" + b"\x00\x00"*(w-2) + b"\xFF\xFF")*(h-2) bot = b"\xFF\xFF"*w return top + body + bot def test_lcd(): baudrate = 24_000_000 cs = machine.Pin( 13, machine.Pin.OUT ) sck = machine.Pin( 10, machine.Pin.OUT ) mosi= machine.Pin( 11, machine.Pin.OUT ) miso= machine.Pin( 12, machine.Pin.IN ) dc = machine.Pin( 14, machine.Pin.OUT ) bl = machine.Pin( 15, machine.Pin.OUT ) bl.value( 1 ) lcd = St7789( baudrate, cs, sck, mosi, miso, dc ) lcd.clear( 0x001F ) # 1/4 screen pixels square with white border red backgorund w, h = 320//4, 240//4 bmp = build_square_buf( w, h ) t0 = time.ticks_us() lcd.draw_bitmap_dma( 100, 100, w, h, bmp ) t1 = time.ticks_us() print( "Maximum FPS @24MHz:", 24e6/( 320*240*16 ) ) # FPS = F/(W*H*BPP) print( "Achieved FPS:", 1/(16*(t1-t0)*1e-6) ) # Note: Test only draws 1/16 of the sreen area print( "Draw TSC calibration pattern") w, h = 10, 10 bmp = build_square_buf( w, h ) lcd.draw_bitmap_dma( 50, 50, w, h, bmp ) lcd.draw_bitmap_dma( 250, 50, w, h, bmp ) lcd.draw_bitmap_dma( 250, 200, w, h, bmp ) lcd.draw_bitmap_dma( 50, 200, w, h, bmp ) test_lcd() print( "done" ) ### 3. XPT2046 - Touch Screen LCD import time import machine class Xpt2046: CHANNEL_X = 0x90 CHANNEL_Y = 0xD0 def __init__( self, baudrate, cs, sck, mosi, miso, ax=1, bx=0, ay=1, by=0 ): self.buf_tx = bytearray( 3 ) self.buf_rx = bytearray( 3 ) self.sck = sck self.mosi= mosi self.miso= miso self.cs = cs self.cs.value( 1 ) self.ax = ax self.bx = bx self.ay = ay self.by = by self.baudrate = baudrate self.spi_init() def spi_init( self ): self.spi = machine.SPI( 1, baudrate=self.baudrate, polarity=0, phase=0, sck=self.sck, mosi=self.mosi, miso=self.miso ) def _read( self ): self.cs.value( 0 ) self.buf_tx[0] = Xpt2046.CHANNEL_X self.buf_tx[1] = 0x00 self.buf_tx[2] = 0x00 self.spi.write_readinto( self.buf_tx, self.buf_rx ) x = (self.buf_rx[1]<<4) | (self.buf_rx[2]>>4) self.buf_tx[0] = Xpt2046.CHANNEL_Y self.buf_tx[1] = 0x00 self.buf_tx[2] = 0x00 self.spi.write_readinto( self.buf_tx, self.buf_rx ) y = (self.buf_rx[1]<<4) | (self.buf_rx[2]>>4) self.cs.value( 1 ) if( x == 2047 ): x = 0 return x, y def read( self ): xacc = 0 yacc = 0 for i in range( 4 ): x, y = self._read() if( x and y ): xacc += x yacc += y else: return 0, 0 x = xacc/4 y = yacc/4 x = self.ax*x + self.bx y = self.ay*y + self.by return int( 320-x ), int( y ) def test_tsc(): baudrate= 1_000_000 cs = machine.Pin( 16, machine.Pin.OUT ) sck = machine.Pin( 10, machine.Pin.OUT ) mosi= machine.Pin( 11, machine.Pin.OUT ) miso= machine.Pin( 12, machine.Pin.OUT ) # Calibration values AX = 0.176831 BX =-45.643 AY = 0.13084 BY =-16.191 tsc = Xpt2046( baudrate, cs, sck, mosi, miso, ax=AX, bx=BX, ay=AY, by=BY ) for i in range( 10 ): x, y = tsc.read() if( x and y ): print( x, y ) time.sleep( 1 ) test_tsc() print("done") ### 4. Draw import time import machine import lvgl as lv import sys # This leds shows when lcd is busy dma_led = machine.Pin( 25, machine.Pin.OUT ) print( "lv.init()" ) lv.init() print( "disp_buf_t.init()" ) HRES, VRES = 320, 240 fb1 = bytearray( HRES*64 ) fb2 = bytearray( HRES*64 ) disp_draw_buf = lv.disp_draw_buf_t() disp_draw_buf.init( fb1, fb2, len( fb1 )//lv.color_t.SIZE ) is_fb1 = True dma_running = False def disp_drv_flush_cb( disp_drv, area, color ): global is_fb1, dma_running #print( "disp_drv_flush_cb", area.x1, area.x2, area.y1, area.y2 ) if( is_fb1 ): fb = memoryview( fb1 ) else: fb = memoryview( fb2 ) is_fb1 = not is_fb1 if( dma_running == True ): lcd.wait_dma() dma_running = False dma_led.off() x = area.x1 y = area.y1 w = area.x2 - area.x1 + 1 h = area.y2 - area.y1 + 1 lcd.draw_bitmap_dma( x, y, w, h, fb[0:2*w*h], is_blocking=False ) dma_running = True dma_led.on() disp_drv.flush_ready() print( "disp_drv_t.init()" ) disp_drv = lv.disp_drv_t() disp_drv.init() disp_drv.draw_buf = disp_draw_buf disp_drv.flush_cb = disp_drv_flush_cb disp_drv.hor_res = HRES disp_drv.ver_res = VRES disp_drv.register() xx, yy, ss = 0, 0, 0 def indev_drv_read_cb( indev_drv, data ): global xx, yy, ss, dma_running #print( "indev_drv_read_cb" ) if( dma_running == True ): lcd.wait_dma() dma_running = False dma_led.off() tsc.spi_init() x, y = tsc.read() lcd.spi_init() # Reinit SPI with LCD settings if( x or y ): xx = x yy = y ss = 1 else: ss = 0 data.state = ss data.point.x = xx data.point.y = yy return False print( "indev_drv_t.init()" ) indev_drv = lv.indev_drv_t() indev_drv.init() indev_drv.type = lv.INDEV_TYPE.POINTER indev_drv.read_cb = indev_drv_read_cb indev_drv.register() #def cb_lv_log( msg ): # print( msg, end="" ) #lv.log_register_print_cb( cb_lv_log ) print( "lv_run" ) def lv_run( timeout_ms=1000, period_ms=10 ): t0 = time.ticks_ms() while( time.ticks_diff( time.ticks_ms(), t0 ) < timeout_ms ): tt0 = time.ticks_ms() lv.task_handler() tt1 = time.ticks_ms() dt = tt1-tt0 if( period_ms > dt ): time.sleep_ms( period_ms - dt ) lv.tick_inc( period_ms ) else: lv.tick_inc( dt ) print("done") * lv.init() * disp_buf_t.init() * disp_drv_t.init() * indev_drv_t.init() * lv_run * done ### 5. Setup Demo lcd_baudrate = 24_000_000 tsc_baudrate = 1_000_000 lcd_cs = machine.Pin( 13, machine.Pin.OUT ) tsc_cs = machine.Pin( 16, machine.Pin.OUT ) sck = machine.Pin( 10, machine.Pin.OUT ) mosi= machine.Pin( 11, machine.Pin.OUT ) miso= machine.Pin( 12, machine.Pin.IN ) dc = machine.Pin( 14, machine.Pin.OUT ) bl = machine.Pin( 15, machine.Pin.OUT ) bl.value( 1 ) # Calibration values AX, BX = 0.176831, -45.643 AY, BY = 0.130840, -16.191 tsc = Xpt2046( tsc_baudrate, tsc_cs, sck, mosi, miso, ax=AX, bx=BX, ay=AY, by=BY ) # Init lcd last one to start lvgl with SPI LCD baudrate lcd = St7789( lcd_baudrate, lcd_cs, sck, mosi, miso, dc ) lcd.clear( 0x0000 ) print( "done" ) ### 6. build & run demo def cb_btn( event ): print( "Hello World!" ) scr = lv.obj() btn = lv.btn( scr ) lbl = lv.label( btn ) lbl.set_text( "Press me!" ) btn.center() btn.add_event_cb( cb_btn, lv.EVENT.CLICKED, None ) lv.scr_load( scr ) lv_run( 10_000 ) print( "done" )