您尚未登录。

楼主 #1 2018-11-30 16:04:12

605364021
会员
注册时间: 2018-10-23
已发帖子: 251
积分: 251

请问有人可以教一下怎么写lradc驱动吗?

V3S开发板,我想写个简单lradc驱动实现按键检测,大致功能是可以检测哪个按键按下,请问有人写过这样的驱动或者可以给下思路吗?

离线

楼主 #3 2018-11-30 16:49:10

605364021
会员
注册时间: 2018-10-23
已发帖子: 251
积分: 251

Re: 请问有人可以教一下怎么写lradc驱动吗?

晕哥 说:

V3s 的 lradc 驱动已经写好,用户层只要访问 /dev/input/eventX 就可以了, 自己编译一个 evtest.c 就可以测试按键输入了。

我知道有自带的sun4i-lradc-keys驱动,但是我刚刚开始学写驱动,希望自己写个简单的lradc驱动来实现相关功能,但我没搞懂怎么如何去检测是哪个按键按下...网上大部分都是1个gpio对应1个按键,但是V3S用的是ADC按键

最近编辑记录 605364021 (2018-11-30 16:52:16)

离线

楼主 #5 2018-11-30 17:22:26

605364021
会员
注册时间: 2018-10-23
已发帖子: 251
积分: 251

Re: 请问有人可以教一下怎么写lradc驱动吗?

didi 说:
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内核模块来完成检测功能,

离线

楼主 #7 2018-11-30 17:32:32

605364021
会员
注册时间: 2018-10-23
已发帖子: 251
积分: 251

Re: 请问有人可以教一下怎么写lradc驱动吗?

但是我对检测这个功能怎么来实现没有想明白,可以说下实现的思路吗?

离线

楼主 #9 2018-11-30 17:42:31

605364021
会员
注册时间: 2018-10-23
已发帖子: 251
积分: 251

Re: 请问有人可以教一下怎么写lradc驱动吗?

v3s 说:
605364021 说:

但是我对检测这个功能怎么来实现没有想明白,可以说下实现的思路吗?

找个杂项设备的最小实现例程,然后弄个轮询读adc值,接着用接口把数据传到应用层

杂项设备我会实现,但是就是这个读ADC值这部分,不知道怎么实现读取ADC值,但是lradc里面有LRADC_CTRL,LRADC_INTC,LRADC_INTS,LRADC_DATA04个部分,搞得有点乱。

离线

楼主 #11 2018-11-30 20:53:24

605364021
会员
注册时间: 2018-10-23
已发帖子: 251
积分: 251

Re: 请问有人可以教一下怎么写lradc驱动吗?

v3s 说:

LRADC_CTRL,LRADC_INTC,LRADC_INTS,LRADC_DATA04个部分 看名字依次应该是控制寄存器,中断设置寄存器,中断状态寄存器,中断数据寄存器,如果你用轮询,可以不要管中间两个寄存器。
第一个寄存器是开启或者关闭adc的,最后一个寄存器读数据。

_20181130204842.png
_20181130204900.png
图1是4个寄存器的物理地址,第二图是LRADC_CTRL的0位功能,我将虚拟地址映射到0x01C22800并将CTRL第0位置0,这样开启adc正确?

离线

楼主 #13 2018-11-30 21:09:47

605364021
会员
注册时间: 2018-10-23
已发帖子: 251
积分: 251

Re: 请问有人可以教一下怎么写lradc驱动吗?

cityf 说:

看下回读这几个寄存器,是不是默认配置,如果是,说明映射正确,写也会正常。

上图的offset是偏移地址?我理解是这几个寄存器的起始地址是0x01C22800+offset地址,这样理解正确?

离线

楼主 #15 2018-11-30 22:10:14

605364021
会员
注册时间: 2018-10-23
已发帖子: 251
积分: 251

Re: 请问有人可以教一下怎么写lradc驱动吗?

_20181130220543.png
_20181130220543.png
这2个图是LRADC_CTRL寄存器的功能,我开启这个寄存器是需要全部功能都要选择?还是只需要设置第0位?

离线

楼主 #16 2018-11-30 22:20:09

605364021
会员
注册时间: 2018-10-23
已发帖子: 251
积分: 251

Re: 请问有人可以教一下怎么写lradc驱动吗?

_20181130220548.png

离线

楼主 #18 2018-12-02 21:56:03

605364021
会员
注册时间: 2018-10-23
已发帖子: 251
积分: 251

Re: 请问有人可以教一下怎么写lradc驱动吗?

_20181130204842.png
_20181202215304.png
图一是芯片手册的物理地址,图二是我写驱动的虚拟映射地址,通过虚拟映射来控制LRADC_CTRL和LRADC_DATA0 能看下这样映射会有问题?

离线

楼主 #20 2018-12-02 22:02:48

605364021
会员
注册时间: 2018-10-23
已发帖子: 251
积分: 251

Re: 请问有人可以教一下怎么写lradc驱动吗?

#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

离线

楼主 #22 2018-12-02 23:13:29

605364021
会员
注册时间: 2018-10-23
已发帖子: 251
积分: 251

Re: 请问有人可以教一下怎么写lradc驱动吗?

晕哥 说:

[   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);//使能时钟

}

我看网上也是这样写的.....

离线

楼主 #24 2018-12-03 11:39:14

605364021
会员
注册时间: 2018-10-23
已发帖子: 251
积分: 251

Re: 请问有人可以教一下怎么写lradc驱动吗?

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;
}

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn