termios etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
termios etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

3 Ekim 2017 Salı

termios yapısı

termios yapısı
POSIX seri port API'sinde (örneğin RS232) herşey termios yapısı etrafında döner. Yapı şöyle
struct termios {
  tcflag_t    c_iflag;    /* input flags */
  tcflag_t    c_oflag;    /* output flags */
  tcflag_t    c_cflag;    /* control flags */
  tcflag_t    c_lflag;    /* local flags */
  cc_t        c_cc[NCCS]; /* control chars */
  speed_t     c_ispeed;   /* input speed */
  speed_t     c_ospeed;   /* output speed */
};
Linux'ta Posix standardından daha fazla bir çok bayrak var.

canonical kullanım
Şöyle yaparız.
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS8;         /* 8-bit characters */
newtio.c_cflag &= ~PARENB;     /* no parity bit */
newtio.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
newtio.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

newtio.c_lflag |= ICANON | ISIG;  /* canonical input */
newtio.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);

newtio.c_iflag &= ~INPCK;
newtio.c_iflag |= ICRNL;
newtio.c_iflag &= ~(INLCR | IGNCR | IUCLC | IMAXBEL);
newtio.c_iflag &= ~(IXON | IXOFF | IXANY);   /* no SW flowcontrol */

newtio.c_oflag &= ~OPOST;

c_cflag Alanı
Kontrol bayrakları içindir. Posix şu bayrakları tanımlar.

CSIZE Bayrağı: Bir karakter büyüklüğünü tanımlar. 5,6,7,8 bit olabilir. 5 ve 6 bit veri büyüklükleri çok eskiden kullanılırdı.
CSTOPB Bayrağı: İki veya bir stop biti kullanır. Açıklaması şöyle. Tüm veri alındıktan sonra start bit'in tekrar gözlemlenmesi için ne kadar süre geçmesi gerektiğini belirtir.
With this kind of communication, both the sender and receiver have to agree ahead of time on a few parameters. The two key parameters here are the baud rate and the number of data bits.

The line starts out in the idle level. The transmitter first sends the start bit, which is at the opposite level. It then sends the data bits, followed by the stop bit.

Unlike what others have said, the stop bit is not a checksum. It is required so that there is always a transition at the leading edge of the start bit. Otherwise, if the last data bit ov the previous character happened to be of the same polarity as a start bit, the receiver wouldn't be able to see the start bit if a new character was sent immediately. The stop bit is essentially enforced line idle time between characters. That time is also used to absorb some clock mismatch between the sender and receiver.

When idle, the receiver monitors the line looking for it to change to the non-idle state. When that happens, it starts a stopwatch. Since it is configured for the same bit rate as the transmitter, it knows when each data bit is sent, whether it differs from a previous bit or not. The middle of the first data bit is at 1½ bit times from the leading edge of the start bit. The second at 2½ bit times, etc.

After the last data bit is received, the receiver waits for the line to go back to the idle state, then waits for the next start bit again.
Start Bit Gelince
1.5 cycle beklenir ve sonra okuma işlemi başlar. Açıklaması şöyle
The start bit is used to trigger the read cycle in the receiver. The receiver synchronises itself on the start bit and then waits 1.5 cycles to start sampling bits. Thereafter the bits are sampled at the baud rate. This initial delay means that even with a 5% clock error the receiver should still be within the bit timing for the last bit.

CREAD : Açıklama yaz
PARENB : Parity kontrolünü etkinleştirir.
PARODD : Parity etkinse odd veya even olabilir.
HUPCL : Açıklama yaz
CLOCAL : Açıklama yaz

8N1 8 bit data, No Parity, 1 Stop bit anlamına gelir.

c_oflag alanları
Raw Output
Açıklaması şöyle. Text Editor tarzı işlerde kullanılır
Up next is understanding how to set your terminal into "raw mode." By default, your terminal processes text line-by-line, echoing every character that we type and much more. We don't want all of that. However, to enter raw mode, we'll need to learn about native Unix APIs and how to access them with Java.

One such API is the Termios API. It effectively lets you set your terminal into raw mode; hence, I'll show you how to use JNA to access the Termios API — and do a fair amount of bit-wise ANDs to get your terminal into the right mode.

