3回答

0收藏

OK6410 linux PWM驱动设计

嵌入式系统 嵌入式系统 3126 人阅读 | 3 人回复 | 2014-01-16


今天我为大家讲解下OK6410关于PWM蜂鸣器驱动的软件和硬件方面的一些知识点。首先我们为了写pwm的驱动程序我们先来了解下它的硬件电路和关于PWM的一些知识点。
在飞凌嵌入式OK6410开发板中,蜂鸣器的IO口为GPF15
关于GPF15GPIO详细参考S3C6410PDF手册
GPF14 [29:28] 00 = Input       01 = Output   10 = PWM TOUT[0]  11 = CLKOUT[0]
GPF15 [31:30] 00 = Input        01 = Output  10 = PWM TOUT[1]  11 = Reserved
下面我们重点介绍PWM定时器工作:
S3C6410X中有5个定时器,这些定时器产生内部中断。其中,Timer0和Timer1具有PWM功能,而Timer2,3,4没有此功能。
PWM具有两种操作模式:自动装载模式,一次触发模式。为实现PWM功能,芯片提供了16个功能寄存器。这些功能寄存器都连接APB总线。
定时器具有双缓冲特性,这样就能在不停止当前定时器操作的情况下,为下次定时器运行装入新的数值。尽管为定时器设置了新数值,但当前的定时操作能够成功完成。定时器从TCNTBn读取的值是为下次延时定时用的,并不影响当前定时器的运行。当TCNTn减小到0的时候,TCNTBn的值会自动复制到TCNTn中,这就是说的自动装载操作。定时器的当前计数值可以从定时计数观察寄存器中TCNTOn读取。如果TCNTn为0且从装载也为0的话则TCNTn不在进行下次操作。
在S3C6410的PDF手册中介绍PWM的工作流程:
The Pulse Width Modulation function (PWM) uses the value of the TCMPBn register. The timer control logic
changes the output level when the down-counter value matches the value of the compare register in the timer
control logic. Therefore, the compare register determines the turn-on time (or turn-off time) of a PWM output.
The TCNTBn and TCMPBn registers are double buffered to allow the timer parameters to be updated in the
middle of a cycle. The new values will not take effect until the current timer cycle completes.
A simple example of a PWM cycle is shown in the figure below.  
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-23666.png

