copyleft 发表于 2014-5-26 15:17:37

如何写TQ210 Android4,0.4 内核ADC驱动

本帖最后由 copyleft 于 2014-6-3 09:25 编辑

    环境:TQ210 Board v4, coreB, android4.0.4
    内核配置文件:使用天嵌所给的内核配置文件config_TQ210.......
    要求:TQ210共10个AD通道,AIN6-AIN9用于电阻屏的坐标采集通道,要能正常使用,AIN0-AIN5接传感器。在内核中写ADC的驱动,读取这6个通道的值,上层通过 read 分时读取这6个通道的值。

    尝试过的方法:在 mach-s5pv210 下已经有adc.c的驱动文件了,查看里面的代码发现:int s3c_adc_get_adc_data(int ch) 已经通过 EXPORT_SYSMBOL(s3c_adc_get_adc_data),将其导出,所以我在我的驱动文件中,直接:#include <mach/adc.h> 然后再调用 s3c_adc_get_adc_data ,保存其值。重新编译内核后,下载到板子上出现错误:modules linked in xxxxx 查看相应的信息发现错误在 s3c_adc_get_adc_data 调用上。我也尝试过将其编译成一个ko文件,再系统启动,加载adc驱动后,再用insmod命令加载我自己的驱动,也出现了同样的错误信息。


    请问:我该使用什么方法才能实现:在接有电阻触摸屏的情况下,能正常使用AIN0-AIN5这6个通道?
               或者直接将 mach-s5pv210 下的 adc.c 中的大部分内容复制到我的驱动文件中,然后再根据情况使用?
               可不可以复用已经写好的adc驱动?
谢谢大家了!

Calvin 发表于 2014-5-27 09:38:54

你有没有仔细查看adc.c文件?那里不是有个 ADC_WITH_TOUCHSCREEN宏么?define了这宏之后也不行么?

copyleft 发表于 2014-5-27 09:54:07

Calvin 发表于 2014-5-27 09:38
你有没有仔细查看adc.c文件?那里不是有个 ADC_WITH_TOUCHSCREEN宏么?define了这宏之后也不行么?

#define ADC_WITH_TOUCHSCREEN
在adc.c的开关部分就这样定义了,adc.c,tq210_ts.c我都没有对里面的宏或者寄存器进行设置,修改,只是查看它们里面的内容。也就是:我只是调用了s3c_adc_get_adc_data(i) 这个函数,上面的信息是我将我所写的驱动弄成一个ko,开机过后再insmod进去的信息,如果直接编译进内核也会得到相同的信息。
    我的驱动是根据drivers/power/test_power.c进行修改了,只是想在里面加入ADC,将其设计成定时采集AIN1,然后将AD值除以100,认为是电量值上报给系统,其它地方都没有修改

Calvin 发表于 2014-5-27 13:57:34

copyleft 发表于 2014-5-27 09:54
#define ADC_WITH_TOUCHSCREEN
在adc.c的开关部分就这样定义了,adc.c,tq210_ts.c我都没有对里面的宏或 ...
这样做我没有做过,需要你自行研究一下这些驱动的逻辑问题了;成功以后可以分享一下

copyleft 发表于 2014-5-27 14:27:01

Calvin 发表于 2014-5-27 13:57
这样做我没有做过,需要你自行研究一下这些驱动的逻辑问题了;成功以后可以分享一下

可以
成功后总结一下。。。

copyleft 发表于 2014-5-27 15:08:33

Calvin 发表于 2014-5-27 13:57
这样做我没有做过,需要你自行研究一下这些驱动的逻辑问题了;成功以后可以分享一下

对了
我想问一下:如果将ADC的所有通道都做成一般的AD输入口,在内核 配置中将TS禁止掉,该怎么配置ADC的寄存器?

