生活随笔
收集整理的這篇文章主要介紹了
Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
編寫驅動的第一步仍是看原理圖:
? ? ? ?可以看到,該蜂鳴器由 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 plaincopy
unsigned?int???*gpd0con;?? void?*timer_base;<span?style="white-space:pre">???</span>?? ?? gpd0con?=?ioremap(GPD0CON,4);?? timer_base?=?ioremap(TIMER_BASE?,?0x14);??
2、Time0初始化操作(這里使用的已經是虛擬地址)
? ? ? ?這里現將數據從寄存器中讀出,修改后再寫回寄存器,具體寄存器操作可以移步Exynos4412裸機開發——PWM定時器:? ?
[cpp]?view plaincopy
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 plaincopy
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 plaincopy
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 plaincopy
#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)?? {?? ??? ????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");?? ????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 plaincopy
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 plaincopy
#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 plaincopy
? ? ?? #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"?? ?? ?? #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 plaincopy
#ifndef?__PWM_MUSIC_H?? #define?__PWM_MUSIC_H?? ?? #define?BIG_D?? ?? #define?PCLK?(202800000/4)?? ?? typedef?struct?? {?? ????int?pitch;??? ????int?dimation;?? }Note;?? ?? ?? ?? ?? ?? #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?? ??? ??? #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[]={?? ?????? ????{LA,TIME+TIME/2},?{SOL,TIME/2},{MI,TIME},{SOL,TIME},?? ?? ?????? ????{DO*2,TIME},{LA,TIME/2},{SOL,TIME/2}?,{LA,2*TIME},?? ?????? ????{MI,TIME},{SOL,TIME/2},{LA,TIME/2},{SOL,TIME},?? ?????? ????{MI,TIME},{DO,TIME/2},{LA/2,TIME/2},?? ?????? ????{SOL,TIME/2},{MI,TIME/2},{RE,TIME*2},{RE,TIME+TIME/2},?? ?????? ????{MI,TIME/2},{SOL,TIME},{SOL,TIME/2},{LA,TIME/2},?? ?????? ????{MI,TIME},{RE,TIME},{DO,TIME*2},{SOL,TIME+TIME/2},?? ?????? ????{MI,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME/2},?? ?????? ????{DO,TIME/2},{SOL/2,TIME*3}?? ?? };?? ?? ?? Note?GreatlyLongNow[]={??????? ?????? ????{RE,TIME},?{MI,TIME},{MI,TIME},{MI,TIME+TIME/2},{RE,TIME/2},{DO,TIME},?? ?????? ????{LA/2,TIME},{DO,TIME},{RE,TIME},{DO,TIME*3},{RE,TIME},{MI,TIME},{MI,TIME},?? ?????? ????{MI,TIME+TIME/2},{SOL,TIME/2},{MI,TIME},{MI,TIME},{RE,TIME},{MI,TIME},?? ?????? ????{MI,TIME*3},{SOL,TIME},{LA,TIME},{LA,TIME},{LA,TIME+TIME/2},{SOL,TIME/2},?? ?????? ????{MI,TIME},{MI,TIME},{SOL,TIME},{LA,TIME},{SOL,TIME*3},{RE,TIME},{MI,TIME},?? ?????? ????{MI,TIME},{RE,TIME+TIME/2},{MI,TIME/2},{MI,TIME},{RE,TIME},{MI,TIME},?? ?????? ????{LA/2,TIME},{DO,TIME/2},{LA/2,TIME/2},{LA/2,TIME*2},?? ?????? ????{RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},?? ?????? ????{RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},?? ?????? ????{RE,TIME},{MI,TIME},{DO,TIME},{RE,TIME+TIME/2},{MI,TIME/2},{SOL,TIME},?? ?????? ????{LA,TIME/2},{LA,TIME/2},{LA,TIME/2},{SOL,TIME/2},{MI,TIME},?? ?????? ????{RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},?? ?????? ????{LA/2,TIME},{SOL/2,TIME+TIME/2},{LA/2,TIME/2},{LA/2,TIME*3},?? ?????? ????{RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},?? ?????? ????{RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},?? ?????? ????{RE,TIME},{MI,TIME},{DO,TIME},{RE,TIME+TIME/2},{MI,TIME/2},{SOL,TIME},?? ?????? ????{LA,TIME/2},{LA,TIME/2},{LA,TIME/2},{SOL,TIME/2},{MI,TIME},?? ?????? ????{RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},?? ?????? ????{LA/2,TIME},{SOL/2,TIME+TIME/2},{LA/2,TIME/2},{LA/2,TIME*3}?? ?? };?? Note?FishBoat[]={??? ????{MI,TIME+TIME/2},{SOL,TIME/2},{LA,TIME/2+TIME/4},{DO*2,TIME/4},{LA,TIME/2},?? ?????? ????{SOL,TIME/2},{MI,TIME*3},{RE,TIME},{DO,TIME+TIME/2},{MI,TIME/2},{RE,TIME/2+TIME/4},?? ?????? ????{MI,TIME/4},{RE,TIME/2},{DO,TIME/2},{RE,TIME*4},{MI,TIME+TIME/2},{SOL,TIME/2},?? ?????? ????{RE,TIME},{DO,TIME},{LA,TIME/2+TIME/4},{DO*2,TIME/4},{LA,TIME/2},{SOL,TIME/2},?? ?????? ????{LA,TIME*2},{SOL/2,TIME+TIME/2},{LA/2,TIME/2},{DO,TIME/2+TIME/4},{MI,TIME/4},?? ?????? ????{RE,TIME/2},{DO,TIME/2},{SOL/2,TIME*4},?? ?????? ????{MI,TIME+TIME/2},{SOL,TIME/2},{LA,TIME/2+TIME/4},{DO*2,TIME/4},{LA,TIME/2},?? ?????? ????{SOL,TIME/2},{MI,TIME*3},{SOL,TIME/2},{LA,TIME/2},{DO*2,TIME+TIME/2},{LA,TIME/2},?? ?????? ????{SOL,TIME/2+TIME/4},{LA,TIME/4},{SOL,TIME/2},{MI,TIME/2},{RE,TIME*4},?? ?????? ????{MI,TIME+TIME/2},{SOL,TIME/2},{RE,TIME/2+TIME/4},{MI,TIME/4},{RE,TIME/2},{DO,TIME/2},?? ?????? ????{LA,TIME/2+TIME/4},{DO*2,TIME/4},{LA,TIME/2},{SOL,TIME/2},{LA,TIME*2},{DO,TIME+TIME/2},?? ?????? ????{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 设备驱动的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。