s3c2440 ADC驱动

来源:互联网 发布:rhino5 for mac授权码 编辑:程序博客网 时间:2024/05/17 07:25

S3C2440 ADC结构图 

 

A/D转换时间 当GCLK频率为50MHz,预分频系数为49时,10位转换时间如下: A/D converter freq. = 50MHz/(49+1) = 1MHz
Conversion time = 1/(1MHz / 5cycles) = 1/200KHz = 5 us
   触屏接口模式 1.普通转换模式
单转换模式更多的用于普通的ADC转换,通过设置ADCCON初始化这个模式,并通过都写ADCDAT0来完成转换过程。
2.分离X/Y坐标模式
设置ADCTSC寄存器为0x69进入x坐标转换模式,触屏控制器会将转换后的x坐标值保存在ADCDAT0中,然后发出INT_ADC中断。
设置ADCTSC寄存器为0x9a进入x坐标转换模式,触屏控制器会将转换后的y坐标值保存在ADCDAT1中,然后发出INT_ADC中断。
3.自动(顺序)X/Y坐标转换模式
设置ADCTSC寄存器为0x0c,会自动进入X/Y坐标模式,触摸屏控制器会自动转换触点的x/y坐标值然后分别保存到ADCDAT0和ADCDAT1寄存器中,然后产生INT_ADC中断。
4.等待中断模式
设置ADTSC寄存器为0xd3即可令触摸屏控制器处于等待中断模式,当触屏被按下时会产生INT_TC中断,这是触摸屏要进入2或3模式来读取x,y坐标。 驱动源码:

#include #include #include #include #include #include #include #include #include #include #include #include #include 

static void __iomem *adc_base; //用于保存映射后的虚拟地址

#define ADCCON (adc_base + S3C2410_ADCCON) //ADC control

#define ADCTSC (adc_base + S3C2410_ADCTSC) //ADC touch screen control

#define ADCDLY (adc_base + S3C2410_ADCDLY) //ADC start or Interval Delay

#define ADCDAT0 (adc_base + S3C2410_ADCDAT0) //ADC conversion data 0

#define ADCDAT1 (adc_base + S3C2410_ADCDAT1) //ADC conversion data 1

#define ADCUPDN (adc_base + 0x14) //Stylus Up/Down interrupt status

/* ADCCON寄存器设置 */
#define PRESCALE_DIS (0 << 14) //预分频禁止
#define PRESCALE_EN (1 << 14) //预分频使能
#define PRSCVL(x) ((x) << 6) //预分频系数,0~255之间
#define ADC_INPUT(x) ((x) << 3) //设置模拟信号输入频道
#define ADC_START (1 << 0) //通过读取启动ADC
#define ADC_ENDCVT (1 << 15) //转换结束标志

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

#define DEVICE_NAME “adc”

typedef struct {
wait_queue_head_t adc_waitq; //adc工作队列
int channel;
int prescale;
} ADC_DEV;

static ADC_DEV adc_dev;

static volatile int ev_adc = 0; //adc状态标记,1表示数据可读

static int adc_data;

DECLARE_MUTEX(ADC_LOCK);

/* 设置输入频道为ch,预分频系数为prescale,并使能ADC转换 */
static void start_adc(int ch, int prescale)
{ unsigned int tmp;

tmp = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT(ch);
writel(tmp, ADCCON);

tmp = readl(ADCCON);
tmp |= ADC_START;
writel(tmp, ADCCON);
}

/* adc中断处理函数 */
static irqreturn_t adc_irq(int irq, void *dev_id)
{

if (!ev_adc) {
/* 读取adc转换结果,手册上可以得出转换结果位于9:0位 */
adc_data = readl(adc_base + S3C2410_ADCDAT0) & 0x3ff;

ev_adc = 1; //标记数据可读
wake_up_interruptible(&adc_dev.adc_waitq);
}

return IRQ_HANDLED;
}

