驱动——platform驱动ADC

本文介绍了STM32平台驱动ADC的过程,包括设备树在平台驱动中的作用、如何通过平台总线获取硬件资源和中断号,以及字符设备的注册和设备节点的创建。文章还讨论了平台总线驱动的模板和ADC驱动的寄存器初始化,强调了ADC转换过程中的中断处理和数据读取。

我们再谈一下平台总线。
前面讲平台总线是可以让drv和dev实现分离。主要作用是用于平台升级。
现在结合设备树来看一下 平台总线的好处。(平台总线是虚拟总线,用于一些ADC,PWM这样的没有实体总线连接(iIC spi…)的一些设备,为了管理这些设备统一虚拟出平台总线来管理)

1)首先我们要了解到
当我们在设备树文件上写了应该设备后(属于我们的直接定义的设备,可以有2^20个子设备(前面说可以放无限个是错误的)
它就会把设备树的信息放在/sys/bus/plaoform/device下(那我要是iic设备就会放在/sys/bus/i2c下)
2)这样我们只要写DRV程序就可以了,利用platfrom根据compatinle进行匹配,然后用
platform_get_resource拿到硬件信息;
platform_get_irq拿到中断号
这样就可以在驱动程序中拿到硬件资源了,并且我们只需要写驱动程序,而硬件信息只需要修改设备树就可以了。
就不用像以前一样,还要写dev写drv写bus。。。这么麻烦。
———————————————————————————————————————
申请设备号的三种方式:
1)
register_chrdev(unsigned int major, const char * name, const struct file_operations * fops)
在 2.4 的内核我们使用 register_chrdev(0, “hello”, &hello_fops) 来进行字符设备设备节点的分配,这种方式每一个主设备号只能存放一种设备,它们使用相同的 file_operation 结构体,也就是说内核最多支持 256 个字符设备驱动程序。

  1. register_chrdev_region(dev, 1, FSPWM_DEV_NAME);
    cdev_init(&fspwm->cdev, &fspwm_ops);
    cdev_add(&fspwm->cdev, dev, 1);
    register_chrdev_region() 函数用于分配指定的设备编号范围。它需要手动初始化cdev并且将cdev加入到内核的cdev链表中。

3)
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
alloc_chrdev_region() 函数用于动态申请设备编号范围。
——————————————————————————————————————
创建设备节点的两种方式。
对设备进行操作,其实就是对cdev进行一些操作。cdev里面有主设备号以及file_operatorion.
那么创建设备节点就可以便于用户找到主设备号并进行file_operatorion.操作。
1)手动创建:利用命令 :mknod /dev/(你自定义设备的名字) c(字符设备)/d(块设备) 主设备号(一定要和你申请的一样)

2)自动创建:对于热插拔事件,我们应用程序要完成,插入加载模块,并且自动创建设备节点,拔自动卸载模块,和注销设备节点。
对于普通事件我们也一般采用自动创建的方式。
:class_create(THIS_MODULE,“xxx”);
:device_create(…);
————————————————————————————————————————
我们还是讲怎么ADC驱动吧!!!!
平台总线驱动的另一种模板:
我们之前的都是module_init module_exit。。。实例化应该drv,设置匹配的complation 在init里面把drv注册进去,在exit里面移除。
。。。。千篇一律。。
那么我们在内核中,把这种固定的用平台总线的驱动封装了一个函数
module_platform_driver(fspwm_drv);
然后我们就可以这样做:

tatic const struct of_device_id fspwm_of_matches[] = {
	{ .compatible = "fs4412,fspwm", },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fspwm_of_matches);

struct platform_driver fspwm_drv = { 
	.driver = { 
		.name    = "fspwm",
		.owner   = THIS_MODULE,
		.of_match_table = of_match_ptr(fspwm_of_matches),
	},  
	.probe   = fspwm_probe,
	.remove  = fspwm_remove,
};

module_platform_driver(fspwm_drv);

其实module_platform_driver(fspwm_drv);里面也调用了init 和exit就是进行了封装而已。

——————————————————————————————————————
我们还是讲ADC驱动吧。。。这有什么讲的?我们模型都有了,驱动不是照套吗?
1)写设备树。
2)按上面的编写驱动代码模型。匹配成功之后调用proble。
3)在proble里面申请设备号,创建设备节点,获取设备信息。地址映射。
这样我们就拿到硬件信息了,还有虚拟地址。
那么不管他是ADC,PWM,KEY(当然key是用input子系统好一点),Led…都是对一些地址进行一些读写操作。
那我们来看一下对ADC需要哪些操作?
我们在proble下面肯定要对一些寄存器进行初始化:
writel((1 << 16) | (1 << 14) | (19 << 6), fsadc->adccon);
1<< 16是设置精度为12;
1<<14使能分频
19<<6设置为20倍分频。
初始化好了,之后我们就该操作了。我们要的是把继电器中连续变化的模拟量转化成数字量
在这里插入图片描述
在这里插入图片描述
可以看到我们的电位器连接到ain3口。所以我们要选择adcmux为0011
我们去读这个ADCDAT的0~11位就可以读到结果,
当我们数据转化的时候就会触发中断去读ADCDAT的数据,然后读取完毕,就会利用clrint清除中断。
————————————————详情请看arm——ADC裸机开发
驱动代码:

