V3S开发板,我想写个简单lradc驱动实现按键检测,大致功能是可以检测哪个按键按下,请问有人写过这样的驱动或者可以给下思路吗?
离线
V3s 的 lradc 驱动已经写好,用户层只要访问 /dev/input/eventX 就可以了, 自己编译一个 evtest.c 就可以测试按键输入了。
我知道有自带的sun4i-lradc-keys驱动,但是我刚刚开始学写驱动,希望自己写个简单的lradc驱动来实现相关功能,但我没搞懂怎么如何去检测是哪个按键按下...网上大部分都是1个gpio对应1个按键,但是V3S用的是ADC按键
最近编辑记录 605364021 (2018-11-30 16:52:16)
离线
605364021 说:晕哥 说:V3s 的 lradc 驱动已经写好,用户层只要访问 /dev/input/eventX 就可以了, 自己编译一个 evtest.c 就可以测试按键输入了。
我知道有自带的sun4i-lradc-keys驱动,但是我刚刚开始学写驱动,希望自己写个简单的lradc驱动来实现相关功能,但我没搞懂怎么如何去检测是哪个按键按下...网上大部分都是1个gpio对应1个按键,但是V3S用的是ADC按键
https://github.com/Lichee-Pi/linux/blob/zero-4.13.y/drivers/input/keyboard/sun4i-lradc-keys.c
注册成为输入设备,按后利用adc中断输出自定义的key值给用户层。
你也可以写一个最简单的驱动程序,利用这个adc,从轮询开始,先别用中断。
sun4i-lradc-keys是用平台驱动设备模型为架构来编写的,但是这个驱动我想用杂项设备来做可以?做成一个.ko内核模块来完成检测功能,
离线
但是我对检测这个功能怎么来实现没有想明白,可以说下实现的思路吗?
离线
605364021 说:但是我对检测这个功能怎么来实现没有想明白,可以说下实现的思路吗?
找个杂项设备的最小实现例程,然后弄个轮询读adc值,接着用接口把数据传到应用层
杂项设备我会实现,但是就是这个读ADC值这部分,不知道怎么实现读取ADC值,但是lradc里面有LRADC_CTRL,LRADC_INTC,LRADC_INTS,LRADC_DATA04个部分,搞得有点乱。
离线
LRADC_CTRL,LRADC_INTC,LRADC_INTS,LRADC_DATA04个部分 看名字依次应该是控制寄存器,中断设置寄存器,中断状态寄存器,中断数据寄存器,如果你用轮询,可以不要管中间两个寄存器。
第一个寄存器是开启或者关闭adc的,最后一个寄存器读数据。
图1是4个寄存器的物理地址,第二图是LRADC_CTRL的0位功能,我将虚拟地址映射到0x01C22800并将CTRL第0位置0,这样开启adc正确?
离线
看下回读这几个寄存器,是不是默认配置,如果是,说明映射正确,写也会正常。
上图的offset是偏移地址?我理解是这几个寄存器的起始地址是0x01C22800+offset地址,这样理解正确?
离线
这2个图是LRADC_CTRL寄存器的功能,我开启这个寄存器是需要全部功能都要选择?还是只需要设置第0位?
离线
离线
图一是芯片手册的物理地址,图二是我写驱动的虚拟映射地址,通过虚拟映射来控制LRADC_CTRL和LRADC_DATA0 能看下这样映射会有问题?
离线
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h> /*创建设备节点*/
#include <linux/clk.h>
#include <linux/wait.h> /*定义DECLARE_WAIT_QUEUE_HEAD*/
#include <linux/irqreturn.h> /*定义了irqreturn_t等*/
#include <linux/interrupt.h> /*request_irq disable_irq enable_irq*/
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/irq.h> /*其中包含了#include "mach/irqs.h" */
#define ADC_MAJOR 102
#define ADC_NAME "my_adc"
#define SUCCESS 0
/* LRADC_CTRL bits */
#define FIRST_CONVERT_DLY(x) ((x) << 24) /* 8 bits 31:24*/
#define CHAN_SELECT(x) ((x) << 22) /* 2 bits 23:22 */
#define CONTINUE_TIME_SEL(x) ((x) << 16) /* 4 bits 19:16*/
#define KEY_MODE_SEL(x) ((x) << 12) /* 2 bits 14:12*/
#define LEVELA_B_CNT(x) ((x) << 8) /* 4 bits 11:8*/
#define HOLD_EN(x) ((x) << 6)
#define LEVELB_VOL(x) ((x) << 4) /* 2 bits 5:4*/
#define SAMPLE_RATE(x) ((x) << 2) /* 2 bits 3:2*/
#define ENABLE(x) ((x) << 0)
#define IRQ_ADC 62
static int adc_open(struct inode *,struct file *);
static int adc_release(struct inode *,struct file *);
static int __init adc_init(void);
static int __exit adc_exit(void);
static ssize_t adc_read(struct file *,char *,size_t,loff_t *);
volatile unsigned long adc_con;
unsigned long adc_dat0;
int flag;//等待任务完成标志
unsigned long buf;//存放转换完成的数据
//声明等待队列
DECLARE_WAIT_QUEUE_HEAD(adc_wait);
struct clk *adc_clk;
static irqreturn_t adc_interrupt(int irq,void * dev_id)//中断处理程序
{
if(flag==0)
{
buf=(readl(adc_dat0) & 0x3f );//读取转换完成的数据,readw(unsigned int addr)读取16位数据(2字节)
flag=1;
wake_up_interruptible(&adc_wait);//唤醒等待其上的进程
printk("Read value is %ld/n",buf);
}
return IRQ_HANDLED;
}
struct file_operations adc_ops =
{
.owner = THIS_MODULE,
.read = adc_read,
.open = adc_open,
.release = adc_release,
};
static int __init adc_init(void)
{
int ret;
adc_clk = clk_get(NULL,"adc");//获取时钟
clk_enable(adc_clk);//使能时钟
ret=register_chrdev(ADC_MAJOR,ADC_NAME,&adc_ops); //注册设备
if(ret<0)
{
printk("register device fail/n");
return ret;
}
adc_con=(unsigned long)ioremap(0x01C22800,4);
adc_dat0=(volatile unsigned long)ioremap(0x01C2280C,4);
if( !(adc_con & adc_dat0) )
{
printk("Failed to ioremap/n");
goto handle;
}
printk("Initialized.../n");
return SUCCESS;
handle:
unregister_chrdev(ADC_MAJOR,ADC_NAME);
return -1;
}
static int adc_open(struct inode * inode,struct file * file) //打开设备函数
{
//注册中断
int ret;
//disable_irq(IRQ_ADC);
//enable_irq(IRQ_ADC);
ret=request_irq(IRQ_ADC,adc_interrupt,IRQF_SHARED,ADC_NAME,1);//注册中断 IRQ_ADC在 mach/irqs.h中定义
if(ret<0)
{
printk("IRQ %d can not request/n",IRQ_ADC);
return ret;
}
return SUCCESS;
}
static int adc_release(struct inode * inode,struct file * file) //关闭设备函数
{
free_irq(IRQ_ADC,1);//释放中断
return SUCCESS;
}
static ssize_t adc_read(struct file *file,
char * buffer,
size_t length,
loff_t * offset)//设备读取函数
{
writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |
SAMPLE_RATE(0) | ENABLE(1), adc_con); //设置ADCCON writew (unsigned char data , unsigned short addr )写16位数据 2字节
/*writew((readw(adc_con) | 0x1),adc_con); //启动AD转换*/
wait_event_interruptible(adc_wait,flag);
flag=0;
}
static int __exit adc_exit(void) //驱动卸载函数
{
iounmap(adc_con);
iounmap(adc_dat0);
unregister_chrdev(ADC_MAJOR,ADC_NAME);
clk_disable(adc_clk);
clk_put(adc_clk);
printk("The adc is unintialized/n");
}
module_init(adc_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");
这是我写的lradc驱动,可以生产.ko模块,但是在zero上加载时会提示
[ 86.001381] lradc: loading out-of-tree module taints kernel.
[ 86.007740] Unable to handle kernel paging request at virtual address fffffffe
[ 86.014967] pgd = c3bd4000
[ 86.017784] [fffffffe] *pgd=43e88861, *pte=00000000, *ppte=00000000
[ 86.024062] Internal error: Oops: 37 [#1] SMP ARM
[ 86.028758] Modules linked in: lradc(O+)
[ 86.032688] CPU: 0 PID: 98 Comm: insmod Tainted: G O 4.13.16-licheepi-zero+ #9
[ 86.041025] Hardware name: Allwinner sun8i Family
[ 86.045722] task: c3966e00 task.stack: c3bf2000
[ 86.050259] PC is at clk_enable+0x8/0x10
[ 86.054187] LR is at adc_init+0x28/0x1000 [lradc]
[ 86.058884] pc : [<c038b988>] lr : [<bf005028>] psr: a00e0013
[ 86.065140] sp : c3bf3de8 ip : 00000000 fp : bf0020c0
[ 86.070357] r10: 00000024 r9 : 3c414d9c r8 : 00000001
[ 86.075574] r7 : c3beb280 r6 : 00000000 r5 : bf005000 r4 : bf0022c0
[ 86.082090] r3 : fffffffe r2 : 00000000 r1 : 00000000 r0 : fffffffe
[ 86.088609] Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none
[ 86.095733] Control: 10c5387d Table: 43bd406a DAC: 00000051
[ 86.101469] Process insmod (pid: 98, stack limit = 0xc3bf2210)
[ 86.107294] Stack: (0xc3bf3de8 to 0xc3bf4000)
[ 86.111649] 3de0: c389f740 c0701a38 c311e000 ffffe000 bf005000 c010179c
[ 86.119817] 3e00: c07d8d20 bf002108 00000000 c0a35a10 c0a35a24 c431afff ffe00000 fffff000
[ 86.127984] 3e20: c31032d0 c3beba40 c3e7a920 00000000 00000000 00003bc9 00040000 3c414d9c
[ 86.136152] 3e40: bf0020c0 00000001 bf0020c0 00000001 c3bebac0 00000001 3c414d9c c018d748
[ 86.144320] 3e60: 00000001 c3beb2a4 c3bf3f50 00000001 c3beb2a4 c018ca5c bf0020cc 00007fff
[ 86.152488] 3e80: bf0020c0 c0189e08 c34a5d38 bf0020c0 bf002108 c070301c bf0021f0 c0703000
[ 86.160655] 3ea0: c082d7d0 c07dd8f0 c07dd954 c07dd8fc bf00227c 00000000 00001728 c01fac00
[ 86.168822] 3ec0: 00001728 00000000 c01facfc 00000000 00000000 00000000 00000000 00000000
[ 86.176989] 3ee0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 86.185156] 3f00: 00000000 00000000 7fffffff 00000000 00000003 000c5008 0000017b c0107684
[ 86.193324] 3f20: c3bf2000 00000000 00000000 c018d170 7fffffff 00000000 00000003 c3bad018
[ 86.201491] 3f40: c311e000 c4318000 00001728 00000000 c4318356 c4318000 00001728 c4319278
[ 86.209659] 3f60: c431913c c4318c88 00003000 00003090 00000000 00000000 00000000 0000174c
[ 86.217826] 3f80: 0000001c 0000001d 00000015 00000000 00000011 00000000 000c5008 be94fd1f
[ 86.225994] 3fa0: be94fbe4 c01074c0 000c5008 be94fd1f 00000003 000c5008 00000000 be94fd1f
[ 86.234162] 3fc0: 000c5008 be94fd1f be94fbe4 0000017b be94fd1f 00000000 b6f6c000 00000000
[ 86.242329] 3fe0: be94fa40 be94fa30 00025f10 b6ee8a42 800e0030 00000003 00000000 00000000
[ 86.250519] [<c038b988>] (clk_enable) from [<bf005028>] (adc_init+0x28/0x1000 [lradc])
[ 86.258444] [<bf005028>] (adc_init [lradc]) from [<c010179c>] (do_one_initcall+0x44/0x16c)
[ 86.266709] [<c010179c>] (do_one_initcall) from [<c018d748>] (do_init_module+0x60/0x1f0)
[ 86.274797] [<c018d748>] (do_init_module) from [<c018ca5c>] (load_module+0x1d80/0x2280)
[ 86.282794] [<c018ca5c>] (load_module) from [<c018d170>] (SyS_finit_module+0xa8/0xb8)
[ 86.290620] [<c018d170>] (SyS_finit_module) from [<c01074c0>] (ret_fast_syscall+0x0/0x3c)
[ 86.298791] Code: e1a00004 e8bd8070 e2503000 012fff1e (e5930000)
[ 86.304969] ---[ end trace 0df6ff5a82d15799 ]---
Segmentation fault
离线
[ 86.050259] PC is at clk_enable+0x8/0x10
[ 86.054187] LR is at adc_init+0x28/0x1000 [lradc]从这里看出,挂在 adc_init 这个函数里面, 具体是 clk_enable 这行, 检查一下为什么使能时钟出错?
全局变量 struct clk *adc_clk;
static int __init adc_init(void)
{
adc_clk = clk_get(NULL,"adc");//获取时钟
clk_enable(adc_clk);//使能时钟
}
我看网上也是这样写的.....
离线
lradc.ko模块可以加载,但是在打开设备时提示申请中断失败
# cat /proc/interrupts
CPU0
19: 7017 GIC-0 27 Level arch_timer
21: 0 GIC-0 50 Level /soc/timer@01c20c00
22: 0 GIC-0 82 Level 1c02000.dma-controller
23: 18046 GIC-0 92 Level sunxi-mmc
24: 12219 GIC-0 93 Level sunxi-mmc
25: 1 GIC-0 103 Level musb-hdrc.1.auto
26: 0 GIC-0 104 Level ehci_hcd:usb1
27: 0 GIC-0 105 Level ohci_hcd:usb2
28: 0 GIC-0 72 Level 1c20400.rtc
35: 800 GIC-0 32 Level ttyS0
36: 0 GIC-0 38 Level mv64xxx_i2c
IPI0: 0 CPU wakeup interrupts
IPI1: 0 Timer broadcast interrupts
IPI2: 0 Rescheduling interrupts
IPI3: 0 Function call interrupts
IPI4: 0 CPU stop interrupts
IPI5: 0 IRQ work interrupts
IPI6: 0 completion interrupts
Err: 0
# exec 6</dev/lradc
[ 293.796488] genirq: Failed to request resources for my_adc (irq 62) on irqchip sunxi_pio_edge
-sh: can't open /dev/lradc: Invalid argument
我在驱动程序open函数中使用request_irq进行申请中断,其中62我是在v3s芯片手册中查到的lradc中断号,知道会是什么原因导致注册中断失败?
static int adc_open(struct inode * inode,struct file * file) //打开设备函数
{
//注册中断
int ret;
ret=request_irq(62,adc_interrupt,IRQF_SHARED,ADC_NAME,1);//注册中断 IRQ_ADC在 mach/irqs.h中定义
if(ret<0)
{
printk("IRQ %d can not request/n",IRQ_ADC);
return ret;
}
return 0;
}
离线