利用設備樹和平台總線驅動led(GPIO)


測試平台是訊為的itop-4412開發板

驅動led步驟

步驟:

  • 修改設備樹,添加led相關的節點,編譯后燒錄進板卡
  • 編寫driver驅動代碼,初始化platform_driver結構體,使of_match_table屬性的compatible與設備樹中的一致
  • 在驅動入口函數中,向平台注冊driver
  • 匹配成功
  • 在probe函數里獲取gpio編號(從設備樹獲取)
  • 向內核申請gpio
  • 設置gpio方向
  • 注冊雜項設備
  • 編寫文件操作集里的ioctl函數,控制gpio
  • 編寫應用程序,打開對應雜項設備,使用ioctl控制led燈

常用的gpio函數

獲取gpio編號

<linux/of_gpio.h>

int of_get_named_gpio(struct device_node *np,
                                   const char *propname, int index);

參數:節點,屬性名,標號

返回:得到GPIO編號;負值表示失敗

向內核申請GPIO

申請gpio,申請后其他進程就不能再申請這個gpio,相當於被占用

int gpio_request(unsigned gpio, const char *label);

參數:gpio編號,給這個gpio起的名字

返回值:0成功,其他值失敗

操作GPIO

設置gpio方向

int gpio_direction_input(unsigned gpio);  //設置輸入,參數是gpio編號,返回0成功
int gpio_direction_output(unsigned gpio, int value);

讀寫gpio的值

int gpio_get_value(unsigned gpio);
void gpio_set_value(unsigned gpio, int value); 

將gpio導出到用戶空間

int gpio_export(unsigned gpio, bool direction_may_change);
//gpio編號,用戶是否可以改變gpio的方向

導出后用戶層/sys/class/gpio/目錄下會有gpio+編號的目錄,和在用戶層export申請gpio效果類似

對應的取消導出為gpio_unexport函數

釋放gpio

gpio_free(unsigned gpio);

1. 修改設備樹

在設備樹中添加:

taxue_leds:taxue_leds {
		compatible = "taxue_leds";
		gpios1 = <&gpl2 0 GPIO_ACTIVE_HIGH>;
		gpios2 = <&gpk1 1 GPIO_ACTIVE_HIGH>;
		status = "disabled";
	};


&taxue_leds {
      status = "okay";
};

2. 賦值of_match_table

定義並初始化platform_driver結構體

#define DEVICE_NAME "taxue_leds"  //和設備樹節點中的compatible一致
//平台device和設備樹device可以通過match_table匹配
static const struct of_device_id of_leds_match[] = {
    {.compatible = DEVICE_NAME},
    {},
};

static struct platform_driver pdrv = {
    .probe = drProbe,
    .remove = drRemove,
    .driver = {
        .name = "DEVICE_NAME",    //如果使用設備樹中的設備,則不根據name匹配
        .owner = THIS_MODULE,
        .of_match_table = of_leds_match,  //使用match_table進行匹配
    },
};

3. 注冊平台driver

//這段寫在驅動入口處	
	int ret=0;
    ret = platform_driver_register(&pdrv);
    if(ret < 0){
        printk("platform driver regist failed\n");
        return -1;
    }

4. 匹配

由平台總線匹配

5. 在probe函數里獲取gpio編號

int ledgpios[2];

{
    led_node = of_find_node_by_path("/taxue_leds");  //從設備樹路徑獲取節點
    if(led_node == NULL){
        printk("find node failed\n");
        return -1;
    }

    ledgpios[0] = of_get_named_gpio( led_node, "gpios1", 0);  //獲取gpio編號,存在ledgpios[0]里
}

6. 向內核申請gpio

在probe函數里

	ret = gpio_request(ledgpios[0], "led0");   //向內核申請gpio
    if(ret != 0){
        printk("request led gpio 0 failed\n");
        return -1;
    }

7. 設置gpio方向

在probe函數里

gpio_direction_output(ledgpios[0],0);  //設置為輸出,默認低電平

8. 注冊雜項設備

在probe里注冊

  • 初始化file_operations文件操作集
  • 初始化miscdevice(主要包含:設備節點名,文件操作集)
  • 調用misc_register函數注冊雜項設備

9. 編寫文件操作集里的ioctl函數

#define CMD_TEST_2 _IOW('A', 2, int)
//參數:句柄、接口命令、傳遞的數據
//返回值:
long misc_ioctl(struct file *fd, unsigned int cmd, unsigned long b){
    switch(cmd){
        case CMD_TEST_2:
            printk("CMD_TEST_2 date=%ld\n",b);
            if(b >0){
                gpio_set_value(ledgpios[0], 1);  //如果傳入的值大於0就將gpio置高,led亮
            }else{
                gpio_set_value(ledgpios[0], 0);  //如果傳入的值等於0就將gpio置低,led滅
            }
            break;
    }
    return 0;
}

