驱动开发之ADC驱动与通用设备树

驱动开发之ADC驱动与通用设备树:

通用设备树:

让驱动去操作设备树,可以选择platform架构,也可以不选择platform架构。

vi -t /arch/arm/boot/dts/exynos4412-fs4412中:

566 ofled{
567 led2 = <&gpx2 7 0>;
568 led1 = <&gpx1 0 0>;
569 led3-4 = <&gpf3 4 0>,<&gpf3 5 0>;
570 };

 

接口:

1 struct device_node *of_find_node_by_path(const char *path)
2 功能:查找设备树节点信息(必须包含路径)
3 返回值:如果在设备树中找到了指定节点,返回值就指向了找到的节点。
1 static inline int of_get_named_gpio(struct device_node *np,const char *propname, int index)
2 参数1:of_find_node_by_path的返回值
3 参数2:属性名称
4 参数3:属性内容的索引值
5 返回值:引脚编号(系统动态分配的一个整数值,让这个整数值和实际的物理引脚建立关系)

----->此时内核还不认识这些引脚编号么
-------------->

1 int gpio_request(unsigned gpio, const char *label)
2 功能:注册引脚编号
3 参数1:引脚编号
4 参数2:引脚的别名
1 static inline int gpio_direction_output(unsigned gpio, int value)
2 功能:设置引脚为输出模式
3 参数1:引脚编号
4 参数2:指定的是数据寄存器中的默认值
1 static inline void gpio_set_value(unsigned gpio, int value)
2 功能:对指定引脚中的数据寄存器设置值
3 参数1:引脚编号
4 参数2:数据值
1 static inline int gpio_to_irq(unsigned int gpio)
2 功能:获取虚拟中断号
3 参数:gpio引脚编号

参考代码LED:

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/fs.h>
 4 #include <linux/device.h>
 5 #include <asm/gpio.h>
 6 #include <linux/of.h>
 7 #include <linux/of_gpio.h>
 8 
 9 
10 int major;
11 struct class *cls;
12 struct device *devs;
13 struct device_node *np;
14 
15 int gpx2_7;
16 int gpx1_0;
17 int gpf3_4;
18 int gpf3_5;
19 
20 int ofled_open(struct inode *inode,struct file *filp)
21 {
22     gpio_set_value(gpx2_7,1);
23     gpio_set_value(gpx1_0,1);
24     gpio_set_value(gpf3_4,1);
25     gpio_set_value(gpf3_5,1);
26     return 0;
27 }
28 
29 int ofled_close(struct inode *inode,struct file *filp)
30 {
31     gpio_set_value(gpx2_7,0);
32     gpio_set_value(gpx1_0,0);
33     gpio_set_value(gpf3_4,0);
34     gpio_set_value(gpf3_5,0);
35     return 0;
36 }
37 
38 struct file_operations fops = {
39     .owner = THIS_MODULE,
40     .open = ofled_open,
41     .release = ofled_close,
42 };
43 
44 int led_init(void)
45 {
46     major = register_chrdev(0,"ofled",&fops);
47     cls = class_create(THIS_MODULE,"ofled");
48     devs = device_create(cls,NULL,MKDEV(major,0),NULL,"ofled");
49     
50     //查找节点名称
51     np = of_find_node_by_path("/ofled");
52     
53     //获取gpio引脚编号
54     gpx2_7 = of_get_named_gpio(np,"led2",0);
55     gpx1_0 = of_get_named_gpio(np,"led1",0);
56     gpf3_4 = of_get_named_gpio(np,"led3-4",0);
57     gpf3_5 = of_get_named_gpio(np,"led3-4",1);
58 
59     //注册引脚编号
60     gpio_request(gpx2_7,NULL);
61     gpio_request(gpx1_0,NULL);
62     gpio_request(gpf3_4,NULL);
63     gpio_request(gpf3_5,NULL);
64     
65     //设置输出模式
66     gpio_direction_output(gpx2_7,1);
67     gpio_direction_output(gpx1_0,1);
68     gpio_direction_output(gpf3_4,1);
69     gpio_direction_output(gpf3_5,1);
70     return 0;
71 }
72 module_init(led_init);
73 
74 void led_exit(void)
75 {
76     return;
77 }
78 module_exit(led_exit);
79 
80 MODULE_LICENSE("GPL");
ofled.c
 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 
 6 int main(int argc, const char *argv[])
 7 {
 8     int fd;
 9 
10     fd = open("/dev/ofled",O_RDWR);
11 
12     sleep(3);
13 
14     close(fd);
15     return 0;
16 }
app.c

