原标題: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,不是共享類型,是以,自己寫的 在每次open的時候,總提示ADC中斷注冊失敗。
解決方案:
重新配置核心,選擇觸摸屏驅動以子產品的形式編譯,而不是直接編譯進核心,這樣Linux在啟動的時候不會自動加載觸摸屏驅動,當然,IRQ_ADC中斷号不會被占用。這樣可以測試自己寫的ADC驅動了。
以下是驅動源代碼:
view plainprint?
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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_HAND ;
}
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("Fai 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檔案:
view plainprint?
obj-m := adc.o
KERNELDIR ?= / /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
以下是測試代碼:
view plainprint?
#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轉換值的變化,說明驅動工作正常。
責任編輯: