色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何驅動Linux開發板LED燈

CHANBAEK ? 來源:嵌入式攻城獅 ? 作者:安迪西 ? 2023-04-14 11:41 ? 次閱讀

Linux下的任何外設驅動,最終都是要配置相應的硬件寄存器。 前面的文章中介紹了新舊字符設備的驅動開發框架,也介紹了IMX6ULL處理器GPIO的工作原理及配置方法,本篇我們將實際操作一個GPIO,點亮Linux驅動開發路上的第一個燈

1. 地址映射

1.1 MMU介紹

MMU (Memory Manage Unit),即內存管理單元,它提供統一的內存空間抽象,程序訪問虛擬內存中的地址,MMU將虛擬地址翻譯成實際的物理地址,之后CPU即可操作實際的物理地址

MMU具有如下功能:

  • 保護內存:MMU給一些指定的內存塊設置了讀、寫以及可執行的權限,這些權限存儲在頁表當中,MMU會檢查CPU當前所處的是特權模式還是用戶模式,只有權限匹配才可以訪問
  • 提供方便統一的內存空間抽象,實現虛擬地址到物理地址的轉換:CPU可以運行在虛擬的內存當中,虛擬內存一般比物理內存大很多,使得CPU可以運行比較大的應用程序

圖片

1.2 IO映射函數

Linux內核啟動時會初始化MMU,設置內存映射,之后CPU訪問的都是虛擬地址。 在程序編寫時,可使用下面兩個函數進行物理內存和虛擬內存之間的轉換

ioremap():將物理地址映射為虛擬地址

#define ioremap(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE) 
void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype){ 
    return arch_ioremap_caller(phys_addr, size, mtype, __builtin_return_address(0)); 
} 
//phys_addr:被映射的IO起始地址(物理地址)
//size:需要映射的空間大小,以字節為單位
//mtype: ioremap的類型
//return: __iomem類型的指針,指向映射成功后返回的虛擬空間起始地址

ioremap():將物理地址映射為虛擬地址

void iounmap (volatile void __iomem *addr) 
//addr: 要取消映射的虛擬地址空間首地址
//return: void

1.3 IO內存訪問函數

使用ioremap函數將寄存器的物理地址映射到虛擬地址以后,理論上就可以直接通過指針訪問這些地址了,但是為了符合驅動的跨平臺以及可移植性,推薦使用一組操作函數來對映射后的內存進行讀寫操作

u8  readb(const volatile void __iomem *addr);    /*讀取一個字節*/
u16 readw(const volatile void __iomem *addr);    /*讀取一個字*/
u32 readl(const volatile void __iomem *addr);    /*讀取一個雙字*/
    
void writeb(u8 value,  volatile void __iomem *addr);  /*寫入一個字節*/
void writew(u16 value, volatile void __iomem *addr);  /*寫入一個字*/
void writel(u32 value, volatile void __iomem *addr);  /*寫入一個雙字*/

2. 程序編寫

本實驗目的:編寫Linux下的LED燈驅動,通過應用程序對I.MX6U開發板上的LED燈(GPIO1_IO03)進行開關操作

2.1 驅動程序編寫

LED驅動屬于字符設備驅動,之前介紹了新舊兩種字符驅動的寫法,本篇中按照新字符設備驅動的框架來編寫

圖片

接下來分步驟完善具體的驅動代碼:

GPIO寄存器宏定義、設備結構體定義

#define NEWCHRLED_CNT  1             //設備號個數
#define NEWCHRLED_NAME "newchrled"   //名字
#define LEDOFF    0                  //關燈
#define LEDON     1                  //開燈 
/* 寄存器物理地址 */
#define CCM_CCGR1_BASE            (0X020C406C) 
#define SW_MUX_GPIO1_IO03_BASE    (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE    (0X020E02F4)
#define GPIO1_DR_BASE             (0X0209C000)
#define GPIO1_GDIR_BASE           (0X0209C004)
/* 映射后的寄存器虛擬地址指針 */
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;
/* newchrled設備結構體 */
struct newchrled_dev{
    dev_t devid;               //設備號
    struct cdev cdev;          //cdev
    struct class *class;       //類  
    struct device *device;     //設備
    int major;                 //主設備號
    int minor;                 //次設備號
};

