字符设备驱动程序 --使用GPIO控制引脚高低电平(点亮LED)

本文主要是介绍字符设备驱动程序 --使用GPIO控制引脚高低电平(点亮LED),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

字符设备驱动框架

1. cdev是一个描述字符设备的结构体。

<include/linux/cdev.h>struct cdev {struct kobject kobj;				// 内嵌的内核对象struct module *owner;				// 该字符设备所在的内核模块的对象指针const struct file_operations *ops;	// 该结构描述了字符设备所能实现的方法,是极为关键的一个结构体struct list_head list;				// 该结构描述了字符设备所能实现的方法,是极为关键的一个结构体dev_t dev;							// 字符设备的设备号,由主设备号和次设备号构成unsigned int count;					// 隶属于同一主设备号的次设备号的个数
};

void cdev_init(struct cdev *cdev, const struct file_operations *fops);

对 cdev 结构体做初始化, 最重要的就是建立 cdev 和 file_operations 之间的连接

cdev_init(&led_dev,&fops);

int cdev_add(struct cdev *p, dev_t dev, unsigned count);

1.该函数需要一个cdev结构体变量的地址,struct cdev结构体中包含了 dev_t和struct file_operations ,所以它能够把dev_t和驱动程序联系在一 起

2. dev参数就是之前申请到的设备号

3.申请的次设备号的个数

cdev_add(&led_dev,dev_num,1);

 2. dev_t 来定义设备号(分为主、次设备号)以确定字符设备的唯一性;

在32位机中是4个字节。主设备号占高12位,次设备号占20位。主设备号从0~4095,次设备号虽然占20位,但是它的有效范围是从 0~256。(在早期的 Unix 系统中,次设备号通常只占用一个字节(8 位),因此最大值为 256。)

主设备号用来标识与设备文件相连的驱动程序,用来反映设备类型

次设备号被驱动程序用来辨别操作的是哪个设备,用来区分同类型的设备

3.file_operations

struct file_operations *fops:这是一个指向file_operations结构体变量的指针,这个结构体里面的成员绝大多数都是函数的指针。

这些函数的指针指向一个我们编写的函数,每个函数都有着各自的作用。这里列举常用的几个:

  1.  open 函数用于打开设备文件
  2.  release 函数用于释放(关闭)设备文件,与应用程序中的close 函数对应
  3. read 函数用于读取设备文件
  4. write 函数用于向设备文件写入(发送)数据
  5.  poll 是个轮询函数,用于查询设备是否可以进行非阻塞的读写
  6.  owner 拥有该结构体的模块的指针,一般设置为 THIS_MODULE
static struct file_operations fops =
{.owner = THIS_MODULE,.open = led_driver_open,.release = led_driver_close,.read = led_driver_read,.write = led_driver_write,
};

4.模块加载函数获取设备号

1.register_chrdev( ):注册字符设备(linux 版本 2.4 之前注册方式)。

int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops);

  • register_chrdev_region( ):静态获取设备号

  • alloc_chrdev_region( ):动态获取设备号

在Linux内核源码顶层目录      linux-2.6.32.2/drivers/char   下创建  led_driver.c  文件 

static int __init led_driver_init(void);

点亮想LED,就要使用GPIO外设控制LED引脚的高低电平,就跟裸机程序一样,先要配置对应的寄存器。

操作led使用的两个寄存器GPBCON、 GPBDAT两个寄存器地址0x56000010、0x56000014这两个地址是物理地址,但是如果在程序中直接使用这两个地址linux会认为它们都是虚拟地址。

处理器中有一块叫做MMU(Memory Manage Unit)的内存映射单元,可以把引脚的物理地址映射到虚拟地址。

linux内核api函数提供了物理内存和虚拟内存的重映射带参宏:

#define ioremap(cookie,size)

第一个参数是物理地址,第二个参数是映射出的地址基类型字节数,宏 返回一个void *指针。

用字符设备驱动 控制gpio  来点亮 LED 的驱动程序。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define GPBCON  (0x56000010)
#define GPBDAT  (0x56000014)static unsigned int *REG_GPBCON;
static unsigned int *REG_GPBDAT;static int led_driver_open(struct inode *pNode,struct file *fp)
{return 0;
}static int led_driver_close(struct inode *pNode,struct file *fp)
{return 0;
}static ssize_t led_driver_read(struct file *fp,char __user*userBuffer,size_t len,loff_t *offset)
{return 0;
}static ssize_t led_driver_write(struct file *fp,const char __user *userBuffer,size_t len,loff_t*offset)
{static char c;copy_from_user(&c,userBuffer,1);//从用户空间复制一个字节到内核空间的c变量中。printk("c=%c\n",c);//c &= 0x0F;//将c的高四位清零,保留低四位。if(c == '0'){printk("00000\n");*REG_GPBDAT &=~(0x0f<<5);//将REG_GPBDAT寄存器的第5位至第9位清零。 点亮led}else if(c == '1'){		printk("11111\n");*REG_GPBDAT |=(0x0f<<5);//将REG_GPBDAT寄存器的第5位至第9位 1111。 关闭led}	//*REG_GPBDAT|=(c<<5);//将c的低四位左移5位后赋值给REG_GPBDAT的第5位至第9位。wreturn 1;
}static struct file_operations fops =
{.owner = THIS_MODULE,.open = led_driver_open,.release = led_driver_close,.read = led_driver_read,.write = led_driver_write,
};static dev_t dev_num;//设备号
struct cdev led_dev;//设备
static struct class *p_class;
static struct device *p_device;static int __init led_driver_init(void)
{int ret;//申请设备号  此设备号的起始值为 0    申请1个设备   设备起个名字叫做 led_deviceret = alloc_chrdev_region(&dev_num,0,1,"led_device");if(ret){printk("alloc_chrdev_region is error\n");goto alloc_chrdev_region_err;}printk("major = %u,minior = %u\n",MAJOR(dev_num),MINOR(dev_num));//将设备初始化cdev_init(&led_dev,&fops);//向内核添加驱动程序  相当于  register_chrdev函数 的注册ret = cdev_add(&led_dev,dev_num,1);if(ret){printk("cdev_add is error\n");goto cdev_add_err;}//创建一个设备类  led classp_class = class_create(THIS_MODULE,"led class");if(IS_ERR(p_class)){printk("class_create is error!");goto class_create_err;}//创建一个设备类 属于 之前led class的类别   这两步可以实现设备节点的自动添加//不需要使用mknod /dev/led 手动添加设备节点p_device = device_create(p_class,NULL,dev_num,NULL,"led");if(p_device == NULL){printk("device_create is error\n");goto device_create_err;}//把需要配置引脚的物理地址映射到虚拟地址REG_GPBCON = ioremap(GPBCON,4);  //io引脚的控制寄存器REG_GPBDAT = ioremap(GPBDAT,4);  //io引脚的数据寄存器//配置寄存器*REG_GPBCON &= ~(0xff << 10);*REG_GPBCON |=(0X55<< 10);*REG_GPBDAT |=(0X0F << 5);return 0;
device_create_err:class_destroy(p_class);
class_create_err:cdev_del(&led_dev);
cdev_add_err:unregister_chrdev_region(dev_num,1);
alloc_chrdev_region_err:return ret;
}static void __exit led_driver_exit(void)
{iounmap(REG_GPBCON);iounmap(REG_GPBDAT);device_destroy(p_class,dev_num);class_destroy(p_class);cdev_del(&led_dev);unregister_chrdev_region(dev_num,1);printk("led_driver_exit ok\n");
}module_init(led_driver_init);module_exit(led_driver_exit);MODULE_LICENSE("GPL");

应用程序

#include<stdio.h>
#include<stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>int main(int argc, const char *argv[])
{int fd = open("/dev/led",O_RDWR);if(fd < 0){printf("fail to open led\n");return -1;}char s = 1;while(1){s = '0';write(fd,&s,1);printf("LIANG");sleep(3);s = '1';write(fd,&s,1);sleep(3);printf("MIE");}return 0;
}

如何在板子上电后自动加载驱动?

(1)静态加载就是把驱动程序直接编译进内核,系统启动后可以直接调用。静态加载的缺点是调试起来比较麻烦,每次修改一个地方都要重新编译和下载内核,效率较低。若采用静态加载的驱动较多,会导致内核容量很大,浪费存储空间。

(2)动态加载利用了Linux的module特性,可以在系统启动后用insmod命令添加模块(.ko),在不需要的时候用rmmod命令卸载模块,采用这种动态加载的方式便于驱动程序的调试,同时可以针对产品的功能需求,进行内核的裁剪,将不需要的驱动去除,大大减小了内核的存储容量。

3、insmod与modprobe命令
在Linux中,modprobe和insmod都可以用来加载module,不过现在一般都推荐使用modprobe而不是insmod了。
modprobe和insmod的区别是什么呢?
(1)modprobe可以解决load module时的依赖关系,比如load moudleA就必须先load mouduleB之类的,它是通过/lib/modules//modules.dep文件来查找依赖关系的。而insmod不能解决依赖问题。
(2)modprobe默认会去/lib/modules/目录下面查找module,而insmod只在给它的参数中去找module(默认在当前目录找)。

这样,有时insmod也有它的有用之处,举个例子吧。
有/root/my-mod.ko这个module,cd /root/,然后用insmod my-mod.ko(insmod /root/my-mod.ko)就可以insert这个module了,但是用modprobe my-mod.ko(modprobe /root/my-mod.ko)却提示"FATAL: Module my-mod.ko not found",这就是因为modprobe是到/lib/modules/`uname -r`/下去找module的,如果没找到就是这样了。

                        
原文链接:https://blog.csdn.net/qq_40334837/article/details/89574380

这篇关于字符设备驱动程序 --使用GPIO控制引脚高低电平(点亮LED)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1104415

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

【北交大信息所AI-Max2】使用方法

BJTU信息所集群AI_MAX2使用方法 使用的前提是预约到相应的算力卡,拥有登录权限的账号密码,一般为导师组共用一个。 有浏览器、ssh工具就可以。 1.新建集群Terminal 浏览器登陆10.126.62.75 (如果是1集群把75改成66) 交互式开发 执行器选Terminal 密码随便设一个(需记住) 工作空间:私有数据、全部文件 加速器选GeForce_RTX_2080_Ti