TQ210搭载Android4.0.3系统构建之ADC从驱动到HAL到JNI到应用程序(驱动篇)

发布者:EnchantedMelody最新更新时间:2024-12-16 来源: cnblogs关键字:TQ210  ADC  HAL  JNI 手机看文章 扫描二维码
随时随地手机看文章

ADC的驱动也采用platform设备驱动的方式进行编写,platform_device为platform_driver提供ADC控制器/ADC数据存储器/ADC延时器的地址,在platform_driver的probe函数在进行ioremap的映射,进而操作相应的寄存器


对于ADC寄存器的操作,可分为三步:


1.使用clk_get获取adc时钟,接着使用clk_enable使能adc时钟


2.设置ADCCON的工作方式,预分频比之类的


3.当开始读取数据值时,开启ADC转换,判断是否开始转换,判断是否转换完成,最终读取数据,返回给用户空间


ADCIN1的模拟量输入端接一个滑动变阻器,通过滑动变阻器,提供不同的模拟量输入端。


adc平台设备代码


adc_under_device.c


#include

#include

#include

#include


static struct resource adc_under_resource[]=  //adc的资源

{

[0]=  //adc_con0

{

.start=0xE1700000,

.end=0xE1700000+0x3,

.flags=IORESOURCE_MEM,

},

[1]=//adc_delay0

{

.start=0xE1700000+0x8,

.end=0xE1700000+0xB,

.flags=IORESOURCE_MEM,

},

[2]=//adc_data0

{

.start=0xE1700000+0xC,

.end=0xE1700000+0xF,

.flags=IORESOURCE_MEM,

},

[3]=//adc_con1

{

.start=0xE1701000,

.end=0xE1701000+0x3,

.flags=IORESOURCE_MEM,

},

[4]=//adc_delay1

{

.start=0xE1701000+0x8,

.end=0xE1701000+0xB,

.flags=IORESOURCE_MEM,

},

[5]=//adc_data1

{

.start=0xE1701000+0xC,

.end=0xE1701000+0xF,

.flags=IORESOURCE_MEM,

},

};



static void adc_under_release(struct device *dev)  //释放adc设备函数

{

}


static struct platform_device adc_under_platform_device=  //adc的平台函数

{

.name='adc_under',  //用于与驱动进行匹配的名称

.id=-1,

.num_resources=sizeof(adc_under_resource)/sizeof(adc_under_resource[0]),

.resource=adc_under_resource,

.dev=

{

.release=adc_under_release,

       },

};



static int __init adc_under_init(void)  //adc模块初始化函数

{

int ret=platform_device_register(&adc_under_platform_device); //注册adc平台设备,为adc驱动提供资源

if(ret==0)  printk(KERN_INFO 'adc_under_platform_device register success.n');

else printk(KERN_INFO 'adc_under_platform_device register failed.n');

return ret;

}


static void __exit adc_under_exit(void) //adc的模块卸载函数

{

platform_device_unregister(&adc_under_platform_device); //卸载adc平台设备

printk(KERN_INFO 'adc_under_platform_device unregister success.n');

}


module_init(adc_under_init);

module_exit(adc_under_exit);

MODULE_LICENSE('GPL');


编译文件 Makefile


obj-m :=adc_under_device.o

KERNELDIR :=~/java/Kernel_3.0.8_TQ210_for_Android_v1.0/

PWD :=$(shell pwd)


build:kernel_module

kernel_module:

make  -C  $(KERNELDIR) M=$(PWD) modules

clean:

make -C $(KERNELDIR)   M=$(PWD) clean



adc平台驱动文件


adc_under_driver.c


#include

#include

#include

#include

#include

#include

#include   //包含copy_to_user...

#include    //包含mdelay...

#include


#define ADC_DEVICE_NAME 'adc_unders'   //adc的设备名称



static struct class *cls;  //类指针

static void __iomem *adccon1,*adcdata1,*adcdly1; //adc控制器/数据存储器/延时器

//__iomem表示指向一个I/O的内存空间,目的为了开发通用的驱动,编译器不会对其进行检查

static struct clk *adc_clk;//adc时钟指针


#define ADCCON (*(volatile unsigned long *)(adccon1))  //adc控制器宏

#define ADCDATA (*(volatile unsigned long *)(adcdata1))//将__iomem指针转为unsigned long *型指针

#define ADCDLY (*(volatile unsigned long *)(adcdly1)) //volatile表示变量是易变的,总是需要从内存地址中读取,而不是从寄存器中读取,对该变量不进行优化


int major;  //存储设备主设备号



static int adc_under_open(struct inode *inode,struct file *file) //adc打开函数

{

       printk(KERN_INFO 'adc_under_open.n');

return 0;

}


static int adc_under_close(struct platform_device *pdev)  //adc关闭函数,将申请的资源释放

{

unregister_chrdev(major, ADC_DEVICE_NAME);  //从chrdevs指针数组中删掉adc字符设备

device_destroy(cls, MKDEV(major,0));    //从subsystem中将其删除,并将其引用减一

class_destroy(cls);  //删除cls类

iounmap(adccon1);  //取消物理地址到虚拟地址的映射

iounmap(adcdata1);

iounmap(adcdly1);

printk(KERN_INFO 'adc_under_close success.n');

return 0;

}



static int  adc_under_read(struct file *file,char __user *buf,size_t count,loff_t *fops)  //adc读取转换后的数字量

{

int adc_val,n,con;

ADCCON |=0x01;  //开启ADC的转换

while(ADCCON&0x1);  //判断是否开始转换

while(!ADCCON&0x8000); //判断是否转换完成

con=ADCCON&0xffff;

adc_val=(ADCDATA&0x03ff);  //获取到转换后的结果值

mdelay(100);

n=copy_to_user(buf, &adc_val, sizeof(adc_val));  //将adc_val的值从内核空间拷到用户空间

printk(KERN_INFO 'adc_under_read,adc_val=%d,con=0x%xn',adc_val,con);

return n;

}


struct file_operations adc_under_fops=  //adc文件操作

{

.owner=THIS_MODULE,

.open=adc_under_open,

.read=adc_under_read,

};


static void adc_reg_init(void)  //adc的初始化函数

{

int con;

adc_clk=clk_get(NULL, 'adc');  //获取到adc的时钟,在S5PV210中,进行ADC转换的时候,使用的是PCLK(Max 66MHz) is used

clk_enable(adc_clk); //使能clk时钟

ADCDLY=100;  //adc采样转换的延迟时间,影响stylus down,x-conversion,y-conversion时间

ADCCON=(0<<0) | //没有操作

(0<<1) |  //disable start by read operation

(0<<2) |  //正常操作模式

(65<<6) |  //分频值为65 ,因为PCLK=66 所以prscvl=65 即 a/d转换频率为1MHz ,Conversion time=5us

(1<<14) ;// 使能预分频值

con=ADCCON&0xffff;  //调试信息

printk(KERN_INFO 'adc_reg_init.con=0x%xn',con);

}


static int adc_under_probe(struct platform_device *pdev)  //adc的探针函数,与platform_device匹配后调用,用于初始化platform_driver

{

       struct resource *rescon1,*resdata1,*resdly1;  //资源指针

rescon1=platform_get_resource(pdev,IORESOURCE_MEM, 3);  //获取到ADCCON1的资源,详情见adc_under_device.c

if(rescon1==NULL) { printk(KERN_INFO 'can not find rescon1.n'); return -1;}  //判断是否获得资源

adccon1=ioremap(rescon1->start, rescon1->end-rescon1->start+1);  //物理地址映射为虚拟地址

if(adccon1==NULL) { printk(KERN_INFO 'no mem to adccon1.n'); return -1;}  //判断映射是否成功

 

  resdata1=platform_get_resource(pdev,IORESOURCE_MEM, 5);//获取到ADCDATA1的资源,详情见adc_under_device.c

  if(resdata1==NULL) { printk(KERN_INFO 'cann not find resdata1->n'); return -1;} //判断是否获得资源

  adcdata1=ioremap(resdata1->start, resdata1->end-resdata1->start+1);//物理地址转为虚拟地址

if(adcdata1==NULL) { printk(KERN_INFO 'no mem to adcdata1.n'); return -1;}//判断映射是否成功

  

  resdly1=platform_get_resource(pdev,IORESOURCE_MEM, 4); //获取到ADCDLY1的资源,详情见adc_under_device.c

  if(resdly1==NULL) { printk(KERN_INFO 'can not find resdly1.n'); return -1; } //判断是否获取资源

adcdly1=ioremap(resdly1->start, resdly1->end-resdly1->start+1); //物理地址映射为虚拟地址

if(adcdly1==NULL) { printk(KERN_INFO 'no mem to adcdly1.n'); return -1;}//判断映射是否成功


adc_reg_init();  //adc寄存器的初始化


major=register_chrdev(0, ADC_DEVICE_NAME,&adc_under_fops); //创建并注册字符设备

cls=class_create(THIS_MODULE, ADC_DEVICE_NAME);  //创建类

device_create(cls,NULL, MKDEV(major,0), NULL, ADC_DEVICE_NAME); //建立/dev/adc_unders设备文件,利用udev策略

printk(KERN_INFO 'adc_under_probe success.n');

return 0;

}