struct newchrled_dev newchrled; //led設備

控制LED亮滅函數

void led_switch(u8 sta){
    u32 val = 0;
    if(sta == LEDON) {
        val = readl(GPIO1_DR);
        val &= ~(1 << 3); 
        writel(val, GPIO1_DR);
    }else if(sta == LEDOFF) {
        val = readl(GPIO1_DR);
        val|= (1 << 3); 
        writel(val, GPIO1_DR);
    } 
}

設備操作函數集合

/* 打開設備 */
static int led_open(struct inode *inode, struct file *filp){
    filp->private_data = &newchrled;  //設置私有數據
    return 0;
}
/* 從設備讀取數據 */
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){
    return 0;
}
/* 向設備寫數據 */
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt){
    int retvalue;
    unsigned char databuf[1];
    unsigned char ledstat;

    retvalue = copy_from_user(databuf, buf, cnt);
    if(retvalue < 0) {
        printk("kernel write failed!\\r\\n");
        return -EFAULT;
    }

    ledstat = databuf[0];  //獲取狀態值

    if(ledstat == LEDON) { 
        led_switch(LEDON);  //打開LED燈
    } else if(ledstat == LEDOFF) {
        led_switch(LEDOFF);  //關閉LED燈
    }
    return 0;
}
/* 關閉設備 */
static int led_release(struct inode *inode, struct file *filp){
    return 0;
}
/* 設備操作函數 */
static struct file_operations newchrled_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .read = led_read,
    .write = led_write,
    .release =  led_release,
};

在驅動入口函數中:初始化GPIO外設

/* 驅動入口函數 */
static int __init led_init(void){
    u32 val = 0;
    /* 初始化LED */
    /* 1、寄存器地址映射 */
    IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
    SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
    GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
    GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);
    /* 2、使能GPIO1時鐘 */
    val = readl(IMX6U_CCM_CCGR1);
    val &= ~(3 << 26);     //之前的設置 
    val |= (3 << 26);      //設置新值
    writel(val, IMX6U_CCM_CCGR1);
    /* 3、設置復用功能,并設置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);

在驅動入口函數中:注冊字符設備

/* 注冊字符設備驅動 */
    /* 1、創建設備號 */
    if (newchrled.major) {      //定義了設備號
        newchrled.devid = MKDEV(newchrled.major, 0);
        register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
    } else {                    //沒有定義設備號
        alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); 
        newchrled.major = MAJOR(newchrled.devid);
        newchrled.minor = MINOR(newchrled.devid);
    }
    printk("newcheled major=%d,minor=%d\\r\\n",newchrled.major, newchrled.minor);  
    /* 2、初始化cdev */
    newchrled.cdev.owner = THIS_MODULE;
    cdev_init(&newchrled.cdev, &newchrled_fops); 
    /* 3、添加一個cdev */
    cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);
    /* 4、創建類 */
    newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
    if (IS_ERR(newchrled.class)) {
        return PTR_ERR(newchrled.class);
    }
    /* 5、創建設備 */
    newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);
    if (IS_ERR(newchrled.device)) {
        return PTR_ERR(newchrled.device);
    }
 
    return 0;
}

在驅動出口函數中:取消地址映射,注銷字符設備驅動

/* 驅動出口函數 */
static void __exit led_exit(void){
    /* 取消映射 */
    iounmap(IMX6U_CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);
    /* 注銷字符設備驅動 */
    cdev_del(&newchrled.cdev);
    unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT);
    device_destroy(newchrled.class, newchrled.devid);
    class_destroy(newchrled.class);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

2.2 應用程序編寫

LED驅動加載成功以自動創建設備節點,應用程序通過向節點文件寫0或1,關閉/打開LED燈

#define LEDOFF  0
#define LEDON  1

