使用gpio中断开关灯 -新人小白

1、修改设备树
位置/home/milk/duo-buildroot-sdk-develop/build/boards/cv180x/cv1800b_milkv_duo_sd/dts_riscv

	led1{
		compatible = "led1";
		gpio = <&porta 15 GPIO_ACTIVE_LOW>;
	};	
	led_key{
		compatible = "key1";
		gpio = <&porta 14 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

2、编译设备树
在/home/milk/duo-buildroot-sdk-develop下

source   build/cvisetup.sh
defconfig cv1800b_milkv_duo_sd
build_uboot

编译完成后将新的cv1800b_milkv_duo_sd.dtb文件替换件SD卡具体操作
或者新生成镜像烧录到SD卡
使用ls /sys/firmware/devicetree/base/
查看一下设备树是否加载
3.编写platform平台总线driver

可以不使用platform,直接在init操作
代码如下

#include "linux/module.h"
#include "linux/init.h"
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>

#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/timer.h>



struct device_node *key_node;//获取设备树节点
struct device_node *led_node;//获取设备树节点


int led_gpio = 0;
int key_gpio = 0;

int irq;
int value = 0;

struct timer_list mytime;


void mytimer_func(struct timer_list *timer)
{   
    value=!value;
    if(led_gpio != 0)
        gpio_set_value(led_gpio,value);
}
//需要设置 hz
static irqreturn_t key_handler(int irq,void *args)
{
    printk("key_handler");
    mod_timer(&mytime,jiffies +2);//20ms消抖
    return IRQ_HANDLED;
}
int led_probe(struct platform_device *pdev)
{
    int ret;
    mytime.expires = jiffies+2;
    timer_setup(&mytime,mytimer_func,0);
    add_timer(&mytime);

    key_node = of_find_node_by_path("/led_key");
    led_node = of_find_node_by_path("/led1");
    if(key_node == NULL|| led_node == NULL)
    {
        printk("node error\n");
        return -1;
    }
    key_gpio = of_get_named_gpio(key_node,"gpio",0);//
    led_gpio = of_get_named_gpio(led_node,"gpio",0);//
    if(key_gpio < 0 || led_gpio < 0)
    {
        printk("gpio not found\n");
        return -1;
    }
    printk("key_gpio = %d led_gpio = %d\n",key_gpio,led_gpio);

    gpio_request(key_gpio,NULL);
    gpio_direction_input(key_gpio);

    gpio_request(led_gpio,NULL);
    gpio_direction_output(led_gpio,0);
    
    irq = gpio_to_irq(key_gpio);//方法一·获取中断号
    printk("irq: %d\n", irq);
    ret = request_irq(irq,key_handler,IRQF_TRIGGER_RISING,"led_key",NULL);
    if(ret < 0)
    {
        printk("request_irq fail\n");
        return -1;
    }
     return 0;
}
int led_remove(struct platform_device *pdev)
{
    gpio_free(led_gpio);
    gpio_free(key_gpio);
    printk("led_remove\n");
    return 0;
}
const struct platform_device_id led_idt_able={
    .name = "led_test"
};
//如果有设备书节点 优先匹配设备属下的节点名称
const struct of_device_id of_led_table[]={
    {.compatible = "key1"},
    {}
};
struct platform_driver led_driver={
    .probe = led_probe,
    .remove = led_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "123",
        .of_match_table = of_led_table
    },
	.id_table = &led_idt_able
};
static int __init led_driver_init(void)//j加载
{
    int ret;
    ret = platform_driver_register(&led_driver);
    if(ret < 0)
    {
        printk("led_driver_register fail\n");
        return ret;
    }
    printk("led_driver_register ok\n");
    return 0;
}

static void led_driver_exit(void)//卸载
{
    printk("byb byb world\n");
    del_timer(&mytime);
    free_irq(irq,NULL);
    platform_driver_unregister(&led_driver);
}

module_init(led_driver_init);//绑定模块加载
module_exit(led_driver_exit);//绑定模块卸载

MODULE_LICENSE("GPL");
MODULE_AUTHOR("lin");
MODULE_VERSION("v1.0");

4、编译

obj-m +=led_key.o

KDIR:=/home/milk/duo-buildroot-sdk-develop/linux_5.10/build/cv1800b_milkv_duo_sd
PWD?=$(shell pwd)
all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	rm -f *.ko *.o *.mod *.symvers *.order *.mod.c *.cmd

5、下载到板子
使用scp或者nfs服务下载到板子,使用insmod安装.ko, lsmod 可以查看模块

scp led_key.ko root@192.168.42.1:/root/
3 Likes

Excellent! Thank you very much!

I went to the duo-buildroot-sdk/linux_5.10/drivers/soc/cvitek folder and added

obj-m += led_key/led_key.o

to the Makefile. Made the led_key folder and put the C code into the led_key.c file. Recompiled:

build_uboot
build_kernel

and then found the led_key.ko file and transferred it to the Duo. Also, the new fip.bin and rawimages/boot.sd files were transferred to the boot partition (I mounted the boot partition as /media/boot on the Duo to be able to quickly put these files).

reboot

Before inserting the module, interrupts were only enabled on gpioa13 (SD card removal interrupt):

[root@milkv-duo]~# devmem 0x03020030
0x00002000

Inserting the module:

[root@milkv-duo]~# insmod led_key.ko
[root@milkv-duo]~# dmesg
...
[  169.054842] led_driver_register ok
[  169.185801] key_handler

Checking interrupts:

[root@milkv-duo]~# cat /proc/interrupts
...
48:          2  gpio-dwapb  14  led_key

Now I can see that interrupts became enabled on both gpioa13 (SD card removal) and gpioa14 (my key):

[root@milkv-duo]~# devmem 0x03020030
0x00006000
1 Like

SOLVED! See another comment below.

Maybe you can help a little bit with this case:

I am going to set up a touch screen based on the FT6232 chip. This chip is connected to the board via i2c0 and is already supported by Linux drivers.

gpioa15 is used by reset pin of the chip. I2C connection works just fine, the controller is detected under address 0x38.

[root@milkv-duo]~# i2cdetect -ry 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- 38 -- -- -- -- -- -- --    
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- 56 -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Now I am adding the description of the device into the dts file:

/* does not work, do not copy-paste */

/* EDIT: works! */
&i2c0 {
status = “okay”;
clock-frequency = <100000>;

    touchscreen@38 {
        compatible = "focaltech,ft6236";
        reg = <0x38>;

        interrupt-parent = <&porta>;
        interrupts = <14 2>;

        touchscreen-size-x = <480>;
        touchscreen-size-y = <320>;
        status = "okay";
        reset-gpios = <&porta 15  GPIO_ACTIVE_LOW>;
    };
};

gpioa14 is used as an interrupt input from the touchscreen controller. The defconfig file also has touchscreen driver enabled:

# Touch
CONFIG_INPUT=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_EDT_FT5X06=y

Once the kernel, the uboot and the fip are recompiled, uploaded to the board and the board is restarted, the touchscreen driver now occupies the I2C address 0x38:

[root@milkv-duo]~# i2cdetect -ry 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- 56 -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

The controller chip is now initialized and when I touch the screen I can see short pulses at gpioa14 with an oscilloscope.

Now I check the interrupt layout:

[root@milkv-duo]~# cat /proc/interrupts
 ...
 46:          0  T-Head PLIC  14  ft6236
 47:          0  gpio-dwapb  13  cd-gpio-irq

I can see that interrupt 46 is allocated to my I2C touchscreen controller chip (should be at gpioa14) and interrupt 47 corresponds to the SD card removal pin (gpioa13).

It looks normal now.

Next, I run the evtest program:

[root@milkv-duo]~# evtest /dev/input/event0
Input driver version is 1.0.1
Input device ID: bus 0x18 vendor 0x0 product 0x0 version 0x0
Input device name: "generic ft5x06 (11)"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 330 (BTN_TOUCH)
...
...
Properties:
  Property type 1 (INPUT_PROP_DIRECT)
Testing ... (interrupt to exit)

The problem begins now. When I touch the screen, the evtest program does not show any event, so it looks like the interrupt from gpioa14 is not processed by the driver.

Checking the interrupt enable register for gpioa:

[root@milkv-duo]~# devmem 0x03020030
0x00002000

I see here that gpioa14 was not configured as an interrupt source in the INTEN register, so that means that the gpio-dwapb driver does not configure it!

gpioa14 is configure as GPIO, of course:

[root@milkv-duo]~# devmem 0x0300101c 
0x00000003

Question: is there a way to make the interrupt work in a standard way, i.e. via the interrupt-partent=<> and interrupts=<> lines in the dts file? What I mean is maybe my syntax is wrong or I have forgotten to add some important components which are requited by the standard ft6236 driver to correctly ask the gpio-dwapb driver configure the hardware?

I am sorry for the long post. I just wanted to make it easily understandable for the subscibers.

Thank you,

1 Like

SOLUTION:

I traced (virtually) into the irq.c driver and found a suspicious line:

} while (p && of_get_property(p, "#interrupt-cells", NULL) == NULL);

I modified the cv180x_base_riscv.dtsi file, adding #interrupt-cells=<2> to the porta branch, as follows:

    gpio0: gpio@03020000 {
            porta: gpio-controller@0 {
                    interrupt-controller;
                    interrupts = <60 IRQ_TYPE_LEVEL_HIGH>;
                    interrupt-parent = <&plic0>;
                    #interrupt-cells = <2>;
            };
    };

Now Linux is passing the interrupt to the gpio driver, not to the top level interrupt controller driver, and my touchscreen is working correctly!

Checking the interrupt enable register for porta:

[root@milkv-duo]~# devmem 0x03020030
0x00006000

Bit 14 is now set to “1”, which means the hardware is configured.

Checking the interrupt status, I noticed that the ft6236 interrupt is taken care by the gpio-dwapb driver:

[root@milkv-duo]~# cat /proc/interrupts
...
 46:          0  gpio-dwapb  13  cd-gpio-irq
 48:       3008  gpio-dwapb  14  ft6236

Now the capacitive touch screen works with LVGL:

https://www.youtube.com/watch?v=CalmfGGC-Ic

Thank you @haha for inspiring me find this problem!