嵌入式系统版本:linux2.6.24
驱动程序:
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/clk.h>
#define ADC_MAJOR 102 //主设备号
#define ADC_MINOR 0 //次设备号
#define DEVICE_NAME "adc_dev"//设备名称
#define SUCCESS 0
//=====================寄存器地址映射===========================
#define adc_con (unsigned long)ioremap(0x58000000,4)
#define adc_dat0 (volatile unsigned long)ioremap(0x5800000c,4)
static int Device_Open = 0;
static struct clk *adc_clk;//定义adc时钟结构
int adc_init(void);
void adc_cleanup(void);
static int device_open(struct inode *,struct file *);
static int device_release(struct inode *,struct file *);
static ssize_t device_read(struct file *,char *,size_t,loff_t *);
int init_module(void);
void cleanup_module(void);
//=================================================================
// 填充file_operations
//================================================================
struct file_operations adc_ops =
{
.owner = THIS_MODULE,
.read = device_read,
.open = device_open,
.release=device_release,
};
//===========================================================
// 注册设备
//===========================================================
struct cdev *my_cdev;
struct class *my_class;
int __init adc_init(void)
{
#if 0
Major = register_chrdev(ADC_MAJOR,DEVICE_NAME,&adc_ops); //注册操作,返回主设备值
if(Major <0)
{
printk("ADC init_module:failed with %d\n",Major); //主设备号小于0,则注册失败
return Major;
}
devfs_mk_cdev(MKDEV(ADC_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEVICE_NAME);
#endif
int err, i;
int devno = MKDEV(ADC_MAJOR, ADC_MINOR);//获取设备号 .
//===========初始化cdev================
my_cdev = cdev_alloc();
cdev_init(my_cdev, &adc_ops);//初始化cdev
my_cdev->owner = THIS_MODULE;
err = cdev_add(my_cdev, devno, 1);//添加cdev
if (err != 0)
printk("adc register failed!\n");
//============注册设备==================
my_class = class_create(THIS_MODULE, "adc_class");
if(IS_ERR(my_class)) {
printk("Err: failed in creating class.\n");
return -1;
}
/* register your own device in sysfs, and this will cause udevd to create corresponding device node */
class_device_create(my_class, NULL, devno, NULL, DEVICE_NAME "%d", ADC_MINOR );
//=============打开adc时钟=====================
adc_clk = clk_get(NULL, "adc");
if (!adc_clk)
{
printk(KERN_ERR "failed to get adc clock source\n");
return -ENOENT;
}
clk_enable(adc_clk);
printk(DEVICE_NAME " initialized\n");
return 0;
}
//===========================================================
// 打开设备文件
//===========================================================
static int device_open(struct inode * inode,struct file *file)
{
if(Device_Open)
{
return -EBUSY;
}
Device_Open++;
return SUCCESS;
}
//===========================================================
// 关闭设备文件
//===========================================================
static int device_release(struct inode * inode,struct file *file)
{
Device_Open --;
return 0;
}
//===========================================================
// 读取设备文件
//===========================================================
static ssize_t device_read(struct file *file,
char * buffer,
size_t length,
loff_t * offset)
{
unsigned long buf;
printk("1\n");
__raw_writel((1<<14)|(5<<6)|(0<<3), adc_con); //选择ADC通道
__raw_writel(__raw_readl(adc_con)|0x1, adc_con); //启动ADC
printk("2\n");
while(__raw_readl(adc_con) &0x1); //等待ADC启动完成
printk("3\n");
while(!(__raw_readl(adc_con) & 0x8000)); //等待ADC完成
printk("4\n");
buf=__raw_readl(adc_dat0) & 0x3ff; //读取ADC值
*buffer=(unsigned char)buf;
buffer++;
*buffer=(unsigned char)(buf>>8);
return 2;
}
//===========================================================
// 注销设备
//===========================================================
void adc_cleanup()
{
//========关闭ADC时钟========
if(adc_clk)
{
clk_disable(adc_clk);
clk_put(adc_clk);
adc_clk = NULL;
}
//========注销设备========
cdev_del(my_cdev);
class_device_destroy(my_class, MKDEV(ADC_MAJOR, ADC_MINOR));
class_destroy(my_class);
printk("AdcDriver removed success!\r\n");
}
module_init(adc_init);
module_exit(adc_cleanup);
MODULE_LICENSE("GPL");
注意:对于红色的部分,如果去掉会导致系统卡死在驱动中,最终崩溃,因为系统移植的时候adc时钟是默认关闭的,需要重新开启。见:./arch/arm/mach-s3c2410/clock.c
测试程序:
#include "stdio.h"
#include "sys/types.h"
#include "sys/ioctl.h"
#include "stdlib.h"
#include "termios.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "sys/time.h"
int main(int argc, char **argv)
{
int fd,ret,adcdat0;
char buf[2];
fd=open("/dev/adc_dev",0);//打开设备
if(fd<0)
{
printf("open adc error\n");
exit(1);
}
else
{
printf("success\n");
while(1)
{
read(fd,buf,2);//读设备
adcdat0=buf[1];
adcdat0=adcdat0<<8|buf[0];
printf("adc_value = %d \n",adcdat0);
sleep(1);
}
}
close(fd);
}