static int adc_open(struct inode *inode, struct file *file)
{ int ret;
/* 初始化adc等待队列 */
init_waitqueue_head(&(adc_dev.adc_waitq));

ret = request_irq(IRQ_ADC,adc_irq, IRQF_SHARED, DEVICE_NAME, &adc_dev);
if (ret) {
printk(KERN_ERR “IRQ%d error %dn”, IRQ_ADC, ret);
return -EINVAL;
}

return 0;
}

static ssize_t adc_read(struct file *filp, char *buffer, size_t count,
loff_t * ppos)
{ /* 获取信号量 */
if (down_trylock(&ADC_LOCK)) {
return -EBUSY;
}

if (!ev_adc) {
/* 当数据不可读时,非阻塞方式读取直接返回 */
if (filp->f_flags & O_NONBLOCK) {
return -EAGAIN;
} else {
/* 启动adc */
start_adc(adc_dev.channel, adc_dev.prescale);
/* 阻塞进程,等待adc转换完成 */
wait_event_interruptible(adc_dev.adc_waitq, ev_adc);
}
}

/* 进程唤醒后,标记adc不可读 */
ev_adc = 0;

copy_to_user(buffer, (char *) &adc_data, sizeof(adc_data));

/* 释放信号量 */
up(&ADC_LOCK);

return sizeof(adc_data);
}

static int adc_release(struct inode *inode, struct file *filp)
{ free_irq(IRQ_ADC,&adc_dev);
return 0;
}

static struct file_operations adc_fops = {
.owner = THIS_MODULE,
.open = adc_open,
.read = adc_read,
.release = adc_release,
};

static struct miscdevice adc_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &adc_fops,
};

static int __init adc_init(void)
{ int ret;

/* 设置adc频道和预分频系数 */
adc_dev.channel = 0;
adc_dev.prescale = 0xff;

/* 获取ADC时钟,关于时钟框架我目前还不甚了解,
* 过几天开始分析 */
adc_clk = clk_get(NULL, “adc”);
if (!adc_clk) {
printk(KERN_ERR “failed to find adc clock sourcen”);
return -ENOENT;
}

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

/* 映射IO内存 */
adc_base = ioremap(S3C2410_PA_ADC, 0x14);//一共20个端口,故为0x14
if (adc_base == NULL) {
printk(KERN_ERR “Failed to remap register blockn”);
ret = -EINVAL;
goto err_noclk;
}

/* 注册设备 */
ret = misc_register(&adc_miscdev);
if (ret) {
printk(KERN_ERR
“cannot register miscdev on minor=%d (%d)n”,
MISC_DYNAMIC_MINOR, ret);
goto err_nomap;
}

printk(DEVICE_NAME ” initialized!n”);

return 0;

err_noclk:
clk_disable(adc_clk);
clk_put(adc_clk);

err_nomap:
iounmap(adc_base);

return ret;
}

static void __exit adc_exit(void)
{ free_irq(IRQ_ADC,&adc_dev);
iounmap(adc_base);
if (adc_clk) {
clk_disable(adc_clk);
clk_put(adc_clk);
adc_clk = NULL;
}

misc_deregister(&adc_miscdev);
}

EXPORT_SYMBOL(ADC_LOCK);

module_init(adc_init);
module_exit(adc_exit);

MODULE_LICENSE(“GPL”);
MODULE_DESCRIPTION(“MINI2440 ADC Driver”); 测试程序在下页

测试程序源码:

#include   #include   #include     int main(int argc, char **argv)  {      int fd;      fd = open("/dev/adc", 0);      if(fd < 0)      {          printf("Open ADC Device Faild!n");          exit(1);      }      while(1)      {          int ret;          int data;             ret = read(fd, &data, sizeof(data));          if(ret != sizeof(data))          {              if(errno != EAGAIN)              {                  printf("Read ADC Device Faild!n");              }              continue;          }          else          {              printf("Read ADC value is: %dn", data);          }      }      close(fd);      return 0;  }
原创粉丝点击