linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

这节的内容说不上是驱动,只是写个代码让触摸屏能够工作,随便介绍一下时钟子系统(我不知道这样叫合不合适),仅次而已。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

一、程序不能工作

程序的源代码在13th_ts_input/13th_ts_input/1st。大致的操作就是配置寄存器,设置触摸屏为自动坐标转换模式,具体请根据程序对照S3C2440文档。

但是写完的程序不能工作,检查原因。

1、中断注册失败:

cat

/prov/interrupt就知道,系统已经注册了adc和tc中断,为了能让我的模块加载成功,内核编译时不能加入adc和触摸屏驱动。

make

menuconfig

1、Device

Drivers ---> Input device support --->[ ] Touchscreens

2、Device

Drivers ---> Character devices --->[ ] ADC driver for

FriendlyARM Mini2440/QQ2440 development boards

重新编译后启动,模块加载成功,但触摸屏还是不能工作。

2、使能adc时钟

如果有编写过裸机程序的应该知道,ad转换和触摸屏的正常工作还依赖于时钟控制寄存器的配置,必须使能adc时钟(CLKCON[15])。

但在linux下,有一套专门的规矩来使能时钟,接下来先从内核启动时的代码开始介绍:

该网友的博客上有更详细的介绍,可以看看,虽然是2410的。2410下clock源码分析

1、arch/arm/mach-s3c2440/mach-mini2440.c

265

static void __init mini2440_map_io(void)

266

{

267

s3c24xx_init_io(mini2440_iodesc, ARRAY_SIZE(mini2440_iodesc));

268s3c24xx_init_clocks(12000000);

//初始化系统时钟

269s3c24xx_init_uarts(mini2440_uartcfgs,

ARRAY_SIZE(mini2440_uartcfgs));

270

}

内核启动时会通过mini2440_map_io函数调用s3c24xx_init_clocks来初始化系统时钟,接下来看一下函数原型。

2、arch/arm/plat-s3c/init.c

75

void __init s3c24xx_init_clocks(int xtal)

76

{

77if (xtal == 0)

78xtal = 12*1000*1000;

79

80if (cpu == NULL)

81panic("s3c24xx_init_clocks: no cpu setup?\n");

82

83if (cpu->init_clocks == NULL)

84panic("s3c24xx_init_clocks: cpu has no clock init\n");

85else

86(cpu->init_clocks)(xtal);

//查找struct

cpu_table

87

}

函数的原型很简单,就是调用了cpu结构里面的成员init_clocks。接下来看一下2440的cpu结构体在里面的成员。

3、arch/arm/plat-s3c24xx/cpu.c

68

