LCDT01 - LCD terminál

2014-08-15

 

Az adatterminált ha nem is a kezdetektől, de igen régóta használják a számítástechnikában. Ma is használják a hálózati eszközökkel és mikrovezérlőkkel foglalkozók, ha nem is különálló eszközként, de terminál emulátor program formájában. Régóta vannak grafikus megoldások is, de most kifejezetten karakteres, tehát betük begépelésére és kiírására alkalmas eszközről lesz szó. Kicsit vonatkoztassunk el a tablettől, PC-től, laptoptól és egyéb mai csacskaságoktól. Van a számítógépünk, egy búgó, fekete doboz, úgy ahogy a mérnökök megalkották. Ahhoz, hogy az ember valahogy kapcsolatba kerüljön vele, szükség van egy eszközre, amin keresztül az utasításokat beadhatjuk. Azután szükség van egy másik eszközre, amelyen keresztül a gép az eredményt kiadhatja. Kezdetben ez eléggé kevéssé volt interaktív és papírkímélő folyamat. Azután egyszer csak feltalálták a terminált, ami valójában két részből áll. Áll egy monitorból, amire a számítógép kiirhatja az üzeneteit, és áll egy billentyűzetből, amin keresztül az operátor utasíthatja a gépet. Külalakra a terminál hasonlított egy mai PC-re. A terminálok tipikusan soros vonalon keresztül kapcsolódnak a számítógéphez, ezt RS232, vagy V24 néven illetik. Ennek az összeköttetésnek maximális hossza olyan 30m körül van. Ha egy terminált távol akarunk elhelyezni, mondjuk 1000m-en belül, esetleg ipari környezetben, akkor úgynevezett áramhurkos összeköttetést kell alkalmaznunk, de ennek a témának majd egy másik írást fogok szentelni.

A project-nek egyébként egyszerű indíttatása az volt - mint mindennek az életben - hogy vettem egy kis billentyűzetet a Lomex-ben.

Kiosztása megfelel egy általános telefon billentyűzetének, gumi harangos, vezető tappancsos kialakítású, mint a TV távirányítók. Talán jobb lett volna, ha nem szedem szét, mert azóta sem vagyok biztos benne, mennyit kell inni ahhoz, hogy valaki ilyen panelt tevezzen? Egyszer működött, és soha többé nem változtattak a bevált sémán?

Egyéb jelölés hiányában önkényesen kijelöltem az egyik lábat 1-esnek.

A megépített készülék tesztelés közben.

Nézzük az LCDT01 kapcsolási rajzát. A jobb alsó részen látható a billentyűzet és a bekötése. Fölötte 2 sor x 16 karakteres LCD modul, amit már régebbről ismerünk. Úgy gondolom ezt az elrendezést még több áramkörben fel fogom használni, ezért takarékosságból közös lábak vezérlik az LCD és a billentyűzet modult. Az ATmega8 még vezérel egy csipogót, meg egy LED-et. Bal oldalt fent vannak a soros port lábai. Tényleges készüléknél ezekre csatlakozik a V24, vagy az áramhurkos interface-t. Középen vanak a programozó fejhez menő lábak, és alul az I2C interface-hez szükséges lábak. Ez jelenleg még nincs lekezelve. Az ATmega8-nak vannak még szabad lába, amikre igény szerint további dolgok köthetők. A hangszóró hangereje a vele sorba kapcsolt ellenálással kis mértékben szabályozható. Nagyobb hangszóró esetén, egészítsük ki a kapcsolást egy tranzisztoros erősítővel. A billentyűzetet le kellett 1k2 ellenállásokkal választanom a uC lábairól, mert nem lehet kizárni, hogy lesz olyan felhasználó, aki képes egyszerre több gombra is rátenyerelni. Ki tudja mi lenne a rövidre zárt lábakkal?

Más áramköröknél korábban már a program kód több része is ismertetésre került. A program alapvetően megszakítás vezérelt. Az ISR( USART_RXC_vect) figyeli a soros portra érkező karaktereket, és átadja a cmd() parancsfeldolgozó függvénynek. A T0 regiszter hozzávetőleg 100Hz-es megszakításra van beállítva. Az ISR( TIMER0_OVF_vect) másodpercenként 100x lekérdezi a billentyűzetet (ezt alaposabban is meg fogjuk nézni), ha van lenyomott gomb, elküldi a soros vonalon. Maga a program fő hurka, nem csinál mást, mint figyeli a speaker változót, és ki/be kapcsolgatja a hangszórót, míg tartalma nem lesz nulla. Szerintem elég áttekinthető lett a kód, de meg kell nézegetni hozzá a header file-jaimat is, mert sok saját makró van benne.

Előszőr bonyoultabb programon gondolkodtam. A megszakítás kezelő rutinok csak jelzőket állítgattak volna, a fő hurokban ezeket figyelte volna a program, és így lettek volna az események lekezelve. Azután utána számolgattam, hogy a leggyorsab ember is csak legfeljebb 10 gombot tud leütni másodpercenként, és 9600 baud mellett egy betü elküldése olyan 1msec-t vesz igénybe. Bejövő oldalon ez legfeljebb 1000 karaktert jelent másodpercenként, miközben a kijelzőre legfeljebb 32 írható ki egyszerre. Egy betü kiírásához nem kell 100usec-sem. Egyedül a képenyő törlés végrehajtásához kell 2msec, amire esetleg oda kell figyelni. A csipogás megoldása sem tökéletes, a hangba belemodulál a megszakítás-kezelés. Viszont így egyszerű a programkód.

A billentyűzet lekérdezése két függvényen keresztül történik. A kbd() lekérdezi a billentyű mátrixot, és rögtön a billentyűhöz tartozó ASCII kódot adja vissza. Ha nincs lenyomott gomb, 0-t ad vissza. Érdekes módon, csak azt a sort kapcsolom be kimenetként, amelyiket 0-ra akarok húzni, a többi sorhoz tartozó kimenet le van tiltva közben. Úgy tapasztaltam, mivel ellenállásokon keresztül vannak a gombokhoz csatlakozó vezetékek le/felhúzva, a vezetékek kapacitásának kisütéséhez/feltöltéséhez kell egy kis idő, ezért lettek a kódba a NOP-ok betéve. Több gomb lenyomása esetén a kbd() annak a kódját adja vissza, amelyiket sorrendben később kérdez le.

A másik függvény a kbd_antiprell(). Ez a gombok prellegését van hivatva kiküszöbölni. Úgy működik, hogy figyeli, hány lekérdezési periódus óta van lenyomva folyamatosan a gomb. A harmadiknál elküldi a karaktert, de továbbiakbansemmit nem küld, amíg nem kap 0-t, vagyis nem lesz olyan állapot, hogy egyik gomb sincs lenyomva. Ekkor újraindul a működése.

Most e sorok írása közben rájöttem, hogy az 1k2 ellenállások helyett jobb lett volna diódákat tennem, a program is egyszerűbb lett volna. Most már, majd csak legközelebb.

/*******************************************************************************
*   Author       -  Kiraly Tibor
*                   http://www.tkiraaly.hu
*   Date         -  2014.08.12.
*   Chip         -  Atmel ATmega8 & HD44780
*   Compiler     -  avr-gcc (WinAVR)
*
*   LCDT01 - LCD terminal
*   ATmega8, 2x16 karakteres LCD, 3x4 gombos tasztatura, LED, csipogo 
*   
*   Ez egy hagyomanyos soros vonali terminal. A soros vonalon erkezo betuket
*   kiirja a kepernyore, a lenyomott billentyuk kodjat pedig elkuldi a soros
*   vonalon. Kepes a magyar kis betuket is kiirni (WIN kodolas szerint).
*   A soros vonal parameterei: 9800 Baud, 8 bit, No parity, 1 stop bit.   
*   Ertelmezett parancsok:
*   ESC, T    - kepernyo torlese
*   ESC, S    - csipogas
*   ESC, L    - LED bekapcsolasa
*   ESC, K    - LED kikapcsolasa
*   ESC, X    - felso sor kijelolese
*   ESC, Y    - also sor kijelolese
*   ESC, 0..F - kurzor mozgatasa a kijelolt sor adott poziciojaba 
*   
********************************************************************************
*   PonyProg Configuration and Security Bits (bepipalva):
*
*   CKSEL3, CKSEL2, CKSEL1, CKSEL0
*   P       P       -       -       0011  Calibrated Internal RC Oscillator 4MHz
*   P       -       P       P       0100  Calibrated Internal RC Oscillator 8MHz
*
*   Calibrated Internal RC Oscillator
*   SUT1  SUT0
*
*   -     P         Slowly rising power
*   
********************************************************************************
*    LCD:
*    14   LCD D7       - AVR PD7 - 13
*    13   LCD D6       - AVR PD6 - 12
*    12   LCD D5       - AVR PD5 - 11
*    11   LCD D4       - AVR PD4 -  6
*    10   LCD D3
*     9   LCD D2
*     8   LCD D1
*     7   LCD D0
*     6   LCD E        - AVR PD3 - 5
*     5   LCD RW       - GND
*     4   LCD RS       - AVR PD2 - 4
*     3   LCD KONTR    - 10 kOhm trim.
*     2   LCD VDD      - +5V
*     1   LCD VSS      - GND
*     
*    KBD:
*     1 KBD COL1       - AVR PC0 - 23
*     2 KBD COL2       - AVR PC1 - 24
*     3 KBD COL3       - AVR PC2 - 25
*     4 KBD ROW1       - AVR PD7 - 13 - 4K7      
*     5 KBD ROW2       - AVR PD6 - 12 - 4K7      
*     6 KBD ROW3       - AVR PD5 - 11 - 4K7      
*     7 KBD ROW4       - AVR PD4 - 10 - 4K7      
*
*    Soros port:
*     1 VCC
*     2 X
*     3 RX             - AVR RXD  - 2
*     4 TX             - ACR TXD  - 3
*     5 GND
*     
*    Programozo fej:
*     1 RST            - AVR NRES - 1
*     2 X
*     3 SCK            - AVR SCK  - 19
*     4 MISO           - AVR MISO - 18
*     5 MOSI           - AVR MOSI - 17
*     6 GND
*
*    Egyeb:
*     LED              - AVR PB0 - 14
*     Csipogo          - AVR PC3 - 26
*     I2C CLOCK        - AVR SCL - 28
*     I2C DATA         - AVR SDA - 27
*                      - AVR PB1 - 15
*                      - AVR PB2 - 16
*                      - AVR PB6 - 9
*                      - AVR PB7 - 10
*
*******************************************************************************/

#define F_CPU                _4MHZ
#define T0_DIVIDER           1024                // 0,256 msec




#include "tkiraaly_atmega8.h"
#include <util/delay.h>
#include <avr/pgmspace.h>
#include "tkiraaly_lcddef.h"





// LED - PB0
#define LED_ENABLE           PB0_OUT
#define LED_BE               BC( PORTB, 0)
#define LED_KI               BS( PORTB, 0)

// LCD_E - PD3
#define LCD_E_ENABLE         PD3_OUT
#define LCD_E_0              BC( PORTD, 3)
#define LCD_E_1              BS( PORTD, 3)

// LCD_RS - PD2
#define LCD_RS_ENABLE        PD2_OUT
#define LCD_RS_UTASITAS      BC( PORTD, 2)
#define LCD_RS_ADAT          BS( PORTD, 2)

// LCD adat port - PORTD felso 4 bit - KBD sorok is
#define LCD_PORT             PORTD
#define LCD_PORT_ENABLE      DDRD|= 0B11110000
#define LCD_PORT_DISABLE     DDRD&= 0B00001111


