|
测试程序使用打印机的并口中断特性。在打印机端口的信号中存在 ACK ,此信号表示打印机已经顺利的接受了传送的数据。当计算机向打印机传送数据时,为了确认数据是否正常传送,就需要检查 ACK信号,该信号连接在打印机端口的第 10脚上。与其他引脚不同,该 pin上的信号会发生中断。当该 pin的电压从 5V 变为 0V 时,产生中断。所以,下面的测试,将在测试过程中,将 10 脚和 25 脚(GND pin)连接,使其发生中断。
这个实例包含驱动程序和应用程序两部分。应用程序的测试流程为:
(1) 先用 write() 函数开启 LED ,然后使用 read() 函数通过无限循环等待中断的发生。
(2) 将第 10 pin 和第 25 pin,中断发生,此时 read()会读取到中断发生的次数,然后进行输出。
(3) 然后,以 1s 为周期,重复 5 次关闭 LED 开关操作,结束运行。
在做这个试验时,需要通过 BIOS 检查一下自己的 PC 并口的中断号是什么,一般情况下为 7 ,但也有的会安排别的数字,像我的中断号是 3 ,正是因为这原因,导致浪费许多检查出错的时间。另外,驱动程序在使用当前发行版的内核时可能是不行的,所以需要重新编译一个新的不把并口驱动编译进内核或以模块的方式加载到内核中的新内核(我使用了2.6.31.2),这样实验就容易成功。
驱动程序代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#define INT_DEV_NAME "int_dev"
#define INT_DEV_MAJOR 240
#define INT_WRITE_ADDR 0x378
#define INT_READ_ADDR 0x379
#define INT_CTRL_ADDR 0x37A
#define PRINT_IRQ 3
#define PRINT_IRQ_ENABLE_MASK 0x10
#define INT_BUFF_MAX 64
typedef struct {
unsigned long time;
}__attribute__ ((packed)) R_INT_INFO;
R_INT_INFO intbuffer [INT_BUFF_MAX];
int intcount = 0;
/*清除缓存*/
void int_clear (void)
{
int lp;
for (lp = 0; lp < INT_BUFF_MAX; lp++) {
intbuffer[lp].time = 0;
}
intcount = 0;
}
irqreturn_t int_interrupt (int irq, void *dev_id)
{
printk ("come in interrupt\n");
if (intcount < INT_BUFF_MAX) {
intbuffer[intcount].time = get_jiffies_64();
intcount++;
}
return IRQ_HANDLED;
}
int int_open (struct inode *inode, struct file *filp)
{
set_irq_type(PRINT_IRQ, IRQ_TYPE_LEVEL_LOW); /*设置中断为低电平触发*/
if (!request_irq (PRINT_IRQ, int_interrupt, IRQF_DISABLED, INT_DEV_NAME, NULL)) {
printk ("register ok\n");
outb (PRINT_IRQ_ENABLE_MASK, INT_CTRL_ADDR); /*使能IRQ控制*/
}
int_clear();
return 0;
}
ssize_t int_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
int readcount;
char *ptrdata;
int loop;
readcount = count / sizeof (R_INT_INFO); /*读以sizeof(R_INT_INFO)为单位的内容个数 */
if (readcount > intcount) /*读取字节数超出则读取所有现存的数据*/
readcount = intcount;
ptrdata = (char *) &intbuffer[0];
for (loop = 0; loop < readcount * sizeof(R_INT_INFO); loop++) {
printk ("loop_time = %d\n", loop);
put_user (ptrdata[loop], (char *) &buf[loop]); /*把中断时读取的数据写往用户空间*/
}
return (readcount * sizeof(R_INT_INFO));
}
ssize_t int_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
unsigned char status;
int loop;
int_clear();
for (loop = 0; loop < count; loop++) {
get_user (status, (char *) buf);
outb (status, INT_WRITE_ADDR);
}
return count;
}
int int_release (struct inode *inode, struct file *filp)
{
outb (0x00, INT_CTRL_ADDR); /*禁止中断*/
free_irq (PRINT_IRQ, NULL); /*从内核中注销中断服务函数*/
return 0;
}
struct file_operations int_fops = {
.owner = THIS_MODULE,
.read = int_read,
.write = int_write,
.open = int_open,
.release = int_release,
};
int int_init (void)
{
int result;
result = register_chrdev (INT_DEV_MAJOR, INT_DEV_NAME, &int_fops);
if (result < 0) return result;
return 0;
}
void int_exit (void)
{
unregister_chrdev (INT_DEV_MAJOR, INT_DEV_NAME);
}
module_init (int_init);
module_exit (int_exit);
MODULE_LICENSE ("Dual BSD/GPL");
应用测试程序代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEVICE_FILENAME "/dev/int_dev"
typedef struct {
unsigned long time;
}__attribute__ ((packed)) R_INT_INFO;
#define INT_BUFF_MAX 64
int main()
{
int dev;
R_INT_INFO intbuffer[INT_BUFF_MAX];
int intcount;
char buff[128];
int loop = 0;
dev = open (DEVICE_FILENAME, O_RDWR | O_NDELAY);
if (dev >= 0) {
printf ("start...\n");
buff[0] = 0x00;
write (dev, buff, 1); /*关闭LED*/
sleep(1);
buff[0] = 0xFF;
write (dev, buff, 1); /*打开LED*/
sleep (1);
buff[0] = 0x00;
write (dev, buff, 1); /*关闭LED*/
sleep (1);
buff[0] = 0xFF;
write (dev, buff, 1); /*打开LED*/
printf ("wait...input\n");
while (1) {
memset (intbuffer, 0, sizeof (intbuffer));
intcount = read (dev, (char *)&intbuffer[0], sizeof(R_INT_INFO)) / sizeof(R_INT_INFO);
if (intcount) {
while (intbuffer [loop].time != 0) {
printf ("intbuffer [%d] = %d\n", loop, intbuffer [loop]);
loop++;
}
break;
}
}
printf ("input ok...\n");
sleep (1);
memset (intbuffer, 0, sizeof(intbuffer));
printf ("read interrupt times\n");
intcount = read (dev, (char *)intbuffer, sizeof(intbuffer)) / sizeof(R_INT_INFO);
for (loop = 0; loop < intcount; loop++)
printf ("index = %d time = %ld\n", loop, intbuffer[loop].time);
printf ("led flashing...\n");
for (loop = 0; loop < 5; loop++) {
buff[0] = 0xFF;
write (dev, buff, 1);
sleep (1);
buff[0] = 0x00;
write (dev, buff, 1);
sleep (1);
}
close (dev);
} else {
printf ("open int_dev failed\n");
}
return 0;
} 说明:
在应用程序中,使用了 read() 函数,它用来读取中断的情况。在第 1 个 read() 函数中,是通过无限轮询的方式去判断中断的,如果返回的 intcount 不为 0 ,那么说明有中断发生。这里需要注意的是,read() 里的第 2 个参数使用了强制地址转换,将缓冲区的首地址强制转换为 (char *) 型。这是由于驱动程序里的 read() 函数中的 put_user() 函数的第 2个参数也是 (char *)型的。put_user()函数每次传递 1, 2, 4, 8个字节会有比较高的效率,所以这里做了类型的强制转换。如传送R_INT_INFO类型结构体时,这里虽然传送的只是一个 unsigned long类型数据,却要分为 4 次(unsigned long 共 4 字节)传输,这是考虑到,这个结构体以后有可能会扩容,所以在用 put_user() 传送时仍然强制转换为 (char *)类型做每次单字节的传输。
通过输出的运行结果可以发现,每次输出读到的中断次数不同,这是因为在接触金属引脚时发生抖动(chattering )的缘故,这会导致发生多次的中断。在实际应用中,去抖是要必须处理的一个现象。 |
|