编写驱动的第一步仍是看原理图:
可以看到,该蜂鸣器由 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