74HCT245 jako port wejściowy - nie działa, ch

Jak już parę razy pisałem od jakiegoś czasu eksperymentuję ze starymi systemami mikroprocesorowymi. Powody nie są praktyczne, a czysto dydaktyczne - to okazja do bardziej niskopoziomowego wejścia w tę tematykę i rozpoczęcie zabawy z asemblerem. Jakiś czas temu wykonałem płytkę bazową z WDC65C02:

formatting link
zacząłem dodawać do niej moduły, np.
formatting link
W roli portu wejściowego wykorzystuję zwykle układ 74HCT245. Z jednej strony np. przyciski rezystorami podciągającymi, z drugiej magistrala danych, wejście aktywujące sterowane bramką NAND do której wejść podłączony jest dekoder adresów i sygnał RD (6502 go co prawda nie generuje, ale na głównej płytce dodałem kilka odpowiedzialnych za to bramek, żeby dało się bezpośrednio podpiąć również układy przeznaczone do pracy na magistrali intela.

Zarówno stronę sprzętową, jak i programową udało mi się już przetestować na jednym projekcie - przyciski działają w nim prawidłowo. Problemy zaczęły się natomiast, gdy spróbowałem przenieść to samo rozwiązanie do innego projektu. Tam zastosowałem ten sam układ, z tą jedną różnicą, że przyciski i magistrala są podłączone do 245 z odwrotnej strony, ale to nie powinno mieć znaczenia, bo odpowiednio zmieniony został stan na wejściu DIR.

Okazało się jednak, że w tym drugim urządzeniu to rozwiązanie nie chce już działać. To znaczy przyciski niby działają - ich wciśnięcia sa rejestrowane przez program, ale jednocześnie pojawia się niestabilność systemu. Po pierwsze pojawiają się fałszywe sygnały z przycisków, po drugie urządzenie zaczyna działać niestabilnie i co jakiś czas resetuje się. Problem znika, jeśli zakomentuję linie odpowiedzialne za sprawdzanie stanu przycisków.

Na chwilę obecną sprawdziłem już następujące kwestie:

- Na pewno port wejściowy jest podłączony do prawidłowego wyjścia dekodera adresów i w programie sprawdzany jest właściwy, odpowiadający mu adres.

- Na pewno na wejściu DIR wymuszony jest właściwy stan i przyciski podłączone są do wejść układu 245, a magistrala danych do jego wyjść.

- Do tego samego dekodera adresów (74HCT138) na płytce podłączone jest także jeszcze jedno urządzenie (wyświetlacz hd44780) i ono działa prawidłowo.

- Przyciski są podciągnięte do VCC. Próbowałem zmniejszyć wartości rezystorów podciągających z 10k do 3,3k - nie pomogło.

- Próbowałem wymienić 245 na inny egzemplarz. Wymieniłem także towarzyszące mu bramki 74HCT00. Nie pomogło.

Spróbuję jeszcze wymienić dekoder adresów (138), ale powoli już kończą mi się pomysły. Ktoś ma może pomysł, gdzie jeszcze mogę szukać przycyzny?

Reply to
Atlantis
Loading thread data ...

Użytkownik "Atlantis" napisał w wiadomości grup dyskusyjnych:5f3e204e$0$520$ snipped-for-privacy@news.neostrada.pl...

Opis by sugerowal, ze te 245 wystawia dane na magistrale w niepożądanym momencie.

Cos mi chodzi po glowie, ze 245 byla "wzmocniona" w stosunku do "standardowy TTL", aby mogla lepiej sterowac magistralą, i cos mi chodzi po glowie, ze moze nie bylo to symetryczne ... czyli jedna strona byla "magistralowa" ... ale to bylo LS, HCT trzeba by doczytac.

Ale ... powod moze byc tez zupelnie inny. Chocby czysto softwareowy - jakis blad w funkcjach przyciskow, zla obsluga przerwania itp.

Na stale ? Choc to w sumie bez znaczenia - czytac chcesz, to kierunek umozliwia zaklocenie magistrali.

Dobrze by bylo obejrzec sygnaly sterujace na oscyloskopie - czy OE dociera do 745 odpowiednio szybko, i odpowiednio szybko zanika.

I jak zwykle - daj wolniejszy zegar, jak przejdzie ... moze cos nie wyrabia szybkosciowo.

J.

Reply to
J.F.

To było moje pierwsze skojarzenie. Chociaż dziwne jest to, że program nie tylko rejestruje samoczynne (fałszywe) naciśnięcia klawiszy, ale też prawidłowo reaguje na te prawdziwe. Przypomniałem sobie też, że kiedyś już testowałem działanie tego portu, co jakiś czas sprawdzając w pętli, czy po wciśnięciu przycisków zmieni się wartość odczytywana z portu - zmieniała się, a program działał stabilnie i nie wieszał się. Czyli wyglądałoby na to, że to jednak nie sam odczyt z portu jest czynnością destrukcyjną z punktu widzenia stabilności programu.

Moim drugim pomysłem była funkcja zajmująca się debouncingiem. Tylko jak już pisałem, ta działa prawidłowo w innym urządzeniu opartym na tej samej architekturze.

Funkcja sprawdzania przycisku wygląda następująco:

O tym też myślałem, jednak nie pasuje mi żadna z potencjalnych przyczyn, które brałem pod uwagę. Przerwania nie są wykorzystywane do obsługi przycisków. Wszystkie inne funkcjonalności oparte na przerwaniach (UART RX, timer, RTC) działają prawidłowo. Myślałem o potencjalnym nadpisaniu stosu, ale:

- Niewielki sprzętowy stos 6502 jest wykorzystywany przez kompilator CC65 (to z niego korzystam, niewielkie fragmenty kodu pisząc w asemblerze).

- Do większości operacji wykorzystywany jest programowy stos, który zaczyna pisać w górnej części pamięci RAM, idąc w dół.

- Układ posiada 8 kB pamięci RAM, co jest stosunkowo dużą wielkością jak na tego typu konstrukcję.

- Do tej pory nie korzystam ze zbyt dużej liczby zmiennych i jest raczej mało prawdopodobne, żeby stos zaczął nadpisywać pamięć.

- Zwłaszcza, że staram się ostrożnie gospodarować stosem. Nie tworzę zbyt dużych ilości zmiennych statycznych wewnątrz funkcji (jeśli to możliwe wykorzystując statyczne zmienne globalne, dzielone między funkcjami), nie przekazuję zbyt dużej ilości parametrów (zamiast tego posługuję się np. wskaźnikami na strukturę albo zmiennymi globalnymi ustawianymi przed wywołaniem funkcji). Staram się też unikać zagnieżdżonych wywołań funkcji i rekurencji.

Zegar już teraz jest wolny - jedynie 1 MHz. Właśnie z uwagi na duże moduły połączone taśmą ze złączami IDC. To raczej sporo poniżej możliwości WDC65C02 oraz wymagań współczesnych układów HCT...

Reply to
Atlantis

Brakująca funkcja do sprawdzania przycisku:

formatting link

Reply to
Atlantis

Atlantis snipped-for-privacy@wp.pl napisał(a):

Tego nie łapię. Zmienne statyczne w funkcjach nie są stosie. Gdyby były na stosie, nie mogłyby być statyczne.

Reply to
Grzegorz Niemirowski

Błąd wynikający z pisania z rozpędu. Oczywiście miałem na myśli niestatyczne zmienne lokalne. Zamiast nich stosuję albo statyczne zmienne globalne (jeśli można oszczędzić trochę pamięci, np. współdzieląc między funkcjami bufory albo zmienne przechowujące wartości tymczasowe) albo statyczne zmienne lokalne.

Reply to
Atlantis

Wymiana układy 74HCT138 też nie pomogła. Tym samym wymieniłem wszystkie układy współpracujące z 74HCT245. W akcie desperacji podmieniłem też wszystkie "mostki" z kynaru, jakie miałem na płytce - w razie gdyby któryś nie łączył. Też nic.

Sprawdziłem też, czy przypadkiem winy nie ponosi kod w funkcjach obsługujących naciśnięcie przycisku. Podmieniłem go na proste zapalanie i gaszenie diody. Pomimo tego problem ciągle występuje. Znika dopiero wtedy, gdy zakomentuję w pętli głównej funkcje sprawdzające stan przycisków.

Tak więc albo problem jest programowy i biblioteka do obsługi przycisków coś miesza (co byłoby dziwne, bo na innym urządzeniu działa), albo coś jest nie tak ze stosem, albo faktycznie chodzi o kierunek transmisji

74HCT245. Tyle tylko, że zanim zabiorę się za przeprojektowywanie modułu, wolałbym wykluczyć przyczyny programowe.
Reply to
Atlantis

Czy strobujesz sygnał OE w 74LS245 z sygnałem zegarowym FI2 w 6502 ?

Z dekodera adresów prawdopodobnie dostajesz stan niski przez cały czas trwania stabilnego adresu na magistrali adresowej. Wyjścia buforów powinny być aktywne tylko w czasie gdy jest adres, stan wysoki na R/W i stan wysoki na Fi2

Reply to
Grzegorz Kurczyk

Nie bezpośrednio. Na głównej płytce z CPU mam prosty układ z bramek

74HCT00, który z sygnałów RW oraz PHI2 generuje "intelowskie" sygnały RD i WR, które są wyprowadzone na magistralę systemową. Dzięki temu łatwiej korzystać z pamięci i peryferiów zaprojektowanych dla tej magistrali. Sygnał RD działa prawidłowo, bo wykorzystuję go do komunikacji z innymi peryferiami. np. układem RTC M6242B.

Ten właśnie wygenerowany sygnał RD, razem z wyjściem dekodera adresów steruje wejściem OE 245, za pośrednictwem bramki NAND. Oczywiście obydwa sygnały są wcześniej zanegowane, ponieważ są aktywne w stanie niskim.

I właśnie taka kombinacja sygnałów R/W i Fi2 generuje sygnał RD, który z kolei razem z wyjście dekodera adresów steruje wejściem OE 245.

W podobny sposób mm to zrealizowane w drugiej konstrukcji (tej, która działa prawidłowo). Jedyna różnica to fakt, że wejścia/wyjścia A i B pracują w odwrotnej konfiguracji. No i fakt, że w tym nowszym urządzeniu zrobiłem dekoder adresów na GAL-u, a nie 74HCT138 i paru bramkach.

Reply to
Atlantis

Chyba sie nie rozumiemy - mowie o przedluzeniu dzialania portu o np pol impulsu zegara, co powoduje, ze w nastepnym cyklu dane z portu nakladaja sie na dane z pamieci i procesor odczytuje smieci jako rozkaz. Albo dekoder adresow uruchamia 245 dodatkowo w zlym momencie.

Byc moze, ale ... mozliwe tez, ze wtedy program uzywa niekolidujacych adresow ...

Funkcja sprawdzania przycisku wygląda następująco:

Wpisz w obszar stosu np 55h, a ppotem zobacz ile ich zostalo.

Daj 500kHz czy 250kHz i sie zobaczy czy to w tym problem.

Po tylu systemach powinienes miec juz taki generatorek gotowy do podpiecia :-)