参考代码KEY:

  1 #include <linux/init.h>
  2 #include <linux/module.h>
  3 #include <linux/fs.h>
  4 #include <linux/device.h>
  5 #include <linux/of.h>
  6 #include <linux/of_gpio.h>
  7 #include <asm/gpio.h>
  8 #include <linux/interrupt.h>
  9 #include <linux/irqreturn.h>
 10 #include <asm/uaccess.h>
 11 #include <linux/sched.h>
 12 
 13 struct device_node *np;
 14 
 15 int major;
 16 struct class *cls;
 17 struct device *devs;
 18 
 19 int gpx1_1;
 20 int gpx1_2;
 21 int gpx3_2;
 22 
 23 int irqno1;
 24 int irqno2;
 25 int irqno3;
 26 
 27 int key;
 28 
 29 wait_queue_head_t keyq;//创建等待队列头
 30 int flag = 0;
 31 
 32 irqreturn_t fs4412_ofkey_handler(int irqno,void *id) //中断处理函数
 33 {
 34     if(irqno == irqno1)//判断哪个按键被按下执行相应的操作
 35         key = 1;
 36     
 37     if(irqno == irqno2)
 38         key = 2;
 39 
 40     if(irqno == irqno3)
 41         key = 3;
 42 
 43     wake_up_interruptible(&keyq);//唤醒
 44     flag = 1;
 45     return IRQ_HANDLED;
 46 }
 47 
 48 int fs4412_ofkey_open(struct inode *inode,struct file *filp)
 49 {
 50     return 0;
 51 }
 52 
 53 ssize_t fs4412_ofkey_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off)//给应用层提供接口
 54 {
 55     int ret;
 56 
 57     wait_event_interruptible(keyq,flag != 0);//阻塞等待,中断处理函数处理完成唤醒阻塞
 58     ret = copy_to_user(ubuf,&key,sizeof(key));//将数据拷贝到应用层
 59 
 60     flag = 0;//方便后面操作阻塞
 61     return sizeof(key);
 62 }
 63 //文件操作结构体
 64 struct file_operations fops = {
 65     .owner = THIS_MODULE,
 66     .open = fs4412_ofkey_open,
 67     .read = fs4412_ofkey_read,
 68 };
 69 
 70 int fs4412_ofkey_init(void)
 71 {
 72     int ret;
 73     major = register_chrdev(0,"ofkey",&fops);//注册设备编号
 74     cls = class_create(THIS_MODULE,"ofkey");//创建设备类(名为ofkey的目录)
 75     devs = device_create(cls,NULL,MKDEV(major,0),NULL,"ofkey");//创建设备文件(链接文件)
 76 
 77     np = of_find_node_by_path("/fskey");//查找设备树节点信息
 78 
 79     gpx1_1 = of_get_named_gpio(np,"key1",0);//获取引脚编号,0表示索引值,设备树中led1 = <>(0),<>(1),<>(3)...
 80     gpx1_2 = of_get_named_gpio(np,"key2",0);
 81     gpx3_2 = of_get_named_gpio(np,"key3",0);
 82 
 83     gpio_request(gpx1_1,NULL);//注册引脚编号,取别名不取默认为NULL
 84     gpio_request(gpx1_2,NULL);
 85     gpio_request(gpx3_2,NULL);
 86 
 87     gpio_direction_input(gpx1_1);//设置引脚为输出模式
 88     gpio_direction_input(gpx1_2);
 89     gpio_direction_input(gpx3_2);
 90     
 91     irqno1 = gpio_to_irq(gpx1_1);//获取中断号
 92     irqno2 = gpio_to_irq(gpx1_2);
 93     irqno3 = gpio_to_irq(gpx3_2);
 94 
 95     ret = request_irq(irqno1,fs4412_ofkey_handler,IRQF_TRIGGER_FALLING,"key1",NULL);//注册中断
 96     ret = request_irq(irqno2,fs4412_ofkey_handler,IRQF_TRIGGER_FALLING,"key2",NULL);//IRQF_TRIGGER_FALLING中断触发方式为下降沿触发
 97     ret = request_irq(irqno3,fs4412_ofkey_handler,IRQF_TRIGGER_FALLING,"key3",NULL);
 98 
 99     init_waitqueue_head(&keyq);//初始化等待队列头
100     return 0;
101 }
102 module_init(fs4412_ofkey_init);//模块加载
103 
104 void fs4412_ofkey_exit(void)
105 {
106     return ;
107 }
108 module_exit(fs4412_ofkey_exit);//模块卸载
109 MODULE_LICENSE("GPL");//模块声明
ofkey.c
 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 
 6 int main(int argc, const char *argv[])
 7 {
 8     int fd;
 9 
10     fd = open("/dev/ofkey",O_RDWR);
11 
12     int key;
13     while(1)
14     {
15         read(fd,&key,sizeof(key));
16         printf("key = %d\n",key);
17     }
18     close(fd);
19     return 0;
20 }
app.c

 