10. 編寫應用程序

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include <unistd.h>
#include<sys/ioctl.h>

#define CMD_TEST_2 _IOW('A', 2, int)

int main(int argc, char *argv[]){
    int fd=0;
    int i=0;

    fd = open("/dev/hello_led", O_RDWR); //設備名具體需要根據驅動注冊設備節點時的名字
    if(fd < 0){
        printf("open failed\n");
        exit(1);
    }
    printf("open success\n");

    //讓led間隔一秒閃爍,循環10次
    for(i=0;i<10;i++){
        ioctl( fd, CMD_TEST_2, 0);
        sleep(1);
        ioctl( fd, CMD_TEST_2, 1);
        sleep(1);
    }

    close(fd);
    return 0;
}

驅動完整代碼

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>

#include <linux/miscdevice.h>

#include <linux/of.h>
#include <linux/of_gpio.h>

#define CMD_TEST_0 _IO('A', 0)
#define CMD_TEST_2 _IOW('A', 2, int)

#define DEVICE_NAME "taxue_leds"
struct device_node *led_node=NULL;
int ledgpios[2];

int misc_open(struct inode *a,struct file *b){
    printk("misc open \n");
    return 0;
}

int misc_release (struct inode * a, struct file * b){
    printk("misc file release\n");
    return 0;
}

//參數:句柄、接口命令、傳遞的數據
//返回值:
long misc_ioctl(struct file *fd, unsigned int cmd, unsigned long b){
    printk("cmd type=%c\t nr=%d\t dir=%d\t size=%d\n", _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_DIR(cmd), _IOC_SIZE(cmd));

    switch(cmd){
        case CMD_TEST_0:
            printk("CMD_TEST_0\n");
            gpio_set_value(ledgpios[0], 0);
            break;
        case CMD_TEST_2:
            printk("CMD_TEST_2 date=%ld\n",b);
            if(b >0){
                gpio_set_value(ledgpios[0], 1);
            }else{
                gpio_set_value(ledgpios[0], 0);
            }
            break;
    }
    return 0;
}

//文件操作集
static struct file_operations misc_fops = {
    .owner = THIS_MODULE,
    .open  = misc_open,
    .release = misc_release,
    .unlocked_ioctl = misc_ioctl
};

static struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,    //自動分配從設備號
    .name  = "hello_led",          //設備節點名
    .fops  = &misc_fops             //文件操作集
};

int drProbe(struct platform_device *dev){
    int ret;
    printk("probe here\n");

    led_node = of_find_node_by_path("/taxue_leds");  //從設備樹路徑獲取節點
    if(led_node == NULL){
        printk("find node failed\n");
        return -1;
    }

    ledgpios[0] = of_get_named_gpio( led_node, "gpios1", 0);  //獲取gpio編號

    ret = gpio_request(ledgpios[0], "led0");   //向內核申請gpio
    if(ret != 0){
        printk("request led gpio 0 failed\n");
        return -1;
    }

    gpio_direction_output(ledgpios[0],0);  //設置為輸出,默認低電平

    ret = misc_register(&misc_dev);   //注冊雜項設備
    if(ret < 0){
        printk("misc regist failed\n");
        return -1;
    }

    printk("misc regist succeed\n");
    return 0;
}

int drRemove(struct platform_device *dev){
    gpio_free(ledgpios[0]);
    misc_deregister(&misc_dev);
    printk("driver remove\n");
    return 0;
}

//平台device和設備樹device可以通過match_table匹配
static const struct of_device_id of_leds_match[] = {
    {.compatible = DEVICE_NAME},
    {},
};

static struct platform_driver pdrv = {
    .probe = drProbe,
    .remove = drRemove,
    .driver = {
        .name = "DEVICE_NAME",    //如果使用設備樹中的設備,則不根據name匹配
        .owner = THIS_MODULE,
        .of_match_table = of_leds_match,  //使用match_table進行匹配
    },
};

static int driver_init_led(void){
    int ret=0;
    ret = platform_driver_register(&pdrv); //向平台注冊driver
    if(ret < 0){
        printk("platform driver regist failed\n");
        return -1;
    }
    return 0;
}

static void driver_exit_led(void){
   platform_driver_unregister(&pdrv);
   printk("platform driver exit!\n");
}

module_init(driver_init_led);  //模塊入口,加載驅動時執行參數內的函數
module_exit(driver_exit_led);  //模塊出口,卸載模塊時執行參數內的函數

MODULE_LICENSE("Dual BSD/GPL"); //遵循BSD和GPL開源許可
MODULE_AUTHOR("TAXUE");  //模塊作者


免責聲明!

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



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