int main(int argc, char *argv[]){
    int fd, retvalue;
    char *filename;
    unsigned char databuf[1];
 
    if(argc != 3){
        printf("Error Usage!\\r\\n");
        return -1;
    }

    filename = argv[1];

    /* 打開led驅動 */
    fd = open(filename, O_RDWR);
    if(fd < 0){
        printf("file %s open failed!\\r\\n", argv[1]);
        return -1;
    }

    databuf[0] = atoi(argv[2]); //要執行的操作:打開或關閉
    /* 向/dev/led文件寫入數據 */
    retvalue = write(fd, databuf, sizeof(databuf));
    if(retvalue < 0){
        printf("LED Control Failed!\\r\\n");
        close(fd);
        return -1;
    }

    retvalue = close(fd); 
    if(retvalue < 0){
        printf("file %s close failed!\\r\\n", argv[1]);
        return -1;
    }
    return 0;
}

3. 編譯測試

3.1 程序編譯

驅動程序編譯:創建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 := newchrled.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 newchrledApp.c -o newchrledApp

3.2 運行測試

為了方便,選擇通過TFTP從網絡啟動,并使用NFS掛載網絡根文件系統。 確保開發板能正常啟動,在Ubuntu中將驅動和測試文件復制到modules/4.1.15目錄中

加載驅動模塊

depmod                 #第一次加載驅動的時候需要運行此命令
modprobe newchrled.ko  #加載驅動

驅動加載成功后會自動在/dev目錄下創建設備節點文件/dev/newchrdev,輸入如下命令查看

ls /dev/newchrled -l

之后就可使用應用程序來測試驅動是否能正常工作

./newchrledApp /dev/newchrled 1    #打開LED燈
./newchrledApp /dev/newchrled 0    #關閉LED燈

若要卸載驅動輸入如下命令

rmmod newchrled.ko

至此,Linux 驅動開發路上的第一個燈被成功點亮。 本文主要是通過操作寄存器來點亮開發板上的LED,通過編寫相應的驅動程序和應用程序,實現程序設計的分層

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • led燈
    +關注

    關注

    22

    文章

    1592

    瀏覽量

    107950
  • Linux
    +關注

    關注

    87

    文章

    11292

    瀏覽量

    209331
  • 開發板
    +關注

    關注

    25

    文章

    5032

    瀏覽量

    97375
  • GPIO
    +關注

    關注

    16

    文章

    1204

    瀏覽量

    52055
  • MMU
    MMU
    +關注

    關注

    0

    文章

    91

    瀏覽量

    18283
