[16C450]Obsługa układu UART 16C450

Pozostałe układy mikrokontrolerów, układy peryferyjne i inne, nie mieszczące się w powyższych kategoriach.
Awatar użytkownika
gaweł
Geek
Geek
Posty: 1259
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

[16C450]Obsługa układu UART 16C450

Postautor: gaweł » piątek 05 sty 2018, 17:45

Obsługa układu UART 16C450 w środowisku mikrokontrolera AVR
u16c450_il00.jpg


Współczesne mikrokontrolery mają w swojej strukturze zintegrowane różne podzespoły przeznaczone do transmisji szeregowej. Jednak często możliwości sprzętowe takich podzespołów wypadają dość blado w stosunku do tego, co może popularny (w PC-tach wręcz standardowy) układ kontrolera asynchronicznej transmisji szeregowej o oznaczeniu 16C450. Układ ten jest udoskonaloną wersję wcześniejszego układu oznaczonego jako 82C50 (oznaczenie sugeruje, że pierwotnym producentem tego układu był INTEL, ale nie wiem, czy jest to zgodne z prawdą). Różnica między 82C50 a 16C450 dotyczy trochę innej logiki zgłaszania przerwań oraz układ 16C450 ma dodany jeden rejestr (Scratchpad Register o względnym adresie 7), który jest zwykłym 8-bitowym rejestrem, który nie wpływa na działanie całego kontrolera, można go traktować jako komórkę do ogólnego przeznaczenia. Ta cecha pozwala na programowe wykrycie typu układu: wpis w 16C450 do tego rejestru i późniejszy odczyt tej samej wartości wskazuje na układ 16C450 (w 8250 nie ma tego rejestru). Z czasem pojawiła się kolejna ulepszona wersja tego kontrolera o oznaczeniu 16C550. Ulepszenie dotyczy dodania do kontrolera niewielkiej sprzętowej kolejki FIFO na transmitowane znaki (układ 16C450 nie kolejkuje transmitowanych znaków). Podstawowa obsługa wymienionych układów UART jest zgodna (program poprawnie zadziała w każdym przypadku, najwyżej nie zostanie wykorzystana pełna funkcjonalność samego układu). Pojawiły się inne mutacje kontrolera, przykładowo: 16C452 czy 16C1450.
Wymienione układy:
u16c450_il01.jpg
u16c450_il02.jpg
u16c450_il03.jpg
u16c450_il04.jpg

Układ ten (16C450) zawiera w sobie:
  • programowalny podzielnik częstotliwości taktującej pozwalający na uzyskiwanie wymaganej prędkości transmisji,
  • nadajnik danych pozwalający na obsługę transmisji w przerwaniach,
  • odbiornik danych pozwalający na obsługę transmisji w przerwaniach,
  • obsługę wejściowych 4 linii modemowych pozwalający na uzyskanie sygnału o zmianie jakiejkolwiek linii w przerwaniach,
    obsługę wyjściowych 2 linii modemowych,
  • wykrywanie i generowanie specyficznych stanów na linii transmitowanych danych (sygnał break), odebranie takiego sygnału może być zgłoszone poprzez przerwanie,
  • transmisję znaków o długości 5, 6, 7 i 8 bitów,
  • transmisja znaków z bitem parzystości, bitem nieparzystości lub bez bitu parzystości,
  • transmisja znaków z 1 lub 2 bitami stopu,
  • dwóch pinów wyjściowych (OUT1 i OUT2) do ogólnego zastosowania, które można programowo sterować (odpowiadają określonym bitom w rejestrze sterującym modemem; w komputerach PC [tych starszych], gdzie przerwania są zgłaszane stanem wysokim, by uniknąć konfliktu na slocie, w/w wyjścia sterują łącznikiem trójstanowym do kontrolera przerwań).
Ciekawą cechą jest możliwość programowego „zapętlenia” kontrolera (będzie sam odbierał nadawane znaki).
Układ 16C450 implementuje w sobie pełnomodemowy interfejs występujący w standardzie RS232, czyli obsługje modemy, które określone stany pracy sygnalizują poprzez zmiany linii modemowych.
W środowisku mikrokontrolera AVR, układ UART jest obsługiwany jako element wpięty w przestrzeń zewnętrznej (dla AVR'a) pamięci. Przykładowo mikrokontroler AT90S8515, ATMEGA8515, ATMEGA128 (i wiele innych) mają taką możliwość, by obsługiwać zewnętrzną pamięć RAM.
Koncepcję rozwiązania pokazują następujące rysunki schematów:
u16c450_il05.png
u16c450_il06.png
u16c450_il07.png
W przestrzeni mikrokontrolera (ilustracja 6) zostaje wpięty układ jako 8 komórek pamięci. Ich umiejscowienie jest zdeterminowane rozwiązaniem układu generującego sygnał wyboru (zrealizowanego przez bramki U102A i U102B). Takie rozwiązaniu oznacza, że cały PORTA, PORTC i dwa piny PORTD są przeznaczone do budowy przyłącza do zewnętrznego kontrolera UART. PORTA stanowi dwukierunkową szynę danych oraz stanowi część szyny adresowej. PORTC uzupełnia szynę adresową do 16-bitowej magistrali. Wybrane piny PORTD są sygnałami sterującymi zapisem oraz odczytem odpowiednich komórek pamięci zewnętrznej (w tym przypadku rejestrów samego układu UART 16C450). PORTA współdzielony jest między szyną danych a młodszą połową szyny adresowej. W klasycznym rozwiązaniu sygnałem ALE (pin 33 układu U101) realizowany jest zapis stanu PORTUA do zewnętrznego zatrzasku (typowo układ 74HC573), którego wyjście jest młodszą szyną adresową. W tym konkretnym przypadku można sobie uprościć rozwiązania sprzętowe i zrezygnować z użycia zatrzasku, gdyż na dostępnej wprost starszej części szyny adresowej jest wystarczająca liczba linii adresowych pozwalających na wyselekcjonowanie odpowiednich rejestrów w przyłączanym kontrolerze. Jedyną implikacją takiego rozwiązania jest to, że rejestry kontrolera 16C450 nie stanowią ciągłego bloku w przestrzeni adresowej a są „dość rzadko” rozsiane w całej 16-bitowej przestrzeni adresowej. Wybrane linie adresowe (tutaj A8, A9 i A10) są doprowadzone go układu UART, który sam w sobie stanowi 8-komórkowy obszar w przestrzeni adresowej.
Wystąpienie operacji zapisu w programie do jakiejkolwiek komórki kontrolera oznacza, że na wyjściu WR (pin 18 układu U101) wystąpi stan logicznego zera na czas zapisu. Analogicznie, w operacjach odczytu jakiejkolwiek komórki kontrolera na wyjściu RD (pin 19 układu U101) wystąpi stan logicznego zera na czas odczytu. W sytuacjach pasywnych, to jest, gdy nie jest realizowana operacja zapisu lub odczytu, na wymienionych liniach występuje stan logicznej jedynki. Te sygnały mają swoje odbicie w rozwiązaniu dekodera adresowego (bramki U102A i U102B). Na wyjściu dekodera adresowego wystąpi stan aktywny (stan logicznego zera) w sytuacji, gdy procesor będzie realizował operację odczytu lub zapisu do komórki pamięci zewnętrznej gdy linia adresowa A15 będzie miała stan logicznej jedynki. W przeciwnym wypadku, na wyjściu dekodera adresowego będzie występował stan pasywny (logicznej jedynki).
Sam układ 16C450 wymaga wygenerowania startowego sygnału reset. Jest on przyłączony do wybranego (właściwie dowolnego) pinu portu mikrokontrolera. W tym przypadku został wybrany pin 5 PORTD. W przeciwieństwie do często występujących rozwiązań różnych układów przyłączanych w systemach mikroprocesorowych, stan aktywny sygnału zerującego dla układu 16C450 odpowiada logicznej jedynce (w opisywanym rozwiązaniu ma to jedynie implikacje w oprogramowaniu mikrokontrolera).
Wysłanie lub odebranie danych przez układ UART może być zgłaszane poprzez przerwanie (można również zastosować metodę poolingową i odczytywać odpowiedni rejestr statusowy, by przekonać, się, że kontroler odebrał znak lub jest gotowy do wysłania kolejnego → w tym przypadku nie jest konieczna realizacja sprzętowego zgłaszania przerwania). W opisywanym rozwiązaniu, sygnał przerwania generowany przez UART 16C450 ze stanem aktywnym wysokim, przechodzi przez negator (U102D) i wchodzi do mikrokontrolera na wejście INT0. W przypadku zastosowania mikrokontrolera AVR, gdzie można programowo określić, czy przerwanie jest aktywowane odpowiednim stanem lub po wystąpieniu odpowiedniego zbocza na linii przerwania, wspomniany negator nie jest elementem obowiązkowym. W przypadku zastosowania mikrokontrolera przykładowo z rodziny C51, taki negator jest niezbędny. Przerwanie generowane przez kontroler UART będzie widziane przez mikrokontroler jako zewnętrzne przerwanie INT0.
Aplikacja samego kontrolera pokazana jest na ilustracji 7. Do układu przyłączony jest rezonator kwarcowy X101 (z okolicami). Częstotliwość tego rezonatora stanowi podstawę do generowania sygnału taktującego nadawaniem oraz odbieraniem znaków. Typową częstotliwością jest F=1.8432MHz. Ta częstotliwość pozwala poprzez całkowitoliczbowe podzielniki uzyskać standardowe prędkości transmisji. Należy pamiętać, że sam kontroler wewnętrznie dzieli tą częstotliwość przez 16. Przykładowo dla uzyskania prędkości 9600bps, podzielnik taki wynosi 12 (1843200 : 16 = 115200; 115200 : 12 = 9600). W aplikacji istotnym elementem jest zapętlenie sygnału BAUDOUT (U201, pin 17) na wejście RCLK (U201, pin 10), co pozwala na nadawanie i odbieranie z tą samą prędkością transmisyjną. Sam układ 16C450 ma trzy wejścia typu Chip Select: dwa aktywne stanem wysokim, jedno aktywne stanem niskim. W przedstawianym rozwiązaniu wejścia „jedynkowe” są one „zdeaktywowane” poprzez podanie sygnału logicznej jedynki a do rzeczywistego wyboru układu używane jest wejście ze stanem aktywnym niskim (sygnał generowany przez dekoder adresowy opisany wyżej). Sygnały danych szeregowych oraz linii modemowych są poprzez układ interfejsu RS232 (U202) wyprowadzone na zewnątrz (do złącza P201). Z racji, tego, że kostka MAX232 obsługuje dwa kanały nadawcze i dwa kanały odbiorcze, oprócz danych szeregowych, do aktywnego użycia przewidziano sygnały RTS i CTS. Pozostałe linie modemowe są nieużywane (jako wyjściowe: są nieprzyłączone; jako wejściowe: są wysterowane do stanu aktywnego → podany stan logicznej jedynki). W interfejsie na styku pomiędzy kontrolerem UART a interfejsem RS232 wszystkie sygnały są w logice ujemnej, to znaczy stanem aktywnym jest zero, stanem pasywnym jest jeden.
Środowisko eksperymentu pokazuje ilustracja 8.
u16c450_il08.jpg
Po zaprogramowaniu pamięci mikrokontrolera, program przyłączony do emulatora terminala działa następująco:
u16c450_il09.png


W programie istotnymi elementami są:

Kod: Zaznacz cały

#define DataRegister                   0x8000
#define InterruptEnRegister            0x8100
#define InterruptIdRegister            0x8200
#define LineControlRegister            0x8300
#define ModemControlRegister           0x8400
#define LineStatusRegister             0x8500
#define ModemStatusRegister            0x8600
#define ScratchRegister                0x8700
Powyższe stałe odzwierciedlają działanie dekodera adresowego (linia A15=1, linie A8=x, A9=y, A10=z, pozostałe linie są nieistotne; x y z determinują wybór odpowiedniego rejestru w kontrolerze 16C450).
Procedura obsługi przerwania zewnętrznego INT0:

Kod: Zaznacz cały

ISR ( INT0_vect )
{
  volatile uint8_t * Port ;
  uint8_t IIR ;
  uint8_t UARTStatus ;
  uint8_t Data ;
  /*----------------------------------------------------------------*/
  Port = ( uint8_t * ) InterruptIdRegister ;
  IIR = ( * Port ) & 0x07 ;
  if ( ( IIR & 0x01 ) == 0 )
  {
    IIR = IIR >> 1 ;
    switch ( IIR )
    {
      case 0             :   /* 0 0 - Modem status */
        Port = ( uint8_t * ) ModemStatusRegister ;
        UARTStatus = * Port ;
        break ;
      case 1             :   /* 0 1 - transmitter */
        if ( RS232TXDRec . CycFr == RS232TXDRec . CycTo )
        {
          RS232TXDRec . State = IdleState ;
        } /* if ... */
        else
        {
          Port = ( uint8_t * ) DataRegister ;
          * Port = RS232TXDRec . Buffer [ RS232TXDRec . CycFr ] ;
          RS232TXDRec . CycFr ++ ;
          if ( RS232TXDRec . CycFr >= TXDBuffSize )
            RS232TXDRec . CycFr = 0 ;
        } /* if ... else */ ;
        break ;
      case 2             :   /* 1 0 - reciever */
        Port = ( uint8_t * ) DataRegister ;
        Data = * Port ;
        RS232RXDRec . Buffer [ RS232RXDRec . CycTo ] = Data ;
        RS232RXDRec . CycTo ++ ;
        if ( RS232RXDRec . CycTo >= RXDBuffSize )
          RS232RXDRec . CycTo = 0 ;
        break ;
      case 3             : /* error */
        Port = ( uint8_t * ) LineStatusRegister ;
        UARTStatus = * Port ;
        break ;
    } /* switch */ ;
  } /* if */ ;
} /* INT0_vect */
Odczyt rejestru do identyfikacji przerwania pozwala na przejście do odpowiedniej obsługi. Sam odczyt tego statusu jest potwierdzeniem dla UART (15C450) obsługi przerwania (zgasi stan przerwania w 16C450).
Nadawanie znaków jest zrealizowane z kolejkowaniem ich w odpowiednich strukturach, pierwszy znak idzie bezpośrednio do sprzętu, każdy następny jest buforowany i obsługa przerwań sama je wygarnie do sprzętu. W przypadku odbioru danych, odczytane dane są gromadzone w kolejce, która może zostać wygarnięta przez program.

Kod: Zaznacz cały

static void RS232SendSerial ( uint8_t SerialCh )
{
  volatile uint8_t * Port ;
  uint8_t NextInx ;
  /*----------------------------------------------------------------*/
  cli ( ) ;
  if ( RS232TXDRec . State == IdleState )
  {
    RS232TXDRec . State = BusyState ;
    Port = ( uint8_t * ) DataRegister ;
    * Port = SerialCh ;
    sei ( ) ;
  } /* if ... */
  else
  {
    for ( ; ; )
    {
      NextInx = RS232TXDRec . CycTo + 1 ;
      if ( NextInx >= TXDBuffSize )
        NextInx = 0 ;
      if ( NextInx != RS232TXDRec . CycFr )
        break ;
      sei ( ) ;
      nop ( ) ;
      nop ( ) ;
      nop ( ) ;
      cli ( ) ;
    } /* for */ ;
    RS232TXDRec . Buffer [ RS232TXDRec . CycTo ] = SerialCh ;
    RS232TXDRec . CycTo ++ ;
    if ( RS232TXDRec . CycTo >= TXDBuffSize )
      RS232TXDRec . CycTo = 0 ;
    sei ( ) ;
  } /* if ... else */ ;
} /* RS232SendSerial */
Zainicjowanie kontrolera do pracy:

Kod: Zaznacz cały

static void RS232UARTHardwareInit ( uint8_t RS232SerialSpeed )
{
  volatile uint8_t * Port ;
  uint8_t Data ;
  uint8_t UARTMode ;
  /*----------------------------------------------------------------*/
  UARTMode = 0x03 ;             /* Tryb: N81 */
  Port = ( uint8_t * ) LineControlRegister ;
  * Port = 0x080 ;              /* ustawienie w UART rozszerzenia adresowego do zaprogramowania predkosci */
  Port = ( uint8_t * ) DataRegister ;
  * Port = RS232SerialSpeed ;   /* mlodszy bajt podzielnika predkosci */
  Port = ( uint8_t * ) InterruptEnRegister ;
  * Port = 0 ;                  /* starszy bajt podzielnika predkosci */
  Port = ( uint8_t * ) LineControlRegister ;
  * Port = UARTMode ;           /* zaprogramowania trybu transmisji (N81) */
  Port = ( uint8_t * ) ModemControlRegister ;
  * Port = 0x00B ;              /* zaprogramowania stanu linii modemowych-> wszystkie do stanu aktywnego */
  Port = ( uint8_t * ) LineStatusRegister ;
  Data = * Port ;               /* wygarniecie smieci z roznych rejestrow */
  Port = ( uint8_t * ) ModemControlRegister ;
  Data = * Port ;               /* wygarniecie smieci z roznych rejestrow */
  Port = ( uint8_t * ) DataRegister ;
  Data = * Port ;               /* wygarniecie smieci z roznych rejestrow */
  Data = * Port ;               /* wygarniecie smieci z roznych rejestrow */
  Port = ( uint8_t * ) InterruptIdRegister ;
  Data = * Port ;               /* wygarniecie smieci z roznych rejestrow */
  Port = ( uint8_t * ) InterruptEnRegister ;
  * Port = ( uint8_t ) 0x03 ;   /* zezwolenie dla UART na przerwania od transmisji: nadajnik + odbiornik */
} /* RS232UARTHardwareInit */
Tu należy zwrócić uwagę na pewną dziwność: sposób programowania podzielnika do prędkości. W tym celu do rejestru LineControlRegister należy wpisać coś, co ma ustawiony bit 7 (przykładowo 0x80). Po tym następne dwa zapisy (w dodatku do różnych rejestrów: najpierw do DataRegister [choć to nie są dane do wysłania] i później do InterruptRegister [choć nie mają nic wspólnego z grzebaniem w rejestrze przerwań]) mają zawierać młodszą i starszą część podzielnika.
By móc obsługiwać zewnętrzną pamięć (czyli rejestry kontrolera UART), należy „włączyć” taką możliwość:

Kod: Zaznacz cały

static void HardwareInit ( void )
{
  /*----------------------------------------------------------------*/
  (...)
  MCUCR = ( 1 << SRE ) | ( 1 << SRW ) | ( 1 << ISC01 ) ; // ISC01=1 ISC00=0 -> falling edge
} /* HardwareInit */


Załączniki: program prezentujący działanie:
uart16c450.zip

dokumentacja układu 16C450:
st16c450.pdf
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Wróć do „Inne mikroklocki, również peryferyjne”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 3 gości