J.

Reply to
J.F.

W dniu 2020-08-20 o 20:08, Atlantis pisze:

W czasach, gdy stosowaliśmy scalaki THT i nie najlepiej projektowaliśmy płytki (GND pociągnięte ścieżkami) kilka razy zdarzyło nam się walczyć z jakimś problemem którego przyczyną w końcu okazywała się zbyt duża długość ścieżek podłączających kondensatory blokujące (też THT) do któregoś scalaka. P.G.

Reply to
Piotr Gałka

Co masz na myśli? Zarówno w funkcji sprawdzania stanu przycisku, jak i przy odczytywaniu portu bezpośrednio, korzystam z tej samej definicji:

#define BTNS (*(volatile uint8_t*)0x6300)

Sprawdziłem jeszcze raz i faktycznie samo odczytanie portu nie powoduje żadnych problemów. Zakomentowałem problematyczne funkcje key_update(). Zamiast tego w pętli co sekundę odczytuję≥ wartość portu i wyświetlam ją na wyświetlaczu hd44780. W takiej wersji układ działa prawidłowo - wciskanie poszczególnych przycisków powoduję zmianę wyświetlanej wartości, układ działa stabilnie i się nie resetuje.

Kod funkcji sprawdzającej przycisku znajduje się tutaj:

