#!/usr/bin/python
# -*- encoding: utf-8 -*-

# posledni uprava: 20.4.2013


# pripojeni displeje HB12625-B (120x64 bodu):
# displej vyvod (vyznam)  -    pripojeno na ...
# 1  (GND)                - RasPi (GPIO GND   - pin 6)
# 2  (+ napajeni)         - RasPi (GPIO +3,3V - pin 1)
# 3  (kontrast)           - zdroj zaporneho regulovatelneho napeti asi -11V az -12V
# 4  (RS Data/Command)    - GPA0 (Expander MCP23017 - noha 21) nebo RasPi (GPIO23 pin 16)
# 5  (R/W Read/Write)     - RasPi (GPIO GND - pin 6)
# 6  (E - Strobe)         - GPA1 (Expander MCP23017 - noha 22) nebo RasPi (GPIO22 pin 15)
# 7  (Data bit0)          - GPB0 (Expander MCP23017 - noha 1)
# 8  (Data bit1)          - GPB1 (Expander MCP23017 - noha 2)
# 9  (Data bit2)          - GPB2 (Expander MCP23017 - noha 3)
# 10 (Data bit3)          - GPB3 (Expander MCP23017 - noha 4)
# 11 (Data bit4)          - GPB4 (Expander MCP23017 - noha 5)
# 12 (Data bit5)          - GPB5 (Expander MCP23017 - noha 6)
# 13 (Data bit6)          - GPB6 (Expander MCP23017 - noha 7)
# 14 (Data bit7)          - GPB7 (Expander MCP23017 - noha 8)
# 15 (L/R Leva/Prava)     - GPA2 (Expander MCP23017 - noha 23)
# 16 (Reset)              - GPA3 (Expander MCP23017 - noha 24)
# 17 
# 18 
# 19 (Podsvet 100V/400Hz) - generator stridaveho napeti 100V/400Hz - nepovinne
# 20 (GND)                - RasPi (GPIO GND - pin 6)

# napajeni expanderu MCP23017 je v tomto pripade 3,3V !!



import time              # ruzne operace s casem (pauzy)
import smbus             # I2C komunikace s expanderem
import RPi.GPIO as GPIO  # pouziva se jen pri pripojeni signalu E, nebo RS primo na GPIO v RasPi
import math              # vyuziva se jen v prikladech pri kresleni kruznic

mapa={}             # pamet, do ktere se uklada aktualni stav zobrazenych pixelu na displeji
font={}             # pamet, ve ktere je ulozeny font, nacetny z externiho souboru


bus = smbus.SMBus(1)   # novejsi varianta RasPi (512MB)
#bus = smbus.SMBus(0)  # starsi varianta RasPi (256MB)
addr = 0x20 # adresa expanderu MCP23017 (podle nastavenych adresovacich vyvodu)



# pomocne globalni promenne, pomoci kterych se zrychluje komunikace
# k prepinani pinu L/R dochazi jen v pripade, ze od minule doslo ke zmene kvadrantu
stara_lr = 0    # predchozi stav ovladaciho pinu L/P
porta=0         # globalni promenna ve ktere je aktualni stav portu "A" na expanderu



# tabulka pro prekodovani ceskych znaku z unicode na pozici ve fontu
cz={}
cz[345] = 253      # r s hackem
cz[237] = 161      # i s carkou
cz[353] = 231      # s s hackem
cz[382] = 167      # z s hackem
cz[357] = 156      # t s hackem
cz[269] = 159      # c s hackem
cz[253] = 236      # y s carkou
cz[367] = 133      # u s krouzkem
cz[328] = 229      # n s hackem
cz[250] = 163      # u s carkou
cz[283] = 216      # e s hackem
cz[271] = 212      # d s hackem
cz[225] = 160      # a s carkou
cz[233] = 130      # e s carkou
cz[243] = 162      # o s carkou

