Linux设备驱动(五)–系统时钟与定时器

时钟脉冲:脉冲信号是一个按一定电压幅度,一定时间间隔连续发出的脉冲信号,脉冲信号之间的时间间隔称为周期。
时钟频率:在单位时间(如1秒)内产生的时钟脉冲个数。
频率的其相应的单位有:Hz(赫)、kHz(千赫)、MHz(兆赫)、GHz(吉赫);其中1GHz=1000MHz,1MHz=1000kHz.1kHz=1000Hz。

s3c2440有外部晶振XTIPLL和外部时钟频率EXTCLK.并有两个PLL(锁相环):MPLL和UPLL.
UPLL:USB设备专用

  • UCLK:USB设备的时钟频率

MPLL:用于CPU和其他外围设备,MPLL会产生3个部分的时钟频率:FCLK、HCLK、PCLK

  • FCLK:用于CPU核
  • HCLK:用于高速总线设备
  • PCLK:用于低速总线设备

下图为PLL设置流程图:

LOCKTIME寄存器设置Lock Time所需的时间

MPLLCON寄存器设置FCLK和Fin(时钟源频率)的倍数
通过MPLLCON寄存器设置MDIV、PDIV、SDIV三个值得到FCLK与Fin的倍率
FCLK和Fin的计算公式:

MPLL(FCLK)= (2*m*Fin)/(p*2^s)
m=MDIV+8 p=PDIV+2 s=SDIV

CLKDIVN寄存器设置FCLK、HCLK、PCLK三者的比例

定时器输出时钟频率=PCLK/{prescaler value+ 1}/{divider value}
{prescaler value+ 1} = 0 ~ 255
prescaler value=TCFG0
{divider value} = 2 , 4 , 8 , 16
divider value=TCFG1

TCON:设置装载定时器计数值,并启动定时器.

定时器内部控制逻辑的工作流程:
①初始化或者获取PCLK值。
②程序初始,设置TCMPBn、TCNTBn这两个寄存器,它们表示定时器n的比较值、初始计数值。
③随之设置TCON寄存器启动定时器n,这时,TCMPBn、TCNTBn的值将被装入其内部寄存器TCMPn、TCNTn中。在定时器n的工作频率下,TCNTn开始减一计数,其值可以通过读取TCNTOn寄存器得知。
④当TCNTn的值等于TCMPn的值时,定时器n的输出管脚TOUTn反转;TCNTn继续减一计数。
⑤当TCNTn的值到达0时,其输出管脚TOUTn再次反转,并触发定时器n的中断。
⑥当TCNTn的值到达0时,如果在TCON寄存器中将定时器n设为“自动加载”,则TCMPB0和TCNTB0寄存器的值被自动装入TCMP0和TCNT0寄存器中,下一个计数流程开始。
定时器n的输出管脚TOUTn初始状态为高电平,以后在TCNTn的值等于TCMPn的值、TCNTn的值等于0时反转。

下图为详细的工作流程图:

最后通过例子结合芯片手册的定时器设置章节理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#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>
 
#define DEVICE_NAME "motor"
 
#define Age_Life	10
#define Back_Life	110
#define Car_Stop	00
 
static struct cdev *motor_cdev;
static dev_t devno;
static struct semaphore lock;
 
 
static void Motor_Ferg(unsigned long freq,unsigned int direction)
{
	unsigned long tcon;
	unsigned long tcnt;
	unsigned long tcfg1;
	unsigned long tcfg0;
 
	struct clk *clk_p;
	unsigned long pclk;
 
        /*设置GPH10为OUTPUT模式*/
	s3c2410_gpio_cfgpin(S3C2410_GPH(10),S3C2410_GPIO_OUTPUT);
	/*设置GPH10的转向,由direction控制方向*/
	s3c2410_gpio_setpin(S3C2410_GPH(10),direction);
	/*设置GPB1为定时器1模式*/
	s3c2410_gpio_cfgpin(S3C2410_GPB(1),S3C2410_GPB1_TOUT1);
 
        /*读取TCON、TCFG1、TCFG0三个寄存器的值*/
	tcon	= __raw_readl(S3C2410_TCON);
	tcfg1	= __raw_readl(S3C2410_TCFG1);
	tcfg0	= __raw_readl(S3C2410_TCFG0);
 
        /*定时器0和1的预分频值的掩码*/
	tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
	/*设置预分频率为50赫兹*/
	tcfg0 |=  (50-1);
 
        /*定时器定1分频值的掩码*/
	tcfg1 &= ~S3C2410_TCFG1_MUX1_MASK;
	/*设置定时器1为16分频*/
	tcfg1 |= S3C2410_TCFG1_MUX1_DIV16;
 
        /*将设置好的分频值写入寄存器TCFG0和TCFG1当中*/
	__raw_writel(tcfg1, S3C2410_TCFG1);
        __raw_writel(tcfg0, S3C2410_TCFG0);
 
        /*获取PCLK值*/
	clk_p = clk_get(NULL,"pclk");
	pclk  = clk_get_rate(clk_p);
 
        /*得到定时器的输入时钟,进而设置PWM的调制频率*/
	tcnt  = (pclk/50/16)/freq;
 
        /*设置TCNTB1和TCMPB1的值并写入寄存器*/
	__raw_writel(tcnt, S3C2410_TCNTB(1));
        __raw_writel(tcnt/2, S3C2410_TCMPB(1));
 
        /*设置定时器1启动,装载TCNTB1和TCMPB1并自动重载模式*/
	tcon &= ~0xff0;
	tcon |= 0xb00;
 
        /*写入寄存器TCON*/
	__raw_writel(tcon,S3C2410_TCON);
 
        /*设置TCNTB1和TCMPB1的值手动更新并写入寄存器*/
	tcon &= ~0x600;
	__raw_writel(tcon,S3C2410_TCON);
}
 
/*停止运作函数*/
static void Motor_Stop(void)
{
	s3c2410_gpio_cfgpin(S3C2410_GPB(1),S3C2410_GPIO_OUTPUT);
	s3c2410_gpio_setpin(S3C2410_GPB(1),0);
	s3c2410_gpio_cfgpin(S3C2410_GPH(10),S3C2410_GPIO_OUTPUT );
	s3c2410_gpio_setpin(S3C2410_GPH(10),0);
}
 
/*控制函数*/
static long motor_ioctl(
		struct file *file,
		unsigned int cmd,
		unsigned long arg)
{
    unsigned int direction;
	switch(cmd){
		case Age_Life:
            direction=1;
			Motor_Ferg(arg,direction);
			break;
 
        case Back_Life:
            direction=0;
            Motor_Ferg(arg,direction);
            break;
 
		case Car_Stop:
			Motor_Stop();
			break;
	}
	return 0;
}
 
/*打开函数*/
static int motor_open(struct inode *inode,struct file *file)
{
	if(!down_trylock(&lock))
		return	0;
	else
		return -EBUSY;
}
 
/*关闭函数*/
static int motor_release(struct inode *inode,struct file *file)
{
	Motor_Stop();
	up(&lock);
	return	0;
}
 
/*设置各函数*/
static struct file_operations motor_fops=
{
	.owner		= THIS_MODULE,
	.unlocked_ioctl = motor_ioctl,
	.release	= motor_release,
	.open		= motor_open,
};
 
/*注册驱动函数*/
static int __init motor_init(void)
{
	int ret,err;
	int major,minor;
	major=200;
	minor=0;
 
	sema_init(&lock,1);
	ret=register_chrdev_region(major,2,DEVICE_NAME);
 
	if(ret<0)
		return ret;
 
	devno=MKDEV(major,minor);
 
	motor_cdev=cdev_alloc();
	cdev_init(motor_cdev,&motor_fops);
	err=cdev_add(motor_cdev,devno,2);
	if(err)
	{
		unregister_chrdev_region(devno,2);
		return -EFAULT;
	}
 
	printk(DEVICE_NAME "\tinitialized!\n");
	return ret;
}
 
/*注销驱动函数*/
static void __exit motor_exit(void)
{
	cdev_del(motor_cdev);
	unregister_chrdev_region(devno,2);
	printk(DEVICE_NAME "\tunloaded\n");
}
 
module_init(motor_init);
module_exit(motor_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yayi");

发表评论

电子邮件地址不会被公开。 必填项已用*标注