本帖最后由 gjianw217 于 2015-8-22 02:25 编辑
前面进行了OK210试用体验的入门篇介绍,算是初步入门,分别包含:
【OK210试用体验】入门篇(1):开箱验板
【OK210试用体验】入门篇(2):板载资源
【OK210试用体验】入门篇(3):开发环境(软件安装,开发环境,烧写系统)
【OK210试用体验】入门篇(4):编程入门(NFS登录,驱动入门)
【OK210试用体验】功能篇(1):Linux字符驱动之Led
【OK210试用体验】功能篇(2):Linux字符驱动之Key按键
【OK210试用体验】功能篇(3):Linux Input子系统之Key按键
【OK210试用体验】功能篇(4):Linux字符驱动之DS18B20
今天是功能篇的第五篇:Linux字符驱动之PWM蜂鸣器,本节主要分3部分:硬件分析,软件基础,驱动编程。
一、硬件分析
在【OK210试用体验】的第二篇:板载资源中,简单分析了蜂鸣器的功能和作用。其实对蜂鸣器的操作,主要是通过S5PV210的PWM来实现的,因为在OK210上,连接的是一个无源蜂鸣器,必须通过外部的驱动信号,才能控制其的“鸣叫”。
首先从OK210的底板原理图中可知,OK210开发板上的DS18B20连接通过一个三极管组成的放大威廉希尔官方网站
连接到了核心板的XpwmTOUT[0]引脚上,如下图所示:
而XpwmTOUT[0]引脚由S5PV210用户手册,可知,该引脚位于GPD0[0]引脚上,默认为GPI,即当作通用输入端口使用,如下图所示:
但的第一功能名为TOUT_0,继续查阅,可知,该功能可作为PWM输出使用,如下所示,
所以,我们要对蜂鸣器进行操作,就是通过对XpwmTOUT[0]引脚的设置,即将其设置为TOUT_0功能,通过配置PWM的波形来实现蜂鸣器的鸣叫。
二、软件基础
如上所述,无源蜂鸣器没有自带震荡威廉希尔官方网站
,必须外部提供2-5Khz左右的方波,才能驱动其发声,而要想产生方波,就会用到S5PV210的PWM模块。
S5PV210共有5个32bit的PWM定时器,其中定时器0、1、2、3有PWM功能,定时器4没有输出引脚。这些定时器都可产生中断。每个定时器可选择输入时钟为PCLK或SCLK_PWM。对PWM的操作,主要通过几个寄存器来完成,操作步骤如下:
1、设置TCFG0寄存器:配置定时器的一级分频值
2、设置TCFG1寄存器:配置定时器的二级分频值
3、设置TCNTBn寄存器:递减计数器缓冲寄存器
4、设置TCMPBn寄存器:比较缓冲寄存器
5、设置TCON寄存器:
(1)手动更新on(执行后,CPU会把TCNTBn的值加载到递减计数器中)
(2)手动更新off、自动重载、启动定时器
不过在Linux内核中,三星公司和飞凌公司已经为我们配置好了对这些PWM模块的使用,具体参见源码目录下 arch/arm/plat-s3c/的pwm.c文件,在驱动编程中,主要使用pwm_config()、pwm_enable()、pwm_disable(pwm4buzzer)这三个函数。另外,也可参见另一篇博文【S5PV210 PWM】。
三 驱动编程
有图有真相,按照如下运行后,即可听到“鸣叫”。
1 驱动程序
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #define DEVICE_NAME "pwm"
- #define PWM_IOCTL_SET_FREQ 1
- #define PWM_IOCTL_STOP 0
- #define NS_IN_1HZ (1000000000UL)
- #define BUZZER_PWM_ID 0
- #define BUZZER_PMW_GPIO S5PV210_GPD0(0)
- static struct pwm_device *pwm4buzzer;
- static struct semaphore lock;
- static void pwm_set_freq(unsigned long freq) {
- int period_ns = NS_IN_1HZ / freq;
- pwm_config(pwm4buzzer, period_ns / 2, period_ns);
- pwm_enable(pwm4buzzer);
- }
- static void pwm_stop(void) {
- pwm_config(pwm4buzzer, 0, NS_IN_1HZ / 100);
- pwm_disable(pwm4buzzer);
- }
- static int my_pwm_open(struct inode *inode, struct file *file) {
- if (!down_trylock(&lock))
- return 0;
- else
- return -EBUSY;
- }
- static int my_pwm_close(struct inode *inode, struct file *file) {
- up(&lock);
- return 0;
- }
- static long my_pwm_ioctl(struct file *filep, unsigned int cmd,
- unsigned long arg)
- {
- switch (cmd) {
- case PWM_IOCTL_SET_FREQ:
- if (arg == 0)
- return -EINVAL;
- pwm_set_freq(arg);
- break;
- case PWM_IOCTL_STOP:
- default:
- pwm_stop();
- break;
- }
- return 0;
- }
- static struct file_operations my_pwm_ops = {
- .owner = THIS_MODULE,
- .open = my_pwm_open,
- .release = my_pwm_close,
- .unlocked_ioctl = my_pwm_ioctl,
- };
- static struct miscdevice my_misc_dev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = DEVICE_NAME,
- .fops = &my_pwm_ops,
- };
- static int __init my_pwm_dev_init(void) {
- int ret;
- printk(DEVICE_NAME " my_pwm_dev_initn");
- ret = gpio_request(BUZZER_PMW_GPIO, DEVICE_NAME);
- if (ret) {
- printk("request GPIO %d for pwm failedn", BUZZER_PMW_GPIO);
- return ret;
- }
- gpio_set_value(BUZZER_PMW_GPIO, 0);
- s3c_gpio_cfgpin(BUZZER_PMW_GPIO, S3C_GPIO_OUTPUT);
- pwm4buzzer = pwm_request(BUZZER_PWM_ID, DEVICE_NAME);
- if (IS_ERR(pwm4buzzer)) {
- printk("request pwm %d for %s failedn", BUZZER_PWM_ID, DEVICE_NAME);
- return -ENODEV;
- }
- pwm_stop();
- s3c_gpio_cfgpin(BUZZER_PMW_GPIO, S3C_GPIO_SFN(2));
- gpio_free(BUZZER_PMW_GPIO);
- init_MUTEX(&lock);
- ret = misc_register(&my_misc_dev);
- return ret;
- }
- static void __exit my_pwm_dev_exit(void) {
- printk(DEVICE_NAME " my_pwm_dev_exitn");
- pwm_stop();
- misc_deregister(&my_misc_dev);
- }
- module_init(my_pwm_dev_init);
- module_exit(my_pwm_dev_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("gjianw217@163.com");
- MODULE_DESCRIPTION("PWM Driver");
2 应用程序
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #define PWM_IOCTL_SET_FREQ 1
- #define PWM_IOCTL_STOP 0
- int main(int argc ,char* argv[])
- {
- int m_fd=0;//
- m_fd = open("/dev/pwm", O_RDONLY);
- int freq=1000;
- if(argc>1)
- freq=atoi(argv[1]);
- printf("%d tt",freq);
- ioctl(m_fd, PWM_IOCTL_STOP);
- ioctl(m_fd, PWM_IOCTL_SET_FREQ,freq);
- getchar();
- ioctl(m_fd, PWM_IOCTL_STOP);
- close(m_fd);
- return 0;
- }
3 Makefile文件
- #pwm Makefile
- ARCH=arm
- CROSS_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-
- APP_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-
- #obj-m := app-drv.o
- obj-m := pwm-drv.o
- #KDIR := /path/to/kernel/linux/
- KDIR := /home/ok210/android-kernel-samsung-dev/
- PWD := $(shell pwd)
- default:
- make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
- app:pwm-app.c
- $(APP_COMPILE)gcc -o app pwm-app.c
- clean:
- $(MAKE) -C $(KDIR) M=$(PWD) clean
欢迎大家关注本人的微信公众号【口袋物联】,微信号为koudaiwulian。