cz[344] = 252      # R s hackem
cz[205] = 214      # I s carkou
cz[352] = 230      # S s hackem
cz[381] = 166      # Z s hackem
cz[356] = 155      # T s hackem
cz[268] = 172      # C s hackem
cz[221] = 237      # Y s carkou
cz[366] = 222      # U s krouzkem
cz[327] = 213      # N s hackem
cz[218] = 233      # U s carkou
cz[282] = 183      # E s hackem
cz[270] = 210      # D s hackem
cz[193] = 181      # A s carkou
cz[201] = 144      # E s carkou
cz[211] = 224      # O s carkou

cz[228] = 132      # prehlasovane a
cz[235] = 137      # prehlasovane e
cz[239] = 139      # prehlasovane i
cz[246] = 148      # prehlasovane o
cz[252] = 129      # prehlasovane u
cz[196] = 142      # prehlasovane A
cz[214] = 153      # prehlasovane O
cz[220] = 154      # prehlasovane U

cz[176] = 255      # stupen
cz[177] = 241      # plus minus
cz[171] = 174      # dvojsipka vlevo
cz[166] = 173      # prerusene svislitko
cz[223] = 225      # beta


##  Timto kouskem kodu se definuje prevodni tabulka.
##  Do promenne "text" se vlozi specialni neASCII znak nebo vice znaku.
##  Po spusteni kodu se tyto znaky vytisknou a k nim se vytiskne i jejich kod.
##  Tento kod se zada jako index promenne cz[].
##  A k takto zindexovane promenne se priradi pozice znaku v souboru "font.txt".
#  text="äëiöüÄÖÜ°±«¦ß"
#  utext=unicode(text, "utf-8")
#  for i in range(len(utext)):
#    print utext[i:i+1] , ord(utext[i:i+1])



# ==============================
# HLAVNI PROGRAM
# ==============================

def main():
  init()             # zakladni nastaveni systemu - smery portu na expanderu, reset a nastaveni displeje

# priklady pouziti :


  disclear()
  time.sleep(1)


  # bezne texty s ruznou sirkou mezery
  for i in range(8):
    slovo ("Řádka č." + str(i), 1, i, i, False)
  time.sleep(2)

  # to same s inverzi
  disclear(0xFF)
  for i in range(8):
    slovo ("Řádka č." + str(i), 1, i, i, True)
  time.sleep(2)

  disclear()


# zobrazeni vsech znaku s kodem vetsim,nez 127 v 16 sloupcich a na 8 radkach pomoci funkce znak
  slovo ("Seznam neASCII" , 6 , 3 , 3 )
  slovo ("znaků" , 35 , 5 , 5 )
  time.sleep(2)
  disclear()
  slovo ("kódy 128 až 191" , 15 , 0)
  start = 128
  for i in range(start,192):
    znak(i,((i-start)*8) % 120 , int((i-start)/15) + 3)
  time.sleep(3)
  disclear()
  slovo ("kódy 192 až 255" , 15 , 0)
  start = 192
  for i in range(start,256):
    znak(i,((i-start)*8) % 120 , int((i-start)/15) + 3)
  time.sleep(3)
  disclear()


  # prime kresleni na displej (dve kruznice uprostred displeje)
  disclear()
  for kruh in range(0,6283,2):
    x1 = int(((math.sin(kruh/1000.0) * 32.0)) + 60)
    y1 = int(((math.cos(kruh/1000.0) * 32.0)) + 32)
    plot(x1,y1,1)
    x2 = int(((math.sin(kruh/1000.0) * 20.0)) + 60)
    y2 = int(((math.cos(kruh/1000.0) * 20.0)) + 32)
    plot(x2,y2,1)
  time. sleep(2)
    
  disclear()
  slovo ("To same", 40, 3)
  slovo ("ale rychleji", 25, 4)
  time. sleep(2)
  disclear()

  # to same kresleni, ale s vyuzitim pameti (rychlejsi)
  for kruh in range(0,6283,2):
    x1 = int(((math.sin(kruh/1000.0) * 32.0)) + 60)
    y1 = int(((math.cos(kruh/1000.0) * 32.0)) + 32)
    mem_plot(x1,y1,1)
    x2 = int(((math.sin(kruh/1000.0) * 20.0)) + 60)
    y2 = int(((math.cos(kruh/1000.0) * 20.0)) + 32)
    mem_plot(x2,y2,1)
  mem_dump()
  time. sleep(2)

  # kresleni horizontalnich car
  disclear()
  # nejtenci cara pres cely displej
  h_cara(10)

  #stredni kratsi cara uprostred
  h_cara (33,20,100) 
  h_cara (34,20,100)  

  # nejtlustsi cara dole
  h_cara (50,40,80)  
  h_cara (51,40,80)  
  h_cara (52,40,80)  


  # vodorovne rotujici napis
  napis = "Rotující nápis"
  rotace(napis ,26 ,3 ,12, 0.1)


  # vertikalne rotujici cely displej (po mikroradkach)
  vers={}
  vers[0] = "KYTICE - ERBEN"
  vers[1] = " "
  vers[2] = "Zemřela matka"
  vers[3] = "a do hrobu dána"
  vers[4] = "siroty po ní"
  vers[5] = "zůstaly."
  vers[6] = " "
  vers[7] = " "
  vers[8] = " "


  vert_rolovani(vers)
  # uvedeni souradnicoveho systemu do puvodniho stavu a smazani displeje
  # pred smazanim displeje je vhodne ho zablokovat, jinak dochazi k probliknuti posunutych textu
  setbit_lr(0)
  prikazy()
  bus.write_byte_data(addr,0x13,0x3E) # zhasnuti (zablokovani) leve poloviny displeje
  strobe()
  setbit_lr(1)
  bus.write_byte_data(addr,0x13,0x3E) # zhasnuti (zablokovani) prave poloviny displeje
  strobe()
  disclear()    # smazani displeje
  init_disp()   # a obnoveni souradnicoveho systemu spolecne s odblokovanim displeje

  time. sleep(2)




  disclear()

  slovo("... a teď  POZOR ...",0,3)
  time.sleep(2)

  # nacteni bitmapoveho obrazku ze souboru na displej
  load_bmp12064("/home/pi/zem.bmp")  # Duchodce z Vysociny




# ++++++++  dale nasleduji vsechny podprogramy +++++++++++++++


# =========  
# podle zpusobu pripojeni ridicich signalu zakomentovat / zrusit komentar u nasledujicich bloku

# strobe pri pripojeni vyvodu E na expander (pomalejsi)
def strobe():
  global porta
  novy = (porta | 0x02) # GPA1 nastavit na 1
  bus.write_byte_data(addr,0x12,novy) 
  bus.write_byte_data(addr,0x12,porta) 

# strobe pri pripojeni vyvodu E na GPIO22 na RasPi (rychlejsi)
#def strobe():
#  time.sleep(0.0000001)
#  GPIO.output(15, GPIO.HIGH)
#  time.sleep(0.0000001)
#  GPIO.output(15, GPIO.LOW)



## prepinani vyvodu RS pri pouziti expanderu (pomalejsi)
def setbit_rs(bit):   # nastaveni bitu GPA0 na "1" nebo "0" 
  global porta
  if (bit == 1):
    novy = (porta | 0x01) # GPA0 nastavit na 1 = data
  else:
    novy = (porta & 0xFE) # GPA0 nastavit na 0 = prikazy
  bus.write_byte_data(addr,0x12,novy) 
  porta=novy
  
# prepinani vyvodu RS pri pouziti GPIO23 (rychlejsi)
#def setbit_rs(bit):
#  if (bit == 1):
#    GPIO.output(16, GPIO.HIGH)
#  else:
#    GPIO.output(16, GPIO.LOW)  

# =========  




def prikazy():  # nastaveni vyvodu pro prepinani rezimu  (prikazy/data) na "0" ... (prikazy)
  setbit_rs(0)

def data():     # nastaveni vyvodu pro prepinani rezimu  (prikazy/data) na "1" ... (data)
  setbit_rs(1)



