设备树dts文件
arch/arm/boot/dts/suniv.dtsi 添加:

lradc: lradc@1c23400 {compatible = "allwinner,sun4i-a10-lradc-keys";reg = <0x01c23400 0x400>;interrupts = <22>;status = "disabled";
};&lradc {vref-supply = <&reg_vcc3v3>;status = "okay";button@200 {label = "Volume Up";linux,code = <132>;channel = <0>;voltage = <200000>;};button@400 {label = "Volume Down";linux,code = <133>;channel = <0>;voltage = <400000>;};button@600 {label = "Select";linux,code = <134>;channel = <0>;voltage = <600000>;};button@800 {label = "Start";linux,code = <135>;channel = <0>;voltage = <800000>;};
};

编译生成设备树:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- dtbs -j4

驱动源码

#include <linux/err.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>#define LRADC_CTRL     0x00
#define LRADC_INTC      0x04
#define LRADC_INTS      0x08
#define LRADC_DATA0     0x0c
#define LRADC_DATA1     0x10/* LRADC_CTRL bits */
#define FIRST_CONVERT_DLY(x)    ((x) << 24) /* 8 bits */
#define CHAN_SELECT(x)      ((x) << 22) /* 2 bits */
#define CONTINUE_TIME_SEL(x)    ((x) << 16) /* 4 bits */
#define KEY_MODE_SEL(x)     ((x) << 12) /* 2 bits */
#define LEVELA_B_CNT(x)     ((x) << 8)  /* 4 bits */
#define HOLD_KEY_EN(x)      ((x) << 7)
#define HOLD_EN(x)      ((x) << 6)
#define LEVELB_VOL(x)       ((x) << 4)  /* 2 bits */
#define SAMPLE_RATE(x)      ((x) << 2)  /* 2 bits */
#define ENABLE(x)       ((x) << 0)/* LRADC_INTC and LRADC_INTS bits */
#define CHAN1_KEYUP_IRQ     BIT(12)
#define CHAN1_ALRDY_HOLD_IRQ    BIT(11)
#define CHAN1_HOLD_IRQ      BIT(10)
#define CHAN1_KEYDOWN_IRQ   BIT(9)
#define CHAN1_DATA_IRQ      BIT(8)
#define CHAN0_KEYUP_IRQ     BIT(4)
#define CHAN0_ALRDY_HOLD_IRQ    BIT(3)
#define CHAN0_HOLD_IRQ      BIT(2)
#define CHAN0_KEYDOWN_IRQ   BIT(1)
#define CHAN0_DATA_IRQ      BIT(0)/* struct lradc_variant - Describe sun4i-a10-lradc-keys hardware variant* @divisor_numerator:        The numerator of lradc Vref internally divisor* @divisor_denominator:  The denominator of lradc Vref internally divisor*/
struct lradc_variant {u8 divisor_numerator;u8 divisor_denominator;
};static const struct lradc_variant lradc_variant_a10 = {.divisor_numerator = 2,.divisor_denominator = 3
};static const struct lradc_variant r_lradc_variant_a83t = {.divisor_numerator = 3,.divisor_denominator = 4
};struct sun4i_lradc_keymap {u32 voltage;u32 keycode;
};
/*sun4i_lradc_data结构体*/
struct sun4i_lradc_data {struct device *dev;struct input_dev *input;void __iomem *base;struct regulator *vref_supply;struct sun4i_lradc_keymap *chan0_map;const struct lradc_variant *variant;u32 chan0_map_count;u32 chan0_keycode;u32 vref;
};
/*lradc中断*/
static irqreturn_t sun4i_lradc_irq(int irq, void *dev_id)
{struct sun4i_lradc_data *lradc = dev_id;u32 i, ints, val, voltage, diff, keycode = 0, closest = 0xffffffff;ints  = readl(lradc->base + LRADC_INTS);  //读取中断类型/** lradc supports only one keypress at a time, release does not give* any info as to which key was released, so we cache the keycode.*/
//中断类型为按键释放if (ints & CHAN0_KEYUP_IRQ) {input_report_key(lradc->input, lradc->chan0_keycode, 0); /*向输入子系统报告产生按键事件*/lradc->chan0_keycode = 0;  //按键编码置零}
//中断类型为按键按下,并且当前按键编码为0if ((ints & CHAN0_KEYDOWN_IRQ) && lradc->chan0_keycode == 0) {val = readl(lradc->base + LRADC_DATA0) & 0x3f;  //读取adc值voltage = val * lradc->vref / 63;  //计算电压值(vref/63 是电压分辨率,采样位数6bit)
//根据电压,匹配按键值for (i = 0; i < lradc->chan0_map_count; i++) {diff = abs(lradc->chan0_map[i].voltage - voltage);//匹配电压差值最小的键值if (diff < closest) {closest = diff;keycode = lradc->chan0_map[i].keycode;  //确定当前按下的键值}}lradc->chan0_keycode = keycode;input_report_key(lradc->input, lradc->chan0_keycode, 1);  /*向输入子系统报告产生按键事件*/}input_sync(lradc->input);   /*通知接收者,一个报告发送完毕*/writel(ints, lradc->base + LRADC_INTS); //写回寄存器(还未查阅芯片手册,应该是清除中断吧)return IRQ_HANDLED;
}static int sun4i_lradc_open(struct input_dev *dev)
{struct sun4i_lradc_data *lradc = input_get_drvdata(dev); /* 从设备的私有数据区获取设备描述结构体,可通过nput_set_drvdata 保存数据*/int error;/*regulator 是驱动中电源管理的基础设施。要先注册到内核中,然后使用这些电压输出的模块get其regulator,在驱动中的init里,在适当时间中进行电压电流的设置.与 gpio 差不多? 一样是基础设施?一种称为校准器(regulator)的动态电压和电流控制的方法,很有参考意义和实际使用价值。*/error = regulator_enable(lradc->vref_supply);  //打开校准器if (error)return error;//通过此接口获取当前电源电压。lradc->vref = regulator_get_voltage(lradc->vref_supply) *lradc->variant->divisor_numerator /lradc->variant->divisor_denominator;/**配置LRADC_INTC寄存器,设定采样时间为4ms / 250hz。等待2 * 4 ms的key to*稳定按下,等待(1 + 1)* 4毫秒后释放按键*/writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |SAMPLE_RATE(0) | ENABLE(1), lradc->base + LRADC_CTRL);writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC);return 0;
}static void sun4i_lradc_close(struct input_dev *dev)
{struct sun4i_lradc_data *lradc = input_get_drvdata(dev);/* Disable lradc, leave other settings unchanged */writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |SAMPLE_RATE(2), lradc->base + LRADC_CTRL);writel(0, lradc->base + LRADC_INTC);regulator_disable(lradc->vref_supply);
}
//加载设备树资源
static int sun4i_lradc_load_dt_keymap(struct device *dev,struct sun4i_lradc_data *lradc)
{struct device_node *np, *pp;int i;int error;np = dev->of_node;if (!np)return -EINVAL;lradc->chan0_map_count = of_get_child_count(np);if (lradc->chan0_map_count == 0) {dev_err(dev, "keymap is missing in device tree\n");return -EINVAL;}lradc->chan0_map = devm_kmalloc_array(dev, lradc->chan0_map_count,sizeof(struct sun4i_lradc_keymap),GFP_KERNEL);if (!lradc->chan0_map)return -ENOMEM;i = 0;for_each_child_of_node(np, pp) {struct sun4i_lradc_keymap *map = &lradc->chan0_map[i];u32 channel;error = of_property_read_u32(pp, "channel", &channel);if (error || channel != 0) {dev_err(dev, "%pOFn: Inval channel prop\n", pp);return -EINVAL;}error = of_property_read_u32(pp, "voltage", &map->voltage);if (error) {dev_err(dev, "%pOFn: Inval voltage prop\n", pp);return -EINVAL;}error = of_property_read_u32(pp, "linux,code", &map->keycode);if (error) {dev_err(dev, "%pOFn: Inval linux,code prop\n", pp);return -EINVAL;}i++;}return 0;
}static int sun4i_lradc_probe(struct platform_device *pdev)
{struct sun4i_lradc_data *lradc;struct device *dev = &pdev->dev;int i;int error;lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL);if (!lradc)return -ENOMEM;error = sun4i_lradc_load_dt_keymap(dev, lradc);if (error)return error;lradc->variant = of_device_get_match_data(&pdev->dev);if (!lradc->variant) {dev_err(&pdev->dev, "Missing sun4i-a10-lradc-keys variant\n");return -EINVAL;}lradc->vref_supply = devm_regulator_get(dev, "vref");if (IS_ERR(lradc->vref_supply))return PTR_ERR(lradc->vref_supply);lradc->dev = dev;lradc->input = devm_input_allocate_device(dev);if (!lradc->input)return -ENOMEM;lradc->input->name = pdev->name;lradc->input->phys = "sun4i_lradc/input0";lradc->input->open = sun4i_lradc_open;lradc->input->close = sun4i_lradc_close;lradc->input->id.bustype = BUS_HOST;lradc->input->id.vendor = 0x0001;lradc->input->id.product = 0x0001;lradc->input->id.version = 0x0100;__set_bit(EV_KEY, lradc->input->evbit);for (i = 0; i < lradc->chan0_map_count; i++)__set_bit(lradc->chan0_map[i].keycode, lradc->input->keybit);input_set_drvdata(lradc->input, lradc);lradc->base = devm_ioremap_resource(dev,platform_get_resource(pdev, IORESOURCE_MEM, 0));if (IS_ERR(lradc->base))return PTR_ERR(lradc->base);error = devm_request_irq(dev, platform_get_irq(pdev, 0),sun4i_lradc_irq, 0,"sun4i-a10-lradc-keys", lradc);if (error)return error;error = input_register_device(lradc->input);if (error)return error;return 0;
}static const struct of_device_id sun4i_lradc_of_match[] = {{ .compatible = "allwinner,sun4i-a10-lradc-keys",.data = &lradc_variant_a10 },{ .compatible = "allwinner,sun8i-a83t-r-lradc",.data = &r_lradc_variant_a83t },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match);static struct platform_driver sun4i_lradc_driver = {.driver = {.name    = "sun4i-a10-lradc-keys",.of_match_table = of_match_ptr(sun4i_lradc_of_match),},.probe  = sun4i_lradc_probe,
};module_platform_driver(sun4i_lradc_driver);MODULE_DESCRIPTION("Allwinner sun4i low res adc attached tablet keys driver");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");

按键输入测试代码


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>#include <linux/input.h>int fd;void my_signal_fun(int signum)
{struct input_event buttons_event, leds_event;/* [cgw]: 异步通知产生时返回的数据 */read(fd, &buttons_event, sizeof(struct input_event));/* [cgw]: 打印事件类型,事件码,事件值 */printf("type: 0x%x code: 0x%x value: 0x%x\n", buttons_event.type,buttons_event.code,  buttons_event.value);// /* [cgw]: 返回的是KEY_L或KEY_S值 */// if (buttons_event.code == KEY_L || buttons_event.code == KEY_S) {//     /* [cgw]: 按键弹起 */ //     if (buttons_event.value == 0) {//         /* [cgw]: 构造一个EV_LED事件 *///         //leds_event.type = EV_SND;//         leds_event.type = EV_LED;//         //leds_event.code = SND_BELL;//         leds_event.code = LED_MUTE;//         /* [cgw]: KEY_L和KEY_S控制LED的亮灭 *///         if (buttons_event.code == KEY_L) {//             leds_event.value = 0xAA;//         } else if (buttons_event.code == KEY_S) {//             leds_event.value = 0xEE;    //         }//         /* [cgw]: 发送LED控制事件 *///         write(fd, &leds_event, sizeof(struct input_event));//         printf("led write!\n");//     }// }}int main(int argc, char **argv)
{int Oflags;/* [cgw]: 设置需要处理的信号SIGIO,即输入文件会请求一个SIGIO* 信号,当有新数据到来这个信号会发给filp->f_owner进程*/signal(SIGIO, my_signal_fun);fd = open("/dev/input/event0", O_RDWR | O_NONBLOCK);//printf("fd = 0x%x\n", fd);if (fd < 0){printf("can't open!\n");}/* [cgw]: 根据文件标识符fd,设置能够获得这个文件的进程(owner) * getpid()获得当前进程ID*/fcntl(fd, F_SETOWN, getpid());/* [cgw]: 获得file->f_flags标志 */Oflags = fcntl(fd, F_GETFL); /* [cgw]: 置位FASYNC使能异步通知 */fcntl(fd, F_SETFL, Oflags | FASYNC);while (1){/* [cgw]: 休眠 */sleep(1000);}return 0;
}

lradc按键驱动程序分析(for F1C100S)相关推荐

  1. linux3.4.0 按键驱动程序分析(pandaboard omap4460)

    linux3.4.0 按键驱动程序分析(pandaboard omap4460) 在内核中,按键的驱动程序已经设计好了,要使自己板上的按键工作起来,需要做的是在相应的文件中添加硬件信息,然后对内核进行 ...

  2. MCU——简易单片机按键驱动程序分析

    前言:下面是自己写的一个简易的单片机按键程序,主要采用了物理键值和逻辑键值查表映射的方法.此外还添加了长按,短按以及连续按键的功能. 1. 键值映射数据结构表   不管是直接采用MCU的引脚做矩阵键盘 ...

  3. linux键盘驱动程序分析,基于Linux按键驱动分析与编程

    硬件平台:Mini2440 Size of NAND:256M linux kernel:linux-2.6.32.2 一.首先编写按键驱动要用到的Mini2440的硬件是中断控制器和定时器 那么li ...

  4. linux 按键驱动中断 rockchip_7.自己写中断方式按键驱动程序(详解)

    request_irq()和free_irq()分析完毕后,接下来开始编写上升沿中断的按键驱动 如下图,需要设置4个按键的EINT0, EINT2, EINT11, EINT19的模式为双边沿,且设置 ...

  5. 20150217 IMX257实现GPIO-IRQ中断按键驱动程序

    IMX257实现GPIO-IRQ中断按键驱动程序 2015-02-17 李海沿 昨天我们已经实现了中断查询的方式实现GPIO按键驱动程序,但是,有一个缺点就是,当我们把应用程序放在后台执行时,即便没有 ...

  6. STM32-I2C总线驱动程序分析

    文章目录 I2C硬件电路原理图 I2C 简介 添加相应的文件并添加进工程文件 I2C驱动程序结构 I2C驱动程序分析 LM75A温度传感器电路原理图 LM75A温度传感器驱动程序分析 杨桃32学习笔记 ...

  7. 第12课第3节 字符设备驱动程序之查询方式的按键驱动程序

    第12课第3节 字符设备驱动程序之查询方式的按键驱动程序 cat /proc/devices //查询主设备号 insmod ./second_drv.ko ls /dev/button -l pos ...

  8. android 电容屏(三):驱动调试之驱动程序分析篇

    平台信息: 内核:linux3.4.39 系统:android4.4  平台:S5P4418(cortex a9) 作者:瘋耔(欢迎转载,请注明作者) 欢迎指正错误,共同学习.共同进步!! 关注博主新 ...

  9. TI BLE协议栈 按键流程分析

    之前在蓝牙技术群看到好多网友不知道按键流程到底是什么情况,平时也没时间,在群里也一两句说不明白,也就说了下可以去看下zigbee按键流程过程,其实都是相通的,现在特意发帖分享下,希望能起到一个抛砖引玉 ...

最新文章

  1. MySql的upsert操作
  2. haproxy代理hive
  3. 奉献给你:《Visual C# 2005程序开发与界面设计秘诀》
  4. 学习笔记10-C语言-小项目-五子棋
  5. ***error*** (zip#Browse) unzip not available on your system
  6. 【Java】JDBC连接MySQL驱动
  7. (九)在Kubernetes上运行人脸识别
  8. 更高效地利用 Jupyter+pandas 进行数据分析,6 种常用数据格式效率对比!
  9. java http请求_零基础学Java,掌握Java基础难不难?
  10. 通过eclipse对apk加密混淆的方法
  11. 国内最火的10款Java开源项目,都是国人开发,CMS居多
  12. 傅里叶级数的通俗理解
  13. CDH安装过程中出现:主机 cdh04 上的内存被调拨过度。总内存分配额是 4.0 艾字节 个字节,但是 RAM 只有 62.8 吉字节 个字节(其中的 12.6 吉字节 个字节是保留给系统使用的)。
  14. sed 批量替换字符串
  15. 机器周期、指令周期、时钟周期、总线周期
  16. 如何提高商城的转化率
  17. (六)Activiti之实现学生请假流程
  18. 叠加等边三角形的绘制 python_python 叠加等边三角形的绘制
  19. eclipse不提示错误问题
  20. Unity三体运行模拟体验

热门文章

  1. 【STL】unordered_set和unordered_map
  2. 万能地图下载器下载谷歌卫星地图在CAD中套合
  3. 锐龙R7 PRO 5875U性能怎么样?相当于什么水平级别
  4. 蚂蚁金服2019届【技术类】校园招聘
  5. anaconda navigator启动时一直卡在 loading applications 页面(已解决)
  6. mysql 创建唯一约束表
  7. 基于SSM架构的新闻管理系统设计与实现论文
  8. Word转PDF功能实现,文档转换工具通过PHP开发
  9. gps模拟无需root,带有gps 定位的模拟器
  10. python安装包下载太慢