我试着将 mach-tq210/adc.c 中的一些相关配置方法移植到驱动中:在初始化函数调用中通过 ioremap 将0xe1700000进行映射,然后设置:S3C_ADCCON_PRSCVL 为19,S3C_ADCDLY=100,S3C_ADCCON_RESSEL_12BIT,S3C_ADCCON_STDBM,并设置成当读取时转换。读取AD值时,通过调用与 s3c_adc_convert 类似功能的函数(函数名不同,但对寄存器的配置相同),读取AD值,但这样会得到 :unable to handle kernel paging request at virtual address f583d004 pgd=c0004000 ........... LR is at ioremap_page_range+0xf0/0x15c ............ 的错误信息
不知道我这种方法是不是正确?请问有出现这个错误可能会有哪些,通过网上查找,我没有找到这个错误出现的具体原因。不知道我应该怎么修改才能改正这个错误,或者有其它的方法。
我试过将内核与文件 系统都下载 成TQ210出厂时的android镜像文件,然后再启动系统里面自带的ADC测试 程序,得到的结果是程序没有响应(等待一段时间后,会弹出:是否等待或关闭这个应用程序,重新启动后,不打开任何一个AD通道,可以直接退出),我好像以前测试过ADC是可以使用的,但现在不知道为什么不能使用了。接上电阻触摸屏后却可以正常响应触摸。不知道这里面是什么出现了问题,原本 想通过这个ADC驱动看能不能解决 问题,到最后还是没能解决 问题。。。。

Calvin 发表于 2014-5-27 18:01:23

copyleft 发表于 2014-5-27 15:08
对了
我想问一下:如果将ADC的所有通道都做成一般的AD输入口,在内核 配置中将TS禁止掉,该怎么配置ADC ...

你试下去掉这个#define ADC_WITH_TOUCHSCREEN

copyleft 发表于 2014-5-28 15:47:10

Calvin 发表于 2014-5-27 18:01
你试下去掉这个#define ADC_WITH_TOUCHSCREEN

这个我试试
我将对ADC寄存器的配置写到我的驱动文件中,得到 的结果是:TSADCCON0中的ADC转换完成标志一直都是0,也就是始终没有转换完成。其代码如下:
probe函数:
static int __init test_adc_bat_probe(struct platform_device *pdev)
{
    int ret = -1;
   
    /* for adc: AIN0 - AIN9 */
    adc_base_addr = ioremap(S5PV210_PA_ADC + TSADCCON0, 0x1024);
    if(adc_base_addr == NULL)
    {
      ret = -ENOENT;
      printk(KERN_ERR "%s: ioremap for adc failed", __func__);
      return ret;
    }
   
    adc_normal_mode();

    /* set clk */
    adc_clk = clk_get(&pdev->dev, "adc");
    if(IS_ERR(adc_clk))
    {
      printk(KERN_ERR "%s: get adc clk failed!\n", __func__);
      return -1;
    }

    /* anable clk */
    clk_enable(adc_clk);

    /* for irq_adc */
    ret = request_irq(adc_irq_res.start, adc_irq_func, 0, "adc_irq_func", (void *)pbat);
    if(ret != 0)
    {
      printk(KERN_ERR "%s: request irq for adc failed!\n", __func__);
      return -1;
    }

    ret = test_power_init(pdev);
    if(ret != 0)
    {
      printk(KERN_ERR "%s: test_power_init failed!\n", __func__);
      return -1;
    }

    device_init_wakeup(&pdev->dev, 1);

    return 0;
}
probe中的adc_normal_mode函数如下:
static void adc_normal_mode(void)
{
    /* prescaler value is 19 */
    writel(S3C_ADCCON_PRSCEN | ((19 & 0xff) << 6), adc_base_addr + TSADCCON0);

    /* delay time 10ms */
    writel(10, adc_base_addr + TSADCCON0 + TSDLY);

    /* adc bits: 12 */
    writel(readl(adc_base_addr + TSADCCON0) | S3C_ADCCON_RESSEL_12BIT, adc_base_addr + TSADCCON0);

    /* when read data register, start to convert */
    writel((readl(adc_base_addr + TSADCCON0) | S3C_ADCCON_READ_START), adc_base_addr + TSADCCON0);

    /* switches and pull-up resistor should be turn off */
    writel(0x58, adc_base_addr + TSADCCON0 + TSCON);
    writel(0x58, adc_base_addr + TSADCCON1 + TSCON);
}
probe中的test_power_init函数如下:
static int __init test_power_init(struct platform_device *pdev)
{
        int i;
        int ret;

    pbat = kmalloc(sizeof(struct test_adc_bat), GFP_KERNEL);
    if(pbat == NULL)
    {
      printk(KERN_ERR "%s: kmalloc failed\n", __func__);
      return -1;
    }
    memset(pbat, 0, sizeof(struct test_adc_bat));

    /* init struct test_adc_bat */
    pbat->adc_client      = NULL;
    pbat->ac_online_pin   = S5PV210_GPJ2(0);
    pbat->charge_over_pin   = S5PV210_GPH2(0);
    pbat->supply_timer_time = 1500;      /* 500 ms */
    pbat->adc_channel       = 1;

    /* init ac online pin */
    ret = gpio_request(pbat->ac_online_pin, "ac_online_pin");
    if(ret < 0)
    {
      printk(KERN_ERR "%s: request ac online pin failed!\n", __func__);
      return -1;
    }

    /* make gpio to input */
    ret = s3c_gpio_cfgpin(pbat->ac_online_pin, S3C_GPIO_SFN(0));
    if(ret < 0)
    {
      printk(KERN_ERR "%s: config gpio to input failed!\n", __func__);
      goto gpio_failed;
    }

    ret = gpio_direction_input(pbat->ac_online_pin);
    if(ret != 0)
    {
      printk(KERN_ERR "%s: config gpio to input failed!\n", __func__);
      goto gpio_failed;
    }

    /* init charge over pin */
    ret = gpio_request(pbat->charge_over_pin, "charge_over_pin");
    if(ret < 0)
    {
      printk(KERN_ERR "%s: request charge over pin failed!\n", __func__);
      return -1;
    }

    /* make gpio to input */
    ret = s3c_gpio_cfgpin(pbat->charge_over_pin, S3C_GPIO_SFN(0));
    if(ret < 0)
    {
      printk(KERN_ERR "%s: config gpio to input failed!\n", __func__);
      goto gpio_failed;
    }

    ret = gpio_direction_input(pbat->charge_over_pin);
    if(ret != 0)
    {
      printk(KERN_ERR "%s: config gpio to input failed!\n", __func__);
      goto gpio_failed;
    }

    /* init timer, init supply_timer(timer_list members) */
    supply_timer.expires = jiffies + msecs_to_jiffies(pbat->supply_timer_time);
    setup_timer(&supply_timer, supply_timer_func, 0);

    /* add timer to system and start it */
    add_timer(&supply_timer);


        for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) {
      ret = power_supply_register(&pdev->dev, &test_power_supplies);
                if (ret) {
                        pr_err("%s: failed to register %s\n", __func__,
                                test_power_supplies.name);
                        goto failed;
                }
        }

        return 0;

failed:
        while (--i >= 0)
                power_supply_unregister(&test_power_supplies);

gpio_failed:
    gpio_free(pbat->ac_online_pin);
    gpio_free(pbat->charge_over_pin);
    del_timer_sync(&supply_timer);

    kfree(pbat);
        return ret;
}
其中
struct test_adc_bat
{
    struct s3c_adc_client   *adc_client;          /* for adc */
    unsigned long         ac_online_pin;      /* GPJ2_0 */
    unsigned long         charge_over_pin;      /* GPH2_0 */
    unsigned int            supply_timer_time;    /* msecs, default 500ms, for supply_timer */
    unsigned int            adc_channel;          /* ad chinnel: 0 ~ 9 => AIN0 ~ AIN9 */
};
static struct test_adc_bat *pbat = NULL;
static struct timer_list supply_timer;
test_adc_bat , supply_timer,adc_base_addr都是全局变量。
IRQ如下:
static struct resource adc_irq_res =
{
    .start= IRQ_ADC,
    .end    = IRQ_ADC,
    .flags= IORESOURCE_IRQ,
};
我在定时器函数中调用test_bat_adc_convert 想得到通道1的AD值,test_bat_adc_convert 函数如下:
static unsigned int test_bat_adc_convert(int ch)
{
    unsigned intret = 0;
    unsigned long dat0 = 0;
    unsigned long dat1 = 2000;

    writel((readl(adc_base_addr + S3C_ADCCON) | S3C_ADCCON_PRSCEN) & ~S3C_ADCCON_STDBM, adc_base_addr + S3C_ADCCON);

    /* select the ch in mutx */
    writel((ch & 0x0f), adc_base_addr + S3C_ADCMUX);
    udelay(20);

    /* start adc */
    writel(readl(adc_base_addr + S3C_ADCCON) | S3C_ADCCON_READ_START, adc_base_addr + S3C_ADCCON);
    readl(adc_base_addr + S3C_ADCDAT0);

    do{
      dat0 = readl(adc_base_addr + S3C_ADCCON);
      udelay(100);
    } while(!(dat0 & S3C_ADCCON_ECFLG));

    dat1 = readl(adc_base_addr + S3C_ADCDAT0);

    writel((readl(adc_base_addr + S3C_ADCCON) | S3C_ADCCON_STDBM) & ~S3C_ADCCON_PRSCEN, adc_base_addr + S3C_ADCCON);

    return (dat1 & S3C_ADCDAT0_XPDATA_MASK_12BIT);
}
得到的结果是:整个内核都停在 test_bat_adc_convert 中的 do{....}while(...) 中,不知道我什么地方设置错了,该怎么对ADC进行设置 才能读到ADC的采样值?
我感觉ADC没有启动,但从得到 信息可以看到ADC是配置是没什么错误的,不知道我什么地方没有配置正确出现了这种情况,还请Cavin指教。。。。。。




copyleft 发表于 2014-6-3 09:24:27

本帖最后由 copyleft 于 2014-6-3 16:23 编辑

出现前面图片中的问题已经解决了,至于触摸屏能不能使用就不知道了,应该能使用吧
解决方法:
我将 arch/arm/mach-s5pv210/adc.c 与 drivers/power/test_power.c 进行合并,其实我这个驱动就是电池电量驱动。然后再将 adc.c 中无用的代码部分删除,合并。修改:arch/arm/mach-s5pv210/adc.c 中大概第79行,将:static DEFINE_MUTEX(adc_mutex);修改为:DEFINE_MUTEX(adc_mutex); 也就是去掉 static ,然后在要使用adc的驱动中:#include<mach/adc.h> ,这样就可以直接 调用 :int s3c_adc_get_adc_data(int channel); 得到某个通道的AD值。根据adc.c中代码,可以知道adc.c中自己对触摸屏已经支持了,也就是可以在使用触摸屏1时,使用其它的AD通道读取AD值,但这个我还没有测试过,应该是可以的
{:5_290:}{:5_290:}{:5_290:}终于解决问题了。。。。
代码如下:


我尝试将 test_bat_probe 中的 test_adc_bat_probe(pdev); 去掉,然后再调用 s3c_adc_get_adc_data(1); 还是会出现前面的锁的情况,但加入后,就不会出现问题,也可以解决adc的问题,static int test_adc_bat_probe(struct platform_device *pdev) 函数很简单,我也不知道为什么去掉后就会出现前面的问题,加上就不会出现前面的问题。这个还得进一步测试才知道原因。。。。
{:5_329:}{:5_329:}{:5_329:}{:5_329:}

页: [1]
查看完整版本: 如何写TQ210 Android4,0.4 内核ADC驱动