本文转载自:https://blog.csdn.net/zqixiao_09/article/details/50858776

版权声明:本文为博主原创文章,未经博主允许不得转载。    https://blog.csdn.net/zqixiao_09/article/details/50858776
       编写驱动的第一步仍是看原理图:

可以看到,该蜂鸣器由 GPD0_0 来控制 ,查手册可知该I/O口由Time0 来控制,找到相应的寄存器:

a -- I/O口寄存器及地址

GPD0CON  0x114000a0

b -- Time0 寄存器及地址

基地址为:TIMER_BASE 0x139D0000

这些物理寄存器地址都是相邻的,我们这里用偏移量来表示:

寄存器名      地址偏移量            所需配置

TCFG0          0x0000              [7-0]     0XFF

TCFG1          0x0004              [3-0]     0X2

TCON            0x0008              [3-0]     0X2       0X9   0X0

TCNTB0        0x000C             500

TCMPB0       0x0010              250

前面已经知道,驱动是无法直接操纵物理地址的,所以这里仍需物理地址向虚拟地址的转换,用到 ioremap() 函数、writel()函数、readl()函数:

1、地址映射操作

unsigned int   *gpd0con;
void *timer_base;<span style="white-space:pre">    </span>//之所以是void类型,偏移量为4时,只是移动4个字节,方便理解
 
gpd0con = ioremap(GPD0CON,4);
timer_base = ioremap(TIMER_BASE , 0x14);
2、Time0初始化操作(这里使用的已经是虚拟地址)

这里现将数据从寄存器中读出,修改后再写回寄存器,具体寄存器操作可以移步Exynos4412裸机开发——PWM定时器:

writel((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);
writel ((readl(timer_base +TCFG0  )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0);
writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 );
3、装载数据,配置占空比

writel(500, timer_base +TCNTB0  );
writel(250, timer_base +TCMPB0 );
writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );
4、相关控制函数

void beep_on(void)
{
    writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x9 <<0),timer_base +TCON );
}
 
void beep_off(void)
{
    writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x0 <<0),timer_base +TCON );
}

下面是驱动程序,这里我们用到了 write() read() ioctl() 函数,具体解析移步:

驱动程序:beep.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
 
static int major = 250;
static int minor=0;
static dev_t devno;
static struct class *cls;
static struct device *test_device;
 
#define GPD0CON       0x114000a0
#define TIMER_BASE    0x139D0000           
#define TCFG0         0x0000               
#define TCFG1         0x0004                            
#define TCON          0x0008             
#define TCNTB0        0x000C          
#define TCMPB0        0x0010           
 
static unsigned int *gpd0con;
static void *timer_base;
#define  MAGIC_NUMBER    'k'
#define  BEEP_ON    _IO(MAGIC_NUMBER    ,0)
#define  BEEP_OFF   _IO(MAGIC_NUMBER    ,1)
#define  BEEP_FREQ   _IO(MAGIC_NUMBER   ,2)
 
static void fs4412_beep_init(void)
{
    gpd0con = ioremap(GPD0CON,4);
    timer_base = ioremap(TIMER_BASE,0x14);
    
    writel ((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);
    writel ((readl(timer_base +TCFG0  )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0);
    writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 );
 
    writel (500, timer_base +TCNTB0  );
    writel (250, timer_base +TCMPB0 );
    writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );
}
 
void fs4412_beep_on(void)
{
    writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x9 <<0),timer_base +TCON );
}
 
void fs4412_beep_off(void)
{
    writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x0 <<0),timer_base +TCON );
}
 
 
static int beep_open (struct inode *inode, struct file *filep)
{
 //    fs4412_beep_on();
    return 0;
}
 
static int beep_release(struct inode *inode, struct file *filep)
{
     fs4412_beep_off();
     return 0;
}
 
#define BEPP_IN_FREQ 100000
static void beep_freq(unsigned long arg)
{
    writel(BEPP_IN_FREQ/arg, timer_base +TCNTB0  );
    writel(BEPP_IN_FREQ/(2*arg), timer_base +TCMPB0 );
 
}
 
static long beep_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
    switch(cmd)
    {
        case BEEP_ON:
            fs4412_beep_on();
            break;
        case BEEP_OFF:
            fs4412_beep_off();
            break;
        case BEEP_FREQ:
            beep_freq( arg );
            break;
        default :
            return -EINVAL;
    }
}
 
