• 正文
    • 設備樹下的字符設備驅動框架
    • 1. 修改設備樹文件
    • 2. 編寫驅動程序
    • 3. 編寫測序程序
    • 4. 編譯測試
  • 相關推薦
申請入駐 產業(yè)圖譜

設備樹下的字符設備驅動框架

2022/07/09
979
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

公眾號:嵌入式攻城獅(ID:andyxi_linux)

作者:安迪西

設備樹下的字符設備驅動框架

沒有引入設備樹時,相關寄存器物理地址是直接定義在驅動文件中的,通過地址映射成為虛擬地址后,再操作虛擬地址完成GPIO的初始化。設備樹的本質也是操作寄存器,只不過寄存器的相關信息放在了設備樹中,配置寄存器時使用OF函數(shù)從設備樹中讀取寄存器數(shù)據(jù)后再進行配置

下圖為設備樹下的字符設備驅動框架圖:

接下來根據(jù)上面的框架圖,以驅動LED (GPIO1_IO03)為例,分步介紹具體的代碼編寫流程

1. 修改設備樹文件

在內核源碼的/arch/arm/boot/dts/文件夾中復制一份官方I.MX6ULL EVK EMMC版的設備樹文件imx6ull-14x14-evk-emmc.dts,并自定義文件名,此處重命名為了imx6ull-andyxi-emmc.dts,在根節(jié)點中添加LED設備節(jié)點

andyxiled {
    #address-cells = <1>;      /*reg中起始地址占用一個字長*/
    #size-cells = <1>;         /*reg中地址長度占用一個字長*/
    compatible = "andyxi-led";
    status = "okay";
    reg = < 0X020C406C 0x04    /*CCM_CCGR1_BASE*/ 
            0X020E0068 0x04    /*SW_MUX_GPIO1_IO03_BASE*/
            0X020E02F4 0x04    /*SW_PAD_GPIO1_IO03_BASE*/
            0X0209C000 0x04    /*GPIO1_DR_BASE*/
            0X0209C004 0x04 >; /*GPIO1_GDIR_BASE*/
};

設備樹修改完成后,在內核源碼的根目錄下執(zhí)行make命令編譯設備樹

make dtbs                       #編譯設備樹
make imx6ull-andyxi-emmc.dtb    #單獨編譯指定設備樹

編譯完成后,使用新的設備樹啟動Linux內核,之后可進入/proc/device-tree文件夾查看dtsled節(jié)點是否存在

#啟動Linux系統(tǒng)后,在開發(fā)板中查看節(jié)點
cd /proc/device-tree         #查看andyxiled節(jié)點是否存在

2. 編寫驅動程序

創(chuàng)建驅動程序文件dtsled.c,添加如下代碼

? 宏定義及設備結構體定義

#define DTSLED_CNT 1            //設備號個數(shù)
#define DTSLED_NAME "dtsled"    //名字
#define LEDOFF 0                //關燈
#define LEDON 1                 //開燈
/* 映射后的寄存器虛擬地址指針 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
/* dtsled 設備結構體 */
struct dtsled_dev{
    dev_t devid;               //設備號
    struct cdev cdev;          //cdev
    struct class *class;       //類
    struct device *device;     //設備
    int major;                 //主設備號
    int minor;                 //次設備號
    struct device_node *nd;    //設備節(jié)點
};

struct dtsled_dev dtsled;      //led設備

? 編寫設備操作函數(shù):設備操作函數(shù)和LED開關函數(shù),具體代碼可參考Linux點燈一文相關部分? 驅動入口函數(shù)中:使用OF函數(shù)獲取設備樹中的屬性值,并初始化

