天嵌 ARM开发社区

 找回密码
 注册
查看: 5437|回复: 8

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

[复制链接]
copyleft 发表于 2014-5-26 15:17:37 | 显示全部楼层 |阅读模式
本帖最后由 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驱动?
谢谢大家了!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
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函数:
  1. static int __init test_adc_bat_probe(struct platform_device *pdev)
  2. {
  3.     int ret = -1;
  4.    
  5.     /* for adc: AIN0 - AIN9 */
  6.     adc_base_addr = ioremap(S5PV210_PA_ADC + TSADCCON0, 0x1024);
  7.     if(adc_base_addr == NULL)
  8.     {
  9.         ret = -ENOENT;
  10.         printk(KERN_ERR "%s: ioremap for adc failed", __func__);
  11.         return ret;
  12.     }
  13.    
  14.     adc_normal_mode();

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

  22.     /* anable clk */
  23.     clk_enable(adc_clk);

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

  31.     ret = test_power_init(pdev);
  32.     if(ret != 0)
  33.     {
  34.         printk(KERN_ERR "%s: test_power_init failed!\n", __func__);
  35.         return -1;
  36.     }

  37.     device_init_wakeup(&pdev->dev, 1);

  38.     return 0;
  39. }
复制代码

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

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

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

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

  11.     /* switches and pull-up resistor should be turn off */
  12.     writel(0x58, adc_base_addr + TSADCCON0 + TSCON);
  13.     writel(0x58, adc_base_addr + TSADCCON1 + TSCON);
  14. }
复制代码

probe中的test_power_init函数如下:
  1. static int __init test_power_init(struct platform_device *pdev)
  2. {
  3.         int i;
  4.         int ret;

  5.     pbat = kmalloc(sizeof(struct test_adc_bat), GFP_KERNEL);
  6.     if(pbat == NULL)
  7.     {
  8.         printk(KERN_ERR "%s: kmalloc failed\n", __func__);
  9.         return -1;
  10.     }
  11.     memset(pbat, 0, sizeof(struct test_adc_bat));

  12.     /* init struct test_adc_bat */
  13.     pbat->adc_client        = NULL;
  14.     pbat->ac_online_pin     = S5PV210_GPJ2(0);
  15.     pbat->charge_over_pin   = S5PV210_GPH2(0);
  16.     pbat->supply_timer_time = 1500;      /* 500 ms */
  17.     pbat->adc_channel       = 1;

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

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

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

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

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

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

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

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


  63.         for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) {
  64.         ret = power_supply_register(&pdev->dev, &test_power_supplies[i]);
  65.                 if (ret) {
  66.                         pr_err("%s: failed to register %s\n", __func__,
  67.                                 test_power_supplies[i].name);
  68.                         goto failed;
  69.                 }
  70.         }

  71.         return 0;

  72. failed:
  73.         while (--i >= 0)
  74.                 power_supply_unregister(&test_power_supplies[i]);

  75. gpio_failed:
  76.     gpio_free(pbat->ac_online_pin);
  77.     gpio_free(pbat->charge_over_pin);
  78.     del_timer_sync(&supply_timer);

  79.     kfree(pbat);
  80.         return ret;
  81. }
复制代码

其中
  1. struct test_adc_bat
  2. {
  3.     struct s3c_adc_client   *adc_client;          /* for adc */
  4.     unsigned long           ac_online_pin;        /* GPJ2_0 */
  5.     unsigned long           charge_over_pin;      /* GPH2_0 */
  6.     unsigned int            supply_timer_time;    /* msecs, default 500ms, for supply_timer */
  7.     unsigned int            adc_channel;          /* ad chinnel: 0 ~ 9 => AIN0 ~ AIN9 */
  8. };
  9. static struct test_adc_bat *pbat = NULL;
  10. static struct timer_list supply_timer;
复制代码

test_adc_bat , supply_timer,adc_base_addr  都是全局变量。
IRQ如下:
  1. static struct resource adc_irq_res =
  2. {
  3.     .start  = IRQ_ADC,
  4.     .end    = IRQ_ADC,
  5.     .flags  = IORESOURCE_IRQ,
  6. };
复制代码

我在定时器函数中调用test_bat_adc_convert 想得到通道1的AD值,test_bat_adc_convert 函数如下:
  1. static unsigned int test_bat_adc_convert(int ch)
  2. {
  3.     unsigned int  ret = 0;
  4.     unsigned long dat0 = 0;
  5.     unsigned long dat1 = 2000;

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

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

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

  13.     do{
  14.         dat0 = readl(adc_base_addr + S3C_ADCCON);
  15.         udelay(100);
  16.     } while(!(dat0 & S3C_ADCCON_ECFLG));

  17.     dat1 = readl(adc_base_addr + S3C_ADCDAT0);

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

  19.     return (dat1 & S3C_ADCDAT0_XPDATA_MASK_12BIT);
  20. }
复制代码

得到的结果是:整个内核都停在 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值,但这个我还没有测试过,应该是可以的
终于解决问题了。。。。
代码如下:


我尝试将 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) 函数很简单,我也不知道为什么去掉后就会出现前面的问题,加上就不会出现前面的问题。这个还得进一步测试才知道原因。。。。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

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

本版积分规则

关闭

i.MX8系列ARM cortex A53 M4 工控板上一条 /1 下一条

Archiver|手机版|小黑屋|天嵌 嵌入式开发社区 ( 粤ICP备11094220号 )

GMT+8, 2024-5-2 12:56 , Processed in 1.062500 second(s), 23 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表