formatting link

Chwilowo nie mam, ale spróbuję coś wykombinować. ;)

Reply to
Atlantis

Z ciekawości wykonałem nową wersje modułu, w którym układ 74HCT245 jest podłączony dokładnie tak samo, jak w tym drugim, działającym urządzeniu. Przy okazji poprawiłem trochę sposób prowadzenia ścieżki zasilania i pola masy. Okazało się jednak, że to nie tutaj leżała przyczyna.

Zacząłem jeszcze raz analizować sytuację i nagle mnie olśniło - byłem w stanie odczytywać wartość portu i to nie powodowało problemów. Jednak gdy tylko stosowałem swoją bibliotekę (która również czyta z tego portu) urządzenie zaczęło działać niestabilnie i resetować się. Istniała jednak jeszcze jedna różnica - testowa funkcja odczytująca wartość portu była wywoływana raz na sekundę. Natomiast funkcja sprawdzająca wartość przycisku jest wywoływana w każdym przebiegu pętli.

Zrobiłem więc eksperyment - ustawiłem programowy timer, powodujący wywoływanie funkcji key_update() co jakiś 40 ms. Okazuje się, że problem przestał występować. Nie pojawiają się też "fałszywe" wciśnięcia przycisków. Na dobrą sprawę mógłbym to tak zostawić, nawet upraszczając bibliotekę, bo to opóźnienie załatwia sprawę debouncingu.

Tyle tylko, że sprawa nie daje mi spokoju. Dlaczego tak się dzieje? Dlaczego w drugim urządzeniu, opartym na tej samej architekturze, mogę wywoływać funkcję key_update() z pętli głównej i nic się nie dzieje, a w drugim występują takie problemy i trzeba stosować timer?

Mogę od razu obalić teorię, która w pierwszym momencie przychodzi do głowy - to działające urządzenie ma jeszcze mniej "zajętą" pętlę główną, więc nie ma tam niczego, co mogłoby robić za naturalne opóźnienie.

I jeszcze jedno: funkcja key_update jest wywoływana kilkukrotne - za każdym razem przyjmując jako argument adres do struktury odnoszącej się do jednego z przycisków. Przy każdym wywołaniu sprawdzany jest port. Jednak wielokrotne wywołania nie są najwyraźniej problemem same w sobie, bo wspomniany wcześniej timer programowy również wywołuje funkcję trzykrotnie.

Reply to
Atlantis

Chyba trochę się pospieszyłem. Wprowadzona zmiana poprawiła sytuację, ale jej nie naprawiła. Urządzenia pracuje stabilniej. Pierwszy reset wystąpił po jakichś 10 minutach, a nie kilkunastu-kilkudziesięciu sekundach...

Reply to
Atlantis

I jeszcze jeden update. Wygląda na to, że:

