博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
嵌入式linux开发の字符设备驱动程序的开发流程
阅读量:2443 次
发布时间:2019-05-10

本文共 7417 字,大约阅读时间需要 24 分钟。

本篇文章以编写电位器驱动程序为例,详细介绍并总结下设备驱动的开发流程

硬件:am3354(TI)

系统内核:linux3.2

我们在有了板子和选定好使用的内核后,在开始编写驱动之前要查看原理图,即外设使用的那几个引脚。我们还需要在板子文件中(arch/arm/根据厂商芯片名.c)把引脚设置好。因为可能你使用的这几个引脚被用于别的功能了。

在确定好使用那几个引脚后,我们要确定是使用何种框架编写(是按照杂项设备(特殊的字符设备,可以节省设备号),平台设备,字符设备),一般我是习惯用字符设备的那一套流程写

在初始化函数里,我们需要做的事情:确定并申请设备号,把设备和操作函数关联起来,创建设备节点。

 
static int major =250;//定义主设备号 static int minior=0;int number_of_device= 1;struct cdev cdev;dev_t dev =0;struct class *myclass;
// 模块加载函数static int light_init(void){        int ret;	int error;		led_init(); //设备引脚方向	dev=MKDEV(major,minior);	ret = register_chrdev_region(dev,number_of_device,"res");//申请设备号	if(ret<0)         {          printk("unable to register  driver!\n");          return ret;         } 	//生成设备节点	cdev_init(&cdev,&light_fops);//初始化cdev设备结构体	cdev.owner=THIS_MODULE;	cdev.ops=&light_fops;	error = cdev_add(&cdev,dev,1);//把设备结构体加入到系统中	if(error)         {         printk("unable to add driver!\n");             }	 myclass=class_create(THIS_MODULE,"res");//创建类节点	 	 if(IS_ERR(myclass))	 {         printk("Err: failed in creating class.\n");         return 0;      }    printk (KERN_INFO "char device registered\n");	    device_create(myclass,NULL, dev, NULL,"res");//创建设备节点                    return 0;	}
 

里面用到的几个函数详细解释下

(1)

dev=MKDEV(major,minior);

cdev结构体成员dev_t成员定义了设备号,为32位,使用上面的宏可以通过主设备号和次设备号生成dev_t

(2)

ret = register_chrdev_region(dev,number_of_device,"res");

该函数的作用是向系统申请设备号,参数:设备号,几个设备,设备名字。该函数用于已知起始设备号的情况。成功会返回一个非负整数

(3)

cdev_init(&cdev,&light_fops);//初始化cdev设备结构体

 

error = cdev_add(&cdev,dev,1);//把设备结构体加入到系统中

这几个函数是要连在一块用,在内核中所有的字符设备都会记录在一个叫kobj_map结构的cdev_map变量中,这个变量包含一个散列表来快速存取所有对象。kobj_map()函数就是把字符设备编号和cdev变量一起保存到散列表里,当后续需要打开一个字符设备文件时,内核通过调用kobj_lookup()函数,根据设备编号就可以找到cdev变量

(4)

myclass=class_create(THIS_MODULE,"res");//创建类节点	 	 if(IS_ERR(myclass))	 {         printk("Err: failed in creating class.\n");         return 0;      }    printk (KERN_INFO "char device registered\n");	    device_create(myclass,NULL, dev, NULL,"res");//创建设备节点

内核中定义了struct class 结构体,同时内核提供了class_create()函数,可以用来创建一个类,这个类存放在sysfs下面,一旦创建号这个类,在调用device_create()函数来创建设备节点。

在初始化完成后,我们需要完善结构体中的读,写,控制操作函数,方便上层应用的调用

struct file_operations light_fops = {    .owner = THIS_MODULE,    //.unlocked_ioctl = light_ioctl,    .open = light_open,    .write =light_write,    .release = light_release,};

 

通过对代码的剖析,我们知道编写字符驱动的框架其实很简单,掌握了本质,事情就变得简单。

 

附:代码

/*********************** Copyright (C) Enbo.Tech* Date:         2018-05-15* Author:       Liwx* Vision:       V1.0 * Introduction: this is a simple digital res driver***********************/#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*******************************************/#define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio))static int major =250;//定义主设备号 static int minior=0;int number_of_device= 1;struct cdev cdev;dev_t dev =0;struct class *myclass;char write_data[10];/*******************************************/void led_up(void){ printk (KERN_INFO" chip select\n"); gpio_set_value(GPIO_TO_PIN(1,15), 0); // select chip gpio_set_value(GPIO_TO_PIN(1,9), 1); // set up mode }void create_pulse(unsigned char n){ int num=0; printk (KERN_INFO" chip pulse is %d\n",n); for(num=0;num < n;num++) { gpio_set_value(GPIO_TO_PIN(1,8), 1); mdelay(10); gpio_set_value(GPIO_TO_PIN(1,8), 0); mdelay(10); }}void led_down(void){ gpio_set_value(GPIO_TO_PIN(1,15), 0); //select chip gpio_set_value(GPIO_TO_PIN(1,9), 0); // set down mode printk (KERN_INFO" chip select\n");}void led_init(void){ int result; printk(KERN_INFO"gpio init\n"); /* Allocating GPIOs and setting direction */ result = gpio_request(GPIO_TO_PIN(1,8), "TTS_INC"); if (result != 0) printk(KERN_INFO"gpio_request(1_8) failed!\n"); result = gpio_request(GPIO_TO_PIN(1,9), "TTS_U/D"); if (result != 0) printk(KERN_INFO"gpio_request(1_9) failed!\n"); result = gpio_request(GPIO_TO_PIN(1,15), "TTS_CS"); if (result != 0) printk(KERN_INFO"gpio_request(1_15) failed!\n"); result = gpio_direction_output(GPIO_TO_PIN(1,8), 1); if (result != 0) printk(KERN_INFO"gpio_direction(1_8) failed!\n"); result = gpio_direction_output(GPIO_TO_PIN(1,9), 1); if (result != 0) printk(KERN_INFO"gpio_direction(1_9) failed!\n"); result = gpio_direction_output(GPIO_TO_PIN(1,15), 1); if (result != 0) printk(KERN_INFO"gpio_direction(1_15) failed!\n"); }MODULE_AUTHOR("Liwx");MODULE_LICENSE("Dual BSD/GPL");// 打开和关闭函数int light_open(struct inode *inode,struct file *filp){ printk (KERN_INFO "open device success!\n"); return 0;}int light_release(struct inode *inode,struct file *filp){ return 0; }/*文件的读操作,上层对此设备调用read时会执行*/ static ssize_t light_write(struct file *filp, char *buf, size_t count, loff_t *ppos) { ssize_t ret =0; if (copy_from_user (write_data, buf, count)) { ret = -EFAULT; } printk (KERN_INFO"Received \n"); if(write_data[0]==1) { printk (KERN_INFO" res up\n"); led_up(); create_pulse(write_data[1]); } else { printk (KERN_INFO" res down\n"); led_down(); create_pulse(write_data[1]); } return count; } struct file_operations light_fops = { .owner = THIS_MODULE, //.unlocked_ioctl = light_ioctl, .open = light_open, .write =light_write, .release = light_release,};// 模块加载函数static int light_init(void){ int ret; int error; led_init(); dev=MKDEV(major,minior); ret = register_chrdev_region(dev,number_of_device,"res"); if(ret<0) { printk("unable to register driver!\n"); return ret; } //生成设备节点 cdev_init(&cdev,&light_fops); cdev.owner=THIS_MODULE; cdev.ops=&light_fops; error = cdev_add(&cdev,dev,1); if(error) { printk("unable to add driver!\n"); } myclass=class_create(THIS_MODULE,"res"); if(IS_ERR(myclass)) { printk("Err: failed in creating class.\n"); return 0; } printk (KERN_INFO "char device registered\n"); device_create(myclass,NULL, dev, NULL,"res"); return 0; }// 模块卸载函数static void light_exit(void){ dev_t devno = MKDEV(major,minior); cdev_del(&cdev); unregister_chrdev_region (devno, number_of_device); device_destroy(myclass, devno); class_destroy(myclass); printk("Goodbye,cruel world!\n"); }module_init(light_init);module_exit(light_exit);
 

应用程序

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
char cmd_data[2];int main(int argc, char **argv){ int i, n, fd; int oc; extern int optind; const char *count_s; const char *dir_s; int count=0; int dir; fd = open("/dev/res", O_RDWR); if (fd < 0) { printf("can't open /dev/res!\n"); exit(1); } count=atoi(argv[1]); if(count <=100) printf("pulse's number is %d\n",count); else { printf("please input number is less than 100\n"); return 0; } dir=atoi(argv[2]); if(dir == 1) printf("u/d is high level\n"); else printf("u/d is low level\n"); cmd_data[0]=dir; cmd_data[1]=count; int byte=write(fd,cmd_data,2); if(byte < 0) printf("write error\n"); return 0;}

makefile

#!/bin/bash#通知编译器我们要编译模块的哪些源码#这里是编译itop4412_hello.c这个文件编译成中间文件itop4412_hello.oobj-m += res_driver_xd.o #源码目录变量,这里用户需要根据实际情况选择路径#作者是将Linux的源码拷贝到目录/home/topeet/android4.0下并解压的KDIR := /home/liwx/src_xd/kernel-3.2#当前目录变量PWD ?= $(shell pwd)#make命名默认寻找第一个目标#make -C就是指调用执行的路径#$(KDIR)Linux源码目录,作者这里指的是/home/topeet/android4.0/iTop4412_Kernel_3.0#$(PWD)当前目录变量#modules要执行的操作all:	make -C $(KDIR) M=$(PWD) modules		#make clean执行的操作是删除后缀为o的文件clean:	rm -rf *.o

 

 

 

 

 

转载地址:http://egpqb.baihongyu.com/

你可能感兴趣的文章
第六章(backup and recovery 笔记)
查看>>
[转]数据库三大范式
查看>>
恢复编录的创建和使用.txt
查看>>
truncate 命令使用
查看>>
[script]P_CHECK_BLACK.sql 检查当前用户下是否有varchar2字段的末尾包含空格
查看>>
实验-数据分布对执行计划的影响.txt
查看>>
实验-闪回数据库
查看>>
实验-闪回表
查看>>
oracle审计
查看>>
typeof运算符_JavaScript typeof运算子
查看>>
react 前端拆分_React中的代码拆分
查看>>
叶节点到根节点的路径_节点路径模块
查看>>
前端测试简介
查看>>
如何查找公共子字符串长度_如何在C中查找字符串的长度
查看>>
javascript运算符_JavaScript比较运算符
查看>>
字符串tostring_字符串toString()方法
查看>>
number.isnan_Number isNaN()方法
查看>>
虚拟dom_虚拟DOM
查看>>
vue组件引入scss变量_如何将SCSS与Vue.js单个文件组件一起使用
查看>>
开发人员,学习营销
查看>>