mgulsoy.github.io

Bir Kişisel Blog

Linux Üzerinde Paralel Port Erişimi

15 Feb 2024

Lisedeyken bilgisayar kullanmaya başladığımda en çok etkilendiğim kısım paralel port olmuştu. Buraya başka bir şeyler bağlayıp programlama yaparak bir otomasyon sistemi yapmayı hayal ederdim. Şimdi basit bir şekilde Ubuntu işletim sistemi kullanarak paralel port pinlerini kontrol etmeye çalışalım.

Paralel port aslında eski bir teknoloji. Güncel sistemlerin pek çoğunda aslında bulunmuyor bile. Tarihi eser sayılabilecek yazıcıları kontrol etmek için kullanılıyor. Fakat bazı uygulamalarda gerçekten işe yarıyor. Bu yazıdaki işlemleri yapabilmek için temek C programlama bilgisine ihtiyaç bulunmaktadır.

Paralel port (Kaynak: wikipedia)

Pinler

Port pin tanımları ve fonksiyonları şöyledir:

Pin Sinyal Yön
1 Strobe (Active Low) I/O
2 DATA 0 SPP(O) , ECP(I/O)
3 DATA 1 SPP(O) , ECP(I/O)
4 DATA 2 SPP(O) , ECP(I/O)
5 DATA 3 SPP(O) , ECP(I/O)
6 DATA 4 SPP(O) , ECP(I/O)
7 DATA 5 SPP(O) , ECP(I/O)
8 DATA 6 SPP(O) , ECP(I/O)
9 DATA 7 SPP(O) , ECP(I/O)
10 ACK I
11 BUSY I
12 PAPER OUT I
13 SELECT I
14 Line Feed (Active Low) I/O
15 ERROR I
16 RESET I/O
17 SELECT (Active Low) I/O
18-25 GROUND REF

Paralel port çalışma yöntemleri profil olarak tanımlanır. Bunları şöyle açıklayabiliriz:

  • SPP: Standart Parallel Port
  • EPP: Enhanced Parallel Port (Geliştirilmiş paralel port)
  • ECP: Extended Capabilities Port (Yetenekleri arttırılmış port)

Bu bilgiye göre pinlerin yönleri şöyle ifade edilebilir:

  • SPP(O) : Eğer port SPP profiline göre ayarlanmışsa pin sadece çıkış (OUTPUT/source) yönünde çalışır.
  • ECP(I/O) : Eğer port ECP profiline göre ayarlanmışsa pin hem çıkış (OUTPUT/source) hem de giriş(INPUT/sink) yönünde çalışabilir.
  • I : Pin sadece giriş(INPUT/sink) yönünde çalışabilir.
  • I/O : Pin hem çıkış (OUTPUT/source) hem de giriş(INPUT/sink) yönünde çalışabilir.

Hazırlık

Portu kontrol etmek için basit bir C uygulaması yazacağız. Fakat bunu yapabilmemiz için önce sistemimizde bir C derleyicisine ihtiyacımız olacak. Bunun için GCC yükleyeceğiz.

sudo apt -y install gcc

Komutunu kullanarak gcc yükledikten sonra kodlamaya başlayabiliriz.

Örnek Kod

Linux sistemlerinde donanımlar /dev klasörü altında birer dosya olarak nitelenir. Bu dosyaları kullanarak donanımlara veya sürücülerine erişip kontrol etmek mümkündür. Biz de öyle yapacağız. Önce sistemdeki birinci paralel port aygıtı olan /dev/parport0 ‘ı açmaya çalışacağız. Sonra bu aygıtı başkası kullanmasın diye onu tekelimize alacağız.

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/parport.h>
#include <linux/ppdev.h>

#define DEVICE "/dev/parport0"

int main(int argc, char **argv) {

  // önce dosyayı açarız. 
  int fd = open(DEVICE, O_RDRW);
  if(fd < 0) {
    // Eğer dosya açma işlemi 0'da küçük bir sonuç dönerse işlem hatalı gerçekleşmiş demektir.
    fprintf(stderr, "Dosya acilamiyor: %s\n", DEVICE);
    perror("OPEN DEVICE"); // Hata mesajını göster.
    return 5; // Uygulamadan çık
  }

  // Port aygıtına erişim tamam şimdi tekelimize alacağız:
  int res = ioctl(fd,PPCLAIM); // ioctl ile PPCLAIM komutunu çalıştır.
  if(res < 0) {
    // Komut başarısız.
    perror("PPCLAIM");
    close(fd); // dosyayı kapat.
    return 5; // uygulamadan çık.
  }

  // Komut başarılı. Şimdi strobe pininden sinyal verecceğiz:
  strobe_blink(fd);

  // Kontrol işlemlerimiz tamamlandığında portu serbest bırakacağız ve dosyayı kapatacağız.
  ioctl(fd, PPRELEASE); // hata olsa da ilgilenmiyoruz.
  close(fd);
  
  return 0;
}