static struct file_operations beep_ops=
{
    .open     = beep_open,
    .release = beep_release,
    .unlocked_ioctl      = beep_ioctl,
};
 
static int beep_init(void)
{
    int ret;    
    devno = MKDEV(major,minor);
    ret = register_chrdev(major,"beep",&beep_ops);
 
    cls = class_create(THIS_MODULE, "myclass");
    if(IS_ERR(cls))
    {
        unregister_chrdev(major,"beep");
        return -EBUSY;
    }
    test_device = device_create(cls,NULL,devno,NULL,"beep");//mknod /dev/hello
    if(IS_ERR(test_device))
    {
        class_destroy(cls);
        unregister_chrdev(major,"beep");
        return -EBUSY;
    }    
    fs4412_beep_init();
    return 0;
}
 
void fs4412_beep_unmap(void)
{
    iounmap(gpd0con);
    iounmap(timer_base);
}
 
static void beep_exit(void)
{
    fs4412_beep_unmap();
 
    device_destroy(cls,devno);
    class_destroy(cls);    
    unregister_chrdev(major,"beep");
    printk("beep_exit \n");
}
 
MODULE_LICENSE("GPL");
module_init(beep_init);
module_exit(beep_exit);
makefile:

ifneq  ($(KERNELRELEASE),)
obj-m:=beep.o
$(info "2nd")
else
#KDIR := /lib/modules/$(shell uname -r)/build
KDIR := /home/fs/linux/linux-3.14-fs4412
PWD:=$(shell pwd)
all:
    $(info "1st")
    make -C $(KDIR) M=$(PWD) modules
    arm-none-linux-gnueabi-gcc test.c -o beeptest
    sudo cp beep.ko beeptest /tftpboot
clean:
    rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order
endif

下面是是个简单的测试程序test.c,仅实现蜂鸣器响6秒的功能:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
 
#define  MAGIC_NUMBER    'k'
#define   BEEP_ON    _IO(MAGIC_NUMBER    ,0)
#define   BEEP_OFF   _IO(MAGIC_NUMBER    ,1)
#define   BEEP_FREQ   _IO(MAGIC_NUMBER    ,2)
 
main()
{
    int fd;
 
    fd = open("/dev/beep",O_RDWR);
    if(fd<0)
    {
        perror("open fail \n");
        return ;
    }
 
    ioctl(fd,BEEP_ON);
 
    sleep(6);
    ioctl(fd,BEEP_OFF);    
 
    close(fd);
}

这是个音乐播放测试程序,慎听!!分别为《大长今》、《世上只有妈妈好》、《渔船》,这个单独编译一下
/*
 * main.c : test demo driver
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include "pwm_music.h"
 
/*ioctl 鍛戒护*/
#define magic_number 'k'
#define BEEP_ON _IO(magic_number,0)
#define BEEP_OFF _IO(magic_number,1)
#define SET_FRE _IO(magic_number,2)
 
 
 
int main(void)
{
    int i = 0;
    int n = 2;
    int dev_fd;
    int div;
    dev_fd = open("/dev/beep",O_RDWR | O_NONBLOCK);
    if ( dev_fd == -1 ) {
        perror("open");
        exit(1);
    }
 
    for(i = 0;i<sizeof(GreatlyLongNow)/sizeof(Note);i++ )
    {
        div = (GreatlyLongNow[i].pitch);
    
        ioctl(dev_fd, SET_FRE, div);
        ioctl(dev_fd, BEEP_ON);
        usleep(GreatlyLongNow[i].dimation * 100);
        ioctl(dev_fd, BEEP_OFF);
    }
    
    for(i = 0;i<sizeof(MumIsTheBestInTheWorld)/sizeof(Note);i++ )
    {
        div = (MumIsTheBestInTheWorld[i].pitch);
        ioctl(dev_fd, SET_FRE, div);
        ioctl(dev_fd, BEEP_ON);
        
        usleep(MumIsTheBestInTheWorld[i].dimation * 100);
        ioctl(dev_fd, BEEP_OFF);
    }
 
 
 
    for(i = 0;i<sizeof(FishBoat)/sizeof(Note);i++ )
    {
        div = (FishBoat[i].pitch);
        ioctl(dev_fd, SET_FRE, div);
        ioctl(dev_fd, BEEP_ON);
        usleep(FishBoat[i].dimation * 100);
        ioctl(dev_fd, BEEP_OFF);
    }
    return 0;
}
附所用头文件:
#ifndef __PWM_MUSIC_H
#define __PWM_MUSIC_H
 
