驱动之路四------adc驱动(input设备)
开发板:smdk6410
开发环境:Linux
突然想起一点,写这些驱动,内核需要配成支持抢占才行。
前面的博客已经将其它的基本知识都解释了,这里也就不过多的阐述了,咱就直接写代码吧
这次写的是adc驱动,将其做为输入设备进行使用,
先写头文件,s3c_adc.h
#ifndef __ADC_H
#define __ADC_H #include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/input.h> struct adc_info {
char name[32];
int user;
int status;
void __iomem *v;
struct input_dev *dev;
struct clk *clk;
struct timer_list timer;
int irq;
irqreturn_t (*handle)(int no, void *data);
}; #define S3C_PA_ADC 0x7e00b000
#define S3C_SZ_ADC SZ_4K #define ADCCON 0x000
#define ADCTSC 0x004
#define ADCDLY 0x008
#define ADCDAT0 0x00C
#define ADCDAT1 0x010
#define ADCUPDN 0x014
#define ADCCLRINT 0x018
#define ADCCLRINTPNDNUP 0x020 #define S3C_IRQ_ADC_S IRQ_ADC
#define S3C_IRQ_ADC_E IRQ_ADC #endif
头文件主要即使设备信息结构体和相关的宏定义,
现在写设备文件,
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h> #include "s3c_adc.h" void b_release(struct device *dev)
{
printk("Device is released\n");
}
//资源也是两类,MEM一类,IRQ一类
struct resource b_res[] = {
[0] = {
.start = S3C_PA_ADC,
.end = S3C_PA_ADC + S3C_SZ_ADC - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = S3C_IRQ_ADC_S,
.end = S3C_IRQ_ADC_E,
.flags = IORESOURCE_IRQ,
}
}; struct platform_device dev = {
.name = "s3c-my-adc",
.id = -1,
.num_resources = ARRAY_SIZE(b_res),
.resource = b_res,
.dev = {
.release = b_release,
}
}; static __init int module_test_init(void)
{
return platform_device_register(&dev);
} static __exit void module_test_exit(void)
{
platform_device_unregister(&dev);
} module_init(module_test_init);
module_exit(module_test_exit); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Musesea");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("Test for module");
看过前几篇驱动的应该都直到,套路比较固定,只要将其中的很少的东西修改以下就行,就不详细说每一步的功能了,
下面写驱动文件,这才是大头,
#include <linux/init.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/timer.h> #include "s3c_adc.h" void start_adc(struct adc_info *a)
{
u32 tmp; tmp = readl(a->v + ADCCON);
tmp |= 1;
writel(tmp, a->v + ADCCON);
}
//这个函数运行在中断上下文,函数体内可不能含有可睡眠的函数
void do_timer(unsigned long data)
{
struct adc_info *a; a = (struct adc_info *)data; //中断上下文
start_adc(a);
mod_timer(&a->timer, jiffies + HZ / 2);
} int get_adc(struct adc_info *a)
{
u32 tmp; tmp = readl(a->v + ADCDAT0);
tmp = tmp & 0xfff;
return tmp;
} irqreturn_t do_adc(int no, void *data)
{
struct adc_info *a = data;
int adc_val; //获取adc转换的结果
adc_val = get_adc(a); input_report_abs(a->dev, ABS_X, adc_val);
input_sync(a->dev); //清中断
writel(1, a->v + ADCCLRINT); return IRQ_HANDLED;
} void s3c_adc_exit(struct adc_info *a)
{
printk("Driver is release.\n");
} void s3c_adc_init(struct adc_info *a)
{
u32 tmp; //tmp = readl(a->v + ADCCON);
tmp = (1 << 16) | (1 << 14) | (21 << 6);
writel(tmp, a->v + ADCCON);
} int b_probe(struct platform_device *pdev)
{
struct resource *a_res, *irq_res;
struct adc_info *adc;
int ret; //1.申请资源
a_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if(!a_res || !irq_res)
return -EBUSY; //2.分配adc_info
adc = kzalloc(sizeof(struct adc_info), GFP_KERNEL);
if(!adc)
return -ENOMEM; //3.ioremap
adc->v = ioremap(a_res->start, a_res->end - a_res->start + 1);
if(!adc->v)
{
ret = -ENOMEM;
goto remap_error;
} //申请设备结构体
adc->dev = input_allocate_device();
if(!adc->dev)
{
ret = -ENOMEM;
goto input_allocate_device_error;
} adc->dev->name = pdev->name;
adc->dev->uniq = "20131113";
adc->dev->phys = "/dev/eventx";
adc->dev->id.bustype = BUS_HOST;
adc->dev->id.vendor = 110;
adc->dev->id.product = 120;
adc->dev->id.version = 119; //4.设置该设备要支持的事件类型
set_bit(EV_SYN, adc->dev->evbit);
set_bit(EV_ABS, adc->dev->evbit); //使该设备支持绝对的x事件
//写绝对事件时不要使用set_bit,使用内核给出的下列函数
input_set_abs_params(adc->dev, ABS_X, 0, 4095, 0, 0); //5.注册input设备
ret = input_register_device(adc->dev);
if(ret)
goto input_register_device_error;
//将adc保存到pdev中
platform_set_drvdata(pdev, adc); //6.打开clock;注意在初始化adc之前一定要写打开时钟;只要使用时钟的设备,在初始化之前都要先打开时钟
adc->clk = clk_get(NULL, "adc");
clk_enable(adc->clk); //7.初始化adc
//sprintf(adc->name, "adc");
adc->user = 0;
adc->irq = irq_res->start;
adc->handle = do_adc; //涉及时钟的硬件在初始化之前一定要确认时钟打开
s3c_adc_init(adc); //8.申请中断(adc)
ret = request_irq(adc->irq, adc->handle, 0, pdev->name, adc);
if(ret){ goto request_irq_error;
} //9.建立一个adc->timer_list,定时时间为0.5s,处理函数为do_adc->timer
setup_timer(&adc->timer, do_timer, (unsigned long)adc);
mod_timer(&adc->timer, jiffies + HZ / 2);//这里不要用0.5*HZ,内核是不支持浮点数的,也可以写成(HZ>>1) return 0; free_irq(adc->irq, adc);
request_irq_error:
input_unregister_device(adc->dev);
input_register_device_error:
input_free_device(adc->dev);
input_allocate_device_error:
iounmap(adc->v);
remap_error:
kfree(adc);
return ret;
} int b_remove(struct platform_device *pdev)
{
struct adc_info *adc; adc = platform_get_drvdata(pdev); del_timer_sync(&adc->timer);
free_irq(adc->irq, adc);
s3c_adc_exit(adc); //clock的反操作
clk_disable(adc->clk);
clk_put(adc->clk); input_free_device(adc->dev);
iounmap(adc->v);
kfree(adc); return 0;
} struct platform_driver drv = {
.driver = {
.name = "s3c-my-adc",
},
.probe = b_probe,
.remove = b_remove,
}; static __init int module_test_init(void)
{
return platform_driver_register(&drv);
} static __exit void module_test_exit(void)
{
platform_driver_unregister(&drv);
} module_init(module_test_init);
module_exit(module_test_exit); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Musesea");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("Test for module");
应该注意的地方在代码中做了标注了,至此又一个驱动搞完了。
还是那句话,大家若是发现有什么问题,一定要告诉我,大家一起学习了。
驱动之路四------adc驱动(input设备)的更多相关文章
- linux 混杂设备驱动之adc驱动
linux2.6.30.4中,系统已经自带有了ADC通用驱动文件---arch/arm/plat-s3c24xx/adc.c,它是以平台驱动设备模型的架构来编写的,里面是一些比较通用稳定的代码,但是l ...
- Linux input子系统学习总结(三)----Input设备驱动
Input 设备驱动 ---操作硬件获取硬件寄存器中设备输入的数据,并把数据交给核心层: 一 .设备驱动的注册步骤: 1.分配一个struct input_dev : struct ...
- Linux内核驱动学习(四)Platform设备驱动模型
Linux platform设备驱动模型 文章目录 Linux platform设备驱动模型 前言 框架 设备与驱动的分离 设备(device) 驱动(driver) 匹配(match) 参考 前言 ...
- FL2440驱动添加(5)ADC驱动学习笔记
由图可知,模拟ADC分为两部分功能,一部分是触屏功能,另一部分就是普通ADC功能.分别可以产生INT_TC和INT_ADC 两个中断.该ADC模块总共有8个通道可以进行模拟信号的输入,分别是AIN0. ...
- mini2440驱动奇谭——ADC驱动与測试(动态挂载驱动)
博客:http://blog.csdn.net/muyang_ren 实现功能:开发板动态载入adc驱动模块并能通过測试程序 系统:Ubuntu 14.04 驱动交叉编译内核:linux-2. ...
- STC8H开发(十四): I2C驱动RX8025T高精度实时时钟芯片
目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...
- 【安富莱二代示波器教程】第6章 示波器设计—双通道ADC驱动
第6章 示波器设计—双通道ADC驱动 本章节为大家讲解示波器的ADC驱动,采用STM32自带ADC实现.关于STM32F429的ADC,可以说处处有地雷,不小心就踩上了,如果简单的使用, ...
- [ADC]Linux ADC驱动
ADC TI adc user guide: http://processors.wiki.ti.com/index.php/Linux_Core_ADC_Users_Guide 问题: 在tools ...
- “四核”驱动的“三维”导航 -- 淘宝新UI(需求分析篇)
前言 孔子说:"软件是对客观世界的抽象". 首先声明,这里的"三维导航"和地图没一毛钱关系,"四核驱动"和硬件也没关系,而是为了复杂的应用而 ...
随机推荐
- ubuntu14.04 Markdown编辑器推荐之Remarkable
如今已经习惯了用Markdown编辑器写博文的习惯,那么ubuntu以下有什么好用的呢?搜索中发现了这个叫Remarkable的免费Markdown编辑器.为什么推荐这个呢?说说它的特点: 实时预览 ...
- 素数距离问题_ny_24.java
素数距离问题 时间限制: 3000 ms | 内存限制: 65535 KB 难度: 2 描述 现在给出你一些数,要求你写出一个程序,输出这些整数相邻最近的素数,并输出其相距长度.如果左右有等距 ...
- Filter及FilterChain的使用具体解释
一.Filter的介绍及使用 什么是过滤器? 与Servlet类似,过滤器是一些web应用程序组件,能够绑定到一个web应用程序中.可是与其它web应用程序组件不同的是,过滤器是"链&quo ...
- JavaScript引用类型之Array数组的排序方法
数组中已经存在两个JavaScript给我们定义好的重排序的方法:reverse()和sort()方法,下面来简单分析下: 1.reverse() 用于反转数组项的顺序,代码如下: <sc ...
- gulp使用心得
本文假设你之前没有用过任何任务脚本(task runner)和命令行工具,一步步教你上手Gulp.不要怕,它其实很简单,我会分为五步向你介绍gulp并帮助你完成一些惊人的事情.那就直接开始吧. 第一步 ...
- 2014.8.18for循环
for循环 1.初始状态 2.循环条件 3.循环体 4.状态改变 语法 for( 初始状态 ; 循环条件 ; 状态改变 ) { 循环体; } eg: ; i <= ; i++) { Con ...
- Csharp多态的实现(虚方法)
1.什么是抽象类 1.1虚方法是用virtual修饰,在子类中用override进行重写 1.2虚方法是一个方法,放在类里面(可以再下面的代码中看到) 1.3虚方法可以 重写,也可以不重写(这个可以再 ...
- 用C++写一个简单的发布者
节点是一个可执行程序,它连接到了ROS的网络系统中.我们将会创建一个发布者,也就是说话者节点,它将会持续的广播一个信息. 改变目录到之前所建立的那个包下: cd ~/catkin_ws/src/beg ...
- JQuery中serialize() 序列化
更多2014/8/24 来源:jquery学习浏览量:1671 学习标签: serialize 本文导读:在jQuery中,当我们使用ajax时,常常需要拼装input数据以键值对(Key/Value ...
- 一个失败的操作系统MULTICS
Unix的诞生和Multics(Multiplexed Information and Computing System)是有一定渊源的.当时开发者Brian Kernighan开玩笑地戏称这个不完善 ...