Bu kodu main.c adı ile kaydedebiliriz. Temel olarak yaptığımız şey portu açmak, PPCLAIM komutu ile sahipliğini almak, I/O işlemi yapmak. PPRELEASE komutu ile serbest bırakmak ve kapatmak. Şimdi strobe_blink(fd) çağrımına bakalım:

int strobe_blink(int fd) {

  struct ppdev_frob_struct frob; // bir frob yapısı tanımla
  frob.mask = PARPORT_CONTROL_STROBE; // Bit maskesi belirle, hangi kontrol pinini kumanda edeceğimizi belirler.
  for(int i = 0; i < 30; i++) {
    // Strobe pinini aktif yap! ( dikkat: pin ters mantık çalışır )
    frob.val = 0xFF;
    ioctl(fd, PPFCONTROL, &frob);
    usleep(500000); // 500ms bekle

    // pasif yap
    frob.val = 0x00;
    ioctl(fd, PPFCONTROL, &frob);
    usleep(500000); // 500ms bekle
  }
}

Bu fonksiyon ile strobe (1) pinini yarım saniyelik sinyaller üretmek üzere kullanmış olduk. Programı derlemek için gcc -o pport main.c komutunu kullanabiliriz. Bu bize pport adlı bir çalıştırılabilir dosya üretecektir. Bu dosyayı sudo ile çağrılmalı veya CAP_SYS_RAWIO ile yetkilendirilmelidir. Zira direkt olara donanım erişimi söz konusu.

Diğer Kontroller

Aşağıdaki fonksiyonları da kullarak başka kontrolleri de sağlayabiliriz.

Data Pinlerine Yazma (Çıkış)

Bu fonksiyon argümanlarından data 8 bitlik bir tip olduğundan her bir bit, bir data bus pinine karşılık gelir.

int write_data_pins(int fd, unsigned char data) {
  return (ioctl(fd, PPWDATA, &data));
}

Data Pinlerinden Okuma (Giriş)

int read_data_pins(int fd) {
  unsigned char data;

  int mod = IEEE1284_MODE_ECP;  // port profilini ECP olarak değiştir
  int res = ioctl(fd,PPSETMODE,&mod);

  if(res < 0){
    // Profil belirlenirken hata:
    perror("PPSETMODE");
    return res;
  }

  // Profil belirleme başarılı
  // Port yönü giriş yapılır.
  res = ioctl(fd, PPDATADIR, &mod);
  if(res < 0){
    // Port giriş yapılırken hata:
    perror("PPDATADIR");
    return res;
  }

  // birkaç saniye beklenir, bu sürede elle bir giriş sinyali üretilebilir.
  sleep(10);

  // veri porttan okunur:
  res = ioctl(fd, PPRDATA, &data);
  if(res < 0){
    // Port giriş okunurken hata:
    perror("PPRDATA");
    return res;
  }

  // data değişkeni içinde port data pinleri verisi bulunur.
  return (int)data;
}

Durum Pinlerinden Okuma

int read_status_pins(int fd) {

  int val;
  int res = ioctl(fd, PPRSTATUS, &val);
  if(res < 0){
    // Profil belirlenirken hata:
    perror("PPRSTATUS");
    return res;
  }

  val ^= PARPORT_STATUS_BUSY;   // busy pini değeri ters çevrilmelidir.

  printf("/BUSY  = %s\n",
		((val & PARPORT_STATUS_BUSY)==PARPORT_STATUS_BUSY)?"HI":"LO");
	printf("ERROR  = %s\n",
		((val & PARPORT_STATUS_ERROR)==PARPORT_STATUS_ERROR)?"HI":"LO");
	printf("SELECT = %s\n",
		((val & PARPORT_STATUS_SELECT)==PARPORT_STATUS_SELECT)?"HI":"LO");
	printf("PAPEROUT = %s\n",
		((val & PARPORT_STATUS_PAPEROUT)==PARPORT_STATUS_PAPEROUT)?"HI":"LO");
	printf("ACK = %s\n",
		((val & PARPORT_STATUS_ACK)==PARPORT_STATUS_ACK)?"HI":"LO");
	return 0;
}

Pinlerden okuma yazma yapılırken pinlerin akımlarına dikkat edilmelidir. Zira pinler çok yüksek akımları sağlayamayabilir. Ne kadar akım sağlayabileceği ise donanım üreticisi tarafından belirlenir. Pinler giriş konfigürasyonu olarak ayarlandığında tri-state veya HIGH-Z olarak bulunmayıp pull-up olarak da çalışıyor olabilir.

Kaynaklar:

OpensourceForU

Linux Kernel Docs, Parport

Linux Kernel Docs, Parport, low level

How to control peripheral ports: Accessing and writing on Parallel Port with C on Linux. Part I

Linux Parport programming

The Linux 2.4 Parallel Port Subsystem

alibaba LVS Source, github

Linux source, github

Writing a parport device driver, tldp.org