1. 程式人生 > >嵌入式Linux裝置驅動開發之:按鍵驅動程式例項

嵌入式Linux裝置驅動開發之:按鍵驅動程式例項

11.6  按鍵驅動程式例項

11.6.1  按鍵工作原理

高電平和低電平相接怎麼會變成低電平呢

 就像你把電源正極的負極相連一樣會把電壓拉低。大電流會從高電平引腳流向低電平引腳,把高電平引腳拉低。

LED和蜂鳴器是最簡單的GPIO的應用,都不需要任何外部輸入或控制。按鍵同樣使用GPIO介面,但按鍵本身需要外部的輸入,即在驅動程式中要處理外部中斷。按鍵硬體驅動原理圖如圖11-7所示。在圖11-7的4×4矩陣按鍵(K1~K16)電路中,使用4個輸入/輸出埠(EINT0、EINT2、EINT11和EINT19)和4個輸出埠(KSCAN0~KSCAN3)。

圖11.7  按鍵驅動電路原理圖

按鍵驅動電路使用的埠和對應的暫存器如表11-18所示。

表11.18 按鍵電路的主要埠

管    腳

端    口

輸入/輸出

管    腳

端    口

輸入/輸出

KEYSCAN0

GPE11

輸出

EINT0

EINIT0/GPF0

輸入/輸出

KEYSCAN1

GPG6

輸出

EINT2

EINT2/GPF2

輸入/輸出

KEYSCAN2

GPE13

輸出

EINT11

EINT11/GPG3

輸入/輸出

KEYSCAN3

GPG2

輸出

EINT19

EINT19/GPG11

輸入/輸出

因為通常中斷埠是比較珍貴且有限的資源,所以在本電路設計中,16個按鍵複用了4箇中斷線。那怎麼樣才能及時而準確地對矩陣按鍵進行掃描呢?

某個中斷的產生表示,與它所對應的矩陣行的4個按鍵中,至少有一個按鍵被按住了。因此可以通過檢視產生了哪個中斷,來確定在矩陣的哪一行中發生了按鍵操作(按住或釋放)。例如,如果產生了外部2號線中斷(EINT2變為低電平),則表示K7、K8、K9和K15中至少有一個按鍵被按住了。這時候4個EINT埠應該通過GPIO配置暫存器被設定為外部中斷埠,而且4個KSCAN埠的輸出必須為低電平。

在確定按鍵操作所在行的位置之後,我們還得檢視按鍵操作所在列的位置。此時要使用KSCAN埠組,同時將4個EINT埠配置為通用輸入埠(而不是中斷埠)。在4個KSCAN埠中,輪流將其中某一個埠的輸出置為低電平,其他3個埠的輸出置為高電平。這樣逐列進行掃描,直到按鍵所在列的KSCAN埠輸出為低電平,此時按鍵操作所在行的EINT管腳的輸入埠的值會變成低電平。例如,在確認產生了外部2號中斷之後,進行逐列掃描。若發現在KSCAN1為低電平時(其他埠輸出均為高電平),GPF2(EINT2管腳的輸入埠)變為低電平,則可以斷定按鍵K8被按住了。

以上的討論都是在按鍵的理想狀態下進行的,但實際的按鍵動作會在短時間(幾毫秒至幾十毫秒)內產生訊號抖動。例如,當按鍵被按下時,其動作就像彈簧的若干次往復運動,將產生幾個脈衝訊號。一次按鍵操作將會產生若干次按鍵中斷,從而會產生抖動現象。因此驅動程式中必須要解決去除抖動所產生的毛刺訊號的問題。

11.6.2  按鍵驅動程式

首先按鍵裝置相關的資料結構的定義如下所示:

/* butt_drv.h */

……

typedef struct _st_key_info_matrix            /* 按鍵資料結構 */

