《LINUX设备驱动程序》学习之与硬件通信(并行接口)实例

在学习这一节内容时,我根据书本写了一个并口驱动程序,然后用一个LED发光二极管、一个电阻以及一些导线和电脑主机的并口连接了一条回路,最后通过测试程序控制LED灯的开启、关闭,验证了并口驱动程序的正确性。整个过程没出现什么意外状况,但是有网友指出,这个实验是非常危险的,所以如果你想尝试,得足够了解可能会出现的意外情况。

1. 并行接口(并口)简介

并行接口是常见的一种I/O接口,通常主机上是25针D型接口。其引脚如下:

 

为操作并行口,SPP(Standard Parallel Port标准并行接口)定义了寄存器,并映射到PC机的I/O空间。寄存器包括了以并口地址为基址的3块连续 的寄存器,并口地址常见为3BCH、378H和278H,其中都包括数据、状态和控制寄存器,分别对应数据、状态和控制信号线操作,通常称为数据端口、状 态端口和控制端口。打印机卡1的地址常为378H,其中数据口0378H、状态口0379H、控制口037AH;打印机卡2的地址常为278H,其中数据 口0278H、状态口0279H、控制口027AH。支持新的IEEE 1284标准的并口,使用8到16个寄存器,地址为378H or 278H,即插即用(Plug and Play)兼容的的并口适配器也可以重新加载。

并行接口输出的是TTL标准逻辑电平,其中,标准TTL为+5V,低压TTL 为+3.3V。一般情况下,小于0.8V是低电平,大于2V是高电平。

2. 驱动程序

  2.1 驱动程序源码

  

复制代码
/*ParaPortDEV.c*/
/*Created by Chung-shu on November 26th, 2012*/
#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/ioport.h>

#define DEV_NAME "ParaPortDEV"
#define PARA_PORT_DATA_REGISTER_ADDR 0x0378
#define PARA_PORT_STATUS_REGISTER_ADDR 0x0379

MODULE_LICENSE ("GPL");

int para_port_open(struct inode *inode, struct file *filp);
int para_port_release(struct inode *inode, struct file *filp);
ssize_t para_port_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t para_port_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos);

int dev_major=240;
int dev_minor=0;

struct file_operations para_port_fops =
{
    .owner   = THIS_MODULE,
    .read    = para_port_read,
    .write   = para_port_write,
    .open    = para_port_open,
    .release = para_port_release,
};

int para_port_init(void)
{
    int result;

    result = register_chrdev(dev_major, DEV_NAME, &para_port_fops);
    if (result < 0)
    {
        printk("register character device error!\n");
        return result;
    }

    if(request_region(PARA_PORT_DATA_REGISTER_ADDR, 3, DEV_NAME)==NULL)
    {
        printk("register IO port error!\n");
        exit(1);
    }
    return 0;
}

void para_port_exit(void)
{
    release_region(PARA_PORT_DATA_REGISTER_ADDR, 3);
    unregister_chrdev(dev_major, DEV_NAME);
}

int para_port_open(struct inode *inode, struct file *filp)
{
    return 0;
}

int para_port_release(struct inode *inode, struct file *filp)
{
    return 0;
}

ssize_t para_port_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
    unsigned char status;
    int           i;

    for (i = 0; i <count; i++)
    {
        status = inb(PARA_PORT_STATUS_REGISTER_ADDR);
        rmb();
        put_user(status, (char *)(buf+i));
    }

    return count;
}

ssize_t para_port_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
    unsigned char data;
    int           i;

    for (i = 0; i < count; i++)
    {
        get_user(data, (char *)(buf+i));
        outb(data, PARA_PORT_DATA_REGISTER_ADDR);
        wmb();
    }

    return count;
}

module_init(para_port_init);
module_exit(para_port_exit);
复制代码

 

 

 

  2. 2 Makefile源码

复制代码
obj-m:=ParaPortDEV.o
#KERNELDIR:=/lib/modules/2.6.38.8/build
KERNELDIR:=/usr/src/linux-headers-2.6.38-8-generic
PWD:=$(shell pwd)

modules:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
    rm -f *.o
复制代码

  2.3 安装驱动程序步骤

复制代码
~/linux_study/para_port$ make

~/linux_study/para_port$ sudo insmod ParaPortDEV.ko

~/linux_study/para_port$ sudo mknod /dev/ParaPortDEV c 240 0

~/linux_study/para_port$ sudo chgrp staff /dev/ParaPortDEV

~/linux_study/para_port$ sudo chmod 664 /dev/ParaPortDEV
复制代码

 

3. 测试程序

复制代码
/*test_para_port_dev.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#define DEV_NAME "/dev/ParaPortDEV"

int main(void)
{
    int i, dev;
    char buff[10];

    dev=open(DEV_NAME, O_RDWR|O_NDELAY);

    if(dev==-1)
    {
        printf("open error: %s.\n", strerror(errno));
        return -1;
    }

    printf("read from para port:\n");
    for(i=0; i<10; i++)
    {
        if(read(dev, buff+i, 1)==-1)
        {
            printf("%dth read error: %s.\n", i, strerror(errno));
            break;
        }
        sleep(1);
        printf("data = %0X\n", buff[i]);
    }

    printf("write to para port:\n");
    for(i=0; i<10; i++)
    {
        /*turn the light on*/
        buff[i] = 0xFF;
        if(write(dev, buff+i, 1)==-1)
        {
            printf("%dth write error: %s.\n", i, strerror(errno));
            break;
        }
        printf("light on\n");
        sleep(1);

        /*turn the light off*/
        buff[i] = 0x00;
        if(write(dev, buff+i, 1)==-1)
        {
            printf("%dth write error: %s.\n", i, strerror(errno));
            break;
        }
        printf("light off\n");
        sleep(1);
    }

    close(dev);

    return 0;
}
复制代码

 

4. 连接电路

在并口上的 2 脚和 25 脚分别引出两根引线,2 脚接 LED 灯的长脚,25 脚接 LED 灯的短脚,再在电路上串联一个500欧左右的电阻,电路图如下:

 

5. 测试

~/linux_study/para_port$ sudo ./test_para_port_dev

 在应用程序中,使用 read() 系统调用不断尝试读取并口设备状态寄存器。如果并口上没有外接设备,则会打印信息:

复制代码
read from para port:
data = 7F
data = 7F
data = 7F
data = 7F
data = 7F
data = 7F
data = 7F
data = 7F
data = 7F
data = 7F
复制代码

当通过引线将13脚和25脚短接,则13引脚有低电平输入,这时候输出:

复制代码
read from para port:
data = 6F
data = 6F
data = 6F
data = 6F
data = 6F
data = 6F
data = 6F
data = 6F
data = 6F
data = 6F
复制代码

然后,通过调用 write() 函数向并口设备数据寄存器写数据,使 LED 灯开灯、关灯10次,最后关闭设备文件并退出。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值