0.前言 点灯在裸机上似乎非常容易,只需要在代码中按照手册配置一系列寄存器,编译后烧录即可。 但是在上了系统的平台上,对于硬件的操作就需要遵循一定的规范。 1.准备 点灯无疑需要对芯片的GPIO进行操作。需要对芯片进行操作,我们必须清楚使用的芯片型号以获取其官方的参考手册。 在运行平台上,使用 cat /proc/cpuinfo 查看cpu信息。 如下所示,我使用的是 i.MX6 UltraLite,这样就可以去官网获得对应的手册了。 processor : 0 model name : ARMv7 Processor rev 5 (v7l) BogoMIPS : 3.00 Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x0 CPU part : 0xc07 CPU revision : 5 Hardware : Freescale i.MX6 UltraLite (Device Tree) Revision : 0000 Serial : 0000000000000000 开发环境中也需要配置对应的工具链,完成诸如配置交叉编译工具、设置linux源码路径等工作,本文暂时不加赘述。 2.功能分析 对于不同的板级结构,操作led的逻辑也不同。我使用的led是共阳极的,所以点亮led需要将对应的GPIO置为低电平,连接led的GPIO为GPIO5_3. 查阅手册,我们需要做以下几个步骤才能使得GPIO置低: 1 使能对应GPIO的时钟 2 设置IO复用为GPIO功能 3 设置GPIO为输出模式 4 修改GPIO的数据寄存器,将对应位设置为0 3.驱动代码框架 该驱动为字符设备驱动,对于这类驱动,有较为统一的格式,入口函数、出口函数、设备号指定、文件操作函数、创建类与设备。 在linux中,一切皆为文件,对于设备的操作实际上就是一种文件操作。 我们需要定义一个文件操作结构体,其成员就是我们在对驱动文件进行对应操作时需要做的事情,即一个个函数。例如在应用程序使用open()打开驱动文件时,驱动程序会执行led_open()函数中的操作。 static struct file_operations led_fops ={ .owner = THIS_MODULE, .write = led_write, .open = led_open, }; 那么我们就需要对函数进行具体的编写,使得驱动可以完成我们需要的事情: 这类函数的返回值、参数等内容可以参考其他字符驱动程序来进行编写。 static ssize_t led_write(struct file *filp,const char __user *buf,size_t count, loff_t *ppos){ /*get data from app*/ char val; copy_from_user(&val,buf,1); /*set gpio register output 1 or 0*/ if(val == 1){ /*set gpio to make led on*/ *GPIO5_DR &= 0x7; //0111 } else{ /*set gpio to make led off*/ *GPIO5_DR |= 0x8; //1000 } return 1; } static int led_open(struct inode *inode,struct file *filp){ /*enable gpio configure pin as gpio mode configure gpio as output */ //*CCM_CCGR1 default on *IOMUX &= ~0xf;//set as gpio mode *IOMUX |= 0x5; *GPIO5_GDIR |= (1<<3);//set the pin as output mode return 0; } 其中,我对各类寄存器指针进行了定义,它们指向哪里需要参考你的芯片的手册。不过我们不能直接让他们指向物理地址,需要使用ioremap进行映射,让它们指向一个虚拟地址,我们在入口函数中进行。同时在入口函数中进行的还有注册设备、创建设备等操作: static int __init led_init(void) { printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); /*register driver*/ major = register_chrdev(0,"chk_led",&led_fops);// /*map the virtual address*/ /*ioremap*/ IOMUX = ioremap(0x02290000 + 0x14,4); CCM_CCGR1 = ioremap(0x020c4000 + 0x6c,4); GPIO5_GDIR = ioremap(0x020ac004,4); GPIO5_DR = ioremap(0x020ac000,4); /*class create*/ /*system will automatically create /dev/myled */ led_class = class_create(THIS_MODULE,"my_led"); device_create(led_class, NULL, MKDEV(major,0),NULL,"myled"); return 0; } 这样,驱动程序进入时就会对你需要的寄存器进行映射,并且注册设备号,在/dev/下创建驱动文件。 有入口肯定有出口函数,用于在卸载驱动时取消注册设备号,并取消io设备的虚拟映射,最后销毁设备和类。 static void __exit led_exit(void){ iounmap(IOMUX); iounmap(CCM_CCGR1); iounmap(GPIO5_GDIR); iounmap(GPIO5_DR); unregister_chrdev(major,"chk_led"); /*destory the device created before:/dev/myled */ /*destory the class*/ device_destroy(led_class,MKDEV(major,0)); class_destroy(led_class); } 完整的驱动代码如下: /* A Simple LED Driver char device driver hardware structure: VDD_3V3 --->|LED2---- GPIO5_3 Chip:IMX6UL Author: chk Enable GPIO5 clock <CCM_CCGR1> 0x020c4000 + 0x6c 0x020c406c Configure the pad as GPIO mode <IOMUX> 0x02290000 + 0x14 Configure the pad as output mode <GPIO5_GDIR> 0x020ac004 Write the value to registers <GPIO5_DR> 0x020ac000 a driver: 0.major 1.file_operations 2.register_chrdev 3.entrance 4.exit */ #include <linux/fs.h> #include <linux/init.h> #include <linux/module.h> #include <asm/io.h> #include <linux/uaccess.h> #include <linux/device.h> static int major; static struct class *led_class; static volatile unsigned int *IOMUX; static volatile unsigned int *CCM_CCGR1; static volatile unsigned int *GPIO5_GDIR; static volatile unsigned int *GPIO5_DR; //you can not use the physical address directly, need to re map to a virtual address static ssize_t led_write(struct file *filp,const char __user *buf,size_t count, loff_t *ppos){ /*get data from app*/ char val; copy_from_user(&val,buf,1); /*set gpio register output 1 or 0*/ if(val == 1){ /*set gpio to make led on*/ *GPIO5_DR &= 0x7; //0111 } else{ /*set gpio to make led off*/ *GPIO5_DR |= 0x8; //1000 } return 1; } static int led_open(struct inode *inode,struct file *filp){ /*enable gpio configure pin as gpio mode configure gpio as output */ //*CCM_CCGR1 default on *IOMUX &= ~0xf;//set as gpio mode *IOMUX |= 0x5; *GPIO5_GDIR |= (1<<3);//set the pin as output mode return 0; } static struct file_operations led_fops ={ .owner = THIS_MODULE, .write = led_write, .open = led_open, }; /*init function*/ static int __init led_init(void) { printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); /*register driver*/ major = register_chrdev(0,"chk_led",&led_fops);// /*map the virtual address*/ /*ioremap*/ IOMUX = ioremap(0x02290000 + 0x14,4); CCM_CCGR1 = ioremap(0x020c4000 + 0x6c,4); GPIO5_GDIR = ioremap(0x020ac004,4); GPIO5_DR = ioremap(0x020ac000,4); /*class create*/ /*system will automatically create /dev/myled */ led_class = class_create(THIS_MODULE,"my_led"); device_create(led_class, NULL, MKDEV(major,0),NULL,"myled"); return 0; } /*exit function*/ static void __exit led_exit(void){ iounmap(IOMUX); iounmap(CCM_CCGR1); iounmap(GPIO5_GDIR); iounmap(GPIO5_DR); unregister_chrdev(major,"chk_led"); /*destory the device created before:/dev/myled */ /*destory the class*/ device_destroy(led_class,MKDEV(major,0)); class_destroy(led_class); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); 为了测试驱动程序,我们需要编写一个简单的应用程序来调用它: //a test programme for my_led driver //usage: ./ledtest /dev/myled on #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <unistd.h> int main(int argc,char **argv){ int fd; char status = 0; if(argc != 3){ printf("arg error\n"); printf("usage eg: ./ledtest /dev/myled on"); return -1; } fd = open(argv[1],O_RDWR); if(fd < 0){ printf("can not open device %s\n",argv[1]); return -1; } if(strcmp(argv[2],"on") == 0){ status = 1; } else{ status = 0; } write(fd,&status,1); return 0; } 应用程序中,调用open来打开驱动文件,用write来向内核空间传入值来控制寄存器。 编译的makefile如下,确保你已经配置好了环境变量: KERN_DIR = your_path/Linux-4.9.88 all: make -C $(KERN_DIR) M=$(shell pwd) modules $(CROSS_COMPILE)gcc -o ledtest ledtest.c clean: make -C $(KERN_DIR) M=$(shell pwd) modules clean rm -rf modules.order rm -f ledtest obj-m += led.o |
|Archiver|手机版|小黑屋|软件开发编程门户 ( 陇ICP备2024013992号-1|甘公网安备62090002000130号 )
GMT+8, 2025-1-18 10:02 , Processed in 0.041373 second(s), 16 queries .
Powered by Discuz! X3.5
© 2001-2024 Discuz! Team.