from machine import Pin
from utime import sleep_ms, ticks_us
import rp2
import struct
import uctypes
from myDMA import myDMA

LCD_RD = 8   #Read Signal
LCD_WR = 9   #Write Signal
LCD_RS = 10  #Command/Data Select
LCD_CS = 11  #Chip Select 
LCD_RST = 12 #Reset Signal

BMP_WIDTH = 240
BMP_HEIGHT = 320
BUF_LINE = 32 #320の約数
BUF_SIZE = BMP_WIDTH * BUF_LINE * 3

PIO0_BASE = 0x50200000
PIO1_BASE = 0x50300000
TXF0 = 0x010
TXF1 = 0x014

#8bitをGPIOに送信する
@rp2.asm_pio(out_init=(rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW),
             sideset_init=rp2.PIO.OUT_HIGH,
             out_shiftdir=rp2.PIO.SHIFT_RIGHT,
             pull_thresh=8,
             autopull=True)
def lcd_write_bus1():
    out(pins, 8).side(0)
    nop().side(1)

#24bitRGBを16bitRGB(R:5bit,G:6bit.B:5bit)に変換してGPIOに送信する
@rp2.asm_pio(out_init=(rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW,
                       rp2.PIO.OUT_LOW),
             sideset_init=rp2.PIO.OUT_HIGH,
             out_shiftdir=rp2.PIO.SHIFT_LEFT,
             in_shiftdir=rp2.PIO.SHIFT_LEFT)
def lcd_write_bus2():
    pull().side(0)         #Blue8bit -> osr
    out(x, 29).side(0)     #Blueの4~8bit(5bit) -> x
    pull().side(0)         #Green8bit -> osr
    out(y, 27).side(0)     #Greenの6~8bit(3bit) -> x
    out(isr, 3).side(0)    #Greenの3~5bit(3bit) -> y
    in_(x, 5).side(0)      #Greenの3~5bit(3bit)とBlueの4~8bit(5bit) -> isr
    mov(x, isr).side(0)    #isr -> x (一時保管)
    pull().side(0)         #Red -> osr
    out(isr, 29).side(0)   #Redの4=8bit -> isr
    in_(y, 3).side(0)      #Redの4~8bit(5bit)とGreenの3~5bit(3bit) -> isr
    mov(pins, isr).side(0#結果の上位8bitをGPIOへ送信
    nop().side(1)          #LCD_WR -> 1
    mov(pins, x).side(0)   #結果の下位8bitをGPIOへ送信
    nop().side(1)          #LCD_WR -> 1

#bmpをLCDに表示
def bmp_load(file_name):
    with open(file_name, 'rb'as bmp_file:
        offset = read_bmp_header(bmp_file)
        if offset != False:
            bmp_draw(bmp_file, offset)

#bmpをLCDに転送
#@micropython.native
def bmp_draw(bmp_file, offset):
    address_set(00, BMP_WIDTH, BMP_HEIGHT)
    lcd_cs.value(0)
    lcd_rs.value(1)
    sm1.active(0)
    sm2.active(1)
    bmp_file.seek(offset)
    start = ticks_us()
    dma = myDMA(0, timer=0,clock_MUL=1, clock_DIV=5)
    dma.setCtrl(dst_inc=False, data_size=1)
    for i in range(BMP_HEIGHT / BUF_LINE):
        bmp = bmp_file.read(BUF_SIZE)
        dma.move(uctypes.addressof(bmp), PIO0_BASE+TXF1, BUF_SIZE, start=True)
        while dma.isBusy():
            pass
    end = ticks_us() 
    sm2.active(0)
    sm1.active(1)
    length_us = end - start    
    print('{} s'.format(length_us/1000000))

#bmpのヘッダーを読み込む
def read_bmp_header(bmp_file):
    file_type = struct.unpack('<h', bmp_file.read(2))[0]
    print('file type = 'hex(file_type))
    if file_type != 0x4d42:
        return False
    file_size = struct.unpack('<i', bmp_file.read(4))[0]
    print('file size(kbyte) = ', file_size / 1024)
    reserved = bmp_file.read(4)
    offset = struct.unpack('<i', bmp_file.read(4))[0]
    print('offset = ', offset)
    header_size = struct.unpack('<i', bmp_file.read(4))[0]
    print('header size = ', header_size)
    if header_size != 40:
        return False
    bmp_width = struct.unpack('<i', bmp_file.read(4))[0]
    print('bmp width = ', bmp_width)
    if bmp_width != BMP_WIDTH:
        return False
    bmp_height = struct.unpack('<i', bmp_file.read(4))[0]
    print('bmp height = ', bmp_height)
    if bmp_height != BMP_HEIGHT:
        return False
    planes = struct.unpack('<h', bmp_file.read(2))[0]
    print('planes = ', planes)
    if planes != 1:
        return False
    bmp_depth = struct.unpack('<h', bmp_file.read(2))[0]
    print('bmp depth = ', bmp_depth)
    compression = struct.unpack('<i', bmp_file.read(4))[0]
    print('compression = ', compression)
    if compression != 0:
        return False
    return offset

def lcd_write_com(d):
    lcd_rs.value(0)
    sm1.put(d)

def lcd_write_data(d):
    lcd_rs.value(1)
    sm1.put(d)

def address_set(x1, y1, x2, y2):
    lcd_write_com(0x2a)
    lcd_write_data(x1 >> 8)
    lcd_write_data(x1 & 0xFF)
    lcd_write_data(x2 >> 8)
    lcd_write_data(x2 & 0xFF)
    lcd_write_com(0x2b)
    lcd_write_data(y1 >> 8)
    lcd_write_data(y1 & 0xFF)
    lcd_write_data(y2 >> 8)
    lcd_write_data(y2 & 0xFF)
    lcd_write_com(0x2c)

#LCD初期化(付属CDの_9341uno.inoを移植)
def lcd_init():
    lcd_rst.value(1)
    sleep_ms(5)
    lcd_rst.value(0)
    sleep_ms(15)
    lcd_rst.value(1)
    sleep_ms(15)
    lcd_cs.value(1)
    lcd_wr.value(1)
    lcd_cs.value(0)
    lcd_write_com(0xcb)  
    lcd_write_data(0x39
    lcd_write_data(0x2c
    lcd_write_data(0x00
    lcd_write_data(0x34
    lcd_write_data(0x02)
    lcd_write_com(0xcf)  
    lcd_write_data(0x00
    lcd_write_data(0xc1
    lcd_write_data(0x30
    lcd_write_com(0xe8)  
    lcd_write_data(0x85
    lcd_write_data(0x00
    lcd_write_data(0x78
    lcd_write_com(0xea)  
    lcd_write_data(0x00
    lcd_write_data(0x00
    lcd_write_com(0xed)  
    lcd_write_data(0x64
    lcd_write_data(0x03
    lcd_write_data(0x12
    lcd_write_data(0x81
    lcd_write_com(0xf7)  
    lcd_write_data(0x20
    lcd_write_com(0xc0)    #Power control 
    lcd_write_data(0x23)   #VRH[5:0] 
    lcd_write_com(0xc1)    #Power control 
    lcd_write_data(0x10)   #SAP[2:0]BT[3:0] 
    lcd_write_com(0xc5)    #VCM control 
    lcd_write_data(0x3e)   #Contrast
    lcd_write_data(0x28
    lcd_write_com(0xc7)    #VCM control2 
    lcd_write_data(0x86)   #--
    lcd_write_com(0x36)    #Memory Access Control 
    lcd_write_data(0x48)   
    lcd_write_com(0x3a)    
    lcd_write_data(0x55
    lcd_write_com(0xb1)    
    lcd_write_data(0x00)  
    lcd_write_data(0x18
    lcd_write_com(0xb6)    #Display Function Control 
    lcd_write_data(0x08
    lcd_write_data(0x82)
    lcd_write_data(0x27)
    lcd_write_com(0x11)    #Exit Sleep 
    sleep_ms(120
    lcd_write_com(0x29)    #Display on
    lcd_write_com(0x2c)   

#GPIO初期化
lcd_rd = Pin(LCD_RD, Pin.OUT, value = 1)
lcd_wr = Pin(LCD_WR, Pin.OUT, value = 1)
lcd_rs = Pin(LCD_RS, Pin.OUT, value = 1)
lcd_cs = Pin(LCD_CS, Pin.OUT, value = 1)
lcd_rst = Pin(LCD_RST, Pin.OUT, value = 1)
for i in range(9):
    Pin(i, Pin.OUT, value = 1)

#StateMachine初期化
sm1 = rp2.StateMachine(0, lcd_write_bus1, out_base=Pin(0), sideset_base=Pin(9))
sm2 = rp2.StateMachine(1, lcd_write_bus2, out_base=Pin(0), sideset_base=Pin(9))
sm1.active(1)

#bmp表示
lcd_init()
bmp_load('sample.bmp')