那么,三种GPIO驱动的方式就显而易见了,分别是sysfs,字符驱动,mmap/ioremap直接读写寄存器,从刚刚的分析可得,从封装程度来看,由高到低依次是sysfs>字符驱动>mmap。
本帖操作的GPIO为外扩引脚中的GPIO3_IO16,此引脚由英蓓特厂家从CPU中直接引出到外扩针脚,通过查询设备树源码文件dts,GPIO3_IO16已经事先在NXP官方evk开发板中的设备树源码由IOMUX控制器初始化,英蓓特厂家遵循NXP官方的分配方案将此作为GPIO引脚引出并直接基于官方的设备树来编写,我们开发者才可以如此方便地用sysfs或者内核驱动的方式来控制此引脚,按照IMX8引脚换算GPIO3_IO16=32*(3-1)+16=80,即操作gpio80。
sysfs驱动方式是通过操作/sys/class/gpio下的文件进行,比如驱动gpio80,依次执行以下指令:
cd /sys/class/gpioecho 80 > export cd gpio80 echo out > direction echo 1 > value echo 0 > value
复制代码然后就是内核代码驱动方案,我这边为了省事直接在内核源码中添加一个节点叫mygpio80,不在设备树中绑定GPIO引脚号,只是将其作为象征性节点来用:
/{mygpio80 { compatible = "mygpio80"; pinctrl-names = "default"; }; };
复制代码编译内核dtb设备树文件:
make dtbs
复制代码将生成的设备树文件em-sbc-imx8m.dtb放到开发板SD卡的内核信息分区mmcblk0p1中,替换同名文件:
mount /dev/mmcblk0p1 /media cd /media
复制代码那么这个修改后的dtb文件就可以将mygpio80节点暴露出来,给驱动源码使用了,但此时/dev设备节点还没生成,还需要驱动源码编译生成ko文件并插入到系统中。我为了省事直接在开发板上编译驱动源码,而为了做驱动源码本地编译工作,则需要将英蓓特厂商给出的内核源码拷到一个U盘上面,然后把U盘插到开发板上,并且U盘的文件系统格式必须是开发板可以直接识别的EXT格式:
mount /dev/sda1 /mntcd /mnt cd imx8maaxksrc make ARCH=arm64 -j4
复制代码然后是编写mygpio80的专属drv即驱动源码:
#include <linux/err.h>#include <linux/gpio.h> #include <linux/of_gpio.h> #include <linux/gpio/consumer.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/delay.h> #include <linux/device.h> #include <asm/uaccess.h> static int mygpio80_major = 0; static int mygpio80_gpio = 80; //GPIO号 static struct class *mygpio80_class; //类 static struct device *mygpio80_dev; //设备 static const char* mygpio80_name = "mygpio80"; //创建设备号 int MYGPIO80_open(struct inode *inode, struct file *flips) { printk("--------------%s--------------\n",__FUNCTION__); return 0; } static ssize_t MYGPIO80_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { buf[0]=gpio_get_value(mygpio80_gpio); } static ssize_t MYGPIO80_write(struct file *file,const char __user *buf, size_t nbytes, loff_t *ppos) { if(buf[0]==1) gpio_set_value(mygpio80_gpio,1); else if(buf[0]==0) gpio_set_value(mygpio80_gpio,0); } static int MYGPIO80_close(struct inode *inode, struct file *flip) { printk("--------------%s--------------\n",__FUNCTION__); return 0; } static struct file_operations mygpio80_fops = { .owner = THIS_MODULE, .read = MYGPIO80_read, .write = MYGPIO80_write, .open = MYGPIO80_open, .release = MYGPIO80_close, }; static const struct of_device_id of_mygpio80_match[] = { { .compatible = "mygpio80", }, {}, }; MODULE_DEVICE_TABLE(of, of_mygpio80_match); static int mygpio80_probe(struct platform_device *pdev) { int ret; enum of_gpio_flags flag; printk("-------%s-------------\n", __FUNCTION__); //struct device_node *mygpio80_gpio_node = pdev->dev.of_node; //mygpio80_gpio = of_get_named_gpio_flags(mygpio80_gpio_node->child, "gpios", 0, &flag); if (!gpio_is_valid(mygpio80_gpio)) { printk("mygpio80-gpio: %d is invalid\n", mygpio80_gpio); return -ENODEV; } else printk("mygpio80-gpio: %d is valid!\n", mygpio80_gpio); if (gpio_request(mygpio80_gpio, "mygpio80-gpio")) { printk("gpio %d request failed!\n", mygpio80_gpio); gpio_free(mygpio80_gpio); return -ENODEV; } else printk("gpio %d request success!\n", mygpio80_gpio); //能够读到配置信息之后就可以开始创建设备节点 mygpio80_major = register_chrdev(0, "mygpio80",&mygpio80_fops); if(mygpio80_major < 0) { printk(KERN_ERR "reg error!\n"); goto err_0; } else printk("mygpio80_major = %d\n",mygpio80_major); mygpio80_class = class_create(THIS_MODULE,"mygpio80_class");//creat class if( IS_ERR(mygpio80_class)) { printk(KERN_ERR "fail create class\n"); ret = PTR_ERR(mygpio80_class); goto err_1; } //创建/dev/mygpio80 mygpio80_dev = device_create(mygpio80_class, NULL,MKDEV(mygpio80_major,0), NULL, mygpio80_name); if(IS_ERR(mygpio80_dev)) { printk(KERN_ERR "fail create device!\n"); ret = PTR_ERR(mygpio80_dev); goto err_2; } gpio_direction_output(mygpio80_gpio, 1); gpio_set_value(mygpio80_gpio, 1); return 0; err_2: device_destroy(mygpio80_class,MKDEV(mygpio80_major,0)); err_1: class_destroy(mygpio80_class); err_0: unregister_chrdev(mygpio80_major,mygpio80_name); return -1; } static int mygpio80_remove(struct platform_device *pdev) { printk("-------%s-------------\n", __FUNCTION__); device_destroy(mygpio80_class,MKDEV(mygpio80_major,0)); class_destroy(mygpio80_class); unregister_chrdev(mygpio80_major,mygpio80_name); return 0; } static void mygpio80_shutdown(struct platform_device *pdev) { printk("-------%s-------------\n", __FUNCTION__); } static struct platform_driver mygpio80_driver = { .probe = mygpio80_probe, .remove = mygpio80_remove, .shutdown = mygpio80_shutdown, .driver = { .name = "mygpio80_driver", .of_match_table = of_mygpio80_match, }, }; module_platform_driver(mygpio80_driver); MODULE_AUTHOR("donatello1996"); MODULE_DESCRIPTION("MYGPIO80"); MODULE_LICENSE("GPL"); <div>
复制代码#include <stdio.h>#include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> int main () { int fd ; int retval ; unsigned char val; fd = open ( "/dev/mygpio80" , O_RDWR) ; if ( fd == -1 ) { perror ( "open /dev/mygpio80 error\n" ) ; exit ( -1 ) ; } printf ( "open /dev/mygpio80 successfully\n" ) ; while (1) { val=1; write(fd,&val,1); sleep(1); val=0; write(fd,&val,1); sleep(1); } close ( fd ) ; }
复制代码ROOTFS_DIR = /homeifeq ($(KERNELRELEASE), ) KERNEL_DIR = /mnt/imx8maaxksrc CUR_DIR = $(shell pwd) APP_NAME = mygpio80_test CC = aarch64-linux-gnu-gcc all : make -C $(KERNEL_DIR) M=$(CUR_DIR) modules $(CC) $(APP_NAME).c -o $(APP_NAME) clean : make -C $(KERNEL_DIR) M=$(CUR_DIR) clean rm -rf $(APP_NAME) install: cp -raf *.ko $(ROOTFS_DIR) cp -raf $(APP_NAME) /usr/local/bin else obj-m += mygpio80_drv.o #obj-m += math.o endif
复制代码insmod mygpio80_drv.ko
复制代码使用
lsmod
复制代码这里看到,驱动源码Makefile里面指向的内核源码路径就是/mnt/imx8maaxksrc,即内核源码文件,将驱动源码和内核源码都放到板子上面本地编译,保证原材料新鲜,不会出现不兼容的问题。
如果是要用示波器抓波形的话很简单,将延时去掉即可:
while (1) { val=1; write(fd,&val,1); val=0; write(fd,&val,1); }
复制代码上传驱动源码和ko文件:
全部回复 0
暂无评论,快来抢沙发吧