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. matplotlib作图中文显示问题

    def set_ch(): from pylab import mpl mpl.rcParams['font.sans-serif'] = ['FangSong'] # 指定默认字体 mpl.rcPa ...

  2. org.apache.hadoop.conf-Configured

    org.apache.hadoop.conf中的最后一个类,也是这个包中以后用的最频繁的一个,Configurable算是肉体,Configuration算是灵魂吧 package org.apach ...

  3. UIImage图片处理,旋转、截取、平铺、缩放等操作

    来源:iOS_小松哥 链接:http://www.jianshu.com/p/9ab1205f5166 有时候我们需要处理图片,比如改变大小,旋转,截取等等,所以今天说一说图片处理相关的一些操作. 本 ...

  4. Linux 下configure 参数配置与软件的安装与卸载

    Linux环境下的软件安装,并不是一件容易的事情:如果通过源代码编译后在安装,当然事情就更为复杂一些:现在安装各种软件的教程都非常普遍:但万变不离其中,对基础知识的扎实掌握,安装各种软件的问题就迎刃而 ...

  5. SOA与C#

    What is SOA? SOA or Service oriented architecture is an architecture style for building business app ...

  6. MySQL(24):事务的隔离级别

    1. 事务的隔离级别引入: 数据库是多线程并发访问的,所以很容易出现多个线程同时开启事务的情况,这样的就会出现脏读.重复读以及幻读的情况.在数据库操作中,为了有效保证并发读取数据的正确性,需要为事务设 ...

  7. JavaScript工厂模式代码

    function createPerson(name,age,job){ var o=new Object(); o.name=name; o.age=age; o.job=job; o.sayNam ...

  8. C#中怎么在EXCEL中的单元格中画斜线啊 ??

    Code Snippet 做法: 1,先添加引用COM,找 Excel 2,using Excel = Microsoft.Office.Interop.Excel; 3, 代码 private Ex ...

  9. C#中常用的排序算法的时间复杂度和空间复杂度

    常用的排序算法的时间复杂度和空间复杂度   常用的排序算法的时间复杂度和空间复杂度 排序法 最差时间分析 平均时间复杂度 稳定度 空间复杂度 冒泡排序 O(n2) O(n2) 稳定 O(1) 快速排序 ...

  10. (一)u-boot2013.01.01 for TQ210:《Uboot简介》

    一直想写一个s5pv210硬件平台的u-boot的移植文档,但一直都忙着没时间写.先写一些u-boot的脚本分析吧,包括makefile,mkconfig,config.mk,主要侧重于语法句意的分析 ...