哈工大嵌入式软硬件设计上机实验教程(三)-按键驱动实验

前言

这是哈工大嵌入式软硬件设计上机实验教程第三期按键驱动实验。

DC

点击这里查看往期教程

哈工大嵌入式软硬件设计上机实验教程(一)-ARM裸机程序开发实验-按键控制LED
哈工大嵌入式软硬件设计上机实验教程(二)-U-Boot、Linux 内核的系统移植实验
哈工大嵌入式软硬件设计上机实验教程(三)-按键驱动实验

Linux下的按键驱动实验

1.添加驱动节点及配置

在linux-smart210/drivers/char/目录下的kconfig文件中,添加本实验文件节点,例如:

config embedded_test
	tristate "Mini210 test"
	depends on CPU_S5PV210
	help
	  demo

Makefile中添加

obj-$(CONFIG_embedded_test)	+= embedded_test.o

2.编写驱动程序

建议辅助开发板pcb查找对应管脚gpio号。
可能需要用到的gpio相关函数:

void gpio_free(unsigned gpio) //释放gpio 资源 int
gpio_direction_input(unsigned gpio) //设置gpio 为输入 int
gpio_direction_output(unsigned gpio, int value) //设置gpio 为输出 void
gpio_set_value(unsigned gpio, int value) //设置gpio的值 int
gpio_set_debounce(unsigned gpio, unsigned debounce)
//设置gpio的消抖时间,主要用于按键消抖 int gpio_to_irq(unsigned gpio) //获取gpio对应的中断线路
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long
flags, const char * name, void * dev) //gpio中断,当产生中断时调用handle函数 int
gpio_request(unsigned gpio, const char *label) //根据gpio
number申请gpio资源,label为gpio名称 int s3c_gpio_cfgpin (unsigned int pin,
unsigned int config) //设置GPIO的一个引脚的功能。

示例代码如下

代码命名为embedded_test.c

这里需要注意一下,这个代码是老师给的例程,功能是打印按键和跑马灯,需要自己写一个按键控制LED的驱动。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/ioctl.h>
#include <mach/map.h>
#include <mach/gpio.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
struct button_desc {
	int gpio;
	int number;
	char *name;	
	struct timer_list timer;
};
static struct button_desc buttons[] = {{ S5PV210_GPH2(0), 0, "KEY0" },   { S5PV210_GPH2(1), 1, "KEY1" },   { S5PV210_GPH2(2), 2, "KEY2" },  { S5PV210_GPH2(3), 3, "KEY3" }};//按键gpio号
static int led_gpios[] = {S5PV210_GPJ2(0),S5PV210_GPJ2(1),S5PV210_GPJ2(2),S5PV210_GPJ2(3)};//LED灯gpio号
static volatile char key_values[] = {'0', '0', '0', '0'};//按键gpio值
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);//创建一个等待队列头
static volatile int ev_press = 0;//事件触发
static void mini210_buttons_timer(unsigned long _data){
	struct button_desc *bdata = (struct button_desc *)_data;
	unsigned tmp = gpio_get_value(bdata->gpio);//获取gpio值
	int down = !tmp;
	gpio_set_value(led_gpios[bdata->number], !down);//设置gpio值
	if (down != (key_values[bdata->number] & 1)) {
		key_values[bdata->number] = '0' + down;
		ev_press = 1;
		wake_up_interruptible(&button_waitq);
	}
}
static irqreturn_t button_interrupt(int irq, void *dev_id){
	struct button_desc *bdata = (struct button_desc *)dev_id;
	mod_timer(&bdata->timer, jiffies + msecs_to_jiffies(40));
	return IRQ_HANDLED;
}
static void mini210_buttons_open(){
	int irq,i,err = 0;
	for (i = 0; i < 4; i++) {
		setup_timer(&buttons[i].timer, mini210_buttons_timer,(unsigned long)&buttons[i]);
		irq = gpio_to_irq(buttons[i].gpio);//获取gpio对应的中断
		err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_BOTH, buttons[i].name, (void *)&buttons[i]);//中断申请
	}
	ev_press = 1;
}
static void mini210_buttons_close(){
	int irq, i;
	for (i = 0; i < 4; i++) {
		irq = gpio_to_irq(buttons[i].gpio);//获取gpio对应的中断
		free_irq(irq, (void *)&buttons[i]);//释放中断
		del_timer_sync(&buttons[i].timer);
	}
}
static struct file_operations dev_fops = {//文件操作集
	.open		= mini210_buttons_open,
	.release	= mini210_buttons_close, 
};
static struct miscdevice misc = {//混杂设别节点
	.minor		= MISC_DYNAMIC_MINOR,//设备号
	.name		= "buttons",//混杂设备名称
	.fops		= &dev_fops,//文件操作集
};
static int __init button_dev_init(void){//初始化
	int ret,i;
	for (i = 0; i < 4; i++) {
		ret = gpio_request(led_gpios[i], "LED");//申请gpio资源
		s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);//设置gpio方向
		gpio_set_value(led_gpios[i], 1);//设置gpio值
	}
	ret = misc_register(&misc);//注册混杂设备节点
	return ret;
}
static void __exit button_dev_exit(void){//退出
	int i;
	for (i = 0; i < 4; i++)  gpio_free(led_gpios[i]);//释放gpio
	misc_deregister(&misc);//注销混杂设备节点
}
module_init(button_dev_init);//初始化
module_exit(button_dev_exit);//退出
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");

以下代码可以实现按键控制LED,用以替代上面代码,命名不变

#include <linux/module.h>
#include <linux/init.h>

#include <linux/miscdevice.h>	/***字符设备当中主设备号为10的一类设备称为混杂设备miscdevice***/
#include <linux/fs.h>	/***file_operations***/

#include <mach/gpio.h>	/***gpio定义***/

#include <linux/irq.h>	/***IRQ_TYPE_EDGE_BOTH, IRQ_HANDLED***/
#include <linux/interrupt.h>	/***request_irq, free_irq***/

#include <linux/sched.h> /***timer_list, mod_timer, setup_timer, del_timer_sync |  
			     DECLARE_WAIT_QUEUE_HEAD, wake_up_interruptible, wait_event_interruptible***/

#include <asm/uaccess.h> /***copy_to_user***/

static volatile int ev_press = 0;//事件触发
static volatile char key_values[] = {'0', '0', '0', '0'};//按键缺省值
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);//创建一个等待队列

static int led_gpios[] = {S5PV210_GPJ2(0),S5PV210_GPJ2(1),S5PV210_GPJ2(2),S5PV210_GPJ2(3)};//LED灯gpio号
//按键信息
struct button_desc {
	int gpio;
	int number;
	char *name;	
	struct timer_list timer;	//定时器变量
};
static struct button_desc buttons[] = {{ S5PV210_GPH2(0), 0, "KEY0" },   { S5PV210_GPH2(1), 1, "KEY1" },   { S5PV210_GPH2(2), 2, "KEY2" },  { S5PV210_GPH2(3), 3, "KEY3" }};//按键gpio号

//中断处理程序,不能使用会引起阻塞和调度的函数.
static irqreturn_t button_isr(int irqno, void *dev_id){

	struct button_desc *bdata = (struct button_desc *)dev_id;
	mod_timer(&bdata->timer, jiffies + msecs_to_jiffies(40));	//启动定时器,超时时间40ms
//	printk("KEY %d: pressed!\n", bdata->number);

	return IRQ_HANDLED;
}

//超时操作函数
static void buttons_timer(unsigned long _data){
	struct button_desc *bdata = (struct button_desc *)_data;
	unsigned tmp = gpio_get_value(bdata->gpio);//获取按键gpio值
	int down = !tmp;
	gpio_set_value(led_gpios[bdata->number], !down);//设置LED管脚电平
	printk("KEY %d: %08x\n", bdata->number, down);

	/***现在只有驱动程序知道按键的状态,怎么让应用程序也知道呢?当应用程序向驱动程序发出读请求时,
		将启动一个wait_event进入休眠,在驱动程序检测到按键动作之后将其唤醒wake_up.***/
	if (down != (key_values[bdata->number] & 1)) {//检测按键电平是否翻转
		key_values[bdata->number] = '0' + down;
		ev_press = 1;	//唤醒条件
		wake_up_interruptible(&button_waitq); //唤醒等待队列,但只是加入到cpu调度中,并不一定会马上执行.
	}


}

static int button_open(struct inode *node, struct file *filp){
	int irq,i,err = 0;
	for (i = 0; i < 4; i++) {
		setup_timer(&buttons[i].timer, buttons_timer,(unsigned long)&buttons[i]); //初始化和注册定时器,buttons_timer为超时处理函数
		irq = gpio_to_irq(buttons[i].gpio);//获取按键对应的中断号
		/***注册中断int request_irq(unsigned int irq, void (*handler)(int, void*, struct pt_regs *), 
						unsigned long flags, const char *devname, void *dev_id); 
		: dev_id是传给ISR的参数,IRQ_TYPE_EDGE_BOTH双边沿触发***/
		err = request_irq(irq,button_isr,IRQ_TYPE_EDGE_BOTH,buttons[i].name, (void *)&buttons[i]);//申请中断	
	}
	printk("button_open!\n");
	return err;
}

static int button_close(struct inode *node, struct file *filp){
	int irq, i;
	for (i = 0; i < 4; i++) {
		irq = gpio_to_irq(buttons[i].gpio);//获取按键对应的中断
		free_irq(irq, (void *)&buttons[i]);//释放中断
		del_timer_sync(&buttons[i].timer);//删除定时器
	}
	return 0;
}

static int button_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
	unsigned long err;

	if (!ev_press) {
		if (filp->f_flags & O_NONBLOCK)
			return -EAGAIN;
		else
			wait_event_interruptible(button_waitq, ev_press); //增加一个阻塞事件,唤醒条件ev_press==1
	}

	ev_press = 0;

	err = copy_to_user((void *)buff, (const void *)(&key_values), min(sizeof(key_values), count));//从内核空间向用户空间拷贝数据

	return err ? -EFAULT : min(sizeof(key_values), count);
}