static struct platform_driver adc_under_platform_driver=  //adc的平台驱动

{

.probe=adc_under_probe,

.remove=adc_under_close,

.driver=

{

.name='adc_under',  //用于与platform_device匹配的名称

},

};


static int __init adc_driver_init(void)  //adc模块初始化函数

{

int ret=platform_driver_register(&adc_under_platform_driver);  //注册平台驱动

if(ret==0) printk(KERN_INFO 'adc_under_platform_driver success.n');

else printk(KERN_INFO 'adc_under_platform_driver failed.n');

return ret;

}


static void __exit adc_driver_exit(void) //模块卸载函数

{

  platform_driver_unregister(&adc_under_platform_driver);  //卸载平台驱动

}


module_init(adc_driver_init);

module_exit(adc_driver_exit);

MODULE_LICENSE('GPL');


编译文件 Makefile


obj-m :=adc_under_driver.o

KERNELDIR :=~/java/Kernel_3.0.8_TQ210_for_Android_v1.0/

PWD :=$(shell pwd)


build:kernel_module

kernel_module:

make  -C  $(KERNELDIR) M=$(PWD) modules

clean:

make -C $(KERNELDIR)   M=$(PWD) clean



最后的测试文件


adc_under_test.c


#include

#include

#include



#define DEVICE_NAME '/dev/adc_unders'


int main(void)

{

int fd,ret=0,data,count=0;

fd=open(DEVICE_NAME, O_RDWR);

if(fd<0) {printf('can not open fd=%sn',DEVICE_NAME); return -1; }

while(count<10){

ret=read(fd,&data,sizeof(data));

if(ret<0)

printf('read data is error n');

printf('adc is %f Vn',(float)data*3.3/1024);

sleep(2);

count++;

}


close(fd);

return ret;

}


测试文件的Android.mk