{

    unsigned char    key_id;                    /* 按鍵ID */

    unsigned int    irq_no;                    /* 對應的中斷號 */

    unsigned int    irq_gpio_port;            /* 對應的中斷線的輸入埠地址*/

    unsigned int    kscan_gpio_port;        /* 對應的KSCAN埠地址 */

} st_key_info_matrix;

typedef struct _st_key_buffer                /* 按鍵緩衝資料結構 */

{

    unsigned long jiffy[MAX_KEY_COUNT];    /* 按鍵時間, 5s以前的銨鍵作廢*/

    unsigned char buf[MAX_KEY_COUNT];            /* 按鍵緩衝區 */

    unsigned int head,tail;                    /* 按鍵緩衝區頭和尾 */

} st_key_buffer;

……

下面是矩陣按鍵陣列的定義,陣列元素的資訊(一個按鍵資訊)按照0行0列,0行1列,…,3行2列,3行3列的順序逐行排列。

static st_key_info_matrix key_info_matrix[MAX_COLUMN][MAX_ROW] =

{

    {{10,    IRQ_EINT0,  S3C2410_GPF0,   S3C2410_GPE11},     /* 0行0列 */

    {11,    IRQ_EINT0,  S3C2410_GPF0,   S3C2410_GPG6},

    {12,    IRQ_EINT0,  S3C2410_GPF0,   S3C2410_GPE13},

    {16,    IRQ_EINT0,  S3C2410_GPF0,   S3C2410_GPG2}},

    {{7,    IRQ_EINT2,  S3C2410_GPF2, S3C2410_GPE11},     /* 1行0列 */

    {8,     IRQ_EINT2,  S3C2410_GPF2,  S3C2410_GPG6},

    {9,     IRQ_EINT2,  S3C2410_GPF2,   S3C2410_GPE13},

    {15,    IRQ_EINT2,  S3C2410_GPF2,   S3C2410_GPG2}},

    {{4,    IRQ_EINT11, S3C2410_GPG3,  S3C2410_GPE11},       /* 2行0列 */

    {5,     IRQ_EINT11, S3C2410_GPG3,  S3C2410_GPG6},

    {6,     IRQ_EINT11, S3C2410_GPG3,  S3C2410_GPE13},

    {14,    IRQ_EINT11, S3C2410_GPG3,   S3C2410_GPG2}},

    {{1,    IRQ_EINT19, S3C2410_GPG11, S3C2410_GPE11},      /* 3行0列 */

    {2,     IRQ_EINT19, S3C2410_GPG11, S3C2410_GPG6},

    {3,     IRQ_EINT19, S3C2410_GPG11, S3C2410_GPE13},

    {13,    IRQ_EINT19, S3C2410_GPG11, S3C2410_GPG2}},

};

下面是與按鍵相關的埠的初始化函式。這些函式已經在簡單的GPIO字元裝置驅動程式裡被使用過。此外,set_irq_type()函式用於設定中斷線的型別,在本例項中通過該函式將4箇中斷線的型別配置為下降沿觸發式。

static void init_gpio(void)

{

    s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_OUTP); /* GPE11 */

    s3c2410_gpio_setpin(S3C2410_GPE11, 0);

    s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_OUTP); /* GPE13 */

    s3c2410_gpio_setpin(S3C2410_GPE13, 0);

    s3c2410_gpio_cfgpin(S3C2410_GPG2, S3C2410_GPG2_OUTP); /* GPG2 */

    s3c2410_gpio_setpin(S3C2410_GPG2, 0);

    s3c2410_gpio_cfgpin(S3C2410_GPG6, S3C2410_GPG6_OUTP); /* GPG6 */

    s3c2410_gpio_setpin(S3C2410_GPG6, 0);

    s3c2410_gpio_cfgpin(S3C2410_GPF0, S3C2410_GPF0_EINT0); /* GPF0 */

    s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2); /* GPF2 */

    s3c2410_gpio_cfgpin(S3C2410_GPG3, S3C2410_GPG3_EINT11); /* GPG3 */

    s3c2410_gpio_cfgpin(S3C2410_GPG11, S3C2410_GPG11_EINT19); /* GPG11 */

    set_irq_type(IRQ_EINT0, IRQT_FALLING);

    set_irq_type(IRQ_EINT2, IRQT_FALLING);

    set_irq_type(IRQ_EINT11, IRQT_FALLING);

    set_irq_type(IRQ_EINT19, IRQT_FALLING);

}

