关注分享主机优惠活动
国内外VPS云服务器

linux adc设备是什么意思?

本文主要介绍linux adc设备所指的相关知识。内容详实易懂,操作简单快捷,具有一定的参考价值。相信大家看完这篇文章都会有所收获。让我们一起来看看吧。

Linux adc是混合设备驱动;在linux2.6.30.4中,系统已经自带了ADC通用驱动文件“arch/arm/plat-s3c24xx/adc.c”,该文件是在平台驱动的设备模型框架下编写的,包含了一些相对通用且稳定的代码。

在linux2.6.30.4中,系统已经有了自己的ADC通用驱动文件——ARCH/ARM/PLAT-s 3c 24 xx/ADC . c,它是在平台驱动的设备模型框架下编写的,包含了一些相对通用和稳定的代码。但是linux2.6.30.4中的ADC通用驱动文件并不完善,没有读取功能。后来我去看了linux3.8版本3.8的通用ADC文件——ARCH/ARM/PLAT——Samsung/ADC。c,哪个更完美。

但是,本节不分析这个文件,而是使用另一种架构来编写ADC驱动程序。因为ADC驱动真的很简单,所以我们不用平台驱动的器件模型作为架构来写。这一次,我们使用混合(misc)设备驱动程序。

什么是杂项设备驱动程序?

答:miscdevice共享一个主设备编号MISC_MAJOR(10),但辅助设备编号不同。所有miscdevice设备形成一个链表。访问设备时,内核根据设备号寻找对应的miscdevice设备,然后调用其file_operations结构中注册的文件操作接口进行操作。

结构错误设备{
int minor//二次设备编号。如果设置为MISC_DYNAMIC_MINOR,系统将自动分配它。
const char * name//设备名称
const struct file _ operations * fops;//操作功能
struct list _ head list
结构设备*父设备;
struct device * this _ device
};dev_init入口函数分析;

静态int __init dev_init(void)
{
int ret

base _ addr = iore map(s3c 2410 _ PA _ ADC,0x 20);
if (base_addr == NULL)
{
printk(KERN _ ERR & quot;未能重新映射寄存器块\ n & quot);
return-ENOMEM;
}

adc_clock = clk_get(NULL,& quotadc & quot);
如果(!adc _时钟)
{
printk(KERN _ ERR & quot;无法获取adc时钟源\ n & quot);
return-ENOENT;
}
clk _ enable(ADC _ clock);
ADC TSC = 0;

ret = request_irq(IRQ_ADC,adcdone_int_handler,IRQF_SHARED,DEVICE_NAME,& ampadcdev);
如果(返回)
{
ioun map(base _ addr);
返回ret
}

ret = misc_register。misc);

printk(设备名称& quot已初始化\ n & quot);
返回ret
}首先映射ADC寄存器地址并转换成虚拟地址,然后获取ADC时钟并使能ADC时钟,然后申请ADC中断。中断处理功能是

Adcdone_int_handler,并且标志为IRQF_SHARED,即共享中断,因为触摸屏也需要申请ADC中断,最后注册一个混合器件。

当应用程序打开时(& quot/dev/ADC & quot;,...),它会调用驱动中的open函数,所以我们来看看open函数是做什么的。

static int tq2440_adc_open(结构索引节点*索引节点,结构文件*filp)
{
/*初始化等待队列头*/
init_waitqueue_head。(ADC dev . wait));

/*开发板上ADC的通道2与一个电位器相连*/
ADC dev . channel = 2;//设置ADC的通道
ADC dev . prescale = 0x ff;

DPRINTK(& quot;ADC已打开\ n & quot);
返回0;
}很简单。先初始化一个等待队列头,因为entry函数中有ADC中断的应用,所以必须使用等待队列,然后设置ADC通道,因为TQ2440的ADC输入通道默认为2,预分频器值设置为0xff。

应用读取时会调用驱动中的read函数,那么我们来看看read函数是做什么的。

