嵌入式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