ADC底层驱动:

 

添加设备树:

1、查看原理图
  XadcAIN3 不是中断引脚,只是指定了ad转换通道为3
2、查看芯片手册第十章
  INTG10 [3] ADC General ADC
  中断组合器的第十组,第三位控制了ADC中断
3、vi Documetation/devicetree/bindings/arm/samsung/interrupt-combiner.txt
4、需要使用寄存器
  ADCCON:
    [0] 代表ad转换的起始位,每次转换成功后都会被清零。每次转换数据成功后都会自动产生一次中断
    [13,6] 设置的预分频值
    [14] 使能预分频值
    [16] 设置转换精度
  ADCDAT:用来存放转换后的数据(驱动需要读取这里的内容)
  CLRINTADC:中断清除寄存器,被写入任意值。
  ADCMUX:设置AD转换通道为3

------>

1 fs4412-adc{
2 compatible = ",";
3 interrupt-parent = <&combiner>; vi exynos4.dtsi文件中找到combiner:
4 interrupts = <10 3>;
5 reg = <126c0000 0x20>;
6 };
1 获取指定(IORESOURCEB表示中断)资源类型:
2 platform_get_resource(pdev,IORESOURCE_IRQ,0);

参考代码ADC:

  1 #include <linux/module.h>
  2 #include <linux/fs.h>
  3 #include <linux/platform_device.h>
  4 #include <linux/device.h>
  5 #include <asm/io.h>
  6 #include <asm/uaccess.h>
  7 #include <linux/interrupt.h>
  8 #include <linux/sched.h>
  9 #include <linux/irqreturn.h>
 10 
 11 
 12 int major;
 13 struct class *cls;
 14 struct device *devs;
 15 
 16 struct resource *res_mem;
 17 struct resource *res_irq;
 18 
 19 void __iomem *adc_base;//必须是void __iomem类型因为偏移地址不一样
 20 wait_queue_head_t adcq;
 21 int flag = 0;
 22 
 23 irqreturn_t fs4412_adc_handler(int irqno,void *id)
 24 {
 25     //开始必须要清中断,内核没有进行清中断我们必须在驱动中手动清中断
 26     writel(0,adc_base + 0x18);
 27     wake_up_interruptible(&adcq);
 28     flag = 1;
 29     return IRQ_HANDLED;
 30 }
 31 
 32 struct of_device_id fs4412_adc_match_tbl[] = {
 33     {
 34         .compatible = "fs4412,adc",
 35     },
 36     {},
 37 };
 38 
 39 int fs4412_adc_open(struct inode *inode,struct file *filp)
 40 {
 41     return 0;
 42 }
 43 
 44 ssize_t fs4412_adc_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off)
 45 {
 46     int ret;
 47     int data;
 48     //开始转换数据
 49     writel(readl(adc_base) | 1 << 0,adc_base);//转换完成后才会出现中断
 50     
 51     //阻塞
 52     wait_event_interruptible(adcq,flag != 0);
 53 
 54     //只有转化完成且中断唤醒阻塞后才能读取数据,直接不能读取数据
 55     data = readl(adc_base + 0x0c) & 0xfff;
 56     ret = copy_to_user(ubuf,&data,sizeof(data));
 57     flag = 0;
 58     return sizeof(data);
 59 }
 60 
 61 struct file_operations fops = {
 62     .owner = THIS_MODULE,
 63     .open = fs4412_adc_open,
 64     .read = fs4412_adc_read,
 65 };
 66 
 67 int fs4412_adc_probe(struct platform_device *pdev)
 68 {
 69     int ret;
 70     printk("match ok\n");
 71 
 72     major = register_chrdev(0,"adc",&fops);
 73     cls = class_create(THIS_MODULE,"adc");
 74     devs = device_create(cls,NULL,MKDEV(major,0),NULL,"adc");
 75 
 76     res_irq = platform_get_resource(pdev,IORESOURCE_IRQ,0);//获取指定(中断)资源类型
 77     ret = request_irq(res_irq->start,fs4412_adc_handler,0,"adc",NULL);
 78 
 79 
 80     res_mem = platform_get_resource(pdev,IORESOURCE_MEM,0);//获取指定(内存)资源类型
 81     adc_base = ioremap(res_mem->start,0x20);//将物理地址映射成虚拟地址
 82 
 83     writel(255 << 6 | 1 << 14 | 1 << 16,adc_base);
 84     writel(3,adc_base + 0x1c);
 85 
 86     init_waitqueue_head(&adcq);
 87     return 0;
 88 }
 89 
 90 int fs4412_adc_remove(struct platform_device *pdev)
 91 {
 92     return 0;
 93 }
 94 
 95 struct platform_driver pdrv = {
 96     .driver = {
 97         .name = "adc",
 98         .of_match_table = fs4412_adc_match_tbl,
 99     },
100 
101     .probe = fs4412_adc_probe,
102     .remove = fs4412_adc_remove,
103 };
104 module_platform_driver(pdrv);
105 MODULE_LICENSE("GPL");
ADC.c
 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 
 6 int main(int argc, const char *argv[])
 7 {
 8     int fd;
 9 
10     fd = open("/dev/adc",O_RDWR);
11 
12     int data;
13     while(1)
14     {
15         read(fd,&data,sizeof(data));
16         printf("data = %d\n",data);
17         sleep(1);
18     }
19     close(fd);
20     return 0;
21 }
app.c

 

