1 驱动程序的编写
驱动是LINUX开发的必经之路,应用层对底层的调用经过了库与内核,内核以下才是驱动层,当你在应用程序运行对底层的控制时,驱动程序为你的控制提供了接口,或者说是策略。
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/device.h> #include <asm/io.h>
#define DEVICE_NAME "PWM_MOUDLE" #define PWM_MOUDLE_PHY_ADDR 0x6CA00000 //This Address is based XPS 这个地址ISE EDK中分配的地址就是硬件的东东啦
/* 描写叙述驱动程序的一些信息,不是必须的 */ MODULE_AUTHOR("Xilinx XUP"); // 驱动程序的作者 MODULE_DESCRIPTION("PWM moudle dirver"); // 一些描写叙述信息 MODULE_VERSION("v1.0"); MODULE_LICENSE("GPL"); // 遵循的协议
static int pwm_driver_major; static struct class* pwm_driver_class = NULL; static struct device* pwm_driver_device = NULL; unsigned long pwm_fre_addr = 0; //pwm moulde's frequency visual address unsigned long pwm_duty_addr = 0; //pwm moulde's duty visual address static long frequency=0;
/*这个结构是字符设备驱动的核心*/
static struct file_operations pwm_driver_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自己主动创建的__this_module变量 在Export.h (c:usersadministratordesktoplinux-3.3-digilentincludelinux):#define THIS_MODULE (&__this_module)*/ };
static ssize_t sys_pwm_frequency_set (struct device* dev, struct device_attribute* attr, const char* buf, size_t count) { long value = 0; int i; frequency=0; outl(value, pwm_fre_addr); //close pwm moudle before we modfiy the frequency for (i = 0; i < count-1; i++){ frequency *= 10; frequency += buf[i] - '0'; } if(value>100000000) value=100000000; value=100000000/frequency; // 100Mhz/frequency 100Mhz is set by XPS outl(value, pwm_fre_addr); return count; }
static ssize_t sys_pwm_duty_set (struct device* dev, struct device_attribute* attr, const char* buf, size_t count) //duty cycle { long value = 0; int i; // outl(value, pwm_duty_addr); //close pwm moudle before we modfiy the duty cycle for (i = 0; i < count-1; i++){ value *= 10; value += buf[i] - '0'; } if (value>100) value=100; value=100000000/frequency*value/100; if (value!= 0) value = value | 0x80000000; outl(value, pwm_duty_addr); return count; }
static DEVICE_ATTR(pwm_frequency, S_IWUSR, NULL, sys_pwm_frequency_set); static DEVICE_ATTR(pwm_duty, S_IWUSR, NULL, sys_pwm_duty_set);
/* 运行insmod xxx.ko时就会运行pwm_driver_module_init()函数 *
static int __init pwm_driver_module_init(void) { int ret; /* 注冊字符设备驱动程序 * 參数为主设备号、设备名字、file_operations结构; * 这样,主设备号就和详细的file_operations结构联系起来了, * 操作主设备为BUTTON_MAJOR的设备文件时,就会调用s3c24xx_buttons_fops中的相关成员函数 * BUTTON_MAJOR能够设为0,表示由内核自己主动分配主设备号 */ pwm_driver_major=register_chrdev(0, DEVICE_NAME, &pwm_driver_fops);//内核注冊设备驱动 if (pwm_driver_major < 0){ printk("failed to register device. "); return -1; } pwm_driver_class = class_create(THIS_MODULE, "pwm_driver"); //创建PWM设备类 if (IS_ERR(pwm_driver_class)){ printk("failed to create pwm moudle class. "); unregister_chrdev(pwm_driver_major, DEVICE_NAME); return -1; }
pwm_driver_device = device_create(pwm_driver_class, NULL, MKDEV(pwm_driver_major, 0), NULL, "pwm_device"); //利用pwm_driver设备类创建一个pwm_device if (IS_ERR(pwm_driver_device)){ printk("failed to create device . "); unregister_chrdev(pwm_driver_major, DEVICE_NAME); return -1; } ret = device_create_file(pwm_driver_device, &dev_attr_pwm_frequency); //在pwm_device设备中创建frequency与duty两个文件 if (ret < 0) printk("failed to create pwm_frequency endpoint "); ret = device_create_file(pwm_driver_device, &dev_attr_pwm_duty); if (ret < 0) //将pwm模块的物理地址映射到虚拟地址上 也就是EDK中分配的地址 printk("failed to create pwm_duty endpoint "); pwm_fre_addr = (unsigned long)ioremap(PWM_MOUDLE_PHY_ADDR, sizeof(u32));//To get Custom IP--PWM moudle's virtual address pwm_duty_addr = pwm_fre_addr+4; printk(" pwm driver initial successfully! "); return 0; }
/*
运行rmmod xxx.ko时就会运行pwm_driver_module_exit()函数
*/
static void __exit pwm_driver_module_exit(void) { device_remove_file(pwm_driver_device, &dev_attr_pwm_frequency); device_remove_file(pwm_driver_device, &dev_attr_pwm_duty); device_destroy(pwm_driver_class, MKDEV(pwm_driver_major, 0)); class_unregister(pwm_driver_class); class_destroy(pwm_driver_class); unregister_chrdev(pwm_driver_major, DEVICE_NAME); printk("pwm module exit. "); }
/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(pwm_driver_module_init); module_exit(pwm_driver_module_exit);
2驱动程序的编译
makefile编写
ifneq ($(KERNELRELEASE),) obj-m := pwm_driver.o else KERNEL_DIR := <YOUR_DIR>/ZedBoard/Kernel/Digilent-linux-3.3 PWD := $(shell pwd) all: $(MAKE) -C $(KERNEL_DIR) SUBDIRS=$(PWD) modules ARCH=arm clean: rm *.o *.ko *.mod.c endif
<YOUR_DIR>/ZedBoard/Kernel/Digilent-linux-3.3 是你的路径
最后make 生成pwm_driver.ko 复制到zedboard文件系统上
3 驱动程序的測试
载入驱动
insmod pwm_driver.ko在/dev/ 下能够找到我们注冊的设备 pwm_device
进入/sys/class/..文件夹
在zedboard 的shell上运行 echo 1000 > pwm_frequency
echo 50 > pwm_duty