1) Przyczyną niestabilnego zachowania układu nie są częste odczyty stanu wejść 74HCT245. Umieściłem w pętli głównej kilka następujących sobie instrukcji przypisania stanu tego portu zmiennej. Instrukcje te są wywoływane przy każdym przebiegu pętli. Do zmiennej trafia zawartość portu. Nic się nie wiesza ani nie resetuje. 2) Przyczyną nie jest raczej zbyt szybki zegar. Gdyby tak było, doświadczałbym ogólnego braku stabilności systemu. Tymczasem do momentu wykorzystania funkcji key_update() wszystko jest w porządku. Nie wygląda więc na to, żeby problem był sprzętowy. 3) Niemniej na innym urządzeniu funkcja key_update() działa poprawnie...
Reply to
Atlantis

Ok, wygląda to tak, jakby faktycznie winę za niestabilne działanie układu ponosiło przepełnienie stosu. Zamiast wywoływać funkcję key_update() z pętli głównej, umieściłem tam odpowiadający jej fragment kodu (cc65 na chwilę obecną jest dość ubogim kompilatorem, wśród jego ograniczeń znajduje się m.in. brak funkcji inline). Wszystko zaczęło działać.

Trochę mnie to dziwi, bo:

- Nie mam w tym kodzie głęboko zagnieżdżonych funkcji. Funkcje key_update() była wywoływana bezpośrednio z pętli głównej, ona sama wywołuje tylko funkcję millis() - wzorowaną na Arduino funkcję zwracającą ilość taktów systemowych generowanych w przerwaniu (ze względu na niskie taktowanie są one zwiększane co 20 ms, a nie 1 ms).

- Sprzętowy stos 6502 jest co prawda niewielki, ale cc65 generuje własny, programowy stos.

- Pamięci jest sporo jak na taki układ (8kB) i jak na razie zbyt mocno jej nie wykorzystuję. Najwięcej zużywają chyba bufory dla UART-a (2x 256 bajtów). Jest też kilka pomniejszych buforów i trochę zmiennych. Aż sprawdziłem, czy przypadkiem łańcuch tekstowe podawane jako argumenty funkcji nie trafiają przypadkiem do RAM-u, ale w generowanym przez kompilator kodzie asemblerowym są oznaczone jako .rodata.

Gdyby kogoś szczególnie interesowała ta zagadka, to wrzuciłem kody źródłowe na githuba,

To jest ten problematyczny:

formatting link
A tutaj działający:
formatting link
key_update w io.c)

Reply to
Atlantis

Użytkownik "Atlantis" napisał w wiadomości grup dyskusyjnych:5f4535af$0$536$ snipped-for-privacy@news.neostrada.pl...

Mowilem - wpisz w stos jakies znane wartosci i sprawdzaj ile ich sie ostalo.

Sprzetowy chyba tez uzywa.

Ale Ty stosu sprzetowego potrzebujesz ... chyba

Ten jest chyba dobry - bo juz nie wołasz key_update().

A mozesz wrzucic assembler do funkcji key_update ? Moze ma tam troche wewnetrznych zmiennych tymczasowych. No i wolasz z niej te key_func, prepare_disp, update_disp jak sie domyslam.

J.

Reply to
J.F.

Na razie wstrzymałem się z tym testem, bo jego przeprowadzenie jest trochę uciążliwe ma tak antycznej platformie, gdy nie ma się dostępu do sprzętowego debuggera i nie można tak po prostu ustawić pułapki i podejrzeć zawartości pamięci. Na chwilę obecną obserwacje zdają się wskazywać właśnie na problem ze stosem...

Tak, oczywiście. Miałem na myśli to, że używa go w ograniczonym zakresie. Bodajże m.in. wartości zwracane przez funkcje są odkładane na tym stosie. I to chyba tylko wtedy, gdy są one za duże, żeby dało się je przekazać przez rejestry.

Wydaje mi się, że sam w części kodu napisanej w asemblerze użyłem instrukcji PHA i PLA tylko kilka razy. Na pewno występuj one w części odpowiadającej za wykonanie przerwania (zrzucenie na stos i przywrócenie kontekstu) oraz w jednej z procedur opóźniających. Widzę, że w kodzie generowanym przez kompilator też czasem się pojawiają, gdy np. chwilowo trzeba zwolnić rejestr albo akumulator.