static struct cpu_table cpu_ids[] __initdata = {

69

{

70.idcode = 0x32410000,

71.idmask = 0xffffffff,

72.map_io = s3c2410_map_io,

73.init_clocks = s3c2410_init_clocks,

74.init_uarts = s3c2410_init_uarts,

75.init = s3c2410_init,

76.name = name_s3c2410

77

},

。。。。。

87

{

88.idcode = 0x32440000,

89.idmask = 0xffffffff,

90.map_io = s3c244x_map_io,

91.init_clocks = s3c244x_init_clocks,

//2440的init_clocks函数原型在这里

92.init_uarts = s3c244x_init_uarts,

93.init = s3c2440_init,

94.name = name_s3c2440

95

},

可以看,cpu->init_clocks的原型就是s4c244x_init_clocks,继续看该函数里面做了什么操作

4、/arch/arm/plat-s3c24xx/s3c244x.c

125

void __init s3c244x_init_clocks(int xtal)

126

{

127

/* initialise the clocks here, to allow other things like the

128

* console to use them, and to add new ones after the

initialisation

129

*/

130

131s3c24xx_register_baseclocks(xtal);

//三个步骤,接下来看一下

132s3c244x_setup_clocks();

133s3c2410_baseclk_add();

134

}

函数里面有三个操作,接下来逐个逐个看。

5、arch/arm/plat-s3c/clock.c

340

int __inits3c24xx_register_baseclocks(unsigned

long xtal)

341

{

342printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec

Electronics\n");

343

344clk_xtal.rate = xtal;

345

346

/* register our clocks */

347

348if (s3c24xx_register_clock(&clk_xtal)

< 0)

//注册时钟

349printk(KERN_ERR "failed to register masterxtal\n");

350

351if (s3c24xx_register_clock(&clk_mpll) < 0)

352printk(KERN_ERR "failed to registermpll

clock\n");

353

354if (s3c24xx_register_clock(&clk_upll) < 0)

355printk(KERN_ERR "failed to registerupllclock\n");

356

357if (s3c24xx_register_clock(&clk_f) < 0)

358printk(KERN_ERR "failed to register cpufclk\n");

359

360if (s3c24xx_register_clock(&clk_h) < 0)

361printk(KERN_ERR "failed to register cpuhclk\n");

362

363if (s3c24xx_register_clock(&clk_p) < 0)

364printk(KERN_ERR "failed to register cpupclk\n");

365

366return 0;

367

}

跟名字一样,s3c24xx_register_baseclocks注册了系统中基本所需的时钟,如pclk等

6、arch/arm/plat-s3c24xx/s3c244x.c

76

void __init_or_cpufreqs3c244x_setup_clocks(void)

77

{

78struct clk *xtal_clk;

79unsigned long clkdiv;

80unsigned long camdiv;

81unsigned long xtal;

82unsigned long hclk, fclk, pclk;

83int hdiv = 1;

84

85xtal_clk = clk_get(NULL, "xtal");

86xtal = clk_get_rate(xtal_clk);

87clk_put(xtal_clk);

88

89fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2;

90

91clkdiv = __raw_readl(S3C2410_CLKDIVN);

92camdiv = __raw_readl(S3C2440_CAMDIVN);

93

94

/* work out clock scalings */

95

96switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) {

97case S3C2440_CLKDIVN_HDIVN_1:

98hdiv = 1;

99break;

100

101case S3C2440_CLKDIVN_HDIVN_2:

102hdiv = 2;

103break;

104

105case S3C2440_CLKDIVN_HDIVN_4_8:

106hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4;

107break;

108

109case S3C2440_CLKDIVN_HDIVN_3_6:

110hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3;

111break;

112}

113

114hclk = fclk / hdiv;

115pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN) ? 2 : 1);

116

117

/* print brief summary of clocks, etc */

118

119printk("S3C244X: core %ld.%03ld MHz, memory %ld.%03ld MHz,

peripheral %ld.%03ld MH z\n",

120print_mhz(fclk), print_mhz(hclk), print_mhz(pclk));

121

122s3c24xx_setup_clocks(fclk, hclk, pclk);

123

}

上面的函数通过从寄存器读取信息并给fclk、hclk和pclk赋值,然后通过s3c24xx_setup_clocks函数添加到指定结构体。

7、arch/arm/plat-s3c24xx/s3c2410-clock.c

211 int __init

s3c2410_baseclk_add(void)

