这次主要是说一下PWM驱动,本来这一次想做一下LCD背光的,我看网上都是通过PWM1的方式调节LCD背光的,然后看了一下电路图,我这个LCD没有接那个接口,就接了一个w1总线的接口,通过网上查询,我这一款好像是通过1-wire总线的方式进行调节的,所以这次准备的PWM就没有写成LCD背光,只是单纯地通过蜂鸣器测试一下PWM,电路图如下:
所以这里测试蜂鸣器,相关电路图如下:
因为三星已经把相应的驱动写好了,我们只需要写少量代码即可实现PWM驱动.
因为现在一些特殊的情况,没有时间细细研究目前所写的驱动程序,关于PWM的基础知识,请自行百度,主要是SOC集成的定时器来做的,然后高低电平会根据用户不同的配置,进行不同比例的高低电平时间,称之为占空比,这里就废话不多说,直接上PWM驱动蜂鸣器的的代码,因为,三星已经帮我们把PWM驱动的框架都写好了,所以,我们不需要自己去操作寄存器来配置PWM的一些参数,我们直接一个pwm_request()就搞定了.下面是驱动代码:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/fb.h> #include <linux/backlight.h> #include <linux/err.h> #include <linux/pwm.h> #include <linux/slab.h> #include <linux/miscdevice.h> #include <linux/delay.h> #include <linux/gpio.h> #include <mach/gpio.h> #include <plat/gpio-cfg.h> #define BUZZER_PWM_GPIO EXYNOS4_GPD0(1) #define DEVICE_NAME "pwm1" #define PWM_BUZ_ID 1 // Backlight #define NS_IN_1HZ (1000000000UL) // 纳秒级,1Hz就是1秒1次,就是1000000000UL纳秒 #define PWM_SET_FREQ _IOW('L', 0x1234, int) #define PWM_STOP _IOW('L', 0x1235, int) static struct semaphore sem_lock; static struct pwm_device *pwm_buz = NULL; void pwm_set_freq(unsigned long freq) { int period_ns = NS_IN_1HZ / freq; pwm_config(pwm_buz, period_ns / 2, period_ns); pwm_enable(pwm_buz); s3c_gpio_cfgpin(BUZZER_PWM_GPIO, S3C_GPIO_SFN(2)); } void pwm_stop(void) { s3c_gpio_cfgpin(BUZZER_PWM_GPIO, S3C_GPIO_OUTPUT); pwm_config(pwm_buz, 0, NS_IN_1HZ / 100); pwm_disable(pwm_buz); } int exynos_pwm_open(struct inode *inode, struct file *filp) { int ret = -1; down_trylock(&sem_lock); // 1,申请GPIO口 ret = gpio_request(BUZZER_PWM_GPIO, DEVICE_NAME); if(ret < 0){ printk("gpio request failed !\n"); return -ENODEV; } // 2,设置GPIO口为输出,电平为1 gpio_direction_output(BUZZER_PWM_GPIO, 0); return 0; } long exynos_pwm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { switch(cmd) { case PWM_SET_FREQ: if(0 == arg) return -EINVAL; pwm_set_freq(arg); break; case PWM_STOP: default: pwm_stop(); break; } return 0; } int exynos_pwm_release(struct inode *inode, struct file *filp) { up(&sem_lock); // 释放GPIO gpio_free(BUZZER_PWM_GPIO); return 0; } const struct file_operations exynos4412_fops = { .open = exynos_pwm_open, .unlocked_ioctl = exynos_pwm_ioctl, .release = exynos_pwm_release, }; struct miscdevice exynos4412_misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &exynos4412_fops, }; static void __exit exynos4412_pwm_exit(void) { pwm_stop(); misc_deregister(&exynos4412_misc); gpio_free(BUZZER_PWM_GPIO); } static int __init exynos4412_pwm_init(void) { int ret = -1; // 1,申请GPIO口 ret = gpio_request(BUZZER_PWM_GPIO, DEVICE_NAME); if(ret < 0){ printk("gpio request failed !\n"); return -ENODEV; } // 2,设置GPIO口为输出,电平为0 gpio_direction_output(BUZZER_PWM_GPIO, 0); // 3,释放GPIO gpio_free(BUZZER_PWM_GPIO); // 4,申请pwm pwm_buz = pwm_request(PWM_BUZ_ID, DEVICE_NAME); if(IS_ERR(pwm_buz)){ printk("pwm request failed !\n"); return -ENODEV; } // 5,pwm关闭 pwm_stop(); // 6,初始化信号量,在开启此驱动时保护资源独享 sema_init(&sem_lock, 1); // 7,注册杂项字符设备 ret = misc_register(&exynos4412_misc); if(ret < 0) printk("misc register failed !\n"); return ret; } module_init(exynos4412_pwm_init); module_exit(exynos4412_pwm_exit); MODULE_LICENSE("GPL");
然后,接下来是驱动测试代码:
#include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <linux/input.h> #define PWM_SET_FREQ _IOW('L', 0x1234, int) #define PWM_STOP _IOW('L', 0x1235, int) int main(void) { int fd; fd = open("/dev/pwm1", O_RDWR); if(fd < 0){ perror("open failed"); exit(1); } for(;;) { ioctl(fd, PWM_SET_FREQ, 2); sleep(1); ioctl(fd, PWM_SET_FREQ, 4); sleep(1); ioctl(fd, PWM_SET_FREQ, 10); sleep(1); ioctl(fd, PWM_STOP, 0); sleep(1); } ioctl(fd, PWM_STOP, 0); if(close(fd) < 0){ perror("close failed"); exit(1); } return 0; }
还有Makefile:
#指定内核源码路径 KERNEL_DIR = /home/george/1702/exynos/linux-3.5 #指定当前路径 CUR_DIR = $(shell pwd) MYAPP = pwm_app MODULE = exynos4412_pwm all: make -C $(KERNEL_DIR) M=$(CUR_DIR) modules arm-none-linux-gnueabi-gcc -o $(MYAPP) $(MYAPP).c clean: make -C $(KERNEL_DIR) M=$(CUR_DIR) clean $(RM) $(MYAPP) install: cp -raf *.ko $(MYAPP) /home/george/1702/exynos/filesystem/1702 #指定编译当前目录下那个源文件 obj-m = $(MODULE).o
当然,我们需要把友善之臂写的PWM的驱动通过make menuconfig去掉:
之后,编译内核,重新加载新内核,并安装自己写的驱动程序,然后测试验证OK,发出不同频率的响声.