// SPEAKER
#define SPK_ENABLE           PC3_OUT
#define SPK_BE               BC( PORTC, 3)
#define SPK_KI               BS( PORTC, 3)




void lcd_init4( void);                           // LCD inicializalasa 4 bitre
void lcd_putc( U8);                              // betu kiirasa
void lcd_putcmd( U8);                            // parancskod kiadasa
void lcd_yx( U8, U8);                            // kurzor pozicioja 0..3/0..15
void lcd_cls( void);                             // kepernyo torles
void lcd_puts( const char *);                    // string kiirasa
U8 kbd( void);                                   // billentyuzet lekerdezese es dekodolasa
U8 kbd_antiprell( U8);                           // billentyuzet perges mentesito
void usart_putc( U8);                            // USART egy karakter kuldese
void lcd_putc_hu( U8);                           // betu kiirasa, magyar Win konverzio
void lcd_defc( const uint8_t *);                 // egyedi betu(k) beallitasa  
void cmd( U8);                                   // bejovo char ertelmezese
void beep( void);                                // 1000 HZ-es csippantas




const uint8_t magyar_betuk[] PROGMEM =
{
   0,
                                                 // á - 0
   Cx______X_,
   Cx_____X__,
   Cx____XXX_,
   Cx_______X,
   Cx____XXXX,
   Cx___X___X,
   Cx____XXXX,
   Cx________,
                                                 // é - 1
   Cx______X_,
   Cx_____X__,
   Cx____XXX_,
   Cx___X___X,
   Cx___XXXXX,
   Cx___X____,
   Cx____XXX_,
                                                 // í- 2
   Cx________,
   Cx______X_,
   Cx_____X__,
   Cx________,
   Cx____XX__,
   Cx_____X__,
   Cx_____X__,
   Cx____XXX_,
   Cx________,
                                                 // ó - 3
   Cx______X_,
   Cx_____X__,
   Cx____XXX_,
   Cx___X___X,
   Cx___X___X,
   Cx___X___X,
   Cx____XXX_,
   Cx________,
                                                 // ő - 4
   Cx_____X_X,
   Cx____X_X_,
   Cx____XXX_,
   Cx___X___X,
   Cx___X___X,
   Cx___X___X,
   Cx____XXX_,
   Cx________,
                                                 // ú - 5
   Cx______X_,
   Cx_____X__,
   Cx________,
   Cx___X___X,
   Cx___X___X,
   Cx___X__XX,
   Cx____XX_X,
   Cx________,
                                                 // Ű - 6
   Cx_____X_X,
   Cx____X_X_,
   Cx________,
   Cx___X___X,
   Cx___X___X,
   Cx___X__XX,
   Cx____XX_X,
   Cx________,
   
   0xFF
};




volatile U8 speaker= 0;                           // csipogas hossza msec-ben



 
ISR( USART_RXC_vect)                             // USART megszakitas kezelese
{
   cmd( UDR);                                    // bejovo char feldolgozasa
}




ISR( TIMER0_OVF_vect)                            // masodpercenkent kb 100*
{
   U8 c;
   T0_T(10000);                                  // 10000usec - 100Hz
   c= kbd_antiprell( kbd());                     // billentyuzet lekerdezese
   if( c)                                        // ha nyomtak egy gombot
   {
      usart_putc( c);                            // char kuldese
      speaker= 30;                               // billentyu hang
   }
}




int main( void)
{
   SPK_ENABLE;
   LED_ENABLE;
   LED_BE;
   USART_SET_9600_8N1;
   T0_F(100);
   T0_NORMAL;
   T0_IT_OVF_ENABLE;
   IT_ENABLE;
   lcd_init4();
   lcd_cls();
   lcd_defc( magyar_betuk);
   lcd_yx( 0, 0);
   lcd_puts( PSTR( "tkiraaly.hu 2014"));
   lcd_yx( 1, 2);
   lcd_puts( PSTR( "LCD Terminal"));
   _delay_ms( 1000);
   lcd_cls();
   for(;;)
   {
      if( speaker)
      {
         SPK_BE;
         _delay_us( 500);
         SPK_KI;
         _delay_us( 500);
         speaker--;
      }
   }
   return 0;
}




void lcd_init4( void)                            // LCD inicializalasa 4 bitre
{
   LCD_PORT_ENABLE;
   LCD_E_ENABLE;
   LCD_RS_ENABLE;
   _delay_ms( 15);
   LCD_RS_UTASITAS;
   LCD_PORT= 0B00100000;                         // 4 bit interface
   LCD_E_1;
   LCD_E_0;
   _delay_ms( 5);
   LCD_E_1;
   LCD_E_0;
   _delay_us( 120);
   lcd_putc( 0B00101000);                        // 4 bit interface, 2 sor, 5x8 pontos betu
   lcd_putc( 0B00101000);                        // 2x kell kiadni, vagy LCD D3-t VDD-re kell kotni
   lcd_putc( 0B00001100);                        // kijelzes be, cursor ki
   lcd_putc( 0B00000110);                        // kiiras jobbra
   LCD_RS_ADAT;
}




void lcd_putcmd( U8 cmd)                         // parancskod kiadasa
{
   LCD_RS_UTASITAS;
   lcd_putc( cmd);
   LCD_RS_ADAT;
}




void lcd_putc( U8 c)                             // egy betu kiiras
{
   LCD_E_1;                                      // felso 4 bit
   LCD_PORT= ( LCD_PORT & 0x0F) | ( 0xF0 & c);
   LCD_E_0;
   LCD_E_1;                                      // also 4 bit
   LCD_PORT= ( LCD_PORT & 0x0F) | ( 0xF0 & c << 4);
   LCD_E_0;
   _delay_us( 37);                               // var 37 usec  
}




void lcd_yx( U8 sor, U8 betu)                    //  kurzor pozicionalasa 0..3/0..15
{
   U8 cim= 0x80;                                 // parancs kodja
   if (sor & 0B00000001) cim+= 64;               // 1. es 3. sor
   if (sor & 0B00000010) cim+= 20;               // 2. es 3. sor
   cim+= betu & 0x0F; 
   lcd_putcmd( cim);
}




void lcd_cls( void)                              // kepernyo torles
{
   lcd_putcmd( 0x01);
   _delay_ms( 2);
}




void lcd_puts( const char *s)                    // string kiiras
{
   register unsigned char c;
   while ( ( c= pgm_read_byte( s++))) lcd_putc( c);
}




U8 kbd( void)                                    // billentyuzet lekerdezese es dekodolasa
{
   U8 c;
   c= 0;
   LCD_PORT_DISABLE;
   LCD_PORT &= 0x0F;                             // LCD es KBD port reszben kozos
   PD7_OUT;                                      // 1. sor
   NOP;                                          // 4MHz-nel 1 NOP-pal mar jo
   if( BTC( PINC, 0)) c= '1';
   if( BTC( PINC, 1)) c= '2';
   if( BTC( PINC, 2)) c= '3';
   LCD_PORT_DISABLE;
   PD6_OUT;                                      // 2. sor
   NOP;
   if( BTC( PINC, 0)) c= '4';
   if( BTC( PINC, 1)) c= '5';
   if( BTC( PINC, 2)) c= '6';
   LCD_PORT_DISABLE;
   PD5_OUT;                                      // 3. sor
   NOP;
   if( BTC( PINC, 0)) c= '7';
   if( BTC( PINC, 1)) c= '8';
   if( BTC( PINC, 2)) c= '9';
   LCD_PORT_DISABLE;
   PD4_OUT;                                      // 4. sor
   NOP;
   if( BTC( PINC, 0)) c= '*';
   if( BTC( PINC, 1)) c= '0';
   if( BTC( PINC, 2)) c= '#';
   LCD_PORT_ENABLE;
   return c;
}




