如何写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驱动?
谢谢大家了!
你有没有仔细查看adc.c文件?那里不是有个 ADC_WITH_TOUCHSCREEN宏么?define了这宏之后也不行么? 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,认为是电量值上报给系统,其它地方都没有修改 copyleft 发表于 2014-5-27 09:54
#define ADC_WITH_TOUCHSCREEN
在adc.c的开关部分就这样定义了,adc.c,tq210_ts.c我都没有对里面的宏或 ...
这样做我没有做过,需要你自行研究一下这些驱动的逻辑问题了;成功以后可以分享一下 Calvin 发表于 2014-5-27 13:57
这样做我没有做过,需要你自行研究一下这些驱动的逻辑问题了;成功以后可以分享一下
可以
成功后总结一下。。。 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驱动看能不能解决 问题,到最后还是没能解决 问题。。。。 copyleft 发表于 2014-5-27 15:08
对了
我想问一下:如果将ADC的所有通道都做成一般的AD输入口,在内核 配置中将TS禁止掉,该怎么配置ADC ...
你试下去掉这个#define ADC_WITH_TOUCHSCREEN 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 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]