RV1126按键中断驱动和应用调试

发布时间 2023-07-05 17:14:08作者: 阿风小子
 本人使用的调试平台是荣品的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命令辅助调试。