您的位置:首页 > 运维架构 > Linux

Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动

2016-06-19 18:12 861 查看
编写驱动的第一步仍是看原理图:



可以看到,该蜂鸣器由 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、地址映射操作

[cpp] view
plain copy







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定时器:

[cpp] view
plain copy







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、装载数据,配置占空比

[cpp] view
plain copy







writel(500, timer_base +TCNTB0 );

writel(250, timer_base +TCMPB0 );

writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );

4、相关控制函数

[cpp] view
plain copy







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

[cpp] view
plain copy







#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:

[cpp] view
plain copy







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秒的功能:

[cpp] view
plain copy







#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);

}

这是个音乐播放测试程序,慎听!!分别为《大长今》、《世上只有妈妈好》、《渔船》,这个单独编译一下

[cpp] view
plain copy







/*

* 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;

}

附所用头文件:

[cpp] view
plain copy







#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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: