Linux驱动学习之:PWM驱动
来源:互联网 发布:京东和淘宝哪个靠谱 编辑:程序博客网 时间:2024/06/01 20:45
s3c2440芯片中一共有5个16位的定时器,其中有4个定时器(定时器0~定时器3)具有脉宽调制功能,因此用s3c2440可以很容易地实现PWM功能。载有s3c2440芯片的Mini2440 板子带有一个蜂鸣器,它是由 PWM 控制的,下面是它的连接原理图:
操控PWM主要分以下四步:
1、PWM是通过引脚TOUT0~TOUT3输出的,而这4个引脚是与GPB0~GPB3复用的,因此要实现PWM功能首先要把相应的引脚配置成TOUT输出。
2、再设置定时器的输出时钟频率,它是以PCLK为基准,再除以用寄存器TCFG0配置的prescaler参数,和用寄存器TCFG1配置的divider参数。
3、然后设置脉冲的具体宽度,它的基本原理是通过寄存器TCNTBn来对寄存器TCNTn(内部寄存器)进行配置计数,TCNTn是递减的,如果减到零,则它又会重新装载TCNTBn里的数,重新开始计数,而寄存器TCMPBn作为比较寄存器与计数值进行比较,当TCNTn等于TCMPBn时,TOUTn输出的电平会翻转,而当TCNTn减为零时,电平会又翻转过来,就这样周而复始。因此这一步的关键是设置寄存器TCNTBn和TCMPBn,前者可以确定一个计数周期的时间长度,而后者可以确定方波的占空比。由于s3c2440的定时器具有双缓存,因此可以在定时器运行的状态下,改变这两个寄存器的值,它会在下个周期开始有效。
4、最后就是对PWM的控制,它是通过寄存器TCON来实现的,一般来说每个定时器主要有4个位要配置(定时器0多一个死区位):启动/终止位,用于启动和终止定时器;手动更新位,用于手动更新TCNTBn和TCMPBn,这里要注意的是在开始定时时,一定要把这位清零,否则是不能开启定时器的;输出反转位,用于改变输出的电平方向,使原先是高电平输出的变为低电平,而低电平的变为高电平;自动重载位,用于TCNTn减为零后重载TCNTBn里的值,当不想计数了,可以使自动重载无效,这样在TCNTn减为零后,不会有新的数加载给它,那么TOUTn输出会始终保持一个电平(输出反转位为0时,是高电平输出;输出反转位为1时,是低电平输出),这样就没有PWM功能了,因此这一位可以用于停止PWM。
- #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/regs-gpio.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>
- #define DEVICE_NAME "pwm" //设备名
- #define PWM_IOCTL_SET_FREQ 1 //定义宏变量,用于后面的 ioctl 中的控制命令
- #define PWM_IOCTL_STOP 0 //定义宏变量,用于后面的 ioctl 中的控制命令
- //定义信号量 lock用于互斥,因此,改驱动程序只能同时有一个进程使用
- static struct semaphore lock;
- /* freq: pclk/50/16/65536 ~ pclk/50/16
- * if pclk = 50MHz, freq is 1Hz to 62500Hz
- * human ear : 20Hz~ 20000Hz
- */
- //设置 pwm 的频率,配置各个寄存器
- static void PWM_Set_Freq( unsigned long freq )
- {
- unsigned long tcon;
- unsigned long tcnt;
- unsigned long tcfg1;
- unsigned long tcfg0;
- struct clk *clk_p;
- unsigned long pclk;
- //set GPB0 as tout0, pwm output 设置 GPB0 为 tout0,pwm 输出
- s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPB0_TOUT0);
- tcon = __raw_readl(S3C2410_TCON); //读取寄存器 TCON 到 tcon
- tcfg1 = __raw_readl(S3C2410_TCFG1); //读取寄存器 TCFG1 到 tcfg1
- tcfg0 = __raw_readl(S3C2410_TCFG0); //读取寄存器 TCFG0 到 tcfg0
- //设置TCFG0寄存器,prescaler = 50
- tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; // S3C2410_TCFG_PRESCALER0_MASK 定时器 0 和1 的预分频值的掩码,清除TCFG[0~8]
- tcfg0 |= (50 - 1); // 设置预分频为 50
- //设置TCFG1寄存器,mux = 1/16
- tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK; //S3C2410_TCFG1_MUX0_MASK 定时器 0 分割值的掩码:清除TCFG1[0~3]
- tcfg1 |= S3C2410_TCFG1_MUX0_DIV16; //定时器 0 进行 16 分割
- __raw_writel(tcfg1, S3C2410_TCFG1); //把 tcfg1 的值写到分割寄存器 S3C2410_TCFG1 中
- __raw_writel(tcfg0, S3C2410_TCFG0); //把 tcfg0 的值写到预分频寄存器 S3C2410_TCFG0 中
- clk_p = clk_get(NULL, "pclk"); //得到 pclk
- pclk = clk_get_rate(clk_p);
- tcnt = (pclk/50/16)/freq; //得到定时器的输入时钟,进而设置 PWM 的调制频率
- __raw_writel(tcnt, S3C2410_TCNTB(0)); //PWM 脉宽调制的频率等于定时器的输入时钟,确定一个计数周期的时间长度
- __raw_writel(tcnt/2, S3C2410_TCMPB(0)); //占空比是 50%
- tcon &= ~0x1f; //清空低5位,其中:TCON[4] --Dead zone enable, TCON[3] -- Timer 0 auto reload on/off, TCON[2] -- Timer 0 output inverter on/off, TCON[1] -- Timer 0 manual update, TCON[0] -- Timer 0 start/stop
* 0xb: 0000 1011
* disable dead zone, auto reload for Timer 0, output inverter off, Update TCNTB0&TCMPB0, start for Timer 0
*/
- tcon |= 0xb;
- __raw_writel(tcon, S3C2410_TCON); //把 tcon 写到计数器控制寄存器 S3C2410_TCON 中
- tcon &= ~2; //clear manual update bit
- __raw_writel(tcon, S3C2410_TCON);
- }
- static void PWM_Stop(void)
- {
- s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPIO_OUTPUT); //设置 GPB0 为输出
- s3c2410_gpio_setpin(S3C2410_GPB(0), 0); //设置 GPB0 为低电平,使蜂鸣器停止
- }
- static int s3c24xx_pwm_open(struct inode *inode, struct file *file)
- {
- if (!down_trylock(&lock)) //是否获得信号量,是 down_trylock(&lock)=0,否则非 0
- return 0;
- else
- return -EBUSY; //返回错误信息:请求的资源不可用
- }
- static int s3c24xx_pwm_close(struct inode *inode, struct file *file)
- {
- PWM_Stop();
- up(&lock); //释放信号量 lock
- return 0;
- }
- /*cmd 是 1,表示设置频率;cmd 是 2 ,表示停止 pwm*/
- static int s3c24xx_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
- {
- switch (cmd) {
- case PWM_IOCTL_SET_FREQ: //if cmd=1 即进入 case PWM_IOCTL_SET_FREQ
- if (arg == 0) //如果设置的频率参数是 0
- return -EINVAL; //返回错误信息,表示向参数传递了无效的参数
- PWM_Set_Freq(arg); //否则设置频率
- break;
- case PWM_IOCTL_STOP: // if cmd=2 即进入 case PWM_IOCTL_STOP
- PWM_Stop(); //停止蜂鸣器
- break;
- }
- return 0; //成功返回
- }
- /*初始化设备的文件操作的结构体*/
- static struct file_operations dev_fops = {
- .owner = THIS_MODULE,
- .open = s3c24xx_pwm_open,
- .release = s3c24xx_pwm_close,
- .ioctl = s3c24xx_pwm_ioctl,
- };
- static struct miscdevice misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = DEVICE_NAME,
- .fops = &dev_fops,
- };
- static int __init dev_init(void)
- {
- int ret;
- init_MUTEX(&lock); //初始化一个互斥锁
- ret = misc_register(&misc); //注册一个 misc 设备
- printk (DEVICE_NAME"\tinitialized\n");
- return ret;
- }
- static void __exit dev_exit(void)
- {
- misc_deregister(&misc); //注销设备
- }
- module_init(dev_init);
- module_exit(dev_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("FriendlyARM Inc.");
- MODULE_DESCRIPTION("S3C2410/S3C2440 PWM Driver");
- #include <stdio.h>
- #include <termios.h>
- #include <unistd.h>
- #include <stdlib.h>
- #define PWM_IOCTL_SET_FREQ 1
- #define PWM_IOCTL_STOP 2
- #define ESC_KEY 0x1b
- static int getch(void)
- {
- struct termios oldt,newt;
- int ch;
- if (!isatty(STDIN_FILENO)) {
- fprintf(stderr, "this problem should be run at a terminal\n");
- exit(1);
- }
- // save terminal setting
- if(tcgetattr(STDIN_FILENO, &oldt) < 0) {
- perror("save the terminal setting");
- exit(1);
- }
- // set terminal as need
- newt = oldt;
- newt.c_lflag &= ~( ICANON | ECHO );
- if(tcsetattr(STDIN_FILENO,TCSANOW, &newt) < 0) {
- perror("set terminal");
- exit(1);
- }
- ch = getchar();
- // restore termial setting
- if(tcsetattr(STDIN_FILENO,TCSANOW,&oldt) < 0) {
- perror("restore the termial setting");
- exit(1);
- }
- return ch;
- }
- static int fd = -1;
- static void close_buzzer(void);
- static void open_buzzer(void)
- {
- fd = open("/dev/pwm", 0);
- if (fd < 0) {
- perror("open pwm_buzzer device");
- exit(1);
- }
- // any function exit call will stop the buzzer
- atexit(close_buzzer);
- }
- static void close_buzzer(void)
- {
- if (fd >= 0) {
- ioctl(fd, PWM_IOCTL_STOP);
- close(fd);
- fd = -1;
- }
- }
- static void set_buzzer_freq(int freq)
- {
- // this IOCTL command is the key to set frequency
- int ret = ioctl(fd, PWM_IOCTL_SET_FREQ, freq);
- if(ret < 0) {
- perror("set the frequency of the buzzer");
- exit(1);
- }
- }
- static void stop_buzzer(void)
- {
- int ret = ioctl(fd, PWM_IOCTL_STOP);
- if(ret < 0) {
- perror("stop the buzzer");
- exit(1);
- }
- }
- int main(int argc, char **argv)
- {
- int freq = 1000 ;
- open_buzzer();
- printf( "\nBUZZER TEST ( PWM Control )\n" );
- printf( "Press +/- to increase/reduce the frequency of the BUZZER\n" ) ;
- printf( "Press 'ESC' key to Exit this program\n\n" );
- while( 1 )
- {
- int key;
- set_buzzer_freq(freq);
- printf( "\tFreq = %d\n", freq );
- key = getch();
- switch(key) {
- case '+':
- if( freq < 20000 )
- freq += 10;
- break;
- case '-':
- if( freq > 11 )
- freq -= 10 ;
- break;
- case ESC_KEY:
- case EOF:
- stop_buzzer();
- exit(0);
- default:
- break;
- }
- }
- }
0
上一篇:工薪阶层如何用5年理出100W?绝对实用方法
下一篇:Android之Wifi学习(1)
相关热门文章
- 欢迎学习三思在ChinaUnix博客...
- 欢迎扶朕起来朕要学习在ChinaU...
- 欢迎要努力学习啊在ChinaUnix...
- 欢迎学习_x1在ChinaUnix博客安...
- 欢迎用心学习在ChinaUnix博客...
- linux 常见服务端口
- xmanager 2.0 for linux配置
- 【ROOTFS搭建】busybox的httpd...
- openwrt中luci学习笔记
- 什么是shell
- linux dhcp peizhi roc
- 关于Unix文件的软链接
- 求教这个命令什么意思,我是新...
- sed -e "/grep/d" 是什么意思...
- 谁能够帮我解决LINUX 2.6 10...
给主人留下些什么吧!~~
评论热议
0 0
- Linux驱动学习之:PWM驱动
- Linux驱动学习之:PWM驱动
- Linux驱动学习之:PWM驱动
- Linux驱动学习之:PWM驱动
- linux驱动之PWM(定时器)
- linux驱动开发之pwm蜂鸣器
- linux驱动开发之pwm蜂鸣器
- linux驱动开发之pwm蜂鸣器
- Linux系统PWM驱动
- Linux系统PWM驱动
- [S5PV210 Linux字符驱动之PWM蜂鸣器驱动
- Android linux PWM驱动(s5pv210)
- mini2440驱动分析之PWM
- mini2440驱动分析之PWM
- Linux驱动学习笔记之触摸屏驱动
- Linux驱动学习之:按键中断驱动
- Linux驱动学习笔记之触摸屏驱动
- Linux驱动学习笔记之触摸屏驱动
- H323、H248(MGCP)、SIP三协议浅析
- voip基本原则
- VoIP的原理及技术
- 程序员到底是一个什么职业?
- 工薪阶层如何用5年理出100W?绝对实用方法
- Linux驱动学习之:PWM驱动
- Android之Wifi学习(1)
- Android之Wifi学习(2)------连接Wifi
- Android WIFI模块分析
- linux中gets()函数和fgets()函数
- 函数参数的传递问题(一级指针和二级指针)
- 为什么要使用copy_from_user?
- linux设备驱动归纳总结(三):4.ioctl的实现
- Java并发与多线程(2) 生产者与消费者
原创粉丝点击
热门IT博客
热门问题
老师的惩罚
人脸识别
我在镇武司摸鱼那些年
重生之率土为王
我在大康的咸鱼生活
盘龙之生命进化
天生仙种
凡人之先天五行
春回大明朝
姑娘不必设防,我是瞎子
哑光
什么是哑光
dior999哑光与滋润哪个适合裸妆
哑光和雾面的区别
哑光眼影什么牌子好
哑光眼影和珠光眼影
哑光是什么意思
哑光口红是什么意思
初学者眼影选珠光还是哑光
哑剧
校园幽默正能量哑剧
哑医
哑医莫子晚
哑医全文免费阅读全文
哑受
哑舍赤锁h医生受
哑口无言
哑口无言意思
哑口套
哑口
哑口无声
哑口无言的意思是什么
哑口无言的意思
哑口套效果图
哑口无言的近义词
中式哑口套效果图
怎样骂小三让对方哑口无言
2018年装修不流行哑口套了
哑夫
哑夫养成记
哑夫种田记
哑夫太腹黑
绝色哑夫好缠人
哑夫农妻种田忙
哑夫养成记咬咬
哑夫猎户家的娘子
怜竹哑夫懒鱼小调
惹火小农妻哑夫太腹黑
哑夫猎户的小娘子
哑女
和美家