下面講解按鍵驅動的主要介面,以下為驅動模組的入口和解除安裝函式。

/* 初始化並新增struct cdev結構到系統之中 */

static void button_setup_cdev(struct cdev *dev, 

                        int minor, struct file_operations *fops)

{

    int err;

    int devno = MKDEV(button_major,minor);

    cdev_init(dev, fops); /* 初始化結構體struct cdev */

    dev->owner = THIS_MODULE;

    dev->ops = fops; /* 關聯到裝置的file_operations結構 */

    err = cdev_add(dev, devno, 1); /* 將struct cdev結構新增到系統之中 */

    if (err)

    {

        printk(KERN_INFO"Error %d adding button %d\n",err, minor);

    }

}

……

/* 驅動初始化 */

static int  button_init(void)

{

    int ret; 

    /* 將主裝置號和次裝置號定義到一個dev_t資料型別的結構體之中 */

    dev_t dev = MKDEV(button_major, 0); 

    if (button_major)

    {/* 靜態註冊一個裝置,裝置號先前指定好,並設定裝置名,用cat /proc/devices來檢視 */

        ret = register_chrdev_region(dev, 1, BUTTONS_DEVICE_NAME);

    }

    else

    { /*由系統動態分配主裝置號 */

        ret = alloc_chrdev_region(&dev, 0, 1, BUTTONS_DEVICE_NAME);

        button_major = MAJOR(dev); /* 獲得主裝置號 */

    }

    if (ret < 0)

    {

        printk(KERN_WARNING"Button:unable to get major %d\n",button_major);

       return ret;

    }

    /* 初始化和新增結構體struct cdev到系統之中 */

    button_setup_cdev(&button_dev, 0, &button_fops); 

    printk("Button driver initialized.\n");

    return 0;

}

/* 驅動解除安裝 */

static void __exit button_exit(void)

{

    cdev_del(&button_dev); /* 刪除結構體struct cdev */

    /* 解除安裝裝置驅動所佔有的資源 */

    unregister_chrdev_region(MKDEV(button_major, 0), 1); 

    printk("Button driver uninstalled\n");

}

module_init(button_init); /* 初始化裝置驅動程式的入口 */

module_exit(button_exit); /* 解除安裝裝置驅動程式的入口 */

MODULE_AUTHOR("David");

MODULE_LICENSE("Dual BSD/GPL");

按鍵字元裝置的file_operations結構定義為:

static struct file_operations button_fops =

{

    .owner = THIS_MODULE,

    .ioctl = button_ioctl,

    .open = button_open,

    .read = button_read,

    .release = button_release,

};

以下為open和release函式介面的實現。

/* 開啟檔案, 申請中斷 */

static int button_open(struct inode *inode,struct file *filp) 

{

    int ret = nonseekable_open(inode, filp);

    if (ret < 0) 

    {

        return ret;

    }

    init_gpio();                /* 相關GPIO埠的初始化*/

    ret = request_irqs();     /* 申請4箇中斷 */

    if (ret < 0) 

    {

        return ret;

    }

    init_keybuffer();            /* 初始化按鍵緩衝資料結構 */

    return ret;

}

/* 關閉檔案, 遮蔽中斷 */

static int button_release(struct inode *inode,struct file *filp)

{

    free_irqs();                /* 遮蔽中斷 */

    return 0;

}

在open函式介面中,進行了GPIO埠的初始化、申請硬體中斷以及按鍵緩衝的初始化等工作。在以前的章節中提過,中斷埠是比較寶貴而且數量有限的資源。因此需要注意,最好要在第一次開啟裝置時申請(呼叫request_irq函式)中斷埠,而不是在驅動模組載入的時候申請。如果已載入的裝置驅動佔用而在一定時間段內不使用某些中斷資源,則這些資源不會被其他驅動所使用,只能白白浪費掉。而在開啟裝置的時候(呼叫open函式介面)申請中斷,則不同的裝置驅動可以共享這些寶貴的中斷資源。

以下為中斷申請和釋放的部分以及中斷處理函式。

/* 中斷處理函式,其中irq為中斷號 */

static irqreturn_t button_irq(int irq, void *dev_id, struct pt_regs *regs)

{

    unsigned char ucKey = 0;

    disable_irqs();        /* 遮蔽中斷 */

    /* 延遲50ms, 遮蔽按鍵毛刺 */

    udelay(50000);

    ucKey = button_scan(irq);    /* 掃描按鍵,獲得進行操作的按鍵的ID */

    if ((ucKey >= 1) && (ucKey <= 16))

        {

        /* 如果緩衝區已滿, 則不新增 */

        if (((key_buffer.head + 1) & (MAX_KEY_COUNT - 1)) != key_buffer.tail)

        {

            spin_lock_irq(&buffer_lock);

            key_buffer.buf[key_buffer.tail] = ucKey;

             key_buffer.jiffy[key_buffer.tail] = get_tick_count();

            key_buffer.tail ++;

            key_buffer.tail &= (MAX_KEY_COUNT -1);

            spin_unlock_irq(&buffer_lock);

        }

    }

    init_gpio();        /* 初始化GPIO埠,主要是為了恢復中斷埠配置 */

    enable_irqs();      /* 開啟中斷 */

    return IRQ_HANDLED;/* 2.6核心返回值一般是這個巨集 */

}

/* 申請4箇中斷 */

static  int request_irqs(void)

{

    int ret, i, j;

    for (i = 0; i < MAX_COLUMN; i++)

    {

        ret = request_irq(key_info_matrix[i][0].irq_no, 

button_irq, SA_INTERRUPT, BUTTONS_DEVICE_NAME, NULL);

        if (ret < 0)

        {

            for (j = 0; j < i; j++)

            {

                free_irq(key_info_matrix[j][0].irq_no, NULL); 

            }

            return -EFAULT;

        }

    }

    return 0;

}

/* 釋放中斷 */

static __inline void free_irqs(void)

{

    int i;

    for (i = 0; i < MAX_COLUMN; i++)

    {

        free_irq(key_info_matrix[i][0].irq_no, NULL);

    }

}

中斷處理函式在每次中斷產生的時候會被呼叫,因此它的執行時間要儘可能得短。通常中斷處理函式只是簡單地喚醒等待資源的任務,而複雜且耗時的工作則讓這個任務去完成。中斷處理函式不能向用戶空間傳送資料或者接收資料,不能做任何可能發生睡眠的操作,而且不能呼叫schedule()函式。

為了簡單起見,而且考慮到按鍵操作的時間比較長,在本例項中的中斷處理函式button_irq()裡,通過呼叫睡眠函式來消除毛刺訊號。讀者可以根據以上介紹的對中斷處理函式的要求改進該部分程式碼。

按鍵掃描函式如下所示。首先根據中斷號確定操作按鍵所在行的位置,然後採用逐列掃描法最終確定操作按鍵所在的位置。

/* 

** 進入中斷後, 掃描銨鍵碼 

** 返回: 按鍵碼(1~16), 0xff表示錯誤 

*/

static __inline unsigned char button_scan(int irq)

{

    unsigned char key_id = 0xff;

    unsigned char column = 0xff, row = 0xff;    

    s3c2410_gpio_cfgpin(S3C2410_GPF0, S3C2410_GPF0_INP); /* GPF0 */

    s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_INP); /* GPF2 */

    s3c2410_gpio_cfgpin(S3C2410_GPG3, S3C2410_GPG3_INP); /* GPG3 */

    s3c2410_gpio_cfgpin(S3C2410_GPG11, S3C2410_GPG11_INP); /* GPG11 */

    switch (irq)

    { /* 根據irq值確定操作按鍵所在行的位置*/

        case IRQ_EINT0:

        {

            column = 0;

        }

        break;

        case IRQ_EINT2:

        {

            column = 1;

        }

        break;

        case IRQ_EINT11:

        {

            column = 2;

        }

        break;

        case IRQ_EINT19:

        {

            column = 3;

        }

        break;

    }    

    if (column != 0xff)

    { /* 開始逐列掃描, 掃描第0列 */

        s3c2410_gpio_setpin(S3C2410_GPE11, 0); /* 將KSCAN0置為低電平 */

        s3c2410_gpio_setpin(S3C2410_GPG6, 1);

        s3c2410_gpio_setpin(S3C2410_GPE13, 1);

        s3c2410_gpio_setpin(S3C2410_GPG2, 1);

       if(!s3c2410_gpio_getpin(key_info_matrix[column][0].irq_gpio_port))

        { /* 觀察對應的中斷線的輸入埠值 */

            key_id = key_info_matrix[column][0].key_id;

            return key_id;

        }

        /* 掃描第1列*/

        s3c2410_gpio_setpin(S3C2410_GPE11, 1);

        s3c2410_gpio_setpin(S3C2410_GPG6, 0); /* 將KSCAN1置為低電平 */

        s3c2410_gpio_setpin(S3C2410_GPE13, 1);

        s3c2410_gpio_setpin(S3C2410_GPG2, 1);

        if(!s3c2410_gpio_getpin(key_info_matrix[column][1].irq_gpio_port))

        {

            key_id = key_info_matrix[column][1].key_id;

            return key_id;

        }

        /* 掃描第2列*/

        s3c2410_gpio_setpin(S3C2410_GPE11, 1);

        s3c2410_gpio_setpin(S3C2410_GPG6, 1);

        s3c2410_gpio_setpin(S3C2410_GPE13, 0); /* 將KSCAN2置為低電平 */

        s3c2410_gpio_setpin(S3C2410_GPG2, 1);

        if(!s3c2410_gpio_getpin(key_info_matrix[column][2].irq_gpio_port))

        {

            key_id = key_info_matrix[column][2].key_id;

            return key_id;

        }

        /* 掃描第3列*/

        s3c2410_gpio_setpin(S3C2410_GPE11, 1);

        s3c2410_gpio_setpin(S3C2410_GPG6, 1);

        s3c2410_gpio_setpin(S3C2410_GPE13, 1);

        s3c2410_gpio_setpin(S3C2410_GPG2, 0); /* 將KSCAN3置為低電平 */

        if(!s3c2410_gpio_getpin(key_info_matrix[column][3].irq_gpio_port))

        {

            key_id = key_info_matrix[column][3].key_id;

            return key_id;

        }

    } 

    return key_id;

}

以下是read函式介面的實現。首先在按鍵緩衝中刪除已經過時的按鍵操作資訊,接下來,從按鍵緩衝中讀取一條資訊(按鍵ID)並傳遞給使用者層。

/* 從緩衝刪除過時資料(5s前的按鍵值) */

static void remove_timeoutkey(void)

{

    unsigned long tick;

    spin_lock_irq(&buffer_lock); /* 獲得一個自旋鎖 */

    while(key_buffer.head != key_buffer.tail)

    {

        tick = get_tick_count() - key_buffer.jiffy[key_buffer.head];

        if (tick  < 5000)    /* 5s */

            break;

        key_buffer.buf[key_buffer.head] = 0;

        key_buffer.jiffy[key_buffer.head] = 0;

        key_buffer.head ++;

        key_buffer.head &= (MAX_KEY_COUNT -1);

    }

    spin_unlock_irq(&buffer_lock); /* 釋放自旋鎖 */

}