def setbit_lr(bit):   # nastaveni bitu GPA2 na "1" nebo "0" - prepinani leva/prava polovina displeje
  global porta
  if (bit == 1):
    novy = (porta | 0x04) # GPA2 nastavit na 1
  else:
    novy = (porta & 0xFB) # GPA2 nastavit na 0 
  bus.write_byte_data(addr,0x12,novy) 
  porta=novy
  


def setbit_rst(bit):   # nastaveni bitu GPA3 na "1" nebo "0" - RESET
  global porta
  if (bit == 1):
    novy = (porta | 0x08) # GPA3 nastavit na 1
  else:
    novy = (porta & 0xF7) # GPA3 nastavit na 0 
  bus.write_byte_data(addr,0x12,novy) 
  porta=novy



# prepocet ze souradnic celeho displeje (0 az 119) na:
#  kvadrant(leva/prava), page(znakova radka), osu X (sloupec) a bit v bajtu 
def prepocet(superx,supery):
  if(superx > 59):    
    bitlr=1
    superx=superx-60  
  else:
    bitlr=0
  page = int(supery/8)
  minibit = supery - (page * 8)
  return bitlr, superx , page, minibit



# vykresleni jednoduche horizontalni cary ve vysce "supery"
# pomoci parametru je mozne upravit parametry zacatku a konce cary
# parametr "styl" ma stejny vyznam, jako u funkce "plot" (nakreslit/smazat/invertovat)
def h_cara(supery, od=0 , do=120, styl=1):  
  for x in range(od, do+1):
    plot(x,supery,styl)
    



def plot(superx, supery, styl=1): # tisk/smazani/negace 1 pixelu na souradnicich [superx,supery] 
    global stara_lr

    #kontrola vstupnich rozsahu promennych superx a supery
    if (superx < 120 and superx >= 0 and supery < 64 and supery >= 0): 
      pozice_xy = prepocet(superx,supery)

      # prepinani kvadrantu (leva/prava) provadet pouze v pripade, ze se od minule nastaveni kvadrantu zmenilo
      if (stara_lr != pozice_xy[0]):
        setbit_lr(pozice_xy[0])
        stara_lr = pozice_xy[0]

      prikazy()
      bus.write_byte_data(addr,0x13,0xB8 + pozice_xy[2])       # page (znakova radka 0 az 7)
      strobe()
      bus.write_byte_data(addr,0x13,0x40 + pozice_xy[1])       # x-osa (mikrosloupec 0 az 120)
      strobe()
      staramapa = mapa[pozice_xy[0],pozice_xy[1],pozice_xy[2]] # puvodni obsah displeje do pomocne promenne
      data()

      # obsah pomocne promenne "staramapa" sloucit s novym bodem podle toho, jak je nastaveny styl
      if (styl == 0):  # smazani bodu
        novy=(~(0x01 << pozice_xy[3]) & staramapa)
        bus.write_byte_data(addr,0x13,novy) 

      if (styl == 1):  # zobrazeni bodu
        novy=((0x01 << pozice_xy[3]) | staramapa)
        bus.write_byte_data(addr,0x13,novy) 

      if (styl == 2):  # inverze bodu
        novy=((0x01 << pozice_xy[3]) ^ staramapa)
        bus.write_byte_data(addr,0x13,novy) 

      mapa[pozice_xy[0],pozice_xy[1],pozice_xy[2]] = novy     # aktualizace promenne mapa[]
      strobe()



