linux信号量驱动程序,驱动程序和应用程序之间信号量通讯说明

本文主要介绍在Linux操作系统下面,驱动程序内部的中断函数产生中断时被调用以后,如何通过信号量来和应用程序之间进行实时的通讯。本文所有程序的硬件平台是基于PPC405EP开发板的,运行的内核是Linux

2.4.25。

以DPRAM的驱动程序为例(DPRAM驱动程序的介绍和说明请参照“PowerPC405EP

FPGA开发板补充说明v2.doc”文档),标准的DPRAM在PPC405下面可以正常的读写,使用驱动函数里面的实现的read和write函数可以完成对DPRAM某个地址下,连续的某些存储空间的读写操作。但是在实际的通讯过程中,往往总是依靠外部其他设备完成对DPRAM的读写以后,再向PPC405发送一次中断来通知PPC405

DPRAM中的数据已经准备好,或者已经读取结束。PPC405应该响应该中断,应用程序应该按照程序流程和中断作出进一步的判断。

中断函数是位于DPRAM驱动里面的,当驱动加载时,DPRAM的驱动内部的init函数应该向Linux系统注册中断号和中断函数,比如:

request_irq(DPRAM_IRQ, dpram_interrupt, SA_INTERRUPT, "dpram",

NULL);

其中,DPRAM_IRQ是DPRAM驱动中使用到的中断号,实际中采用28和29,对应PPC405的EIRQ4和EIRQ5。而dpram_interrupt是某个中断号对应的中断程序,当该中断号对应的中断引脚接收到低电平脉冲时,这个被注册的中断程序就会被调用。而在该中断程序里面,应该向应用程序发送信号量,通知应用程序某个中断发生,由应用程序进行进一步的判断和操作。

实际中驱动程序中注册中断部分摘要如下所示:

#define DPRAM_IRQ_1  28  //EIRQ4 of PPC405EP

#define DPRAM_IRQ_2  29  //EIRQ5 of PPC405EP

//register the IRQ for DPRAM system

result = request_irq(DPRAM_IRQ_1, ppc_dpram_interrupt_1,

SA_INTERRUPT, "dpram_irq_1", NULL);

if (0 != result)