static struct file_operations dev_fops = {
	.open = button_open,
	.release = button_close,
	.read = button_read,
};

static struct miscdevice misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "buttons",
	.fops = &dev_fops,
//剩余的miscdevice结构体成员由内核自动完成初始化工作
};

void led_hw_init(void){
	int i,ret;
	for (i = 0; i < 4; i++) {
		ret = gpio_request(led_gpios[i], "LED");//申请gpio资源
		s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);//设置gpio方向
		gpio_set_value(led_gpios[i], 1);//设置gpio值
	}
}	

//注册设备int misc_register(struct miscdevice *misc);
static int __init button_dev_init(void){
	
	led_hw_init();

	return misc_register(&misc);
	printk("button_init\n");
}

//注销设备
static void __exit button_dev_exit(void){
	int i;
	for (i = 0; i < 4; i++)  gpio_free(led_gpios[i]);//释放LED gpio
	misc_deregister(&misc);
	printk("button_exit\n");
}




module_init(button_dev_init);
module_exit(button_dev_exit);

MODULE_LICENSE("GPL");	//此处不声明会报错: unknown symbal gpio_free

3.编辑内核选项

命令行输入

make menuconfig

进入内核编辑界面,进入

Device Drivers/Character devices

将Mini210 test配置成M(module),同时将其上下的LED和Bottom也设置成M。
在这里插入图片描述

4.编译内核

命令行输入

sudo make uImage

这个环节时间可能有一点久。

5.编译驱动

命令行输入:

sudo make modules
make modules_install INSTALL_MOD_PATH=./modules

6.替换驱动和内核

将2G大小的文件系统盘挂载到/mnt

sudo mount /dev/sdb2 /mnt

删除sd卡中文件系统下的原有驱动文件,替换为新编译出的驱动文件。

# sudo rm -rf  /media/(文件系统分区挂载点) /lib/modules/3.0.8-FriendlyARM/*

sudo rm -rf  /mnt/lib/modules/3.0.8-FriendlyARM/*

# sudo cp -r modules/lib/modules/3.0.8-FriendlyARM/*  /media/(文件系统分区挂载点)/lib/modules/3.0.8-FriendlyARM/

sudo cp -r modules/lib/modules/3.0.8-FriendlyARM/*  /mnt/lib/modules/3.0.8-FriendlyARM/

卸载挂载

sudo umount /mnt -l

将100M大小的内核分区挂载到/mnt

sudo mount /dev/sdb1 /mnt

并用新生成的内核替换旧内核。

sudo rm -rf  /mnt/uImage
sudo cp arch/arm/boot/uImage  /mnt

卸载挂载

sudo umount /mnt -l

7.编写驱动启动代码。

将下面文件保存为test_key.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/time.h>
int main()
{
	int i=0,ret=0,fd=-1;
	static char buff[8]={'0','0','0','0'};
	fd=open("/dev/buttons",0);
	while(1) {
		ret = read(fd, &buff, sizeof(buff));
	}
	close(fd);
	return 0;
}

8.编译启动程序

采用交叉编译链编译启动程序,并将可执行文件拷贝到文件系统分区中。

arm-linux-gcc test_key.c -o button

将2G大小的文件系统盘挂载到/mnt

sudo mount /dev/sdb2 /mnt
sudo cp button  /mnt

卸载挂载

sudo umount /mnt -l

9.开机并加载驱动

命令行输入

modprobe embedded_test

执行可执行文件

./button

此时经过测试可以实现按键控制对应的led灯。

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
嵌入开发环境搭建可以使用eclipse作为IDE。以下是嵌入eclipse的搭建步骤: 1. 下载eclipse:首先,你需要从eclipse官方网站下载适用于嵌入开发的eclipse版本。根据你的操作系统选择合适的版本进行下载。 2. 安装eclipse:下载完成后,解压缩文件并将其安装到你的计算机上。根据操作系统的不同,安装步骤可能会有所不同。 3. 安装插件:为了进行嵌入开发,你需要安装适用于嵌入开发的插件。常用的插件有CDT(C/C++开发工具)和GNU ARM插件。你可以通过eclipse的插件管理器来安装这些插件。 4. 配置编译器:在eclipse中,你需要配置编译器以便进行编译和调试。根据你使用的嵌入平台和编译器,配置方法可能会有所不同。通常,你需要指定编译器的路径和相关参数。 5. 创建项目:在eclipse中,你可以创建一个新的嵌入项目。选择合适的项目模板,并根据你的需求进行配置。 6. 编写代码:在项目中,你可以编写嵌入代码。使用eclipse的编辑器来编写代码,并利用插件提供的功能进行代码补全、调试等操作。 7. 编译和调试:使用eclipse的编译功能来编译你的代码。如果配置正确,你可以使用eclipse的调试功能来调试你的嵌入应用程序。 8. 上传和运行:将编译后的代码上传到你的嵌入设备上,并运行你的应用程序。 以上是嵌入eclipse的搭建步骤。希望对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值