一、中断
中断处理在实时操作系统中非常重要,因为通过中断机制,系统可以感知外部事件。系统对中断的响应速度是许多实时系统的一个重要特性。
与Xenomai的GPIO管脚实时交互需要一个实时设备驱动程序。
设备驱动
对于添加到计算机中的每一个新硬件,计算机都需要一个所谓的硬件驱动程序来与之交互。对于linux,硬件驱动程序是使用内核模块实现的。这样的硬件驱动程序可以通过所谓的设备文件访问用户空间。用户空间程序可以通过使用open函数打开此设备文件来与硬件交互 :
int fd=open("/dev/devicefile", …)
open调用向设备返回一个文件句柄。这个句柄可以用于从设备读取数据,也可以使用读写函数将数据写入设备。例如:
int value;
read(fd1, &value, 4);
write(fd2, &value, sizeof(value));
请注意,要读取和写入一个GPIO引脚,需要初始化两个文件句柄,如下所述。
基本上,linux通过将设备表示为虚拟文件来使用文件API与设备交互。然而,设备通常更复杂,它们支持比读取和写入数据更专门的交互。对于这些交互,linux支持ioctl函数,它有如下接口:
int ioctl(int fd, int request, …);
第一个参数fd必须是设备的打开文件描述符。第二个参数是设备相关的请求代码。第三个参数是指向内存的无类型指针,它的使用取决于特定的请求代码。通过这种方式,可以提供几个请求代码来实现特定设备的专门交互。
可以使用/dev/中的设备驱动程序从linux控制Raspberry Pi(树莓派)上的GPIO管脚,其中每个管脚都有一个单独的驱动程序来控制。然而,在Xenomai中使用linux设备驱动程序时,程序执行会从高优先级实时内核转移到低优先级的linux内核。因此,实时内核需要专门的驱动程序。
实时设备驱动(RTDM)
Xenomai使用实时驱动模型(RTDM)来实现实时驱动。在这些练习中,我们使用一个已经实现的RTDM驱动程序,用于树莓派上的GPIO管脚,称为xeno_GPIO。
本课程中使用的sd卡上的映像会在启动时自动加载xeno_gpio RTDM设备驱动程序。在命令行中键入“lsmod”可以看到,其中列出了当前加载的内核模块:
sikander@pc:~$ lsmod
Module Size Used by
xeno_gpio 4531 0
ccm 7391 2
rfcomm 57295 0
bnep 13213 2
snd_hda_codec_hdmi 37022 1
snd_hda_codec_conexant 11105 1
kvm_amd 2157076 0
arc4 1784 2
snd_hda_codec_generic 59052 1 snd_hda_codec_conexant
kvm 493834 1 kvm_amd
snd_hda_intel 27448 7
irqbypass 2791 1 kvm
rtl8723be 76614 0
snd_hda_codec 105077 4 snd_hda_intel,snd_hda_codec_conexant,snd_hda_codec_hdmi,snd_hda_codec_generic
要将RTDM设备驱动程序与linux设备驱动程序分开,它们的设备文件位于/dev/RTDM/子目录中。对于每个GPIO pin都有这样一个文件 :
$ cd /dev/rtdm/pinctrl-bcm2835/
$ ls
gpio0 gpio12 gpio16 gpio2 gpio23 gpio27 gpio30 gpio34 gpio38 gpio41 gpio45 gpio49 gpio52 gpio9
gpio1 gpio13 gpio17 gpio20 gpio24 gpio28 gpio31 gpio35 gpio39 gpio42 gpio46 gpio5 gpio6
gpio10 gpio14 gpio18 gpio21 gpio25 gpio29 gpio32 gpio36 gpio4 gpio43 gpio47 gpio50 gpio7
gpio11 gpio15 gpio19 gpio22 gpio26 gpio3 gpio33 gpio37 gpio40 gpio44 gpio48 gpio51 gpio8
命令rmmod xeno_gpio卸载xeno_gpio RTDM设备驱动程序,lsmod将显示相应的设备文件被删除。使用modprobe xeno_gpio命令可以重新加载设备驱动程序。
使用实时GPIO设备驱动程序读取和写入GPIO引脚
我们以pin 23为例,描述了初始化一个GPIO pin以进行读取以及读取该pin的值的函数。同样的写作。
下面的代码片段不包括错误处理,例如当fd<0或ret<0。POSIX API的错误处理模式可以在 tips page of the lab site中找到。
代码片段应当包括以下内容:
包含以下头文件:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <alchemy/task.h>
#include <alchemy/timer.h>
#include <rtdm/gpio.h>
从GPIO输入端口进行读取
在驱动程序中定义了 四种 系统调用的ioctl()命令:
switch (request) {
case GPIO_RTIOC_DIR_OUT:
ret = rtdm_safe_copy_from_user(fd, &val, arg, sizeof(val));
if (ret)
return ret;
ret = gpio_direction_output(gpio, val);
if (ret == 0) {
chan->has_direction = true;
chan->is_output = true;
}
break;
case GPIO_RTIOC_DIR_IN:
ret = gpio_direction_input(gpio);
if (ret == 0)
chan->has_direction = true;
break;
case GPIO_RTIOC_IRQEN:
if (chan->requested)
return -EBUSY;
ret = rtdm_safe_copy_from_user(fd, &trigger,
arg, sizeof(trigger));
if (ret)
return ret;
ret = request_gpio_irq(gpio, pin, chan, trigger);
break;
case GPIO_RTIOC_IRQDIS:
release_gpio_irq(gpio, pin, chan);
chan->requested = false;
break;
default:
return -EINVAL;
}
GPIO_RTIOC_DIR_OUT:设置端口为输出模式