本人使用的调试平台是荣品的rv1126开发板,最近在调试按键中断。经过查看原理图,发现竟然没有一个空闲的IO,所以使用UART1的RX作为按键中断引脚。

 

 

驱动部分:

        因为UART1原先已经在设备树中已经有了定义,需要将 kernel/arch/arm/boot/dts/rongpin/rv1126_1109_common.dtsi 中的uart1去掉

//&uart1 {
//	pinctrl-names = "default";
//	pinctrl-0 = <&uart1m0_xfer>;
//	status = "okay";
//};

并在kernel/arch/arm/boot/dts/rp-rv1126.dts 中的rp_gpio,加入该IO:

	rp_gpio {
		status = "okay";
		compatible = "rp_gpio";
        
        gpio0b7{
            gpio_num = <&gpio0 RK_PB7 IRQ_TYPE_EDGE_FALLING>; 
			gpio_function = <2>;
        };
        ......

        设备树修改完毕,下面开始编写代码。

        rv1126的gpio采用的是gpiolib架构,工程师可以不必查阅寄存器即可完成配置,在kernal/drivers/rongpin/rp_gpio.c中修改。

        1、修改probe:

        

static int rp_gpio_probe(struct platform_device *pdev) {
	struct device_node *np = pdev->dev.of_node;
	struct device_node *child_np;
	static struct proc_dir_entry *root_entry_gpio;
	enum of_gpio_flags  gpio_flags;
	int ret = 0;
	int gpio_cnt = 0;	
	char gpio_name_num[GPIO_NUM_MAX];
	int gpio_in_cnt = 0;


	gpio_data = devm_kzalloc(&pdev->dev, sizeof(struct rp_gpio_data),GFP_KERNEL);
	if (!gpio_data) {
		dev_err(&pdev->dev, "failed to allocate memory\n");
		return -ENOMEM;
	}

	gpio_data->gpio_dts_num = of_get_child_count(np);
        printk("rp_gpio prepare build %d gpio\n",gpio_data->gpio_dts_num);

    	if (gpio_data->gpio_dts_num == 0){
        	dev_info(&pdev->dev, "no gpio defined\n");
	}

	/* create node */
	root_entry_gpio = proc_mkdir("rp_gpio", NULL);
	
	for_each_child_of_node(np, child_np)
	{
		/* parse dts */
		gpio_data->rp_gpio_num[gpio_cnt].gpio_num = of_get_named_gpio_flags(child_np, "gpio_num", 0, &gpio_flags);
		if (!gpio_is_valid(gpio_data->rp_gpio_num[gpio_cnt].gpio_num)){
			return -1;
		}		

		gpio_data->rp_gpio_num[gpio_cnt].gpio_name = (char*)child_np -> name;
		gpio_data->rp_gpio_num[gpio_cnt].action = gpio_flags;
		gpio_data->rp_gpio_num[gpio_cnt].gpio_ctrl = gpio_cnt;
		of_property_read_u32(child_np, "gpio_function", &(gpio_data->rp_gpio_num[gpio_cnt].gpio_function));

		printk("rp_gpio request %s\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_name);

		
		switch(gpio_data->rp_gpio_num[gpio_cnt].gpio_function) {
			case GPIO_FUNCTION_INPUT :		/* init input gpio */
				ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");
				if (ret < 0)
				{
					printk("gpio%d request error\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
				}else{
					printk("success request gpio %d in\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					
					//gpio_set_value(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
					gpio_direction_input(gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					event_flag = gpio_flags;
					of_property_read_u32(child_np, "send_mode", &(gpio_data->rp_gpio_num[gpio_cnt].send_mode));
					of_property_read_u32(child_np, "gpio_event", &(gpio_data->rp_gpio_num[gpio_cnt].gpio_event));
					gpio_in_cnt++;
				}
				break;
#if 1
			case GPIO_FUNCTION_IRQ :		/* init input gpio */
				
				ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");
				if (ret < 0)
				{
					printk("gpio%d request error\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
				}else{
					printk("success request gpio %d irq\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					
					//gpio_set_value(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
					printk("gpio_to_irq(gpio_data->rp_gpio_num[gpio_cnt].gpio_num) = %d\n",gpio_to_irq(gpio_data->rp_gpio_num[gpio_cnt].gpio_num));
					irq_gpio = gpio_data->rp_gpio_num[gpio_cnt].gpio_num;
					gpio_direction_input(gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					ret  = request_irq(gpio_to_irq(gpio_data->rp_gpio_num[gpio_cnt].gpio_num),
								button_irq_handler,
								//IRQ_TYPE_EDGE_FALLING,
								IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
								"key_irq",
								NULL);

					
					//1、分配一个input_dev结构体
					buttons_dev = input_allocate_device();
					if(!buttons_dev)
					{
						printk("input_allocate_device error!\n");
						return -ENOMEM;
					}
					buttons_dev->name = "input_key";
					buttons_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
					input_set_capability(buttons_dev, EV_KEY, KEY_0);
				
					ret = input_register_device(buttons_dev);
					if (ret) {
						printk("register input device failed!\r\n");
						return ret;
					}

					//初始化定时器,用于按键消抖
					timer_setup(&buttons_timer,my_buttons_timer_function,0);
					buttons_timer.expires = jiffies + msecs_to_jiffies(20);
					add_timer(&buttons_timer);
				}
				break;
#endif
			case GPIO_FUNCTION_OUTPUT :		/* init output gpio */
				ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");
				if (ret < 0){
					printk("gpio%d request error\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					//return ret;
				}else{
					gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
					printk("success request gpio%d out\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
				}
				break;
				
			case GPIO_FUNCTION_FLASH :
				ret = gpio_request(gpio_data->rp_gpio_num[gpio_cnt].gpio_num, "gpio_num");
				if (ret < 0){
					printk("gpio%d request error\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					//return ret;
				}else{
					gpio_direction_output(gpio_data->rp_gpio_num[gpio_cnt].gpio_num,!gpio_data->rp_gpio_num[gpio_cnt].action);
					printk("success request gpio%d flash\n",gpio_data->rp_gpio_num[gpio_cnt].gpio_num);
					gpio_in_cnt++;

				}
				break;
		}
		
		sprintf(gpio_name_num,gpio_data->rp_gpio_num[gpio_cnt].gpio_name,gpio_cnt);
		proc_create(gpio_name_num, 0666 , root_entry_gpio , &gpio_ops);
		gpio_cnt++;
	}

	
	gpio_wq = create_singlethread_workqueue("gpio_wq");
	INIT_WORK(&gpio_work, gpio_work_func);
	
	platform_set_drvdata(pdev, gpio_data);	
	return 0;
}

        代码中的case GPIO_FUNCTION_IRQ是新增加的,主要是申请IO、设置IO方向、注册中断函数、配置输入子系统、初始化定时器。其中定时器是用来消抖的。按键信号通过输入子系统传递到应用程序。

        2、增加外部中断以及定时器服务函数。


unsigned irq_gpio;
static struct input_dev *buttons_dev;
static struct timer_list buttons_timer;

static irqreturn_t button_irq_handler(int irq, void *dev_id)
{
    mod_timer(&buttons_timer, jiffies+msecs_to_jiffies(20));
    return IRQ_HANDLED;
}


//定时器中断处理函数
static void my_buttons_timer_function(struct timer_list* list)
{
	int gpio_value = 0;
	gpio_value = gpio_get_value(irq_gpio);

	if(gpio_value == 0)
	{
		input_report_key(buttons_dev, KEY_0, 1);
		input_sync(buttons_dev);
	}
	if(gpio_value == 1)
	{
		input_report_key(buttons_dev, KEY_0, 0);
		input_sync(buttons_dev);
	}
}

        

应用程序:

        编写应用程序之前,先使用cat /proc/bus/input/devices 确定驱动使用了哪个输入子系统的event。

        应用程序比较简单。

        检测输入子系统信号采用一个线程,以下函数为线程函数:

        

void *scan_key_main(void *data)
{
    int fd = 0;
    struct input_event buttons_event;
    unsigned long cur_ms = 0;
    data = data;
    
    //fd = open("/dev/event0", O_RDWR | O_NONBLOCK);
    fd = open("/dev/input/event3", O_RDWR);
    if (fd < 0)
    {
        printf("can't open!\n");
    }
 
    while (1)
    {
        read(fd, &buttons_event, sizeof(struct input_event));
 
//        if(buttons_event.type == EV_SYN)
//            continue;
 
        cur_ms = (buttons_event.time.tv_sec * 1000) + (buttons_event.time.tv_usec/1000);
 
        //打印时间,事件类型,事件码,事件值
        printf("cur_ms:%ld type:0x%x code:%d value:%d\n",
            cur_ms,
            buttons_event.type,
            buttons_event.code,
            buttons_event.value);
    }
}

        如果在调试过程中出现问题,可以使用hexdump /dev/input/eventX命令辅助调试。

更多推荐