【Jetson-nano的GPIO测试】

1 硬件原理图分析

在这里插入图片描述
上图为Jetson-nano对外扩展的40pin接口,这次使用15和33引脚,15对应PY.02,作为输出,33对应PE.06,作为输入

2 程序编写

编写设备树

可以在Jetson-nano上运行如下命令查看开发板上运行的设备树文件

cat /proc/device-tree/nvidia,dtsfilename

Jetson-nano设备树相关源码都在源码Linux_for_Tegra/source/public/hardware/nvidia目录下,GPIO主要涉及3个文件的更改

platform/t210/porg/kernel-dts/porg-platforms/tegra210-porg-pinmux-p3448-0000-b00.dtsi

lcd_te_py2 {
	nvidia,pins = "lcd_te_py2";
	nvidia,function = "rsvd1";
	nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
	nvidia,tristate = <TEGRA_PIN_ENABLE>;
	nvidia,enable-input = <TEGRA_PIN_DISABLE>;
};
pe6 {
	nvidia,pins = "pe6";
	nvidia,function = "rsvd0";
	nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
	nvidia,tristate = <TEGRA_PIN_ENABLE>;
	nvidia,enable-input = <TEGRA_PIN_DISABLE>;
};

platform/t210/porg/kernel-dts/porg-platforms/tegra210-porg-gpio-p3448-0000-b00.dtsi

gpio_default: default {
	gpio-input = <
		TEGRA_GPIO(A, 5)
		TEGRA_GPIO(X, 4)
		TEGRA_GPIO(X, 5)
		TEGRA_GPIO(X, 6)
		TEGRA_GPIO(Y, 1)
		TEGRA_GPIO(V, 1)
		TEGRA_GPIO(Z, 2)
		TEGRA_GPIO(H, 2)
		TEGRA_GPIO(H, 5)
		TEGRA_GPIO(H, 6)
		TEGRA_GPIO(I, 1)
		TEGRA_GPIO(CC, 4)
		TEGRA_GPIO(E, 6)  /* 新增 */
	>;
	gpio-output-low = <
		TEGRA_GPIO(S, 7)
		TEGRA_GPIO(T, 0)
		TEGRA_GPIO(Z, 3)
		TEGRA_GPIO(H, 0)
		TEGRA_GPIO(H, 3)
		TEGRA_GPIO(H, 4)
		TEGRA_GPIO(H, 7)
		TEGRA_GPIO(I, 0)
		TEGRA_GPIO(I, 2)
		TEGRA_GPIO(Y, 2)  /* 新增 */
	>;
	gpio-output-high = <
		TEGRA_GPIO(A, 6)
		TEGRA_GPIO(X, 3)
		TEGRA_GPIO(CC, 7)
	>;
};

platform/t210/porg/kernel-dts/tegra210-porg-p3448-common.dtsi

/* 根节点下新增gpio_test节点 */
gpio_test {
	compatible = "gpio-test";
	status = "okay";
	input-gpio = <&gpio TEGRA_GPIO(E, 6) GPIO_ACTIVE_HIGH>;
	output-gpio = <&gpio TEGRA_GPIO(Y, 2) GPIO_ACTIVE_LOW>;
};

编写驱动

// gpiobase.c
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>


#define GPIO_BASE_CNT		1		  	/* 设备号个数 */
#define GPIO_BASE_NAME		"gpiobase"	/* 名字 */

/* 设备结构体 */
struct gpiobase_dev{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
    struct device_node *nd; /* 设备节点 */
    int gpioIdInput;     	/* Input GPIO 编号 */
	int gpioIdOutput;   	/* Output GPIO 编号 */
};

struct gpiobase_dev gpioBaseDev;	/* 字符设备 */

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int gpiobase_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &gpioBaseDev; /* 设置私有数据 */
	return 0;
}

/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t gpiobase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    struct gpiobase_dev *dev = filp->private_data;

	if (cnt > 1) {
		printk("read length err!\r\n");
		return -EFAULT;
	}

    int gpioValue = gpio_get_value(dev->gpioIdInput);    /* 获取 GPIO 的值 */
    if (gpioValue < 0) {
        printk("get gpio value failed!\r\n");
		return -EFAULT;
    }

    char databuf = gpioValue ? 1 : 0;

	int ret = copy_to_user(buf, &databuf, cnt);
	if(ret < 0) {
		printk("kernel read failed!\r\n");
		return -EFAULT;
	}

	return 0;
}