/* 讀鍵盤 */

static ssize_t button_read(struct file *filp, 

                            char *buffer, size_t count, loff_t *f_pos)

{

    ssize_t ret = 0;

    remove_timeoutkey(); /* 刪除過時的按鍵操作資訊 */

    spin_lock_irq(&buffer_lock);

    while((key_buffer.head != key_buffer.tail) && (((size_t)ret) < count))

    {

        put_user((char)(key_buffer.buf[key_buffer.head]), &buffer[ret]);

        key_buffer.buf[key_buffer.head] = 0;

        key_buffer.jiffy[key_buffer.head] = 0;

        key_buffer.head ++;

        key_buffer.head &= (MAX_KEY_COUNT -1);

        ret ++;

    }

    spin_unlock_irq(&buffer_lock);

    return ret;

}

以上介紹了按鍵驅動程式中的主要內容。

11.6.3  按鍵驅動的測試程式

按鍵驅動程式的測試程式所下所示。在測試程式中,首先開啟按鍵裝置檔案和gpio裝置(包括4個LED和蜂鳴器)檔案,接下來,根據按鍵的輸入值(按鍵ID)的二進位制形式,LED D9~D12發亮(例如,按下11號按鍵,則D9、D10和D12會發亮),而蜂鳴器當每次按鍵時發出聲響。

/* butt_test.c */

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#include <sys/time.h>

#include <sys/types.h>

#include <unistd.h>

#include <asm/delay.h>

#include "butt_drv.h"

#include "gpio_drv.h"

main()

{

    int butt_fd, gpios_fd, i;

    unsigned char key = 0x0;

    butt_fd = open(BUTTONS_DEVICE_FILENAME, O_RDWR); /* 開啟按鈕裝置 */

    if (butt_fd == -1)

    {

        printf("Open button device button errr!\n");

        return 0;

    }

    gpios_fd = open(GPIO_DEVICE_FILENAME, O_RDWR); /* 開啟GPIO裝置 */

    if (gpios_fd == -1)

    {

        printf("Open button device button errr!\n");

        return 0;

    }

    ioctl(butt_fd, 0);    /* 清空鍵盤緩衝區, 後面引數沒有意義 */

    printf("Press No.16 key to exit\n"); 

    do

    {    

        if (read(butt_fd, &key, 1) <= 0) /* 讀鍵盤裝置,得到相應的鍵值 */

        {

            continue;

        }

        printf("Key Value = %d\n", key);

        for (i = 0; i < LED_NUM; i++)

        {

            if ((key & (1 << i)) != 0)

            {

                ioctl(gpios_fd, LED_D09_SWT + i, LED_SWT_ON); /* LED發亮*/

            }

        }

        ioctl(gpios_fd, BEEP_SWT, BEEP_SWT_ON); /* 發聲*/

        sleep(1);

        for (i = 0; i < LED_NUM; i++)

        {

            ioctl(gpios_fd, LED_D09_SWT + i, LED_SWT_OFF);    /* LED熄滅*/

        }

        ioctl(gpios_fd, BEEP_SWT, BEEP_SWT_OFF);

    } while(key != 16); /* 按16號鍵則退出 */

    close(gpios_fd); 

    close(butt_fd);

    return 0;

}

首先編譯和載入按鍵驅動程式,而且要建立裝置檔案節點。

$ make clean;make    /* 驅動程式的編譯*/

$ insmod butt_dev.ko /* 載入buttons裝置驅動 */

$ cat /proc/devices  /* 通過這個命令可以查到buttons裝置的主裝置號 */

$ mknod /dev/buttons  c  252  0  /* 假設主裝置號為252, 建立裝置檔案節點*/

