Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Tablica LED.Nietypowe i dość skomplikowane składanie danych.

KJ 06 Kwi 2005 01:08 2397 11
  • #1 06 Kwi 2005 01:08
    KJ
    Poziom 31  

    Mam następujący problem: posiadam tablice diodową 126 na 15 LED i trzeba napisać drugi generator znaków (jeden już mam trochę o nim jest w poście Bascom i cuda które się dzieją jeśli admin uzna że oba posty powinny zostać scalone to proszę scalić zakładam nowy z powodu iż tamten był defakto o problemach z kompilatorem nie o samej tablicy) Sprawa wygląda następująco: Chcę zrobić 2 linie po 20 znaków niby nic trudnego ale napotkałem na problem w postaci architektury tablicy która wygląda jak na poniższym rysunku. mam podział na pionowe "strony" o wymiarach 8 na 15 led dane do każdej strony są wpisywane jako całe 8 bitów wystawianych na port procesora. Dane są wpisywane od góry do dołu strony pierwszej poczym drugiej trzeciej itd.... Jak chcę wyświetlić 2x20 znaków to potrzebuję składać dane na bieżąco bo znak będzie wtedy szeroki na 5 pixeli wysoki na 7 należy jeszcze na końcu znaku dorzucić jedną kolumnę pustą(przerwa między znakami) i wychodzi na to że w pierwszej stronie musi się znaleźć cały pierwszy znak oraz cały 21(pierwszy drugiej linii) do niego musi zostać"doklejona" pierwsza kolumna znaki 2 oraz 22(drugiego drugiej linii) w drugiej stronie będzie znak 2 i 22 bez pierwszej kolumny oraz 2 kolumny znaków 3 i 23(trzeciego drugiej linii) w stronie trzeciej i kolejnych następuje przesunięcie o kolejne kolumny. Kody znaków znajdowały by się w tablicy w pamięci flash. Cała powierzchnia wyświetlacza znajduje się w tablicy umieszczonej w ram-ie program wyświetlający pracuje w przerwaniu od timera i tylko odświeża ekran. Procesor to Atmega32 Program pisany w bascomie. Nie mam pojęcia jak się za to zabrać i jakich funkcji użyć trzeba by jakoś dziwnie przesuwać dane z tablicy flash następnie wpisywać je na odpowiednie pozycje tablicy ram. Generator który jest działa podobnie ale tam sprawa jest o tyle prostsza że wyświetla tylko 16 znaków w jednej linii znaki szerokie na 7 pixeli + przerwa wiec jeden znak = jedna strona. Może ktoś ma jakiś pomysł ?? Jutro postaram się podesłać kompletny schemat tablicy.

    0 11
  • #2 06 Kwi 2005 09:30
    GienekS
    Poziom 32  

    Ja nie widzę problemu. Skoro obraz całego wyświetlacza jest w pamięci RAM procka to cała zabawa polega jedynie na przepisaniu wskazanego znaku z generatora znaku do tego RAM-u. Ja mam trochę inną matrycę 16x96 i też to tak robię i funkcjonuje bez problemu. Jedynie inaczej realizuję wpis do tej tablicy ale to w tym momencie nie jest istotne.

    0
  • #3 06 Kwi 2005 19:31
    shg
    Specjalista techniki cyfrowej

    Jeżeli między znakami masz jedną kolumnę odstępu, to na pierwszej stronie będą dwie, a nie jedna kolumna z drugiego znaku, no ale to szczegół ;)
    Nie wiem, jak masz zapisaną "czcionkę". Jak na moje oko, to może być po bajcie na każdą linię i takich linii 7, jedna po drugiej, potem następny znak itd. Nieużywane bity (te z prawej, znaki wyrównane sa do lewej krawędzi) ustawione są na 0

    zmienne:
    kol_w - aktualna strona (kolumna 8 bitowa na matrycy) (0 - 15), zwróć uwagę na to, bo ja wszystkie tablice zaczynam od 0, a nie bezsensownie od 1 jak bascom (w ogóle bascom to kicha ;) tablice w ramie mają indeksy od 1, a w pamięci programu od 0). Tak jest łatwiej liczyć.
    kol_z - aktualna kolumna znaku (6 bit = znak + przerwa) (0 - 19)
    y - pozycja na stronie, czyli wiersz od 0 do 14
    wiersz - no wiersz, a co? :D 0, albo 1, czyli pierwszy, albo drugi
    led1 - 8 bitowa kombinacja ledów, którą trzeba zapalić w wierszu y na stronie kol_w. zakładam, że zorganizowane to jest w jedynie słuszny sposób, czyli najstarszy bit z lewej.
    led2 - jak wyżej, ale w kol_w+1, dotyczy tego samego znaku!
    znak[] - tablica zawierająca tekst do wyświetlenia elementy o indeksach od 0 do 19 - pierwsza linia, od 20, do 29 - druga linia.
    mat[] - matryca - zaczyna się od 0 i po kolei - pierwszy wiersz pierwszej strony, drugi wiersz pierwszej strony, ... , 15 wiersz pierwszej strony, pierwszy wiersz drugiej strony.
    matadr - aktualny indeks dla tablicy mat[], czyli aktualnie obrabiana ósemka LEDów, kolejność, tak jak przy mat[] napisałem
    zsh - poziome (w prawo) przesunięcie znaku względem początku strony, np. 2 oznacza, że pierwsza kolumna znaku wyświetlana jest w 3 kolumnnie strony.

    A i taki patent, bo można to na dwa sposoby zrobić - pierwszy, to w locie obliczać przesunięcia znaku, a drugi - zapamiętać je na stałe, w Twoim wypadku są tylko 4 możliwe - 0, 2, 4 i 6 pixeli od początku strony, oba patenty są proste, drugi jest szybszy, ale żeby coś zmienić, trzeba zmieniać kawałek programu, a nie tylko jedną stałą (nie zawsze). Na szczęście nie będzie żadnych pierdół typu dynamiczne zmiany czcionki itd. (chyba się nie mylę? :D ), więc rozwiązanie drugie

    znaksh[] - tablica przesunięć, cztery elementy (przesunięcia powtarzają się w coczwartym znaku):
    znaksh[] = [0, 6, 4, 2]

    genzn[] - wzorce znaków, tablica zorganizowana tak, jak pisałem na początku. kolejne indeksy tej tablicy oznaczają kolejne wiersze kolejnych znaków :)




    geni - indeks w tablicy znaków (powyższa)
    zg - jedna linia znaku pobrana z tablicy

    będę pisał w jakimś pseudo-BASICu, nie specjalnie przejmowałem się optymalizacją, chciałem pokazać tylko samą ideę

    Code:
    FOR kol_z = 0 TO 19    ; najpierw pętla przemiatająca kolejne kolumny, ale znaków (uwaga!)
    
      kol_w = (kol_z * 6) >> 3 ; numer strony na której znajduje się pierwsza kolumna wyświetlanego znaku,  >> 3 - przesunięcie w prawo (obojętnie, czy logiczne, czy arytmetyczne, oba będą działać), czyli w istocie  dzielenie przez 8  z zaokrągleniem w dół (8, bo 8 pixeli na stronę).  *6, bo 6 pixeli na znak, taka proporcja ;)

      zsh = znaksh[kol_z AND 3]    ; indeksem jest pozycja x znaku modulo 4, (modulo w szybszym wydaniu :) )
      matadr = kol_w*15    ; adres komórki, w pamięci matrycy
    ; ewentualnie powyższe w wersji szybszej na procesorach z barrel-shifterem, albo bez mnożenia (bascom, nie wie, że atmega ma instrukcje mnożenia, więc i tu może być wydajniej tym sposobem):
    ; matadr = (kol_w << 4) - kol_w   ; "<<" to przesunięcie w lewo oczywiście
      zn = tekst[kol_z]    ; aktualnie wyświetlany znak (pierwsza linia)
      geni = zn * 7    ; początek znaku w "czcionce"
    ; ewentualnie: geni = (zn << 3) - zn
      FOR y = 0 TO 6    ; teraz kolejne wiersze matrycy dla pierwszej linii
        led1 = mat[matadr+y]    ; do led1 załaduje aktualną zawartość matrycy, dla lewej części znaku (ewentualnie całego, jeżeli się zmieści)
        zg = genzn[geni + y]    ; pobiera jedną linie czcionki do wyświetlenia
    ; i teraz można zrobić to na dwa sposoby, albo zapisać znak do starszego bajtu w dwubajtowym słowie i przesunąć całe słowo o zsh, potem starszy bajt wpisać do led1, a młodszy do led2, albo wykonać dwa osobne przesunięcia - jedno dla led1, drugie dla led2. poza tym, trzeba jeszcze wyczyścić poprzednią zawartość led1 i led2, ale tylko tam, gdzie wyświetlany będzie znak. zrobię to sposobem drugim (w sumie trudniejszy :D ):
        led1 = led1 AND NOT(%11111100 >> zsh)    ; troszkę zakręcone ;) a więc: najpierw maska dla pojedynczego znaku (ostawione 6 bitów z lewej) jest przesuwana w prawo o tyle, ile wynosi przesunięcie poziome znaku, tym razem MUSI to byćpzesunięcie logiczne, tak, żeby od lewej strony "wjeżdżały" zera (bascomowy SHIFT jest logiczny). następnie tak spreparowana maska jest negowana (NOT) i w miejscach, gdzie będzie wyświetlany znak pojawiają się zera, tam, gdzie "wjechały" zera przy przesuwaniu będą teraz jedynki, podobnie z tymi zerami na końcu (zostaną tylko jeżeli zsh=0). wykonanie operacji AND zeruje w zmiennej led1 te bity, które w masce były ustawione na 0
        temp = zg    ; jakaś zmienna tymczasowa, zaraz "zepsujemy" zg, a będzie jeszcze potrzebne
        zg = zg >> zsh    ; przesuwamy lewą część znaku na właściwą pozycję (lewa, to znaczy ta, która będzie wyświetlona na "lewej" stronie, czyli o niższym numerze :) ), przesunięcie musi być logiczne
        led1 = led1 OR zg    ; część znaku jużprawie wyświetlona ;)
    ; teraz druga część
        IF (zsh > 2)    ; nie ruszamy drugiej części, jeżeli znak w całości mieści się w pierwszej
          led2 = mat[matadr+15+y]    ; a tutaj zawartość sąsiedniej strony, odczytujemy tylko, kiedy jest potrzebna, czyli teraz :)

           led2 = led2 AND (%11111111 >> (zsh-2))
    ; tu jest nieco inaczej, przesuwamy maskę w prawo o tyle, ile kolumn ze znaku będzie wyświetlanych na drugiej stronie, znowu od  lewej "wjadą" zera, a AND wyczyści te bity, gdzie w masce będzie 0
          temp = temp << (8 - zsh)    ; w temp zostawiamy tylko prawą część znaku, która będzie widoczna na "prawej" stronie
          led2 = led2 OR temp    ; zapisujemy temp do prawej strony
          mat[matadr+15+y] = led2    ; zapisujemy prawą stronę znaku + to, co zostało z sąsiedniego znaku spowrotem do matrycy
        ENDIF
        mat[matadr+y] = led1    ; zapisujemy lewą część
      NEXT    ; następna linia w matrycy

    ;------------------------
        mat[matadr+7] = 0    ; czyścimy linię pod pierwszym znakiem
    ;------------------------

    ; druga linia znaków, może i nieoptymalnie pod względem długości kodu, ale pod względem prędkości, o wiele lepiej :)
    ; prawie wszystko tak samo, jak powyżej (dotyczy też proponowanych "optymalniejszych" ;) wersji niektórych operacji), tam gdzie jest inaczej jest komentarz)

      zn = tekst[kol_z+20]    ; aktualnie wyświetlany znak (druga linia linia)
      geni = zn * 7
      FOR y = 8 TO 14    ; teraz kolejne wiersze matrycy dla drugiej linii
        led1 = mat[matadr+y]
        zg = genzn[geni + y - 8]    ; pobiera jedną linie czcionki do wyświetlenia, - 8, żeby indeksy linii zaczynały się od 0

        led1 = led1 AND NOT(%11111100 >> zsh)
        temp = zg
        zg = zg >> zsh
        led1 = led1 OR zg
    ; teraz druga część znaku
        IF (zsh > 2)
          led2 = mat[matadr+15+y]
           led2 = led2 AND (%11111111 >> (zsh-2))
          temp = temp << (8 - zsh)
          led2 = led2 OR temp
          mat[matadr+15+y] = led2
        ENDIF
        mat[matadr+y] = led1
      NEXT    ; następna linia w matrycy

    NEXT    ; następna kolumna znaków


    starczy. :D
    W skrócie, co ta procedura robi: konwersja CAŁEGO tekstu z tablicy tekst na graficzne dane przeznaczone do bezpośredniego wyświetlenia na matrycy 8x15x16

    Zrobienie osobnej procedury do wyświetlania tylko jakiegoś kawałka tekstu też jest w miarę proste, ale tak jest jeszcze prościej i dość wydajnie, przy okazji jest wyświetlanie z podwójnym buforowaniem :D
    Poza tym teraz napisanie procedur do wyświetlania tekstu jest prostsze, bo wystarczy tylko kopiować do tablicy, a potem wszystko odświeżyć

    Jeżeli pominąć tworzenie maski i kasowanie poprzzedniej zawartości, czyli tą część, gdzie jest led1 = led1 AND cośtam (i tak samo dla led2), to przez wielokrotny zapis można uzyskać nakładanie się na siebie znaków, czyli np. najpierw zapiszesz tekst z literą "l", potem jeszcze raz to samo, ale w miejscu "l" będzie slesz "/" i będziesz miał "ł" :)

    ładny śmietnik z tego kodu wyszedł :D
    "%" - oczywiście system binarny. w bascomie było chyba "b" za liczbą, o ile dobrze pamiętam

    0
  • #4 07 Kwi 2005 00:03
    KJ
    Poziom 31  

    Na razie się nie mylisz z dynamicznymi czcionkami ale może kiedyś ?? :) Będę z tym kombinował nie miałem za bardzo czasu na dokładne przeanalizowanie tego "programu" :) ale wydaje mi się że to ma szanse działać. Piszę w bascomie bo taki język w miarę znam a ASM to dla mnie totalna czarna magia. Jedyne co jest jeszcze sensowne to C ale nie wiem czy ma sens uczyć się C a dopiero potem kończyć projekt.

    0
  • #5 07 Kwi 2005 00:13
    LordBlick
    VIP Zasłużony dla elektroda

    Osobiście mogę tylko potwierdzić, że kończąc projekt, najszybciej można się nauczyć nawet asemblera... ;) To co kolega shg napisał nie ma nic współnego ani z asemblerem, ani z C, najwięcej ma cech bascoma, nie rozumiem, dlaczego kolega KJ nie rozumie, tym bardziej, że pełno tu komentarzy mocno objaśniających, wystarczy zamienić ";" na "'"... ;)
    Pozdrawiam, Light-I

    0
  • #6 07 Kwi 2005 01:15
    KJ
    Poziom 31  

    Wiem że to co kolega shg napisał nie jest niczym gotowym po prostu miałem czas tylko na pobieżne przejrzenie odpowiedzi jutro przeczytam wszystko dokładnie i będę pisał. Co do stwierdzenia że asm to dla mnie czarna magia odnosiło się nie do kodu tylko do zdania shg że bascom to kicha ;)

    0
  • #7 09 Kwi 2005 17:50
    KJ
    Poziom 31  

    No dobrze mniej wiencej rozumiem jak to trzeba zrobić ale nie bardzo umiem sobie poradzić z tym kawałkiem :

    zsh = znaksh[kol_z AND 3] ; indeksem jest pozycja x znaku modulo 4

    Jak zrobić modulo 4 w bascomie ??

    0
  • #8 09 Kwi 2005 19:23
    zumek
    Poziom 39  

    By nie niszczyć zmiennej kol_z , musisz użyć jakiejś zmiennej pomocniczej.

    Code:

    kol_0=kol_z AND 3
    zsh = znaksh[kol_0]


    Piotrek

    0
  • #9 09 Kwi 2005 21:11
    shg
    Specjalista techniki cyfrowej

    Nie rób modulo, bascomowi zeżre to w cholerę cylki
    Zrób tak, jak jest, czyli AND 3 - to jest to samo, co modulo 4, a ogólnie wszystkie operacje typu modulo potęga dwójki można zastąpić taką konstrukcją:
    a MOD (2^b) = a AND ((2^b)-1)
    np. a MOD 32 = a AND 31, a MOD 512 = a AND 511
    Ten sposób działa szybciej na każdej architekturze, bez wyjątków :) w wypadku bascoma AVR może być nawet ze 100 razy szybszy, bo bascom milcząco zakłada, że procki AVR nie mają instrukcji dzielenia (a ATmega ma) i wykonuje dzielenie w sposób conajmniej mało wydajny, że tak powiem "ręcznie" (algorytm jest podobny do dzielenia pisemnego, tylko, że binarnie)

    A modulo tak:
    wynik = a MOD b

    0
  • #10 09 Kwi 2005 22:40
    LordBlick
    VIP Zasłużony dla elektroda

    shg napisał:
    [..]bascom milcząco zakłada, że procki AVR nie mają instrukcji dzielenia (a ATmega ma)
    Jak wyglada opkode instrukcji dzielenia w AVR ? Która ATmega ma ? ZTCW jest mnożenie (MUL), mnożenie znak/bez znaku (MULS), cząstkowe mnożenie (FMUL) + znak/bez znaku (FMULS), ale o dzieleniu nie słyszałem. Jest ponadto instrukcja modulo na dwóch dowolnych rejestrach (EOR). Polecan spis instrukcji asemblera na końcu pdf-a (najczęściej dalej jest już tylko opis obudów, erraty, changelog i spis treści) od dowolnej ATmeg-i.
    Pozdrawiam, Light-I

    0
  • #11 10 Kwi 2005 16:04
    shg
    Specjalista techniki cyfrowej

    Ano faktycznie nie ma dzielenia, coś mi się popaprało jak ostatnio zestaw instrukcji przeglądałem, bo MC68k mają MULU, MULS DIVU i DIVS, no i te mnemoniki takie same były i w ogóle... ;)
    EOR = modulo? EOR = Exclusive OR

    0
  • #12 11 Kwi 2005 06:48
    LordBlick
    VIP Zasłużony dla elektroda

    shg napisał:
    EOR = modulo? EOR = Exclusive OR
    Ano dla odmiany mi tez się pomieszało EXOR z modulo, chyba mają podobny symbol matematyczny, czy jakoś tak... Modulo to oczywiście reszta z dzielenia, a EXOR to ustawienie różniących się na poszczególnych miejscach bitów.
    Pozdrawiam, Light-I

    0