U8 kbd_antiprell( U8 c)                          // billentyuzet perges mentesito
{
   static U8 n= 0;                               // nyomvatartas szamlalasahoz 
   U8 a= 0;                                      // visszadott ertek, default 0
   if( c) n++;                                   // ha van lenyomott gomb, szamlalas
   else n= 0;
   if( n == 10) n= 9;                            // szamlalo max 9
   if( n == 3) a= c;                             // harmadik periodusnal megy a gomb
   return a;
}




void usart_putc( U8 c)                           // USART egy karakter kuldese
{
    while( BTC( UCSRA, UDRE));                   // 1, ha kesz az adat fogadasara
    UDR= c;
}




void lcd_putc_hu( U8 c)                          // magyar betu kiirasa LCD-re
{
   switch( c)
   {
      case 0xD6:                                 // Ö
      case 0xF6:                                 // ö
                  c= 0xEF;
                  goto kiir;
      case 0xDC:                                 // Ü
      case 0xFC:                                 // ü
                  c= 0xF5;
                  goto kiir;
      case 0xC1:                                 // Á
      case 0xE1:                                 // á
                  c= 0;
                  goto kiir;
      case 0xC9:                                 // É
      case 0xE9:                                 // é
                  c= 1;
                  goto kiir;
      case 0xCD:                                 // Í
      case 0xED:                                 // í
                  c= 2;
                  goto kiir;
      case 0xD3:                                 // Ó
      case 0xF3:                                 // ó
                  c= 3;
                  goto kiir;
      case 0xD5:                                 // Ő
      case 0xF5:                                 // ő
                  c= 4;
                  goto kiir;
      case 0xDA:                                 // Ú
      case 0xFA:                                 // ú
                  c= 5;
                  goto kiir;
      case 0xDB:                                 // Ű
      case 0xFB:                                 // ű
                  c= 6;
   }
   kiir:
   lcd_putc( c);
}




void cmd( U8 c)                                  // bejovo char ertelmezese
{
   static U8 p= 0;                               // elozo bejovo char
   static U8 sor= 0;
   if( p == 27)                                  // ha az elozo ESC volt
   {
      switch( c)
      {
         case '0':
         case '1':
         case '2':
         case '3':
         case '4':
         case '5':
         case '6':
         case '7':
         case '8':
         case '9':
                   c-= 48;
                   lcd_yx( sor, c);
                   goto vege;
         case 'A':
         case 'B':
         case 'C':
         case 'D':
         case 'E':
         case 'F':
                   c-= 55;
                   lcd_yx( sor, c);
                   goto vege;
         case 'a':
         case 'b':
         case 'c':
         case 'd':
         case 'e':
         case 'f':
                   c-= 87;
                   lcd_yx( sor, c);
                   goto vege;
         case 'X':
         case 'x':
                   sor= 0;
                   goto vege;
         case 'Y':
         case 'y':
                   sor= 1;
                   goto vege;
         case 'T':                               // kepernyo torles
         case 't':
                   lcd_cls();
                   goto vege;
         case 'L':
         case 'l':
                   LED_BE;
                   goto vege;
         case 'K':
         case 'k':
                   LED_KI;
                   goto vege;
         case 'S':                               // csipogas
         case 's':
                   speaker= 100;
                   goto vege;
      }
   }
   if( c != 27) lcd_putc_hu( c);
   vege:
   p= c;
}    




void lcd_defc( const uint8_t *s)                 // egyedi betu(k) beallitasa
{
   register unsigned char c;
   LCD_RS_UTASITAS;
   lcd_putc( ( ( pgm_read_byte( s++) & 0B00000111) << 3) | 0B01000000);   // betu cime + parancs 
   LCD_RS_ADAT;
   while ( ( c= pgm_read_byte( s++)) != 0xFF) lcd_putc( c);
   LCD_RS_UTASITAS;
   LCD_RS_ADAT;
}

Itt a vége, fuss el véle, legytek az én vendégeim, innen letölthetitek a hozzávalókat összecsomagolva.