static int __init led_init(void) { 
    u32 val = 0; 
    int ret; 
    u32 regdata[14]; 
    const char *str; 
    struct property *proper; 
    /* 獲取設備樹中的屬性數(shù)據(jù) */ 
    /* 1、獲取設備節(jié)點:andyxiled */ 
    dtsled.nd = of_find_node_by_path("/andyxiled"); 
    if(dtsled.nd == NULL) { 
        printk("andyxiled node can not found!rn"); 
        return -EINVAL; 
    } else { 
        printk("andyxiled node has been found!rn"); 
    } 
    /* 2、獲取compatible屬性內容 */ 
    proper = of_find_property(dtsled.nd, "compatible", NULL); 
    if(proper == NULL) { 
        printk("compatible property find failedrn"); 
    } else { 
        printk("compatible = %srn", (char*)proper->value); 
    }  
    /* 3、獲取status屬性內容 */ 
    ret = of_property_read_string(dtsled.nd, "status", &str); 
    if(ret < 0){ 
        printk("status read failed!rn"); 
    } else { 
        printk("status = %srn",str); 
    }
    /* 4、獲取reg屬性內容 */ 
    ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10); 
    if(ret < 0) { 
        printk("reg property read failed!rn"); 
    } else { 
        u8 i = 0; 
        printk("reg data:rn"); 
        for(i = 0; i < 10; i++) 
        printk("%#X ", regdata[i]); 
        printk("rn"); 
    }

    /* 初始化LED */ 
#if 0 
    /* 1、寄存器地址映射 */ 
    IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]); 
    SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]); 
    SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]); 
    GPIO1_DR = ioremap(regdata[6], regdata[7]); 
    GPIO1_GDIR = ioremap(regdata[8], regdata[9]); 
#else 
    IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0); 
    SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1); 
    SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2); 
    GPIO1_DR = of_iomap(dtsled.nd, 3); 
    GPIO1_GDIR = of_iomap(dtsled.nd, 4); 
#endif 
    /* 2、使能GPIO1時鐘 */ 
    val = readl(IMX6U_CCM_CCGR1); 
    val &= ~(3 << 26);     //之前的設置
    val |= (3 << 26);      //設置新值
    writel(val, IMX6U_CCM_CCGR1); 
    /* 3、設置GPIO1_IO03復用功能,并設置IO屬性 */ 
    writel(5, SW_MUX_GPIO1_IO03); 
    writel(0x10B0, SW_PAD_GPIO1_IO03); 
    /* 4、設置GPIO1_IO03為輸出功能 */ 
    val = readl(GPIO1_GDIR); 
    val &= ~(1 << 3);      //之前的設置 
    val |= (1 << 3);       //設置為輸出
    writel(val, GPIO1_GDIR); 
    /* 5、默認關閉LED */ 
    val = readl(GPIO1_DR); 
    val |= (1 << 3); 
    writel(val, GPIO1_DR);

? 驅動入口函數(shù)中:注冊字符設備驅動,代碼與Linux點燈一文中的一樣? 驅動出口函數(shù)中:注銷設備驅動,刪除類和設備,代碼可參考Linux點燈一文

3. 編寫測序程序

實現(xiàn)操作驅動文件對外設進行控制的功能。創(chuàng)建測試程序文件dtsledApp.c,代碼內容與Linux點燈一文中的測試程序代碼一致,此處不再贅述

4. 編譯測試

? 編譯驅動程序:當前目錄下創(chuàng)建Makefile文件,并make編譯

KERNELDIR := /home/andyxi/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_andyxi
CURRENT_PATH := $(shell pwd)
obj-m := dtsled.o

build: kernel_modules

kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

? 編譯測試程序:無需內核參與,直接編譯即可

arm-linux-gnueabihf-gcc dtsledApp.c -o dtsledApp

? 運行測試:拷貝驅動模塊和測試程序到開發(fā)板,啟動開發(fā)板,加載驅動模塊后,使用應用程序測試驅動是否正常工作

depmod                         #第一次加載驅動的時候需運行此命令
modprobe dtsled.ko             #加載驅動
./dtsledApp /dev/dtsled 1      #打開LED燈
./dtsledApp /dev/dtsled 0      #關閉LED燈
rmmod dtsled.ko                #卸載驅動模塊

相關推薦

登錄即可解鎖
  • 海量技術文章
  • 設計資源下載
  • 產業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

公眾號:嵌入式攻城獅;專注于分享和記錄嵌入式開發(fā)技術,主要包含C語言、STM32、STM32CubeMX、lwIP、FreeRTOS、Linux、Zigbee、WIFI、BLE、LoRa、NB-loT、PCB電路設計、QT等等。