/*
 * @description		: 向设备写数据 
 * @param - filp 	: 设备文件,表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - cnt 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t gpiobase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	struct gpiobase_dev *dev = filp->private_data;

	if (cnt > 1) {
		printk("write length err!\r\n");
		return -EFAULT;
	}

	unsigned char databuf;
	int ret = copy_from_user(&databuf, buf, cnt);
	if(ret < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}

	int writeValue = databuf ? 1 : 0;
	gpio_set_value(dev->gpioIdOutput, writeValue); /* 写 GPIO */

	return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int gpiobase_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* 设备操作函数 */
static struct file_operations gpiobase_fops = {
	.owner = THIS_MODULE,
	.open = gpiobase_open,
	.read = gpiobase_read,
	.write = gpiobase_write,
	.release = 	gpiobase_release,
};

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init gpiobase_init(void)
{
    /* 设置 GPIO */
    /* 获取设备节点: gpio_test */
    gpioBaseDev.nd = of_find_node_by_path("/gpio_test");
    if(gpioBaseDev.nd == NULL) {
        printk("gpioBaseDev node cant not found!\r\n");
        return -EINVAL;
    }
    else {
        printk("gpioBaseDev node has been found!\r\n");
    }

    /* 获取设备树中的 gpio 属性,得到 gpio 编号 */
    gpioBaseDev.gpioIdInput = of_get_named_gpio(gpioBaseDev.nd, "input-gpio", 0);
    if(gpioBaseDev.gpioIdInput < 0) {
        printk("can't get input-gpio");
        return -EINVAL;
    }
    printk("input-gpio num = %d\r\n", gpioBaseDev.gpioIdInput);

	gpioBaseDev.gpioIdOutput = of_get_named_gpio(gpioBaseDev.nd, "output-gpio", 0);
    if(gpioBaseDev.gpioIdOutput < 0) {
        printk("can't get output-gpio");
        return -EINVAL;
    }
    printk("output-gpio num = %d\r\n", gpioBaseDev.gpioIdOutput);

    /* 设置 GPIO */
    int ret = gpio_direction_input(gpioBaseDev.gpioIdInput);	/* input */
    if(ret < 0) {
        printk("can't set input gpio!\r\n");
    }
	ret = gpio_direction_output(gpioBaseDev.gpioIdOutput, 0);	/* output 输出低电平 */
	if(ret < 0) {
		printk("can't set output gpio!\r\n");
	}

	/* 注册字符设备驱动 */
	/* 创建设备号 */
	if (gpioBaseDev.major) {		/*  定义了设备号 */
		gpioBaseDev.devid = MKDEV(gpioBaseDev.major, 0);
		register_chrdev_region(gpioBaseDev.devid, GPIO_BASE_CNT, GPIO_BASE_NAME);
	} else {						/* 没有定义设备号 */
		alloc_chrdev_region(&gpioBaseDev.devid, 0, GPIO_BASE_CNT, GPIO_BASE_NAME);	/* 申请设备号 */
		gpioBaseDev.major = MAJOR(gpioBaseDev.devid);	/* 获取分配号的主设备号 */
		gpioBaseDev.minor = MINOR(gpioBaseDev.devid);	/* 获取分配号的次设备号 */
	}
	printk("gpioBaseDev major=%d,minor=%d\r\n",gpioBaseDev.major, gpioBaseDev.minor);	
	
	/* 初始化cdev */
	gpioBaseDev.cdev.owner = THIS_MODULE;
	cdev_init(&gpioBaseDev.cdev, &gpiobase_fops);
	
	/* 添加一个cdev */
	cdev_add(&gpioBaseDev.cdev, gpioBaseDev.devid, GPIO_BASE_CNT);

	/* 创建类 */
	gpioBaseDev.class = class_create(THIS_MODULE, GPIO_BASE_NAME);
	if (IS_ERR(gpioBaseDev.class)) {
		return PTR_ERR(gpioBaseDev.class);
	}

	/* 创建设备 */
	gpioBaseDev.device = device_create(gpioBaseDev.class, NULL, gpioBaseDev.devid, NULL, GPIO_BASE_NAME);
	if (IS_ERR(gpioBaseDev.device)) {
		return PTR_ERR(gpioBaseDev.device);
	}
	
	return 0;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit gpiobase_exit(void)
{
	/* 注销字符设备驱动 */
	cdev_del(&gpioBaseDev.cdev);	/* 删除cdev */
	unregister_chrdev_region(gpioBaseDev.devid, GPIO_BASE_CNT); /* 注销设备号 */

	device_destroy(gpioBaseDev.class, gpioBaseDev.devid);
	class_destroy(gpioBaseDev.class);
}

module_init(gpiobase_init);
module_exit(gpiobase_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("han");

Makefile

KERNELDIR := /home/e/jetson/output
CROSS_COMPILE=/home/e/jetson/source/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
CURRENT_PATH := $(shell pwd)
obj-m := gpiobase.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

编译命令

make ARCH=arm64

编写测试App

// gpiobaseApp.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

/*
 * @description		: main主程序
 * @param - argc 	: argv数组元素个数
 * @param - argv 	: 具体参数
 * @return 			: 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
	if(argc < 3){
		printf("Error Usage!\r\n");
		return -1;
	}
	if(strlen(argv[2]) > 1){
		printf("please input \'w\' or \'r\'\r\n");
		return -1;
	}

	char *filename = argv[1];

	/* 打开驱动文件 */
	int fd  = open(filename, O_RDWR);
	if(fd < 0){
		printf("Can't open file %s\r\n", filename);
		return -1;
	}

	int ret;
	char readbuf;
	if (argv[2][0] == 'r') { /* 从驱动文件读取数据 */
		ret = read(fd, &readbuf, 1);
		if (ret < 0) {
			printf("read file %s failed!\r\n", filename);
		}
		else {
			/*  读取成功,打印出读取成功的数据 */
			printf("read data:%d\r\n",readbuf);
		}
	}
	else if (argv[2][0] == 'w') {
 		/* 向设备驱动写数据 */
		if (argc < 4) {
			printf("Error Usage!\r\n");
			return -1;
		}
		char writebuf = atoi(argv[3]) ? 1 : 0;
		ret = write(fd, &writebuf, 1);
		if(ret < 0){
			printf("write file %s failed!\r\n", filename);
		}
	}
	else {
		printf("please input \'w\' or \'r\'\r\n");
		return -1;
	}

	/* 关闭设备 */
	ret = close(fd);
	if(ret < 0){
		printf("Can't close file %s\r\n", filename);
		return -1;
	}

	return 0;
}

编译命令

/home/e/jetson/source/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc gpiobaseApp.c -o gpiobaseApp

3 GPIO测试

编译内核

make ARCH=arm64 O=/home/e/jetson/output -j8

这一步主要是是为了编译修改的设备树文件。因为之前编译过,这次只修改了设备树文件,所以编译时间很短。编译完成后,成功生成的dtb文件就在/home/e/jetson/output/arch/arm64/boot/dts目录下。
可以使用dtc工具将dtb文件反编译为dts

/home/e/jetson/output/scripts/dtc/dtc -I dtb -O dts tegra210-p3448-0000-p3449-0000-b00.dtb > tegra210-p3448-0000-p3449-0000-b00.dts

/home/e/jetson/output/scripts/dtc/dtc为内核编译时生成的
当然也可以自行安装dtc工具

sudo apt-get install device-tree-compiler

反编译完成后打开生成的dts文件,查看新增的gpio_test节点是否存在,存在则说明修改成功。
接下来就将dtb文件拷贝到Jetson-nano的/boot目录下,替换原dtb文件,同时更改启动配置文件/boot/extlinux/extlinux.conf

TIMEOUT 30
DEFAULT primary

MENU TITLE L4T boot options

LABEL primary
      MENU LABEL primary kernel
      LINUX /boot/Image
      FDT /boot/tegra210-p3448-0000-p3449-0000-b00.dtb  # 这一行为新增,指定启动的设备树文件
      INITRD /boot/initrd
      APPEND ${cbootargs} quiet root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 console=ttyS0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0

然后重启,重启后查看设备树下是否有gpio_test节点

ls /proc/device-tree

然后将gpiobase.ko与gpiobaseApp拷贝到开发板上,用杜邦线连接开发板的15和33引脚

sudo insmod gpiobase.ko
sudo ./gpiobaseApp /dev/gpiobase w 0
sudo ./gpiobaseApp /dev/gpiobase r
sudo ./gpiobaseApp /dev/gpiobase w 1
sudo ./gpiobaseApp /dev/gpiobase r
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值