嵌入式Linux驱动 GPIO操作 包括驱动和应用层程序对驱动进行测试

嵌入式Linux驱动 GPIO操作

这里记录的是嵌入式linux驱动对gpio的基本操作。但是并不是规范的驱动编写方式,在后面的学习中还要进行改进。
改进过后点击这里
实现的内容是:gpio驱动编写>>>>应用编写(闪烁灯)。
目标板是iTOP4412。CPU为 Exynos4412。

代码

代码部分包括 驱动层代码对GPIO寄存器的直接操作。然后是应用层对驱动的测试代码以验证驱动的正确性。再是Makefile文件规定文件的编译规则。
代码有详细注释。

驱动代码

#include <linux/init.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/io.h>

#define GPIOL2_CON 0x11000000+0x0100  //这里是从芯片数据手册中查得的GPIOL2的基地址
#define LEN 8  //要进行虚拟地址映射的地址长度 单位:字节

volatile unsigned  long *gpiol20_cof;//用于指向经过虚拟地址映射的gpio控制寄存器地址
volatile unsigned  long *gpiol20_dat;//用于指向经过虚拟地址映射的gpio数据寄存器地址

static int DEV_MAJOR=300;  //设备主设备号(高12bit)
static int DEV_MINIOR=0;//设备次设备号(底20bit)
dev_t dev_num ;

static struct class  *devcls;  //设备类
static struct device * dev; //设备
//由于没有用到读功能所以没进行read函数的具体实现。也可以不写。
ssize_t chr_drv_read (struct file *filp, char __user *buffer, size_t count, loff_t *fpos)
{
	printk("-------%s-------\n",__FUNCTION__);
	return 0 ;
}

ssize_t chr_drv_write (struct file *flip, const char __user *buffer, size_t count, loff_t *fpos)
{
	int ret;
	int value;
	static int num=0;
	printk("write:%d ",num++);
	ret = copy_from_user(&value, buffer, count);  //从应用空间拷贝数据到内核空间

	if(value == 1)
		{
		printk("vaule:%d \n",value);
		*gpiol20_dat |=1<<0;  //设置gpiol2[0]为高电平
		}
	else
		{
		printk("vaule:%d \n",value);
		*gpiol20_dat &=~(1<<0);//设置gpiol2[0]为底电平
		}
	
	return 0;
}
//没用到不进行具体实现
int chr_drv_open (struct inode *inode, struct file *filp)
{
	printk("-------%s-------\n",__FUNCTION__);	
	return 0;
}

int chr_drv_release (struct inode *inode, struct file *filp)
{
	printk("-------%s-------\n",__FUNCTION__);
	return 0;
}
//文件操作的操作集 函数指针指向上面定义的实现函数
static struct file_operations file_ops={
		.owner = THIS_MODULE,
		.open = chr_drv_open,
		.read = chr_drv_read,
		.write = chr_drv_write,
		.release = chr_drv_release,
};
//设备装载初始化代码		
static int __init chr_io_init(void)
{
	int ret;
	printk("-----------%s--------\n",__FUNCTION__);
	dev_num= MKDEV(DEV_MAJOR, DEV_MINIOR); //得到设备号
	ret = register_chrdev(DEV_MAJOR , "io",&file_ops);//注册设备
	
	if(ret)
		goto reg_err;
	//创建设备节点
	devcls = class_create(THIS_MODULE, "io_cls");
	dev = device_create(devcls, NULL, dev_num, NULL, "io");
	//进行虚拟地址映射
	gpiol20_cof= ioremap(GPIOL2_CON, LEN);
	gpiol20_dat = gpiol20_cof+1;

	/*配置为输出模式*/
	*gpiol20_cof &=~(1<<0);
	*gpiol20_cof |=1<<0;
	
	return 0;
		
reg_err:
	return ret;
}

static void __exit chr_io_exit(void)
{

	unregister_chrdev(DEV_MAJOR, "io");
	
	device_destroy(devcls, dev_num); //销毁设备节点  和创建设备节点时相反
	class_destroy(devcls);
	printk("-----------%s--------\n",__FUNCTION__);

}

MODULE_LICENSE("GPL");
module_init(chr_io_init);
module_exit(chr_io_exit);

应用层代码 测试驱动

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
	int fd;
	int  buf=0;
	fd  = open("/dev/io",O_RDWR);
	if(fd<0)
	{
		printf("open /dev/io failed!\n fd:%d\n",fd);
		return fd;
	}
	while(1)  //循环向文件写1和0 也就是控制led亮灭
	{
		buf =0;
		write(fd,&buf,4);
		sleep(1);
		buf =1;
		write(fd,&buf,4);
		sleep(1);
	}
	close(fd);
	return 0;
}

Makefile

#!/bin/bash

obj-m +=io.o

KDIR :=/home/topeet/linux_dirver_learning/iTop4412_Kernel_3.0
#KDIR :=/lib/modules/3.5.0-23-generic/build

PWD ?=$(shell pwd) #获取当前路径

all:
	make -C $(KDIR) M=$(PWD) modules #编译驱动模块
	arm-none-linux-gnueabi-gcc -o chr_test  chr_test.c #编译测试文件
clean: #清理文件
	rm -rf *.o *.order *.mod.c *.mod.o *.symvers
install: #将生成的文件拷贝到对应的目录
	cp *.ko chr_test /home/topeet/tftpboot

发布了18 篇原创文章 · 获赞 92 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_27350133/article/details/84780230
今日推荐