字符驅動設備之按鍵驅動
- 硬件平臺:韋東山嵌入式Linxu開發板(S3C2440.v3)
- 軟件平臺:運行於VMware Workstation 12 Player下UbuntuLTS16.04_x64 系統
- 參考資料:《嵌入式Linux應用開發手冊》
- 開發環境:Linux 2.6.22.6 內核、arm-linux-gcc-3.4.5-glibc-2.3.6工具鏈
前言
在本片博文中,編寫按鍵驅動,通過查詢的方式得到按鍵信息。
一、框架搭建
1、編寫驅動程序
static int button_drv_open(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t button_drv_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
return 0;
}
2、告訴內核有這個驅動程序
2.1 構建file_operations()結構體
static struct file_operations button_drv_fops = {
.owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創建的__this_module變量 */
.open = button_drv_open,
.read = button_drv_read,
};
2.2 構建入口函數button_drv_init()
由內核自動分配主設備節點、自動創建設備節點
static struct class *buttondrv_class;
static struct class_device *buttondrv_class_dev;
int major;
static int button_drv_init(void)
{
major = register_chrdev(0, "button_drv", &button_drv_fops); // 註冊, 告訴內核
buttondrv_class = class_create(THIS_MODULE, "buttondrv");
buttondrv_class_dev = class_device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); /* /dev/button */
return 0;
}
2.3 構建出口函數button_drv_exit()
static void button_drv_exit(void)
{
unregister_chrdev(major, "button_drv"); // 卸載
class_device_unregister(buttondrv_class_dev);
class_destroy(buttondrv_class);
}
3、修飾入口、出口函數並添加模塊的許可證聲明
module_init(button_drv_init);
module_exit(button_drv_exit);
MODULE_LICENSE("GPL");
4、完整的框架代碼
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
static struct class *buttondrv_class;
static struct class_device *buttondrv_class_dev;
static int button_drv_open(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t button_drv_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
return 0;
}
static struct file_operations button_drv_fops = {
.owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創建的__this_module變量 */
.open = button_drv_open,
.read = button_drv_read,
};
int major;
static int button_drv_init(void)
{
major = register_chrdev(0, "button_drv", &button_drv_fops); // 註冊, 告訴內核
buttondrv_class = class_create(THIS_MODULE, "buttondrv");
buttondrv_class_dev = class_device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); /* /dev/button */
return 0;
}
static void button_drv_exit(void)
{
unregister_chrdev(major, "button_drv"); // 卸載
class_device_unregister(buttondrv_class_dev);
class_destroy(buttondrv_class);
}
module_init(button_drv_init);
module_exit(button_drv_exit);
MODULE_LICENSE("GPL");
5、修改Makefile文件
KERN_DIR = /work/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += button_drv.o
5、編譯驗證
- 通過服務器上傳到虛擬機,執行
make
- 通過NFS網絡系統,把
button_drv.ko
文件上傳到開發版的根文件系統 - 加載驅動,觀察功能是否可以正常實現。
二、硬件操作
1、查看原理圖
由原理圖可知
按鍵S2-EINT0-GPF0 按鍵S3-EINT2-GPF2
按鍵S4-EINT11-GPG3 按鍵S5-EINT19-GPG11
2、查看s3c2440芯片手冊
3、代碼編寫
3.1 代碼分佈
button_drv_init()
中完成地址映射button_drv_open()
中完成IO口設置爲輸入button_drv_read()
中完成按鍵信息的讀取
3.2 驅動代碼
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
int major;
static struct class *buttondrv_class;
static struct class_device *buttondrv_class_dev;
volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;
static int button_drv_open(struct inode *inode, struct file *file)
{
/* 配置GPF0,2爲輸入 */
*gpfcon &= ~((0x3<<(0*2)) | (0x3<<(2*2)));
/* 配置GPF3、11爲輸出 */
*gpfcon &= ~((0x3<<(3*2)) | (0x3<<(11*2)));
return 0;
}
static ssize_t button_drv_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
int key_values[4]; //0-gdf0, 1-gdf2, 2-gdg3, 3-gdg11
int value;
unsigned long ret = 0;
/* 如果字節數不對,則返回 */
if(nbytes != sizeof(key_values))
return -;EINVAL
/* 讀取GPF0,2 */
value = *gpfdat;;
key_values[0] = (value & (1<<0));
key_values[1] = (value & (1<<2));
/* 讀取GPG3,11 */
value = *gpgdat;
key_values[2] = (value & (1<<3));
key_values[3] = (value & (1<<11));
/* 數據給到用戶?*/
ret = copy_to_user(buf, key_values, sizeof(key_values));
if(ret < 0){
printk("func button_drv_read() err: copy_to_user");
return -EFAULT;
}
return sizeof(key_values);
}
static struct file_operations button_drv_fops = {
.owner = THIS_MODULE, /* 這是一個宏,推向編譯模塊時自動創建的__this_module變量 */
.open = button_drv_open,
.read = button_drv_read,
};
static int button_drv_init(void)
{
major = register_chrdev(0, "button_drv", &button_drv_fops); //註冊一個字符設備,名字爲button_drv
buttondrv_class = class_create(THIS_MODULE, "button_drv"); //創建一個類,名字爲buttond_rv(/class/button_drv)
buttondrv_class_dev = class_device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); //創建一個設備節點,名爲button(/dev/button)
/* 映射物理地址 */
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon + 1;
gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
gpgdat = gpfcon + 1;
return 0;
}
static void button_drv_exit(void)
{
unregister_chrdev(major, "button_drv"); // 卸載字符設備
class_device_unregister(buttondrv_class_dev); //刪除設備節點
class_destroy(buttondrv_class); //銷燬類
/* 取消映射 */
iounmap(gpfcon);
iounmap(gpgcon);
}
module_init(button_drv_init);
module_exit(button_drv_exit);
MODULE_LICENSE("GPL");
3.3 測試代碼
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
/* buttondrvtest on
* buttondrvtest off
*/
int main(int argc, char **argv)
{
int fd;
int ret = 0;
int key_value[4] = {0};
int count = 0;
fd = open("/dev/button", O_RDWR);
if (fd < 0){
printf("can't open!\n");
return -1;
}
while(1){
ret = read(fd, key_value, sizeof(key_value));
if(ret < 0){
printf(" func read() err\n");
}else{
if(!key_value[0] || !key_value[1] || !key_value[2] || !key_value[3]){
printf("%04d key pressed: %d %d %d %d\n",
count++, key_value[0], key_value[1], key_value[2]
, key_value[3]);
}
}
}
return ret;
}
三、燒寫驗證
- 通過服務器上傳到虛擬機,執行
make
。 - 通過NFS網絡系統,把
button_drv.ko
文件上傳到開發版的根文件系統。 - 加載驅動,觀察功能是否可以正常實現。
- 交叉編譯
buttondrvtest.c
文件,得到buttondrvtest.o
文件上傳到開發版的根文件系統。 - 執行
./buttondrvtest
。
可以看到正常運行。
四、查詢方式缺點
1、不確定性高
2、CPU佔用率大
可以看到,運行測試程序的時候CPU的佔用率到達了恐怖的99%,這是因爲測試程序一直在讀取鍵值。