嵌入式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