Tak, to jest aktualna wersja kodu, w której wywaliłem wywołania tej funkcji. Jednak to właśnie ten projekt buł problematyczny. Drugi, pomimo wywołań kay_update() zawsze działał całkowicie poprawnie.

Kompiluje się do następującego kodu asemblerowego:

; --------------------------------------------------------------- ; void __near__ key_update (__near__ struct key *) ; ---------------------------------------------------------------

.segment "CODE"

.proc _key_update: near

.segment "BSS"

L001E: .res 1,$00

.segment "CODE"

jsr pushax lda $6600 jsr pusha0 ldy #$03 jsr ldaxysp sta ptr1 stx ptr1+1 lda (ptr1) jsr tosanda0 jsr bnega sta L001E lda L001E jsr pusha0 ldy #$03 jsr ldaxysp sta ptr1 stx ptr1+1 ldy #$01 lda (ptr1),y jsr tosicmp0 beq L0023 lda L001E beq L0026 jsr pushw0sp jsr _millis ldy #$02 jsr staspidx bra L002F L0026: jsr _millis jsr pushax ldy #$03 jsr ldaxysp sta ptr1 stx ptr1+1 ldy #$02 lda (ptr1),y jsr tossuba0 cmp #$03 bcc L002F jsr ldax0sp ldy #$03 sta ptr1 stx ptr1+1 lda (ptr1),y iny ora (ptr1),y beq L002F jsr ldax0sp ldy #$04 jsr ldaxidx jsr callax L002F: jsr ldax0sp sta ptr1 stx ptr1+1 lda L001E ldy #$01 sta (ptr1),y L0023: jmp incsp2

.endproc

A tak, faktycznie. Te funkcje są wywoływane. Funkcja key_func przez wskaźnik na funkcję zapisany w strukturze, a z niej jeszcze po kolei prepare_disp i update_disp.

Reply to
Atlantis

W C moze byc problem, ale mialem na mysli wpisanie w obszar stosu np

55 gdzies na początku programu, a potem w petli glownej zliczenie ile ich zostalo i wyswietlanie ...

Raczej podejrzewam ten kawalek w C, co on tam wyprawia, jak mu wskaznik na strukture przekazesz.

; --------------------------------------------------------------- ; void __near__ key_update (__near__ struct key *) ; ---------------------------------------------------------------

.segment "CODE" .proc _key_update: near

.segment "BSS" L001E: .res 1,$00

.segment "CODE"

jsr pushax lda $6600 jsr pusha0

; jesli te pushax, pusha0 dotycza programowego stosu CC, to istotnie az tak bardzo "sprzetowego" stosu nie obciaza.

ldy #$03 jsr ldaxysp sta ptr1 stx ptr1+1 lda (ptr1) jsr tosanda0 jsr bnega sta L001E lda L001E jsr pusha0 ldy #$03 jsr ldaxysp sta ptr1 stx ptr1+1 ldy #$01 lda (ptr1),y jsr tosicmp0 beq L0023 lda L001E beq L0026 jsr pushw0sp jsr _millis ldy #$02 jsr staspidx bra L002F L0026: jsr _millis jsr pushax ldy #$03 jsr ldaxysp sta ptr1 stx ptr1+1 ldy #$02 lda (ptr1),y jsr tossuba0 cmp #$03 bcc L002F jsr ldax0sp ldy #$03 sta ptr1 stx ptr1+1 lda (ptr1),y iny ora (ptr1),y beq L002F jsr ldax0sp ldy #$04 jsr ldaxidx jsr callax L002F: jsr ldax0sp sta ptr1 stx ptr1+1 lda L001E ldy #$01 sta (ptr1),y L0023: jmp incsp2

.endproc

J.

Reply to
J.F.

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.