赞
踩
本驱动用的开发板是:
飞凌公司的OK2440开发板;
Linux内核版本是:linux2.6.35;
编译器:arm-linux-gcc-4.3.2
1)每个定时器都有一个比较缓存寄存器(TCMPB)和一个计数缓存寄存器(TCNTB);
2)定时器0、1共享一个8位的预分频器(预定标器),定时器2、3、4共享另一个8位的预分频器(预定标器),其值范围是0~255;
3)定时器0、1共享一个时钟分频器,定时器2、3、4共享另一个时钟分频器,这两个时钟分频器都能产生5种不同的分频信号值(即:1/2、1/4、1/8、1/16和TCLK);
4)两个8位的预分频器是可编程的且根据装载的值来对PCLK进行分频,预分频器和钟分频器的值分别存储在定时器配置寄存器TCFG0和TCFG1中;
5)有一个TCON控制寄存器控制着所有定时器的属性和状态,TCON的第0~7位控制着定时器0、第8~11位控制着定时器1、第12~15位控制着定时器2、第16~19位控制着定时器3、第20~22位控制着定时器4。
还是根据S3C2440手册的描述和上图的结构,要开始一个PWM定时器功能的步骤如下(假设使用的是第一个定时器):
1)分别设置定时器0的预分频器值和时钟分频值,以供定时器0的比较缓存寄存器和计数缓存寄存器用;
2)设置比较缓存寄存器TCMPB0和计数缓存寄存器TCNTB0的初始值(即定时器0的输出时钟频率);
3)关闭定时器0的死区生成器(设置TCON的第4位);
4)开启定时器0的自动重载(设置TCON的第3位);
5)关闭定时器0的反相器(设置TCON的第2位);
6)开启定时器0的手动更新TCNTB0&TCMPB0功能(设置TCON的第1位);
7)启动定时器0(设置TCON的第0位);
8)清除定时器0的手动更新TCNTB0&TCMPB0功能(设置TCON的第1位)。
由此可以看到,PWM的输出频率跟比较缓存寄存器和计数缓存寄存器的取值有关,而比较缓存寄存器和计数缓存寄存器的值又跟预分频器和时钟分频器的值有关;要使用PWM功能其实也就是对定时器的相关寄存器进行操作。手册上也有一个公式:定时器输出频率 = PCLK / {预分频器值 + 1} / 时钟分频值。总的来说,就是设置几个与定时器相关的寄存器的值,即可以让定时器输出一定频率的PWM波。
- <span style="font-size:14px;">#include <linux/module.h> //所有模块都需要这个头文件
- #include <linux/kernel.h> // 声明了printk()这个内核态用的函数
- #include <linux/fs.h> // 文件系统有关的,结构体file_operations在头文件 linux/fs.h中定义
- #include <linux/init.h> // init和exit相关宏
- #include <linux/delay.h>
- #include <linux/poll.h>
- #include <asm/irq.h>
- #include <asm/io.h>
- #include <linux/interrupt.h>
- #include <asm/uaccess.h> /*Linux 中的用户和内核态内存交互函数(这些函数在 include/asm/uaccess.h 中被声 明): unsigned long copy_from_user(void *to, const void *from, unsigned long n); unsigned long copy_to_user (void * to, void * from, unsigned long len)*/
- #include <mach/regs-gpio.h> // 寄存器设置
- #include <mach/hardware.h> // IO空间的物理地址定义
- #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> //在这个头文件中主要是misc(混合)设备注册和注销,linux中用struct miscdevice来描述一个混杂设备
- #include <linux/gpio.h>
-
- #define DEVICE_NAME "PWM0"
- static struct semaphore lock;
- /*semaphore是内核中比较重要和常用的同步方式之一,他主要的特点是实现了Sleep机制下的同步。也就是当获取一个semaphore但是又不能立刻获取的时候,他使当前的执行进程进入到Sleep状态中等待,当semaphore可以获取的时候,从新开始运行,而不像splin lock在获取锁的时候是BusyWait。
- 首先看其定义:
- struct semaphore {
- atomic_t count; // 原子变量,是后续的实际代码中,我们能看到其即为我们在初始化时所设置的信号量。
- int sleepers; // 有几个等待者。
- wait_queue_head_t wait; // 等待队列
- };
- 初始化函数:
- static inline void sema_init (struct semaphore *sem, int val)
- {
- atomic_set(&sem->count, val); // 原子操作,把信号量的值设为原子操作的值。
- sem->sleepers = 0; // 设为等待者为0。
- init_waitqueue_head(&sem->wait); // 初始化等待队列。
- } */
-
- static int ok2440_pwm_open(struct inode *inode, struct file *file)
- {
- if (!down_trylock(&lock))
- return 0;
- else
- return -EBUSY;
- }
- /*P函数:int down_trylock(struct semaphore *sem);带有“_trylock”的永不休眠,若信号量在调用是不可获得,会返回非零值。
- 因此可知上述代码是判断信号量是否可以获得。可获得则return 0,不可获得则返回return -EBUSY*/
-
- static int ok2440_pwm_close(struct inode *inode, struct file *file)
- {
- up(&lock);
- return 0;
- }
-
- static int ok2440_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
- {
- // 定义一些局部变量
- unsigned long tcfg0; //定时器配置寄存器0,设置定时器0-4的预分频值
- unsigned long tcfg1; //定时器配置寄存器1,设置各个pwm定时器的除值各DMA应答通道
- unsigned long tcntb; //定时器缓冲区寄存器,通过TCNTB0\TCMPB0共同现实频率改变,发出不同的声音
- unsigned long tcmpb;
- unsigned long tcon; //定时器控制寄存器,设置timer0-4的状态
-
- if(cmd <= 0) // 如果输入的参数小于或等于0的话,就让蜂鸣器停止工作
- {
- s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPIO_OUTPUT); // 设置GPIO引脚的功能:本驱动中PWM所涉及的GPIO引脚设为输出功能
- s3c2410_gpio_setpin(S3C2410_GPB(0), 0); //由原理图可知直接给低电平可让蜂鸣器停止工作
-
- }
- else //如果输入的参数大于0,就让蜂鸣器开始工作,不同的参数,蜂鸣器的频率也不一样
- {
- struct clk *clk_p;
- unsigned long pclk;
- s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPB0_TOUT0);
- tcon = __raw_readl(S3C2410_TCON); // 读取寄存器TCON到tcon, mach/uncompress.h中定义了__raw_readl的定义
- tcfg1 = __raw_readl(S3C2410_TCFG1); // 读取寄存器TCFG1到tcfg1
- tcfg0 = __raw_readl(S3C2410_TCFG0); // 读取寄存器TCFG0到tcfg0
-
- tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; /*#define S3C2410_TCFG_PRESCALER0_MASK (255<<0),S3C2410_TCFG_PRESCALER0_MASK定时器0和1的预分频值的掩码,TCFG[0~8]*/
- tcfg0 |= (50 - 1); // 预分频为50,设置tcfg0的值为49
- tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK; //#define S3C2410_TCFG1_MUX0_MASK (15<<0)
- tcfg1 |= S3C2410_TCFG1_MUX0_DIV16; //#define S3C2410_TCFG1_MUX0_DIV16 (3<<0) 定时器0进行16分割
-
- __raw_writel(tcfg1, S3C2410_TCFG1); //把tcfg1的值写到时钟分频寄存器S3C2410_TCFG1中
- __raw_writel(tcfg0, S3C2410_TCFG0); //把tcfg0的值写到预分频寄存器S3C2410_TCFG0中
-
- clk_p = clk_get(NULL, "pclk");/* clk_get获取一个名为id的时针 * 输入参数dev: 可以为NULL* 输入参数id: 时针名称,如fclk、hclk、pclk等 * 返回值: 返回该时钟的clk结构体 */
- pclk = clk_get_rate(clk_p); //从系统平台时钟队列中获取pclk的时钟频率,在include/linux/clk.h中定义
- tcntb = (pclk/50/16)/arg; // 计算定时器0的输出时钟频率(pclk/{prescaler0 + 1}/divider value)
- tcmpb = tcntb>>1;
- __raw_writel(tcntb, S3C2410_TCNTB(0)); //PWM脉宽调制的频率等于定时器的输出时钟频率
- __raw_writel(tcmpb, S3C2410_TCMPB(0)); //占空比是tcntb/2为占空比50%
-
- tcon &= ~0x1f; //此时tcon=11100000
- tcon |= 0xb; // 此时tcon=11101011
- /*
- 关闭定时器0的死区生成器(设置TCON的第4位);
- 开启定时器0的自动重载(设置TCON的第3位);
- 关闭定时器0的反相器(设置TCON的第2位);
- 开启定时器0的手动更新TCNTB0&TCMPB0功能(设置TCON的第1位);
- 启动定时器0(设置TCON的第0位);*/
- __raw_writel(tcon, S3C2410_TCON);
- tcon &= ~2; //清除定时器0的手动更新TCNTB0&TCMPB0功能(设置TCON的第1位)。
- __raw_writel(tcon, S3C2410_TCON);
- }
- return 0;
- }
-
- static struct file_operations dev_fops = {
- .owner = THIS_MODULE, //这是一个宏,推向编译模块时自动创建的__this_module变量
- .open = ok2440_pwm_open,
- .release = ok2440_pwm_close,
- .ioctl = ok2440_pwm_ioctl,
- };
-
- static struct miscdevice misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = DEVICE_NAME,
- .fops = &dev_fops,
- };
- /*misc(混合)设备注册和注销:
- struct miscdevice {
- int minor;
- const char *name;
- const struct file_operations *fops;
- struct list_head list;
- struct device *parent;
- struct device *this_device;
- };*/
-
- static int __init dev_init(void)
- {
- int ret;
- init_MUTEX(&lock); /*带有“_LOCKED”的是将信号量初始化为0,即锁定,允许任何线程访问时必须先解锁。没带的为1,这种信号量在任何给定时刻只能由单个进程或线程拥有*/
- ret = misc_register(&misc);
- printk (DEVICE_NAME" initialized\n");
- return ret;
- }
-
- static void __exit dev_exit(void)
- {
- misc_deregister(&misc);
- // printk("<1> display a little,hei hei \n\n ");
- }
- module_init(dev_init);
- module_exit(dev_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("luo");
- MODULE_DESCRIPTION("PWM Drivers for ok2440 Board");
- </span>
- <span style="font-size:14px;">#include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/ioctl.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- int main(int argc, char **argv)
- {
- int fd;
- unsigned long temp =0;
- int i;
- fd = open("/dev/PWM0", 0);
- if (fd < 0) {
- perror("open device EmbedSky-BEEP");
- exit(1);
- }
- printf("Please enter the times number (0 is stop) :\n");
- for(i=0;;i++)
- {
- scanf("%d",&temp);
- printf("times = %d \n",temp);
- if(temp == 0)
- {
- ioctl(fd, 0);
- printf("Stop Control Beep!\n");
- // break;
- continue;
- }
- else
- {
- ioctl(fd, 1, temp);
- }
- }
- close(fd);
- return 0;
- }
- </span>
- <span style="font-size:14px;">#include <linux/module.h> //所有模块都需要这个头文件
- #include <linux/kernel.h> // 声明了printk()这个内核态用的函数
- #include <linux/fs.h> // 文件系统有关的,结构体file_operations在头文件 linux/fs.h中定义
- #include <linux/init.h> // init和exit相关宏
- #include <linux/delay.h>
- #include <linux/poll.h>
- #include <asm/irq.h>
- #include <asm/io.h>
- #include <linux/interrupt.h>
- #include <asm/uaccess.h> /*Linux 中的用户和内核态内存交互函数(这些函数在 include/asm/uaccess.h 中被声 明): unsigned long copy_from_user(void *to, const void *from, unsigned long n); unsigned long copy_to_user (void * to, void * from, unsigned long len)*/
- #include <mach/regs-gpio.h> // 寄存器设置
- #include <mach/hardware.h> // IO空间的物理地址定义
- #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> //在这个头文件中主要是misc(混合)设备注册和注销,linux中用struct miscdevice来描述一个混杂设备
- #include <linux/gpio.h> //s3c2410_gpio_cfgpin(); s3c2410_gpio_getpin();
-
- #define DEVICE_NAME "PWM1"
- static struct semaphore lock;
- /*semaphore是内核中比较重要和常用的同步方式之一,他主要的特点是实现了Sleep机制下的同步。也就是当获取一个semaphore但是又不能立刻获取的时候,他使当前的执行进程进入到Sleep状态中等待,当semaphore可以获取的时候,从新开始运行,而不像splin lock在获取锁的时候是BusyWait。
- 首先看其定义:
- struct semaphore {
- atomic_t count; // 原子变量,是后续的实际代码中,我们能看到其即为我们在初始化时所设置的信号量。
- int sleepers; // 有几个等待者。
- wait_queue_head_t wait; // 等待队列
- };
- 初始化函数:
- static inline void sema_init (struct semaphore *sem, int val)
- {
- atomic_set(&sem->count, val); // 原子操作,把信号量的值设为原子操作的值。
- sem->sleepers = 0; // 设为等待者为0。
- init_waitqueue_head(&sem->wait); // 初始化等待队列。
- } */
-
- static int ok2440_pwm_open(struct inode *inode, struct file *file)
- {
- if (!down_trylock(&lock))
- return 0;
- else
- return -EBUSY;
- }
- /*P函数:int down_trylock(struct semaphore *sem);带有“_trylock”的永不休眠,若信号量在调用是不可获得,会返回非零值。
- 因此可知上述代码是判断信号量是否可以获得。可获得则return 0,不可获得则返回return -EBUSY*/
-
- static int ok2440_pwm_close(struct inode *inode, struct file *file)
- {
- up(&lock);
- return 0;
- }
-
- static int ok2440_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
- {
- // 定义一些局部变量
- unsigned long tcfg0; //定时器配置寄存器0,设置定时器0-4的预分频值
- unsigned long tcfg1; //定时器配置寄存器1,设置各个pwm定时器的除值各DMA应答通道
- unsigned long tcntb; //定时器缓冲区寄存器,通过TCNTB0\TCMPB0共同现实频率改变,发出不同的声音
- unsigned long tcmpb;
- unsigned long tcon; //定时器控制寄存器,设置timer0-4的状态
-
- if(cmd <= 0) // 如果输入的参数小于或等于0的话,就让蜂鸣器停止工作
- {
- s3c2410_gpio_cfgpin(S3C2410_GPB(1), S3C2410_GPIO_OUTPUT); // 设置GPIO引脚的功能:本驱动中PWM所涉及的GPIO引脚设为输出功能
- s3c2410_gpio_setpin(S3C2410_GPB(1), 0); //由原理图可知直接给低电平可让蜂鸣器停止工作
-
- }
- else //如果输入的参数大于0,就让蜂鸣器开始工作,不同的参数,蜂鸣器的频率也不一样
- {
- struct clk *clk_p;
- unsigned long pclk;
- s3c2410_gpio_cfgpin(S3C2410_GPB(1), S3C2410_GPB1_TOUT1);
- tcon = __raw_readl(S3C2410_TCON); // 读取寄存器TCON到tcon, mach/uncompress.h中定义了__raw_readl的定义
- tcfg1 = __raw_readl(S3C2410_TCFG1); // 读取寄存器TCFG1到tcfg1
- tcfg0 = __raw_readl(S3C2410_TCFG0); // 读取寄存器TCFG0到tcfg0
-
- tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; /*#define S3C2410_TCFG_PRESCALER0_MASK (255<<0),S3C2410_TCFG_PRESCALER0_MASK定时器0和1的预分频值的掩码,TCFG[0~8]*/
- tcfg0 |= (50 - 1); // 预分频为50,设置tcfg0的值为49
- tcfg1 &= ~S3C2410_TCFG1_MUX1_MASK; //#define S3C2410_TCFG1_MUX1_MASK (15<<0)
- tcfg1 |= S3C2410_TCFG1_MUX1_DIV16; //#define S3C2410_TCFG1_MUX1_DIV16 (3<<0) 定时器0进行16分割
-
- __raw_writel(tcfg1, S3C2410_TCFG1); //把tcfg1的值写到时钟分频寄存器S3C2410_TCFG1中
- __raw_writel(tcfg0, S3C2410_TCFG0); //把tcfg0的值写到预分频寄存器S3C2410_TCFG0中
-
- clk_p = clk_get(NULL, "pclk");/* clk_get获取一个名为id的时针 * 输入参数dev: 可以为NULL* 输入参数id: 时针名称,如fclk、hclk、pclk等 * 返回值: 返回该时钟的clk结构体 */
- pclk = clk_get_rate(clk_p); //从系统平台时钟队列中获取pclk的时钟频率,在include/linux/clk.h中定义
- tcntb = (pclk/50/16)/arg; // 计算定时器0的输出时钟频率(pclk/{prescaler0 + 1}/divider value)
- tcmpb = tcntb>>1;
- __raw_writel(tcntb, S3C2410_TCNTB(1)); //PWM脉宽调制的频率等于定时器的输出时钟频率
- __raw_writel(tcmpb, S3C2410_TCMPB(1)); //占空比是tcntb/2为占空比50%
-
- tcon &= ~0x0f00; //此时tcon=1111000011111111
- tcon |= 0x0b00; // 此时tcon=1111101111111111
- /*
- 关闭定时器0的死区生成器(设置TCON的第4位);
- 开启定时器0的自动重载(设置TCON的第3位);
- 关闭定时器0的反相器(设置TCON的第2位);
- 开启定时器0的手动更新TCNTB0&TCMPB0功能(设置TCON的第1位);
- 启动定时器0(设置TCON的第0位);*/
- __raw_writel(tcon, S3C2410_TCON);
- tcon &= ~0x0200; //清除定时器0的手动更新TCNTB0&TCMPB0功能(设置TCON的第1位)。
- __raw_writel(tcon, S3C2410_TCON);
- }
- return 0;
- }
-
- static struct file_operations dev_fops = {
- .owner = THIS_MODULE, //这是一个宏,推向编译模块时自动创建的__this_module变量
- .open = ok2440_pwm_open,
- .release = ok2440_pwm_close,
- .ioctl = ok2440_pwm_ioctl,
- };
-
- static struct miscdevice misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = DEVICE_NAME,
- .fops = &dev_fops,
- };
- /*misc(混合)设备注册和注销:
- struct miscdevice {
- int minor;
- const char *name;
- const struct file_operations *fops;
- struct list_head list;
- struct device *parent;
- struct device *this_device;
- };*/
-
- static int __init dev_init(void)
- {
- int ret;
- init_MUTEX(&lock); /*带有“_LOCKED”的是将信号量初始化为0,即锁定,允许任何线程访问时必须先解锁。没带的为1,这种信号量在任何给定时刻只能由单个进程或线程拥有*/
- ret = misc_register(&misc);
- printk (DEVICE_NAME" initialized\n");
- return ret;
- }
-
- static void __exit dev_exit(void)
- {
- misc_deregister(&misc);
- // printk("<1> display a little,hei hei \n\n ");
- }
- module_init(dev_init);
- module_exit(dev_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("luo");
- MODULE_DESCRIPTION("PWM Drivers for ok2440 Board");
- </span>
- <span style="font-size:14px;">#include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/ioctl.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- int main(int argc, char **argv)
- {
- int fd;
- unsigned long temp =0;
- int i;
- fd = open("/dev/PWM1", 0);
- if (fd < 0) {
- perror("open device EmbedSky-BEEP");
- exit(1);
- }
- printf("Please enter the times number (0 is stop) :\n");
- for(i=0;;i++)
- {
- scanf("%d",&temp);
- printf("times = %d \n",temp);
- if(temp == 0)
- {
- ioctl(fd, 0);
- printf("Stop Control Beep!\n");
- printf("Please enter the times number (0 is stop) :\n");
- // break;
- continue;
- }
- else
- {
- ioctl(fd, 1, temp);
- }
- }
- close(fd);
- return 0;
- }
- </span>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。