由於原理和之前類似,這裏不再累述,直接看代碼即可。
按鍵驅動程序:
#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 <linux/module.h>
#include <linux/device.h> //class_create
static struct class *keys_drv_class;
static struct device *keys_drv_device;
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
int major;
static int keys_drv_open(struct inode * inode, struct file * filp)
{
/* K1 ---- EINT1,K2 ---- EINT4,K3 ---- EINT2,K4 ---- EINT0
* 配置GPF1、GPF4、GPF2、GPF0爲輸入引腳
*/
*gpfcon &= ~((0x3 << (1*2)) | (0x3 << (4*2)) | (0x3 << (2*2)) | (0x3 << (0*2)));
return 0;
}
static ssize_t keys_drv_read(struct file *file, char __user *user, size_t size,loff_t *ppos)
{
unsigned char key_vals[4];
unsigned long val; //用於接收按鍵值
if (size != sizeof(key_vals))
return -EINVAL;
/* K1 ---- EINT1,K2 ---- EINT4,K3 ---- EINT2,K4 ---- EINT0
* 讀GPF1、GPF4、GPF2、GPF0引腳值
*/
val = *gpfdat; //從硬件獲取按鍵值
key_vals[0] = (val & (1<<1)) ? 1 : 0;
key_vals[1] = (val & (1<<4)) ? 1 : 0;
key_vals[2] = (val & (1<<2)) ? 1 : 0;
key_vals[3] = (val & (1<<0)) ? 1 : 0;
/* 讀出值後,將數據傳給應用程序 */
copy_to_user(user, key_vals, sizeof(key_vals)); //sizeof函數裏邊傳入數組名,計算的結果就是這整個數組的大小
return sizeof(key_vals);
}
/* File operations struct for character device */
static const struct file_operations keys_drv_fops = {
.owner = THIS_MODULE,
.open = keys_drv_open,
.read = keys_drv_read,
};
/* 驅動入口函數 */
static int keys_drv_init(void)
{
/* 主設備號設置爲0表示由系統自動分配主設備號 */
major = register_chrdev(0, "second_drv", &keys_drv_fops);
/* 創建keys_drv類 */
keys_drv_class = class_create(THIS_MODULE, "keys_drv");
/* 在keys_drv類下創建buttons設備,供應用程序打開設備*/
keys_drv_device = device_create(keys_drv_class, NULL, MKDEV(major, 0), NULL, "buttons");
/* 將物理地址映射爲虛擬地址 */
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);//這裏就把從0x56000050到0x5600005C這16字節地址映射完了,其中gpfcon爲映射首地址
gpfdat = gpfcon + 1; //根據gpfcon算出gpfdat的虛擬地址,注意這裏“+1”是4字節地址對齊,因爲gpfcon是unsigned long型指針
return 0;
}
/* 驅動出口函數 */
static void keys_drv_exit(void)
{
unregister_chrdev(major, "keys_drv");
device_unregister(keys_drv_device); //卸載類下的設備
class_destroy(keys_drv_class); //卸載類
iounmap(gpfcon); //解除映射
}
module_init(keys_drv_init); //用於修飾入口函數
module_exit(keys_drv_exit); //用於修飾出口函數
MODULE_AUTHOR("CLBIAO");
MODULE_DESCRIPTION("Just for Demon");
MODULE_LICENSE("GPL"); //遵循GPL協議
測試應用程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* second_test
*/
int main(int argc ,char *argv[])
{
int fd;
unsigned char key_vals[4];
int cnt = 0; //用於測試按下一次按鍵程序能夠讀多少次按鍵值
fd = open("/dev/buttons",O_RDWR);
if (fd < 0)
{
printf("open error\n");
}
/* 查詢式不停地讀按鍵值 */
while(1)
{
read(fd,key_vals,sizeof(key_vals));
if(!key_vals[0] || !key_vals[1] || (!key_vals[2]) || (!key_vals[3]))
{
printf("%04d key pressed: %d %d %d %d\n",cnt++,key_vals[0],key_vals[1],key_vals[2],key_vals[3]);
}
}
return 0;
}
運行結果:按下一次按鍵,會出現連續打印一連串按鍵值相同的情況,這是因爲按下一次按鍵過程中,我們的反應相對於CPU來說實在是太慢了,按一次應用程序已經執行好多好多次while(1)裏面的內容了。
如何實現使按下一次按鍵打印一條信息?
在驅動程序裏面添加一個等待按鍵釋放之後再賦值按鍵值。
具體實現:在second_drv_read()函數的“val = *gpfdat;” 這一句下面添加下面語句即可。
unsigned long val1;
val1 = val;
while( (!(val&(1<<1))) || (!(val&(1<<4))) || (!(val&(1<<2))) || (!(val&(1<<0))))//等待按鍵釋放
{
val1 = *gpbdat;
}
搞定