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.

PIC16F628 - problem z wczytywaniem danych z PORTB

tomislaw88 24 Lip 2009 12:26 1868 14
  • #1 24 Lip 2009 12:26
    tomislaw88
    Poziom 9  

    Witam. Mam układ z wymienionym w temacie mikrokontrolerem. Układ ten ma realizować funkcję sterowania diodami podczerwonymi, które będą nadawać kod RC-6 do dekodera Philips DSR 3201/91 - najprościej mówiąc ma to być pilot dla 16 dekoderów. Jest podłączany do komputera przez port równoległy, skąd pobiera dane jaki kod ma wysłać, do którego dekodera i ile razy ten kod ma być wysłany. Mój problem polega na tym, że nie działa mi wczytywanie danych. Port równoległy jest podłączony przez rezystory 100 omów do 8-miu nóżek PORTB. Zastanawiam się czy powinienem dodać coś do układu elektrycznego czy mam po prostu błąd w programie obsługi przerwania lub podczas inicjalizacji mikrokontrolera.
    Z uwagi na to, że PIC reaguje na przerwanie tylko przy zmianie bitów od 4 do 7 na wejściu PORTB, zrobiłem w ten sposób, że 4 najstarsze bity nie mają znaczenia - to po prostu zwiększająca się liczba tak, aby mieć pewność, że zawsze wystąpi przerwanie. Połówka bajtu danych jest przesyłana w najmłodszych 4 bitach. Najpierw starsza połówka przesyłanego bajtu, potem młodsza. Tylko numer diody jest przesyłany na raz, dlatego, że 4 bity wystarczą, aby określić jej numer (max. 16).

    Oto kod początek kodu (dalej była tylko funkcja wysyłająca, która najprawdopodobniej działa - dla niektórych przesyłanych wartości dioda nadaje, dla innych nie chce):

    Code:

       LIST   P=PIC16F628



    #include <p16f628.inc>



       __config _XT_OSC & _LVP_OFF & _WDT_OFF & _BODEN_OFF & _MCLRE_OFF & _PWRTE_ON



    address            equ   0x20   ; pole address pakietu RC-6

    command            equ   0x21   ; pole command pakietu RC-6

    diode            equ   0x22   ; numer diody

    num_repetitions      equ 0x23   ; liczba powtórzeń wysłania

    read_state         equ   0x24   ; stan wczytywania polecenia

    tmp_read_state      equ   0x25   ; tymczasowy stan wczytywania polecenia

    tr_bit            equ   0x26   ; LSB przechowuje stan bitu TR

    wait_reg         equ 0x27   ; ustawione LSB oznacza że już nie czekamy dłużej

    num_wait_loops      equ   0x28   ; liczba pętli oczekiwania ( 1 pętla ~ 286us)

    counter            equ 0x29   ; licznik okresów noœnej

    tmp               equ   0x2A   ; zmienna tymczasowa potrzebna podczas wczytywania danych







       org 0x0000

       goto start



       org 0x0004               ; procedura obsługi przerwania

    int_handler:

       btfss INTCON, RBIF

       goto timer_event

    port_event:

       movf PORTB, 0

       btfss STATUS, Z            ; czy PORTB jest zerem?

       goto next_0               ; nie

       goto reset_int            ; tak

    next_0:

       movf read_state, 0

       movwf tmp_read_state

       decfsz tmp_read_state, 1

       goto next_1

       goto read_address         ; dla read_state = 1

    next_1:

       decfsz tmp_read_state, 1

       goto next_2

       goto read_address_2         ; dla read_state = 2

    next_2:

       decfsz tmp_read_state, 1

       goto next_3

       goto read_command         ; dla read_state = 3

    next_3:

       decfsz tmp_read_state, 1

       goto next_4

       goto read_command_2         ; dla read_state = 4

    next_4:

       decfsz tmp_read_state, 1

       goto next_5

       goto read_diode            ; dla read_state = 5

    next_5:

       decfsz tmp_read_state, 1

       goto read_num_repetitions_2   ; dla read_state = 7 lub wiecej

       goto read_num_repetitions   ; dla read_state = 6

    read_address:

       movf PORTB, 0

       movwf address

       swapf address, 1

       incf read_state, 1

       bcf INTCON, RBIF

       retfie

    read_address_2:

       movf PORTB, 0

       andlw 0x0F

       iorwf address, 1

       incf read_state, 1

       bcf INTCON, RBIF

       retfie

    read_command:

       movf PORTB, 0

       movwf command

       swapf command, 1

       incf read_state, 1

       bcf INTCON, RBIF

       retfie

    read_command_2:

       movf PORTB, 0

       andlw 0x0F

       iorwf command, 1

       incf read_state, 1

       bcf INTCON, RBIF

       retfie

    read_diode:

       movf PORTB, 0

       andlw 0x0F

       movwf diode

       rlf diode, 1

       rlf diode, 1

       movf diode, 0

       andlw 0x3C
       movwf diode
       btfsz diode, 0x5
       bsf diode, 0x0
       bcf diode, 0x5
       movf diode, 0

       iorwf PORTA, 1

       incf read_state, 1

       bcf INTCON, RBIF

       retfie

    read_num_repetitions:

       movf PORTB, 0

       movwf num_repetitions

       swapf num_repetitions, 1

       incf read_state, 1

       bcf INTCON, RBIF

       retfie

    read_num_repetitions_2:

       movf PORTB, 0

       andlw 0x0F

       iorwf num_repetitions, 1

       incf read_state, 1

       bcf INTCON, RBIF

       retfie

    timer_event:

       bsf wait_reg, 0x0

       bcf INTCON, T0IF

       retfie

    reset_int:

       movlw 0x1

       movwf read_state

       bcf INTCON, RBIF

       retfie

       

       



    start:

       bcf STATUS, IRP            ; Bank 0 lub 1
       bcf STATUS, RP1            ;

        bsf STATUS, RP0             ; wybieramy BANK 1

        bcf OPTION_REG, T0CS        ; wybieramy wewnętrzny zegar dla TMR0

        bsf OPTION_REG, PSA         ; podzielnik podłšczony do wathcdoga(timer ma 1:1)

        clrf PIE1                   ; wyłšczamy inne przerwania

        bcf VRCON, VREN             ; wyłaczamy żródło napięcia odniesienia

        bcf VRCON, VROE             ;

        clrf TRISA                  ; PORTA - wyjœcie na wszystkich bitach

        movlw 0xFF                  ; PORTB - wejœcie na wszystkich bitach

        movwf TRISB                 ;

        bcf STATUS, RP0             ; wybieramy BANK 0

        movlw 0x88                  ; konfigurujemy przerwania

        movwf INTCON                ;

        movlw 0x07                  ; wyłšczamy komparatory

        movwf CMCON                 ;
       clrf T1CON               ; wyłączamy timer1
       clrf T2CON               ; wyłączamy timer2
       clrf CCP1CON            ; wyłączamy CCP

        clrf RCSTA               ; wyłaczamy port szeregowy
       clrf TXSTA               ;

        clrf PORTA                  ; czyœcimy rejestry

        clrf PORTB

        clrf PIR1

        clrf address

        clrf command

        clrf diode

       movlw 0x1

       movwf read_state

        clrf num_repetitions

        clrf tr_bit

    loop:

       sleep

       movf read_state, 0

       sublw 0x8

       btfss STATUS, Z            ; czy dane gotowe do wysłania?

       goto loop               ; nie

       bcf INTCON, RBIE         ; tak, wyłšczamy przerwanie I/O

       movlw 0x1               ; czyœcimy read_state

       movwf read_state         ;

    repeat:

       call send               ; wysyłamy pakiet

       call wait               ; czekamy 130ms

       decfsz num_repetitions, 1   ; zmniejszamy licznik i sprawdzamy czy zero?

       goto repeat               ; nie

       bsf INTCON, RBIE         ; tak, włšczamy przerwanie I/O

       incf tr_bit, 1            ; zmieniamy trailer bit na przeciwny

       goto loop               ; powrót do oczekiwania na dane



    wait:

       clrf TMR0               ; czyścimy licznik zegara

       movlw 0x2E               ; 46 przerwań ~ 130 ms

       movwf num_wait_loops      ;

       bsf INTCON, T0IE         ; właczamy przerwanie zegara

    wait_loop:

       btfss wait_reg, 0x0         ; czekamy na ustawienie bitu przez przerwanie

       goto wait_loop            ; bit nieustawiony

       clrf wait_reg            ; bit ustawiony - czyœcimy rejestr oczekiwania

       decfsz num_wait_loops, 1   ; odejmujemy 1, czy zero?

       goto wait_loop            ; nie

       bcf INTCON, T0IE         ; tak, wyłšczamy przerwanie

       bcf INTCON, RBIF         ; czyścimy ewentualnie ustawioną flagę

       return




    Dla niektórych danych wejsciowych uklad dziala, ale nie powtarza nadawania tyle razy ile trzeba, tylko nadaje zawsze albo około 9 sekund albo okolo 0,5 sekundy. Proszę o pomoc. :) I nie bijcie jeśli sprawa jest banalna. :) To mój pierwszy układ na PIC'u. :)

    0 14
  • #2 24 Lip 2009 21:22
    tomislaw88
    Poziom 9  

    Naprawde nikt nie jest w stanie mi pomóc? :( W archiwum w pliku pilot-rc6.asm jest pełny kod źródłowy. W pliku main.cpp jest kod źródłowy programu wysyłającego dane poprzez LPT na linuksa. Dodam jeszcze, że w układzie wyjścia RA0, RA2, RA3 i RA4 to wyścia określające która dioda ma emitować sygnał. Na RA1 jest wyjście sygnału.

    0
    Załączniki:
  • #3 24 Lip 2009 23:28
    adamwesola
    Poziom 24  

    pkt.1 - RA5 jest tylko i wyłącznie wejściem, Ty usiłujesz go ustawiać/zerować
    pkt.2 - W rejestrze OPTION podzielnik ustawiłem na TMR0 (waczdog jest przecież wyłączony) i włączyłem podciąganie portu B.
    pkt.3 - Przerwanie obsługuje tylko zmianę stanu RB4...RB7, odliczaniem 130ms zajmuje się TMR0 i nie robi tego w przerwaniu, bo nie ma takiej potrzeby.
    pkt.4 - Na początku obsługi przerwania, zeruje starsza połówkę portu B i badam stan tylko młodszej, zwróć uwagę czy nie trzeba tego robić w sekcjach: next_0, next_2 i next_5.
    --------------------------

    Cytat:
    Z uwagi na to, że PIC reaguje na przerwanie tylko przy zmianie bitów od 4 do 7 na wejściu PORTB, zrobiłem w ten sposób, że 4 najstarsze bity nie mają znaczenia - to po prostu zwiększająca się liczba tak, aby mieć pewność, że zawsze wystąpi przerwanie.
    Masz świadomość tego ze liczba 0...255 na porcie B, wywoła przerwanie tylko w 16 przypadkach ?
    Poza tym nieco "przebudowałem" program po swojemu, myślę że jest bardziej czytelny i przejrzysty.
    Może coś Ci pomogą moje spostrzeżenia i modyfikacje.
    Dołączam zmieniony program.

    0
  • #4 25 Lip 2009 10:01
    tomislaw88
    Poziom 9  

    Ok, dzięki, dzisiaj wieczorem spróbuję z tą nową wersją kodu. Jeszcze muszę go dokładnie przejrzeć. A co do punktu 2, to podzielnik był ustawiony na watchdoga, żeby timer miał 1:1. Jak ustawisz na TMR0, to będzie 2:1. No ale jeszcze muszę dokładniej sprawdzić wszystko. Dzięki, potem zdam relację co i jak. :)

    0
  • #5 25 Lip 2009 16:14
    adamwesola
    Poziom 24  

    Posiedziałem trochę przy Twoim programie i chyba go już w pełni rozumiem.
    TMR0 służy u Ciebie tylko do odliczania 130ms ? , u mnie tak.
    Ile razy max. wysyłasz pakiet ?
    Sekcja o nazwie state_5 jest dla mnie niejasna: przesunięcia, rola RA5.
    Przesyłam wersje programu , tak jak go zrozumiałem i zgodnie z rozumieniem zmodyfikowałem.

    Zmieniłem załącznik , w nowym usunąłem "reset" zerem i zrobiłem drobne poprawki

    0
  • #6 25 Lip 2009 17:04
    tomislaw88
    Poziom 9  

    No dobra. Przejrzałem Twój kod i mam kilka pytań.

    1. Po co ustawiałeś w rejestrze OPTION bit T0SE skoro T0CS jest wyczyszczony? Wtedy chyba T0SE nie ma znaczenia... Dodam jeszcze, że mam kwarc 3.579545 MHz i zgodnie z zaleceniami dwa kondensatory 22pF podłączone do niego. Taki kwarc miałem akurat. Jest ok czy ma byc 4 MHz?

    2. Dlaczego nie ma w Twoim kodzie konfiguracji układów peryferyjnych? Przecież tam jest tyle funkcji różnych na wyprowadzeniach układu, że trzeba wszystko co zbędne powyłączać, bo inaczej tamte funkcje przesłonią zwykłe działanie typu wejście/wyjście. Chyba, ze o czymś nie wiem.

    3. Co robi instrukcja:

    Code:
    tris PORTA

    ? Nie można było ustawić zer lub odpowiednio jedynek na odpowiednich pozycjach rejestrów TRISA i TRISB?

    4. O co chodzi z tym podciąganiem (pull-up)? Coś czytałem, że ma to na celu uniknięcie stanów nieustalonych na wejściach (albo wyjściach?) układu. Mógłby mi to ktoś wyjaśnić?

    Dodano po 19 [minuty]:

    adamwesola napisał:
    Posiedziałem trochę przy Twoim programie i chyba go już w pełni rozumiem.
    TMR0 służy u Ciebie tylko do odliczania 130ms ? , u mnie tak.
    Ile razy max. wysyłasz pakiet ?
    Sekcja o nazwie state_5 jest dla mnie niejasna: przesunięcia, rola RA5.
    Przesyłam wersje programu , tak jak go zrozumiałem i zgodnie z rozumieniem zmodyfikowałem.


    Tak, TMR0 służy do odliczania 130ms. Pakiet maksymalnie we wstępnych założeniach wysyłam 256 razy ale można w sumie obciąć do 16. Wystarczy pół bajtu. A co do state_5 to chodzi tam o to żeby skopiować bit z 5 pozycji na pozycję 0 żeby potem zrobić po prostu OR z PORTA, a RA5 jest tylko wejściem, dlatego przekopiowuję na RA0.

    0
  • #7 26 Lip 2009 10:16
    adamwesola
    Poziom 24  

    Cześć.
    1. A dlaczego u Ciebie T0SE jest ustawiony ?
    3,58MHz może być, zatem uaktualnij do 130ms procedurę wait, albo włącz oscylator wewn. 4MHz i usuń zewnętrzny, ale sprawdź jaki ma to wpływ na podprogram send

    2.Konfiguracja peryferii ? w Twoim kodzie tez jej niema. Po resecie procka rejestry PIE1,VRCON,T1CON,T2CON,CCP1CON,RCSTA,TXSTA,PIR1 przyjmuja wartosc 0, wiec ładowanie do nich 0 niczego nie zmienia, funkcje realizowane przez te rejestry są powyłączane defaultowo.
    Rejestr OPTION , TRISA, TRISB po resecie przyjmują wartość 255.

    3.Dwie instrukcje : clrw , tris PORTA zeruja rejestr TRISA - czy jest prostszy sposób by cały port A stał się wyjściem ? Rejestru TRISB nie ruszałem bo port B po resecie jest już cały jako wejscie.

    4. POdciaganie , pull-up : tak jest , eliminuje stany nieustalone na wejsciach, wejscie jest poprzez rezystor dołączone do Vcc.

    A teraz najważniejsze : czy to pomyłka czy cecha programu ? - po przyjęciu przerwania program bada czy czy odebrana dana jest zerem, jesli jest zerem to robi swoisty reset, jeśli celowo to zrobileś, ok , do kontroli nad programem, jasne.
    Niestety ma to złą strone, czyni program niezdolnym do odebrania wartości 0, np wysyłasz mu liczbę 16dec czyli 10h, juz widzisz co sie stanie ?

    0
  • #8 27 Lip 2009 12:32
    895614
    Użytkownik usunął konto  
  • #9 27 Lip 2009 13:09
    tomislaw88
    Poziom 9  

    adamwesola napisał:

    A teraz najważniejsze : czy to pomyłka czy cecha programu ? - po przyjęciu przerwania program bada czy czy odebrana dana jest zerem, jesli jest zerem to robi swoisty reset, jeśli celowo to zrobileś, ok , do kontroli nad programem, jasne.
    Niestety ma to złą strone, czyni program niezdolnym do odebrania wartości 0, np wysyłasz mu liczbę 16dec czyli 10h, juz widzisz co sie stanie ?


    Po pierwsze jest to cecha programu. Taki reset moze byc przydatny w pewnych dziwnych sytuacjach. Po drugie zauważ, że W mojej wersji ten błąd nie wystepuje, bo najpierw sprawdzam czy cały rejesrt PORTB jest zerem po skopiowaniu do W. Wiec liczba 10h bedzie wczytana poprawnie. Natomiast w Twojej wersji najpierw zerujesz starszą połówke rejestru W, a potem robisz ANDWF PORTB, W i dopiero potem sprawdzasz czy wynik operacji jest zerem.

    Cytat:
    Witam
    patrząc na Twój listing ten program nie powinien działać
    1.wyzwalasz przerwania bez zachowania rejestru W, oraz Status
    wiec ciągle zmieniany W nie pozwoli Ci w pętli głównej zrobić czegokolwiek.
    Musisz po przyjeciu przerwania zachować rejestr STATUS i W


    Zaraz to zrobie, ale sprawdze dopiero pod wieczor bo teraz jestem w pracy. Dzieki za pomoc. A co do tych 130 ms to nie musi byc koniecznie tyle. Chodzi po prostu o odstep pomiedzy kolejnymi powtorzeniami nadawania pakietu,

    P.S. Sorry za brak niektórych polskich liter, ale to nie mój komp, ;)

    0
  • #10 27 Lip 2009 23:26
    adamwesola
    Poziom 24  

    Czy w ogóle możliwa jest taka kontrola nad programem że 0 będzie "resetem" jak tez i dana ? jak program ma odróżnić co w danym momencie dostał ?
    U mnie każde 0 będzie "resetem", u Ciebie natomiast zawsze daną, a nigdy "resetem". Według mnie trzeba zastosować klasyczny reset - MCLR.

    Kolega zdz_13 poruszył ważną kwestie : wydaje się że Ty i ja założyliśmy że przerwanie (budzenie) będzie przychodziło tylko wtedy gdy wszystko jest zrobione i nie ma potrzeby zachowania STATUS-u ani tez W, a procesor śpi, a wcale tak być nie musi, program/procesor nie wysyła żadnego sygnału gotowosci do przyjecia danych.
    Pytam się : czy w tym momencie (Twój fragment)

    Code:
    loop:
    

       sleep

       movf read_state, 0

       sublw 0x8

       btfss STATUS, Z            ; czy dane gotowe do wysłania?

       goto loop               ; nie
    (mój fragment)
    Code:
    loop:   sleep
    
          btfss   read_state,3   ; czy dane gotowe do wysłania?
             goto    loop               ; nie
    program może zostać przerwany ?
    Wydaje mi się że nie, bowiem w przerwaniu zmieni się read_state oraz rejestry command i address, więc na nic się zda zachowanie STATUS-u i W.
    Lecz czy ta hipitetyczna sytuacja może sie zdarzyć ? czy możliwe jest by w czasie 4us po przerwaniu, na porcie rownoległym PC-ta znów wystapił warunek przerwania ?

    0
  • #11 28 Lip 2009 10:59
    tomislaw88
    Poziom 9  

    Raczej nie jest to mozliwe. bo program wysylajacy dane (kod byl w zalaczniku) wysyla kolejne dane w odstepach milisekundy jesli dobrze pamietam. Dlatego nie zachowywale, tych rejestrów. A jesli chodzi o sam moment nadawania, to zaraz po stwierdzeniu, ze otrzymano wszystkie niezbedne dane przerwania sa wylaczane.
    Nasuwa mi sie jeszcze jedna rzecz. Zmierzylem napiecie na porcie LPT i wyszlo mi, ze w stanie wysokim wynosi ono 3,36 V. Czy jest mozliwe ze to za malo i układ raz łapie i wykonuje procedure obsługi przerwania a innym razem nie?

    0
  • Pomocny post
    #12 28 Lip 2009 19:10
    adamwesola
    Poziom 24  

    3,3V nie powinno być przeszkodą, przerwanie na zmianę stanu RB4...7 jest dość czułe.
    Przeszkoda w działaniu jest tutaj

    Code:
    sleep
    
    movf read_state, 0
    i u mnie tez w tym miejscu
    Code:
    sleep
    
    btfss   read_state,3   ; czy dane gotowe do wysłania?
    zaradzic temu jest bardzo łatwo - koniecznie trzeba wstawić jakąś, koniecznie nieznaczącą instrukcje tuz za sleep, a wiec - nop.
    Dlaczego ? dlatego ze przerwanie najpierw wykonuje następną instrukcje za sleep, a dopiero potem skacze pod adres 4.

    Zgodnie z tym zmieniłem ostatni załącznik z programem

    0
  • #13 29 Lip 2009 10:31
    tomislaw88
    Poziom 9  

    Wielkie dzieki. Zapomnialem o tym w ogole... Dzisiaj w akademiku nie mam pradu wiec nie sprawdze, ale mam nadzieje, ze jutro wszystko zadziala. :)

    0
  • #14 31 Lip 2009 14:38
    tomislaw88
    Poziom 9  

    Sprawdzilem, nie pomoglo... Nie wiem juz co jest nie tak...

    Swoją drogą dlaczego zdz_13 napisał taki kod:

    Code:

          swapf   w_temp, f
          swapf   w_temp, w


    zamiast prostszego:
    Code:

          movf w_temp, w

    ???

    0
  • #15 07 Sie 2009 15:58
    tomislaw88
    Poziom 9  

    Dobra juz sobie poradzilem. Dzieki za wszelka pomoc. :)

    0