静态ssize _ t TQ 2440 _ ADC _ read(struct file * filp,char *buffer,size_t count,loff_t *ppos)
{
char string[20];
int值;
size _ t len

/*尝试获取ADC_LOCK信号量,如果可以立即获取,则获取信号量并返回0。
*否则返回非零,不会导致调用者休眠,可以用在中断上下文中。
*/
if(down _ trylock(& amp;ADC_LOCK) == 0)
{
/*表示模数转换器资源可用*/
ADC _ enable = 1;

/*使能预分频器,选择ADC通道,最后开始ADC转换*/
START_ADC_AIN(adcdev.channel,ADC dev . prescale);

/*等待事件。当ev_adc = 0时,进程被阻塞,直到EV _ ADC >;0 */
wait _ event _ interruptible(ADC dev . wait,ev _ ADC);

ev _ ADC = 0;

DPRINTK(& quot;AIN[%d] = 0x%04x,% d \ n & quot,adcdev.channel,adc_data,((ADCCON & amp0x80)?1:0));

/*将ADC中断处理程序中读取的ADC转换结果赋值*/
value = adc _ data
sprintf(str,& quot% 5d & quot,ADC _ data);
复制到用户(缓冲区,(char *)& amp;adc_data,sizeof(ADC _ data));

ADC _ enable = 0;
up(& amp;ADC _ LOCK);
}
其他
{
/*如果A/D转换器资源不可用,则将值指定为-1 */
值=-1;
}

/*将ADC转换结果输出到str数组,传输到应用空间*/
len = sprintf(str,& quot% d \ n & quot,值);
if(count & gt;= len)
{
/*将str数组中len字节的数据复制到缓冲区,即将ADC转换后的数据传输到应用空间*/
int r = copy_to_user(buffer,str,len);
返回r?r:len;
}
其他
{
return-EINVAL;
}
}tq2440_adc_read函数首先尝试获取adc_LOCK信号量,因为触摸屏驱动也使用ADC资源,相互竞争。获取ADC资源后,启用预分频,选择ADC通道,最后启动ADC转换,然后调用wait_event_interruptible函数等待,直到ev _ ADC >;0进程会继续向下运行,向下运行会读取adc_data数据,调用copy_to_user函数将adc数据传输到应用空间,最后释放ADC_LOCK信号量。

问:当ev _ adc & gt0?默认ev_adc = 0

答:在adcdone_int_handler中断处理程序中,ev_adc在数据读出后被置1。

ADC中断处理程序adcdone_int_handler

/* ADC中断处理程序*/
静态IRQ return _ t ADC done _ int _ handler(int IRQ,void *dev_id)
{
/*可用的模数转换器资源*/
中频(ADC_enable)
{
/*读取ADC转换结果数据*/
adc _ data = ADCDAT0 & amp0x3ff

/*唤醒标志位作为wait_event_interruptible的唤醒条件*/
ev _ ADC = 1;
wake_up_interruptibleADC dev . wait);
}
返回IRQ _ HANDLED
}当AD转换完成后,将触发ADC中断,并进入adcdone_int_handler。该函数将ad转换数据读入adc_data,然后将唤醒标志ev_adc置1,最后调用wake_up_interruptible函数唤醒adcdev.wait等待队列。总结ADC的工作流程:

1.在打开功能中,设置模拟输入通道和预分频值。

第二,在read函数中,启动ad转换,进程休眠。

3.在adc_irq函数中,adc中断在AD转换后触发,数据在ADC中断处理函数中读出以唤醒该过程。

第四,在read函数中,进程被唤醒后,adc转换数据传输到应用程序。

ADC驱动器参考源代码:

/*************************************

名称:EmbedSky_adc.c
版权所有:www.embedsky.net

*************************************/

# include & ltLinux/errno . h & gt;
# include & ltLinux/kernel . h & gt;
# include & ltLinux/module . h & gt;
# include & ltLinux/slab . h & gt;
# include & ltLinux/input . h & gt;
# include & ltLinux/init . h & gt;
# include & ltLinux/serio . h & gt;
# include & ltLinux/delay . h & gt;
# include & ltLinux/clk . h & gt;
# include & ltASM/io . h & gt;
# include & ltASM/IRQ . h & gt;
# include & ltASM/ua access . h & gt;
# include & ltmach/regs-clock . h & gt;
# include & ltplat/regs-timer . h & gt;
# include & ltplat/regs-ADC . h & gt;
# include & ltmach/regs-gpio . h & gt;
# include & ltLinux/cdev . h & gt;
# include & ltLinux/misc device . h & gt;

#包含& quottq2440 _ adc.h & quot

#undef调试
//#定义调试
#ifdef调试
#定义DPRINTK(x...){ printk(KERN _ DEBUG & quot;EmbedSky _ ADC:& quot;x);}
#否则
#定义DPRINTK(x...)(void)(0)
#endif

#定义设备名称& quotadc & quot/*设备节点:/dev/adc */

静态void _ _ iomem * base _ addr

typedef结构
{
wait_queue_head_t等待;/*定义等待队列头*/
int通道;
int预分频;
} ADC _ DEV

DECLARE _ MUTEX(ADC _ LOCK);/*定义并初始化信号量,并将其初始化为1 */
静态int ADC _ enable = 0;/* A/D转换器数据是否可用标志位*/

静态ADC _ DEV adcdev/*用于表示ADC设备*/
静态易失int ev _ ADC = 0;/*作为wait_event_interruptible的唤醒条件*/
静态int adc _ data

静态结构clk * adc _ clock

# define ADC con(*(volatile unsigned long *)(base _ addr+s3c 2410 _ ADC con))//ADC控制
# define ADC TSC(*(volatile unsigned long *)(base _ addr+s3c 2410 _ ADC TSC))//ADC触摸屏控制
# define ADC dly(*(volatile unsigned long *)(base _ addr+s3c 2410 _ ADC dly))//ADC起始或间隔延迟
# define ADC dat 0(*(volatile unsigned long *)(base _ addr+s3c 2410 _ ADC dat 0))//ADC转换数据0
# define ADC dat 1(*(volatile unsigned long *)(base _ addr+s3c 2410 _ ADC dat 1))//ADC转换数据1
# define ADCUPDN(*(volatile unsigned long *)(base _ addr+0x 14))//唱针上升/下降中断状态

# define PRESCALE _ DIS(0 & lt;& lt14)
# define PRESCALE _ EN(1 & lt;& lt14)
# define PRS CVL(x)((x)& lt;& lt6)
# define ADC _ INPUT(x)((x)& lt;& lt3)
# define ADC _ START(1 & lt;& lt0)
# define ADC _ end CVT(1 & lt;& lt15)

/*使能预分频器,选择ADC通道,最后开始ADC转换*/
#定义START_ADC_AIN(ch,预分频)\
do { ADC con = PRESCALE _ EN | PRS CVL(PRESCALE)| ADC _ INPUT((ch));\
ADCCON | = ADC _ START\
}while(0)

/* ADC中断处理程序*/
静态IRQ return _ t ADC done _ int _ handler(int IRQ,void *dev_id)
{
/*可用的模数转换器资源*/
中频(ADC_enable)
{
/*读取ADC转换结果数据*/
adc _ data = ADCDAT0 & amp0x3ff

/*唤醒标志位作为wait_event_interruptible的唤醒条件*/
ev _ ADC = 1;
wake_up_interruptibleADC dev . wait);
}
返回IRQ _ HANDLED
}

静态ssize _ t TQ 2440 _ ADC _ read(struct file * filp,char *buffer,size_t count,loff_t *ppos)
{
char string[20];
int值;
size _ t len

/*尝试获取ADC_LOCK信号量,如果可以立即获取,则获取信号量并返回0。
*否则返回非零,不会导致调用者休眠,可以用在中断上下文中。
*/
if(down _ trylock(& amp;ADC_LOCK) == 0)
{
/*表示模数转换器资源可用*/
ADC _ enable = 1;

/*使能预分频器,选择ADC通道,最后开始ADC转换*/
START_ADC_AIN(adcdev.channel,ADC dev . prescale);

/*等待事件。当ev_adc = 0时,进程被阻塞,直到EV _ ADC >;0 */
wait _ event _ interruptible(ADC dev . wait,ev _ ADC);

ev _ ADC = 0;

DPRINTK(& quot;AIN[%d] = 0x%04x,% d \ n & quot,adcdev.channel,adc_data,((ADCCON & amp0x80)?1:0));

/*将ADC中断处理程序中读取的ADC转换结果赋值*/
value = adc _ data
sprintf(str,& quot% 5d & quot,ADC _ data);
复制到用户(缓冲区,(char *)& amp;adc_data,sizeof(ADC _ data));

ADC _ enable = 0;
up(& amp;ADC _ LOCK);
}
其他
{
/*如果A/D转换器资源不可用,则将值指定为-1 */
值=-1;
}

/*将ADC转换结果输出到str数组,传输到应用空间*/
len = sprintf(str,& quot% d \ n & quot,值);
if(count & gt;= len)
{
/*将str数组中len字节的数据复制到缓冲区,即将ADC转换后的数据传输到应用空间*/
int r = copy_to_user(buffer,str,len);
返回r?r:len;
}
其他
{
return-EINVAL;
}
}

static int tq2440_adc_open(结构索引节点*索引节点,结构文件*filp)
{
/*初始化等待队列头*/
init_waitqueue_head。(ADC dev . wait));

/*开发板上ADC的通道2与一个电位器相连*/
ADC dev . channel = 2;//设置ADC的通道
ADC dev . prescale = 0x ff;

DPRINTK(& quot;ADC已打开\ n & quot);
返回0;
}

静态int tq2440_adc_release(结构索引节点*索引节点,结构文件*filp)
{
DPRINTK(& quot;ADC关闭\ n & quot);
返回0;
}

静态结构file_operations dev_fops = {
车主:这个_模块,
open:TQ 2440 _ ADC _ open,
读取:tq2440_adc_read,
发布:tq2440_adc_release,
};

静态结构miscdevice misc = {
。未成年人=杂项_动态_未成年人,
。name =设备名称,
。fops = & ampdev_fops,
};

静态int __init dev_init(void)
{
int ret

base _ addr = iore map(s3c 2410 _ PA _ ADC,0x 20);
if (base_addr == NULL)
{
printk(KERN _ ERR & quot;未能重新映射寄存器块\ n & quot);
return-ENOMEM;
}

adc_clock = clk_get(NULL,& quotadc & quot);
如果(!adc _时钟)
{
printk(KERN _ ERR & quot;无法获取adc时钟源\ n & quot);
return-ENOENT;
}
clk _ enable(ADC _ clock);
ADC TSC = 0;

ret = request_irq(IRQ_ADC,adcdone_int_handler,IRQF_SHARED,DEVICE_NAME,& ampadcdev);
如果(返回)
{
ioun map(base _ addr);
返回ret
}

ret = misc_register。misc);

printk(设备名称& quot已初始化\ n & quot);
返回ret
}

静态空_出口开发_出口(空)
{
free _ IRQ(IRQ _ ADC & amp;adcdev);
ioun map(base _ addr);

中频(adc_clock)
{
clk _ disable(ADC _ clock);
clk _ put(ADC _ clock);
adc _ clock = NULL
}

杂项_注销(& ampmisc);
}

导出_符号(ADC _ LOCK);
模块初始化(开发初始化);
模块_出口(开发_出口);

模块许可证(& quotGPL & quot);
模块作者(& quotwww . embedsky . net & quot;);
模块描述(& quotEmbedSky SKY2440/TQ2440板的ADC驱动器并支持触摸& quot);ADC应用测试参考源代码:

/*************************************

名称:EmbedSky_adc.c
版权所有:www.embedsky.net

*************************************/

# include & ltstdio.h & gt
# include & ltunistd.h & gt
# include & ltstdlib.h & gt
# include & ltsys/types . h & gt;
# include & ltsys/stat . h & gt;
# include & ltsys/ioctl . h & gt;
# include & ltfcntl.h & gt
# include & ltLinux/fs . h & gt;
# include & lt错误号& gt
# include & ltstring.h & gt

int main(void)
{
int fd
字符温度= 1;

FD = open(& quot;/dev/ADC & quot;, 0);
if(FD & lt;0)
{
perror(& quot;打开ADC设备!");
出口(1);
}
for(;;)
{
充电缓冲器[30];
int len

len = read(fd,buffer,sizeof buffer-1);
if(len & gt;0)
{
buffer[len]= & # 39;\0';
int值;
sscanf(缓冲区,& quot% d & quot,& amp值);
printf(& quot;ADC值:% d \ n & quot,值);
}
其他
{
perror(& quot;读取ADC器件!");
出口(1);
}
睡眠(1);
}
adcstop:
关闭(FD);
}测试结果:

[WJ2440]#。/adc_test
ADC值:693
ADC值:695
ADC值:694
ADC值:695
ADC值:702
ADC值:740
ADC值:768
ADC值:775
ADC值:820
ADC值:844
ADC值:887
ADC值:937
ADC值:978
ADC值:1000
ADC值:1023
ADC值:1023
ADC值:1023

未经允许不得转载:主机频道 » linux adc设备是什么意思?

评论 抢沙发

评论前必须登录!