212 {

213unsigned long clkslow =

__raw_readl(S3C2410_CLKSLOW);

214unsigned long clkcon =

__raw_readl(S3C2410_CLKCON);

215struct clk *clkp;

216struct clk *xtal;

217int ret;

218int ptr;

219

220clk_upll.enable =

s3c2410_upll_enable;

221

222if

(s3c24xx_register_clock(&clk_usb_bus) < 0)

223printk(KERN_ERR "failed

to register usb bus clock\n");

224

225 /* register clocks from

clock array */

226

227clkp =

init_clocks;

//该结构体中存放着需要enable的时钟成员,对应寄存器CLKCON

228for

(ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {

229 /* ensure that we note

the clock state */

230

231clkp->usage = clkcon

& clkp->ctrlbit ? 1 : 0;

232

233ret = s3c24xx_register_clock(clkp);

//注册并使能

234if (ret < 0) {

235printk(KERN_ERR

"Failed to register clock %s (%d)\n",

236clkp->name,

ret);

237}

238}

239

240 /* We must be careful

disabling the clocks we are not intending to

241 * be using at boot time, as

subsystems such as the LCD which do

242 * their own DMA requests to

the bus can cause the system to lockup

243 * if they where in the

middle of requesting bus access.

244 *

245 * Disabling the LCD clock

if the LCD is active is very dangerous,

246 * and therefore the

bootloader should be careful to not enable

247 * the LCD clock if it is

not needed.

248 */

249

250 /* install (and disable) the

clocks we do not need immediately */

251

252clkp =

init_clocks_disable;

//该结构体中存放着需要disable的时钟成员,对应寄存器CLKCON

253for(ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++,

clkp++) {

254

255ret = s3c24xx_register_clock(clkp);

//注册,默认使能

256if (ret < 0) {

257printk(KERN_ERR

"Failed to register clock %s (%d)\n",

258clkp->name,

ret);

259}

260

261s3c2410_clkcon_enable(clkp, 0);

//使能后又将成员disable

262}

263

264 /* show the clock-slow value

*/

265

266xtal = clk_get(NULL,

"xtal");

267

268printk("CLOCK: Slow

mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",

269print_mhz(clk_get_rate(xtal) /

270( 2 *

S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),

271(clkslow &

S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",

272(clkslow &

S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",

273(clkslow &

S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");

274

275s3c_pwmclk_init();

276return 0;

277 }

48 int s3c2410_clkcon_enable(struct

clk *clk, int enable)

49 {

50unsigned int clocks =

clk->ctrlbit;

51unsigned long clkcon;

52

53clkcon =

__raw_readl(S3C2410_CLKCON);

54

55if

(enable)

56clkcon |= clocks;

57else

58clkcon &= ~clocks;

//传入参数为0,所以是disable

59

60 /* ensure none of the

special function bits set */

61clkcon &=

~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);

62

63__raw_writel(clkcon,

S3C2410_CLKCON);

64

65return 0;

66 }

可以看到,s3c2410_baseclk_add分了两部分的操作。

第一部分:获取init_clock的数据结构,并且调用s3c24xx_register_clock来配置寄存器CLKCON并默认使能init_clock结构里面指定时钟。

第二部分:获取init_clock_disable数据结构,除了调用s3c24xx_register_clock注册后,还调用3c2410_clkcon_enable来配置寄存器CLKCON来disable该init_clock_disable结构体里面的时钟。

init_clock和init_clock_disable里面的成员与寄存器CLKCON的成员对应.

接下来看看clkp的数据结构init_clock在哪里定义:

/arch/arm/plat-s3c24xx/s3c2410-clock.c

130 static struct clk init_clocks[]

= {

131 {

132.name = "lcd",

133.id = -1,

134.parent = &clk_h,

135.enable =

s3c2410_clkcon_enable,

136.ctrlbit =

S3C2410_CLKCON_LCDC,

137 }, {

138.name = "gpio",

139.id = -1,

140.parent = &clk_p,

141.enable =

s3c2410_clkcon_enable,

142.ctrlbit =

S3C2410_CLKCON_GPIO,

143 }, {

144.name =

"usb-host",

145.id = -1,

146.parent = &clk_h,

。。。。。。

可以看到,lcd、gpio等这类的时钟是系统启动时就已经使能了,所以之前我的lcd驱动才能正常工作。

90 static struct clk

init_clocks_disable[] = {

91 {

92.name = "nand",

93.id = -1,

94.parent = &clk_h,

95.enable =

s3c2410_clkcon_enable,

96.ctrlbit =

S3C2410_CLKCON_NAND,

97 }, {

98.name = "sdi",

99.id = -1,

100.parent = &clk_p,

101.enable =

s3c2410_clkcon_enable,

102.ctrlbit =

S3C2410_CLKCON_SDI,

103 }, {

104.name = "adc",

//触摸屏的时钟放在

init_clocks_disable中,所以触摸屏不能工作!

105.id = -1,

106.parent = &clk_p,

107.enable =

s3c2410_clkcon_enable,

108.ctrlbit =

S3C2410_CLKCON_ADC,

109 }, {

。。。。。

现在就可以知道,为什么触摸屏不能工作了,因为在系统启动时被禁止了。所以需要在函数中启动它。

既然知道了原因,解决方法就好办了——将adc成员从init)clocks_disable中删除并添加到init_clocks中。

同时还有一个不用修改内核代码的方法。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

二、clk_enable:

第二个方法很简单,从获取内核中的adc时钟,然后将它使能。

先看获取函数:

struct clk *clk_get(struct device

*dev, const char *con_id)

第一个参数中,因为clk->id一般为-1,所以直接传入NULL就可以了。如果clk->id不为-1,函数会通过第一个参数传入的dev获取dev->bus_id。

第二个参数是一个字符串,用来指定你要获取的时钟的名字。

参数传入后,内核查找到一个dev->id(或者-1)是否与clk->id一致,并且con_id与clk->name一致的时钟,如果不一致就会获取失败。

所以,我这里的函数应该是:clk_get(NULL,

“adc”)。

使能时钟使用函数:

int clk_enable(struct clk *clk)

禁止时钟使用函数:

void clk_disable(struct clk *clk)

在原来的函数中使用这三条代码后(13th_ts_input/13th_ts_input/2nd),触摸屏就能工作了。看看效果:

[root: 2nd]# insmod ts_driver.ko

hello ts

[root: 2nd]# tc down

//当接触触摸屏是打印

x:0, y:947

tc up

tc down

x:382, y:608

tc up

tc down

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

三、防抖(13th_ts_input/13th_ts_input/3rd)

同样的,最后在代码中添加个定时器,实现防抖功能。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

linux adc时钟设置,linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟相关推荐

  1. linux设备驱动归纳总结

    前言: (总结已经基本写完,这段时间我会从新排版和修正.错误总会有的,望能指正!) 前段时间学习了嵌入式驱动,趁着没开始找工作,这段时间我会每天抽出时间来复习. 我的总结是根据学习时的笔记(李杨老师授 ...

  2. 【Linux开发】linux设备驱动归纳总结(一):内核的相关基础概念

    linux设备驱动归纳总结(一):内核的相关基础概念 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  3. linux设备驱动归纳总结(六):2.分享中断号【转】

    linux设备驱动归纳总结(六):2.分享中断号 转自:http://blog.chinaunix.net/uid-25014876-id-90837.html xxxxxxxxxxxxxxxxxxx ...

  4. linux驱动内核哪个文件夹,linux设备驱动归纳总结(一):内核的相关基础概念...

    linux设备驱动归纳总结(一):内核的相关基础概念 1. 内核与 linux 设备驱动的作用与关系 内核:用于管理软硬件资源,并提供运行环境.如分配 4G 虚拟空间等. linux 设备驱动:是连接 ...

  5. linux块设备驱动中断程序,linux设备驱动归纳总结(六):1.中断的实现

    linux设备驱动归纳总结(六):1.中断的实现 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  6. linux设备驱动归纳总结--转载小白的博客

    主要是为了自己浏览方便... linux设备驱动归纳总结 linux设备驱动归纳总结(一):内核的相关基础概念 linux设备驱动归纳总结(二):模块的相关基础概念 linux设备驱动归纳总结(三): ...

  7. linux设备驱动归纳总结(十一):简单的看门狗驱动

    http://blog.chinaunix.net/uid-25014876-id-112879.html 设备驱动的归纳已经差不多了,趁着知识点还没有遗忘,写点代码巩固一下,来个简单的看门狗驱动-- ...

  8. linux设备驱动归纳总结(一)内核的相关基础概念【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-59413.html linux设备驱动归纳总结(一):内核的相关基础概念 xxxxxxxxxxxxxx ...

  9. 【Linux开发】linux设备驱动归纳总结(七):2.内核定时器

    linux设备驱动归纳总结(七):2.内核定时器 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

最新文章

  1. Android Application中的Context和Activity中的Context的异同
  2. 一个笑话,关于哲人和普通人的
  3. python的时间差计算
  4. 基于 Flink+Iceberg 构建企业级实时数据湖 | 附 PPT 下载
  5. 尝试修改源码需要用到git存一下
  6. 坚持一个好习惯该有多难?
  7. centos 宝塔面板 mongodb 给新的数据库 创建登录用户和密码
  8. Bootstrap -- 初见 Bootstrap
  9. 我的第一个 RN 项目-趣闻
  10. 航空8联货运单的作用详解
  11. 火狐浏览器国内版和国际版区别
  12. 基于FPGA的DDS混频及原理
  13. [转]周易入门三十五问答
  14. 送女朋友实用的七夕礼物、送女朋友七夕礼物最有意义排行榜
  15. (转)A SQL query walks into a bar and sees two tables. He walks up to them and says 'Can I join you?'
  16. unity使用tiled文件,将数据用unity打开,可以无限tiled地图
  17. Andorid-15k+的面试题。
  18. ssi 指令 php,SSI使用详解(一)_php
  19. python判断路径是文件还是文件夹_python 判断文件还是文件夹的简单实例
  20. python如何爬取动态网页数据

热门文章

  1. 数控matlab实验心得,matlab实验心得体会
  2. 【详细】web项目集成百度商桥步骤 简单修改默认样式 咨询栏可收起与展开
  3. 厚黑经 (白话译文)
  4. 基于WebApp的服装个性化定制订单管理系统
  5. 百度网盟内容匹配广告和展示广告相关技术
  6. 资源网CMS模板价值298元
  7. 课程学习归纳总结(9月)
  8. H5集成环信webIM即时通讯
  9. 线控性能比拼,MKZ与CRV作为自动驾驶开发平台的全面测评
  10. CC2642 OAD文件合成