收藏 人收藏

    評論

    相關推薦

    IMX6ULL正點原子開發板LED驅動

    用C語言裸機編程驅動正點原子I.MAX6ULL開發板上的LED
    的頭像 發表于 05-02 15:22 ?25.9w次閱讀
    IMX6ULL正點原子<b class='flag-5'>開發板</b><b class='flag-5'>LED</b><b class='flag-5'>驅動</b>

    基于STM32開發板LED

    硬件資源LEDSTM32開發板線纜LED驅動電路LED
    發表于 08-11 09:11

    如何配置Linux開發板的GPIO

    (I.MX6ULL)環境:Ubuntu 20.04 (LTS) (內核版本:Linux 5.4.0)交叉編譯器:arm-linux-gnueabihf 4.9.4一、目的編寫裸機代碼(匯編)點亮LED
    發表于 12-15 07:46

    基于Study210開發板點亮LED的相關資料推薦

    Linux 嵌入式系列】點亮LED實戰記錄--基于Study210開發板Linux 嵌入式系列】點亮
    發表于 12-16 07:36

    TQ2440開發板按鍵點亮LED驅動開發詳解

    記錄了作者在TQ2440開發板上實現按鍵點亮LED驅動開發的詳細過程,還記錄了一些容易出現的錯誤,以及怎么解決這些錯誤。 一、驅動
    發表于 11-04 16:46 ?179次下載
    TQ2440<b class='flag-5'>開發板</b>按鍵點亮<b class='flag-5'>LED</b><b class='flag-5'>驅動</b><b class='flag-5'>開發</b>詳解

    嵌入式開發板_iTOP-4412開發板linux系統存儲空間

    [入式開發板]4412開發板linux 系統存儲空間的修改
    發表于 02-29 16:58 ?13次下載

    開發板上的LED閃爍起來吧

    這節課給大家講一下如何讓開發板上網絡編號為 D4 LED 閃爍起來,先看一下開發板上關于 LED 的原理圖
    發表于 11-11 17:17 ?13次下載

    如何配置和操作Linux驅動程序開發板

    本文檔概述了利用Linux開發板Linux 內核開發驅動程序的基礎知識,并簡單介紹了如何配置和操作
    的頭像 發表于 02-15 13:36 ?3048次閱讀
    如何配置和操作<b class='flag-5'>Linux</b><b class='flag-5'>驅動</b>程序<b class='flag-5'>開發板</b>

    利用Linux開發板為TLV320ADC5120開發Linux內核驅動的方法

    利用Linux開發板為TLV320ADC5120開發Linux內核驅動的方法
    發表于 10-28 11:59 ?0次下載
    利用<b class='flag-5'>Linux</b><b class='flag-5'>開發板</b>為TLV320ADC5120<b class='flag-5'>開發</b><b class='flag-5'>Linux</b>內核<b class='flag-5'>驅動</b>的方法

    通過Web網頁控制開發板LED

    接下來將介紹如何通過Web網頁來控制開發板上的LED,本文只是在網頁上實現功能,并無交互功能,與開發板的交互功能實現將在《Web網頁點燈二》中介紹
    的頭像 發表于 04-25 15:05 ?1562次閱讀
    通過Web網頁控制<b class='flag-5'>開發板</b><b class='flag-5'>LED</b><b class='flag-5'>燈</b>

    匯編驅動IMX6ULL LED

    用匯編編寫正點原子Linux開發板Led驅動
    的頭像 發表于 05-01 09:19 ?25.9w次閱讀
    匯編<b class='flag-5'>驅動</b>IMX6ULL <b class='flag-5'>LED</b><b class='flag-5'>燈</b>

    迅為基于RK3568開發板的嵌入式學習之Linux驅動視頻

    迅為基于RK3568開發板的嵌入式學習之Linux驅動視頻
    的頭像 發表于 05-19 16:30 ?968次閱讀
    迅為基于RK3568<b class='flag-5'>開發板</b>的嵌入式學習之<b class='flag-5'>Linux</b><b class='flag-5'>驅動</b>視頻

    STM32 Linux開發板推薦 ,入門進階必備!

    推薦一款適合入門進階學習的Linux開發板:華清遠見FS-MP1A開發板(STM32MP157開發板開發板介紹 FS-MP1A
    發表于 10-22 09:22 ?1次下載

    fpga開發板linux開發板區別

    FPGA開發板Linux開發板是兩種不同的硬件開發平臺,各自具有不同的特點和應用場景。在以下的文章中,我將詳細介紹FPGA開發板
    的頭像 發表于 02-01 17:09 ?2230次閱讀

    linux開發板與樹莓派的區別

    定義和用途 Linux開發板Linux開發板是一種基于Linux操作系統的嵌入式開發板,通常用
    的頭像 發表于 08-30 15:34 ?937次閱讀
    主站蜘蛛池模板: 一区二区三区高清视频| YELLOW免费观看2019| 秋霞伦理高清视频在线| 国产女人喷潮视频免费| 2019夜夜| 无码成人AAAAA毛片含羞草| 美国z0069| 国产午夜精品久久理论片小说| 夜夜澡人人爽人人喊_欧美| 免费的好黄的漫画| 国语大学生自产拍在线观看| 亚洲欧美人成视频在线| 狠狠色狠狠色综合日日2019| 99热这里只就有精品22| 挺进老师的紧窄小肉六电影完整版 | 欧美阿v在线天堂| 国产亚洲精品a在线观看app| 北条麻妃久久99精品| 专干老肥熟女视频网站300部| 帅哥操美女| 九九热在线观看| 国产福利高清在线视频| 98国产精品人妻无码免费| 日本女人bb| 免费三级网址| 久久毛片免费看一区二区三区| 国产噜噜噜精品免费| 疯狂做受XXXX高潮欧美日本| 一本道久在线综合色姐| 无人区免费一二三四乱码 | 99视频在线观看免费视频| 亚洲免费综合色视频| 久久久中日AB精品综合| 99久久久无码国产精品不卡按摩 | ppypp午夜限制不卡影院私人| 在线免费观看毛片| 色欲AV精品人妻一区二区三区 | 稚嫩挤奶h调教h| 亚洲中文字幕国产综合| 亚洲精品成人A8198A片漫画| 无码AV毛片色欲欧洲美洲|