您的位置:首页 > 运维架构 > Linux

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

2012-11-26 16:22 381 查看
注意:

  在学习这一节内容时,我根据书本写了一个并口驱动程序,然后用一个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, ¶_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次,最后关闭设备文件并退出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: