Linux 驱动实例 (ADC/PWM/RTC)

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->adccon);/
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值