接下來,編譯和載入GPIO驅動程式,而且要建立裝置檔案節點。

$ make clean;make /* 驅動程式的編譯*/

相關推薦

嵌入式Linux裝置驅動開發按鍵驅動程式例項

11.6  按鍵驅動程式例項 11.6.1  按鍵工作原理 高電平和低電平相接怎麼會變成低電平呢 就像你把電源正極的負極相連一樣會把電壓拉低。大電流會從高電平引腳流向低電平引腳,把高電平引腳拉低。 LED和蜂鳴器是最簡單的GPIO的應用,都不需要任何外部

⑳tiny4412 Linux驅動開發MMC子系統驅動程式

本次我們來說一下SDIO子系統的控制器的開發部分,這部分也是和硬體平臺相關的,在說這個之前,我們先來了解一下相關硬體的基礎知識和概念. MMC MMC全稱MultiMedia Card,由西門子公司和SanDisk公司1997年推出的多媒體記憶卡標準。MMC卡尺寸為32mm

Linux驅動開發10裝置樹】nanopi的按鍵驅動

介紹 這一節在nanopi上實現按鍵驅動,和LED驅動一樣,通用的按鍵驅動在linux核心中已經實現好,我們只需要按照要求寫好裝置樹即可,不用我們自己實現按鍵驅動。這一節中首先修改裝置樹並測試按鍵驅動,然後分析drivers/input/keyboard/gpio_keys.c檔案,

Linux驅動開發11裝置樹】nanopi的PWM驅動

介紹 前兩節利用裝置樹實現了nanopi的LED驅動和按鍵驅動,這一節來實現nonapi的PWM驅動。PWM驅動在核心中也有相應的實現,因此這裡只是按照要求新增裝置樹檔案即可。這一節和之前一樣,首先修改裝置樹檔案進行測試,然後分析核心相應的軟體實現。 新增裝置樹節點 因為在s

嵌入式Linux裝置驅動開發——selec/poll

應用程式呼叫select,select系統呼叫的原型: int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout); fd_set資料

嵌入式核心及驅動開發學習筆記(十七) 裝置樹的定義規則和獲取方法

概述 在Linux 2.6中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥著大量的垃圾程式碼,相當多數的程式碼只是在描述板級細節,而這些板級細節對於核心來講,不過是垃圾,如板上的platform裝置、resource、i2c_board_info、sp

Linux驅動開發08裝置樹】MPU6050驅動和i2c驅動

介紹 上一節在nanopi裝置樹的I2C節點下增加了一個MPU6050的子節點,並在sysfs中檢視到了該節點已經被正確解析,這一節我們來修改之前的MPU6050驅動,使之能夠匹配到我們的裝置樹節點,然後再分析裝置樹節點是如何載入到i2c總線上的。 MP

Linux驅動開發04裝置驅動和網路裝置驅動

介紹 因為塊裝置驅動和網路裝置驅動實際中用得較少,所以只給出驅動模板,我也沒有具體測試,等到實際用到是再研究吧,溜了溜了。 塊裝置驅動模板 struct xxx_dev { int size; struct request_q

嵌入式Linux裝置驅動開發筆記(一)

一、Linux裝置的分類 字元裝置、塊裝置、網路裝置,三種裝置之間的區別是資料的互動模式,分別為: 位元組流、資料塊、資料包。 二、VFS核心結構體 VFS核心結構體定義在”linux/fs.h”標頭檔案中。 1、struct inode結構體 記

嵌入式Linux裝置驅動開發(二)

上一篇中介紹到裝置驅動如何匹配裝置以及繫結裝置的,在Linux系統下進行註冊,這裡將繼續介紹probe函式的功能。 5、probe函式 Probe()函式必須驗證指定裝置的硬體是否真的存在,probe()可以使用裝置的資源,包括時鐘,platform_dat

嵌入式Linux USB驅動開發教你一步步編寫USB驅動程式

編寫與一個USB裝置驅動程式的方法和其他匯流排驅動方式類似,驅動程式把驅動程式物件註冊到USB子系統中,稍後再使用製造商和裝置標識來判斷是否安裝了硬體。當然,這些製造商和裝置標識需要我們編寫進USB 驅動程式中。 USB 驅動程式依然遵循裝置模型 —— 匯流排、裝置、驅動

Linux驅動開發主裝置號找驅動,次裝置號找裝置

一、引言   很久前接觸linux驅動就知道主裝置號找驅動,次裝置號找裝置。這句到底怎麼理解呢,如何在驅動中實現呢,在介紹該實現之前先看下核心中主次裝置號的管理:   二、Linux核心主次裝置號的管理   Linux的裝置管理是和檔案系統緊密結合的,各種裝置都以檔

Linux裝置驅動button按鍵驅動學習與小結

button按鍵驅動,相對於前面的LED驅動來說。增加了中斷處理以及阻塞與非阻塞方式等新知識點。 先上學習的驅動程式碼。 核心:linux3.0 板子:fl2440 /***************************************************

嵌入式Linux裝置驅動開發(一)

裝置驅動開發是Linux開發領域一個非常重要的部分,在Linux原始碼的85%都是驅動程式的程式碼。裝置驅動開發不僅需要了解硬體底層的知識,還需要擁有作業系統的背景。驅動程式追求的是高效,穩定,驅動程式發生的問題有可能直接導致整個系統的崩潰。 驅動程式不主動執

Linux驅動開發09裝置樹】nanopi的LED驅動

介紹 這節通過在nanopi的裝置樹中新增LED子節點,來實現一個LED驅動。由於linux核心支援LED驅動框架並且有通用的LED驅動,因此這裡只需按照驅動要求新增裝置樹節點就可以了,不用我們自己重寫LED驅動。這一節先在裝置樹中新增一個LED節點,重新編譯

linux驅動開發自動建立裝置節點

在有2.6系列版本中支援udev管理裝置檔案可以方便的建立裝置節點,不必使用mknod來建立,本文使用最小編碼來說明建立的幾個方法。 //主要用到的四個方法在linux/device.h定義: //建立類和釋放類的函式  建立成後將建立/sys/class/name資料

linux驅動開發蜂鳴器驅動源碼分析(一)

linux 蜂鳴器 驅動 蜂鳴器的驅動源碼在/driver/char/buzzer/x210-buzzer.c文件中,源碼如下#include <linux/module.h> #include <linux/kernel.h> #include <linux

linux驅動開發framebuffer應用編程實踐(一)

linux驅動開發之framebuffer驅動 1、framebuffer應用編程 (1)打開設備文件 (2)獲取設備信息 宏定義的命令在/linux/fb.h中 不可變信息FSCREENINFO,使用ioctl參數有FBIOGET_FSCREENINFO宏名,表示用ioctl從

視頻教程免費分享嵌入式stm32項目開發心率檢測儀的設計與實現

視頻教程免費分享:嵌入式stm32項目開發之心率檢測儀的設計與實現 本課程主要基於心率檢測儀的設計與實現講解STM32開發技術,STM32開發板廣泛應用於儀器儀表、家用電器、醫用設備、航空航天、專用設備的智能化管理、機器人及過程控制等領域,完成數據監控、數據處理、數據傳遞等功

嵌入式核心及驅動開發學習筆記(十) 非同步通訊+中斷實現讀取資料

對於linux一切都是檔案,驅動裝置在應用層也是以檔案的形式進行讀寫。之前學了阻塞、非阻塞、多路複用的方式讀裝置,它們都需要應用主動讀取。那麼應用層有沒有一種方式,當底層將資料準備好了,應用程式自動處理這些資料?通過非同步通訊可以實現,這有寫類似硬體層的中斷概念 驅動層(準備好了資料) --&g