Forum: Mikrocontroller und Digitale Elektronik Embedded Linux, SPI, GPIO


von Ruben (Gast)


Lesenswert?

Hallo,

folgende Ausgangssituation: Ich möchte von einem Linux-System 
(Pandaboard) über die SPI-Schnittstelle Daten von einem AD-Wandler 
(ADS1255 von Texas Instruments) lesen.

Der AD-Wandler hat einen DATA-READY Ausgang, der kurzzeitig high wird 
wenn eine Wandlung fertig ist.

Nun möchte ich auf dem Pandaboard folgende Schritte durchführen:
- Interrupt an GPIO-Pin um DATA-READY Signal auszuwerten (bei steigender 
Flanke)
- anschließend Daten auslesen mit spidev-Treiber (kommt später dran)

Der zugehörige GPIO-Pin ist unter /sys/class/gpio/gpioXX als input 
konfiguriert. Unter edge habe ich "rising" eingetragen.

Wie kann ich in einem Linux-System einen Interrupt einrichten? Muss ich 
dazu einen eigenen Treiber schreiben? Bzw. ist mein GPIO-Pin so 
ausreichend konfiguriert?

Ich hoffe ich habe mich einigermaßen verständlich ausgedrückt.

Grüße
Ruben

von Guenter B. (gooofy)


Lesenswert?

ob es der einzige Weg ist, weiss ich nicht, aber es geht auf jeden Fall 
sehr elegant mit einem Kernel-Modul. Hier kann man z.B. in der 
init-Funktion GPIOs auf Interrupts mappen und dann einen entsprechenden 
Callback hinterlegen:
1
static int __init hirc_modinit(void)
2
{
3
  int result;
4
  int i, nlow, nhigh;
5
6
  printk(KERN_INFO PFX "init starts\n");
7
8
  /* Init read buffer. */
9
  result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN);
10
  if (result < 0)
11
    return -ENOMEM;
12
13
  result = gpio_request (HIRC_GPIO, "hirc");
14
15
  if (result) {
16
    printk (KERN_ERR PFX "failed to request GPIO %d: %d\n", HIRC_GPIO, result);
17
    return result;
18
  }
19
20
  gpio_direction_input (HIRC_GPIO);
21
22
  irq = gpio_to_irq (HIRC_GPIO);
23
24
  if (irq < 0) {
25
    printk (KERN_ERR PFX "failed to map GPIO %d to interrupt: %d\n", HIRC_GPIO, irq);
26
27
    gpio_free (HIRC_GPIO);
28
29
    return irq;
30
31
  }
32
33
  printk (KERN_INFO PFX "GPIO %d mapped to IRQ %d\n", HIRC_GPIO, irq);
34
35
  //set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
36
37
  result = request_irq(irq, irq_handler,
38
      //0,
39
      IRQF_DISABLED | IRQF_TRIGGER_RISING ,
40
      //     IRQF_DISABLED | IRQF_TRIGGER_RISING ,
41
      HIRC_DRIVER_NAME, irq_id);
42
43
  switch (result) {
44
    case -EBUSY:
45
      printk(KERN_ERR PFX "irq %d busy\n", irq);
46
      gpio_free (HIRC_GPIO);
47
      return -EBUSY;
48
    case -EINVAL:
49
      printk(KERN_ERR PFX
50
          "bad irq number or handler\n");
51
      gpio_free (HIRC_GPIO);
52
      return -EINVAL;
53
    default:
54
      printk(KERN_INFO PFX "irq %d, gpio %d obtained\n", irq, HIRC_GPIO);
55
      break;
56
  }

(Beispiel stammt aus einem Treiber fuer einen Infrarot-Empfaenger, der 
an einem GPIO haengt - allerdings handelt es sich um einen kleinen 
Router, nicht um ein Pandaboard).

der Interrupt-Handler kann dann mit gpio_get_value den Pin lesen:
1
static irqreturn_t irq_handler(int i, void *blah)
2
{
3
  struct timeval tv;
4
  int dcd;
5
  long deltv;
6
  int data;
7
  static int last_dcd = -1;
8
9
  while (1) {
10
    dcd = gpio_get_value(HIRC_GPIO);
11
12
    if (dcd == last_dcd) {
13
      return IRQ_HANDLED;
14
    }
15
16
    /* get current time */
17
    do_gettimeofday(&tv);
18
19
    deltv = tv.tv_sec-lasttv.tv_sec;
20
    if (tv.tv_sec < lasttv.tv_sec ||
21
        (tv.tv_sec == lasttv.tv_sec &&
22
         tv.tv_usec < lasttv.tv_usec)) {
23
      printk(KERN_WARNING PFX "AIEEEE: your clock just jumped backwards\n");
24
      printk(KERN_WARNING PFX
25
          "%d %d %lx %lx %lx %lx\n",
26
          dcd, sense,
27
          tv.tv_sec, lasttv.tv_sec,
28
          tv.tv_usec, lasttv.tv_usec);
29
      data = PULSE_MASK;
30
    } else if (deltv > 15) {
31
      data = PULSE_MASK; /* really long time */
32
#if 0
33
      printk (KERN_WARNING PFX "really long time: %ld\n", deltv);
34
#endif
35
    } else
36
      data = (int) (deltv*1000000 +
37
          tv.tv_usec -
38
          lasttv.tv_usec);
39
    rbwrite(dcd^sense ? data : (data|PULSE_BIT));
40
    lasttv = tv;
41
    last_dcd = dcd;
42
    wake_up_interruptible(&rbuf.wait_poll);
43
  }
44
45
  return IRQ_HANDLED;
46
}

von Ruben (Gast)


Lesenswert?

Danke für die Antwort. Ich werde das mal ausprobieren.

Grüße
Ruben

von Ruben (Gast)


Lesenswert?

Hallo,

ich habe den Vorschlag von Günter umgesetzt und das geht auch 
grundsätzlich. Allerdings tritt der Interrupt nur einmal auf.

Meinem Verständnis nach muss ich im Interrupt-Handler den Interrupt 
wieder frei schalten.

Hat da jemand einen Tipp für mich?

Vielen Dank im voraus.
Ruben

von leohome (Gast)


Lesenswert?

IRQF_DISABLED | IRQF_TRIGGER_RISING Flag könnte Falsch sein

setze mal statt desse eine 0 rein.

von Bernd B. (behbeh)


Lesenswert?

Hallo,
sowas habe ich versucht mit einem Linux FOX Board. Sollte dann ja auch 
möglich sein damit.
Ein Tip zum DRDY Signal. Bei richtiger Behandlung, also wenn man die 
Daten ausliest, müsste das DRDY Signal mit der ersten Clk-Flanke wieder 
auf High gehen, also muss man den ISR auf negative Flanke stellen, dann 
kann man im Wandlertakt( z.B. 1000Hz ADS1281) die 3 bzw 4 Bytes 
auslesen. Klappt prima bei mir, leider nicht mit Linux.
Bernd

von Mario G. (mario)


Lesenswert?

Hallo,

sorry das ich den alten Thread nochmal ausgrabe aber ich habe ein 
ähnliches Problem: ich möchte einen GPIO als Interrupt nutzen und beim 
eintreten des Interrupts eine Aktion auslösen.

Das Programmieren das Kernelmoduls ist kein Problem, ABER:
Weiß jemand ob es möglich ist aus dem Interrupt-Handler heraus ein 
Aktion im Userspace aufzurufen, z.B. ein Programm starten?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.