#define BIG_D
 
#define PCLK (202800000/4)
 
typedef struct
{
    int pitch;
    int dimation;
}Note;
// 1            2        3         4             5              6       7
// C             D         E          F              G             A         B
//261.6256    293.6648   329.6276 349.2282   391.9954        440        493.8833
 
//C澶ц皟
#ifdef BIG_C
#define DO 262
#define RE 294
#define MI 330
#define FA 349
#define SOL 392
#define LA  440
#define SI  494
#define TIME 6000
#endif
 
 //D澶ц皟
#ifdef BIG_D
#define DO 293
#define RE 330
#define MI 370
#define FA 349
#define SOL 440
#define LA  494
#define SI  554
#define TIME 6000
#endif
 
 
 
Note MumIsTheBestInTheWorld[]={
    //6.                //_5         //3         //5    
    {LA,TIME+TIME/2}, {SOL,TIME/2},{MI,TIME},{SOL,TIME},
 
    //1^           //6_          //_5        //6-
    {DO*2,TIME},{LA,TIME/2},{SOL,TIME/2} ,{LA,2*TIME},
    // 3      //5_        //_6           //5
    {MI,TIME},{SOL,TIME/2},{LA,TIME/2},{SOL,TIME},
    // 3         //1_        //_6,
    {MI,TIME},{DO,TIME/2},{LA/2,TIME/2},
    //5_        //_3        //2-           //2.
    {SOL,TIME/2},{MI,TIME/2},{RE,TIME*2},{RE,TIME+TIME/2},
    //_3     //5            //5_            //_6
    {MI,TIME/2},{SOL,TIME},{SOL,TIME/2},{LA,TIME/2},
    // 3        //2            //1-            //5.
    {MI,TIME},{RE,TIME},{DO,TIME*2},{SOL,TIME+TIME/2},
    //_3        //2_        //_1         //6,_
    {MI,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME/2},
    //_1        //5,--
    {DO,TIME/2},{SOL/2,TIME*3}
 
};
 
 
Note GreatlyLongNow[]={      
    // 2        3            3        3.                _2                1
    {RE,TIME}, {MI,TIME},{MI,TIME},{MI,TIME+TIME/2},{RE,TIME/2},{DO,TIME},
    //6,        1            2        1--                2            3            3
    {LA/2,TIME},{DO,TIME},{RE,TIME},{DO,TIME*3},{RE,TIME},{MI,TIME},{MI,TIME},
    //3.                _5            3            3            2            3
    {MI,TIME+TIME/2},{SOL,TIME/2},{MI,TIME},{MI,TIME},{RE,TIME},{MI,TIME},
    //3--        5            6            6          6.                _5
    {MI,TIME*3},{SOL,TIME},{LA,TIME},{LA,TIME},{LA,TIME+TIME/2},{SOL,TIME/2},
    // 3        3        5                6        5---            2            3
    {MI,TIME},{MI,TIME},{SOL,TIME},{LA,TIME},{SOL,TIME*3},{RE,TIME},{MI,TIME},
    // 3        2.                _3                3          2            3
    {MI,TIME},{RE,TIME+TIME/2},{MI,TIME/2},{MI,TIME},{RE,TIME},{MI,TIME},
    //6,        1_              _6,              6,-
    {LA/2,TIME},{DO,TIME/2},{LA/2,TIME/2},{LA/2,TIME*2},
    //2_        _2            2_                _1            6,
    {RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},
    //2_        _2            2_                _1              6,
    {RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},
    // 2        3        1            2.                    _3            5
    {RE,TIME},{MI,TIME},{DO,TIME},{RE,TIME+TIME/2},{MI,TIME/2},{SOL,TIME},
    //6_        _6                6_            _5            3
    {LA,TIME/2},{LA,TIME/2},{LA,TIME/2},{SOL,TIME/2},{MI,TIME},
    //2_        _2            2_                _1            6,
    {RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},
    //6,        5,.                      _6,             6,--
    {LA/2,TIME},{SOL/2,TIME+TIME/2},{LA/2,TIME/2},{LA/2,TIME*3},
    //2_        _2            2_                _1            6,
    {RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},
    //2_        _2            2_                _1              6,
    {RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},
    // 2        3        1            2.                    _3            5
    {RE,TIME},{MI,TIME},{DO,TIME},{RE,TIME+TIME/2},{MI,TIME/2},{SOL,TIME},
    //6_        _6                6_            _5            3
    {LA,TIME/2},{LA,TIME/2},{LA,TIME/2},{SOL,TIME/2},{MI,TIME},
    //2_        _2            2_                _1            6,
    {RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},
    //6,        5,.                      _6,             6,--
    {LA/2,TIME},{SOL/2,TIME+TIME/2},{LA/2,TIME/2},{LA/2,TIME*3}
 
};
Note FishBoat[]={ //3.                _5            6._                    =1^             6_
    {MI,TIME+TIME/2},{SOL,TIME/2},{LA,TIME/2+TIME/4},{DO*2,TIME/4},{LA,TIME/2},
    //_5            3 -.        2          1.             _3             2._
    {SOL,TIME/2},{MI,TIME*3},{RE,TIME},{DO,TIME+TIME/2},{MI,TIME/2},{RE,TIME/2+TIME/4},
    //=3            2_            _1         2--            3.                _5
    {MI,TIME/4},{RE,TIME/2},{DO,TIME/2},{RE,TIME*4},{MI,TIME+TIME/2},{SOL,TIME/2},
    // 2        1        6._                    =1^                 6_            _5
    {RE,TIME},{DO,TIME},{LA,TIME/2+TIME/4},{DO*2,TIME/4},{LA,TIME/2},{SOL,TIME/2},
    //6-         5,.                    _6,            1._                    =3
    {LA,TIME*2},{SOL/2,TIME+TIME/2},{LA/2,TIME/2},{DO,TIME/2+TIME/4},{MI,TIME/4},
    //2_            _1         5,--
    {RE,TIME/2},{DO,TIME/2},{SOL/2,TIME*4},
    //3.                _5            6._                    =1^            6_
    {MI,TIME+TIME/2},{SOL,TIME/2},{LA,TIME/2+TIME/4},{DO*2,TIME/4},{LA,TIME/2},
    //_5            3-.            5_            _6            1^_                   _6
    {SOL,TIME/2},{MI,TIME*3},{SOL,TIME/2},{LA,TIME/2},{DO*2,TIME+TIME/2},{LA,TIME/2},
    //5._                    =6            5_          _3            2--
    {SOL,TIME/2+TIME/4},{LA,TIME/4},{SOL,TIME/2},{MI,TIME/2},{RE,TIME*4},
    //3.                _5            2._                    =3            2_            _1
    {MI,TIME+TIME/2},{SOL,TIME/2},{RE,TIME/2+TIME/4},{MI,TIME/4},{RE,TIME/2},{DO,TIME/2},
    //6._                =1^                6_            _5            6-            1.
    {LA,TIME/2+TIME/4},{DO*2,TIME/4},{LA,TIME/2},{SOL,TIME/2},{LA,TIME*2},{DO,TIME+TIME/2},
    //_2           3_            _5                2_            _3            1--
    {RE,TIME/2},{MI,TIME/2},{SOL,TIME/2},{RE,TIME/2},{MI,TIME/2},{DO,TIME*4}
};
#endif

编译好程序后
# insmod beep.ko

#mknod /dev/beep c 250 0

#./music

便会听到悦耳的音乐了!

Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动【转】的更多相关文章

  1. 迅为4412开发板Linux驱动教程——编写简单应用调用驱动

    Linux驱动教程:http://pan.baidu.com/s/1c0hljUS 编写简单应用调用驱动--头文件 • 打印头文件 – include <stdio.h>调用打印函数pri ...

  2. Linux 下wifi 驱动开发(四)—— USB接口WiFi驱动浅析

    源: Linux 下wifi 驱动开发(四)—— USB接口WiFi驱动浅析

  3. [Windows驱动开发](二)基础知识——数据结构

    本节主要介绍驱动开发的一些基础知识. 1. 驱动程序的基本组成 1.1. 最经常见到的数据结构 a. DRIVER_OBJECT驱动对象 // WDK中对驱动对象的定义 // 每个驱动程序都会有一个唯 ...

  4. 《linux设备驱动开发详解》笔记——12linux设备驱动的软件架构思想

    本章重点讲解思想.思想.思想. 12.1 linux驱动的软件架构 下述三种思想,在linux的spi.iic.usb等复杂驱动里广泛使用.后面几节分别对这些思想进行详细说明. 思想1:驱动与设备分离 ...

  5. Linux 下wifi 驱动开发(三)—— SDIO接口WiFi驱动浅析

    SDIO-Wifi模块是基于SDIO接口的符合wifi无线网络标准的嵌入式模块,内置无线网络协议IEEE802.11协议栈以及TCP/IP协议栈.可以实现用户主平台数据通过SDIO口到无线网络之间的转 ...

  6. [翻译]Behavior-Driven Development (BDD)行为驱动开发(二)

    测试驱动开发体现了开发人员对软件产品各部分运作方式的理解,而行为驱动开发则关注于开发人员对软件产品最终表现的行为的预期. 行为驱动开发 TDD更像是一种范式而不是一个过程.它描述了一种先编写测试,然后 ...

  7. WindowsNT设备驱动程序开发基础

    一.背景介绍 1.1WindowsNT操作系统的组成1.1.1用户模式(UserMode)与内核模式(KernelMode) 从Intel80386开始,出于安全性和稳定性的考虑,该系列的CPU可以运 ...

  8. 【Spring注解驱动开发】二狗子让我给他讲讲@EnableAspectJAutoProxy注解

    写在前面 最近,二狗子入职了新公司,新入职的那几天确实有点飘.不过慢慢的,他发现他身边的人各个身怀绝技啊,有Spring源码的贡献者,有Dubbo源码的贡献者,有MyBatis源码的贡献者,还有研究A ...

  9. C++第三十九篇 -- 研究一下Windows驱动开发(二)-- 驱动程序中重要的数据结构

    数据结构是计算机程序的核心,I/O管理器定义了一些数据结构,这些数据结构是编写驱动程序时所必须掌握的.驱动程序经常要创建和维护这些数据结构的实例. 一.驱动对象(DRIVER_OBJECT) 每个驱动 ...

随机推荐

  1. DateTime.Compare(t1,t2)比较两个日期大小

    DateTime.Compare(t1,t2)比较两个日期大小,排前面的小,排在后面的大,比如:2011-2-1就小于2012-3-2返回值小于零:  t1 小于 t2. 返回值等于零 : t1 等于 ...

  2. man查看帮助命令

    man -h/-help 1.在man命令帮助信息的界面中,所包含的常用操作按键及其用途 按键 用处 空格键 向下翻一页 PaGe down 向下翻一页 PaGe up 向上翻一页 home 直接前往 ...

  3. python windows环境下安装

    下载python安装包,双击安装后, 在cmd中输入python 若无反应, 在cmd设置环境变量 变量 : set PATH=C:\...\...\...[python的编译器的路径]:%PATH% ...

  4. spring boot 知识点

    spring boot 好处 1. 简化配置,spring boot 提供了默认配置 例如 日志 默认logback日志  info级别 2. 简化部署,内嵌容器,tomcat,jetty,直接部署j ...

  5. Windows server 2008 R2实现多用户远程连接 (转)

    经常使用远程桌面的朋友可能会注意到,Windows server 2008 R2中,远程桌面最多只允许两个人远程连接,第三个人就无法连接过去,但是生产环境中有一些服务器可能有许多人需要连接上去,而微软 ...

  6. css 箭头

    .toTop{ width: 2.5rem; height: 2.5rem; background-color: rgba(228,228,228,.6); position: fixed; bott ...

  7. python mmap对象

    ----使用内存映射的原因 为了随机访问文件的内容,使用mmap将文件映射到内存中是一个高效和优雅的方法.例如,无需打开一个文件并执行大量的seek(),read(),write()调用,只需要简单的 ...

  8. API gateway 之 kong 安装

    kong安装: https://getkong.org/install/centos/ 下载指定版本rpm: wget https://bintray.com/kong/kong-community- ...

  9. FAQ About WOYO PDR007 Dent Removal Heat Induction System

    WOYO PDR 007 is a dent repair tool for auto maintence. WOYO PDR007 Auto Body Paintless Dent Repair K ...

  10. Python Selenium 常用方法总结

    selenium Python 总结一些工作中可能会经常使用到的API. 1.获取当前页面的Url 方法:current_url  实例:driver.current_url    2.获取元素坐标 ...