With ANSI escape codes and terminal raw mode knowledge under our belts, we can finally create our terminal editor skeleton. When we start the editor, we want to see a clear screen. In addition, all empty rows should start with a "~" sign, and we should also have a nice little status bar at the bottom of the screen — time for implementation!
Şöyle yaparız
port_settings.c_oflag &= ~OPOST; //enable raw output 
cfmakeraw yazısına da bakılabilir.

19 Nisan 2017 Çarşamba

Seri Port API'si

Not : Konuyl ilgili olarak TTY ve Devpts başlıklı yazıya göz atabilirsiniz.

Giriş
Seri portlar konusu programlama diline bağlı olarak çok kolay veya karışık bir hal alabiliyor.

UART
UART yazısına taşıdım.

Posix

Seri portları kullanmak özellikle POSIX C API'si ile karmaşık bir iş. Kafa karıştıracak bir sürü bayrak ve metod var. En basit haliyle şu işleri yapmak gerekiyor.

1. Seri portu açmak için şöyle yaparız.
int fd = open( "/dev/ttyUSB0", O_RDWR | O_NONBLOCK | O_NDELAY | O_NCTTY); 
Şöyle yaparız.
int fd = open( "/dev/ttyUSB0", O_RDWR | O_NDELAY | O_NCTTY); 
2. Seri port parametrelerin okumak için şöyle yaparız.
struct termios options {};
tcgetattr(fd,&termAttr);
3. Giriş çıkış hızını ata
/* Set Baud Rate */
cfsetospeed (&options, (speed_t)B9600);
cfsetispeed (&options, (speed_t)B9600);

4. Diğer parametreleri ata
/* cflag (control flag) settings. Make 8n1 */
tty.c_cflag     &=  ~PARENB;  //Parity disabled
tty.c_cflag     &=  ~CSTOPB;  //1 stop bit
tty.c_cflag     &=  ~CSIZE;   //Bitmask for size setting
tty.c_cflag     |=  CS8;      //8 bit data
Flow Control için
tty.c_cflag     &=  ~CRTSCTS;           // no flow control

//c_lflag Settings
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

//c_iflag Settings
options.c_iflag &= ~(IXON | IXOFF | IXANY); //Software flow control disabled

//c_oflag Settings
options.c_oflag &= ~OPOST; //Raw output

5. Parametreleri ata
tcsetattr(fd, TCSAFLUSH, &term_settings)
6. Okuma İşlemini Yap
Önce sinyalin iki kere gelmesini engellmek için sinyali maskeleriz. Şöyle yaparız.
sigemptyset(&saio.sa_mask);
sigaddset(&saio.sa_mask, SIGIO);
Sonra signal handler atamak için şöyle yaparız.
saio.sa_handler = signal_handler_IO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);
handlerın imzası şöyledir.
void signal_handler_IO (int status)
{
  ...
}
Okumak için şöyle yaparız.
while(true)
{ 
  size_t bytes_avail; 
  ioctl(fd, FIONREAD, &bytes_avail); 
  if(bytes_avail == 0) 
    break; 
  int n = read (fd, buf, (sizeof buf)-1); 
  if(n<0) 
  { 
    //error handling
    fprintf(stderr, "Error while receiving message: %s\n", strerror(errno)); 
    return; 
  } 
  buf[n] = '\0'; 
  // you can add a check here to see if (length+n) < 1024 
  strcat(msg, buf); 
  length += n; 
} 

Şimdi bu kafa karıştırıcı parametrelere bakalım.

Metodların Gruplanması
Seri Port API'sine ait metodlar termios yapısını yöneten metodlar ve line control functions başlığı altında iki gruba toplanabilir.  Aşağıdaki şekli şekli buradan aldım.

termios yapısını kullanan metodlar en çok işe yarayanlar. Line Control Functions ile Line Discipline değiştirebilme imkanı vardır.




termios metodları

Termios Yapısı
termios yapısı yazısına taşıdım.

Seri Portlar'u Açarken Controlling Terminal Olmak

Controlling Terminal SIGINT sinyali ile bir uygulamayı öldürebilme yeteneğine sahiptir. Seri Port kullanılırken seri port'tan uygulamamızın öldürülmesini çoğunlukla istemeyiz. Bu yüzden seri port TTY'sini "controlling terminal" haline getirmemek gerekir. Peki seri port TTY'si nasıl "controlling terminal" olabiliyor ?

Aslında bu konuyu ben de doğru dürüst anladım diyemem. POSIX standardında 7.1.1.3 bendinde şöyle bir cümle var.

7.1.1.3 The Controlling Terminal

If a session leader without a controlling terminal opens a terminal device file not already associated with a session without specifying the O_NOCTTY option, then this terminal becomes the controlling terminal for the session leader. This is how a controlling terminal is acquired.
Yani seri port açılırken eğer uygulamanın controlling terminali yoksa durup dururken seri port tty'sini controlling terminalimiz yapmayalım diye O_NOCTTY seçeneğini kullanabilirsiniz diyor.

 Aşağıdaki kod parçasını bunu gösteriyor.

O_NOCTTY : Kullanılırsa seri port TTY uygulamanın "controlling terminal"'i haline gelmez.
O_NDELAY : Kullanılırsa seri port non-blocking olarak açılır.
O_NONBLOCK: O_NDELAY ile aynı işe yarar. Bu sabit POSIX standardında tanımlıdır.

ps -j komutu ile uygulamanın "controlling terminal"i olup olmadığını görmek mümkün.

Why CTRL+C won't work when using RS-232 on Linux? başlıklı soru da ilginç.


termios yapısı c_flag alanları

c_flag alanı ile receiver'ı etkinleştirmek
Tam olarak ne olduğunu anlamadım ancak buradaki açıklamaya göre CLOCAL ve CREAD aşağıdaki örnekteki gibi her zaman lazım.

CLOCAL  : local connection, no modem contol
CREAD   : enable receiving characters

c_cflag alanı ile stop bit atamak
c_cflag alanı ile donanıma aşağıdaki gibi stop bit atamak mümkün.
c_cflag alanı ile karakter büyüklüğü atamak

options.c_cflags &= ~CSIZE; // Mask the character size bit
options.c_cflags |= CS8;    // Select 8 data bits
c_cflag alanı ile parity atamak
PARENB bayrağı ile parity kullanıp kullanmayacağımızı belirtiriz. Eğer parity kullanılacaksa PARODD bayrağı atanmışsa odd parity kullanılır, atanmamışsa even parity kullanılır.
//No parity (8N1):
options.c_cflags &= ~PARENB //No parity
options.c_cflags &= ~CSTOPB //1 stop bit
options.c_cflags &= ~CSIZE; // Mask the character size bit
options.c_cflags |= CS8; //8 data bits

c_flag alanı ile hardware flow control yapmak
Bu seçenek sadece donanım gerekli kabloları ihtiva ediyorsa kullanılabilir.
options.c_cflag &= ~CRTSCTS; // Disable hardware flow control

CRTSCTS : output hardware flow control (only used if the cable has all necessary lines. See sect. 7 of Serial-HOWTO)

struct termios options;
options.c_cflag |= CRTSCTS;//enable hardware flow control
tcsetattr(fd, TCSANOW, &options)
options.c_cflag &= ~CRTSCTS; //disable hardware flow control
Eğer bu kabloları hardware flow control için kullanmıyorsak bile seri portu açtıktan sonra da kontrol etmek istiyorsak aşağıdaki gibi yapabiliriz. TIOCM_RTS ve TIOCMSET kullanılıyor.


c_iflag alanları

c_iflag alanı ile software flow control yapmak

struct termios options;
options.c_ciflag |= (IXON | IXOFF | IXANY);//enable software flow control
tcsetattr(fd, TCSANOW, &options)
struct termios options;
options.c_ciflag &= ~(IXON | IXOFF | IXANY);//disable software flow control
tcsetattr(fd, TCSANOW, &options)

c_iflag alanı ile parity hatası içeren byteları süzmek
Örnekte parity hatası içeren byte'lar programa input olarak verilmez.
options.c_iflag |= IGNPAR;//ignore parity errors
c_oflag alanları
c_oflag alanı ile raw output
port_settings.c_oflag &= ~OPOST; //enable raw output 
cfmakeraw yazısına da bakalıbilir.

c_lflag alanı - local flags
c_lflag alanı ile non canonical input yapmak
ICANON seçeneği kullanılır. Herhangi bir cihaz ile seri iletişim için genellikle non-canonical mode kullanılır. non-canonical en basit haliyle non-buffered I/O olarak düşünülebilir. Örnek olarak ACE kütüphanesi ile gelen TTY_IO.cpp dosyasındaki
     // Enable noncanonical input processing mode 
     options.c_lflag &= ~ICANON; 
satırına bakılabilir.

Buradaki soruda getch() metodunda kullanıcı Enter tuşuna basmadan, tuşladığı karakter hemen alınabilsin diye 0 nolu file descriptor olan stdin non-canonical hale getiriliyor.

Basılan her tuş bir byte ile temsil edilmek zorunda değildir. non-canonical modda basılan tuşu anlamak için read işleminin döndürdüğü byte sayısına bakmak gerekir. Örnek:

struct termios term_original, term_current;
tcgetattr(STDIN_FILENO, &term_original);
term_current = term_original;
term_current.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO);
term_current.c_iflag &= ~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR | INPCK | ISTRIP | IXON | PARMRK);
term_current.c_oflag &= ~(OPOST);
term_current.c_cc[VMIN]  = 1;
term_current.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSADRAIN, &term_current);



c_lflag alanı ile input echo
Girilen karakterlerin echolanarak ekranda gösterilmesi veya gösterilmemesini kontrol eder.
options.c_clflag &= ~ECHO; // Disable echoing of input characters
options.c_clflag &= ~ECHOE;
 c_lflag alanı ile sinyaller
ISIG kullanılır.
options.c_lflag &= ~( ISIG | // disable SIGxxxx signals
                            IEXTEN // disable extended functions
clflag ile raw input
Yukarıdaki tüm bayraklar bir araya gelince raw input yapmak için aşağıdaki gibi yapmak lazım.
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
clflag ile terminal yazmayı engelleme
Arka planda çalışan bir uygulamanın terminale yazmasını engellemek için TOSTOP kullanılır. Örnek:


c_cc alanı
Şöyle kodlanır
options.c_cc[VMIN]   =  1; //read doesn't block. Min. number of chars to read
options.c_cc[VTIME]  =  5; // 0.5 seconds read timeout
Bu satırların anlamı ise buradan aldığım aşağıdaki şekilde anlatılıyor.
select() gibi kullanmak için şöyle yaparız.

VMIN = 0 and VTIME > 0
This is a pure timed read. If data are available in the input queue, it's transferred to the caller's buffer up to a maximum of nbytes, and returned immediately to the caller. Otherwise the driver blocks until data arrives, or when VTIME tenths expire from the start of the call. If the timer expires without data, zero is returned. A single byte is sufficient to satisfy this read call, but if more is available in the input queue, it's returned to the caller. Note that this is an overall timer, not an intercharacter one.

tcgetattr metodu
tcgetattr metodu yazısına taşıdım.

cfsetispeed ve cfsetospeed ile iletişim hızı (Baud Rate)
baud nedir yazısına bakabilirsiniz.

İletişim hızını ayarlamak için cfsetispeed (input) ve cfsetospeed (output) metodları kullanılır.Aslında bu metodlar struct termios yapısının c_cflag alanını değiştiriyor. Ancak niyeyse ayrı metodlar halinde gelmişler.

Bazı platformlar cfsetspeed() extension metodunu da tanımlıyorlar, ancak sanırım bu metod portable değil.

Baud Rate sabitlerini buradan görmek mümkün. Her platformda bulunmasa da özellikle USB cihazları için B2000000 sabitini de görmek mümkün.


Line Control Metodları

tcflush metodu
Bu metod gönderilmemiş tamponda bekleyen veriyi gönderir.
tcflush(fd, TCIFLUSH );
Örnek:
tcflush(fd,TCIOFLUSH)

C#
C# ile seri portu kullanmak için SerialPort sınıfı kullanılabilir. SerialPort nesnesi isimle açılır. Port'un ismi DeviceManager ile öğrenilebilir.

Açma Örneği
Örnek'te SerialPort sınıfına PortName, BaudRate,DataBits,Parity,StopBits atanıyor.

SerialPort myPort = new SerialPort();
myPort.DataReceived += MyPortDataReceived;
myPort.PortName = "COM45";
myPort.BaudRate = 4800;
myPort.DataBits = 8;
myPort.Parity = Parity.None;
myPort.StopBits = StopBits.One;

GetNames Metodu
Port isimlerini alır. Örnek:
// Get all open ports
string[] ports = SerialPort.GetPortNames();

ReadLine Metodu
// Create a new SerialPort object with default settings.
SerialPort s = new SerialPort();

// Set the read/write timeouts
s.ReadTimeout = 500;
s.WriteTimeout = 500;

s.Open();
string message = s.ReadLine();
string message = s.ReadExisting();
s.WriteLine("message");

WriteLine Metodu
Verilen string'i port'a yazar.