# vytisknuti jednoho znaku na pozici superx (0 az 119) a radka (0 az 7)
def znak(kod , superx , radka , inverze=False):
    global stara_lr
    adr_fontu = (kod-32) * 5  # ukazatel na zacatek petice bajtu prislusneho znaku

    if ( superx < 60 ):     # podle X-ove souradnice leve strany znaku nastav kvadrant
      setbit_lr(0)          # na levy
      stara_lr = 0
    else:
      setbit_lr(1)          # nebo pravy
      stara_lr = 1

    # nastaveni pozice leve strany znaku (prvniho prenaseneho bajtu) do prislusneho kvadrantu
    prikazy()
    bus.write_byte_data(addr,0x13,0xB8 + (radka % 8))   # page (znakova radka)
    strobe()
    bus.write_byte_data(addr,0x13,0x40 + (superx % 60)) # x-osa (mikrosloupec)
    strobe()
    data()

    for f in range(5):
      if (superx + f < 120):  # ochrana prekroceni celkove sirky displeje 
          # kdyz pri tisku znaku dochazi k prechodu pres polovinu displeje ...
        if (superx + f > 59 and superx < 60 and stara_lr == 0):
          setbit_lr(1)           # nastav kvadrant na "pravý" ...
          stara_lr = 1
                                 # ... a v tomto kvadrantu nastav pozici na zacatek patricne radky
          prikazy()
          bus.write_byte_data(addr,0x13,0xB8 + (radka % 8)) # page
          strobe()
          bus.write_byte_data(addr,0x13,0x40) # X = 0 (prvni sloupec zleva v pravem kvadrantu)
          strobe()
          data()
      
        if (inverze == False): # podle inverze bud prekopirovat na displej bajt z fontu, nebo ten bajt jeste invertovat
          bajt = font[adr_fontu + f]
        else:
          bajt = ~(font[adr_fontu + f])
              
        bus.write_byte_data(addr,0x13,bajt)                           # zapis dat do displeje
        mapa[stara_lr , ((superx + f) % 60) , (radka % 8) ] = bajt    # aktualizace promenne mapa[]
        strobe()    



# vytisknuti nekolika pismen za sebou oddelenych nastavitelnou mezerou 
def slovo(text, superx , radka, mezera=1 , inverze=False):
  global stara_lr
  if (isinstance(text, unicode) == False):  # pokud text neni v unicode, tak ho preved
    text=  unicode(text, "utf-8")           # prevod textu z UTF-8 na unicode

  # cely text prochazet znak po znaku a tisknout
  for zn in range (len(text)):
    if (ord(text[zn:zn + 1]) > 127):   # pokud jde o neASCII znak, provest prekodovani podle tabulky
      try:                    # pokud neni specialni kod nadefinovany v tabulce, ...
        znak(cz[ord(text[zn:zn + 1])], superx, radka, inverze)
      except:                 # ... skoncil by program chybou neexistujiciho indexu promenne cz[].
        znak(177, superx, radka, inverze)  # V tom pripade se nahradi nedefinovany znak znakem sachovnice

    else:
      znak(ord(text[zn:zn + 1]), superx, radka, inverze) # ASCII znaky s kodem 32 az 127 tisknout normalne

    # za kazdym znakem vytisknout patricny pocet mezer (sirka mezery 1 bod)
    for m in range(mezera):
      if (superx + m + 5 < 120):  # ochrana prekroceni celkove sirky displeje 

        # kdyz pri tisku mezery dochazi k prechodu pres polovinu displeje ...
        if (superx + 5 + m > 59 and superx < 60 and stara_lr == 0): 
          setbit_lr(1)           # nastav kvadrant na "pravý"
          stara_lr = 1

          #  a v tomto kvadrantu nastav pozici na zacatek patricne radky
          prikazy()
          bus.write_byte_data(addr,0x13,0xB8 + (radka % 8)) # page
          strobe()
          bus.write_byte_data(addr,0x13,0x40) # X = 0 (prvni sloupec v pravem kvadrantu)
          strobe()
          data()
      
        if (inverze == False): # podle toho, jak je nastavena inverze, je mezera bud prazdna, nebo plna osmice bodu
          bajt = 0x00
        else:
          bajt = 0xff
              
        bus.write_byte_data(addr,0x13,bajt)                              # zapis do displeje
        mapa[stara_lr , ((superx + 5 + m) % 60) , (radka % 8) ] = bajt   # a aktualizace promenne mapa[]
        strobe()  

    superx= superx + 5 + mezera   # dalsi znak se tiskne az za mezerou
    