#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 "fsadc.h"

#define FSADC_MAJOR	256
#define FSADC_MINOR	6
#define FSADC_DEV_NAME	"fsadc"

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 device *dev;
	struct class *cls;
	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 (atomic_dec_and_test(&fsadc->available))
		return 0;
	else {
		atomic_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)))
			return -EFAULT;
		if (cv.chan > AIN3)
			return -ENOTTY;
		writel(cv.chan, fsadc->adcmux);
		writel(readl(fsadc->adccon) | 1, fsadc->adccon);
		if (wait_for_completion_interruptible(&fsadc->completion))
			return -ERESTARTSYS;
		cv.val = fsadc->adcval & 0xFFF;
		if (copy_to_user( (union chan_val __user *)arg, &cv, sizeof(union chan_val)))
			return -EFAULT;
		break;
	default:
		return -ENOTTY;
	}

	return 0;
}

static irqreturn_t fsadc_isr(int irq, void *dev_id)
{
	struct fsadc_dev *fsadc = dev_id;

	fsadc->adcval = readl(fsadc->adcdat);
	writel(1, fsadc->clrint);
	complete(&fsadc->completion);

	return 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)
{
	printk("--------%s------\n", __FUNCTION__);
	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);					//把匹配成功之后的pdev数据拿到fsadc结构体中

	cdev_init(&fsadc->cdev, &fsadc_ops);
	fsadc->cdev.owner = THIS_MODULE;
	ret = cdev_add(&fsadc->cdev, dev, 1);
	if (ret)
		goto add_err;
	fsadc->cls = class_create(THIS_MODULE,"adc_cls");
	fsadc->dev = device_create(fsadc->cls, NULL, dev,NULL,"fs_adc");

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);   	//从平台总线获取匹配成功的信息里面的memory信息的第0个信息
	if (!res) {
		ret = -ENOENT;
		goto res_err;
	}

	fsadc->adccon = ioremap(res->start, resource_size(res));		//映射
	if (!fsadc->adccon) {
		ret = -EBUSY;
		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);			//你添加了设备树之后,你的设备树信息,会出现在/sys/bus/plaoform/
													///sys/bus/plaoform/device/adc@1......根据compatible匹配成功
													//就可以获取到信息,中断继承,可以拿到中断号
		if (fsadc->irq < 0) {
		ret = fsadc->irq;
		goto irq_err;
	}

	ret = request_irq(fsadc->irq, fsadc_isr, 0, "adc", fsadc);		//申请中断
	if (ret)
		goto irq_err;

	writel((1 << 16) | (1 << 14) | (19 << 6), fsadc->adccon);

	init_completion(&fsadc->completion);
	atomic_set(&fsadc->available, 1);

	return 0;
irq_err:
	iounmap(fsadc->adccon);
map_err:
res_err:
	cdev_del(&fsadc->cdev);
add_err:
	kfree(fsadc);
mem_err:
	unregister_chrdev_region(dev, 1);
reg_err:
	return ret;
}

static int fsadc_remove(struct platform_device *pdev)
{
	dev_t dev;
	struct fsadc_dev *fsadc = platform_get_drvdata(pdev);

	dev = MKDEV(FSADC_MAJOR, FSADC_MINOR);

	writel((readl(fsadc->adccon) & ~(1 << 16)) | (1 << 2), fsadc->adccon);
	free_irq(fsadc->irq, fsadc);
	iounmap(fsadc->adccon);
	cdev_del(&fsadc->cdev);
	kfree(fsadc);
	unregister_chrdev_region(dev, 1);
	return 0;
}

static const struct of_device_id fsadc_of_matches[] = {
	{ .compatible = "fs4412,fsadc", },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsadc_of_matches);

struct platform_driver fsadc_drv = { 
	.driver = { 
		.name    = "fsadc",
		.owner   = THIS_MODULE,
		.of_match_table = of_match_ptr(fsadc_of_matches),
	},  
	.probe   = fsadc_probe,
	.remove  = fsadc_remove,
};

module_platform_driver(fsadc_drv);

MODULE_LICENSE("GPL");

应用代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>

#include "fsadc.h"

int main(int argc, char *argv[])
{
	int fd;
	int ret;
	union chan_val cv;

	fd = open("/dev/fs_adc", O_RDWR);
	if (fd == -1)
		goto fail;

	while (1) {
		cv.chan = 3;
		ret = ioctl(fd, FSADC_GET_VAL, &cv);
		if (ret == -1)
			goto fail;
		printf("current volatage is: %.2fV\n", 1.8 * cv.val / 4095.0);
		sleep(1);
	}
fail:
	perror("adc test");
	exit(EXIT_FAILURE);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值