{

printk(KERN_INFO "PPC DPRAM

driver interrupt handler 1 install failed.\n");

}

result = request_irq(DPRAM_IRQ_2, ppc_dpram_interrupt_2,

SA_INTERRUPT, "dpram_irq_2", NULL);

if (0 != result)

{

printk(KERN_INFO "PPC DPRAM

driver interrupt handler 2 install failed.\n");

}

启动Linux以后,应该可以 cat /proc/inetrrupts 看到如下图所示:

其中,dpram_irq_1和dpram_irq_2就是在DPRAM的驱动程序中注册的中断,分别对应28和29的中断号。现在中断函数的简单处理如下:

static void ppc_dpram_interrupt_1(int irq, void *dev_id,

struct pt_regs *regs)

{

printk("EIRQ 4 of PPC405EP\n");//for debug

}

static void ppc_dpram_interrupt_2(int irq, void *dev_id,

struct pt_regs *regs)

{

printk("EIRQ 5 of PPC405EP\n");//for debug

}

这样系统启动以后,在IRQ4引脚上如果输入低电平的脉冲,在串口终端应该可以看到如下显示:

驱动程序内部可以加入信号量来和应用程序通讯,具体的关于信号量通讯的技术文档,可以参照下面的几个链接来学习,本文中只是应用,更多的细节需要用户自己学习,引申和掌握。

(信号量通讯的参考资料:

http://linux_kernel.blog.com/921870/

http://ftp.xjtu.edu.cn/ftp/pub/document/linux-book/Linux��̰�Ƥ��/05.pdf

)

在驱动的中断函数中,可以依靠向应用程序进程发送信号量来通知程序进程接收数据或者执行其他操作。这部分代码如下所示:

#define COMMAND_CHANGE_PID  0xAA

#define DPRAM_SIGNAL_1  46

#define DPRAM_SIGNAL_2  47

static int  app_pid;

static struct  task_struct

*ptask;

static void ppc_dpram_interrupt_1(int irq, void *dev_id,

struct pt_regs *regs)

{

printk("EIRQ 4 of PPC405EP\n");//for debug

//send a signal to application, tell it that a

eirq4 received.

ptask = find_task_by_pid(app_pid);

if (ptask != NULL)

{

send_sig(DPRAM_SIGNAL_1, ptask, 1);//send signal

DPRAM_SIGNAL_1 to application by EIRQ4

}

}

static void ppc_dpram_interrupt_2(int irq, void *dev_id,

struct pt_regs *regs)

{

printk("EIRQ 5 of PPC405EP\n");//for debug

//send a signal to application, tell it that a

eirq4 received.

ptask = find_task_by_pid(app_pid);

if (ptask != NULL)

{

send_sig(DPRAM_SIGNAL_2, ptask, 1);//send signal

DPRAM_SIGNAL_2 to application by EIRQ5

}

}

因为应用程序的进程号每次启动应用程序时是不定的,而信号量的发送,必须知道应用程序的进程号(send_sig(DPRAM_SIGNAL_2,

ptask, 1);中ptask即是应用程序的进程号)。因此在ioctrl函数里面,实现了应用程序设置进程号的功能:

case COMMAND_CHANGE_PID:

app_pid = arg;

break;

同时,在应用程序中,应该完成信号量函数,注册信号量,向驱动写入应用程序进程号等工作,相关的代码如下:

#include

static struct sigaction irq1_act;

static struct sigaction irq2_act;

static int dpram_fd;

static unsigned char buffer[1024];

//signal process for IRQ1

void irq1_drvSigHandler(int signo)

{

unsigned char *buffer;

unsigned char i;

int  tom;

printf("signal is %d.\n", signo);

if(46 == signo)

{

//TODO: do something

here.

}

}

//signal process for IRQ2

void irq2_drvSigHandler(int signo)

{

unsigned char *buffer;

unsigned char i;

int  tom;

printf("signal is %d.\n", signo);

if(47 == signo)

{

//TODO: do something

here.

}

}

下面是main函数中的注册信号量部分代码:

//register signal

printf("register the signal process

function.\n");

//irq1

irq1_act.sa_handler = irq1_drvSigHandler;

irq1_act.sa_flags = 0;

sigemptyset(&irq1_act.sa_mask);

//irq2

irq2_act.sa_handler = irq2_drvSigHandler;

irq2_act.sa_flags = 0;

sigemptyset(&irq2_act.sa_mask);

//register

ret = sigaction(46, &irq1_act, NULL);

if (ret == -1)

printf("Request signal 46

failed.\n");

ret = sigaction(47, &irq2_act, NULL);

if (ret == -1)

printf("Request signal 47

failed.\n");

//open a device

dpram_fd = open("/dev/dpram",O_SYNC |

O_RDWR,0);

printf("Transmit pid of %d to driver.\n",

getpid());

ret = ioctl(dpram_fd, 0xAA, getpid());

由此,在系统启动以后,EIRQ4上如果输入低电平脉冲,应该可以看到串口如下类似的输出:

附录:PPC405EP平台GPIO输出控制使用说明

如果在应用中需要向其他处理器或者LED灯等输出一个电平信号,可以使用PPC405EP的GPIO来完成。DPRAM驱动的ioctrl函数内部,实现了GPIO7和GPIO8控制外部设备,输出高低电平的功能。

驱动中的这部分代码如下所示:

#define PPC_DPRAM_BIT_1  0x01000000

#define PPC_DPRAM_BIT_2  0x00800000

static int ppc_dpram_ioctl(struct inode *inode, struct file

*filp,

unsigned

int cmd, unsigned long arg)

{

unsigned long i;

switch (cmd) {

case 0:

i = in_be32((volatile

unsigned*)GPIO0_OR);/

i = in_be32((volatile

unsigned*)GPIO0_OR);//read the GPIO0_OR register

i = i

|(PPC_DPRAM_BIT_1);

out_be32((volatile

unsigned*)GPIO0_OR, i);//send out to GPIO0_OR register

break;

case 3:

i = in_be32((volatile

unsigned*)GPIO0_OR);/

i = in_be32((volatile

unsigned*)GPIO0_OR);//read the GPIO0_OR register

i = i

|(PPC_DPRAM_BIT_2);

out_be32((volatile

unsigned*)GPIO0_OR, i);//send out to GPIO0_OR register

break;

而在测试程序中,可以加入下面代码来输出一个低电平:

else if(my=='1')

{

printf("Output a low pulse on

output1.\n");

ioctl(dpram_fd, 1, 0);//set

output1 high

ioctl(dpram_fd, 0, 0);//set

output1 low

ioctl(dpram_fd, 1, 0);//set

output1 high

}

else if(my=='2')

{

printf("Output a low pulse on

output2.\n");

ioctl(dpram_fd, 4, 0);//set

output2 high

ioctl(dpram_fd, 3, 0);//set

output2 low

ioctl(dpram_fd, 4, 0);//set

output2 high

}

用示波器测量,应该看到这两个引脚输出低电平约2.6uS的脉冲一次。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值