結合以前對Linux內核的misc類設備驅動框架的分析 ,本文將編寫基於misc類設備驅動框架的Buzzer設備的實例代碼並對其進行分析。html
misc類設備驅動框架的分析,詳見Linux字符設備驅動框架(三):Linux內核的misc類設備驅動框架。node
硬件接口:linux
CPU:s5pv210;框架
Buzzer的GPIO:GPIO_D0_2 ;測試
LED的工做方式:低電平停,高電平響。spa
P.S.:無源蜂鳴器聲音頻率可控,通常採用PWM控制;有源蜂鳴器用普通IO控制方式便可。code
本代碼取自,九鼎科技x210開發板的BSP。htm
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/poll.h> #include <asm/irq.h> #include <asm/io.h> #include <linux/interrupt.h> #include <asm/uaccess.h> #include <mach/hardware.h> #include <plat/regs-timer.h> #include <mach/regs-irq.h> #include <asm/mach/time.h> #include <linux/clk.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/miscdevice.h> #include <linux/gpio.h> #include <plat/gpio-cfg.h> #define DEVICE_NAME "buzzer" #define PWM_IOCTL_SET_FREQ 1 #define PWM_IOCTL_STOP 0 static struct semaphore lock; // TCFG0在Uboot中設置,這裏再也不重複設置 // Timer0輸入頻率Finput=pclk/(prescaler1+1)/MUX1 // =66M/16/16 // TCFG0 = tcnt = (pclk/16/16)/freq; // PWM0輸出頻率Foutput =Finput/TCFG0= freq static void PWM_Set_Freq( unsigned long freq ) { unsigned long tcon; unsigned long tcnt; unsigned long tcfg1; struct clk *clk_p; unsigned long pclk; //設置GPD0_2爲PWM輸出 s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(2)); tcon = __raw_readl(S3C2410_TCON); tcfg1 = __raw_readl(S3C2410_TCFG1); //mux = 1/16 tcfg1 &= ~(0xf<<8); tcfg1 |= (0x4<<8); __raw_writel(tcfg1, S3C2410_TCFG1); clk_p = clk_get(NULL, "pclk"); pclk = clk_get_rate(clk_p); tcnt = (pclk/16/16)/freq; __raw_writel(tcnt, S3C2410_TCNTB(2)); __raw_writel(tcnt/2, S3C2410_TCMPB(2));//佔空比爲50% tcon &= ~(0xf<<12); tcon |= (0xb<<12); //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0 __raw_writel(tcon, S3C2410_TCON); tcon &= ~(2<<12); //clear manual update bit __raw_writel(tcon, S3C2410_TCON); } void PWM_Stop( void ) { //將GPD0_2設置爲input s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(0)); } static int x210_pwm_open(struct inode *inode, struct file *file) { if (!down_trylock(&lock))//上鎖 return 0; else return -EBUSY; } static int x210_pwm_close(struct inode *inode, struct file *file) { up(&lock);//解鎖 return 0; } // PWM:GPF14->PWM0 static int x210_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { switch (cmd) { case PWM_IOCTL_SET_FREQ: printk("PWM_IOCTL_SET_FREQ:\r\n"); if (arg == 0) return -EINVAL; PWM_Set_Freq(arg); break; case PWM_IOCTL_STOP: default: printk("PWM_IOCTL_STOP:\r\n"); PWM_Stop(); break; } return 0; } //buzzer設備操做集 static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = x210_pwm_open, .release = x210_pwm_close, .ioctl = x210_pwm_ioctl, }; //misc類設備信息初始化 static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops, }; //buzzer設備註冊 static int __init dev_init(void) { int ret; init_MUTEX(&lock); //上鎖 ret = misc_register(&misc); //將buzzer註冊到misc類下 /* GPD0_2 (PWMTOUT2) */ ret =
gpio_request(S5PV210_GPD0(2), "GPD0"); //申請GPIO if(ret) printk("buzzer-x210: request gpio GPD0(2) fail"); s3c_gpio_setpull(S5PV210_GPD0(2), S3C_GPIO_PULL_UP);//將GPIO_D0_2設置爲上拉 s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(1)); //將GPIO_D0_2設置爲輸出模式 gpio_set_value(S5PV210_GPD0(2), 0); //將GPIO_D0_2設置爲低電平 printk ("x210 "DEVICE_NAME" initialized\n"); return ret; } //buzzer設備註銷 static void __exit dev_exit(void) { init_MUTEX_LOCKED(&lock); //解鎖 gpio_free(S5PV210_GPD0(2));//釋放GPIO misc_deregister(&misc); //註銷buzzer設備 } module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("www.9tripod.com"); MODULE_DESCRIPTION("x210 PWM Driver");
Buzzer驅動模塊被裝載進內核以後,可運行以下應用程序測試該驅動是否工做正常。若運行應用程序以後,蜂鳴器連響3聲,則說明驅動工做正常。blog
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <sys/ioctl.h> #define FILE_BUZZER "/dev/buzzer" #define PWM_IOCTL_SET_FREQ 1 #define PWM_IOCTL_STOP 0 int main(void) { int fd = -1; int i = 0; fd = open(FILE_BUZZER, O_RDWR); if (fd < 0) { printf("open buzzer file error.\n"); return -1; } for (i=0; i<3; i++) { ioctl(fd, PWM_IOCTL_SET_FREQ, 10000); sleep(3); ioctl(fd, PWM_IOCTL_STOP); sleep(3); } return 0; }