1.  Initialize the TCNTBn with 159(50+109) and the TCMPBn with 109.  
2.  Start Timer by setting the start bit and manual update bit off.
The TCNTBn value of 159 is loaded into the down-counter, the output is driven low.
3.  When down-counter counts down to the value in the TCMPBn register 109,
the output is changed from low to high
4.  When the down-counter reaches 0, the interrupt request is generated.  
5.  The down-counter is automatically reloaded with TCNTBn, which restarts the cycle.
对于PWM 功能,要用到寄存器TCMPBn,当递减计数器down-counter的值和比较寄存器TCMPBn的值相同时,定时控制逻辑模块就会改变输出电平。因此比较寄存器TCMPBn决定了PWM的输出。
定时器0工作寄存器:
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-2011.png
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-17848.png
TCFG1 (Timer Configuration Register)
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-7879.png
TCON (Timer Control Register)
file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-8913.png
通过对上述内容的了解现在我们来写下PWM的驱动程序
1 #include <linux/module.h>  
2 #include <linux/kernel.h>  
3 #include <linux/fs.h>  
4 #include <linux/init.h>  
5 #include <linux/delay.h>  
6 #include <linux/poll.h>  
7 #include <asm/irq.h>  
8 #include <asm/io.h>  
9 #include <linux/interrupt.h>  
10 #include <asm/uaccess.h>  
11 #include <mach/hardware.h>  
12 #include <plat/regs-timer.h>  
13 #include <mach/regs-irq.h>  
14 #include <asm/mach/time.h>  
15 #include <linux/clk.h>  
16 #include <linux/cdev.h>  
17 #include <linux/device.h>  
18 #include <linux/miscdevice.h>  
19   
20 #include <mach/map.h>  
21 #include <mach/regs-clock.h>  
22 #include <mach/regs-gpio.h>  
23   
24 #include <plat/gpio-cfg.h>  
25 #include <mach/gpio-bank-e.h>  
26 #include <mach/gpio-bank-f.h>  
27 #include <mach/gpio-bank-k.h>  
28   
29 #define DEVICE_NAME     "pwm"  
30   
31 #define PWM_IOCTL_SET_FREQ      1  
32 #define PWM_IOCTL_STOP          0  
33   
34 static struct semaphore lock; //信号量  
35   
36 /* freq:  pclk/50/16/65536 ~ pclk/50/16   
37   * if pclk = 50MHz, freq is 1Hz to 62500Hz  
38   * human ear : 20Hz~ 20000Hz  
39   */  
40 static void PWM_Set_Freq( unsigned long freq )  
41 {  
42     unsigned long tcon;  
43     unsigned long tcnt;  
44     unsigned long tcfg1;  
45     unsigned long tcfg0;  
46   
47     struct clk *clk_p;  
48     unsigned long pclk;  
49   
50     unsigned tmp;  
51   
52     tmp = readl(S3C64XX_GPFCON);   
53     //tmp &= ~(0x3U << 28);  
54     //tmp |=  (0x2U << 28);  
55   
56          tmp &=~(0x3U << 30);  //GPF15  [31:30]    10 = PWM TOUT[1]   
57          tmp |=  (0x2U << 30);  
58   
59     writel(tmp, S3C64XX_GPFCON);  
60   
61     tcon = __raw_readl(S3C_TCON);  //定时器控制寄存器  
62     tcfg1 = __raw_readl(S3C_TCFG1); //时钟多路复用器和 DMA模式的选择  
63     tcfg0 = __raw_readl(S3C_TCFG0); //时钟预定标器和死区结构  
64   
65     //prescaler = 50   Prescaler 0 [7:0] R/W Prescaler 0 value for timer 0 & 1   
66     tcfg0 &= ~S3C_TCFG_PRESCALER0_MASK;  //{prescaler value} = 1~255   
67     tcfg0 |= (50 - 1);  //prescaler value=50  
68   
69     //mux = 1/16  TCFG1  Divider MUX0 [3:0] R/W Select Mux input for PWM Timer 0   
70     tcfg1 &= ~S3C_TCFG1_MUX0_MASK;  
71     tcfg1 |= S3C_TCFG1_MUX0_DIV16; // 0100: 1/16   
72   
73     __raw_writel(tcfg1, S3C_TCFG1);  
74     __raw_writel(tcfg0, S3C_TCFG0);  
75   
76   
77 /* clk_get获取一个名为id的时针  
78  * 输入参数dev:   可以为NULL  
79  * 输入参数id:    时针名称,fclkhclkpclk等  
80  * 返回值:        返回该时钟的clk结构体  
81  *  
82  *再将clk_get返回的clk结构体传递给clk_get_rate,获取该时钟的频率  
83  */  
84 /*PCLK is used for APB bus, which is used by the peripherals   
85  *such as WDT, IIS, I2C, PWM timer, MMC */  
86   
87     clk_p = clk_get(NULL, "pclk");  //获取一个名为id的时针  
88     pclk  = clk_get_rate(clk_p);  //获取该时钟的频率  
89     //定时器输入时钟频率   
90     //Timer input clock Frequency = PCLK / ( {prescaler value + 1} ) / {divider value}   
91     tcnt  = (pclk/50/16)/freq;   
92   
93     __raw_writel(tcnt, S3C_TCNTB(0)); //TCNTB0:定时器0计数缓冲器。   
94     __raw_writel(tcnt/2, S3C_TCMPB(0));//TCMPB0:定时器0比较缓冲寄存器。   
95                  
96     tcon &= ~0x1f;  
97     tcon |= 0xb;        //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0  
98     __raw_writel(tcon, S3C_TCON);  
99      
100 /*Note: Manual update bit must be 1b0 before Start/Stop bit is 1b1.   
101    If Manual update bit is 1b1 and Start/Stop bit is 1b1,   
102    timer counter is not update by new value.   
103    Timer counter value is last value. */  
104      
105     tcon &= ~2;         //clear manual update bit  
106     __raw_writel(tcon, S3C_TCON);  
107 }  
108   
109 void PWM_Stop( void )  
110 {  
111     unsigned tmp;  
112     tmp = readl(S3C64XX_GPFCON);  
113     //tmp &= ~(0x3U << 28);  
114         tmp &= ~(0x3U << 30);  
115   
116     writel(tmp, S3C64XX_GPFCON);  
117 }  
118   
119 /* 该函数尝试获得信号量sem,如果能够立刻获得,  
120 它就获得该信号量并返回0,否则,返回非0值。  
121 它不会导致调用者睡眠,可以在中断上下文使用。  
122   
123 */  
124 static int s3c64xx_pwm_open(struct inode *inode, struct file *file)  
125 {  
126     if (!down_trylock(&lock))  
127         return 0;  
128     else  
129         return -EBUSY;  
130 }  
131   
132   
133 static int s3c64xx_pwm_close(struct inode *inode, struct file *file)  
134 {  
135     up(&lock);  //该函数释放信号量sem,唤醒等待者  
136     return 0;  
137 }  
138   
139   
140 static long s3c64xx_pwm_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)  
141 {  
142     switch (cmd) {  
143         case PWM_IOCTL_SET_FREQ:  
144             if (arg == 0)  
145                 return -EINVAL;  
146             PWM_Set_Freq(arg);  
147             break;  
148   
149         case PWM_IOCTL_STOP:  
150         default:  
151             PWM_Stop();  
152             break;  
153     }  
154   
155     return 0;  
156 }  
157   
158   
159 static struct file_operations dev_fops = {  
160     .owner          = THIS_MODULE,  
161     .open           = s3c64xx_pwm_open,  
162     .release        = s3c64xx_pwm_close,   
163     .unlocked_ioctl = s3c64xx_pwm_ioctl,  
164 };  
165   
166 static struct miscdevice misc = {  
167     .minor = MISC_DYNAMIC_MINOR,  
168     .name = DEVICE_NAME,  
169     .fops = &dev_fops,  
170 };  
171   
172 static int __init dev_init(void)  
173 {  
174     int ret;  
175   
176 /* 该函数用于初始化一个互斥锁,即它把信号量sem的值设置为1,  
177 等同于sema_init (struct semaphore *sem, 1)*/  
178     init_MUTEX(&lock);  
179     ret = misc_register(&misc);  
180   
181     printk (DEVICE_NAME"\tinitialized\n");  
182         return ret;  
183 }  
184   
185 static void __exit dev_exit(void)  
186 {  
187     misc_deregister(&misc);  
188 }  
189   
190 module_init(dev_init);  
191 module_exit(dev_exit);  
192 MODULE_LICENSE("GPL");  
193 MODULE_AUTHOR("FORLINX Inc.");  
194 MODULE_DESCRIPTION("S3C6410 PWM Driver");  

分享到:
回复

使用道具 举报

回答|共 3 个

倒序浏览

沙发

mqqjqr5566

发表于 2014-1-16 21:17:42 | 只看该作者

linux难吗?           
板凳

ddllxxrr

发表于 2014-1-17 08:06:24 | 只看该作者

楼主真是牛人
http://shop34182318.taobao.com/
https://shop436095304.taobao.com
地板

forlinx2011

发表于 2014-1-17 10:18:29 | 只看该作者

ddllxxrr 发表于 2014-1-17 08:06
楼主真是牛人

                                                                                         
您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条