驅動開發之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");

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 }
參考代碼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");//模塊聲明

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

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 }
總結:

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:設置轉換通道