总结:

 

 1 驱动使用设备树时可以通过platform总线来和设备树匹配。
 2 也可以不使用platform总线,需要查询节点名称,进而获取节点中的属性。
 3 
 4 of_find_node_by_path();//查询设备树中指定节点。
 5 
 6 引脚编号 of_get_named_gpio();//从设备树的属性中获取寄存器信息并且产生gpio引脚编号
 7 
 8 gpio_request();//注册引脚编号
 9 
10 gpio_direction_output();//设置为输出模式
11 gpio_direction_input();//设置为输入模式
12 
13 gpio_set_value();//设置寄存器的值
14 gpio_get_value();//获取寄存器的值
15 
16 gpio_to_irq();//从设备树中获取虚拟中断号
17 
18 adc设备树添加:
19 fs4412-adc{
20     compatible = ",";
21     interrupt-parent = <&combiner>;
22     interrupts = <10 3>;
23     reg = <0x126c0000 0x20>;
24 };
25 
26 ADCCON:设置预分频值、使能预分频值、设置转换精度
27        [0]控制是否开始进行AD转换,当AD转换成功后会产生一次中断。
28 ADCDAT:存放转化后的数据
29 CLRINTADC:清除中断      
30 ADCMUX:设置转换通道 
总结

 

posted @ 2018-09-19 19:27  向往的生活ing  阅读(2275)  评论(1编辑  收藏  举报