参考链接:
(1)https://www.cnblogs.com/chungshu/archive/2012/11/26/2789257.html
(2)https://blog.csdn.net/bg2bkk/article/details/8946424
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. 实现功能
用一个LED发光二极管、一个电阻以及一些导线和电脑主机的并口连接了一条回路,最后通过测试程序控制LED灯的开启、关闭,验证了并口驱动程序的正确性。
3. 驱动程序源码
(1)parport_drv.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"parport_drv.h"
/*****************************************************************************************/
#define Drv_major 240
#define Drv_name "parport_drv"
#define Drv_read_addr 0x379
#define Drv_write_addr 0x378
/*****************************************************************************************/
MODULE_LICENSE ("GPL");
/*****************************************************************************************/
int parport_open(struct inode *inode, struct file *filp);
ssize_t parport_write(struct file *filp, const char *buf, size_t count, loff_t *f_ops);
ssize_t parport_read(struct file *filp, char *buf, size_t count, loff_t *f_ops) ;
long parport_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ;
int parport_release(struct inode *inode, struct file *filp) ;
/*****************************************************************************************/
struct file_operations parport_fops = {
.owner = THIS_MODULE,
.write = parport_write,
.read = parport_read,
.open = parport_open,
.unlocked_ioctl = parport_ioctl,
.release= parport_release,
};
/*****************************************************************************************/
int parport_open(struct inode *inode, struct file *filp)
{
printk(KERN_ALERT "open the parport_dev\n");
return 0;
}
/*****************************************************************************************/
ssize_t parport_write(struct file *filp, const char *buf, size_t count, loff_t *f_ops)
{
unsigned char status;
int loop;
for(loop = 0; loop < count; loop++)
{
get_user(status, (char *)buf);
outb(status, Drv_write_addr);
}
return count;
}
/*****************************************************************************************/
ssize_t parport_read(struct file *filp, char *buf, size_t count, loff_t *f_ops)
{
unsigned char status;
int loop;
for(loop = 0; loop < count; loop++)
{
status = inb(Drv_read_addr);
put_user(status, (char *) &buf[loop]);
}
return count;
}
/*****************************************************************************************/
long parport_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int loop;
struct dat data;
switch(cmd)
{
case PARPORT_WRITE:
// outb(status, Drv_write_addr);
copy_from_user(&data, (struct dat *)arg, sizeof(data));
printk(KERN_ALERT "out put %d\n",data.loop);
for(loop = 0; loop < data.loop; loop ++)
{
printk(KERN_ALERT "the %dth loop, write %d\n",loop,data.buf[loop]);
outb(data.buf[loop], Drv_write_addr);
wmb();
}
break;
case PARPORT_CLOSE:
outb(0x00, Drv_write_addr);
wmb();
break;
}
return 0;
}
/*****************************************************************************************/
int parport_release(struct inode *inode, struct file *filp)
{
printk(KERN_ALERT "close the module parport_dev\n");
return 0;
}
/*****************************************************************************************/
int parport_init(void)
{
int result;
result = register_chrdev(Drv_major, Drv_name, &parport_fops);
if(result < 0)
{
printk("register charactre devices error!\n");
return result;
}
printk(KERN_ALERT "hello the module parport_dev\n");
return 0;
}
/*****************************************************************************************/
void parport_exit(void)
{
printk(KERN_ALERT "exit the module parport_drv\n");
unregister_chrdev(Drv_major, Drv_name);
}
/*****************************************************************************************/
module_init(parport_init);
module_exit(parport_exit);
/*****************************************************************************************/
(2) parport_drv.h
#ifndef _PARPORT_DRV_H
#define _PARPORT_DRV_H
#define PARPORT_WRITE 1
#define PARPORT_CLOSE 2
struct dat{
int loop;
unsigned char *buf;
};
#endif
(3)模块编译Makefile
# To build modules outside of the kernel tree, we run "make"
# in the kernel source tree; the Makefile these then includes this
# Makefile once again.
# This conditional selects whether we are being included from the
# kernel Makefile or not.
ifeq ($(KERNELRELEASE),)
# Assume the source tree is where the running kernel was built
# You should set KERNELDIR in the environment if it's elsewhere
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
# The current directory is passed to sub-makes as argument
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
else
# called from kernel build system: just declare what our modules are
obj-m := parport_drv.o
endif
(4)模块加载shell脚本 : load_rdwrdev
#!/bin/sh
insmod parport_drv.ko
mknod /dev/parport_drv c 240 0
chgrp staff /dev/parport_drv
chmod 664 /dev/parport_drv
(5)模块卸载shell脚本 :unload_rdwrdev
#!/bin/sh
rmmod parport_drv.ko
rm /dev/parport_drv
(6)测试代码
par_test.c
#include
#include
#include
#include
#include
#include
#include "parport_drv.h"
/*****************************************************************************************/
#define DEVICE_NAME "/dev/parport_drv"
/*****************************************************************************************/
int main()
{
int fd;
char buf[128];
int loop;
fd = open(DEVICE_NAME, O_RDWR | O_NDELAY);
if(fd < 0)
{
perror("open device");
exit(1);
}
else
{
int i;
int arg=0x99;
unsigned char buf[255];
struct dat da;
da.loop = 4;
da.buf = (unsigned char *)malloc(5 * sizeof(unsigned char));
for(i = 0;i< da.loop; i++)
da.buf[i] = i*2+1;
for(i=0;i
ioctl(fd, PARPORT_WRITE,&da);
sleep(1);
ioctl(fd, PARPORT_CLOSE);
sleep(1);
close(fd);
}
return 0;
}
/*****************************************************************************************/
(5) 测试代码 编译命令: gcc -o par_test par_test.c
4. 实例运行方法
(1)建立6个文件并拷贝源码:parport_drv.c, parport_drv.h, Makefile, par_test.c, load_rdwrdev, unload_rdwrdev
(2)运行:Makefile 生成 parport_drv.o
(3)运行模块加载shell脚本:sudo bash load_rdwrdev
(4)编译测试代码文件 : gcc -o par_test par_test.c
(5)运行: sudo ./par_test
(6) 运行模块卸载shell脚本:sudo bash unload_rdwrdev
5. 实验现象
并口D2引脚示波器信号波形