加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 設(shè)備樹下的字符設(shè)備驅(qū)動(dòng)框架
    • 1. 修改設(shè)備樹文件
    • 2. 編寫驅(qū)動(dòng)程序
    • 3. 編寫測序程序
    • 4. 編譯測試
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

設(shè)備樹下的字符設(shè)備驅(qū)動(dòng)框架

2022/07/09
1239
閱讀需 8 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

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

作者:安迪西

設(shè)備樹下的字符設(shè)備驅(qū)動(dòng)框架

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

下圖為設(shè)備樹下的字符設(shè)備驅(qū)動(dòng)框架圖:

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

1. 修改設(shè)備樹文件

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

andyxiled {
    #address-cells = <1>;      /*reg中起始地址占用一個(gè)字長*/
    #size-cells = <1>;         /*reg中地址長度占用一個(gè)字長*/
    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*/
};

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

make dtbs                       #編譯設(shè)備樹
make imx6ull-andyxi-emmc.dtb    #單獨(dú)編譯指定設(shè)備樹

編譯完成后,使用新的設(shè)備樹啟動(dòng)Linux內(nèi)核,之后可進(jìn)入/proc/device-tree文件夾查看dtsled節(jié)點(diǎn)是否存在

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

2. 編寫驅(qū)動(dòng)程序

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

? 宏定義及設(shè)備結(jié)構(gòu)體定義

#define DTSLED_CNT 1            //設(shè)備號(hào)個(gè)數(shù)
#define DTSLED_NAME "dtsled"    //名字
#define LEDOFF 0                //關(guān)燈
#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 設(shè)備結(jié)構(gòu)體 */
struct dtsled_dev{
    dev_t devid;               //設(shè)備號(hào)
    struct cdev cdev;          //cdev
    struct class *class;       //類
    struct device *device;     //設(shè)備
    int major;                 //主設(shè)備號(hào)
    int minor;                 //次設(shè)備號(hào)
    struct device_node *nd;    //設(shè)備節(jié)點(diǎn)
};

struct dtsled_dev dtsled;      //led設(shè)備

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

static int __init led_init(void) { 
    u32 val = 0; 
    int ret; 
    u32 regdata[14]; 
    const char *str; 
    struct property *proper; 
    /* 獲取設(shè)備樹中的屬性數(shù)據(jù) */ 
    /* 1、獲取設(shè)備節(jié)點(diǎn):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屬性內(nèi)容 */ 
    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屬性內(nèi)容 */ 
    ret = of_property_read_string(dtsled.nd, "status", &str); 
    if(ret < 0){ 
        printk("status read failed!rn"); 
    } else { 
        printk("status = %srn",str); 
    }
    /* 4、獲取reg屬性內(nèi)容 */ 
    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時(shí)鐘 */ 
    val = readl(IMX6U_CCM_CCGR1); 
    val &= ~(3 << 26);     //之前的設(shè)置
    val |= (3 << 26);      //設(shè)置新值
    writel(val, IMX6U_CCM_CCGR1); 
    /* 3、設(shè)置GPIO1_IO03復(fù)用功能,并設(shè)置IO屬性 */ 
    writel(5, SW_MUX_GPIO1_IO03); 
    writel(0x10B0, SW_PAD_GPIO1_IO03); 
    /* 4、設(shè)置GPIO1_IO03為輸出功能 */ 
    val = readl(GPIO1_GDIR); 
    val &= ~(1 << 3);      //之前的設(shè)置 
    val |= (1 << 3);       //設(shè)置為輸出
    writel(val, GPIO1_GDIR); 
    /* 5、默認(rèn)關(guān)閉LED */ 
    val = readl(GPIO1_DR); 
    val |= (1 << 3); 
    writel(val, GPIO1_DR);

? 驅(qū)動(dòng)入口函數(shù)中:注冊(cè)字符設(shè)備驅(qū)動(dòng),代碼與Linux點(diǎn)燈一文中的一樣? 驅(qū)動(dòng)出口函數(shù)中:注銷設(shè)備驅(qū)動(dòng),刪除類和設(shè)備,代碼可參考Linux點(diǎn)燈一文

3. 編寫測序程序

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

4. 編譯測試

? 編譯驅(qū)動(dòng)程序:當(dāng)前目錄下創(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

? 編譯測試程序:無需內(nèi)核參與,直接編譯即可

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

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

depmod                         #第一次加載驅(qū)動(dòng)的時(shí)候需運(yùn)行此命令
modprobe dtsled.ko             #加載驅(qū)動(dòng)
./dtsledApp /dev/dtsled 1      #打開LED燈
./dtsledApp /dev/dtsled 0      #關(guān)閉LED燈
rmmod dtsled.ko                #卸載驅(qū)動(dòng)模塊

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

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