编写驱动的第一步仍是看原理图:
可以看到,该蜂鸣器由 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
#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
便会听到悦耳的音乐了!