# zobrazeni vodorovne rotujiciho napisu
# napis    ... text, ktery bude rotovat
# osax     ... cislo leveho sloupce, ve kterem se budou ztracet pismena (0 az 119)
# radka    ... cislo radky 0 az 7
# sirka    ... sirka oblasti ve znacich (v sesticich pixelu), ktera je vyhrazena pro rotujici napis
# rychlost ... pauza v sekundach mezi jednotlivymi mikroposuvy
def rotace(napis ,osax ,radka ,sirka, rychlost=0.1):

  napis = unicode(napis,"utf-8")
  osax = osax + 5    # posun o jeden znak - v tom prvnim se na sebe skladaji jednotliva pismena
  sirka = sirka - 1  # kvuli tomu se musi upravit i sirka oblasti

  if (osax + (sirka * 6) - 1  < 120):  # kontrola korektniho zadani (zacatek + sirka v pixelech nesmi prelezt okraj)
    napis= napis + ((sirka - 1) * " ") # doplneni mezer za text, aby cely text postupne mizel z displeje
    napis = (sirka * " ") + napis      # doplneni mezer pred text, aby cely text postupne najizdel
    start_vyrezu=0

    for znak in range((1 + len(napis) - sirka) *6):  # hlavni smycka pro postupne zobrazeni celeho napisu
      if (znak % 6 == 0):
        start_vyrezu = start_vyrezu + 1
      slovo(napis[start_vyrezu:start_vyrezu+sirka], osax - (znak % 6 ), radka, 1)
      time.sleep(rychlost)
  


#rolovani celeho displeje nahoru s vyuzitim nastaveni "StartLine" v displeji (po mikroradkach)
# rozhazi souradnicovy system, takze po pouziti je treba displej smazat znova nastavit StartLine na 0
# vsechny radky je treba nadefinovat pred spustenim podprogramu do textoveho indexovaneho pole
def vert_rolovani(textove_pole):
  global stara_lr
  pocet_radek=len(textove_pole) # podle poctu indexu v poli se zjisti pocet nove vkladanych radek

  i=0 # pocitadlo mikroradek
  for roll in range ((pocet_radek+1)*8):
    plot0(i)
    prikazy()
    setbit_lr(0) # leva polovina displeje
    bus.write_byte_data(addr,0x13,0xC0 + i)     # nastaveni StartLine
    strobe()
    setbit_lr(1) # prava polovina displeje
    bus.write_byte_data(addr,0x13,0xC0 + i)     # nastaveni StartLine
    strobe()
    stara_lr=1    



    if( roll%8 == 0 and roll != 0):  # po kazdem 8. posunu o mikroradku se dole vytiskne napis ze vstupu funkce
      slovo( textove_pole[int((roll-1)/8)] , 0 , 7+(i/8))   # napisy se zobrazuji na aktualne spodni radce
      time.sleep(0.1)
    else:
      time.sleep(0.2)   # kompenzace casu pro pripady, kdy se jen posouva displej, ale netiskne napis

    i = i + 1
    if (i > 63):  # kdyz dojde k prerotovani celeho displeje, tak se musi StartLine vynulovat
      i = 0       # a zaroven se vynuluje i pocitadlo mikroradek - promenna "i"

      prikazy()
      setbit_lr(0) # leva polovina displeje
      bus.write_byte_data(addr,0x13,0xC0)     # nastaveni StartLine
      strobe()
      setbit_lr(1) # prava polovina displeje
      bus.write_byte_data(addr,0x13,0xC0)     # nastaveni StartLine
      strobe()
      stara_lr=1    



# co nejrychlejsi smazani 1 pixelu na cele radce "supery" - vyuziva se pro vertikalni rotaci celeho displeje
def plot0(supery):
    for superx in range(120):                # provadi se pouze v promenne "mapa[]"
      pozice_xy = prepocet(superx,supery)
      staramapa = mapa[pozice_xy[0],pozice_xy[1],pozice_xy[2]]
      novy=(~(0x01 << pozice_xy[3]) & staramapa)
      mapa[pozice_xy[0],pozice_xy[1],pozice_xy[2]] = novy 


    # na zaver se prenese pouze 1 radka z promenne "mapa[]" na displej
    # - to je neco jako "mem_dump()", ale jen pro 1 radku
    for lr in range(2):          # 0=leva polovina, 1= prava polovina
      setbit_lr(lr)    
      prikazy()
      bus.write_byte_data(addr,0x13,0xB8 + pozice_xy[2])  # nastaveni page na radku, ktera je aktualne nahore
      strobe()
      bus.write_byte_data(addr,0x13,0x40)                 #  nastaveni adresy na zacatek teto radky  
      strobe()
      data()
      for sloupec in range(60):          # vyplneni pulradky 60 bajty z promenne "mapa[]"
         bus.write_byte_data(addr,0x13,mapa[lr,sloupec,pozice_xy[2]])
         strobe()





def mem_plot(superx,supery, styl=1): # stejne jako plot, akorat se data ukladaji pouze do pameti (do promenne mapa[]) 
    #kontrola vstupnich rozsahu promennych superx a supery
    if (superx < 120 and superx >= 0 and supery < 64 and supery >= 0): 
      pozice_xy = prepocet(superx,supery)
      staramapa = mapa[pozice_xy[0],pozice_xy[1],pozice_xy[2]]
      if (styl == 0):
        novy=(~(0x01 << pozice_xy[3]) & staramapa)
      if (styl == 1):
        novy=((0x01 << pozice_xy[3]) | staramapa)
      if (styl == 2):
        novy=((0x01 << pozice_xy[3]) ^ staramapa)
      mapa[pozice_xy[0],pozice_xy[1],pozice_xy[2]] = novy 


#rychle zobrazeni celeho obsahu pameti (promenne mapa[]) na displeji
def mem_dump():
  global stara_lr
  stara_lr=3

  for lr in range(2):        # 0=levy kvadrant     , 1= pravy kvadrant
    setbit_lr(lr)    
    for page in range(8):    # znakova radka 0 az 7
      prikazy()
      bus.write_byte_data(addr,0x13,0xB8 + page)  
      strobe()
      bus.write_byte_data(addr,0x13,0x40) # nastaveni vnitrni adresy displeje na zacatek radky  
      strobe()
      data()
      for sloupec in range(60):           # vyplneni pulradky 60 bajty promenne "mapa[]"
         bus.write_byte_data(addr,0x13,mapa[lr,sloupec,page])
         strobe()




def disclear(pattern=0x00):  # vyplneni cele plochy displeje zadanou hodnotou (0 = bila ... 255 = cerna)
  global stara_lr
  for lr in range(2):        # 0=levy kvadrant     , 1= pravy kvadrant
    setbit_lr(lr)         
    for page in range(8):    # znakova radka 0 az 7
      prikazy()
      bus.write_byte_data(addr,0x13,0xB8 + page)  
      strobe()
      bus.write_byte_data(addr,0x13,0x40) # nastaveni vnitrni adresy displeje na zacatek radky  
      strobe()
    
      data()
      bus.write_byte_data(addr,0x13,pattern)
      for sloupec in range(60):   # vyplneni pulradky 60 bajty promenne "pattern"
        mapa[lr,sloupec,page] = pattern  
        strobe()
  stara_lr = 1


# init obou kvadrantu na displeji (levy a pravy)
def init_disp():
  for lr in range(2):
    setbit_lr(lr)
    prikazy()
    bus.write_byte_data(addr,0x13,0xC0) # nastaveni TOP radky na 0 
    strobe()
    bus.write_byte_data(addr,0x13,0x40) # nastaveni vnitrni adresy displeje na zacatek radky 
    strobe()
    bus.write_byte_data(addr,0x13,0xB8) # nastaveni PAGE (znakova radka) na 0 
    strobe()
    bus.write_byte_data(addr,0x13,0x3F) # zapnuti displeje
    strobe()
  stara_lr = 1



# nacteni fontu ze souboru do seznamu "font[]"
# POZOR: soubor s fontem musi mit presne definovanou strukturu!
def nacist_font(jmenosouboru):
  fontfile=file(jmenosouboru,"r")
  adresafontu=0
  for radka in fontfile:
    rozlozeno = radka.split(",")                      # vysosani jednotlivych bajtu z jedne radky ...
    for bajt in range(5):
      font[adresafontu] = int(rozlozeno[bajt][-4:],0) # ... a ulozeni kazdeho toho bajtu do seznamu
      adresafontu = adresafontu + 1
  fontfile.close()



# nacteni dvoubarevneho BMP obrazku 120x64 bodu do promenne mapa[]
# POZOR: Bez jakychkoli testu na korektni format BMP souboru!
def load_bmp12064(jmeno_obrazku):
  soubor=open(jmeno_obrazku, "rb")  # nacteni obrazku do promenne data[]
  data = soubor.read()  
  soubor.close()                    # uzavreni souboru

  # podrobna specifikace hlavicky BMP souboru je tady:
  # http://www.root.cz/clanky/graficky-format-bmp-pouzivany-a-pritom-neoblibeny
  # zacatek obrazovych dat urcuji 4 bajty v souboru na pozicich 10 az 13 (desitkove) od zacatku souboru

  zacatekdat = ord(data[10]) + (ord(data[11]) * 256) + (ord(data[12]) * 65536) + (ord(data[13]) * 16777216)

  bajt=zacatekdat
  for r in range (63,-1,-1):  # cteni promenne data[] bajt po bajtu a prevod na souradnice superx,supery
    for s in range (16):
      for b in range(8):
        superx = (s*8) + b     # s = osmibodovy sloupec na displeji (0 az 14) , b = bit v kazdem bajtu 0 az 7
        supery = r             # r = mikroradka na displeji 0 az 63
        maska = (1 << (7-b))   
        #kazdy bit z grafickych dat se na prislusne pozici na displeji (resp. v promenne mapa[]) bud rozsviti, nebo zhasne
        if (ord(data[bajt]) & maska != 0):   # dvoubarevna windowsovska bitmapa ma opacne nastavene bity nez displej:
          mem_plot(superx,supery,0)          # jednickove bity jsou bile body
        else:
          mem_plot(superx,supery,1)          # nulove bity jsou cerne body

      bajt = bajt +1          # prejde na dalsi bajt z grafickych dat

  mem_dump()    # na zaver se obrazek rychle prenese z promenne mapa[], na displej



# zakladni nastaveni (expander, GPIO, nacteni fontu z externiho souboru, reset a smazani displeje)
def init():
  
  # nastaveni expanderu - smery portu (IN/OUT)
  bus.write_byte_data(addr,0x00,0x00) # cely port A na OUT
  bus.write_byte_data(addr,0x01,0x00) # cely port B na OUT
  # uvodni nastaveni vsech pinu expanderu
  bus.write_byte_data(addr,0x13,0x00) # celyport "B" (data) na "0"
  bus.write_byte_data(addr,0x12,0x08) # vsechny signaly na portu "A" mimo RESETu na "0"
  stara_lr = 0


#=================
  # pouzit jen v pripade pripojeni ridicich signalu primo na GPIO konektor v RasPi
  # nastaveni GPIO portu na RasPi pri pripojeni vyvodu E primo na GPIO22
#  GPIO.setwarnings(False)
#  GPIO.setmode(GPIO.BOARD)
#  GPIO.setup(15, GPIO.OUT)    # pin 15 = GPIO22

  # nastaveni GPIO portu na RasPi pri pripojeni vyvodu RS primo na GPIO23
#  GPIO.setwarnings(False)
#  GPIO.setmode(GPIO.BOARD)
#  GPIO.setup(16, GPIO.OUT)    # pin 16 = GPIO23
#=================


  nacist_font("/home/pi/font.txt")

  # reset - kratky nulovy impulz na displejove noze RESET 
  setbit_rst(0)
  time.sleep(0.005)   # trvani 5ms
  setbit_rst(1)

  init_disp()         # nastaveni atualni pozice vsech kvadrantu na levy horni roh + odblokovani
  disclear(0x00)      # vyplneni celeho displeje nulami (smazani) a zaroven prvotni inicializace promenne mapa[]


if __name__ == '__main__':
  main()
