驅動開發之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:設置轉換通道 
總結

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM