##通过修改board.DTS,利用platform_device匹配其设备树下的节点实现操控对应的gpio

board.dts路径

/android/longan/device/config/chips/t527/configs/demo/linux-5.15
  • 1.

1.首先在board.dts文件内的&soc结构体设置自己要控制的gpio相关信息

gzj_led {
		compatible = "led_test";
		gpio-pins = <&pio PG 10 GPIO_ACTIVE_LOW>;
		status = "okay";
	};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

编写规则解析:

 gzj_led:设备树节点名,当设备树烧录到开发板内会可以在/sys/devices/platform/soc@3000000路径下看到

compatible:设置的“led_test”名称是为了在驱动代码中用到,是为了将平台设备与dts进行按照名称匹配

eg:
// 匹配设备树内的 compatible 文件名
struct of_device_id id = {
  .compatible = "led_test"
};

struct platform_driver drv = {
  .probe = led_probe,
  .remove = led_remove,
  .driver = {
        .of_match_table = &id,
        .owner = THIS_MODULE,
        .name  = "led_test",//此处不能省略,因为led_test名称需要和设备节点上的名称进行匹配才能进行
   }
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

gpio-pins = <&pio PG 10 GPIO_ACTIVE_LOW>;

设置pin引脚号及其功能

status = "okay";

打开本gpio引脚,相当于使能

注意:本设备树节点使用的gpio是PG10,因此在该board.dts中其它节点使用PG10的必须将其状态设置为disabled

以上步骤就配置好了一个简单的dts节点

2.将配置好的设备树烧录到开发板

在烧录之前,需要先将sdk文件进行编译(其中就包含了设备树的编译(具体看个人的makefile))

编译步骤

1.回到家目录(android)中,执行source ./build/envsetup.sh

2.make -j8

3.打包  pack

之后等待完成会在android/longan/out/t527_android13_demo_uart0.img看到这样的文件,表示成功

烧录到开发板步骤

1.运行usbdriver驱动为了使用usb转ttl进行烧写固件

2.利用PhoenixSuit V2.01文件里的里的工具里的PhoenixSuit软件进行烧录即可

3.插入usb之前一直按着fel按键,进行usb烧录,然后设备上电,松手即可进行烧录

至此已经将新的设备树烧录到开发板中了

3.编写驱动文件,将驱动文件烧录到开发板(代码水平较差,轻点喷)

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>  // 确保包含此头文件来完整定义 platform_driver 结构体
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/of_gpio.h>

//申请到的gpio号,之后想要对gpio的操作只需要操作此号即可
int gpioNum = 0;


int led_open(struct inode *node, struct file *f)
{
    int ret = gpio_request(gpioNum, "led");//需要判断申请gpio是否成功
    printk("gpionum = %d\n", gpioNum);
    if (ret!= 0) {
        printk("申请gpio失败\n");
        //如果gpio申请失败---查看board.dts里的配置是否有误
        return -1;
    }
    gpio_direction_output(gpioNum, 0);
    //设置gpio的功能为输出,电平为低电平
    return 0;
}

int led_close(struct inode *node, struct file *f)
{
    gpio_free(gpioNum);
    return 0;
}

ssize_t led_write(struct file *f, const char __user *buf, size_t s, loff_t *l)
{
    int value, ret;
    ret = copy_from_user(&value, buf, s);
    printk("当前value为:%d",value);
    printk("copy_from_user ret = %d\n", ret);
    gpio_set_value(gpioNum, value);
    return 0;
}


//通过此结构体可以提供用户层操作的接口
struct file_operations ops = {
  .owner = THIS_MODULE,
  .open = led_open,
  .release = led_close,
  .write = led_write
};


struct miscdevice misc = {
  .minor = 255,//表示此设备号由系统分配
  .name  = "led",//模块加载成功可以在开发板的/dev内找到led设备文件
  .fops = &ops
};

//当平台设备与设备树中的文件名匹配成功便会执行探测函数
int led_probe(struct platform_device *dev)
{
    int ret;
    printk("探测函数执行...\n");
    ret =  misc_register(&misc);
    if (ret == 0)
        printk("杂项注册成功\n");
    else
        printk("杂项注册失败\n");
    // 通过 gpio-pins 匹配board.dts 中设置的设备树引脚名
    //获取成功之后该gpio号就被保存到gpioNum中,就可以用gpio_request函数申请gpio
    gpioNum = of_get_named_gpio(dev->dev.of_node, "gpio-pins", 0);
    //通过在设备树中找gpio-pins到对应的gpio然后获取其设备节点信息,
    //0:表示要获取该属性中索引为 0 的 GPIO 值。如果"gpio-pins"属性包含多个 GPIO 编号,通过索引可以选择特定的一个。
    return 0;
}

int led_remove(struct platform_device *dev)
{
    //当移除模块时删除led设备文件
    misc_deregister(&misc);
    return 0;
}
// 匹配设备树内的 compatible 文件名
struct of_device_id id = {
  .compatible = "led_test"
};

struct platform_driver drv = {
  .probe = led_probe,
  .remove = led_remove,
  .driver = {
        .of_match_table = &id,
        .owner = THIS_MODULE,
        .name  = "led_test",//此处不能省略,因为led_test名称需要和设备节点上的名称进行匹配才能进行
   }
};

static int __init node_get_init(void)
{
    int ret = platform_driver_register(&drv);
    if (ret == 0)
        printk("平台设备总线 驱动端注册成功\n");
    else
        printk("平台设备总线 驱动端注册失败\n");
    return 0;
}


static void __exit  node_get_exit(void)
{
    platform_driver_unregister(&drv);
}

module_init(node_get_init);
module_exit(node_get_exit);
MODULE_LICENSE("GPL");
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.

编写完驱动代码在longan目录下./build.sh编译文件生产.ko文件

推送.ko文件到开发板中

加载驱动文件  insmod  ***.ko

4.通过应用层代码来检验是否成功

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
	int fd = open("/dev/led",O_RDWR);
	perror("open");
	int data = 1;
	while(1){
		scanf("%d",&data);
		write(fd,&data,sizeof(data));
	}
	return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

注意:这段代码要使用交叉编译进行编译,编译后生产的文件就可以推送到开发板中了

推送到开发板要注意,修改其文件的权限,因为没有执行权限,./***运行即可进行验证