驅動實例 — GPIO驅動 485調試 設備樹修改


  應用場景:使用的是3399pro,控制GPIO1_B5(RS485)的高低電平。來控制uart0的收發。

  http://wiki.t-firefly.com/AIO-3399C/driver_gpio.html   有關於3399詳細GPIO使用說明

  http://www.wowotech.net/device_model/429.html  GPIO調試相關

1.調試485確認硬件沒問題

查看哪些引腳被占用:
cat  /sys/kernel/debug/gpio

  查看當前開發板哪些引腳被占用。可以看到uart0的引腳被藍牙占用。(因此到時候要在設備樹中將藍牙disabled了,沒用到)

cd /sys/class/gpio
echo 45 > export  將gpio 45暴露給用戶層
這樣在gpio目錄下就有一個gpio45的文件,就可以直接對gpio45進行操作。

echo out > gpio45/direction
echo 0 > gpio45/value    
echo 1 > gpio45/value 

  這樣直接echo 111 > /dev/ttyS0。可以在PC上的串口助手看到有輸出。

  但是我用cat /dev/ttyS0發現數據一直不能接收進來。

  后面必須要編寫串口程序配置串口才能接收。

 

2.修改設備樹

2.1 禁用藍牙,因為藍牙占用了uart0 

  

   將status = "okay";修改成disabled。

  以下設備樹配置有問題:

  

   

3.編寫GPIO驅動 

#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <asm/io.h>
 
#define GPIO_LOW 0
#define GPIO_HIGH 1
int gpio;
int major;
static struct class *cls;

static int rs485_ctrl_open(struct inode *inode, struct file *file)
{
    printk(KERN_EMERG "%s-%d: enter\n",__FUNCTION__,__LINE__);

    return 0;
}
 
static ssize_t rs485_ctrl_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    int val;
    int ret;
    printk(KERN_EMERG "%s-%d: enter\n",__FUNCTION__,__LINE__);
     
    ret = copy_from_user(&val, buf, count); //copy_to_user();
     
    if (val == 1)
    {
        gpio_set_value(gpio,GPIO_HIGH);
    }
    else
    {
        gpio_set_value(gpio,GPIO_LOW);
    }

    return 0;
}

static long rs485_ctrl_ioctl( struct file *files, unsigned int cmd, unsigned long arg){
    //printk("cmd is %d,arg is %d\n",cmd,arg);
    
    if(cmd > 1){
        printk(KERN_EMERG "rs485 control gpio cmd is 0 or 1\n");
    }
    if(arg > 1){
        printk(KERN_EMERG "rs485 control gpio arg is only 1\n");
    }
    
    gpio_set_value(gpio,cmd);
    
    return 0;
}

static struct file_operations rs485_ctrl_fops = {
    .owner =  THIS_MODULE, 
    .open  =  rs485_ctrl_open, 
    .write =  rs485_ctrl_write,
    .unlocked_ioctl = rs485_ctrl_ioctl,
};
 
static int rs485_ctrl_probe(struct platform_device *pdev)
{
    int ret ;
     
    enum of_gpio_flags flag;
    
    //設備節點結構體
    struct device_node *rs485_ctrl_node = pdev->dev.of_node;
     
    printk(KERN_EMERG "rs485 control gpio %s-%d: enter\n",__FUNCTION__,__LINE__);

    //of_get_named_gpio_flags 從設備樹中讀取 rs485_ctrl_gpio 的 GPIO 配置編號和標志
    gpio = of_get_named_gpio_flags(rs485_ctrl_node,"rs485_ctrl_gpio", 0,&flag);
    //gpio_is_valid 判斷該 GPIO 編號是否有效。
    if (!gpio_is_valid(gpio)){
        printk(KERN_INFO "hello: invalid gpio : %d\n",gpio);
        return -1;
    }
    
    //gpio_request 則申請占用該 GPIO。如果初始化過程出錯,需要調用 gpio_free 來釋放之前申請過且成功的 GPIO 。
    ret = gpio_request(gpio, "rs485_ctrl-gpio");
    if (ret) {
        gpio_free(gpio);
        return -EIO;
    }

    //調用 gpio_direction_output 就可以設置輸出高還是低電平
    gpio_direction_output(gpio, GPIO_HIGH);
    
    major = register_chrdev(0, "rs485_ctrl_dev", &rs485_ctrl_fops);
    cls = class_create(THIS_MODULE, "rs485_ctrl_dev");
    device_create(cls, NULL, MKDEV(major, 0), NULL, "rs485_ctrl_drv");
    gpio_set_value(gpio, GPIO_HIGH);


    printk(KERN_INFO "rs485 control gpio %s-%d: exit\n", __FUNCTION__,__LINE__);

    return 0;
}
 
 
static int rs485_ctrl_remove(struct platform_device *pdev)
{
    printk(KERN_INFO "rs485 control gpio %s\n", __FUNCTION__);
    gpio_free(gpio);
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
    unregister_chrdev(major, "rs485_ctrl_dev");
    
    return 0;
}
static const struct of_device_id of_rs485_ctrl_match[] = {
    { .compatible = "rs485_ctrl" },
    { /* Sentinel */ }
};
static struct platform_driver rs485_ctrl_driver = {
    .probe = rs485_ctrl_probe,
    .remove = rs485_ctrl_remove,
    .driver = {
        .name = "rs485_ctrl_drv",
        .owner = THIS_MODULE,
        .of_match_table = of_rs485_ctrl_match,
    },
};
 
static int __init rs485_ctrl_init(void)
{
    printk(KERN_INFO "rs485 control gpio init %s\n", __FUNCTION__);
    return platform_driver_register(&rs485_ctrl_driver);
}
 
static void __exit rs485_ctrl_exit(void)
{
    platform_driver_unregister(&rs485_ctrl_driver);
    printk(KERN_INFO "rs485 control gpio exit!\n");
}

module_init(rs485_ctrl_init);
module_exit(rs485_ctrl_exit);
MODULE_LICENSE("GPL");

 

4.將GPIO驅動編譯進內核

  要給linux內核添加模塊(驅動)有如下兩種方式:

(1)動態方式:采用insmod命令來給運行中的linux加載模塊。

(2)靜態方式:修改linux的配置菜單,添加模塊相關文件到源碼對應目錄,然后把模塊直接編譯進內核。

 

  內核的配置系統一般由以下幾部分組成:

(1)Makefile:分布在Linux內核源代碼中的Makefile,定義Linux內核的編譯規則。

(2)配置文件(Kconfig):給用戶提供配置選項,修改該文件來改變配置菜單選項。

(3)配置工具(make menuconfig):包括配置命令解釋器(對配置腳本中使用的配置命令進行解釋),配置用戶界面(提供字符界面和圖形界面)。這些配置工具都是使用腳本語言編寫的,如Tcl/TK、Perl等。

  配置工具(make menuconfig)根據kconfig配置腳本產生配置菜單,然后根據配置菜單的配置情況生成頂層目錄下的.config(只有一個.config),在.config里定義了配置選擇的配置宏定義,如下所示:

  

  可以看到這里配置了很多CONFIG_XXX相關的宏定義。

  打開/drivers/char/Makefile:

  

  所以.config里面的配置項也就是應用到了Makefile里面。

  流程就是:Kconfig(修改該文件來改變配置菜單選項) ----> Make menuconfig(配置菜單) ----> .config(配置生成) ----> Makefile(根據.config里面的配置項確定要編譯哪些驅動)

不創建新的驅動文件夾:

  (1)把我們的驅動源文件(rs485_driver.c)放到對應目錄下,具體放到哪里需要根據驅動的類型和特點。這里假設我們放到./driver/char下。

  (2)然后我們修改./driver/char下的Kconfig文件

  

  注意這里的RS485_DRIVER這個名字可以隨便寫,他並不需要跟驅動源文件保持一致,但最好保持一致,等下我們在修改Makefile時會用到這個名字,他將會變成CONFIG_RS485_DRIVER,那個名字必須與這個名字對應。如上所示,tristate定義了這個配置選項的可選項有幾個。(具體查看Kconfig語法規則)

  (3)修改./driver/char下的Makefile文件,如下所示:

  

  Makefile的CONFIG_RS485_DRIVER需要和Kconfig中的RS485_DRIVER對應起來。因為Kconfig中的RS485_DRIVER在經過make menuconfig配置過,會生成.config。在.config中就會變成CONFIG_RS485_DRIVER。

  這樣配置就完成了。只需要再頂層目錄make menuconfig。

  

   

  然后選擇配置。再make一下就可以了。

創建新的驅動文件夾:

  (1)在源碼的對應目錄下建立自己的目錄(rs485),這里假設為/drivers/char/rs485。

  (2) 把驅動源碼放到新建的rs485目錄下,並在此目錄下新建Kconfig和Makefile文件。然后給新建的Kconfig和Makefile添加內容。

  

  

  (3)在drivers/char/Kconfig中加入:source “drivers/char/rs485/Kconfig”

      在drivers/char/Makefile中加入:obj-$(CONFIG_RS485_DRIVER) += rs485/  (這邊直接指定我們創建的目錄,Makefile就會去文件rs485下的Makefile找並編譯)

  然后make menuconfig選擇編譯:

  

 

   在.config就可以看到相關配置信息

  

 


免責聲明!

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



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