LOCAL_PATH :=$(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS :=eng

LOCAL_SRC_FILES :=adc_under_test.c

LOCAL_MODULE :=adc_test

LOCAL_MODULE_PATH :=$(LOCAL_PATH)

include $(BUILD_EXECUTABLE)


对于这个adc的驱动,我在测试的时候出现了一个很奇怪的现象,我在adc驱动测试代码中总共是读取了10次adc的值,偶合会有一两次的值是错的,就是3,3V,即adc没开始转换的值,嗯,对于这个问题暂时还不清楚,是不是我的adc硬件上有问题,待后续研究。


关键字:TQ210  ADC  HAL  JNI 引用地址:TQ210搭载Android4.0.3系统构建之ADC从驱动到HAL到JNI到应用程序(驱动篇)

上一篇:TQ210搭载Android4.0.3系统构建之ADC从驱动到HAL到JNI到应用程序(HAL篇)
下一篇:s5pv210的定时器

推荐阅读最新更新时间:2026-03-25 11:52

TQ210搭载Android4.0.3系统构建之ADC从驱动到HALJNI到应用程序(HAL篇)
其实ADC的HAL层和BEEP、LED的就HAL层很像,所以注释就很少了,详情见BEEP、LED的HAL层 直接上源码吧 adc_under_hal.h #ifndef __ADC_UNDER_H #define __ADC_UNDER_H #include hardware/hardware.h #include stdint.h #include sys/cdefs.h __BEGIN_DECLS //采用C语言的方式编译和连接变量与函数 #define ADC_UNDER_ID adc_unders static struct adc_hw_module_t { struct hw_module_t c
[单片机]
TQ210搭载Android4.0.3系统构建之BEEP从驱动到HALJNI到应用程序(HAL篇)
对于BEEP的HAL层函数与LED的HAL层的函数很相似,就不多说了。 头文件放在/hardware/libhardware/include/hardware/目录下 beep_under_hal.h #ifndef ANDROID_BEEP_UNDER_H #define ANDROID_BEEP_UNDER_H #include hardware/hardware.h #include stdint.h #include sys/cdefs.h __BEGIN_DECLS //以C语言的方式编译和连接函数与变量 #define BEEP_UNDER_MODULE_ID beep_unders //
[单片机]
TQ210搭载Android4.0.3系统构建之LED从驱动到HALJNI到应用程序(应用程序篇)
开发板:TQ210 OS:Android 4.0.3 以下所有内容都是在TQ210开发板上实现,并且很多内容也是天嵌公司提供,我将一些内容进行了删减、替换,然后加入了一些自己的理解,同时也是记录自己学习的旅程。 LedUnderActivity.java package com.unders.led; import com.unders.led.R; import android.app.Activity; import android.app.AlertDialog; import android.os.Bundle; import android.util.Log; import android.vie
[单片机]
<font color='red'>TQ210</font>搭载Android4.0.3系统构建之LED从驱动到<font color='red'>HAL</font>到<font color='red'>JNI</font>到应用程序(应用程序篇)
STM32 ADC转换完成回调函数解析:HAL_ADC_ConvCpltCallback与HAL_ADC_ConvHalfCpltCallback详解
HAL_ADC_ConvCpltCallback 和 HAL_ADC_ConvHalfCpltCallback 是 STM32 HAL 库中用于处理 ADC(模数转换器)转换完成事件的回调函数。它们分别在 ADC 转换完成和转换完成一半时被调用。以下是它们的详细说明: 1. HAL_ADC_ConvCpltCallback 功能:当 ADC 转换完全完成时,此回调函数会被调用。 使用场景:适用于需要处理完整转换数据的场景。例如,当 ADC 完成一组采样后,你可以在这个回调函数中读取转换结果并进行处理。 示例代码: void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* ha
[单片机]
STM32使用HAL库实现ADC单通道转换
  STM32的ADC转换还是很强大的,它具有多个通道选择,这里我就不细说,不了解的可以自行百度,这里只是选取单通道,实现ADC转换。在文章开始之前,我说一下数据左对齐跟右对齐的差别,以前一直糊里糊涂的,记录下来以免以后自己忘记。12位二进制最大值为 0x0FFF 左对齐操作后的结果是 0xFFF0,右对齐后还是0x0FFF。反过来看 ,若寄存器里左对齐的数据值X (相当于实际数据*16,所以左对齐转换的值要/16才是实际的值),则X 4才是实际的数据。而右对齐,则是数据保持不变,采集到多少就多少。至于是按左对齐保存到寄存器还是按照右对齐,就看你的配置里如何选了。   好了,下面就开始说明怎么用STM32CUBEMX实现ADC单通
[单片机]
STM32使用<font color='red'>HAL</font>库实现<font color='red'>ADC</font>单通道转换
STM32 ADC多通道转换DMA模式与非DMA模式两种方法(HAL库)
一、非DMA模式(转)   说明:这个是自己刚做的时候百度出来的,不是我自己做出来的,因为感觉有用就保存下来做学习用,原文链接:https://blog.csdn.net/qq_24815615/article/details/70227385,下面第二部分我会补充自己的DMA模式的方法。   Stm32 ADC 的转换模式还是很灵活,很强大,模式种类很多,那么这也导致很多人使用的时候没细心研究参考手册的情况下容易混淆。不知道该用哪种方式来实现自己想要的功能。网上也可以搜到很多资料,但是大部分是针对之前老版本的标准库的。昨天帮客户解决这个问题,正好做个总结:使用stm32cubeMX配置生成多通道采集的例子。 软件:STM32Cu
[单片机]
STM32 <font color='red'>ADC</font>多通道转换DMA模式与非DMA模式两种方法(<font color='red'>HAL</font>库)
STM32HALADC实验(一)——阻塞查询法
实验现象 生成代码: int main(void) { /* USER CODE BEGIN 1 */ uint16_t adcData; float voltage; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BE
[单片机]
STM32<font color='red'>HAL</font>库<font color='red'>ADC</font>实验(一)——阻塞查询法
TQ210搭载Android 4.0.3测试Google Maps API V2(一.获取地图)
硬件环境:tq210 v4开发板 运行环境:Android 4.0.3 开发环境: Eclipse Version: Juno Service Release 2,ADT 22,Android Sdk 22 参考文档: 这位老兄写的很好 有一个错误就是参看他的博客才解决的 中文版: http://www.cnblogs.com/mengdd/archive/2013/01/01/2841390.html Google官网指导手册 英文版: https://developers.google.com/maps/documentation/android/start#installing_the_google_maps_and
[单片机]
<font color='red'>TQ210</font>搭载Android 4.0.3测试Google Maps API V2(一.获取地图)
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved