ARM-Linux驱动--ADC驱动(中断方式)

硬件平台:FL2440

内核版本:2.6.28

主机平台:Ubuntu 11.04

内核版本:2.6.39

原创作品,转载请标明出处:http://blog.csdn.net/yming0221/archive/2011/06/26/6568937.aspx

 

这个驱动写了好久,因为原来的Linux内核编译的时候将触摸屏驱动编译进内核了,而触摸屏驱动里的ADC中断在注册的时候类型选择的是

 

IRQF_SAMPLE_RANDOM,不是共享类型,所以,自己写的ADC驱动在每次open的时候,总提示ADC中断注册失败。

 

解决方案:

重新配置内核,选择触摸屏驱动以模块的形式编译,而不是直接编译进内核,这样Linux在启动的时候不会自动加载触摸屏驱动,当然,IRQ_ADC中断号不会被占用。这样可以测试自己写的ADC驱动了。

以下是驱动源代码:

#include #include #include #include /*创建设备节点*/ #include #include /*定义DECLARE_WAIT_QUEUE_HEAD*/ #include /*定义了irqreturn_t等*/ #include /*request_irq disable_irq enable_irq*/ #include #include #include /*其中包含了#include "mach/irqs.h" */ #include #include #define ADC_MAJOR 102 #define ADC_NAME "my_adc" #define SUCCESS 0 static int adc_open(struct inode *,struct file *); static int adc_release(struct inode *,struct file *); static int __init adc_init(void); static int __exit adc_exit(void); static ssize_t adc_read(struct file *,char *,size_t,loff_t *); volatile unsigned long adc_con; unsigned long adc_dat0; int flag;//等待任务完成标志 unsigned long buf;//存放转换完成的数据 //声明等待队列 DECLARE_WAIT_QUEUE_HEAD(adc_wait); struct clk *adc_clk; static irqreturn_t adc_interrupt(int irq,void * dev_id)//中断处理程序 { if(flag==0) { buf=(readw(adc_dat0) & 0x3ff );//读取转换完成的数据 flag=1; wake_up_interruptible(&adc_wait);//唤醒等待其上的进程 printk("Read value is %ld/n",buf); } return IRQ_HANDLED; } struct file_operations adc_ops = { .owner = THIS_MODULE, .read = adc_read, .open = adc_open, .release = adc_release, }; static int __init adc_init(void) { int ret; adc_clk = clk_get(NULL,"adc");//获取时钟 clk_enable(adc_clk);//使能时钟 ret=register_chrdev(ADC_MAJOR,ADC_NAME,&adc_ops); //注册设备 if(ret<0) { printk("register device fail/n"); return ret; } adc_con=(unsigned long)ioremap(0x58000000,4); adc_dat0=(volatile unsigned long)ioremap(0x58000000+S3C2410_ADCDAT0,4); if( !(adc_con & adc_dat0) ) { printk("Failed to ioremap/n"); goto handle; } printk("Initialized.../n"); return SUCCESS; handle: unregister_chrdev(ADC_MAJOR,ADC_NAME); return -1; } static int adc_open(struct inode * inode,struct file * file) //打开设备函数 { //注册中断 int ret; //disable_irq(IRQ_ADC); //enable_irq(IRQ_ADC); ret=request_irq(IRQ_ADC,adc_interrupt,IRQF_SHARED,ADC_NAME,1);//注册中断 IRQ_ADC在 mach/irqs.h中定义 if(ret<0) { printk("IRQ %d can not request/n",IRQ_ADC); return ret; } return SUCCESS; } static int adc_release(struct inode * inode,struct file * file) //关闭设备函数 { free_irq(IRQ_ADC,1);//释放中断 return SUCCESS; } static ssize_t adc_read(struct file *file, char * buffer, size_t length, loff_t * offset)//设备读取函数 { writew((1<<14)|(0x31<<6),adc_con); //设置ADCCON writew((readw(adc_con) | 0x1),adc_con); //启动AD转换 wait_event_interruptible(adc_wait,flag); flag=0; } static int __exit adc_exit(void) //驱动卸载函数 { iounmap(adc_con); iounmap(adc_dat0); unregister_chrdev(ADC_MAJOR,ADC_NAME); clk_disable(adc_clk); clk_put(adc_clk); printk("The adc is unintialized/n"); return SUCCESS; } module_init(adc_init); module_exit(adc_exit); MODULE_LICENSE("GPL"); 

Makefile文件:

obj-m := adc.o KERNELDIR ?= /arm/linux-2.6.28.7-2440 PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: rm -f *.o *.ko *.order *.symvers read read: arm-linux-gcc -o read read_adc.c 

以下是测试代码:

#include #include #include #define ADC_DEVICE "/dev/my_adc" int main() { int ret; unsigned int data; ret=open(ADC_DEVICE,0); if(ret<0) { printf("Open adc fail/n"); return ret; } for(;;) { //printf("cnt=%d/n",cnt); read(ret,&data,sizeof(data)); //printf("The value is %d/n",data); } close(ret); return 0; } 

首先新建设备:

mknod /dev/my_adc c 102 32

然后插入驱动 insmod adc.ko

运行测试程序./read

结果如下:

可以看出,调节ad转换器上的旋钮,看到AD转换值的变化,说明驱动工作正常。

你可能感兴趣的:(嵌入式ARM+Linux,Linux内核游记,ARM-Linux驱动开发)