Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动【转】
本文转载自: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 设备驱动【转】的更多相关文章
- 迅为4412开发板Linux驱动教程——编写简单应用调用驱动
Linux驱动教程:http://pan.baidu.com/s/1c0hljUS 编写简单应用调用驱动--头文件 • 打印头文件 – include <stdio.h>调用打印函数pri ...
- Linux 下wifi 驱动开发(四)—— USB接口WiFi驱动浅析
源: Linux 下wifi 驱动开发(四)—— USB接口WiFi驱动浅析
- [Windows驱动开发](二)基础知识——数据结构
本节主要介绍驱动开发的一些基础知识. 1. 驱动程序的基本组成 1.1. 最经常见到的数据结构 a. DRIVER_OBJECT驱动对象 // WDK中对驱动对象的定义 // 每个驱动程序都会有一个唯 ...
- 《linux设备驱动开发详解》笔记——12linux设备驱动的软件架构思想
本章重点讲解思想.思想.思想. 12.1 linux驱动的软件架构 下述三种思想,在linux的spi.iic.usb等复杂驱动里广泛使用.后面几节分别对这些思想进行详细说明. 思想1:驱动与设备分离 ...
- Linux 下wifi 驱动开发(三)—— SDIO接口WiFi驱动浅析
SDIO-Wifi模块是基于SDIO接口的符合wifi无线网络标准的嵌入式模块,内置无线网络协议IEEE802.11协议栈以及TCP/IP协议栈.可以实现用户主平台数据通过SDIO口到无线网络之间的转 ...
- [翻译]Behavior-Driven Development (BDD)行为驱动开发(二)
测试驱动开发体现了开发人员对软件产品各部分运作方式的理解,而行为驱动开发则关注于开发人员对软件产品最终表现的行为的预期. 行为驱动开发 TDD更像是一种范式而不是一个过程.它描述了一种先编写测试,然后 ...
- WindowsNT设备驱动程序开发基础
一.背景介绍 1.1WindowsNT操作系统的组成1.1.1用户模式(UserMode)与内核模式(KernelMode) 从Intel80386开始,出于安全性和稳定性的考虑,该系列的CPU可以运 ...
- 【Spring注解驱动开发】二狗子让我给他讲讲@EnableAspectJAutoProxy注解
写在前面 最近,二狗子入职了新公司,新入职的那几天确实有点飘.不过慢慢的,他发现他身边的人各个身怀绝技啊,有Spring源码的贡献者,有Dubbo源码的贡献者,有MyBatis源码的贡献者,还有研究A ...
- C++第三十九篇 -- 研究一下Windows驱动开发(二)-- 驱动程序中重要的数据结构
数据结构是计算机程序的核心,I/O管理器定义了一些数据结构,这些数据结构是编写驱动程序时所必须掌握的.驱动程序经常要创建和维护这些数据结构的实例. 一.驱动对象(DRIVER_OBJECT) 每个驱动 ...
随机推荐
- 用C#创建一个窗体,在构造函数里面写代码和在from_load事件里面写代码有什么不同?
没太大区别.一区别就是代码加载时间先后的问题.构造函数先加载,load事件中后加载.
- spark-shell下有提示了,但是发现不能退格
配好了Spark集群后,先用pyspark写了两个小例子,但是发现Tab键没有提示,于是打算转到scala上试试,在spark-shell下有提示了,但是发现不能退格,而且提示也不是复写,而是追加,这 ...
- 常见web错误码
404表示文件或资源未找到 java WEB常见的错误代码 .1xx-信息提示:这些状态代码表示临时的响应.客户端在收到常规响应之前,应准备接收一个或多个1xx响应. -继续. -切换协议. .2xx ...
- 4.构造Thread对象你也许不知道的几件事
1.Thread类对象只有在调用了start()方法之后,JVM虚拟机才会给我们创建一个真正的线程!否则就不能说是创建了线程!也就是说new Thread()之后,此时实际上在计算机底层,操作系统实际 ...
- sitecore系统教程之内容编辑器
内容编辑器 内容编辑器是一种编辑工具,可用于管理和编辑网站上的所有内容.它专为熟悉Sitecore及其包含的功能的经验丰富的内容作者而设计. 内容编辑器的外观和功能取决于用户的角色,本地安全设置以 ...
- JavaScript--常用的输出方式
1.alert("要输出的内容"); 在浏览器中弹出一个对话框,然后把要输出的内容展示出来 2.document.write("要输出的内容"); ...
- Swift闭包(I) @autoclosure和@escaping的区别
1. 参考资料 https://www.cnblogs.com/sgxx/p/6209944.html https://www.jianshu.com/p/99ade4feb8c1
- 微信小程序项目
大体思想 微信小程序,没有DOM和BOM概念,所以,不会涉及到操作节点.它的主要思想是操作数据,然后改变视图层,即MVVM,如果知道angularJS,能很快的理解上手小程序. 一些开发小程序时, ...
- QT构建窗体(父窗体传为野指针)异常案例
[1]源码 工作中,时常会遇到各种各样的异常场景,有些异常场景很常见,必要备录,以防再犯. 分享本案例为:QT创建窗体时parent父窗体传野指针引起异常. 本案例源码如下: 1.1 默认新建一个QT ...
- eclipse xml 文件添加注解快捷键
eclipse xml 文件注解快捷键: <!-- --> Ctrl + shift + / 添加注解 Ctrl + shift + \ 取消注解