远程视频监控之驱动篇(PWM)
来源:互联网 发布:淘宝购买儿童戒指 编辑:程序博客网 时间:2024/06/05 09:47
转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/38515237
一.代码
#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 <linux/interrupt.h>#include <linux/gpio.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/uaccess.h>#include <mach/gpio.h>#include <mach/regs-gpio.h>#include <mach/hardware.h>#include <plat/regs-timer.h>#include <mach/regs-irq.h>#include <asm/uaccess.h>#include <asm/mach/time.h>#include <linux/device.h>#include <linux/clk.h>#include <linux/cdev.h>#include <linux/miscdevice.h>#define PWM_IOCTL_SET_FREQ1#define PWM_IOCTL_STOP0static int major;static struct class *wvm_pwm_class; static struct semaphore lock;static void PWM_Set_Freq( unsigned long freq ){//定义局部变量unsigned long tcnt;unsigned long tcon;unsigned long tcfg1;unsigned long tcfg0;struct clk *clk_p;unsigned long pclk;//set GPB0 as tout0, pwm outputs3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPB0_TOUT0); //设置GPB0为输出引脚 //读取寄存器tcon = __raw_readl(S3C2410_TCON); tcfg1 = __raw_readl(S3C2410_TCFG1);tcfg0 = __raw_readl(S3C2410_TCFG0);//prescaler = 50tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; //S3C2410_TCFG_PRESCALER0_MASK(0xff)定时器0和1的预分频值的掩码,TCFG[0~8]tcfg0 |= (50 - 1); //预分频50//mux = 1/16tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK; //清零S3C2410_TCFG1_MUX0_MASK(0xf)tcfg1 |= S3C2410_TCFG1_MUX0_DIV16; //设置tcfg1的值为0x0011即:1/16__raw_writel(tcfg1, S3C2410_TCFG1); //将值tcfg1写入定时器配置寄存器1中__raw_writel(tcfg0, S3C2410_TCFG0); //将值tcfg0写入定时器配置寄存器1中clk_p = clk_get(NULL, "pclk"); pclk = clk_get_rate(clk_p); //从系统平台时钟队列中获取pclk的时钟频率,在include/linux/clk.h中定义tcnt = (pclk/50/16)/freq; //计算定时器0的输出时钟频率(pclk/{prescaler0 + 1}/divider value)__raw_writel(tcnt, S3C2410_TCNTB(0)); //设置定时器0计数缓存寄存器的值__raw_writel(tcnt/2, S3C2410_TCMPB(0)); //设置定时器0比较缓存寄存器的值tcon &= ~0x1f; tcon |= 0xb;//disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0(关闭死区、自动重载、关反相器、更新TCNTB0&TCMPB0、启动定时器0)__raw_writel(tcon, S3C2410_TCON);//设置定时器控制寄存器的0-4位,即对定时器0进行控制tcon &= ~2;//clear manual update bit(清除定时器0的手动更新位)__raw_writel(tcon, S3C2410_TCON);}static void wvm_pwm_drv_Stop(void){s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPIO_OUTPUT); //设置GPB0为输出引脚s3c2410_gpio_setpin(S3C2410_GPB(0), 0); //设置为0,使蜂鸣器停止}static int wvm_pwm_drv_open(struct inode *inode, struct file *file){if (!down_trylock(&lock)) //如果第一次打开能获取到返回一个0,如果在没有释放前,再次要打开会返回一个非0值return 0;elsereturn -EBUSY;}/*cmd 是1,表示设置频率;cmd 是0 ,表示停止pwm*/static long wvm_pwm_drv_ioctl( struct file *file, unsigned int cmd, unsigned long arg){switch (cmd) {case PWM_IOCTL_SET_FREQ:if (arg == 0) //若果参数是0,则返回错误return -EINVAL;PWM_Set_Freq(arg);break;case PWM_IOCTL_STOP:wvm_pwm_drv_Stop();break;}return 0;}static int wvm_pwm_drv_close(struct inode *inode, struct file *file){ wvm_pwm_drv_Stop(); //蜂鸣器停止 up(&lock); //释放互斥锁return 0;}static struct file_operations wvm_pwm_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = wvm_pwm_drv_open, .unlocked_ioctl = wvm_pwm_drv_ioctl, .release = wvm_pwm_drv_close,};static int wvm_pwm_drv_init(void){ major = register_chrdev(0, "wvm_pwm_drv", &wvm_pwm_drv_fops);if(major < 0) { printk( " wvm_pwm_drvregister falid!/n"); return major; }wvm_pwm_class = class_create(THIS_MODULE, "wvm_pwm_drv");if(IS_ERR(wvm_pwm_class)) { printk( " wvm_pwm_drv register class falid!/n"); return -1; }/* 为了让mdev根据这些信息来创建设备节点 */device_create(wvm_pwm_class, NULL, MKDEV(major, 0), NULL, "wvm_pwm"); /* /dev/wvm_pwm */sema_init(&lock, 1); //定义互斥锁 return 0;}static void wvm_pwm_drv_exit(void){unregister_chrdev(major, "wvm_pwm_drv");device_destroy(wvm_pwm_class, MKDEV(major, 0));class_destroy(wvm_pwm_class);}module_init(wvm_pwm_drv_init);module_exit(wvm_pwm_drv_exit);MODULE_LICENSE("GPL");
二.应用测试
#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <sys/ioctl.h>#include <unistd.h>#include <sys/types.h>#include <math.h>int main(int argc, char **argv){ int tmp; int cmd; int fd; //打开蜂鸣器设备 fd = open("/dev/wvm_pwm", O_RDWR); if(fd < 0) { printf("Open PWM Device Faild!\n"); exit(1); } while(1) { cmd=1; ioctl(fd,cmd,700); //蜂鸣器响 usleep(800000); //延时 ioctl(fd,cmd,2000); usleep(800000); } //关闭设备 close(fd); return 0;}
三.分析
1.调用过程
应用程序:ioctl(fd,cmd,700); 调用---> 驱动程序:wvm_pwm_drv_ioctl()
cmd=1 ,arg不等于0 调用---> PWM_Set_Freq()
由此我们看到PWM_Set_Freq()才是整个驱动的核心,下面我将结合硬件来讲解这个函数
2.如何实现
a.设置GPB0为PWM引脚
b.设置定时器配置寄存器(TCFG0,TCFG1),目的是设定定时器的输出频率(这两个寄存器的值决定了输出频率)
预分频值:本程序设为了50
分频值设为1/16
c.设置定时器0计数缓存器(TCNTB0控制PWM频率)和定时器0比较缓存器(TCMTB0控制PWM值),其中TCMTB0为TCNTB0的一半,即占空比为百分之50,输出频率:tcnt = (pclk/50/16)/freq; 其中freq即为我们设定的频率参数。(这里的调节蜂鸣器,只是固定的占空比调节输出频率,如果想调光等,需要的是固定频率,调节占空比,大家可以改动驱动自己尝试下)虽然在本项目中没遇到,但是我觉得还是有必要讲解下,占空比是如何调节的
首先要明确一点当设定完TCNTB0,TCMTB0他们就不变了,TCNTB0对应的内部寄存器TCNT0开始递减 。当其等于TCMTB0电平翻转,当其变为0时,电平再次翻转。所以想控制占空比的话,只需要控制TCNTB0即可。
d.设置定时器控制寄存器(TCON)
首先:关闭死区、自动重载、关反相器、更新TCNTB0&TCMPB0(第一次必须手动更新)、启动定时器0
然后:清除手动更新,因为已经设置了自动重载。
参考:http://blog.chinaunix.net/uid-25445243-id-212935.html
- 远程视频监控之驱动篇(PWM)
- 远程视频监控之驱动篇(LED)
- 远程视频监控之驱动篇(按键)
- 远程视频监控之驱动篇(摄像头)
- 远程视频监控之驱动篇(串口)
- 远程视频监控之应用篇(mjpg-streamer)
- 远程视频监控之应用篇(all_test)
- 远程视频监控之应用篇(环境搭建)
- 远程视频监控之应用篇(mjpg-streamer)
- 远程视频监控之构思篇
- 远程视频监控之硬件篇
- 智能家居之远程视频监控
- 远程视频监控之概览
- 远程视频网络监控
- 远程视频监控
- 监控--nagios之远程监控(二)
- mini2440驱动分析之PWM
- mini2440驱动分析之PWM
- org.eclipse.core.internal.registry.ConfigurationElementHandle
- V大风格v飞多办法过分过分
- JVM学习笔记(一)------基本结构
- poj 3207 2-SAT(圆周点连边不相交)
- Web开发人员基础技巧小知识
- 远程视频监控之驱动篇(PWM)
- 【转帖】menuconfig的执行流程,构建一个menuconfig系统的方法
- oracle监听详解
- Java流类图结构
- [企管怪谈]企业怎么留住领导?
- Apache - IIS&Apache 端口共享
- 关于机器学习 SVM 支持向量机 文章的小结
- 两台服务器网站同步镜像
- MySQL 最常用的一千行