• 【Linux开发】linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟


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


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

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

    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    一、程序不能工作


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

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

    1、中断注册失败:

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

    make menuconfig

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

    2Device Drivers ---> Character devices --->[ ] ADC driver for FriendlyARM Mini2440/QQ2440 development boards


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


    2、使能adc时钟


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

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

    该网友的博客上有更详细的介绍,可以看看,虽然是2410的。

    2410下clock源码分析

    1arch/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));

    268         s3c24xx_init_clocks(12000000); //初始化系统时钟

    269         s3c24xx_init_uarts(mini2440_uartcfgs, ARRAY_SIZE(mini2440_uartcfgs));

    270 }

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

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

    75 void __init s3c24xx_init_clocks(int xtal)

    76 {

    77         if (xtal == 0)

    78                 xtal = 12*1000*1000;

    79

    80         if (cpu == NULL)

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

    82

    83        if (cpu->init_clocks == NULL)

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

    85         else

    86                 (cpu->init_clocks)(xtal); //查找struct cpu_table

    87 }

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

    3arch/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

    131         s3c24xx_register_baseclocks(xtal); //三个步骤,接下来看一下

    132         s3c244x_setup_clocks();

    133         s3c2410_baseclk_add();

    134 }

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

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

    340 int __init s3c24xx_register_baseclocks(unsigned long xtal)

    341 {

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

    343

    344         clk_xtal.rate = xtal;

    345

    346 /* register our clocks */

    347

    348         if (s3c24xx_register_clock(&clk_xtal) < 0) //注册时钟

    349                printk(KERN_ERR "failed to register master xtal ");

    350

    351         if (s3c24xx_register_clock(&clk_mpll) < 0)

    352                 printk(KERN_ERR "failed to register mpll clock ");

    353

    354         if (s3c24xx_register_clock(&clk_upll) < 0)

    355                 printk(KERN_ERR "failed to register upll clock ");

    356

    357         if (s3c24xx_register_clock(&clk_f) < 0)

    358                 printk(KERN_ERR "failed to register cpu fclk ");

    359

    360         if (s3c24xx_register_clock(&clk_h) < 0)

    361                 printk(KERN_ERR "failed to register cpu hclk ");

    362

    363         if (s3c24xx_register_clock(&clk_p) < 0)

    364                 printk(KERN_ERR "failed to register cpu pclk ");

    365

    366         return 0;

    367 }

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

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

    76 void __init_or_cpufreq s3c244x_setup_clocks(void)

    77 {

    78         struct clk *xtal_clk;

    79         unsigned long clkdiv;

    80         unsigned long camdiv;

    81         unsigned long xtal;

    82         unsigned long hclk, fclk, pclk;

    83         int hdiv = 1;

    84

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

    86         xtal = clk_get_rate(xtal_clk);

    87         clk_put(xtal_clk);

    88

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

    90

    91         clkdiv = __raw_readl(S3C2410_CLKDIVN);

    92         camdiv = __raw_readl(S3C2440_CAMDIVN);

    93

    94 /* work out clock scalings */

    95

    96         switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) {

    97         case S3C2440_CLKDIVN_HDIVN_1:

    98                 hdiv = 1;

    99                 break;

    100

    101         case S3C2440_CLKDIVN_HDIVN_2:

    102                 hdiv = 2;

    103                 break;

    104

    105         case S3C2440_CLKDIVN_HDIVN_4_8:

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

    107                 break;

    108

    109         case S3C2440_CLKDIVN_HDIVN_3_6:

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

    111                 break;

    112         }

    113

    114         hclk = fclk / hdiv;

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

    116

    117 /* print brief summary of clocks, etc */

    118

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

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

    121

    122         s3c24xx_setup_clocks(fclk, hclk, pclk);

    123 }

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


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

    211 int __init s3c2410_baseclk_add(void)

    212 {

    213         unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);

    214         unsigned long clkcon = __raw_readl(S3C2410_CLKCON);

    215         struct clk *clkp;

    216         struct clk *xtal;

    217         int ret;

    218         int ptr;

    219

    220         clk_upll.enable = s3c2410_upll_enable;

    221

    222         if (s3c24xx_register_clock(&clk_usb_bus) < 0)

    223                 printk(KERN_ERR "failed to register usb bus clock ");

    224

    225 /* register clocks from clock array */

    226

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

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

    229 /* ensure that we note the clock state */

    230

    231                 clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;

    232

    233                 ret = s3c24xx_register_clock(clkp); //注册并使能

    234                 if (ret < 0) {

    235                         printk(KERN_ERR "Failed to register clock %s (%d) ",

    236                         clkp->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

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

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

    254

    255         ret = s3c24xx_register_clock(clkp); //注册,默认使能

    256         if (ret < 0) {

    257                 printk(KERN_ERR "Failed to register clock %s (%d) ",

    258                 clkp->name, ret);

    259         }

    260

    261         s3c2410_clkcon_enable(clkp, 0); //使能后又将成员disable

    262         }

    263

    264 /* show the clock-slow value */

    265

    266         xtal = clk_get(NULL, "xtal");

    267

    268         printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s ",

    269         print_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

    275         s3c_pwmclk_init();

    276         return 0;

    277 }


    48 int s3c2410_clkcon_enable(struct clk *clk, int enable)

    49 {

    50         unsigned int clocks = clk->ctrlbit;

    51         unsigned long clkcon;

    52

    53         clkcon = __raw_readl(S3C2410_CLKCON);

    54

    55         if (enable)

    56                 clkcon |= clocks;

    57         else

    58                 clkcon &= ~clocks; //传入参数为0,所以是disable

    59

    60 /* ensure none of the special function bits set */

    61         clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);

    62

    63         __raw_writel(clkcon, S3C2410_CLKCON);

    64

    65         return 0;

    66 }

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

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

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


    init_clockinit_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,

    。。。。。。

    可以看到,lcdgpio等这类的时钟是系统启动时就已经使能了,所以之前我的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_idclk->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

    源代码: 13th_ts_input.rar   

  • 相关阅读:
    微信红包限额提升方法
    微信从业人员推荐阅读的100本经典图书
    微信裂变红包
    微信公众平台开发最佳实践(第2版)
    微信公众平台开发(108) 微信摇一摇
    微信支付样例
    微信行业解决方案
    牛逼顿
    微信支付开发(4) 扫码支付模式二
    微信公众平台开发(107) 分享到朋友圈和发送给好友
  • 原文地址:https://www.cnblogs.com/huty/p/8518544.html
Copyright © 2020-2023  润新知