ADC实例
(1)初始化ADC,包括选择精度、设置分频值、设置ADC为正常模式、设置转换启动方式。
(2)注册ADC中断处理函数。
(3)在上层需要ADC数据时,选择好ADC通道,启动转换,然后等待一个完成量。
(4)转换结束后产生中断,在中断处理函数中获取转换结果值,同CLRINTADC寄存器写任意值清除中断,然后唤醒等待完成量的进程。
(5)进程被唤醒,返回转换结果给上层。
adc@126C0000{
compatible="fs4412,fsadc";
reg=<0x126C000 32>;
interrupt-parent=<&combiner>;//开发板中有的中断是直接接入GIC中断控制器的,有的中断则是先通过中断组合器(Combiner)将多个中断复合后再接入GIC中断控制器。 这里ADC终端属于INTG10这一组中断。 combiner是其父中断控制器。interrupt第一个属性是中断在组合器中的组号,第二个cell是中断在该组的序号。
interrupt=<10,3>;
};
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/slab.h>
#include<linux/ioctl.h>
#include<linux/uaccess.h>
#include<linux/io.h>
#include<linux/ioport.h>
#include<linux/platform_device.h>
#include<linux/of.h>
#include<linux/interrupt.h>
#include"adc.h"
#define FSADC_MAJOR 256
#define FSADC_MINOR 6
#define FSADC_DEV_NAME "fsadc"
/*
adc@126C0000{
compatible="fs4412,fsadc";
reg=<0x126C000 32>;
interrupt-parent=<&combiner>;
interrupt=<10,3>;
};
*/
struct fsadc_dev{
unsigned int __iomem *adccon;
unsigned int __iomem *adcdat;
unsigned int __iomem *clrint;
unsigned int __iomem *adcmux;
unsigned int adcval;
struct completion completion;//完成量
atomic_t available;
unsigned int irq;
struct cdev cdev;
};
static int fsadc_open(struct inode *inode,struct file *filp)
{
struct fsadc_dev *fsadc=container_of(inode->i_cdev,struct fsadc_dev,cdev);
filp->private_data=fsadc;
if(atmoic_dec_and_test(&fsadc->available))
return 0;
else{
atomoic_inc(&fsadc->available);
return -EBUSY;
}
}
static int fsadc_release(struct inode *inode,struct file *filp)
{
struct fsadc_dev *fsadc=filp->private_data;
atomic_inc(&fsadc->available);
return 0;
}
static long fsadc_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
struct fsadc_dev *fsadc=filp->private_data;
union chan_val cv;
if(_IOC_TYPE(cmd)!=FSADC_MAGIC)
{return -ENOTTY;}
switch(cmd)//上层选择通道
{
case FSADC_GET_VAL:
if(copy_from_user(&cv,(union chan_val __user *)arg,sizeof(union chan_val)))
//将用户空间传递过来的参数(arg)复制到内核空间的 cv 变量。这是一个 union chan_val 类型的变量,
//包含 ADC 通道(chan)和 ADC 值(val)。
return -EFAULT;
if(cv.chan>AIN3)//检查通道值(cv.chan)是否在允许的范围内(AIN0 到 AIN3)
return -ENOTTY;
writel(cv.chan,fsadc->adcmux);//将通道值(cv.chan)写入 fsadc->adcmux 寄存器,以便 ADC 转换所需的通道。
writel(readl(fsadc->adccon)|1,fsadc->adccon);//设置 fsadc->adccon 寄存器的最低位以启动 ADC 转换。
//通过将寄存器当前值与 1 进行按位或操作来实现的。
if(wait_for_completion_interruptible(&fsadc->completion))//等待 ADC 转换完成。这个函数会阻塞当前进程,
//直到 completion 结构中的事件发生
return -ERESTARTSYS;//通常这表示系统调用将被重启。
cv.val=fsadc->adcval&0xFFF;//这行代码将fsadc->adcval的值(可能是ADC的原始转换结果)进行位操作,并将结果保存在cv.val中。&0xFFF操作是获取这个值的低12位,是因为ADC的分辨率是12位。
if(copy_to_user((union chan_val __user *)arg,&cv,sizeof(union chan_val)))//这是将内核空间的数据复制到用户空间。在这里,它将cv的值复制到用户空间指针arg指向的位置。如果复制失败(例如,由于arg指针是一个无效的用户空间地址),这个函数将返回非零值
return -EFAULT;
break;
default:
return -ENOTTY;
}
return 0;
}
static irqreturn_t fsadc_isr(int irq,void *dev_id)//它是在 ADC 设备完成转换后会出发这个中断 并触发中断时被调用的函数。
{
struct fsadc_dev *fsadc=dev_id;//ADC 设备的所有必要信息。
fsadc->adcval=readl(fsadc->adcdat);// fsadc->adcdat 寄存器中读取 ADC 转换结果,
//并将其存储在 fsadc->adcval 变量中。
writel(1,fsadc->clrint);//向 fsadc->clrint 寄存器写入 1,以清除中断并避免连续触发。
complete(&fsadc->completion);//通知等待 fsadc->completion 结构中的事件的进程(
//在这个例子中,是等待 ADC 转换完成的进程)可以继续执行。
return IRQ_HANDLED;//IRQ_HANDLED,表示中断已经被处理。
}
static struct file_operations fsadc_ops={
.owner=THIS_MODULE,
.open=fsadc_open,
.release=fsadc_release,
.unlocked_ioctl=fsadc_ioctl,
};
static int fsadc_probe(struct platform_device *pdev)
{
int ret;
dev_t dev;
struct fsadc_dev *fsadc;
struct resource *res;
dev=MKDEV(FSADC_MAJOR,FSADC_MINOR);
ret=register_chrdev_region(dev,1,FSADC_DEV_NAME);
if (ret)
goto reg_err;
fsadc=kzalloc(sizeof(struct fsadc_dev),GFP_KERNEL);
if(!fsadc)
{
ret=-ENOMEM;
goto mem_err;
}
platform_set_drvdata(pdev,fsadc);//将设备驱动程序的私有数据与平台设备相关联。
//当在驱动程序的其他部分(例如中断处理程序或者 remove 函数)需要访问这些私有数据时,
//可以通过平台设备结构体轻松地获取到它们。这种机制使得驱动程序可以在不同的设备实例之
//间共享代码,同时保留了每个实例的特定数据。
cdev_init(&fsadc->cdev,&fsadc_ops);
fsadc->cdev.owner=THIS_MODULE;
ret=cdev_add(&fsadc->cdev,dev,1);
if(ret)
goto add_err;
res=platform_get_resource(pdev,IORESOURCE_MEM,0);//获取设备树平台资源中的IO资源
if(!res)
{
res=-ENOENT;
goto res_err;
}
fsadc->adccon=ioremap(res->start,resource_size(res));
if(!fsadc->adccon)
{
ret=-ENOENT;
goto map_err;
}
fsadc->adcdat=fsadc->adccon+3;
fsadc->clrint=fsadc->adccon+6;
fsadc->adcmux=fsadc->adccon+7;
fsadc->irq=platform_get_irq(pdev,0);//从平台数据中获取设备号
if(!fsadc->adccon)
{
ret=fsadc->irq;
goto irq_err;
}
//内核中请求一个中断号(IRQ number)并为其分配一个中断处理函数。
//当设备触发中断时,内核会调用这个处理函数。
ret=request_irq(fsadc->irq,fsadc_isr,0,"adc",fsadc);
if(ret)
goto irq_err;
writel((1<<16)|(1<<14)|(19<<6),fsadc->adc