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:\users\administrator\desktop\linux-3.3-digilent\include\linux):#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.\n");
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.\n");
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 .\n");
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\n"); ret = device_create_file(pwm_driver_device, &dev_attr_pwm_duty);
if (ret < 0) //将pwm模块的物理地址映射到虚拟地址上 也就是EDK中分配的地址
printk("failed to create pwm_duty endpoint\n"); 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!\n");
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.\n");
}

/* 这两行指定驱动程序的初始化函数和卸载函数 */
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

zedboard 驱动理解的更多相关文章

  1. 基于Minifilter框架的文件过滤驱动理解

    概述 Minifilter即File System Minifilter Drivers,是Windows为了简化第三方开发人员开发文件过滤驱动而提供的一套框架,这个框架依赖于一个称之为Filter ...

  2. DDD领域驱动理解

    在理解领域驱动的时候,网上很多大谈理论的文章,这种对于初学者不是太容易接受.根据我自己的学习经历,建议按照如下几个步骤学习: 粗略的看一遍领域驱动的理论,做到心中有形,知道领域驱动是什么,解决什么问题 ...

  3. Binder驱动理解

    1.Binder的三层架构 2.BC.BR的理解 通信模型 Binder协议包含在IPC数据中,分为两类: BINDER_COMMAND_PROTOCOL:binder请求码,以"BC_&q ...

  4. 宋宝华:Linux设备驱动框架里的设计模式之——模板方法(Template Method)

    本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者: 宋宝华 来源: 微信公众号linux阅码场(id: linuxdev) 前言 <设计模式>这本经典 ...

  5. 安卓高级6 SnackBar

    引言 文/李牧羊(简书作者) 原文链接:http://www.jianshu.com/p/2654e6bda3b1 著作权归作者所有,转载请联系作者获得授权,并标注"简书作者". ...

  6. 适合初学者的嵌入式Linux计划

    俗话说万事开头难,刚开始的时候,你是否根本就不知如何开始,上网查资料被一堆堆新名词搞的找不到北,去图书馆看书也是找不到方向?又是arm,又是linux,又是uboot头都大了,不知道自己究竟从哪里开始 ...

  7. zedboard上首个驱动实践——Led

    // led驱动 *myled.c*//头文件 #include<linux/module.h> //最基本的文件,支持动态添加和卸载模块 #include<linux/kernel ...

  8. 浅谈我对DDD领域驱动设计的理解

    从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...

  9. IDDD 实现领域驱动设计-理解领域和子域

    上一篇:<IDDD 实现领域驱动设计-一个简单业务用例的回顾和理解> 在<实现领域驱动设计>第二章的前半部分内容中,提到领域和子域的概念,并且作者把这两者又进行了细致的区分,其 ...

随机推荐

  1. 项目源码--JSP在线客服系统(SSH框架技术)源码

    下载源码 技术要点: 1.网站开发技术框架 2.SSH技术框架(Struct,Spring,Hibrnate) 3.JSP技术框架 4.MYSQL数据库数据存储 5.即时通讯技术 6.源码带详细的中文 ...

  2. 【优先队列】HDU 1873——看病找医生

    来源:点击打开链接 看路径记录的BFS之前,再看一遍优先队列的用法. 优先队列的排序规则可以用运算符重载的方式完成,通常意义下,应该用friend bool operator <进行重载. #i ...

  3. ubuntu thrift 0.9.3编译安装

    Table of Contents 1. 下载thrift源代码 2. 编译并安装 3. 运行测试程序 4. 安装 1 下载thrift源代码 git clone https://git-wip-us ...

  4. __attribute__((unused))

    在gcc手册中找到了有关的解释: unused:This attribute, attached to a function, means that the function is meant to ...

  5. Long Long Message 后缀数组入门题

    Long Long Message Time Limit: 4000MS   Memory Limit: 131072K Total Submissions: 22564   Accepted: 92 ...

  6. shell 获取cpu使用率

    1.shell脚本获取主机每颗CPU使用率的方法. #!/bin/bash interval= cpu_num=`-] -c` start_idle=() start_total=() cpu_rat ...

  7. .Net MVC 4 Web Api 输出Json 格式

    1.Global 中增加json输出 GlobalConfiguration.Configuration.Formatters.JsonFormatter.MediaTypeMappings.Add( ...

  8. mysql-完整性约束条件

    PRIMARY :   主键 AUTO_INCREMENT  : 自增长 FOREIGN KEY : 外键 NOT NULL : 非空 UNIQUE KEY : 唯一 DEFAULT :  默认值 主 ...

  9. 【WinAPI】User32.dll注释

    #region User32.dll 函数 /// <summary> /// 该函数检索一指定窗口的客户区域或整个屏幕的显示设备上下文环境的句柄,以后可以在GDI函数中使用该句柄来在设备 ...

  10. C#自定义时间显示格式

    string time = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); 下面是常见的一些日期时间显示格式 标准的For ...